From 833d0378e0f76647e82f6499661a3c5572746069 Mon Sep 17 00:00:00 2001 From: Sander Striker Date: Tue, 27 Oct 2026 08:26:37 +0100 Subject: [PATCH 1/6] docs: name the sandbox and the egress beside the memory server The README and the reference stack showed the memory server as the whole system. The sandbox and the egress also keep the system safe. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bhj4Ck4y5iwZUsjpgQx8A2 --- README.md | 20 +++++++++++++------- docs/reference-stack.md | 12 +++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6a7dd16..a1e950b 100644 --- a/README.md +++ b/README.md @@ -130,12 +130,15 @@ argued from taste. ## What it is -Two halves around one record. The **memory server** is what an agent harness reaches -over MCP: it serves the record by summaries first, holds the task ledger, and checks every -read and write against the policy. The **launcher** runs a harness confined, with no -network except the **egress**, which forwards its model calls to a provider or a gateway, -meters them, holds the credentials, enforces spend limits and records each call's -decision context. An agent starts empty, loads a small index called T0, works a task it +Several parts around one record, and no one of them is the safety boundary on its own. +The **memory server** is what an agent harness reaches over MCP: it serves the record by +summaries first, holds the task ledger, and checks every read and write against the +policy. The **launcher** runs a harness in a **sandbox**, with no network except the +**egress**, which forwards its model and tool calls to a provider, a gateway or a tool +server, holds each to its destination's ceiling, meters them, holds the credentials, +enforces spend limits and records each call's decision context. The memory server and the +egress enforce the same grants; the sandbox makes the egress the only way out. An agent +starts empty, loads a small index called T0, works a task it claimed, and writes a handoff when it stops; the next agent continues from the handoff, not a transcript. Around them, the **librarian** curates the record on a budget, the **perimeter** decides what may be read, written and admitted, and a person promotes what @@ -145,7 +148,7 @@ the system may rely on. person ──▶ amem: web launcher, CLI, profiles │ launch / work (a claimed task) ▼ - ┌─ confined harness, no network ───────┐ + ┌─ sandbox: harness, no network ───────┐ │ Claude Code, Codex, Gemini CLI, ... │──MCP──▶ memory server ──▶ record/ entries, transcripts, contexts, tapes └──────────────┬───────────────────────┘ │ policy: ├──▶ index SQLite FTS5, BM25 │ model calls │ Rego ├──▶ ledger SQLite, or PostgreSQL @@ -226,6 +229,9 @@ claude mcp add memory -- /path/to/amem --root ~/memory serve --author "agent:cla The agent gets `t0`, `search`, `get`, `write`, `list`, `recall`, `promote`, the ledger's tools and `transcript_turn`, and its writes land at raw trust under the author given. +Unconfined means only that: the record's rules hold for what the harness reads and writes +over MCP, but no sandbox or egress stands around the rest of what it does, and it keeps +its own network and credentials. ## The parts, and their commands diff --git a/docs/reference-stack.md b/docs/reference-stack.md index e70058b..e5a9b9f 100644 --- a/docs/reference-stack.md +++ b/docs/reference-stack.md @@ -19,13 +19,17 @@ Two things fall out of this for free. One materializer per domain, which the pap | Index | Apache Solr, shard key = domain | Lucene BM25 for ranked text, the compiled filter expressed as a query filter, dense vectors as a derived field in the same engine. Index-served searches in single-digit ms. Shard by domain so a session's filter hits one shard | One Solr container. OpenSearch is the non-Apache equivalent if operations prefer it | | Ledger and lease cache | PostgreSQL, one schema per domain | A claim is one statement with row locking and `SKIP LOCKED`: atomic, a few ms. Domains are independent, so partitions and replicas scale by domain. Rebuildable from the log | One container | | Policy engine | OPA as a sidecar, registry and grants as data | Partial evaluation compiles the session filter once; every later check is local. The filter is pushed into the Solr query, so denied entries are never scored | One sidecar per memory-server replica | -| Memory server | A stateless service: MCP and HTTP surface, compiled filter and T0 cached per session, ceiling check per outbound call | Replicas behind a load balancer. T0 is a prefix the harness caches, so orientation costs zero tool calls | One container, scale by count | +| Memory server | A stateless service: MCP and HTTP surface, compiled filter and T0 cached per session, the read and write checks | Replicas behind a load balancer. T0 is a prefix the harness caches, so orientation costs zero tool calls | One container, scale by count | +| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS, with no network but the egress | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | +| Egress | Ours: a forwarding proxy, the confined harness's only network. Ceiling check per outbound call, credentials injected, spend limits, metering, decision contexts recorded | One beside each confined session, forwarding to LiteLLM for models and to tool servers for taps; one local hop on each call | Part of `amem`; its own container under WSL | | Recall deputy, librarians, auditors | Agents on Gofannon, cheap tier through llmao | Already how the platform runs. Librarians are a consumer group on the log like any other | The existing Gofannon and LiteLLM containers | | Taps | MCP servers, one per tracker or list, each registered with a ceiling; as WASI components hosted by the memory server (section 4) | Contributed connectors run sandboxed with exactly the network and filesystem the grant allows | Containers, or components loaded by the server | | Telemetry and analytics | OpenTelemetry to Kafka; Apache Flink or Spark streaming into Apache Iceberg tables on the same object store; queried with Apache DataFusion or Trino | Columnar, append-heavy, cross-corpus. Iceberg snapshots give time travel over telemetry and over the entry metadata table. This is where cost per task, co-retrieval mining, decay sweeps, and lineage queries live | OTel collector writing Parquet; DuckDB or DataFusion to query. Flink when volume justifies it | | Interchange | Markdown bundle with a manifest; Apache Arrow for bulk metadata transport | Arrow is the columnar wire format the analytics side already speaks | Same | | Identity | Authentik for the institution; nobody for a single person | Lives in the serving layer, so the local stack runs with it off | Optional container | +No one row is the safety boundary. The memory server checks what is read and written; the sandbox keeps the harness off the network and the host; the egress decides where each call may go and holds the credentials; the memory server and the egress enforce the same grants. A harness that reaches the memory server without the launcher keeps the first and none of the rest. + ## 3. Latency on the hot path comes from what is never on it - **Orientation** is a cached prefix: T0 and the policy digest arrive with the session, not as tool calls. @@ -37,7 +41,7 @@ Two things fall out of this for free. One materializer per domain, which the pap ## 4. Language: Rust, one core library compiled three ways -The parts that are ours: the memory server, the materializer, the indexer, the ledger projection, taps, the personal server, the CLI, and a browser UI. The recommendation is Rust, with a single core library that compiles to native for Windows and Linux, to WebAssembly for the browser, and to WASI for sandboxed plugins. +The parts that are ours: the memory server, the launcher with its sandbox, the egress, the materializer, the indexer, the ledger projection, taps, the personal server, the CLI, and a browser UI. The recommendation is Rust, with a single core library that compiles to native for Windows and Linux, to WebAssembly for the browser, and to WASI for sandboxed plugins. **Why Rust fits this stack.** The Apache libraries involved are Rust-native: OpenDAL is written in Rust, so is DataFusion, Arrow has a first-class Rust implementation, and the Iceberg Rust library is maturing quickly. Tantivy gives embedded BM25 for the personal server, where Solr is too heavy; Solr itself is an HTTP client. SQLite with FTS5 through rusqlite, Postgres through sqlx, Kafka through the librdkafka bindings. There is an official Rust MCP SDK. One static binary per platform, no runtime to install, Windows a tier-one target. The cost is velocity: services take longer to write than in Go, and async Rust has a learning curve. @@ -59,7 +63,7 @@ So the browser is the person's window onto their own memory and the Foundation's ## 6. Two deployments from one codebase -**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. +**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. **The committer's laptop** is one process from the same crates: files or a local object store for the record, SQLite with FTS5 for index and ledger, regorus over a grants file, no broker, no identity. Same surface, same bundle format. It is the conformance check for the surface and the thing a person can run before their institution has built anything. @@ -77,6 +81,8 @@ One Rust workspace. | `log` | Kafka producer and consumer groups; a file-backed log for the laptop | native | | `policy` | OPA sidecar client; regorus for the local grants file | native, wasm | | `server` | MCP and HTTP surface, session cache, ceiling checks, T0 assembly | native | +| `sandbox` | Confinement: the plan, namespaces on Linux, Seatbelt on macOS | native | +| `egress` | The confined harness's one way out: ceilings, credentials, spend, metering | native | | `plugins` | WASI host for taps and skill bundles | native | | `ui` | The browser reader and curation page | wasm | | `memoryd`, `memory-local` | The two binaries | native, Windows and Linux | From d94ea2dc332914b4237db7ee50090f4531ceb353 Mon Sep 17 00:00:00 2001 From: Sander Striker Date: Tue, 27 Oct 2026 09:36:10 +0100 Subject: [PATCH 2/6] docs: correct which calls go through the egress The egress forwards the harness's model calls only. A tool runs in the launcher, outside the sandbox. A hosted tool's call goes out through the egress. A reader test and a check against the code found the error, and five smaller errors. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bhj4Ck4y5iwZUsjpgQx8A2 --- README.md | 30 +++++++++++++++++------------- docs/reference-stack.md | 14 +++++++------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a1e950b..81dc6ac 100644 --- a/README.md +++ b/README.md @@ -133,16 +133,19 @@ argued from taste. Several parts around one record, and no one of them is the safety boundary on its own. The **memory server** is what an agent harness reaches over MCP: it serves the record by summaries first, holds the task ledger, and checks every read and write against the -policy. The **launcher** runs a harness in a **sandbox**, with no network except the -**egress**, which forwards its model and tool calls to a provider, a gateway or a tool -server, holds each to its destination's ceiling, meters them, holds the credentials, -enforces spend limits and records each call's decision context. The memory server and the -egress enforce the same grants; the sandbox makes the egress the only way out. An agent -starts empty, loads a small index called T0, works a task it -claimed, and writes a handoff when it stops; the next agent continues from the handoff, -not a transcript. Around them, the **librarian** curates the record on a budget, the -**perimeter** decides what may be read, written and admitted, and a person promotes what -the system may rely on. +policy. The **launcher** runs a harness in a **sandbox** with no network: the one thing +the harness can reach is the **egress**, which serves it the memory server's MCP and +forwards its model calls to a provider or a gateway. The egress holds each model call to +its destination's ceiling, meters it, enforces spend limits, records its decision +context, and holds the credentials unless the harness runs on its own subscription login. +A tool the harness asks the memory server for runs in the launcher, outside the sandbox, +and every request it makes is decided against the session's label; a hosted tool's call +leaves through the egress like a model call. The **perimeter**, the policy over +`grants.toml` and the record's policy entries, is what the memory server, the egress and +the tools all enforce. An agent starts empty, loads a small index called T0, works a task +it claimed, and writes a handoff when it stops; the next agent continues from the +handoff, not a transcript. Around them, the **librarian** curates the record on a budget, +and a person promotes what the system may rely on. ``` person ──▶ amem: web launcher, CLI, profiles @@ -229,9 +232,10 @@ claude mcp add memory -- /path/to/amem --root ~/memory serve --author "agent:cla The agent gets `t0`, `search`, `get`, `write`, `list`, `recall`, `promote`, the ledger's tools and `transcript_turn`, and its writes land at raw trust under the author given. -Unconfined means only that: the record's rules hold for what the harness reads and writes -over MCP, but no sandbox or egress stands around the rest of what it does, and it keeps -its own network and credentials. +Such a harness has no sandbox and no egress. The policy still decides what it reads and +writes over MCP, but it keeps its own network and credentials, nothing meters its model +calls, and no model's ceiling caps what it may read. `amem launch --unconfined` is the +middle case: no sandbox, but the model calls still go through the egress. ## The parts, and their commands diff --git a/docs/reference-stack.md b/docs/reference-stack.md index e5a9b9f..50a9cac 100644 --- a/docs/reference-stack.md +++ b/docs/reference-stack.md @@ -20,15 +20,15 @@ Two things fall out of this for free. One materializer per domain, which the pap | Ledger and lease cache | PostgreSQL, one schema per domain | A claim is one statement with row locking and `SKIP LOCKED`: atomic, a few ms. Domains are independent, so partitions and replicas scale by domain. Rebuildable from the log | One container | | Policy engine | OPA as a sidecar, registry and grants as data | Partial evaluation compiles the session filter once; every later check is local. The filter is pushed into the Solr query, so denied entries are never scored | One sidecar per memory-server replica | | Memory server | A stateless service: MCP and HTTP surface, compiled filter and T0 cached per session, the read and write checks | Replicas behind a load balancer. T0 is a prefix the harness caches, so orientation costs zero tool calls | One container, scale by count | -| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS, with no network but the egress | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | -| Egress | Ours: a forwarding proxy, the confined harness's only network. Ceiling check per outbound call, credentials injected, spend limits, metering, decision contexts recorded | One beside each confined session, forwarding to LiteLLM for models and to tool servers for taps; one local hop on each call | Part of `amem`; its own container under WSL | +| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS (not yet run on a Mac), with no network but the egress. `--unconfined` drops the sandbox and keeps the egress, for a platform without a backend | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | +| Egress | Ours: the confined harness's only network. It serves the memory server's MCP and forwards model calls: ceiling check per call, credentials injected, spend limits, metering, decision contexts recorded. It also carries the launcher's calls to hosted tool servers | One beside each confined session, forwarding to LiteLLM for models and to tool servers for hosted taps; a loopback relay and a unix socket between it and the harness | Part of `amem`; under WSL, its own container, which also serves the record | | Recall deputy, librarians, auditors | Agents on Gofannon, cheap tier through llmao | Already how the platform runs. Librarians are a consumer group on the log like any other | The existing Gofannon and LiteLLM containers | | Taps | MCP servers, one per tracker or list, each registered with a ceiling; as WASI components hosted by the memory server (section 4) | Contributed connectors run sandboxed with exactly the network and filesystem the grant allows | Containers, or components loaded by the server | | Telemetry and analytics | OpenTelemetry to Kafka; Apache Flink or Spark streaming into Apache Iceberg tables on the same object store; queried with Apache DataFusion or Trino | Columnar, append-heavy, cross-corpus. Iceberg snapshots give time travel over telemetry and over the entry metadata table. This is where cost per task, co-retrieval mining, decay sweeps, and lineage queries live | OTel collector writing Parquet; DuckDB or DataFusion to query. Flink when volume justifies it | | Interchange | Markdown bundle with a manifest; Apache Arrow for bulk metadata transport | Arrow is the columnar wire format the analytics side already speaks | Same | | Identity | Authentik for the institution; nobody for a single person | Lives in the serving layer, so the local stack runs with it off | Optional container | -No one row is the safety boundary. The memory server checks what is read and written; the sandbox keeps the harness off the network and the host; the egress decides where each call may go and holds the credentials; the memory server and the egress enforce the same grants. A harness that reaches the memory server without the launcher keeps the first and none of the rest. +No one row is the safety boundary. The sandbox gives the harness no network and, of the host, only the system's binaries read-only, a private home and its working tree; the one thing it can reach is the egress. The egress serves the memory server, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run in the launcher, outside the sandbox, and decide every request against the session's label. All of them enforce the same grants. A harness that reaches the memory server without the launcher keeps the memory server's checks and loses the sandbox and the egress. ## 3. Latency on the hot path comes from what is never on it @@ -63,7 +63,7 @@ So the browser is the person's window onto their own memory and the Foundation's ## 6. Two deployments from one codebase -**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. +**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Today the launcher runs the memory server in its own process and the egress reads the session from it; splitting them carries the session's label and the grants across that boundary. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. **The committer's laptop** is one process from the same crates: files or a local object store for the record, SQLite with FTS5 for index and ledger, regorus over a grants file, no broker, no identity. Same surface, same bundle format. It is the conformance check for the surface and the thing a person can run before their institution has built anything. @@ -80,9 +80,9 @@ One Rust workspace. | `ledger` | Two backends: SQLite, Postgres. Claims, leases, origin dedup | native | | `log` | Kafka producer and consumer groups; a file-backed log for the laptop | native | | `policy` | OPA sidecar client; regorus for the local grants file | native, wasm | -| `server` | MCP and HTTP surface, session cache, ceiling checks, T0 assembly | native | -| `sandbox` | Confinement: the plan, namespaces on Linux, Seatbelt on macOS | native | -| `egress` | The confined harness's one way out: ceilings, credentials, spend, metering | native | +| `server` | MCP and HTTP surface, session cache, the read filter and write checks, T0 assembly | native | +| `sandbox` | Confinement: the plan, namespaces on Linux, Seatbelt on macOS; the WSL container backend lives in the binary | native | +| `egress` | The confined harness's one way out: the memory server's MCP over HTTP, and model calls with ceilings, credentials, spend, metering | native | | `plugins` | WASI host for taps and skill bundles | native | | `ui` | The browser reader and curation page | wasm | | `memoryd`, `memory-local` | The two binaries | native, Windows and Linux | From 6b1e6c97c874e505ef457a29e845c1166e29d381 Mon Sep 17 00:00:00 2001 From: Sander Striker Date: Tue, 27 Oct 2026 09:36:28 +0100 Subject: [PATCH 3/6] docs: define a ceiling where the README first uses the word Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bhj4Ck4y5iwZUsjpgQx8A2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81dc6ac..6746120 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ summaries first, holds the task ledger, and checks every read and write against policy. The **launcher** runs a harness in a **sandbox** with no network: the one thing the harness can reach is the **egress**, which serves it the memory server's MCP and forwards its model calls to a provider or a gateway. The egress holds each model call to -its destination's ceiling, meters it, enforces spend limits, records its decision +its destination's ceiling, the highest label that may be sent there, meters it, enforces spend limits, records its decision context, and holds the credentials unless the harness runs on its own subscription login. A tool the harness asks the memory server for runs in the launcher, outside the sandbox, and every request it makes is decided against the session's label; a hosted tool's call From d62466e2d17db01d911eecf4acf752cda9ec4c96 Mon Sep 17 00:00:00 2001 From: Sander Striker Date: Tue, 27 Oct 2026 09:42:34 +0100 Subject: [PATCH 4/6] docs: say which calls the egress meters, and where each tool runs A second reader test and check against the code found these errors. The egress does not meter a tunnelled call. A browser tool does not go through the egress. The sandbox mounts more than the text said. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bhj4Ck4y5iwZUsjpgQx8A2 --- README.md | 52 +++++++++++++++++++++++++---------------- docs/reference-stack.md | 12 +++++----- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 6746120..673e8e8 100644 --- a/README.md +++ b/README.md @@ -130,22 +130,30 @@ argued from taste. ## What it is -Several parts around one record, and no one of them is the safety boundary on its own. -The **memory server** is what an agent harness reaches over MCP: it serves the record by -summaries first, holds the task ledger, and checks every read and write against the -policy. The **launcher** runs a harness in a **sandbox** with no network: the one thing -the harness can reach is the **egress**, which serves it the memory server's MCP and -forwards its model calls to a provider or a gateway. The egress holds each model call to -its destination's ceiling, the highest label that may be sent there, meters it, enforces spend limits, records its decision -context, and holds the credentials unless the harness runs on its own subscription login. -A tool the harness asks the memory server for runs in the launcher, outside the sandbox, -and every request it makes is decided against the session's label; a hosted tool's call -leaves through the egress like a model call. The **perimeter**, the policy over -`grants.toml` and the record's policy entries, is what the memory server, the egress and -the tools all enforce. An agent starts empty, loads a small index called T0, works a task -it claimed, and writes a handoff when it stops; the next agent continues from the -handoff, not a transcript. Around them, the **librarian** curates the record on a budget, -and a person promotes what the system may rely on. +Several parts around one record. No one of them is the safety boundary on its own. + +- The **memory server** is what an agent harness reaches over MCP. It serves the record + by summaries first, holds the task ledger, and checks every read and write against the + policy. In a launch, it reads nothing above the model's ceiling. +- The **launcher** runs a harness in a **sandbox**, which has no network. The one thing + the harness can reach is the egress. +- The **egress** carries the memory server's MCP to the harness and forwards the + harness's model calls to a provider or a gateway. It holds each call to its + destination's **ceiling**: the highest label that may be sent there. A call it forwards + is also metered, held to its spend limit, and recorded with its decision context. A + call it tunnels, as for Copilot and for the ChatGPT, Grok Build and Antigravity logins, + is checked against the ceiling when the tunnel opens and is not metered. The egress + holds the credentials, unless the harness runs on its own subscription login. +- The **tools**, fetch, search and the other taps, run outside the sandbox: in the `amem` + process, on a tool server called through the egress, or in the owner's browser. Each + decides every request against the session's label. +- The **perimeter** is the policy over `grants.toml` and the record's policy entries. The + memory server, the egress and the tools all enforce it. + +An agent starts empty, loads a small index called T0, works a task it claimed, and writes +a handoff when it stops; the next agent continues from the handoff, not a transcript. +Around them, the **librarian** curates the record on a budget, and a person promotes what +the system may rely on. ``` person ──▶ amem: web launcher, CLI, profiles @@ -160,6 +168,9 @@ and a person promotes what the system may rely on. metered, spend-limited, contexts recorded └── librarian: curation, checker, canaries, drift ``` +The MCP arrow is drawn straight to the memory server, but it too passes through the +egress, the harness's one way out. + Everything lives under one root, `~/memory` unless `--root` or `MEMORY_ROOT` says otherwise, and the process touches nothing outside it: @@ -232,10 +243,11 @@ claude mcp add memory -- /path/to/amem --root ~/memory serve --author "agent:cla The agent gets `t0`, `search`, `get`, `write`, `list`, `recall`, `promote`, the ledger's tools and `transcript_turn`, and its writes land at raw trust under the author given. -Such a harness has no sandbox and no egress. The policy still decides what it reads and -writes over MCP, but it keeps its own network and credentials, nothing meters its model -calls, and no model's ceiling caps what it may read. `amem launch --unconfined` is the -middle case: no sandbox, but the model calls still go through the egress. +Such a harness has no sandbox, and its model calls do not go through an egress. The +policy still decides what it reads and writes over MCP. But the harness keeps its own +network and credentials, nothing meters its model calls, and no model's ceiling caps what +it may read. `amem launch --unconfined` is the middle case, for Claude Code and Codex so +far: no sandbox, but the model calls still go through the egress. ## The parts, and their commands diff --git a/docs/reference-stack.md b/docs/reference-stack.md index 50a9cac..7e51d8f 100644 --- a/docs/reference-stack.md +++ b/docs/reference-stack.md @@ -19,16 +19,16 @@ Two things fall out of this for free. One materializer per domain, which the pap | Index | Apache Solr, shard key = domain | Lucene BM25 for ranked text, the compiled filter expressed as a query filter, dense vectors as a derived field in the same engine. Index-served searches in single-digit ms. Shard by domain so a session's filter hits one shard | One Solr container. OpenSearch is the non-Apache equivalent if operations prefer it | | Ledger and lease cache | PostgreSQL, one schema per domain | A claim is one statement with row locking and `SKIP LOCKED`: atomic, a few ms. Domains are independent, so partitions and replicas scale by domain. Rebuildable from the log | One container | | Policy engine | OPA as a sidecar, registry and grants as data | Partial evaluation compiles the session filter once; every later check is local. The filter is pushed into the Solr query, so denied entries are never scored | One sidecar per memory-server replica | -| Memory server | A stateless service: MCP and HTTP surface, compiled filter and T0 cached per session, the read and write checks | Replicas behind a load balancer. T0 is a prefix the harness caches, so orientation costs zero tool calls | One container, scale by count | -| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS (not yet run on a Mac), with no network but the egress. `--unconfined` drops the sandbox and keeps the egress, for a platform without a backend | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | -| Egress | Ours: the confined harness's only network. It serves the memory server's MCP and forwards model calls: ceiling check per call, credentials injected, spend limits, metering, decision contexts recorded. It also carries the launcher's calls to hosted tool servers | One beside each confined session, forwarding to LiteLLM for models and to tool servers for hosted taps; a loopback relay and a unix socket between it and the harness | Part of `amem`; under WSL, its own container, which also serves the record | +| Memory server | A stateless service: MCP and HTTP surface, compiled filter and T0 cached per session, the read and write checks, reads capped at the model's ceiling in a launch | Replicas behind a load balancer. T0 is a prefix the harness caches, so orientation costs zero tool calls | One container, scale by count | +| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS (not yet run on a Mac), with no network but the egress. `--unconfined` drops the sandbox and keeps the egress, for a platform without a backend (Claude Code and Codex so far) | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | +| Egress | Ours: the confined harness's only network. It serves the memory server's MCP and forwards model calls: ceiling check per call, credentials injected, spend limits, metering, decision contexts recorded. A tunnelled call (Copilot, and some subscription logins) is checked when the tunnel opens and not metered. It also carries the launcher's calls to hosted tool servers | One beside each confined session, forwarding to LiteLLM for models and to tool servers for hosted taps; a loopback relay and a unix socket between it and the harness | Part of `amem`; under WSL, its own container, which also runs the memory server | | Recall deputy, librarians, auditors | Agents on Gofannon, cheap tier through llmao | Already how the platform runs. Librarians are a consumer group on the log like any other | The existing Gofannon and LiteLLM containers | | Taps | MCP servers, one per tracker or list, each registered with a ceiling; as WASI components hosted by the memory server (section 4) | Contributed connectors run sandboxed with exactly the network and filesystem the grant allows | Containers, or components loaded by the server | | Telemetry and analytics | OpenTelemetry to Kafka; Apache Flink or Spark streaming into Apache Iceberg tables on the same object store; queried with Apache DataFusion or Trino | Columnar, append-heavy, cross-corpus. Iceberg snapshots give time travel over telemetry and over the entry metadata table. This is where cost per task, co-retrieval mining, decay sweeps, and lineage queries live | OTel collector writing Parquet; DuckDB or DataFusion to query. Flink when volume justifies it | | Interchange | Markdown bundle with a manifest; Apache Arrow for bulk metadata transport | Arrow is the columnar wire format the analytics side already speaks | Same | | Identity | Authentik for the institution; nobody for a single person | Lives in the serving layer, so the local stack runs with it off | Optional container | -No one row is the safety boundary. The sandbox gives the harness no network and, of the host, only the system's binaries read-only, a private home and its working tree; the one thing it can reach is the egress. The egress serves the memory server, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run in the launcher, outside the sandbox, and decide every request against the session's label. All of them enforce the same grants. A harness that reaches the memory server without the launcher keeps the memory server's checks and loses the sandbox and the egress. +No one row is the safety boundary. The sandbox gives the harness no network. Of the host it sees only what the launch mounts: the system's binaries, the harness, the memory binary and the record's projection, all read-only, and a private home and its working tree. The one thing it can reach is the egress. The egress carries the memory server's MCP to the harness, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run outside the sandbox, in the launcher, on a tool server or in the owner's browser, and decide every request against the session's label. The memory server, the egress and the tools enforce the grants. A harness that reaches the memory server without the launcher keeps the memory server's checks, and its own calls lose the sandbox and the egress. ## 3. Latency on the hot path comes from what is never on it @@ -63,7 +63,7 @@ So the browser is the person's window onto their own memory and the Foundation's ## 6. Two deployments from one codebase -**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Today the launcher runs the memory server in its own process and the egress reads the session from it; splitting them carries the session's label and the grants across that boundary. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. +**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Today the memory server runs inside the launcher's process, and the egress reads the session from it. Splitting them will need the session's label and the grants carried across that boundary. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. **The committer's laptop** is one process from the same crates: files or a local object store for the record, SQLite with FTS5 for index and ledger, regorus over a grants file, no broker, no identity. Same surface, same bundle format. It is the conformance check for the surface and the thing a person can run before their institution has built anything. @@ -81,7 +81,7 @@ One Rust workspace. | `log` | Kafka producer and consumer groups; a file-backed log for the laptop | native | | `policy` | OPA sidecar client; regorus for the local grants file | native, wasm | | `server` | MCP and HTTP surface, session cache, the read filter and write checks, T0 assembly | native | -| `sandbox` | Confinement: the plan, namespaces on Linux, Seatbelt on macOS; the WSL container backend lives in the binary | native | +| `sandbox` | Confinement: the plan, namespaces on Linux, Seatbelt on macOS; the WSL container backend lives in `amem` (`crates/memory`) | native | | `egress` | The confined harness's one way out: the memory server's MCP over HTTP, and model calls with ceilings, credentials, spend, metering | native | | `plugins` | WASI host for taps and skill bundles | native | | `ui` | The browser reader and curation page | wasm | From f3f30c4159ea83607717a152fb0bb1e0748de5fa Mon Sep 17 00:00:00 2001 From: Sander Striker Date: Tue, 27 Oct 2026 09:51:50 +0100 Subject: [PATCH 5/6] docs: make the parts' lists complete and each sentence one reading A third reader test and check against the code found two incomplete lists and five sentences with two readings. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bhj4Ck4y5iwZUsjpgQx8A2 --- README.md | 21 +++++++++++---------- docs/reference-stack.md | 6 +++--- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 673e8e8..37d2bae 100644 --- a/README.md +++ b/README.md @@ -134,19 +134,20 @@ Several parts around one record. No one of them is the safety boundary on its ow - The **memory server** is what an agent harness reaches over MCP. It serves the record by summaries first, holds the task ledger, and checks every read and write against the - policy. In a launch, it reads nothing above the model's ceiling. + policy. In a launch, it gives the harness nothing above the **ceiling** of its model's + destination: the highest label that may be sent there. - The **launcher** runs a harness in a **sandbox**, which has no network. The one thing the harness can reach is the egress. - The **egress** carries the memory server's MCP to the harness and forwards the harness's model calls to a provider or a gateway. It holds each call to its - destination's **ceiling**: the highest label that may be sent there. A call it forwards - is also metered, held to its spend limit, and recorded with its decision context. A - call it tunnels, as for Copilot and for the ChatGPT, Grok Build and Antigravity logins, - is checked against the ceiling when the tunnel opens and is not metered. The egress - holds the credentials, unless the harness runs on its own subscription login. -- The **tools**, fetch, search and the other taps, run outside the sandbox: in the `amem` - process, on a tool server called through the egress, or in the owner's browser. Each - decides every request against the session's label. + destination's ceiling. A call it forwards is also metered, held to its spend limit, + and recorded with its decision context. A call it tunnels, as for Copilot, for Codex's + ChatGPT login and for the Grok Build and Antigravity logins, is checked against the + ceiling when the tunnel opens and is not metered. The egress holds the credentials, + unless the harness runs on its own subscription login. +- The **tools**, fetch, search and the other tool taps, run outside the sandbox: in the + `amem` process, on a tool server called through the egress, or in the owner's browser. + Each decides every request against the session's label. - The **perimeter** is the policy over `grants.toml` and the record's policy entries. The memory server, the egress and the tools all enforce it. @@ -233,7 +234,7 @@ pi, in native Linux namespaces from the harness image once `make unpack` has put place (every one but Antigravity is in it), under WSL containers, or, not yet run on a Mac, under Seatbelt on macOS; auth is `key`, `gateway`, or `subscription`, the harness's own login, which the egress passes -through and meters as plan quota. [local/README.md](local/README.md) is the full guide. +through and, where it forwards rather than tunnels, meters as plan quota. [local/README.md](local/README.md) is the full guide. Or point a harness at the memory server alone over stdio, unconfined: diff --git a/docs/reference-stack.md b/docs/reference-stack.md index 7e51d8f..e5e5a9b 100644 --- a/docs/reference-stack.md +++ b/docs/reference-stack.md @@ -20,7 +20,7 @@ Two things fall out of this for free. One materializer per domain, which the pap | Ledger and lease cache | PostgreSQL, one schema per domain | A claim is one statement with row locking and `SKIP LOCKED`: atomic, a few ms. Domains are independent, so partitions and replicas scale by domain. Rebuildable from the log | One container | | Policy engine | OPA as a sidecar, registry and grants as data | Partial evaluation compiles the session filter once; every later check is local. The filter is pushed into the Solr query, so denied entries are never scored | One sidecar per memory-server replica | | Memory server | A stateless service: MCP and HTTP surface, compiled filter and T0 cached per session, the read and write checks, reads capped at the model's ceiling in a launch | Replicas behind a load balancer. T0 is a prefix the harness caches, so orientation costs zero tool calls | One container, scale by count | -| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS (not yet run on a Mac), with no network but the egress. `--unconfined` drops the sandbox and keeps the egress, for a platform without a backend (Claude Code and Codex so far) | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | +| Launcher and sandbox | Ours: each harness confined with Linux namespaces, WSL containers, or Seatbelt on macOS (not yet run on a Mac), with no network but the egress. `--unconfined` drops the sandbox and keeps the egress, for a platform without a sandbox backend; it runs Claude Code and Codex so far | Runs where the harness runs, one per session, so it scales with the people and agents working rather than with the stack | The `amem` binary | | Egress | Ours: the confined harness's only network. It serves the memory server's MCP and forwards model calls: ceiling check per call, credentials injected, spend limits, metering, decision contexts recorded. A tunnelled call (Copilot, and some subscription logins) is checked when the tunnel opens and not metered. It also carries the launcher's calls to hosted tool servers | One beside each confined session, forwarding to LiteLLM for models and to tool servers for hosted taps; a loopback relay and a unix socket between it and the harness | Part of `amem`; under WSL, its own container, which also runs the memory server | | Recall deputy, librarians, auditors | Agents on Gofannon, cheap tier through llmao | Already how the platform runs. Librarians are a consumer group on the log like any other | The existing Gofannon and LiteLLM containers | | Taps | MCP servers, one per tracker or list, each registered with a ceiling; as WASI components hosted by the memory server (section 4) | Contributed connectors run sandboxed with exactly the network and filesystem the grant allows | Containers, or components loaded by the server | @@ -28,7 +28,7 @@ Two things fall out of this for free. One materializer per domain, which the pap | Interchange | Markdown bundle with a manifest; Apache Arrow for bulk metadata transport | Arrow is the columnar wire format the analytics side already speaks | Same | | Identity | Authentik for the institution; nobody for a single person | Lives in the serving layer, so the local stack runs with it off | Optional container | -No one row is the safety boundary. The sandbox gives the harness no network. Of the host it sees only what the launch mounts: the system's binaries, the harness, the memory binary and the record's projection, all read-only, and a private home and its working tree. The one thing it can reach is the egress. The egress carries the memory server's MCP to the harness, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run outside the sandbox, in the launcher, on a tool server or in the owner's browser, and decide every request against the session's label. The memory server, the egress and the tools enforce the grants. A harness that reaches the memory server without the launcher keeps the memory server's checks, and its own calls lose the sandbox and the egress. +No one row is the safety boundary. The sandbox gives the harness no network. Of the host it sees only what the launch mounts: the system's binaries and configuration, the harness, the memory binary and the record's projection, all read-only, and a private home and its working tree. The one thing it can reach is the egress. The egress carries the memory server's MCP to the harness, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run outside the sandbox, in the launcher, on a tool server or in the owner's browser, and decide every request against the session's label. The memory server, the egress and the tools enforce the grants. A harness that reaches the memory server without the launcher keeps the memory server's checks, and the harness's own calls lose the sandbox and the egress. ## 3. Latency on the hot path comes from what is never on it @@ -63,7 +63,7 @@ So the browser is the person's window onto their own memory and the Foundation's ## 6. Two deployments from one codebase -**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Today the memory server runs inside the launcher's process, and the egress reads the session from it. Splitting them will need the session's label and the grants carried across that boundary. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. +**The Foundation's reference deployment** is the compose file above: MinIO, Kafka, Solr, Postgres, OPA, the memory server, Gofannon, LiteLLM, the OTel collector, optionally Authentik. The launcher and the egress are not in it: they run where each harness runs, and reach the stack as clients. Today the memory server runs in the same process as the egress: the launcher's, or under WSL the egress container's. The egress reads the session from it. Splitting the memory server from the egress will need the session's label and the grants carried across that boundary. Replicating it is adding brokers, shards, schemas, and replicas along the domain key. **The committer's laptop** is one process from the same crates: files or a local object store for the record, SQLite with FTS5 for index and ledger, regorus over a grants file, no broker, no identity. Same surface, same bundle format. It is the conformance check for the surface and the thing a person can run before their institution has built anything. From 0e6760be352de536abd1c77bee1f72b5539083c2 Mon Sep 17 00:00:00 2001 From: Sander Striker Date: Tue, 27 Oct 2026 09:56:24 +0100 Subject: [PATCH 6/6] docs: name the device nodes and the egress socket in the sandbox's mounts Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bhj4Ck4y5iwZUsjpgQx8A2 --- docs/reference-stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference-stack.md b/docs/reference-stack.md index e5e5a9b..d65c366 100644 --- a/docs/reference-stack.md +++ b/docs/reference-stack.md @@ -28,7 +28,7 @@ Two things fall out of this for free. One materializer per domain, which the pap | Interchange | Markdown bundle with a manifest; Apache Arrow for bulk metadata transport | Arrow is the columnar wire format the analytics side already speaks | Same | | Identity | Authentik for the institution; nobody for a single person | Lives in the serving layer, so the local stack runs with it off | Optional container | -No one row is the safety boundary. The sandbox gives the harness no network. Of the host it sees only what the launch mounts: the system's binaries and configuration, the harness, the memory binary and the record's projection, all read-only, and a private home and its working tree. The one thing it can reach is the egress. The egress carries the memory server's MCP to the harness, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run outside the sandbox, in the launcher, on a tool server or in the owner's browser, and decide every request against the session's label. The memory server, the egress and the tools enforce the grants. A harness that reaches the memory server without the launcher keeps the memory server's checks, and the harness's own calls lose the sandbox and the egress. +No one row is the safety boundary. The sandbox gives the harness no network. Of the host it sees only what the launch mounts: the system's binaries and configuration, the harness, the memory binary and the record's projection, all read-only; a few device nodes; a private home and its working tree; and the egress's socket. The one thing it can reach is the egress. The egress carries the memory server's MCP to the harness, decides where each model call may go, and holds the credentials unless the harness runs on its own subscription login. The memory server checks what is read and written. The tools run outside the sandbox, in the launcher, on a tool server or in the owner's browser, and decide every request against the session's label. The memory server, the egress and the tools enforce the grants. A harness that reaches the memory server without the launcher keeps the memory server's checks, and the harness's own calls lose the sandbox and the egress. ## 3. Latency on the hot path comes from what is never on it