mirror of
https://github.com/clawdbot/clawdbot.git
synced 2026-01-31 19:37:45 +01:00
fix: wire per-account dm scope guidance (#3095) (thanks @jarvis-sam)
This commit is contained in:
@@ -66,6 +66,7 @@ Status: beta.
|
|||||||
- Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999.
|
- Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999.
|
||||||
- macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal.
|
- macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal.
|
||||||
- CLI: use Node's module compile cache for faster startup. (#2808) Thanks @pi0.
|
- CLI: use Node's module compile cache for faster startup. (#2808) Thanks @pi0.
|
||||||
|
- Routing: add per-account DM session scope and document multi-account isolation. (#3095) Thanks @jarvis-sam.
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
- **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed).
|
- **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed).
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ moltbot security audit --deep
|
|||||||
moltbot security audit --fix
|
moltbot security audit --fix
|
||||||
```
|
```
|
||||||
|
|
||||||
The audit warns when multiple DM senders share the main session and recommends `session.dmScope="per-channel-peer"` for shared inboxes.
|
The audit warns when multiple DM senders share the main session and recommends `session.dmScope="per-channel-peer"` (or `per-account-channel-peer` for multi-account channels) for shared inboxes.
|
||||||
It also warns when small models (`<=300B`) are used without sandboxing and with web/browser tools enabled.
|
It also warns when small models (`<=300B`) are used without sandboxing and with web/browser tools enabled.
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ Use `session.dmScope` to control how **direct messages** are grouped:
|
|||||||
- `main` (default): all DMs share the main session for continuity.
|
- `main` (default): all DMs share the main session for continuity.
|
||||||
- `per-peer`: isolate by sender id across channels.
|
- `per-peer`: isolate by sender id across channels.
|
||||||
- `per-channel-peer`: isolate by channel + sender (recommended for multi-user inboxes).
|
- `per-channel-peer`: isolate by channel + sender (recommended for multi-user inboxes).
|
||||||
Use `session.identityLinks` to map provider-prefixed peer ids to a canonical identity so the same person shares a DM session across channels when using `per-peer` or `per-channel-peer`.
|
- `per-account-channel-peer`: isolate by account + channel + sender (recommended for multi-account inboxes).
|
||||||
|
Use `session.identityLinks` to map provider-prefixed peer ids to a canonical identity so the same person shares a DM session across channels when using `per-peer`, `per-channel-peer`, or `per-account-channel-peer`.
|
||||||
|
|
||||||
## Gateway is the source of truth
|
## Gateway is the source of truth
|
||||||
All session state is **owned by the gateway** (the “master” Moltbot). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
|
All session state is **owned by the gateway** (the “master” Moltbot). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
|
||||||
@@ -44,6 +45,7 @@ the workspace is writable. See [Memory](/concepts/memory) and
|
|||||||
- Multiple phone numbers and channels can map to the same agent main key; they act as transports into one conversation.
|
- Multiple phone numbers and channels can map to the same agent main key; they act as transports into one conversation.
|
||||||
- `per-peer`: `agent:<agentId>:dm:<peerId>`.
|
- `per-peer`: `agent:<agentId>:dm:<peerId>`.
|
||||||
- `per-channel-peer`: `agent:<agentId>:<channel>:dm:<peerId>`.
|
- `per-channel-peer`: `agent:<agentId>:<channel>:dm:<peerId>`.
|
||||||
|
- `per-account-channel-peer`: `agent:<agentId>:<channel>:<accountId>:dm:<peerId>` (accountId defaults to `default`).
|
||||||
- If `session.identityLinks` matches a provider-prefixed peer id (for example `telegram:123`), the canonical key replaces `<peerId>` so the same person shares a session across channels.
|
- If `session.identityLinks` matches a provider-prefixed peer id (for example `telegram:123`), the canonical key replaces `<peerId>` so the same person shares a session across channels.
|
||||||
- Group chats isolate state: `agent:<agentId>:<channel>:group:<id>` (rooms/channels use `agent:<agentId>:<channel>:channel:<id>`).
|
- Group chats isolate state: `agent:<agentId>:<channel>:group:<id>` (rooms/channels use `agent:<agentId>:<channel>:channel:<id>`).
|
||||||
- Telegram forum topics append `:topic:<threadId>` to the group id for isolation.
|
- Telegram forum topics append `:topic:<threadId>` to the group id for isolation.
|
||||||
@@ -94,7 +96,7 @@ Send these as standalone messages so they register.
|
|||||||
{
|
{
|
||||||
session: {
|
session: {
|
||||||
scope: "per-sender", // keep group keys separate
|
scope: "per-sender", // keep group keys separate
|
||||||
dmScope: "main", // DM continuity (set per-channel-peer for shared inboxes)
|
dmScope: "main", // DM continuity (set per-channel-peer/per-account-channel-peer for shared inboxes)
|
||||||
identityLinks: {
|
identityLinks: {
|
||||||
alice: ["telegram:123456789", "discord:987654321012345678"]
|
alice: ["telegram:123456789", "discord:987654321012345678"]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2657,7 +2657,8 @@ Fields:
|
|||||||
- `main`: all DMs share the main session for continuity.
|
- `main`: all DMs share the main session for continuity.
|
||||||
- `per-peer`: isolate DMs by sender id across channels.
|
- `per-peer`: isolate DMs by sender id across channels.
|
||||||
- `per-channel-peer`: isolate DMs per channel + sender (recommended for multi-user inboxes).
|
- `per-channel-peer`: isolate DMs per channel + sender (recommended for multi-user inboxes).
|
||||||
- `identityLinks`: map canonical ids to provider-prefixed peers so the same person shares a DM session across channels when using `per-peer` or `per-channel-peer`.
|
- `per-account-channel-peer`: isolate DMs per account + channel + sender (recommended for multi-account inboxes).
|
||||||
|
- `identityLinks`: map canonical ids to provider-prefixed peers so the same person shares a DM session across channels when using `per-peer`, `per-channel-peer`, or `per-account-channel-peer`.
|
||||||
- Example: `alice: ["telegram:123456789", "discord:987654321012345678"]`.
|
- Example: `alice: ["telegram:123456789", "discord:987654321012345678"]`.
|
||||||
- `reset`: primary reset policy. Defaults to daily resets at 4:00 AM local time on the gateway host.
|
- `reset`: primary reset policy. Defaults to daily resets at 4:00 AM local time on the gateway host.
|
||||||
- `mode`: `daily` or `idle` (default: `daily` when `reset` is present).
|
- `mode`: `daily` or `idle` (default: `daily` when `reset` is present).
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ By default, Moltbot routes **all DMs into the main session** so your assistant h
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This prevents cross-user context leakage while keeping group chats isolated. If the same person contacts you on multiple channels, use `session.identityLinks` to collapse those DM sessions into one canonical identity. See [Session Management](/concepts/session) and [Configuration](/gateway/configuration).
|
This prevents cross-user context leakage while keeping group chats isolated. If you run multiple accounts on the same channel, use `per-account-channel-peer` instead. If the same person contacts you on multiple channels, use `session.identityLinks` to collapse those DM sessions into one canonical identity. See [Session Management](/concepts/session) and [Configuration](/gateway/configuration).
|
||||||
|
|
||||||
## Allowlists (DM + groups) — terminology
|
## Allowlists (DM + groups) — terminology
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export async function noteSecurityWarnings(cfg: MoltbotConfig) {
|
|||||||
|
|
||||||
if (dmScope === "main" && isMultiUserDm) {
|
if (dmScope === "main" && isMultiUserDm) {
|
||||||
warnings.push(
|
warnings.push(
|
||||||
`- ${params.label} DMs: multiple senders share the main session; set session.dmScope="per-channel-peer" to isolate sessions.`,
|
`- ${params.label} DMs: multiple senders share the main session; set session.dmScope="per-channel-peer" (or "per-account-channel-peer" for multi-account channels) to isolate sessions.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ async function noteChannelPrimer(
|
|||||||
"DM security: default is pairing; unknown DMs get a pairing code.",
|
"DM security: default is pairing; unknown DMs get a pairing code.",
|
||||||
`Approve with: ${formatCliCommand("moltbot pairing approve <channel> <code>")}`,
|
`Approve with: ${formatCliCommand("moltbot pairing approve <channel> <code>")}`,
|
||||||
'Public DMs require dmPolicy="open" + allowFrom=["*"].',
|
'Public DMs require dmPolicy="open" + allowFrom=["*"].',
|
||||||
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
'Multi-user DMs: set session.dmScope="per-channel-peer" (or "per-account-channel-peer" for multi-account channels) to isolate sessions.',
|
||||||
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
||||||
"",
|
"",
|
||||||
...channelLines,
|
...channelLines,
|
||||||
@@ -238,7 +238,7 @@ async function maybeConfigureDmPolicies(params: {
|
|||||||
`Approve: ${formatCliCommand(`moltbot pairing approve ${policy.channel} <code>`)}`,
|
`Approve: ${formatCliCommand(`moltbot pairing approve ${policy.channel} <code>`)}`,
|
||||||
`Allowlist DMs: ${policy.policyKey}="allowlist" + ${policy.allowFromKey} entries.`,
|
`Allowlist DMs: ${policy.policyKey}="allowlist" + ${policy.allowFromKey} entries.`,
|
||||||
`Public DMs: ${policy.policyKey}="open" + ${policy.allowFromKey} includes "*".`,
|
`Public DMs: ${policy.policyKey}="open" + ${policy.allowFromKey} includes "*".`,
|
||||||
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
'Multi-user DMs: set session.dmScope="per-channel-peer" (or "per-account-channel-peer" for multi-account channels) to isolate sessions.',
|
||||||
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
`${policy.label} DM access`,
|
`${policy.label} DM access`,
|
||||||
|
|||||||
@@ -591,7 +591,7 @@ const FIELD_HELP: Record<string, string> = {
|
|||||||
"commands.restart": "Allow /restart and gateway restart tool actions (default: false).",
|
"commands.restart": "Allow /restart and gateway restart tool actions (default: false).",
|
||||||
"commands.useAccessGroups": "Enforce access-group allowlists/policies for commands.",
|
"commands.useAccessGroups": "Enforce access-group allowlists/policies for commands.",
|
||||||
"session.dmScope":
|
"session.dmScope":
|
||||||
'DM session scoping: "main" keeps continuity; "per-peer" or "per-channel-peer" isolates DM history (recommended for shared inboxes).',
|
'DM session scoping: "main" keeps continuity; "per-peer", "per-channel-peer", or "per-account-channel-peer" isolates DM history (recommended for shared inboxes/multi-account).',
|
||||||
"session.identityLinks":
|
"session.identityLinks":
|
||||||
"Map canonical identities to provider-prefixed peer IDs for DM session linking (example: telegram:123456).",
|
"Map canonical identities to provider-prefixed peer IDs for DM session linking (example: telegram:123456).",
|
||||||
"channels.telegram.configWrites":
|
"channels.telegram.configWrites":
|
||||||
|
|||||||
@@ -519,7 +519,8 @@ async function collectChannelSecurityFindings(params: {
|
|||||||
title: `${input.label} DMs share the main session`,
|
title: `${input.label} DMs share the main session`,
|
||||||
detail:
|
detail:
|
||||||
"Multiple DM senders currently share the main session, which can leak context across users.",
|
"Multiple DM senders currently share the main session, which can leak context across users.",
|
||||||
remediation: 'Set session.dmScope="per-channel-peer" to isolate DM sessions per sender.',
|
remediation:
|
||||||
|
'Set session.dmScope="per-channel-peer" (or "per-account-channel-peer" for multi-account channels) to isolate DM sessions per sender.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,11 +54,13 @@ export async function maybeBroadcastMessage(params: {
|
|||||||
sessionKey: buildAgentSessionKey({
|
sessionKey: buildAgentSessionKey({
|
||||||
agentId: normalizedAgentId,
|
agentId: normalizedAgentId,
|
||||||
channel: "whatsapp",
|
channel: "whatsapp",
|
||||||
|
accountId: params.route.accountId,
|
||||||
peer: {
|
peer: {
|
||||||
kind: params.msg.chatType === "group" ? "group" : "dm",
|
kind: params.msg.chatType === "group" ? "group" : "dm",
|
||||||
id: params.peerId,
|
id: params.peerId,
|
||||||
},
|
},
|
||||||
dmScope: params.cfg.session?.dmScope,
|
dmScope: params.cfg.session?.dmScope,
|
||||||
|
identityLinks: params.cfg.session?.identityLinks,
|
||||||
}),
|
}),
|
||||||
mainSessionKey: buildAgentMainSessionKey({
|
mainSessionKey: buildAgentMainSessionKey({
|
||||||
agentId: normalizedAgentId,
|
agentId: normalizedAgentId,
|
||||||
|
|||||||
Reference in New Issue
Block a user