Memory namespace model: signed cap claim + worker filter + audit#143
Open
hanwencheng wants to merge 1 commit into
Open
Memory namespace model: signed cap claim + worker filter + audit#143hanwencheng wants to merge 1 commit into
hanwencheng wants to merge 1 commit into
Conversation
…closes #108) Wire memory namespaces (personal/family/work/travel) through the full stack so a device's cap-token can read/write one life-context but not others — the semantic dimension that makes the M1 demo's "toy sees the trip, not the allergy" legible. - agentkeys-types: new `Namespace` enum (v0 set + parse/validate). - broker: `namespaces_allowed` is a SIGNED field in CapPayload; the /v1/cap/memory-* endpoints validate + sign it (unknown name → 400); cred caps always get []. Field order matches the worker mirror so the signature verifies byte-for-byte. - worker-memory: re-verifies the signature then filters by deterministic string-set membership. Cross-namespace read → empty result (never the data); write → ok:false. Unknown namespace → 400. Blob keyed bots/<actor>/memory/<namespace>/<service>.enc so the four namespaces coexist under the agent-facing tools (which don't expose `service`). - MCP server: threads the operator-configured allowlist into cap-mint (agent can't self-widen) and emits a `memory.namespace_violation` audit row (op_kind 13) on a violation. - audit: new canonical op_kind 13 (MemoryNamespaceViolation) + body. - docs: arch.md §17.6 + op_kind table; strategy §3.5 status; memory design wire format. harness stage-3 + stdio demo updated for the required `namespace` field, plus a live cross-namespace negative test. Tests: namespace gate positive+negative (worker verify, broker validate, types), three-act Act 1 now asserts the cross-namespace audit row. Full workspace test + clippy + fmt green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #108.
Wires memory namespaces (
personal/family/work/travel) through the whole stack so a device's cap-token can read/write one life-context but see nothing in the others — the semantic dimension behind the M1 demo's "the toy sees the Chengdu trip, not the peanut allergy." Strategic anchor:agent-iam-strategy.md§3.5; as-built spec:arch.md§17.6.How it works (3 gates on a deterministic membership test)
namespaces_allowed: [...]into the cap payload./v1/cap/memory-*validate each name against the v0 set (unknown → 400); cred caps always get[]. The field sits at the same position in the broker and workerCapPayloadso the signature verifies byte-for-byte.namespace ∉ namespaces_allowed: a read returns an empty result (never the data, doesn't even reveal it exists); a write returnsok:false. Unknown namespace → 400.memory.namespace_violationrow (new canonical op_kind 13).The allowlist is operator-sourced (
MCP_DEFAULT_NAMESPACES_ALLOWED), threaded into cap-mint — the agent can't widen its own scope by asking.What landed
agentkeys-types:Namespaceenum (v0 set +parse/is_valid) — lib.rs.check_namespace_allowed+NamespaceNotAllowed): verify.rs.namespacefield, gate, empty/refused verdicts,bots/<actor>/memory/<namespace>/<service>.enckeying, unknown→400): handlers.rs + Cargo.toml.MemoryNamespaceViolation) + body +TypedAuditBodyarm: op_kind.rs, bodies.rs, mod.rs.verify, brokernormalize_namespaces, types, worker-memory handlers; three-act Act 1 now asserts the cross-namespace empty result + audit row (three_acts.rs).namespacefield; stage-3 gains a live cross-namespace negative test (v2-stage3-demo.sh).Acceptance criteria met: signed-and-re-verified claim, empty read for disallowed namespaces, audit row on violation, positive+negative unit tests.
What did NOT land
<namespace>/path component instead, because the metadata-only filter presupposes the unbuilt 4-type LIST-and-filter retrieval. Deliberate, documented in arch.md §17.6. On-chain per-namespace scope (so a compromised MCP server can't exceed a grant) is also a later-phase hardening — M1 trust model is "operator configures the server."Test plan
cargo test --workspace— green (53 suites, 0 failed)cargo clippy --workspace --all-targets— cleancargo fmt --check— cleanAGENTKEYS_CHAIN=heima bash harness/v2-stage3-demo.sh) — needs broker + chain + AWS🤖 Generated with Claude Code