Memories / Documentation

On this page

Remote daemon (HTTP/WebSocket and relay RPC)

Memories can host the same framed service::Request protocol over:

  1. Local IPC (default; OS-user authenticated; length-prefixed frames)
  2. HTTP + WebSocket (optional listen; token auth; same binary JSON frames as relay RPC)
  3. WSS relay RPC (/v1/rpc/{room}/{slot})

Direct and relay remote attach share one wire scheme: WebSocket binary frames carrying AuthFrame then version-1 Wire (service::Request). Vault sync still uses /v1/channel. The WebUI stays status-only.

Security rules

  • Listen token: --listen-token / MEMORIES_LISTEN_TOKEN, or generated and printed once when HTTP/relay host is enabled. Never put the token in TOML.
  • Token is wiped when the store locks.
  • Remote callers use stable elevation namespaces ws:remote / relay:remote (separate from cli:{pid}). Unlock and sudo over a remote transport elevate that namespace so subsequent one-shot connections keep elevation for the idle window (same rules as local: 5 minutes idle / 15 minutes max).
  • Desktop HTTP/relay hosts expose the full handle_client surface after unlock/sudo (vault, card, memory, file, credential, sync, MCP grant, backup, proxy bind on the host, remote sessions, Run, CryptoJob, etc.), subject to the same elevation checks as local IPC.
  • HTTP listen defaults to loopback; non-loopback needs --allow-non-loopback.
  • Plain HTTP/WS needs no certificates (default for direct LAN).
  • Optional TLS: --tls-cert / --tls-key (PEM) enable HTTPS/WSS on the same listen. An https:// listen URL requires those flags.
  • Off-trust relay must be wss:// with a 43-character room id and token.
  • Anyone who holds the listen token is as trusted as a local elevated client for that transport namespace. Treat the token like a password; prefer loopback or an authenticated network path.

Residual risks

  • Token theft ⇒ full remote control of an unlocked/elevated desktop daemon.
  • Non-loopback HTTP exposes the framed API to the network; use --tls-cert / --tls-key, a reverse proxy, VPN, or firewall when binding beyond loopback.
  • Relay RPC (/v1/rpc/{room}/{slot}) is authenticated, not end-to-end encrypted: its application-level JSON frames carry the shared listen token followed by requests and responses. The relay operator or TLS terminator can read the token and RPC payloads. WSS protects each connection to the relay, not against the relay itself; use a trusted relay for remote daemon access.
  • Vault peer sync (/v1/channel) is separate and carries end-to-end encrypted payloads. For that channel, relay operators see ciphertext lengths and timing, not asset plaintext.
  • Host-side proxy listeners still bind loopback on the daemon machine; remote clients can start them when elevated, then reach them only if they can also reach that host loopback (typically via a separate tunnel).

HTTP/WebSocket host and client

# Host (prints a token once if --listen-token is omitted)
memories daemon --listen http://127.0.0.1:7900
# or HTTPS URL when TLS is configured:
memories daemon --listen https://127.0.0.1:8443 \
  --tls-cert certs/daemon-tls.crt --tls-key certs/daemon-tls.key --listen-token "$TOKEN"

# Client (another shell / machine that can reach the listener)
export MEMORIES_DAEMON=http://127.0.0.1:7900
export MEMORIES_DAEMON_TOKEN="$TOKEN"
memories status
memories unlock
memories sudo
memories vault list

Direct RPC path is /v1/rpc (WebSocket upgrade). Flags: --daemon, --daemon-token (same meaning as the env vars).

Client TLS trust

Certificate and hostname verification are enabled by default. The global --ca-file PATH option adds all certificates from a PEM CA bundle to the normal trust store. For a self-signed daemon, trust its certificate explicitly (its DNS/IP subject alternative names must still match the URL):

memories --daemon https://localhost:8443 --daemon-token "$TOKEN" \
  --ca-file certs/daemon-tls.crt status

# Equivalent CA setting, also usable with relay and HTTPS clients:
export MEMORIES_CA_FILE=certs/daemon-tls.crt
memories --daemon https://localhost:8443 --daemon-token "$TOKEN" status

