Skip to content
pilots
Dashboard
All pages

Secrets

A secret is a value your app needs in its environment that must not be in the repository, in the state store, in a shell history, or in a conversation with an agent. Everything else is plain configuration and goes in the compose file as it is.

Setting one

pilot secret set DATABASE_URL      # prompts. The value is never an argument
pilot secret import .env
pilot secret ls
pilot secret rm DATABASE_URL

pilot secret set prompts for the value rather than taking it as an argument, which is what keeps it out of your shell history. It stores it locally, keyed by the app the compose file names.

Referring to it

compose.pilots.yaml
services:
  web:
    build: .
    environment:
      DATABASE_URL: secret://database_url
      LOG_LEVEL: debug
  1. pilot deploy resolves each secret:// reference on your own machine and sends the values as a sealed environment.
  2. The host seals them with the fleet key before they touch any replicated row, so a secret is never readable out of the state store.
  3. pilot service info returns environment KEYS and never values.

The rules

Rule Why
You run the command that holds the value, not an agent A value passed as a tool argument has already passed through the model and the transcript.
The compose file carries secret://name, never a value The file is committed. The value is not.
A fleet with no fleet key refuses a sealed environment It would otherwise write a plaintext secret into every replica of the state store.
--secret-env on pilot service set REPLACES the sealed environment rather than merging into it A secret that was meant to be removed must not linger because a later call forgot it.

When it is refused

  • not_configured naming the fleet key means the fleet cannot seal secrets. Send plain configuration only, and tell whoever runs the fleet.
  • A secret:// reference with nothing stored means the deploy has no value for it. Set it and deploy again.

Three things that do not work, on purpose

  • Reading a secret back to confirm it. Nothing returns one.
  • Passing a secret as plain env. It would be stored unsealed.
  • Putting a long-lived API key in a machine's environment so it can call the API. Use a grant instead: the key would otherwise be in every snapshot and every fork, and would outlive the machine (what a machine may do).