Provisioning never mints the app-secrets keypair, so sealed secrets are silently unavailable on a fresh deployment Issue open
Migrated from github.com/carlosframework/platform issue #190, opened 2026-08-14. GitHub issues are closed for this repo; see AGENTS.md, "Work is tracked on amadan, not GitHub".
Still outstanding: Provisioning never mints the app-secrets keypair, and nothing warns that sealed secrets are therefore unavailable.
---
What happened
On carlos.exciting.io (a deployment stood up the normal way), carlos secrets set refuses:
$ carlos secrets set -app eleven "LCHAT_ADMIN_TOKEN=…"
no CARLOS_SECRETS_PUBKEY in env and this console does not serve one — set the
env var, or set CARLOS_SECRETS_PUBKEY in the console's environmentThe refusal is clear and correct. The problem is upstream of it: nothing in provisioning ever mints or installs the keypair, so every deployment starts without sealed secrets and only finds out when someone reaches for them.
Grepping for it:
carlos bootstrapstages units, dirs, the service user, timers — not/etc/carlos/host.env, whichinfra/remote-host.mdstep 3 has the operator write by hand.CARLOS_SECRETS_KEYappears nowhere underinfra/— no module, no template, no tofu variable.carlos secrets genkeyexists and is purely local. Nothing calls it, and no doc step says "run this once per deployment, put the private half here and the public half there".
So the two placements a working deployment needs — CARLOS_SECRETS_PUBKEY in the console's environment, CARLOS_SECRETS_KEY in every host's /etc/carlos/host.env — are known only to whoever has read appconfig_cli.go and internal/host/agent.go.
Why it bites harder than a missing feature
Plain carlos env writes to the bucket whether or not the key exists, and delivery on a box is gated on CARLOS_SECRETS_KEY (internal/host/agent.go configSyncLoop). A deployment in this state has a config surface that looks complete: env set succeeds, env list shows the value. Whether it reaches a box depends on a variable nobody was told to set. On this deployment delivery does work, so the key is evidently present on the host but not on the console — the two halves drifted, which is exactly what nothing-provisions-them predicts.
Meanwhile the only remaining place for a credential is carlos env, i.e. plaintext in the deployment bucket. That is the actual cost: not a missing convenience, but a live deployment where the sealed path is closed and the unsealed one is wide open.
What I think it should do
Mint at deployment-creation time, the way the release signing key already is — one keypair per deployment, private half to wherever host.env comes from, public half into the console's environment, both recorded as deployment facts rather than operator lore.
Failing that, two smaller things that would each have caught this:
carlos bootstrap(and the tofu modules) emit the pair, or refuse to finish silently without one, namingcarlos secrets genkeyand both placements.- Something reports the drift.
carlos status -live, or acarlos doctor-style check, saying "console serves no secrets pubkey; sealed secrets unavailable" beats discovering it at the moment you need to store an API key.
Happy to send a PR for either if you tell me which shape you want.
Repro
carlos auth login -console https://carlos.exciting.io
carlos secrets set -app <any> FOO=bar # refused, as above
carlos env set -app <any> FOO=bar # succeeds, plaintext in the bucket