Temporary access
seekrit can mint short-lived credentials on demand — a temporary Postgres login that exists for an hour, then disappears — without giving up the zero-knowledge property that defines the rest of the product. Postgres is the first provider; the machinery is general.
The problem it solves
A long-lived database password in a .env file is a standing liability: it
leaks, it lingers, it is hard to rotate. The Vault-style answer is dynamic
secrets — mint a credential per session, scoped and expiring. The catch is
that a naïve implementation has the control plane generate and hand out the
password, so the service sees your plaintext. seekrit avoids that.
The key trick: ship the verifier, not the password
PostgreSQL stores passwords as SCRAM-SHA-256 verifiers, and CREATE ROLE … PASSWORD '…' stores the string verbatim when it is already in verifier form —
it does not re-hash it. So the flow inverts:
- The machine that will connect generates a random password locally.
- It computes the SCRAM verifier locally (PBKDF2 → HMAC → SHA-256).
- It sends seekrit only the verifier, plus a role name and a TTL.
- seekrit runs
CREATE ROLE … PASSWORD '<verifier>' VALID UNTIL …. - The machine connects to Postgres directly with the password it never shared.
The verifier is not sufficient to authenticate — SCRAM login requires
ClientKey, and the verifier only stores SHA256(ClientKey) (preimage-
resistant) plus ServerKey. So a dump of pg_authid, the query log, or a
backup cannot log in. The plaintext password lives only on the consumer.
This is zero-knowledge at both layers: the control plane relays only the verifier, and Postgres-at-rest holds only a verifier that can't log in. The password is high-entropy and machine-generated, so the SCRAM iteration count (which exists to slow dictionary attacks on human passwords) is irrelevant.
Provider tiers
Not every system supports verifier injection, so providers are classified by how much zero-knowledge they can preserve for the credential they mint:
- Tier 1 — verifier injection. The target accepts a client-computed verifier; plaintext never leaves the consumer. Postgres SCRAM, SSH public keys, mTLS CSR signing, hashed API keys.
- Tier 2 — client-encrypted delivery. The target mints the secret itself, so the broker wraps it to the consumer's public key before returning it. The control plane relays ciphertext; the broker sees plaintext transiently. AWS STS, GCP short-lived tokens.
- Tier 3 — opaque broker. The target mints an opaque token the broker must hold; the best option is to never hand it out and proxy the requests instead.
The broker
Each organization gets its own broker (a Cloudflare Durable Object, one instance per org). It owns the lease lifecycle in its own isolated SQLite, and uses alarms to expire leases — the sweeper that drops the role at its deadline (managed Postgres has no in-database scheduler). It holds two secrets carefully:
- its own keypair, generated once, private half never leaving the DO;
- each target's admin credential, stored only as ciphertext wrapped to that public key, decrypted transiently in memory to provision, never written back.
The provisioning credential — the honest caveat
To run CREATE ROLE, something must authenticate to Postgres as a privileged
role. That admin credential is a second, narrower trust tier — the same trade
every dynamic-secrets engine makes. seekrit minimizes it, and lets you choose
where it lives via the executor:
- in-DO executor — the broker connects to Postgres and runs the SQL itself. Convenient; the admin credential is decrypted transiently inside the DO.
- remote executor — the broker dispatches a signed command to a small
seekrit-provisionerrunning inside your own network, which holds the admin credential. seekrit orchestrates but never sees it — pure zero-knowledge even for provisioning.
The credential you hand out stays zero-knowledge either way; the executor is the dial for how much the provisioning credential is exposed.
Provider compatibility
The verifier-injection trick relies on standard PostgreSQL behavior: a
pre-hashed SCRAM-SHA-256$… password is stored verbatim. That holds on
Render, AWS RDS, Cloud SQL, Azure, Crunchy Bridge, self-hosted, and most
managed Postgres. The in-DO executor authenticates its own admin connection via
SCRAM, MD5, or cleartext (over TLS), so it works whether a provider uses the
modern SCRAM default or still challenges MD5 for login.
Neon is not supported for the zero-knowledge path. Neon manages roles
through its own control plane, which intercepts CREATE ROLE and requires a
plaintext password (it rejects a pre-hashed verifier: "Neon only supports
being given plaintext passwords"). That is incompatible with sending only a
verifier. Use a standard Postgres, or a provider whose role creation isn't
proxied by a control plane.
See the CLI reference (seekrit pg) and the
API reference for the endpoints.