Export & break-glass recovery
Zero-knowledge means seekrit cannot read your secrets. It should also mean you
don't need us to. seekrit archive exports your whole organization as one
signed file, and opens that file offline — with your passphrase, a service
token, or a quorum of recovery custodians. No network, no account, no seekrit.
Use it for a durable backup, for a compliance or due-diligence snapshot, or to prove to yourself (or a security reviewer) that leaving is a file copy rather than a migration project.
What's in an archive
One JSON file, seekrit-archive/v1, holding every row we store for the
organization:
- Structure — applications, groups, environments (including branches and their parents), and which groups each environment composes.
- Secrets — every current ciphertext, and every historical version, exactly as stored.
- Key grants — each environment's data key wrapped to each principal
(
wd1.blobs), for every principal, plus managed-key (KMS) grants and the wrapped recovery shares. - People and credentials — members and roles, service-token and machine-client metadata, outstanding invites.
- Operations — rotation policies, sync connections and bindings, lease targets, and the append-only audit log.
- Agent access policy — every agent identity and the full version history of its published, signed policy bundles, so what an agent was allowed to reach stays verifiable offline, for past versions as well as the current one.
- Your own key — the requesting user's passphrase-encrypted private key
(
pk1.), so the archive plus your passphrase is self-sufficient.
Ciphertext stays ciphertext: exporting decrypts nothing, and the archive is exactly as unreadable to us as the database it came from.
An archive carries your wrapped private key and nobody else's. Every member's key stays where it is; each person exports their own. That's why the manifest records who asked for it.
Three columns are deliberately left out, because including them would either hand you an offline cracking target or an undecryptable blob: service-token and session hashes (authentication verifiers, useless to you), the admin credentials on lease targets, and the destination API credentials on sync connections. The last two are encrypted to a server-side key that no key of yours can open — the row is in the archive, the credential column isn't.
A few whole tables are out too, and for one of them the reason is worth knowing: honey tokens are never in an archive. A decoy works because nobody knows where it's planted, and an archive is a file that travels — so the one place a decoy's placement must not appear is a portable copy of your organization. Every trip is still there, in the audit log. Also omitted: expired lease records, sync run history, in-flight recovery ceremonies, the log-sink credential, and billing rows — each either duplicated by the audit log or not your data. The full list, with reasoning, is in the design notes shipped with the code.
Create one
Admins only, from the CLI:
seekrit archive create --out acme-2026-08-19.json
It prints a row count per section, verifies the file it just wrote, and names
the signing key. Options: --no-versions and --no-audit to shrink it,
--audit-limit <n> to cap audit history (the default keeps the newest 25,000
rows). Or straight from the API:
curl -X POST https://api.seekrit.dev/v1/orgs/$ORG/export \
-H "authorization: Bearer $SEEKRIT_TOKEN" \
-H "content-type: application/json" -d '{}' \
-o acme-2026-08-19.json
Every export writes an org.exported audit row — who, when, how many rows, and
the archive's digest — so an export is as visible as any other privileged
action. Export is never gated on your plan; the ability to leave isn't a
paid feature.
Verify it
The manifest carries a SHA-256 digest per section, and the whole manifest is signed with Ed25519. So an archive is tamper-evident years later, offline:
seekrit archive info acme-2026-08-19.json # summary + verification
seekrit archive verify acme-2026-08-19.json # exits non-zero if anything fails
verify reports which section broke rather than just "bad file". To check
provenance as well as integrity, pin the signer — the public key is published
unauthenticated, so you can fetch it once and keep it:
curl -s https://api.seekrit.dev/.well-known/seekrit-export-signing-key
# → { "algorithm": "ed25519", "publicKey": "…", "keyId": "1ab3bd8b98170773" }
seekrit archive verify acme-2026-08-19.json --key-id 1ab3bd8b98170773
Open it offline
Two ways in, both with no network.
With the CLI, which needs no credentials and never builds a client for these subcommands:
seekrit archive decrypt acme-2026-08-19.json --out ./plaintext
# ./plaintext/apps/web/production.env
# ./plaintext/groups/shared/staging.env
You'll be prompted for your passphrase (or set SEEKRIT_PASSPHRASE). Other key
sources: --token skt_… for a service token, --key-file for a raw private-key
JWK. Add --format json|shell, --env web/production to narrow it, or
--stdout to print instead of writing files. Environments your key holds no
grant on are skipped, not failed — an archive spans the whole organization,
and no single principal is expected to open all of it.
With no install at all, using the standalone decryptor: a single HTML file you keep alongside your archives.
seekrit archive decryptor --out seekrit-decrypt.html
Open it in any browser — including on an air-gapped machine, straight from
file:// — drop the archive in, enter your passphrase, and read your secrets.
It verifies the digests and signature first, and shows values masked until you
reveal them.
The page declares Content-Security-Policy: default-src 'none'. Your browser
will refuse every outbound request it could make, so the page that sees your
passphrase and your plaintext cannot send them anywhere. It's one line at
the top of the file, and you can read it before you trust it.
Break glass: opening an archive nobody holds the key to
The hard case isn't losing the file, it's losing the person. If the only key-holder for an environment is gone, their passphrase is gone with them — and we never had it.
That's what customer-controlled recovery is for, and an archive carries everything the ceremony needs: each environment's data key wrapped to the org recovery key, plus every custodian's wrapped Shamir share. So the ceremony also runs offline, from the archive alone.
Each custodian unwraps their own share from their own archive:
seekrit archive share acme-2026-08-19.json --out share-ana.json
# (or --token skt_… for a service-token custodian)
Then any threshold of shares reconstructs the recovery key and opens everything:
seekrit archive decrypt acme-2026-08-19.json \
--share share-ana.json --share share-bo.json \
--out ./recovered
Below the threshold it fails outright rather than producing plausible garbage. The standalone HTML decryptor accepts pasted shares too, under custodian shares (recovery quorum).
An unwrapped share is sensitive: a threshold of them is the recovery key, and the recovery key opens every environment. Move them as you would the secrets themselves, and delete them when the ceremony is done.
Notes and limits
- It's an exit and audit artifact, not a restore point. There is no import: re-establishing access to a live organization means re-granting keys, which is the recovery ceremony above, not a file upload.
- Values are as stored. A
${REFERENCE}in a secret is expanded when an app reads it (see references); an archive shows the literal stored value. - Truncation is always declared. If an audit cap trims history, the section
says so and
verifyrepeats it. A quiet trim would make an incomplete archive look complete. - Two exports of unchanged data are byte-identical, so you can diff yesterday's archive against today's.
- Keep a decryptor with your archives.
seekrit archive decryptoris the half that has no dependencies at all; an archive plus that file plus your passphrase is the whole recovery path.