carlos / platform Public

Hibernation replicates *.db only, so a sidecar's identity (its private key) does not survive a move Issue open

Migrated from github.com/carlosframework/platform issue #191, opened 2026-08-14. GitHub issues are closed for this repo; see AGENTS.md, "Work is tracked on amadan, not GitHub".

Still outstanding: Hibernation replicates *.db only, so a sidecar's identity does not survive a box move.

---

The gap

internal/ops/litestream.go builds the replica set by globbing filepath.Join(dataDir, "*.db"). That is the whole of what parks in S3.

Everything else in /data/<host>* is box-local: <host>.env, <host>-sidecar.env, and — the one that matters — whatever state a sidecar persists. The activator's own contract makes that state load-bearing: HasSidecar is defined as <host>-sidecar.env existing, and StartSidecar execs <binary> sidecar run under it (internal/activator/backend_exec.go).

For Eleven, the sidecar is an E2EE member. Its session file holds the bot's private key. So an instance that hibernates and is rehydrated on a different box comes back:

  • with no sidecar env file → HasSidecar false → no bot at all, silently; or, if the env file is restaged,
  • with no session → the sidecar tries to self-provision from its one-time invite → the invite is already spent → it cannot rejoin.

Either way the bot is gone, and the thread keys wrapped to its public key are now wrapped to a key nobody holds. The messages stay readable to every human member; the bot's own view of them does not come back. Nothing warns.

Why I'm filing it here rather than on the app

Two honest readings, and I think this one is the platform's:

  1. Platform: hibernation moves an instance between boxes and defines what travels with it. Defining that as "the SQLite files" is a reasonable v1, but the sidecar co-spawn is a first-class platform feature whose state the same platform then declines to move. Those two decisions are inconsistent with each other.
  2. App: Eleven could keep bot identities in the instance database instead of beside it, and get replication for free.

(2) is a real fix and we may do it regardless — it is strictly better for us. But it only fixes apps that know to do it, and the platform currently gives no signal that non-DB state is ephemeral. An app author reading "/data/<host> is your instance's directory" has no way to learn that only some of it survives.

What would help, roughly in order of cheapness

  • Say so. Document that only *.db travels, in the hibernation design and wherever the sidecar contract is described. That alone would have made us design differently.
  • Refuse quietly-broken states. A host that wakes with a sidecar env file but no prior state could at least log that it is provisioning fresh, rather than doing it silently.
  • Replicate a declared state path. An app declaring, say, data: { replicate: "app" } (the capability vocabulary already has a data slot) could have /data/<host>-<app>/ travel with the database.

Not urgent for us — CARLOS spaces have no sidecar yet, precisely because there is no way to stage one from off-box (separate issue). But it should be settled before the first bot-bearing space hibernates, because the failure is silent and the credential is unrecoverable.