Skip to content

archive_failover rows hold guest: false env in plaintext #423

Closed

Description

@CMGS

With archive_failover, the row of an archived or replicated claim holds the claim's env in its claim column, and that includes guest: false values in plaintext. Those values are the tenants' own credentials: the egress proxy injects them so that guest code never sees them.

token_sha256: d3a6267f…                      the sandbox token: a hash only
claim: {"env":{"GH_TOKEN":{"value":"ghp_…","guest":false}}, …}     the credential: as written

An adopter needs the value to go on injecting after node loss, which is why the row carries it. The cost is that the credential now sits wherever that database does: every node's DSN holder, its administrators, its backups and its snapshots.

Today

Plaintext is accepted for now. The docs state it and docs/security.md says to treat that database like backups (#420). A rotation now reaches the row (#422).

Options

  1. Encrypt the values in the row with a key every node of the cell holds and the database does not.
    • It needs a place for the key (a node env var, as cluster_key and preview_secret are configured), and a rotation story: rows written under an old key must stay readable until they are rewritten.
    • It adds one AEAD operation per row write, and none on the claim path.
  2. Keep the values out of the row. An adopter serves the claim with no credential until someone resends the env. The database then holds no secret, but a failover silently degrades egress for that claim until the write, so callers must handle it.
  3. Stay as today, for deployments where the database is already trusted with the credentials.

To decide

  • Is there a deployment where the database's readers should not see tenant credentials? If not, option 3 stands and this closes.
  • For option 1: one cell-wide key, or derived per tenant?

Activity

  1. CMGS commented on Nov 3, 2026

    @CMGS
    ContributorAuthor

    Decision for now: option 3 stands and plaintext stays. This issue stays open for when a deployment needs otherwise.

    State after #420 and #422: docs/security.md says to treat this database like backups. A rotation on an archived or hibernated replicated claim rewrites its row, a running replicated claim's row follows on the next round, and a renew rewrites the row too.

    The one rotation gap left is in #433: a write in the milliseconds between an archive's row write and the claim taking its checkpoint id reaches the journal but not the row.

  2. CMGS commented on Nov 4, 2026

    @CMGS
    ContributorAuthor

    The rotation gap named in the last comment is closed. #437 rewrites the row when an env write lands while the archive intent is set (TestAnEnvWriteDuringTheArchiveReachesItsRow), and #433 records that. Plaintext stays as decided; this issue stays open for a deployment that needs otherwise.

  3. CMGS commented on Nov 8, 2026

    @CMGS
    ContributorAuthor

    host_env_memory_only (#499) implements option 2 as an opt-in, for a deployment whose database readers must not see tenant credentials. With it on, claims.json and the archive and replica rows store guest: false entries without their values, marked missing. The control plane sends them again with PATCH after a hibernate, a restart or an adoption (sandboxd/pool/failover.go; docs/deploy.md, host_env_memory_only; docs/security.md). The default stays option 3, as decided. A row written before the flag is turned on keeps its values until it is next written. Option 1, encrypting the values in the row, is not built. A deployment that needs credentials to survive failover with no plaintext in the database would reopen this. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions