Troubleshooting: Auth, Email & MCP
Once the server boots, the next class of problem comes from the services layered on top. These are the ones that most often surprise people — the first two because they are warnings rather than failures, so the app looks healthy while something quietly does not work, and the MCP ones because they are the opposite: a hard refusal to start, on a variable that used to be correct.
Auth: "JWT signing keys could not be read"
⚠ 2 JWT signing keys could not be read with the current auth secret and were
regenerated — previously issued tokens are no longer valid
⚠ 5 stored connection tokens encrypted with a different key — affected users
must reconnectTwo warnings, one cause: the encryption key changed between this start and the last one. Unless you set AUTH_SECRET yourself it is derived from that key, so a new key rotates the signing secret as well, and everything sealed under the old one stops opening.
Almost always the key was never persisted in the first place. Read the line above them in the same banner:
✓ Encryption key: generated at /var/lib/sovrium/encryption-keygenerated at on a restart — where a settled install says from — means the data directory did not survive, so the key is new on every boot while the database keeps the old ciphertext. That is the ephemeral-filesystem shape: a container with no volume, or a platform that rebuilds the filesystem on each deploy. Pin SOVRIUM_ENCRYPTION_KEY to a fixed value and it stops recurring — see Secrets.
The two are handled differently on purpose. A signing key is derived material, so Sovrium regenerates it and the only cost is that already-issued tokens no longer verify. A connection token is a delegated credential to somebody's third-party account, so it is left exactly as it is and reported instead. Those users reconnect the integration themselves; nothing is discarded on their behalf.
Adopt the key before you drop the variable. Removing SOVRIUM_ENCRYPTION_KEY from a working deployment produces exactly these warnings, because the next start finds no key file and generates one. Run sovrium secret adopt first — it persists the key you already have, so removing the variable changes nothing.
"Email sending disabled — SMTP not configured"
Email sending disabled — SMTP not configured (set SMTP_HOST to enable)A warning, not a failure — and the more dangerous for it. The app boots, sign-up succeeds, password reset returns 200. The mail is written to the log instead of sent, so the links never arrive and the flow looks broken from the user's side only.
Set SMTP_HOST and its companions to enable delivery:
SMTP_HOST=smtp.example.com
SMTP_PORT=587 # default
SMTP_USER=apikey
SMTP_PASS=<secret>See Environment Variables for the full set, and Email Integration for provider setup. Anything with email verification or password reset should be tested against a real SMTP host before it ships.
MCP: "the MCP static tokens were removed"
MCP_TOKEN_ADMIN is set, but the MCP static tokens were removed. They had no user
identity, so the row-level user_access tier never ran for a token-authenticated
caller. Issue an API key instead (app.auth.apiKeys) and present it on the
x-api-key header, then unset MCP_TOKEN_ADMIN.MCP_TOKEN_ADMIN, MCP_TOKEN_MEMBER and MCP_TOKEN_VIEWER no longer exist. If your instance still sets one and MCP_ENABLED=true, the server refuses to start — deliberately. Ignoring the variable would be worse: you would believe /mcp is guarded by the secret you issued, when it is guarded by something else entirely.
The fix is three steps:
# 1. Remove the retired variables
unset MCP_TOKEN_ADMIN MCP_TOKEN_MEMBER MCP_TOKEN_VIEWER MCP_AUTH_STRATEGY# 2. Make sure the app has auth, with self-service keys enabled
auth:
strategies:
- type: emailAndPassword
apiKeys: true- Sign in as the user whose role the client should inherit, mint an API key, and send it on the
x-api-keyheader — notAuthorization: Bearer.
The role is no longer baked into the credential: a key acts as its owner, resolved live on every call. Demote that user and every key they hold narrows with them; ban them and the keys stop working. That is what the static tokens could not do — carrying no user, they never triggered row-level record rules at all.
MCP: "MCP_AUTH_STRATEGY=token names a strategy that no longer exists"
MCP_AUTH_STRATEGY=token names a strategy that no longer exists. /mcp now
dispatches on the header a request carries: x-api-key is verified as an API key,
Authorization: Bearer as an OAuth access token. Unset MCP_AUTH_STRATEGY (oauth2
is still accepted as a deprecated no-op).There is no strategy to choose any more. /mcp decides which verifier to use from the header the request actually presents, so both credentials are live at once — a Claude Desktop user on OAuth and a CI job holding an API key can call the same instance.
Unset the variable. MCP_AUTH_STRATEGY=oauth2 is still accepted so a correct config is not punished, but it selects nothing and will be removed.
MCP: "MCP_ENABLED=true requires app.auth"
MCP_ENABLED=true requires app.auth to be configured. Both MCP credentials — API
keys and OAuth access tokens — are Better Auth plugins, so without app.auth
nobody can authenticate to /mcp. Either configure app.auth or unset MCP_ENABLED.Both surviving credentials are issued by the auth layer, so an app with no app.auth block gives nobody a way in. Mounting /mcp there would leave a route that is either unreachable or unguarded, so the boot stops instead. Add an app.auth block, or leave MCP_ENABLED off.
All three refusals are scoped to MCP_ENABLED=true. A leftover MCP_TOKEN_* on an instance that never mounts /mcp authorizes nothing, so it does not fail the boot — failing a whole app over an inert string would be hostile. Only instances that opted into MCP are affected. See Auth, RBAC & Rate Limiting.
Still stuck?
- Run
sovrium validate <config>to check the configuration on its own. - An unhandled failure prints
Unexpected error:with a message and an issue link. That banner means Sovrium did not anticipate the failure — copy the message into the report. - Search the docs with
⌘K, or open a GitHub issue or discussion.
Related Pages
- Troubleshooting: Startup & Config — errors before the server is up.
- Environment Variables — every variable Sovrium reads.
- Security Hardening — secrets, rotation, and deployment posture.
- MCP Integration — connecting an MCP client end to end.
Last updated September 1, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.