refactor: move channel pairing state to sqlite

This commit is contained in:
Peter Steinberger
2026-05-07 00:09:09 +01:00
parent be790009f8
commit 003dfb4821
24 changed files with 553 additions and 782 deletions

View File

@@ -274,7 +274,7 @@ Control how group/room messages are handled per channel:
- `groupPolicy` is separate from mention-gating (which requires @mentions).
- WhatsApp/Telegram/Signal/iMessage/Microsoft Teams/Zalo: use `groupAllowFrom` (fallback: explicit `allowFrom`).
- Signal: `groupAllowFrom` can match either the inbound Signal group id or the sender phone/UUID.
- DM pairing approvals (`*-allowFrom` store entries) apply to DM access only; group sender authorization stays explicit to group allowlists.
- DM pairing approvals (stored in SQLite pairing state) apply to DM access only; group sender authorization stays explicit to group allowlists.
- Discord: allowlist uses `channels.discord.guilds.<id>.channels`.
- Slack: allowlist uses `channels.slack.channels`.
- Matrix: allowlist uses `channels.matrix.groups`. Prefer room IDs or aliases; joined-room name lookup is best-effort, and unresolved names are ignored at runtime. Use `channels.matrix.groupAllowFrom` to restrict senders; per-room `users` allowlists are also supported.

View File