# DANGEROUS: development only; prefer --ca-file instead.
memories --daemon https://localhost:8443 --daemon-token "$TOKEN" --insecure status

--insecure disables certificate-chain and hostname verification. TLS encryption remains enabled, but an attacker can impersonate the server and read daemon tokens, credentials, or request/response data. Never use it in production or on an untrusted network. It is CLI-only (no MEMORIES_INSECURE environment variable). An explicitly supplied CA file must be readable and contain valid PEM certificates even with --insecure; invalid files fail before connecting. Files are loaded once at startup; restart the process after changing the bundle.

Both options apply to outbound connections made by the current process: direct daemon HTTPS/WSS attach, relay RPC host/client, vault-sync relay clients, cloud OAuth and Drive HTTP, RAG API/model-download HTTP, credential-authenticated HTTPS, automation webhooks, and database TLS sessions/proxies. For operations executed by a daemon, pass the options when starting that daemon; client flags are not sent over RPC and cannot change the daemon's upstream TLS policy:

memories --ca-file /path/internal-ca.pem daemon
# Development only: affects every outbound TLS connection in this daemon.
memories --insecure daemon

Server --tls-cert / --tls-key and shared-token authentication are unchanged. Database --tls-ca CA_ID still resolves a PEM credential in the vault; its trust is combined with the daemon's --ca-file bundle. Neither option disables TLS or SSH host-key pinning. These flags are not TOML settings, are not persisted as vault secrets, and do not configure mobile/FFI callers or TLS clients in external programs/browsers or dependency-managed downloads outside these HTTP builders.

Relay RPC host and client

# Generate a room id (43 URL-safe characters)
memories sync room

# Host joins slot a
memories daemon \
  --relay-host wss://relay.example.com \
  --room "$ROOM" \
  --listen-token "$TOKEN"

# Client joins slot b
export MEMORIES_DAEMON_RELAY=wss://relay.example.com
export MEMORIES_DAEMON_ROOM="$ROOM"
export MEMORIES_DAEMON_TOKEN="$TOKEN"
memories status

Local development may use ws://127.0.0.1:8787 with memories-relay.

For a relay with a private CA, both host and client can use the global flags:

memories --ca-file /path/relay-ca.pem daemon \
  --relay-host wss://relay.example.com --room "$ROOM" --listen-token "$TOKEN"
memories --ca-file /path/relay-ca.pem --daemon-relay wss://relay.example.com \
  --daemon-room "$ROOM" --daemon-token "$TOKEN" status

Mobile Share (foreground host)

The Flutter app can start a Share session that joins relay RPC as host (slot a) while unlocked and in the foreground. Backgrounding or lock tears the session down. Mobile keeps the FFI denylist (no Run/CryptoJob/MCP grant/ remote sessions/proxies, and Share also refuses credential plaintext copy and backup dumps). Phones do not open an HTTP listener. See mobile/README.md.

Local-only / platform exceptions

Surface Desktop remote (HTTP/WS / relay) Mobile FFI / Share
Vault / memory / card / file / sync / status / unlock / sudo Full (elevation where required) Full where FFI allows
MCP grant/revoke, Backup, CredentialCopy Full when elevated Denied on Share; FFI allows copy/backup locally
Run, CryptoJob, Remote, Proxy Full when elevated (runs on host) Denied (no desktop runtime)
HTTP/WS listen Optional (--listen) Not available

Persona automation webhook

When HTTP listen is enabled, inbound automation triggers are accepted at:

POST /v1/persona/{vault}/{persona}/automation/trigger
Authorization: Bearer <persona world.automation.webhook_token>
Content-Type: application/json

{"hook":"HOOK_ID"}

The daemon must be unlocked (same as other write RPCs). The listen token enables the HTTP surface; the automation webhook_token (set via persona automation put --webhook-token) authorizes the hook. A relay host receives the same call at POST /v1/persona/{room}/{vault}/{persona}/automation/trigger on the relay (same body and webhook bearer); the room segment is the only path difference, and the relay forwards one existing persona_automation_trigger wire frame to the connected host instead of serving it locally or posting outbound. Equivalent CLI:

memories persona automation trigger VAULT PERSONA --id HOOK_ID \
  --source webhook --token "$WEBHOOK_TOKEN"