Relay-first sync topology
Relay-first sync topology
Section titled “Relay-first sync topology”A relay-first topology makes the relay the fixed point of your sync network. You start the relay, confirm it is reachable, register every device explicitly, pair devices over a relay-mediated PAKE invitation, start the sync loop, and verify end-to-end propagation. This order eliminates the most common source of confusion: trying to pair or sync against a relay that is not running yet.
Prerequisites
Section titled “Prerequisites”- A host that can run the relay continuously (your own machine, an always-on server, or a VPS).
- The dedicated
od3sa-relaybinary installed on the relay host. od3sa-memoryinstalled on every client device.- A fleet
account-idshared by all devices. - A strong root-key passphrase to encrypt the sidecar files.
- A firewall rule allowing inbound TCP traffic on the relay port (default
8787).
1. Start the relay
Section titled “1. Start the relay”Install and start the dedicated od3sa-relay binary:
curl -fsSL https://0d3sa.com/memory/install.sh | sh -s od3sa-relay
sudo mkdir -p /var/lib/relaysudo chown $(whoami):$(whoami) /var/lib/relay
od3sa-relay \ --db /var/lib/relay/relay.db \ --addr 127.0.0.1:8787You can also set MEMORY_RELAY_DB and MEMORY_RELAY_ADDR instead of passing flags. The relay binds to loopback by default; to bind a non-loopback interface such as --addr 192.168.1.10:8787, add --allow-remote-bind (or set MEMORY_RELAY_ALLOW_REMOTE_BIND=1). For non-loopback without TLS (trusted LAN / private mesh only), also add --insecure-bind. For TLS, supply --tls-cert and --tls-key (or MEMORY_TLS_CERT and MEMORY_TLS_KEY).
2. Verify relay reachability
Section titled “2. Verify relay reachability”From a client device, check the relay health endpoint:
curl http://relay.example.com:8787/healthA healthy relay returns a JSON status report. If you are testing locally, use http://localhost:8787/health. Do not proceed until this check succeeds.
3. Register the first device
Section titled “3. Register the first device”On the device that already has data (or that you want to treat as the source), register it with the relay. The simplest way is a single sync round, which registers the device automatically before it pushes:
od3sa-memory --db ~/.memory/device.db \ sync loop once \ --relay-url http://relay.example.com:8787 \ --account-id your-account \ --root-key-passphrase-file ~/.memory/root-key-passphrase.txt \ --confirmIf you set MEMORY_RELAY_URL, MEMORY_ACCOUNT_ID, and MEMORY_ROOT_KEY_PASSPHRASE in the environment, you can omit those flags. Use files with 0600 permissions for all secrets.
4. Register additional devices
Section titled “4. Register additional devices”Repeat the sync loop once command on every other device that will sync. Each device must use the same --account-id and relay URL. Pairing in the next step also registers devices automatically, but explicit registration confirms that each device can talk to the relay before pairing begins.
5. Pair devices with a relay-mediated invitation
Section titled “5. Pair devices with a relay-mediated invitation”First, create files for the pairing password with restricted permissions:
install -m 0600 /dev/null ~/.memory/pairing-password.txtecho "correct-horse-battery-staple" > ~/.memory/pairing-password.txtchmod 0600 ~/.memory/pairing-password.txtOn the source device, create an invitation:
od3sa-memory --db ~/.memory/device.db \ pair create-invitation \ --relay-url https://relay.example.com \ --account-id your-account \ --password-file ~/.memory/pairing-password.txt \ --device-name "Studio Desktop" \ --root-key-passphrase-file ~/.memory/root-key-passphrase.txt \ --confirmShare on separate channels
Share the invitation code and pairing password through different trusted channels. Never send both on the same channel.
On the joining device, save the code and password to files (received through separate channels):
install -m 0600 /dev/null ~/.memory/invitation-code.txtecho "INVITATION_CODE" > ~/.memory/invitation-code.txt
install -m 0600 /dev/null ~/.memory/pairing-password.txtecho "correct-horse-battery-staple" > ~/.memory/pairing-password.txtThen accept the invitation and start the loop:
od3sa-memory --db ~/.memory/device.db \ pair accept-invitation \ --code-file ~/.memory/invitation-code.txt \ --password-file ~/.memory/pairing-password.txt \ --root-key-passphrase-file ~/.memory/root-key-passphrase.txt \ --start-sync-loop \ --confirmAccepting receives the account root key, records the initiator as a peer, and registers the joining device with the relay.
6. Start the sync loop on the source device
Section titled “6. Start the sync loop on the source device”If you did not already start a loop, start it on the source device:
od3sa-memory --db ~/.memory/device.db \ sync loop start \ --relay-url https://relay.example.com \ --account-id your-account \ --root-key-passphrase-file ~/.memory/root-key-passphrase.txt \ --confirmThis runs in the foreground. For an always-on background loop, run it under a service manager such as systemd. Check status at any time:
od3sa-memory --db ~/.memory/device.db sync loop statusYou can override the default 30-second interval with --sync-interval or MEMORY_SYNC_INTERVAL.
7. Verify end-to-end sync
Section titled “7. Verify end-to-end sync”- Store a memory on the source device through your MCP client or the CLI.
- On the joining device, force a single sync round:
Terminal window od3sa-memory --db ~/.memory/device.db \sync loop once \--relay-url https://relay.example.com \--account-id your-account \--root-key-passphrase-file ~/.memory/root-key-passphrase.txt - Recall the same memory on the joining device.
If both devices run continuous loops, the memory should appear within one loop interval.
Expected outcomes
Section titled “Expected outcomes”curl http://relay.example.com:8787/healthreturns a JSON OK response before any client step.sync loop oncesucceeds from every client device without a registration error.pair create-invitationreturns aninvitation_code.pair accept-invitationfinishes without errors and, with--start-sync-loop, begins syncing.od3sa-memory --db ~/.memory/device.db healthon either device showspeer_countgreater than zero.- A memory stored on one device is recallable on the other.
When you see “connection refused”
Section titled “When you see “connection refused””A connection refused error during registration, pairing, or sync is almost always a topology misconfiguration, not an authentication failure. Diagnose it in this order:
- Relay is not running — confirm the relay process is up and logged no startup errors. The relay must start before any client command.
- Wrong relay host or port — check that
MEMORY_RELAY_URLor--relay-urlpoints to the interface and port the relay is actually listening on. The relay binds to loopback127.0.0.1:8787by default; a non-loopback address needs--allow-remote-bind, and non-loopback without TLS also needs--insecure-bind. - Firewall or network path — confirm the client can reach the relay host and port.
telnet relay.example.com 8787ornc -vz relay.example.com 8787is a faster check than the memory command. - Reverse proxy or TLS mismatch — if the relay is behind a reverse proxy, use the external URL and scheme (
https://when TLS terminates at the proxy). If the relay serves TLS directly, usehttps://and the correct port.