Skip to content
Poppy / Cloud sync

Memories that follow you across machines.

Optional, set up with one command, and auditable end to end. The cloud only ever sees what you've already chosen to remember.

The 30-second setup

poppy setup trags

The CLI opens a browser to trags.ai/setup, prompts you to authorize the current device, and on success stores a usr_* API key in your operating system’s keychain. On a host with no usable keychain backend, headless Linux or CI, the key falls back to ~/.poppy/config.json, which stays 0600; setting POPPY_TRAGS_API_KEY overrides both. From then on:

  • Every local write (a remember from the CLI or an MCP tool call) marks sync as pending; a detached background worker then runs a pull-then-push.
  • Other devices pick the changes up on their next sync; run poppy sync pull by hand when you want them sooner.

Auto-sync semantics

Sync is a watermark-based push/pull, not a real-time stream. The CLI keeps last_pushed_at and last_pulled_at watermarks in ~/.poppy/sync_state.json, keyed by the remote URL, and only sends/receives memories whose updated_at is past the watermark.

  • Push freezes on first failure. If the cloud rejects (network down, token invalid, 5xx), Poppy stops advancing the watermark so nothing is silently skipped. Next successful push covers everything since the last good run.
  • Pull is incremental. Same watermark mechanism in reverse.
  • Conflicts are last-write-wins on the row. Memories are mostly small atomic statements; three-way merge isn’t worth the complexity. Stale rows are skipped on pull.

Watermark recovery

If something goes wrong with the watermark (e.g. you restored a backup of your memories DB):

poppy sync status              # see current watermarks
rm ~/.poppy/sync_state.json    # reset the watermarks
poppy sync run                 # full re-sync, everything not on the cloud is pushed,
                               # everything on the cloud after your local cutoff is pulled

There is no reset subcommand yet; deleting ~/.poppy/sync_state.json is the supported reset. The cloud side dedups by memory_id, so a re-push is idempotent.

Multi-device caveats

  • Two-device latency is one sync apart. Device A pushes automatically on write; Device B sees it after its own next sync. If you need it now, run poppy sync pull by hand on B.
  • Concurrent offline edits collide. Both devices push when they reconnect; the cloud accepts both, last write wins. The older write is skipped on the next pull and is not preserved.
  • Deletes propagate as tombstones. poppy forget, supersedes (poppy remember --supersedes), and deletes made in the Trags web UI all sync as tombstones, so a delete on one device removes the memory everywhere. Cloud copies are kept as deletion records until you delete your account, so the delete reaches every device. Poppy keeps a local copy restorable for 7 days.

What the cloud sees

Stored in Supabase under your account: memory id, content, memory_type, project, source metadata (source_type, source_session_id, source_timestamp), related_to back-references for the supersede chain, and timestamps.

Not stored in the cloud:

  • Poppy’s local embeddings, re-computed on each device. The cloud computes its own search embeddings from the synced content, as the privacy policy describes.
  • FTS5 indexes, local-only
  • Per-device state under ~/.poppy/ (capture journal, sync watermarks, worker logs)

Row-level security in Supabase scopes every read and write to auth.uid(), your user id, and your API token is bound to that id.

Disconnecting

poppy config set auto-sync off      # 1. stop new writes from queueing a sync
pgrep -f 'poppy sync _auto-worker'  # 2. wait until this prints nothing
poppy config set trags-api-key ""   # 3. drop the stored key

There is no uninstall flag for setup trags yet, so disconnecting is those three steps, in that order. The empty string asks Poppy to drop the keychain entry and clears any plaintext fallback in ~/.poppy/config.json.

Do not skip step 2. Auto-sync is a detached poppy sync _auto-worker process spawned on each local write, separate from the daemon, so poppy daemon stop does not touch it. There is no supported command to stop it; one is tracked separately. Turning auto-sync off only stops new writes from queueing work. A round that is already running keeps the API key it resolved when the round began, so clearing the key underneath it does not stop that round, and the memory it had queued still gets pushed.

Do not use the (pending) marker in poppy sync status as proof the worker is done. The worker deletes the pending flag before its debounce and before the network round, so the marker disappears while a sync is still in flight. The reliable check is that no worker process is running. If pgrep still prints a process id, wait for it to exit before clearing the key.

Run the whole sequence while nothing is writing memories, with no agent session open and capture quiet. A write that starts partway through can pass the auto-sync check, spawn a fresh worker after step 2 already reported nothing, and resolve the key before step 3 clears it. Until there is a stop command, this sequence is best effort rather than a guarantee.

The keychain deletion is best effort. If the keychain is locked or its backend errors, Poppy reports success anyway, and the old key resolves again the moment the keychain is available. So unlock the keychain first, then confirm with poppy sync status, which should print Trags not configured. If it prints watermarks instead, a key is still resolving. Be careful reading that line when the keychain itself is unhealthy: if the delete failed silently and the read that follows fails the same way, status prints Trags not configured while the key is still there, and it resolves again once the backend recovers. To be certain, look for the entry itself in your platform’s credential manager, under service poppy-memory and account trags-api-key: followed by the full path to your Poppy directory, or simply re-run the clear command once the keychain is available. If you exported POPPY_TRAGS_API_KEY, unset it too; it wins over both the keychain and the file. Local memories are untouched. Cloud memories are not deleted; visit the dashboard for that.