- docs/v1-vs-v2/: full v1→v2 regression analysis (SUMMARY + 21 per-module docs + ACTION-ITEMS rollup with decisions + timezone recreation spec). - container/agent-runner/scripts/sdk-signal-probe.ts: empirical harness used to characterise Claude Agent SDK event/hook/stderr timing for the stuck-detection design in item 9. - src/channels/chat-sdk-bridge.ts: document the conversations Map staleness in a code comment; fix deferred to when dynamic group registration lands (ACTION-ITEMS item 17). No runtime behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
47 lines
3.6 KiB
Markdown
47 lines
3.6 KiB
Markdown
# sender-allowlist: v1 vs v2
|
|
|
|
## Scope
|
|
- v1: `src/v1/sender-allowlist.ts` (97 LOC), `sender-allowlist.test.ts` (217 LOC) — flat JSON config at `~/.config/nanoclaw/sender-allowlist.json`
|
|
- v2 counterparts: `src/access.ts` (116 LOC), `src/router.ts` (317 LOC), `src/db/schema.ts` (user_roles, agent_group_members, messaging_groups.unknown_sender_policy), `src/container-runner.ts:291-295` (admin injection), `src/types.ts` (MessagingGroupAgent.response_scope)
|
|
|
|
## Capability map
|
|
|
|
| v1 behavior | v2 location | Status | Notes |
|
|
|---|---|---|---|
|
|
| Per-chat entry (`chats[chatJid]`) | `messaging_groups.unknown_sender_policy` | replaced | Policy per channel, not allowlist entries |
|
|
| Default entry | Default `unknown_sender_policy = 'strict'` | **reversed** | v1 default-allow → v2 default-deny |
|
|
| `allow: '*'` wildcard | Not present | removed | |
|
|
| `allow: string[]` (exact-match list) | `agent_group_members` rows + `user_roles` | replaced | Role-based / membership-based |
|
|
| `mode: 'trigger'` (allow for processing) | Implicit (access granted → routed) | kept | |
|
|
| `mode: 'drop'` (silent drop) | `recordDroppedMessage()` (logs only) | **partially lost** | No silent-drop mode; denied = logged |
|
|
| Admin override | owner / global_admin / scoped_admin | **new in v2** | Richer privilege hierarchy |
|
|
| Static JSON file | Central DB (`users`, `user_roles`, `agent_group_members`) | changed | Runtime-mutable, queryable |
|
|
| Exact-string sender | Namespaced `channel_type:handle` user IDs | enhanced | Explicit channel scoping |
|
|
| `logDenied` flag | implicit (log at decision point) | kept | |
|
|
|
|
## Access-model diff
|
|
**v1**: flat allowlist per chat → default-allow → binary allowed/denied.
|
|
**v2**: entity model (`users` + roles + memberships) + per-messaging-group policy (`strict | request_approval | public`) → default-deny for unknowns.
|
|
|
|
**Strictly more expressive:** role hierarchy, per-agent-group scope, three-way unknown handling, user metadata (display_name/kind), runtime reconfig.
|
|
**Lost:** per-message `drop` mode, default-allow posture, simple JSON editing.
|
|
|
|
## Missing from v2
|
|
1. **`request_approval` flow** — marked TODO in `router.ts:295`. Approval-on-first-contact for unknown senders is scaffolded but not wired
|
|
2. **`response_scope` enforcement** — field exists (`'all' | 'triggered' | 'allowlisted'`) but is not checked in `router.ts` or `delivery.ts`
|
|
3. **Trigger-rule matching on `messaging_group_agents`** — `router.ts:198` TODO ("Future: trigger rule matching"); currently only priority-based agent selection
|
|
4. **Silent-drop option for known-noisy senders** — v1's `mode: 'drop'` allowed "I see you but I ignore you"; v2 can only log and drop
|
|
|
|
## Behavioral discrepancies
|
|
1. **Default posture flipped**: v1 open-by-default vs v2 closed-by-default — **breaking for migrations that relied on default-allow**
|
|
2. **Drop semantics**: v1 silent drop; v2 `recordDroppedMessage()` always logs
|
|
3. **Admin bypass**: v1 had no implicit bypass; v2 grants owners/admins access regardless of membership — more permissive for privileged users
|
|
4. **Scope resolution**: v1 per-chat; v2 per-agent-group via `user_roles.agent_group_id` — misalignment if one chat routes to multiple agent groups with different admins
|
|
|
|
## Worth preserving?
|
|
The v2 role-based model is architecturally superior. The gaps worth closing:
|
|
- **Finish `request_approval`** flow — half-implemented scaffolding
|
|
- **Finish `response_scope` enforcement** — exists in schema but unused
|
|
- **Finish trigger-rule matching** in `pickAgent` — without it, every wired agent fires on every message
|
|
- **Consider silent-drop via a dedicated table** (`(agent_group_id, sender_pattern)` with action=drop) — orthogonal to privilege
|