Encrypted secret storage for Pithy. One dedicated store per environment, a worker-only master key, automatic at-rest key rotation, and the `pithy secrets` CLI to manage it.
@pithy-sh/secrets
pithy add secretsSomewhere to put the values you cannot commit: DKIM keys, webhook secrets, rail credentials, the auth signing key. Encrypted at rest, rotated automatically, managed from pithy secrets.
What it sits on
Secrets do not ride the per-feature lifecycle. A deployed Worker still needs its signing key and webhook secrets after a feature branch is torn down, so the store is its own D1 per environment, not a table in the app database.
The master key is worker-only. Nothing outside your Worker can reach it, and there is no API of ours that returns it.
The honest bit
At-rest key rotation happens on its own, and it is append, prove, then expire — never replace. A rotation that fails leaves the old key working rather than locking you out of your own secrets.
Plenty of secret stores encrypt. Far fewer rotate without a maintenance window, and that is the difference worth caring about.
If you build it yourself
Anyone can seal a value. Very few systems can change the key afterwards.
A master key that never leaves the Worker, per-value envelopes, and additional authenticated data binding a value to its own name.
New key, prove it, expire the old one — never replace, or a failed rotation locks you out of everything at once.
Application data comes and goes per branch; a signing key must survive that, which is why it lives in its own database.
Redaction at the boundary, and a toJSON that cannot betray you in a stack trace.
One value per environment, present in all of them at once, without copying it by hand.
pithy add secretsIn the dashboard
Which secrets exist, in which environment, and when each was last rotated — never their values.
The dashboard is the admin interface for your own application: whatever this capability writes, your team can look up, check and act on from a browser. Reading is free, forever, with no time limit. Connect the project when you deploy.
Everything else
Each one composes the same way. Nothing you skip costs you anything.
Designed, not shipped
Written down, argued out, and not built yet. Each one links to the issue it is being designed in, so you can read the reasoning or disagree with it.
One command installs the package, wires its bindings into every environment your project declares, and runs its migrations.