Skip to content

ADR-039 A2: implement the local blocklist (self-protection deny-list) end-to-end #162

Description

@cuttlefisch

Context

Split out of #157. The security review (docs/SECURITY_REVIEW.md §4, finding A2) originally read as
"local blocklist ignored in verify_content_op." On closer inspection the finding is re-scoped: the
local-blocklist deny-list is designed (MembershipView.blocklist, step 7 of
derive_valid_members_governed in shared/sync/src/membership.rs) but entirely unwired in production.

Not a live bypass. Every daemon derive site passes MembershipView::default() (empty blocklist):

  • collab_handler.rs::kb_access (the authz gate)
  • collab_handler.rs::verify_content_op
  • collab_handler.rs::verify_relayed_content_op (via the members derivation)
  • the strong-removal derivation (collab_handler.rs ~L862)

There is no DocStore storage for blocked principals and no RPC/command to set one — so the empty
default is consistent everywhere and no "admit a blocked principal" hole exists today. Global op-log removal
(Revoke/Remove) already works and propagates; A2 is the local self-protection override for a principal
you cannot get globally removed (e.g. you lack quorum) but want this daemon to stop trusting.

Goal

A durable, local-only (never propagated to peers — distinct from a signed op-log removal), per-KB
deny-list that is enforced at every membership-derivation point, with a human + AI management surface.

Prior art for "deny-lists must apply at every enforcement point": SPKI/SDSI (RFC 2692), UCAN revocation.

Design / task breakdown

A2a — daemon core

  • DocStore: durable per-KB blocklist (kb_blocklist(kb_id) -> BTreeSet<String>, add_kb_block,
    remove_kb_block). Durability decision required: kb_anchors/kb_metas are currently in-memory
    only (RwLock<HashMap>) — a self-protection deny-list that evaporates on restart is weak, so prefer a
    local persisted store (NOT inside the shared kbc: CRDT collection, or it would sync to peers). Capture
    the chosen mechanism in the PR.
  • membership_view_for(doc_store, kb_id) -> MembershipView helper that loads the blocklist; replace all
    four MembershipView::default() sites with it (complete mediation — a blocked principal must be denied
    at the gate AND the content paths AND the removal derivation).
  • kb/block_principal + kb/unblock_principal RPC (params: kb_id, fingerprint). Operator-local,
    key-blind (no content). Authz: local operator (mirror the existing management-RPC principal handling;
    decide owner-only vs any-authenticated — a local self-protection action is arguably any local operator).

A2b — editor / parity surface (principle #3 human↔AI peer)

  • Command kb-block-member / kb-unblock-member (+ leader binding under the KB-sharing prefix).
  • Scheme primitives (kb-block-member) / (kb-unblock-member).
  • MCP tools kb_block_member / kb_unblock_member.
  • Surface blocked principals in the *KB Sharing* management buffer + kb_sharing_status introspection.

A2c — adversarial tests (principle #14)

  • A blocked principal's signed content op is rejected at BOTH kb_access and verify_content_op /
    verify_relayed_content_op (selective oracle: a non-blocked member's op still applies).
  • Unblock restores acceptance.
  • The block is local-only: it does NOT appear in the shared kbc: collection and is NOT observed by a
    peer that syncs the same KB.
  • Blocking a non-member is a harmless no-op; blocking the local owner is guarded or explicitly documented
    as self-lock.
  • Persistence: a block set before a simulated restart is still enforced after.

Open questions

  • Per-KB (recommended, matches kb_anchor granularity + MembershipView derivation) vs global-per-daemon.
  • Durability mechanism (local file vs storage-backend special key vs accept in-memory like kb_anchor).
  • Interaction with quorum governance (a local block is orthogonal — it never substitutes for op-log removal).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:collabCollaborative editing / sync / CRDTarea:daemonmae-daemon (persistence, collab hub)enhancementNew feature or requestsecuritySecurity / auth / access control

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions