Rotating the server secret
SECRET encrypts three kinds of stored credentials (AES-256-GCM): webhook signing secrets, design-provider credentials and user connection tokens, and notification-channel secrets (chat webhook URLs and git tokens). Changing it without a plan leaves those rows unreadable, so StoryShelf keeps the old value as a decrypt-only fallback while you migrate.
Procedure
Section titled “Procedure”-
Set the new value and keep the old one:
Terminal window SECRET=<new value>SECRET_PREVIOUS=<old value>The scaffolded
src/index.ts,dev-serverandfly-appread both. In code this isShelfConfig.secretandShelfConfig.previousSecret(a single value, not a list). -
Restart. The server decrypts with
SECRETfirst and falls back toSECRET_PREVIOUS, so webhooks, git statuses and notifications keep working. -
Re-encrypt the stored credentials under the new secret, in either way:
- Automatically at boot: also set
SECRET_MIGRATE=true(ShelfConfig.migrateCredentialsOnBoot). It is off by default. - From the UI: a site admin opens System and presses Re-encrypt with current secret. This is available whether or not auto-migration is on. The same action is
POST /api/v1/admin/credentials/reencrypt.
- Automatically at boot: also set
-
Check the Stored credentials card on the System page: every credential should be under the current secret and none unreadable. Then remove
SECRET_PREVIOUS(andSECRET_MIGRATE).
When something is wrong
Section titled “When something is wrong”A credential that neither secret can decrypt is never silent:
- The server logs an error at boot with the table and row id (never the value).
- The System page shows the Unreadable count with an alert.
POST /api/v1/healthreports a failedcredentialsentry, so the overall status isdegraded.- A channel whose secret cannot be decrypted (including a git channel’s token) is skipped at delivery, with a logged warning.
Fix it by setting SECRET_PREVIOUS to the secret that row was encrypted with, or recreate the webhook, token or channel.
Sessions
Section titled “Sessions”SECRET also signs login sessions and CSRF tokens. Rotating it signs everyone out once; sessions are not carried over by SECRET_PREVIOUS.