Skip to content

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.

  • A host that can run the relay continuously (your own machine, an always-on server, or a VPS).
  • The dedicated od3sa-relay binary installed on the relay host.
  • od3sa-memory installed on every client device.
  • A fleet account-id shared 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).

Install and start the dedicated od3sa-relay binary:

Terminal window
curl -fsSL https://0d3sa.com/memory/install.sh | sh -s od3sa-relay
sudo mkdir -p /var/lib/relay
sudo chown $(whoami):$(whoami) /var/lib/relay
od3sa-relay \
--db /var/lib/relay/relay.db \
--addr 127.0.0.1:8787

You 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).

From a client device, check the relay health endpoint:

Terminal window
curl http://relay.example.com:8787/health

A healthy relay returns a JSON status report. If you are testing locally, use http://localhost:8787/health. Do not proceed until this check succeeds.

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:

Terminal window
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 \
--confirm

If 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.

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:

Terminal window
install -m 0600 /dev/null ~/.memory/pairing-password.txt
echo "correct-horse-battery-staple" > ~/.memory/pairing-password.txt
chmod 0600 ~/.memory/pairing-password.txt

On the source device, create an invitation:

Terminal window
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 \
--confirm

Share 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):

Terminal window
install -m 0600 /dev/null ~/.memory/invitation-code.txt
echo "INVITATION_CODE" > ~/.memory/invitation-code.txt
install -m 0600 /dev/null ~/.memory/pairing-password.txt
echo "correct-horse-battery-staple" > ~/.memory/pairing-password.txt

Then accept the invitation and start the loop:

Terminal window
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 \
--confirm

Accepting 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:

Terminal window
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 \
--confirm

This runs in the foreground. For an always-on background loop, run it under a service manager such as systemd. Check status at any time:

Terminal window
od3sa-memory --db ~/.memory/device.db sync loop status

You can override the default 30-second interval with --sync-interval or MEMORY_SYNC_INTERVAL.

  1. Store a memory on the source device through your MCP client or the CLI.
  2. 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
  3. Recall the same memory on the joining device.

If both devices run continuous loops, the memory should appear within one loop interval.

  • curl http://relay.example.com:8787/health returns a JSON OK response before any client step.
  • sync loop once succeeds from every client device without a registration error.
  • pair create-invitation returns an invitation_code.
  • pair accept-invitation finishes without errors and, with --start-sync-loop, begins syncing.
  • od3sa-memory --db ~/.memory/device.db health on either device shows peer_count greater than zero.
  • A memory stored on one device is recallable on the other.

A connection refused error during registration, pairing, or sync is almost always a topology misconfiguration, not an authentication failure. Diagnose it in this order:

  1. Relay is not running — confirm the relay process is up and logged no startup errors. The relay must start before any client command.
  2. Wrong relay host or port — check that MEMORY_RELAY_URL or --relay-url points to the interface and port the relay is actually listening on. The relay binds to loopback 127.0.0.1:8787 by default; a non-loopback address needs --allow-remote-bind, and non-loopback without TLS also needs --insecure-bind.
  3. Firewall or network path — confirm the client can reach the relay host and port. telnet relay.example.com 8787 or nc -vz relay.example.com 8787 is a faster check than the memory command.
  4. 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, use https:// and the correct port.