membus¶
House shared-memory bus: a thin C# adapter over the claude-mem house server that gives every AI harness (claude, codex, gemini/agy, grok, nanoclaw, ollama, odysseus) scoped read/write access to shared memory tiers — with the write rules enforced in code, not prose.
Part of the polyharness parity epic (P1). Councilled 2026-07-10:
codex-harness-mirror/docs/POLYHARNESS-PARITY-SPEC.md.
Scopes¶
| Scope | Who writes | Purpose |
|---|---|---|
house-all |
frontier harnesses only | durable facts every model should know |
{family}-house (claude/codex/gemini/grok/ollama/odysseus) |
own family only | model-family memory across all 4 PCs |
council:<pc> |
any harness on that PC | per-machine council deliberation (default TTL 14d — promote keepers to house-all) |
frontier:<pc> |
frontier harnesses on that PC | frontier-only machine context |
local-llms-house |
local tier (ollama, odysseus) | shared memory for local models |
worktree-ownership |
any harness | advisory worktree claim/renew/release/takeover events; registry remains the lock |
Frontier = claude, codex, gemini, grok, nanoclaw. Local = ollama. Service = odysseus.
Reads are open house-wide (Bearer key is the gate); writes go through WriteAllowlist.
Scope → projectId mapping is deterministic (RFC 4122 v5 UUID from a fixed namespace), so every
machine computes identical ids with zero config sync. house-all maps to the pre-existing shared
house project, so memories already on the server are the house-all pool.
Enforcement (two layers)¶
- Client:
membus sendrefuses scope writes the allowlist forbids (exit 3). - Server: claude-mem API keys are bound to exactly one project. Each scope gets its own key
(
~/.membus/keyring.json); a harness without a scope's key physically cannot touch that scope's project — the council's "server rejects unlisted projectIds" mandate, delivered via key custody. Keyring distribution decides per-machine capability.
flowchart LR
H[harness CLI/hook] -->|membus send --scope| A[allowlist check]
A -->|denied exit 3| X[refused locally]
A -->|allowed| K[keyring: per-scope Bearer key]
K -->|POST /v1/memories + provenance| S[(claude-mem server :37877\nPostgres, project per scope)]
S -->|403 if key/project mismatch| K
H -->|membus recall --scope --query| S
Usage¶
membus send --scope grok-house --content "fact" [--kind manual] [--ttl-days N] [--dedupe-key K]
membus send --scope council:%COMPUTERNAME% --content - # stdin
membus ownership-event --event claim --repo <id> --branch <b> --lane <WT>
membus recall --scope house-all --query "topic" [--json]
membus scopes # scope catalog + projectIds; [W] = writable for this harness
membus health
Identity/config:
| Setting | Source |
|---|---|
| harness | MEMBUS_HARNESS or --harness (required — never guessed) |
| machine | MEMBUS_MACHINE, else COMPUTERNAME |
| server | MEMBUS_SERVER_URL, default http://ANDYGREATROOMPC:37877 |
| fallback key | CLAUDE_MEM_HOUSE_API_KEY / CLAUDE_MEM_SERVER_API_KEY, else keyring house-all |
| keyring | MEMBUS_KEYRING, default ~/.membus/keyring.json — flat { "scope": "cmem_..." } |
Every write is stamped with provenance metadata:
scope, origin_harness, origin_machine, timestamp, trust_level, ttl_days, dedupe_key, schema_version.
Exit codes: 0 ok · 2 usage/config · 3 write denied by allowlist · 4 server/transport error.
Provisioning (server admin, main PC only)¶
scripts/provision-scopes.ps1 — idempotent: inserts the scope catalog as project rows in the
claude-mem Postgres, mints one memories:read,memories:write key per scope, and writes
~/.membus/keyring.json incrementally (a rerun only mints what's missing). Keys are secrets:
the keyring never enters git; distribute to house PCs via the vault/bootstrap-kit channel.
Threat model (explicit, per codex review 2026-07-10)¶
- Harness identity is self-declared (
MEMBUS_HARNESS): any local process with the machine's keyring can claim any harness. The client allowlist is a policy layer against accidents and non-compliant small models, not a security boundary against a hostile local process. The real boundary is key custody; per-harness keyrings are P6 guardrail work. - Bearer over plain HTTP on the house LAN: matches the existing claude-mem server posture (Private-profile firewall, LocalSubnet-scoped rules, trusted household). Keys are sniffable by a hostile device already inside the LAN — accepted at house scale; TLS is the P6 upgrade.
- Key exfiltration via
--serveris blocked: Bearer keys are only sent to known house hosts (ANDYGREATROOMPC, localhost, 10.0.0.0/24, plusMEMBUS_ALLOWED_HOSTS); anything else requiresMEMBUS_UNSAFE_SERVER=1. - Derived scopes without a keyring entry are refused — never silently retried with the broader house-all key.
Non-goals (P1)¶
- No second memory product: storage, search, and generation stay in claude-mem.
- No pub/sub (council-rejected: memory is pull-shaped; Valkey pub/sub is the upgrade path).
- Per-harness key custody (different keyrings per harness) is P6 guardrail work; P1 ships one keyring per machine + client-side allowlist.