Keys and secrets
The secrets a deployment holds, the credentials a project holds, how each is stored, and which of them can be changed.
Two sets of secrets protect the product. The deployment holds a small set outside the database, and each project holds the credentials its sites and servers use. Who holds which key decides who can forge, read or find a record.
Deployment secrets#
The operator sets these as environment variables. They are not stored in the database.
| Secret | Protects |
|---|---|
| Encryption key | Every encrypted field. It also salts secret-key hashes, email confirmation tokens and the signed links on hosted pages |
| Subject hash pepper | The keyed hash of every identifier, and the IP address hash |
| Evidence chain keys | The seal on every consent event |
| Receipt signing key | The Ed25519 signature on receipts and daily anchors. Only the public half is published |
| Platform key | The endpoints that approve live keys |
| Metrics token | The metrics endpoint |
| IAM session secret | The signature check on the console session cookie. It is shared with the IAM service |
The API refuses to start in production without an encryption key. It checks at start that the evidence keys are well formed.
Derived keys#
When no dedicated pepper, chain key or signing key is set, the API falls back to another secret, in the end the encryption key. The chain key and the signing key are then derived with HKDF-SHA256. Configuration gives the order of sources for each.
A production deployment should set dedicated values and keep them in a secret manager. Setting a different value later changes the hashes, seals and signatures. Records made before the change can then no longer be found or verified.
Project credentials#
| Credential | Looks like | How it is stored |
|---|---|---|
| Publishable key | pk_… |
In clear. It is meant to be public and is bound to a list of allowed origins |
| Secret key | sk_… |
Only as a SHA-512 hash salted with a deployment secret. Shown once |
| Identity secret | idv_… |
Encrypted. Shown once, and can be rotated |
| Webhook signing secret | whsec_… |
Encrypted. Shown once |
| Subject fingerprint key | sfk_… |
Encrypted. Created on first use and never changed |
- A publishable key alone cannot claim that an email address or user id consented. Your server signs an identity proof with the identity secret.
- The settings API never returns the fingerprint key. Showing it in the console is a separate action, written to the audit log.
- The browser runtime, the mobile SDKs and the WordPress and Tag Manager integrations refuse a key that starts with
sk_. - The
/sdkcross-origin policy does not allow theAuthorizationheader, so a page cannot complete a call with a secret key.
What can be changed#
| Key | Can it change | What happens |
|---|---|---|
| Evidence chain key | Yes | Several keys can be listed. New events use the active key, and each event records the id of the key that sealed it |
| Receipt signing key | Avoid it | The deployment publishes and checks with one key at a time. Older receipts and anchors verify only offline, against the public key you saved |
| Subject hash pepper | No | Every stored identifier hash depends on it. With a new value, no record can be found by identifier |
| Encryption key | No | There is no re-encryption tool. A new value makes stored secrets unreadable and every secret key invalid |
| Subject fingerprint key | No | You match receipts and webhooks with it |
| Platform key, metrics token | Yes | The operator sets a new value and restarts |
| A project's secret key | Replace it | Revoking stops the old key at once, and cannot be undone |
| Identity secret | Yes | Proofs made with the old one stop verifying immediately |
| Webhook signing secret | No | You create a new endpoint and delete the old one |
An example#
A copy of the database leaks without the deployment's secrets. The identifiers on its consent records are keyed hashes, so they cannot be matched against a list of known email addresses. The encrypted fields cannot be read. Secret keys are present only as salted hashes.
The copy still shows decisions, countries, times and form wording in clear. Whatever your integration put in metadata is also in clear. So is the webhook delivery log, which can hold readable identifiers for up to 30 days.
Limits#
- Whoever holds both the chain key and the signing key can create validly sealed records. Anchors you keep outside the deployment limit this to days not yet anchored.
- One signing key at a time. The public key set does not carry earlier keys, so save it when the deployment is set up and after any change.
- Secrets are environment variables. The product has no integration of its own with a secret manager or a hardware module. Protecting them is the operator's job.