Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
140 lines
5.4 KiB
Markdown
140 lines
5.4 KiB
Markdown
---
|
|
summary: "Experimental channel ingress API for inbound message authorization"
|
|
read_when:
|
|
- Building or migrating a messaging channel plugin
|
|
- Changing DM or group allowlists, route gates, command auth, event auth, or mention activation
|
|
- Reviewing channel ingress redaction or SDK compatibility boundaries
|
|
title: "Channel ingress API"
|
|
sidebarTitle: "Channel Ingress"
|
|
---
|
|
|
|
Channel ingress is the experimental access-control boundary for inbound
|
|
channel events. Plugins own platform facts and side effects; core owns
|
|
generic policy: DM/group allowlists, pairing-store DM entries, route gates,
|
|
command gates, event auth, mention activation, redacted diagnostics, and
|
|
admission.
|
|
|
|
Use `openclaw/plugin-sdk/channel-ingress-runtime` for new receive paths. The
|
|
older `openclaw/plugin-sdk/channel-ingress` subpath stays exported as a
|
|
deprecated compatibility facade for third-party plugins.
|
|
|
|
## Runtime resolver
|
|
|
|
```ts
|
|
import {
|
|
defineStableChannelIngressIdentity,
|
|
resolveChannelMessageIngress,
|
|
} from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
|
|
const identity = defineStableChannelIngressIdentity({
|
|
key: "platform-user-id",
|
|
normalize: normalizePlatformUserId,
|
|
sensitivity: "pii",
|
|
});
|
|
|
|
const result = await resolveChannelMessageIngress({
|
|
channelId: "my-channel",
|
|
accountId,
|
|
identity,
|
|
subject: { stableId: platformUserId },
|
|
conversation: { kind: isGroup ? "group" : "direct", id: conversationId },
|
|
event: { kind: "message", authMode: "inbound", mayPair: !isGroup },
|
|
policy: {
|
|
dmPolicy: config.dmPolicy,
|
|
groupPolicy: config.groupPolicy,
|
|
groupAllowFromFallbackToAllowFrom: true,
|
|
},
|
|
allowFrom: config.allowFrom,
|
|
groupAllowFrom: config.groupAllowFrom,
|
|
accessGroups: cfg.accessGroups,
|
|
route,
|
|
readStoreAllowFrom,
|
|
command: hasControlCommand ? { allowTextCommands: true, hasControlCommand } : undefined,
|
|
});
|
|
```
|
|
|
|
Do not precompute effective allowlists, command owners, or command groups.
|
|
The resolver derives them from raw allowlists, store callbacks, route
|
|
descriptors, access groups, policy, and conversation kind.
|
|
|
|
## Result
|
|
|
|
Bundled plugins should consume modern projections directly:
|
|
|
|
| Field | Meaning |
|
|
| ------------------ | ------------------------------------------------------------------ |
|
|
| `ingress` | ordered gate decision and admission |
|
|
| `senderAccess` | sender/conversation authorization only |
|
|
| `routeAccess` | route and route-sender projection |
|
|
| `commandAccess` | command authorization; `requested: false` when no command gate ran |
|
|
| `activationAccess` | mention/activation result |
|
|
|
|
Event authorization stays available on the ordered `ingress.graph` and the
|
|
decisive `ingress.reasonCode`; no separate event projection is emitted.
|
|
|
|
Deprecated third-party SDK helpers may rebuild older shapes internally. New
|
|
bundled receive paths should not translate modern results back into local
|
|
DTOs.
|
|
|
|
## Access groups
|
|
|
|
`accessGroup:<name>` entries stay redacted. Core resolves static
|
|
`message.senders` groups itself and calls `resolveAccessGroupMembership` only
|
|
for dynamic groups that require a platform lookup. Missing, unsupported, and
|
|
failed groups fail closed.
|
|
|
|
## Event modes
|
|
|
|
| `authMode` | Meaning |
|
|
| ---------------- | ------------------------------------------------ |
|
|
| `inbound` | normal inbound sender gates |
|
|
| `command` | command gates for callbacks or scoped buttons |
|
|
| `origin-subject` | actor must match the original message subject |
|
|
| `route-only` | route gates only for route-scoped trusted events |
|
|
| `none` | plugin-owned internal events bypass shared auth |
|
|
|
|
Use `mayPair: false` for reactions, buttons, callbacks, and native commands.
|
|
|
|
## Routes and activation
|
|
|
|
Use route descriptors for room, topic, guild, thread, or nested route policy:
|
|
|
|
```ts
|
|
route: {
|
|
id: "room",
|
|
allowed: roomAllowed,
|
|
enabled: roomEnabled,
|
|
senderPolicy: "replace",
|
|
senderAllowFrom: roomAllowFrom,
|
|
blockReason: "room_sender_not_allowlisted",
|
|
}
|
|
```
|
|
|
|
Use `channelIngressRoutes(...)` when a plugin has several optional route
|
|
descriptors; it filters disabled branches while keeping route facts generic
|
|
and ordered by each descriptor's `precedence`.
|
|
|
|
Mention gating is an activation gate. A mention miss returns
|
|
`admission: "skip"` so the turn kernel does not process an observe-only turn.
|
|
Most channels should leave activation after sender and command gates. Public
|
|
chat surfaces that must quiet non-mentioned traffic before sender allowlist
|
|
noise can opt into `activation.order: "before-sender"` when text-command
|
|
bypass is disabled. Channels with implicit activation, such as replies in bot
|
|
threads, can pass `activation.allowedImplicitMentionKinds`; the projected
|
|
`activationAccess.shouldBypassMention` then reports when command or implicit
|
|
activation bypassed an explicit mention.
|
|
|
|
## Redaction
|
|
|
|
Raw sender values and raw allowlist entries are resolver input only. They
|
|
must not appear in resolved state, decisions, diagnostics, snapshots, or
|
|
compatibility facts. Use opaque subject ids, entry ids, route ids, and
|
|
diagnostic ids.
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
pnpm test src/channels/message-access/message-access.test.ts src/plugin-sdk/channel-ingress-runtime.test.ts
|
|
pnpm plugin-sdk:api:check
|
|
```
|