Secrets ยท Storage

The secrets you'd otherwise drop in a .env file.

Secrets is a small KMS-style store, scoped to your org and addressed by path โ€” /app/prod/db_url instead of an opaque ID. Every value is encrypted at rest with AES-256-GCM, versioned on update, and reveal is the only call that returns plaintext. It's also the call worth watching in the audit log, because it's logged every time.

$ exc secret create --path /app/prod/db_url --from-stdin

exc โ€” terminal
$ exc secret create --path /app/prod/db_url --from-stdin
paste the value, then Ctrl-D
$ exc secret reveal --path /app/prod/db_url
postgres://app:[email protected]:5432/app
$ exc secret events --path /app/prod/db_url
2026-06-12 09:14  reveal  api-token-7  ok

What's audited

The only call that touches plaintext is reveal โ€” and that one is always in the log.

Calls that don't touch plaintext โ€” list, get, lookup, version add โ€” are safe to use for metadata checks. Reveal only when the application actually needs the value.

OperationReturns plaintext?Audited?
create / version add no yes
list / get / lookup no yes
reveal yes yes
events audit log itself โ€”
delete (soft) no yes

Rotation

Old versions stay readable on purpose.

version add writes a new version that becomes the default for reveal, but the old version stays readable with --version <n> โ€” useful during a rolling deploy where some consumers still have the old value cached. Identify secrets by path or by the stable integer id if you might rename the path later.

  • --out PATH writes the value to a file with mode 0600
  • --copy puts the value on the clipboard, --env VAR emits a shell-safe export
  • Soft-delete frees the path for re-creation โ€” there is no undo
exc โ€” terminal
$ exc secret version add --path /app/prod/db_url \
    --value 'postgres://app:[email protected]:5432/app'
$ exc secret version list --path /app/prod/db_url
v1  2026-04-02  v2  2026-06-12 (default)
$ exc secret reveal --path /app/prod/db_url --version 1
postgres://app:[email protected]:5432/app

Audit log

The audit log names the identity behind each reveal.

exc secret events reads the audit log for one path: every create, version add, reveal, and delete, with the calling identity and a result. It's the first thing to check when a credential might have leaked, and the last thing to check before you trust a rotation went clean.

  • Scoped to a path โ€” --path /app/prod/db_url
  • Shows the identity that called reveal, not just that it happened
  • Covers create, version add, reveal, and delete in one timeline
exc โ€” terminal
$ exc secret events --path /app/prod/db_url
2026-06-12 09:14  reveal       api-token-7   ok
2026-06-12 08:55  version add  deploy-bot    ok
2026-04-02 14:02  create       api-token-3   ok

When to use it

Secrets is built for long-lived credentials, not for every value your app touches.

Use it for

  • Long-lived credentials shared between several services or VMs
  • Anything you want a clear audit log on
  • Values that change less than once a day

Don't use it for

  • Per-request ephemeral tokens โ€” fetch from the source each time
  • Public configuration โ€” plain config files or env vars are fine
  • High-throughput hot paths โ€” cache the plaintext after the first reveal

Get started

Your database password shouldn't live in .bash_history.

Put it in Secrets, reveal it only where the app needs it, and use exc secret events to verify the access trail.

$ exc secret create --path /app/prod/db_url --from-stdin