← All posts
Aug 1, 2026

Rotating Your Credential Vault's Encryption Keys Without Downtime in Weavestream

Weavestream supports zero-downtime rotation for both JWT signing keys and password encryption keys, with envelope encryption and a bulk re-encrypt CLI — key hygiene for a self-hosted MSP credential vault without a maintenance window.

Most self-hosted tools treat encryption keys as something you set once at install time and never touch again. That’s fine right up until a technician who had root on the box leaves the company, an .env file ends up in a place it shouldn’t, or an auditor asks when you last rotated the keys protecting your client credential vault. “Never” is not an answer that survives a SOC 2 review or a cyber-insurance questionnaire.

Weavestream builds key rotation into the platform itself — both for JWT signing keys (which authenticate every API request) and for the password encryption keys that protect your credential vault. Neither requires downtime, and neither requires you to write your own re-encryption tooling.


Why This Matters More for an MSP Than It Sounds

A self-hosted IT documentation platform holding every client’s credentials is a high-value target, and the encryption key protecting that vault is the single point of failure if it’s ever exposed. “Rotate keys periodically” is a standard line item in security frameworks (PCI-DSS, SOC 2, ISO 27001) precisely because keys leak in mundane ways — a departing employee’s laptop, a misconfigured backup, a screenshot in a support ticket. Rotation limits the blast radius: even if an old key is compromised, it stops being useful once you’ve moved on from it.

The practical problem with rotation is that it’s usually painful enough that nobody does it. If rotating a key means a maintenance window, a service restart that breaks active sessions, and a script you have to write yourself to re-encrypt every row in the database, it gets deprioritized indefinitely. Weavestream’s approach removes most of those excuses.


Envelope Encryption: The Foundation

Weavestream’s password vault uses envelope encryption. Every credential secret, TOTP seed, and encrypted note is protected with AES-256-GCM, and — critically — every ciphertext blob is stamped with the ID (kid) of the key that encrypted it. That stamp is what makes rotation non-destructive: the application always knows which key to reach for when decrypting a given blob, even if that key is no longer the current one.

This is the same pattern used by cloud KMS providers, just running entirely on your own infrastructure. You’re not trusting a third party to manage the keys — you generate them, store them in your own .env, and control the rotation schedule yourself.


Rotating the Password Encryption Key

The password encryption key protects everything in the credential vault: passwords, TOTP secrets, and encrypted notes. Rotating it is a five-step process:

  1. Generate a new key with the bundled ./scripts/keygen.sh script.
  2. Update .env — move the current key into PASSWORD_PREVIOUS_KEYS (tagged with its kid), then set PASSWORD_ENCRYPTION_KEY and a new PASSWORD_ENCRYPTION_KEY_KID.
  3. Restart the stack (docker compose up -d). New passwords are immediately encrypted under the new key; existing ones keep decrypting fine via the previous-keys list.
  4. Bulk re-encrypt with docker compose exec api node dist/cli.js reencrypt-passwords to migrate every existing ciphertext blob to the new key in one pass, instead of waiting for each record’s next natural update.
  5. Remove the old key from PASSWORD_PREVIOUS_KEYS once you’ve confirmed the bulk re-encryption finished.

Nothing in that sequence requires taking the vault offline. Technicians can keep reading and writing credentials the entire time — old blobs decrypt against the previous key, new writes use the current one, and the bulk CLI closes the gap without waiting on individual record edits.


Rotating the JWT Signing Key

JWTs are what authenticate every API request after login. Rotating the signing key follows the same pattern:

  1. Generate a new key (openssl rand -base64 32 works fine).
  2. Move the old key into JWT_PREVIOUS_KEYS tagged with its kid, and set a new JWT_SIGNING_KEY / JWT_SIGNING_KEY_KID.
  3. Restart the stack.
  4. Wait out SESSION_MAX_AGE_DAYS so any tokens signed under the old key have expired, then drop the old key from JWT_PREVIOUS_KEYS.

During the transition, new tokens are signed with the current key, but incoming requests are still verified against every key listed in JWT_PREVIOUS_KEYS if verification against the current key fails. No one gets logged out mid-rotation, and session revocation — which is tracked server-side, not just via JWT expiry — keeps working the entire time.


What’s Not Fully Automated Yet

Weavestream is upfront about one gap: the MFA_ENCRYPTION_KEY, which protects TOTP secrets used for account MFA (a separate concern from TOTP secrets stored in the password vault, which rotate along with the password encryption key), doesn’t yet have an automated re-encryption CLI. Rotating it means re-encrypting every user’s MFA secret, and today that requires reaching out to the project maintainers rather than running a one-line command. Worth knowing before you plan a rotation that assumes full automation across the board.


Coordinated Database Credential Rotation

Key rotation usually travels with a broader housekeeping task: rotating the Postgres and Redis passwords themselves. Weavestream documents this as a short, coordinated sequence — stop the stack, update the password in .env and the connection strings, change the actual database role password inside Postgres via ALTER ROLE, then restart. It’s not zero-downtime like the encryption key rotations above (the stack has to come down briefly), but it’s a documented, repeatable procedure rather than something you have to reverse-engineer from Postgres docs under pressure.


A Rotation Checklist That Fits an Actual Runbook

Weavestream’s docs distill the whole process into a checklist you can drop straight into an internal runbook or a compliance binder:

  • Generate the new key
  • Add the old key to the relevant *_PREVIOUS_KEYS variable
  • Bump the *_KID variable
  • docker compose up -d
  • Verify the stack is healthy
  • For password keys: run reencrypt-passwords
  • After the expiry/grace window: remove the old key from *_PREVIOUS_KEYS
  • docker compose up -d again to load the trimmed key list

That’s the difference between “we should rotate our keys” staying a permanent aspiration and it actually becoming a quarterly (or post-offboarding) task someone on your team can execute in fifteen minutes without breaking anything.


Why Self-Hosting Makes This Better, Not Worse

It’s tempting to assume a SaaS vendor handles key rotation for you so you don’t have to think about it. In practice, most MSPs never see any evidence of a vendor’s rotation practices — you’re trusting a black box. Self-hosting flips that: the rotation procedure is documented, the keys are yours, and you can prove to an auditor exactly when a key was last rotated because you’re the one who ran the command.


Getting Started

If you’re already running Weavestream, the full step-by-step lives in the Key Rotation guide — it’s worth reading in full before your first rotation, particularly the reencrypt-passwords --force flag if you’re migrating off an older key format.

New to Weavestream? The quickstart guide gets the stack running with Docker Compose. Generating your initial PASSWORD_ENCRYPTION_KEY and JWT_SIGNING_KEY is part of that setup — worth noting the kid conventions from day one so your first real rotation isn’t also the first time you’ve touched the key IDs.

← All posts