Alpha — the platform is in development. Docs cover what ships today; anything unbuilt is marked. Concepts
-rw-rw-r-- content:content /docs/concepts/secrets.dtmpl ● verified against v2.0

Secrets & the master key

Some of what the platform stores must be readable back — a mailbox password it uses to fetch mail, the shared secret behind an authenticator code, a softphone's SIP credential. These are sealed at rest under a single master key, and everything below follows from that being one key.

Six kinds of sealed value#

A module registers a kind of sealed value; the platform seals and opens them all the same way. Measured, there are six:

kind what it protects
secrets file the application's own secrets file
email bodies stored .eml message bodies in the VFS
mailbox password a mailbox's IMAP/SMTP password
mailbox oauth a mailbox's OAuth grant
user factor the second factor's shared secret (Elevation)
sip credential a per-user WebRTC-SIP softphone password

Each value is sealed with libsodium, a fresh nonce per value, and carries an enc:v1: marker so the platform can tell a sealed value from a legacy plaintext one and knows how to open it. A module adds a kind by implementing one interface and tagging the service — nothing central changes — which is also what lets rotation walk every kind without knowing what any of them is.

One key, and what that means#

The key lives in the environment, COOLMS_SECRET_MASTER_KEY, a base64-encoded 32-byte value. One key seals all six kinds, and that has two consequences an operator has to take on board:

  • Back it up, separately from the database. Everything sealed above is undecryptable without it. A database restored without the key is a database of values nobody can open — the mail won't fetch, the second factors won't verify, the softphones won't register. The key is provisioned into your environment file by coolms:install on a fresh install (see Install); that file is the thing to back up and to keep out of the database's own backup.
  • An unreadable value fails loudly, by name, not silently. If the key is missing, malformed, or does not open a value, the platform answers 409 and says which of four things is wrong: no key set, a key that is not a valid base64 32-byte value, a value that does not open under the current key, or a value stored unsealed from before encryption at rest. A secret that cannot be read is a stated conflict — never a quiet fall-through to "no second factor" or "no password".

What the operator sets, and what a shell generates#

Setting the key is an environment act — the operator puts COOLMS_SECRET_MASTER_KEY in the deployment's environment and backs it up. There is no screen for it, deliberately: a master key an admin session could read or change from a browser would defeat the point of sealing anything.

Generating a well-formed key is the one part that needs a shell:

docker exec coolms_app php bin/console coolms:secret:generate-key

It prints a base64 32-byte key for the operator to place in the environment. On a fresh install coolms:install provisions one so the system can seal from the first write; generate-key is for the operator who is setting up a real deployment and wants to choose and store the key themselves.

Replacing the key once values are sealed under it is not a matter of editing the variable — that would strand everything already sealed. It is a rotation, which is its own mechanism and its own page.