A direct Bearer-authenticated POST /peer/sync from one pod's
peer_sync.py daemon to another pod's wonderland. Each side has
the other side's key in peer-keys/peers/<peer-id>.key. Each
relationship is independent. Adding a peer is a one-shot paste of a
handshake URL; revoking a peer is one file delete.
Federation. Two pods, or two thousand. Same model.
Mazemaker federation is pod-to-pod memory propagation over HTTP(S). Every pod
stays sovereign — its own DB, its own engine, its own license, its own
vault key. When two pods are paired, each pulls a delta from the other every
five minutes and re-stores it locally with a peer:<peer-id>:
prefix. There is no central server holding memories. The hosted
backend brokers identity and billing — never content. This page is how it
works, three worked examples, and where it scales.
What it is. What it isn't.
The shape of federation is determined by the shape of mazemaker. Local-first stays local-first; nothing about pairing moves data into a hosted tier.
Not a global shared namespace. Not a hosted "Mazemaker Cloud" that ingests memories. Not silent — every peer is added by an explicit operator action and visible in the Architect's M06 PEERS panel. Not a leak of private memories: vault-encrypted content is skipped on the serving side, the peer would only see ciphertext anyway.
Only memories whose label starts with a public-prefix
(public:*, auto:*, decision:*,
bug:*, ops:*, …) and whose content is NOT
vault-encrypted. The label-prefix gate is enforced server-side — a
wide scope = "*" can't get private memories out.
Every byte of the relationship: which peers exist, which scope each
one pulls, the URL each one is reachable at, whether sync is on or off.
All of it is one TOML file (~/.mazemaker/peers.toml) and a
directory of 32-byte keys. The Architect M06 PEERS panel is just a
UI over those files.
Identity. Handshake. Done.
Each pod has a stable identity generated once at first boot. Pairing is a one-shot paste of a handshake URL. There is no DNS, no IP, no shared secret beyond the keys the handshake URLs carry.
The handshake_url is the only token an operator copies. It
looks like mzm-handshake://<pod-id>?key=<43-char-key>.
Whoever holds it can authenticate to that pod's /peer/sync
using the embedded key. That is the entirety of the trust hand-off.
Three topologies. Same model.
The wire protocol doesn't change with scale. Whether you're pairing two laptops on Tailscale, fanning a hub-and-spoke around your own machines, or standing up a public-https mesh across five operators, it's the same paste-handshake and the same five-minute tick.
Alice on alice-tpad.tail-foo.ts.net, Bob on
bob-mbp.tail-foo.ts.net. Each runs the pod with wonderland
exposed on :8765 over Tailscale. Paste-handshake both ways
with the Tailscale URL as direct_url. Within five minutes
auto:* memories flow both directions. M06 PEERS on both
pods shows • ready.
Laptop + workstation + always-on home server. The hub is the only
box with a stable address; the leaves come and go. Leaves' peers.toml
has one entry (the hub), with url = "https://hub.tail-foo.ts.net".
The hub holds the master memory; leaves pull from it. For writes-that-converge,
the operator's MCP client talks to the hub's wonderland directly; federation
propagates back to the leaves on their next pull.
Five operators, none on the same VPN. Each publishes their wonderland
via a Cloudflare Tunnel hostname like
https://wonder-alice.team.example. Each pair handshakes once.
With N peers each pod has N−1 entries in peers.toml.
Scope decision:* shares architectural decisions; public:*
shares the explicit opt-in slice. The same model holds for arbitrarily many
pods — the only limit is the operator's willingness to maintain
key pairs.
Every relationship is a separate row in peers.toml + a
separate file in peer-keys/peers/. Every relationship can be
paused (sync = false), narrowed (scope = "public:*"),
or revoked (one rm). The hosted backend is not in the path
for any of it.
Trust. Per pair. No central authority.
The trust boundary of federation is the Bearer key pair between two pods. There is no PKI, no CA, no central trust authority — just two operators who agreed to exchange handshake URLs.
- Per-pair keys. Compromising one peer's key only affects relationships that key was registered in. There is no master key.
- https is the operator's responsibility. Tailscale, ZeroTier, Cloudflare Tunnels, LAN https with a private CA — whatever the operator trusts the network layer with.
- Wide scope can't get private memories out. The server-side
public-prefix gate is enforced even if the puller requests
scope = "*". - Encrypted-at-rest stays encrypted. Memories whose content
starts with
AES:are skipped on the serving side. The peer's vault key is different from yours — they'd only see ciphertext. - Revocation is one operation.
rm ~/.mazemaker/peer-keys/peers/<peer-id>.key+ remove thepeers.tomlentry. The peer's Bearer no longer authenticates.
WWW-scale. Pods, not platforms.
"Infinite federation across the WWW" is not a marketing phrase. It is a
property of the protocol: a pod's peers.toml can hold any
number of peer entries. Each entry is an independent Bearer-key pair.
There is no global registry. There is no upper limit other than the
operator's willingness to manage the key files.
With N = 5 each pod has 4 peer entries — trivial. With
N = 50, still manageable. With N = 500 the operator
uses the rendezvous service shipped 2026-05-26 at
api.mazemaker.dev/api/peers/*. Every pod heartbeats its
current reachable URL every 60s; other pods resolve a stable
peer_id → URL by presenting the pre-shared
peer_key. The backend never sees memory content (TLS
terminates on the destination wonderland). Verified cross-machine
between two pods on 2026-05-26 — see the verified
contract section below.
Verified contract — two pods, 2026-05-26.
Cross-machine federation tested between
alca-7G (6356db37) on LAN 192.168.0.2 and
tpad (f678a5fc) on LAN 192.168.0.242. Every step
below was exercised end-to-end with real memory propagation.
No placeholders, no “shipped soon” —
actual receipts in the order they happened.
Each pod auto-detects its LAN IP, heartbeats every 60s to
POST /api/peers/register on api.mazemaker.dev with the
license JWT and a SHA-256 of its peer_key. Backend stores
(peer_id, url, peer_key_hash, expires_ts) only —
no content. TTL 5min, refreshed on every heartbeat.
When pod A wants to talk to pod B, it sees
mzm-rendezvous://<B's peer_id> in peers.toml and
calls GET /api/peers/lookup/<B's peer_id>?key=<B's peer_key>.
Backend hashes the key, compares against stored hash, returns the URL.
Resolved URL cached client-side for 240s.
Stored public:bidir-1779757812 on tpad → alca-7G's
puller fetched 1 memory, forwarded into local engine, recall surfaces
it. Round-trip including resolution + sync: < 200ms LAN.
Reverse direction tested too.
tpad killed mid-sync → alca-7G logs err=request_failed,
keeps trying. Backend still returns last URL for 5min grace (DHT-like).
tpad restarted → auto-heartbeat resumes within 10s; sync resumes
without operator intervention.
Pair two pods in 30 seconds.
The full UI lives in the Architect's M06 PEERS panel. The full CLI lives in curl. Both do the same thing.
curl -s http://127.0.0.1:8765/peer/identity | jq -r .handshake_url
mzm-handshake://6356db37-…?key=mENKrJzy…
Send this URL to the peer. Any channel — chat, paper, QR.
curl -s -X POST http://127.0.0.1:8765/peer/add \
-H 'Content-Type: application/json' \
-d '{
"handshake_url": "mzm-handshake://<peer-pod-id>?key=<key>",
"direct_url": "https://peer.tail-foo.ts.net",
"sync": true,
"scope": "public:*"
}'
Inbound is live now. Outbound fires on the next 5-min tick — or
run systemctl --user start mazemaker-peer-sync.service to
trigger immediately.
Open the Architect at architect.mazemaker.dev and
click M06 PEERS. You should see the peer with status
• ready. The peek() badge on the M06
tile reports the count ("N ready · M handshake-pending").
If it's ◖ handshake-pending, the URL is still the
mzm-rendezvous:// placeholder — click SET URL to fix.
Two pods.
Or two thousand. Your call.
Sovereign federation: no central authority, no shared corpus, no silent indexing. Per-pair Bearer keys, public-prefix gate, five-minute tick.