DEV Community

sequicompany
sequicompany

Posted on

Sharing agent memory across machines without a server (or merge conflicts)

We run coding agents on more than one machine — a workstation, a laptop, a small server — and they kept forgetting things when we switched hosts. Chasing that sent us into the MCP memory server's storage model and out the other side with a small CRDT store, now published as server-noonien.

The problem

The official MCP memory server (@modelcontextprotocol/server-memory) keeps the whole knowledge graph in a single JSONL file that every call reads and rewrites in full. On one machine that is fine. The moment two machines want to share it, it is exactly the wrong shape:

  • it lives on one machine, so switching host loses the context;
  • putting it on a folder you sync (Syncthing, Dropbox, a git working tree) causes lost writes, because whole-file read-modify-write from two writers is not safe;
  • the alternatives that avoid this are hosted services — your memory goes to someone else's cloud, behind an account.

The idea: same tools, different store

server-noonien is a drop-in for the official server: the same nine tools, the same inputs and outputs, so you swap the memory server entry and change nothing else in your agent.

What changes is the store. Every mutation is recorded as an operation in an append-only log, split into per-node shards — each machine appends only to its own <node>.jsonl. A read folds all the shards on disk into the graph.

The fold is an LWW-Element-Set: an element (an entity, an observation occurrence, a relation) is present exactly when its operation with the greatest (HLC, node, sequence) is an add. The stamp is a hybrid logical clock, so the order is causal and stable across machines. Deletes are tombstones; deleting an observation is content-level; deleting an entity tombstones its observations and the relations incident to it.

Why it cannot conflict

The fold depends only on the set of operations, never on their order or grouping:

  • idempotent — applying the same operation twice changes nothing;
  • commutative — the order in which shards arrive does not matter;
  • associative — folding in any grouping gives the same graph;
  • convergent — two nodes that have seen the same operations fold the identical graph.

So there is no merge step to get wrong. Those four laws are property-tested (fast-check), not merely asserted.

Two ways for the machines to converge

1. A shared area — the file and s3 backends. The nodes exchange shards through something they both see: a folder you already replicate (Syncthing, Dropbox, a git working tree, a shared mount) or an S3-compatible bucket. The server merges the shards; it does not move them. The bucket has to honour conditional writes (If-Match/If-None-Match) — AWS S3, Cloudflare R2 and MinIO do.

2. Peer to peer — the companion nooniend daemon removes the shared area entirely. It replicates each shard directly between nodes over a private mesh (Tailscale, WireGuard, a LAN), so the nodes need only IP reachability. It never authors operations: it pushes the shard it authors and serves the replicas it holds, so the per-node single-writer rule still holds. Membership is gossiped, liveness comes from the exchange results, and it can restore a node's own lost shard from a peer's replica.

Compaction

An append-only log grows. noonien compact rewrites a node's shard keeping, per element, the latest operation of each node — that set is always shadowed by a later operation of the same node, so the merge result does not change. The rewrite is atomic (a rename, or a conditional write on S3), and a property test checks that compaction preserves the merged graph.

What it is not

Honest limits matter more than the pitch:

  • Eventually consistent, by design. A node sees another's writes after an exchange, not instantly.
  • A node id is a single-writer shard. Exactly one server process appends to <node>.jsonl at a time.
  • file/s3 need a shared area. Convergence without one is what nooniend is for.
  • It is not a hosted service. No account, no cloud, nothing to sign up for.

Try it

npx -y server-noonien
Enter fullscreen mode Exit fullscreen mode

TypeScript, MPL-2.0, Node ≥ 22, on npm as server-noonien. noonien import memory.jsonl migrates an existing official-memory file, and the graph is exposed as the memory://knowledge-graph resource with subscribe/notifications.

server-noonien Official server-memory Hosted memory services
Multi-machine Yes — per-node shards, one graph No — one local file Yes, via the service
Sharing a synced folder Safe — append-only, conflict-free Unsafe — whole-file read-modify-write N/A
Needs a server / database No No Yes
Needs a cloud account No No Yes
Works offline Yes (file backend) Yes No
Storage Folder or S3-compatible bucket, your choice One JSONL file Their cloud
Drop-in for the official tools Yes — same nine tools — No
Migration noonien import memory.jsonl — Export/import dance
Model Explicit knowledge graph Explicit knowledge graph Often vector/semantic
License MPL-2.0 MIT Proprietary

The repository is at https://github.com/sequico/server-noonien — feedback welcome, especially on the operation-log and compaction design, and on the peer-to-peer sync.

Top comments (0)