EMStool LLC — Cadence EMS Scheduler

Encryption Key & Credential Rotation Procedure

Document #: KRP-2026-01 Version: 1.2 Effective: June 29, 2026 (rev. July 16, 2026) Owner: Jacob Russell, Security Officer
This procedure defines the schedule and steps for rotating cryptographic keys, passwords, tokens, and other credentials used in Cadence infrastructure. Regular rotation limits the damage from undetected credential compromise.

1. Rotation Schedule

Credential / KeyRotation FrequencyTrigger for Immediate Rotation
MariaDB table-encryption master key (keyfile)AnnuallySuspected compromise of the keyfile or host — highest-priority key, protects all patient PHI at rest
Database root password (MariaDB)AnnuallySuspected breach; personnel change
Cadence .env DB_PASSAnnuallySuspected breach; shared credential exposure
Cloudflare API TokenAnnuallyToken exposure; access revocation needed
Cloudflare Tunnel TokenOn compromise suspicionUnauthorized tunnel access detected
Resend API KeyAnnuallyUnexpected usage spikes; suspected leak
Backblaze B2 Application KeyAnnuallySuspected exposure
GPG Backup Encryption KeyEvery 2 yearsKey material suspected compromised
PHP Session Secret (SESSION_NAME)AnnuallySession fixation attack detected
Cadence user API keys (per-user)On user request or departureUser reports key compromise
Server SSH keysOn personnel changeSSH key suspected compromised

2. MariaDB Table-Encryption Master Key Rotation

This is the keyfile at /mnt/user/appdata/mariadb-<instance>/config/encryption/keyfile that the file_key_management plugin uses to encrypt every InnoDB tablespace (all patient PHI tables, per migration 0168_encrypt_all_tables.sql). This is the single highest-value key in the system — compromise means every table it protects is exposed. Rotation is more involved than a password change: the old key must remain available and valid until every page it encrypted has been re-encrypted under the new key, or that data becomes permanently unreadable.

  1. Generate new key material and add it as a new key version in the keyfile — do not remove or overwrite the existing key entry. Consult the current MariaDB version's file_key_management plugin documentation for the exact keyfile format in use, since this has changed across MariaDB releases.
  2. Deploy the updated keyfile (with both old and new key versions present) to the instance, matching file mode/ownership of the existing file exactly.
  3. Reload or restart the MariaDB instance so the plugin picks up the new key version. Confirm via SHOW STATUS LIKE 'Innodb_encryption%' / INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION that the server recognizes the new key version.
  4. Set (or confirm) innodb_encryption_rotate_key_age so the background encryption threads re-encrypt existing tablespace pages onto the new key version over time, rather than requiring a manual full re-encrypt.
  5. Monitor INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION until all tablespaces report the new key version (`CURRENT_KEY_VERSION`) as current — do not remove the old key version from the keyfile until this is confirmed for every table.
  6. Only after full re-encryption is confirmed, retire the old key version from the keyfile on the next maintenance window.
  7. Test this entire procedure end-to-end on a disposable throwaway container (see the Restore Test methodology in the Business Continuity Plan) before ever running it against any deployment holding real patient data — a mistake here (removing the old key too early) is not recoverable.

3. Database Password Rotation

Each client deployment runs its own isolated MariaDB container alongside its own SWAG reverse proxy, on that client's own hardware — repeat this procedure per deployment, substituting the container names and host address for that client.

  1. SSH into the deployment's host
  2. Enter MariaDB: docker exec -it MariaDB-<instance> mariadb -u root -p
  3. Generate new password (minimum 32 characters, mixed case + special)
  4. Execute: ALTER USER 'root'@'%' IDENTIFIED BY 'new_password';
  5. Update /mnt/user/appdata/swag-<instance>/webroot/.env — set DB_PASS to new password
  6. Restart the instance's SWAG container to reload env: docker restart swag-<instance>
  7. Verify application connects successfully
  8. Update this document with rotation date

4. Cloudflare API Token Rotation

  1. Log in to Cloudflare dashboard → Profile → API Tokens
  2. Create a new token with identical permissions to the existing token
  3. Update all locations referencing the old token (tunnel config, scripts, GitHub Actions secrets)
  4. Verify all automated processes work with the new token
  5. Revoke the old token
  6. Update saved credentials in secure storage

5. Session Secret Rotation

  1. Edit .env file: update SESSION_NAME to a new unique string
  2. Deploy the updated .env
  3. All existing user sessions will be invalidated — users must re-login
  4. Send advance notice to users if possible (rotating outside incident context)

6. GPG Backup Key Rotation

  1. Generate new GPG keypair: gpg --gen-key
  2. Export public key and store securely off-site
  3. Update backup scripts to use new key fingerprint
  4. Retain old private key securely — needed to decrypt existing backups
  5. Verify new backups decrypt with new key before discarding old key
  6. After 30 days (full backup retention window), old key can be archived

7. User API Key Rotation

Users can manage their own API keys from Cadence Settings → API Keys. Administrators can see which keys exist but cannot view the actual key values. On departure or compromise:

  1. User revokes their own keys at Settings → API Keys → Revoke
  2. Administrator can verify all keys revoked via admin audit log
  3. User generates a new key if continued API access is needed

8. Post-Rotation Verification Checklist

9. Rotation Log

DateCredential RotatedRotated ByReason
Jun 29, 2026Initial documentation of rotation scheduleJacob RussellPolicy establishment
Security Officer Signature
Jacob Russell — Security Officer, EMStool LLC
Date Reviewed
June 29, 2026