Secret Management

Zero-knowledge secrets, alongside your config.

You hold the encryption key. We hold the ciphertext. Encrypt a value with one CLI command, distribute it to every environment through the same delivery path as your flags, and decrypt it in your app with `Quonfig.get("stripe.api.key")`. Quonfig never sees the plaintext — not at rest, not in transit, not in the audit log.

~/our-config

Trust boundary

You own the key. We own the pipes.

Quonfig has zero knowledge of your encryption key. We just store and distribute the ciphertext alongside the rest of your config.

Quonfig owns

  • The CLI that encrypts locally before it ever hits the network
  • Distributing the encrypted blob to every environment, just like any other config
  • SDKs that decrypt in-process when your app calls Quonfig.get()

You own

  • The encryption key itself — a single hex string per trust group
  • Sharing the key with the right developers and services (a password manager or one-time link works fine)
  • Setting QUONFIG_SECRET_KEY_* env vars in dev and production

How it works

Three commands, two configs, one shared secret

Quonfig secrets are built on the same `provided` and `decryptWith` config primitives you already use. Nothing magic. Nothing custom.

1. Generate a hex key

`qfg generate-new-hex-key` produces a random hex string. Drop it into your local `.env` as QUONFIG_SECRET_KEY_DEFAULT and share it with your team via a password manager.

2. Declare the key config

`qfg create quonfig.secrets.encryption.key --env-var=QUONFIG_SECRET_KEY_DEFAULT --type string --confidential` — an empty vessel that resolves from your env, never stored on our side.

3. Encrypt and ship

`qfg create stripe.api.key --type string --value=sk_live_abc --secret` encrypts locally with the hex key and pushes only the ciphertext. Your app gets the plaintext back via `Quonfig.get(...)`.

Per-environment keys

Different keys for production, staging, and dev

Because the encryption key itself is just a `provided` config, you can resolve it to a different env var per environment. Production secrets stay isolated from your dev box.

Isolate production

`qfg set-default quonfig.secrets.encryption.key --environment=production --env-var=QUONFIG_SECRET_KEY_PRODUCTION --confidential`. Now production decrypts with a key your laptop never sees.

Rotate without an outage

Issue a new hex key, set it in the env, re-encrypt with `qfg create --secret` against the new key, then retire the old one. Each step is a config change with a real audit trail.

As many keys as you need

One key per trust group, two for prod-vs-everywhere-else, or many for fine-grained access — the model is the same. `quonfig.secrets.encryption.key` is just a default name, not a hard limit.

Why one platform

Same delivery path. Same SDKs. Same audit trail.

Secrets aren't a separate product. They use the same `Quonfig.get()` call, the same local-evaluation cache, the same git-backed history, and the same per-environment targeting as every other config.

One SDK call

Quonfig.get("stripe.api.key") returns the decrypted string. Your app code doesn't know or care that it was encrypted at rest.

Survives our outage

Encrypted secrets ride the same local-evaluation cache as every other backend SDK config. If we go down, your app keeps resolving the cached ciphertext.

Real audit trail

Every secret rotation is a git commit with author, timestamp, and diff. The audit log shows the ciphertext changed — no plaintext leaks into the history.

Works in CI offline

Make QUONFIG_SECRET_KEY_DEFAULT available to your CI process and tests resolve the same secrets, no network required.

Ready to stop juggling a separate secrets manager?

Encrypt locally, distribute through Quonfig, decrypt in your app. Zero plaintext touches our servers.