Skip to content

#147: bind memory namespace into the signed cap service (approach B)#150

Open
hanwencheng wants to merge 1 commit into
mainfrom
claude/memory-signed-namespace
Open

#147: bind memory namespace into the signed cap service (approach B)#150
hanwencheng wants to merge 1 commit into
mainfrom
claude/memory-signed-namespace

Conversation

@hanwencheng
Copy link
Copy Markdown
Member

Summary

First real increment of #147 (the memory system), using approach B (chosen with the maintainer): fold the memory namespace into the signed service field of the cap-token (service = "memory:<namespace>") instead of adding a new signed cap claim.

Why approach B

The cap-token already signs service, and both the broker (isServiceInScope) and the memory worker (key derivation + AAD + scope re-check) already operate entirely off cap.payload.service. So making the memory service carry the namespace gives, for free:

  • Tamper-proofing — the namespace is in the signed payload; a memory:travel cap can't be edited into memory:personal.
  • Authorization — the existing on-chain isServiceInScope(operator, actor, keccak("memory:travel")) gate now authorizes the namespace. No new mechanism.
  • Storage segregation — the worker keys S3 off the service → bots/<actor>/memory/memory:travel.enc, physically distinct from memory:personal.
  • AAD binding — the envelope AAD already binds the service, so decryption is namespace-bound too.

No CapPayload change, no broker change, no byte-exact broker↔worker signature risk, no breaking cap-format. It also fixes a latent bug: today every namespace collides at the single memory.enc key (the worker ignored the body namespace).

Changes

  • crates/agentkeys-mcp-server/src/tools/memory.rsmemory.put/memory.get now mint the cap with service = "memory:<namespace>" (was a static "memory" + a redundant body field the worker ignored).
  • crates/agentkeys-worker-memory/src/handlers.rs — added a test proving namespace-folded services segregate storage (memory:travelmemory:personal key). No behavior change in the worker — it already keys/scopes/AADs off the signed service.

Verification

  • cargo test -p agentkeys-mcp-server — green (35 tests).
  • cargo test -p agentkeys-worker-memory — green (incl. the new segregation test).

Remaining for #147 (follow-up commits on this PR)

  1. Demo / scope grantharness/phase1-wire-demo.sh + the heima-scope-set step grant memory:travel (and seed in the travel namespace) instead of memory, so the --real flow authorizes the namespaced service. (--light is unaffected — the in-memory backend keys off the body namespace.)
  2. arch.md §15.2 / §17 — document that the memory service is memory:<namespace> (canonical-names + bucket-layout note).
  3. Optional — strip the redundant memory: prefix in the worker S3 key (bots/<actor>/memory/travel.enc) and a cross-namespace-denial integration test in the stage-3/wire harness.

Notes

…h B)

memory.put/get now mint the cap with service="memory:<namespace>" instead
of a static "memory". Because the broker signs `service` and the worker
already derives the S3 key, AAD, and on-chain scope check from
cap.payload.service, this makes the namespace:
  - tamper-proof (signed into the cap),
  - authorized via the existing isServiceInScope gate,
  - storage-segregated (bots/<actor>/memory/memory:<ns>.enc),
  - AAD-bound,
with NO CapPayload change, NO broker change, and no byte-exact
broker<->worker signature risk. Also fixes a latent bug where every
namespace collided at the single memory.enc key.

No worker behavior change (it already keys/scopes/AADs off the signed
service); added a test proving namespace-folded services segregate
storage.

Verified: cargo test -p agentkeys-mcp-server (35) + -p agentkeys-worker-memory green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant