Compliance Storage
Chronicle stores encryption keys alongside the rest of your application data by default. When you configure a dedicated compliance storage, those keys are stored in a separate, independently secured backend — such as HashiCorp Vault — so that key material never resides in the same database as the encrypted events.
If no compliance storage is explicitly configured, Chronicle uses the general storage backend for encryption keys.
Configuration
Section titled “Configuration”The compliance section now contains an encryption subsection that holds the storage configuration. Set a type and connectionDetails inside compliance.encryption.storage:
{ "compliance": { "encryption": { "storage": { "type": "<storage-type>", "connectionDetails": "<connection-string-or-url>" } } }}Migrating from the default storage
Section titled “Migrating from the default storage”If you are configuring a dedicated compliance storage on a system that has already been running, the keys you care about are in the general storage backend — and the new store is empty. Turning it on by itself is a one-way flip: those keys become unreachable, and every [PII] value they protect reads back as an empty string. Nothing reports it. No exception is thrown, nothing is logged, and the health endpoint stays green — the result is byte-for-byte what a completed right-to-erasure looks like.
Set migrateFromDefaultStorage and there is no flip at all:
{ "compliance": { "encryption": { "storage": { "type": "vault", "connectionDetails": "http://vault:8200" }, "migrateFromDefaultStorage": true } }}Both stores are now live, and Chronicle keeps them in step for you:
- A key is looked for in the dedicated store first. When it is only in the default storage, it is served from there and written into the dedicated store as it is read — so the migration happens through ordinary traffic, with no script to write and no verify pass to run.
- New keys are provisioned in the dedicated store and mirrored back to the default storage, so both stay complete. That is what makes the move reversible: set
migrateFromDefaultStorageback tofalse— or drop thestoragesection entirely — and nothing is lost. - Erasing a key erases it from both stores, and records the erasure in both. A deletion that only reaches one of them fails loudly rather than reporting success, because a key surviving in either store is not an erasure — and a store that has the subject recorded as erased will not accept the other store’s copy, so a half-finished erasure cannot be healed back into place by ordinary traffic.
A key moves the first time it is read, so a subject whose data nobody has queried still lives only in the default storage. Before you turn migrateFromDefaultStorage off, confirm the dedicated store actually holds every key — turning it off early puts the subjects that were never read straight back into the empty-string outcome above. Once you have confirmed it, removing the leftover keys from the general storage backend is an ordinary cleanup you decide on separately, not the irreversible last step of a sequence.
Note: Leaving
migrateFromDefaultStorageon indefinitely is valid but rarely what you want — key material keeps being written to the same database as the encrypted data, which is the separation a dedicated compliance storage exists to give you.
The setting has no effect unless storage is configured. Without a dedicated store there is nothing to migrate to, and the general storage backend serves the keys on its own.
| Property | Type | Required | Description |
|---|---|---|---|
| migrateFromDefaultStorage | bool | No | Keep the general storage backend serving encryption keys alongside storage, moving each key into storage as it is read. Defaults to false |
As an environment variable:
export Cratis__Chronicle__Compliance__Encryption__MigrateFromDefaultStorage=trueBackup and restore ordering
Section titled “Backup and restore ordering”The compliance key store is the third item in a Chronicle backup set, alongside the encryption-certificate ring and the storage backend. It is an independent subsystem: the encryption certificate does not protect these keys, and these keys do not protect anything the certificate protects. The two are backed up separately and restored separately, and getting either wrong loses different data.
Restore in this order:
- The encryption-certificate ring, in the shape it had when the backup was taken.
- The storage backend (MongoDB or SQL).
- The compliance key store — to the same point in time as (2).
Step 3 is the one specific to this page, and it is a point-in-time match rather than a “latest wins”:
- A key store restored older than the storage is missing keys for subjects created since. Every
[PII]value belonging to those subjects reads back as an empty string — byte-for-byte identical to a completed right to erasure, reported by nothing. - A key store restored newer than the storage brings back keys for subjects whose erasure the storage backup predates. Nothing breaks, and that is the problem: an erasure you have already reported as complete is silently undone.
When migrateFromDefaultStorage is on, both stores are live and both are part of the backup set. Restoring only the dedicated store leaves the keys that had not been read yet — the ones still living only in the default storage — out of the restore.
Vault and Azure Key Vault both keep every key revision as a distinct secret, so their own backup and point-in-time restore facilities cover this; when the keys live in the general storage backend instead, the database backup already carries them and steps 2 and 3 are the same restore.
HashiCorp Vault provides a purpose-built secrets backend that is well-suited for storing PII encryption keys. Chronicle uses the KV v2 secrets engine to store each key revision at a distinct path.
Authentication
Section titled “Authentication”Chronicle authenticates to Vault using a token. The token is read from the VAULT_TOKEN environment variable at startup. Ensure this variable is set before the Chronicle server process starts.
Configuration
Section titled “Configuration”{ "compliance": { "encryption": { "storage": { "type": "vault", "connectionDetails": "http://vault:8200" } } }}Set VAULT_TOKEN in the environment:
export VAULT_TOKEN=s.myVaultTokenNote: Never include the Vault token in the
connectionDetailsstring or inchronicle.json. Always pass it through the environment to avoid storing secrets in your configuration files.
KV v2 mount point
Section titled “KV v2 mount point”Chronicle uses the secret KV v2 mount point by default. Encryption keys are organized under a path derived from the event store name, namespace, and subject identifier.
Key paths
Section titled “Key paths”Encryption keys are stored at:
secret/<event-store>/<namespace>/<identifier>/<revision>Each revision is an independent secret, which means individual revisions can be deleted without affecting others (for example, when rotating keys or when the full key history is required for a limited time).
| Property | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Must be vault |
| connectionDetails | string | Yes | The Vault server address, for example http://vault:8200 |
Azure Key Vault
Section titled “Azure Key Vault”Azure Key Vault provides a fully managed, cloud-native secrets backend for storing PII encryption keys. Chronicle uses the Azure Key Vault Secrets API to store each key revision as a distinct secret.
Authentication
Section titled “Authentication”Chronicle authenticates to Azure Key Vault using DefaultAzureCredential. This supports multiple authentication methods in order:
- Environment variables (
AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_TENANT_ID) - Workload identity
- Managed identity
- Azure CLI credentials
- Visual Studio / VS Code credentials
Ensure that the identity used has the Key Vault Secrets Officer role (or at minimum Get, List, Set, and Delete secret permissions) on the target Key Vault.
Configuration
Section titled “Configuration”{ "compliance": { "encryption": { "storage": { "type": "azure-key-vault", "connectionDetails": "https://my-vault.vault.azure.net" } } }}Secret naming
Section titled “Secret naming”Encryption keys are stored as individual secrets. Secret names follow this pattern:
chronicle--{event-store}--{namespace}--{identifier}--{revision}Each component is sanitized to lowercase alphanumeric characters with single hyphens replacing any other character sequences. Double hyphens (--) serve as unambiguous separators between components.
| Property | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Must be azure-key-vault |
| connectionDetails | string | Yes | The Azure Key Vault URI, for example https://my-vault.vault.azure.net |