Skip to content

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)

  1. Client: membus send refuses scope writes the allowlist forbids (exit 3).
  2. 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 --server is blocked: Bearer keys are only sent to known house hosts (ANDYGREATROOMPC, localhost, 10.0.0.0/24, plus MEMBUS_ALLOWED_HOSTS); anything else requires MEMBUS_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.