@@ -78,17 +78,20 @@ Access groups are documented in detail here: [Access groups](/channels/access-gr
### Where the state lives
Stored under `~/.openclaw/credentials/`:
Stored in `~/.openclaw/state/openclaw.sqlite`:
- Pending requests: `<channel>-pairing.json`
- Approved allowlist store:
- Default account: `<channel>-allowFrom.json`
- Non-default account: `<channel>-<accountId>-allowFrom.json`
- Pending requests: SQLite `kv` scope `pairing.channel`
- Approved allowlist store: same SQLite record, account-scoped by channel account ID
Account scoping behavior:
- Non-default accounts read/write only their scoped allowlist file.
- Default account uses the channel-scoped unscoped allowlist file.
- Non-default accounts read/write only their scoped allowlist entry.
- Default account uses the `default` account entry.
Older `~/.openclaw/credentials/<channel>-pairing.json`,
`<channel>-allowFrom.json`, and `<channel>-<accountId>-allowFrom.json` files
are legacy import sources only. Run `openclaw doctor --fix` to import them into
SQLite and remove the JSON files.
Treat these as sensitive (they gate access to your assistant).

View File

@@ -118,7 +118,7 @@ Token resolution order is account-aware. In practice, config values win over env
`dmPolicy: "allowlist"` with empty `allowFrom` blocks all DMs and is rejected by config validation.
Setup asks for numeric user IDs only.
If you upgraded and your config contains `@username` allowlist entries, run `openclaw doctor --fix` to resolve them (best-effort; requires a Telegram bot token).
If you previously relied on pairing-store allowlist files, `openclaw doctor --fix` can recover entries into `channels.telegram.allowFrom` in allowlist flows (for example when `dmPolicy: "allowlist"` has no explicit IDs yet).
If you previously relied on pairing-store allowlist state, `openclaw doctor --fix` can recover entries into `channels.telegram.allowFrom` in allowlist flows (for example when `dmPolicy: "allowlist"` has no explicit IDs yet). Older pairing JSON files are imported into SQLite first.
For one-owner bots, prefer `dmPolicy: "allowlist"` with explicit numeric `allowFrom` IDs to keep access policy durable in config (instead of depending on previous pairing approvals).

View File

@@ -231,7 +231,7 @@ content and identifiers.
Runtime behavior details:
- pairings are persisted in channel allow-store and merged with configured `allowFrom`
- pairings are persisted in SQLite channel pairing state and merged with configured `allowFrom`
- scheduled automation and heartbeat recipient fallback use explicit delivery targets or configured `allowFrom`; DM pairing approvals are not implicit cron or heartbeat recipients
- if no allowlist is configured, the linked self number is allowed by default
- OpenClaw never auto-pairs outbound `fromMe` DMs (messages you send to yourself from the linked device)

View File

@@ -71,7 +71,7 @@ openclaw security audit --fix --json | jq '{fix: .fix.ok, summary: .report.summa
- flips common `groupPolicy="open"` to `groupPolicy="allowlist"` (including account variants in supported channels)
- when WhatsApp group policy flips to `allowlist`, seeds `groupAllowFrom` from
the stored `allowFrom` file when that list exists and config does not already
the stored pairing allowlist when that list exists and config does not already
define `allowFrom`
- sets `logging.redactSensitive` from `"off"` to `"tools"`
- tightens permissions for state/config and common sensitive files

View File

@@ -241,9 +241,7 @@ Use this when auditing access or deciding what to back up:
- **Telegram bot token**: config/env or `channels.telegram.tokenFile` (regular file only; symlinks rejected)
- **Discord bot token**: config/env or SecretRef (env/file/exec providers)
- **Slack tokens**: config/env (`channels.slack.*`)
- **Pairing allowlists**:
- `~/.openclaw/credentials/<channel>-allowFrom.json` (default account)
- `~/.openclaw/credentials/<channel>-<accountId>-allowFrom.json` (non-default accounts)
- **Pairing allowlists**: `~/.openclaw/state/openclaw.sqlite` (`kv` scope `pairing.channel`)
- **Model auth profiles**: `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`
- **Codex runtime state**: `~/.openclaw/agents/<agentId>/agent/codex-home/`
- **File-backed secrets payload (optional)**: `~/.openclaw/secrets.json`
@@ -575,7 +573,7 @@ If you run multiple accounts on the same channel, use `per-account-channel-peer`
OpenClaw has two separate "who can trigger me?" layers:
- **DM allowlist** (`allowFrom` / `channels.discord.allowFrom` / `channels.slack.allowFrom`; legacy: `channels.discord.dm.allowFrom`, `channels.slack.dm.allowFrom`): who is allowed to talk to the bot in direct messages.
- When `dmPolicy="pairing"`, approvals are written to the account-scoped pairing allowlist store under `~/.openclaw/credentials/` (`<channel>-allowFrom.json` for default account, `<channel>-<accountId>-allowFrom.json` for non-default accounts), merged with config allowlists.
- When `dmPolicy="pairing"`, approvals are written to the account-scoped pairing allowlist store in `~/.openclaw/state/openclaw.sqlite`, merged with config allowlists. Older `~/.openclaw/credentials/*-pairing.json` and `*-allowFrom.json` files are imported only by `openclaw doctor --fix`.
- **Group allowlist** (channel-specific): which groups/channels/guilds the bot will accept messages from at all.
- Common patterns:
- `channels.whatsapp.groups`, `channels.telegram.groups`, `channels.imessage.groups`: per-group defaults like `requireMention`; when set, it also acts as a group allowlist (include `"*"` to keep allow-all behavior).

View File

@@ -131,9 +131,11 @@ This plan has started landing in slices:
legacy import source and is removed after import; `auth-profiles.json` still
owns credentials and stays file-backed.
- Device identity, local device auth tokens, bootstrap tokens, device/node
pairing ledgers, web push subscriptions/VAPID keys, and APNs registration
state now use the shared SQLite `kv` store. `openclaw doctor --fix` imports
the legacy `identity/*.json`, `devices/*.json`, `nodes/*.json`, and
pairing ledgers, channel pairing requests/allowlists, web push
subscriptions/VAPID keys, and APNs registration state now use the shared
SQLite `kv` store. `openclaw doctor --fix` imports the legacy
`identity/*.json`, `devices/*.json`, `nodes/*.json`,
`credentials/*-pairing.json`, `credentials/*-allowFrom.json`, and
`push/*.json` files into SQLite and removes those files after a successful
import. Runtime paths no longer read or write those JSON ledgers.
- `AgentRuntimeBackend`, `PreparedAgentRun`, and the Node worker runner exist

View File

@@ -143,9 +143,7 @@ Use this when debugging auth or deciding what to back up:
- **Telegram bot token**: config/env or `channels.telegram.tokenFile` (regular file only; symlinks rejected)
- **Discord bot token**: config/env or SecretRef (env/file/exec providers)
- **Slack tokens**: config/env (`channels.slack.*`)
- **Pairing allowlists**:
- `~/.openclaw/credentials/<channel>-allowFrom.json` (default account)
- `~/.openclaw/credentials/<channel>-<accountId>-allowFrom.json` (non-default accounts)
- **Pairing allowlists**: `~/.openclaw/state/openclaw.sqlite` (`kv` scope `pairing.channel`)
- **Model auth profiles**: `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`
- **File-backed secrets payload (optional)**: `~/.openclaw/secrets.json`
- **Legacy OAuth import**: `~/.openclaw/credentials/oauth.json`