Secrets
Secrets
Chinaski keeps its important secret values (the master secret it runs on, and credentials for the services it talks to) out of plain text and out of sight. This page explains how CHINASKI_SECRET works, which settings are stored encrypted, and what happens if you rotate the master secret.
The CHINASKI_SECRET environment variable
This is the single master secret of a Chinaski instance, and it must exist before the server starts: without it the CMS refuses to run at all.
It protects two things:
- Admin sessions. Your sign-in session cookie is cryptographically signed with this key. Users never see it or handle it.
- Encrypted storage. The encryption key protecting credentials stored in the database is derived from it. Those credentials are never written to disk in plain text.
Generating it is a one-time step the installer does for you in the standard deployment: the value is written to a permission-locked env file (0600, inside a 0700 directory) that only the CMS service user can read. In the offline install flow, the recipe is:
export CHINASKI_SECRET=$(openssl rand -hex 32) # do this once; save the valueNever commit it to repositories, don’t share it between instances, and keep a copy somewhere safe; you can’t recover it from the running system.
What gets stored encrypted
Several settings in the admin area are security-sensitive values and are stored in the database encrypted. The registered secret settings are:
- SMTP password
- Webhook secret
- Deploy S3 secret key
- Turnstile secret key
- Backup (Spaces) secret key
- OAuth client secret
The encryption is authenticated (AES-256-GCM with a random per-value nonce and an integrity tag). Encrypted values are stored with a version prefix so the format can evolve. If a row was written by an older version in plain text, the system tolerates it until the setting is next saved, at which point it’s upgraded to encrypted form.
Secrets and the API
The read API never returns secret settings: they are stripped from the response even for authenticated API keys. API writes encrypt a new value the same way the admin UI does. Third-party integrations cannot read your stored credentials through the API.
Rotating the master secret
Changing CHINASKI_SECRET is a serious operation with known consequences:
- Everyone is signed out. Existing session cookies were signed with the old key and become invalid the moment the new key takes effect; all admins and editors must sign in again. There is no overlap or fallback key.
- Stored credentials stop working. The database’s encrypted credentials (SMTP, webhook, Turnstile, OAuth, S3, backup) were encrypted under a key derived from the old secret. With the new secret they can’t be decrypted.
- Fixing it means re-saving. After rotation, open the relevant settings in the admin area and re-save each credential value so it gets re-encrypted under the new key. This same action upgrades any legacy plain-text values you may still have.
There is no built-in command to automate the migration of stored secrets across a rotation in the current release; plan the rotation: pick a maintenance window, rotate the env value, restart the instance, sign in fresh, and re-save each affected setting. Test email, webhooks, and any other configured integration afterwards.
Hygiene practices the system enforces
- Secrets live only in environment or in encrypted database values; the application keeps a plaintext copy in memory only where needed to talk to a service.
- The database file and the env file are owner-readable only; the backup directory is owner-only. Standard installs put them inside directories only the
chinaskiservice user can access. - The scripted setup tool prefers reading the admin password from an environment variable instead of a command-line flag, so the password doesn’t appear in the process list during setup.
- For isolated/airgapped hosts, setting
CHINASKI_AIRGAP=1disables every outbound call the CMS could make; nothing about your deployment needs to leave the machine for the core to run.