GOMODEL_ENCRYPTION_KEY and they are encrypted before
they are written, so a database file, dump, backup, or replica no longer holds
them in plaintext.
Encrypted fields:
Credentials declared in environment variables or
config.yaml are never
written to the database, so they are not affected.
Default: unset. Secrets are stored in plaintext, and GoModel logs one warning
at startup when an enabled feature (providers, and MCP or guardrails when they
are on) loads a plaintext secret.
Enable it
Generate a key and pass it to every instance that shares the database:GOMODEL_ENCRYPTION_KEY=${file:/run/secrets/gomodel-encryption-key} works too.
Existing plaintext secrets
Enabling the key does not rewrite anything. Plaintext values keep working and are encrypted the next time each entry is saved. To encrypt everything at once, run:Rotate the key
To changeGOMODEL_ENCRYPTION_KEY, start with the new key and the old one in
GOMODEL_ENCRYPTION_KEY_PREVIOUS:
GOMODEL_ENCRYPTION_KEY_PREVIOUS.
To replace the data key itself, for example after a suspected leak of a
database copy together with the old key, run:
gomodel secrets reencrypt.
How it works
Each database gets a random 256-bit data key, stored only in wrapped form in theencryption_keys table. The key that wraps it is derived from
GOMODEL_ENCRYPTION_KEY with Argon2id. Secret values are encrypted with
AES-256-GCM and stored as enc:v1:<key-id>:<ciphertext>; the row and field
are bound into the ciphertext, so a value copied into another row or field
fails to decrypt instead of being used there.
The admin API is unchanged: secrets are still masked in responses.
Encryption at rest protects copies of the database. It does not protect
against someone who can read the gateway’s environment or memory, and it does
not replace restricting access to the database and its backups.
Custom distributions
A distribution built on GoModel can wrap the data key with a KMS instead ofGOMODEL_ENCRYPTION_KEY by calling LoadResult.SetKeyWrapper with a
config.KeyWrapper from run.Options.SetupConfig. On the first start with a
new wrapper, keep GOMODEL_ENCRYPTION_KEY set once so the existing data key
can be moved to it. To move from one wrapper to another, pass the old one as
a previous wrapper: SetKeyWrapper(next, previous).