feat(v1.1.7-realtime-migration): encrypt signing keys at rest
Two-phase encryption of app_secrets.realtime_signing_key: - migration 0025 adds NULL-able realtime_signing_key_encrypted + _nonce columns and drops NOT NULL on the plaintext column. - PostgresAppSecretsRepo now holds the master key: new keys are written encrypted-only; reads prefer the encrypted columns and fall back to plaintext during the compat window. - Startup task migrate_plaintext_keys() encrypts any pre-existing plaintext rows (plaintext left in place for rollback safety). - v1.1.8 will drop the plaintext column. The RealtimeAuthority read path is unchanged (it calls signing_key), so SSE keeps working throughout. Unit tests cover the encrypted-wins / plaintext-fallback / post-drop precedence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -189,7 +189,23 @@ pub async fn build_app(
|
||||
let broadcaster_concrete = Arc::new(InProcessBroadcaster::from_env());
|
||||
let broadcaster: Arc<dyn RealtimeBroadcaster> = broadcaster_concrete.clone();
|
||||
let topic_repo: Arc<dyn TopicRepo> = Arc::new(PostgresTopicRepo::new(pool.clone()));
|
||||
let app_secrets_repo = Arc::new(PostgresAppSecretsRepo::new(pool.clone()));
|
||||
let app_secrets_repo = Arc::new(PostgresAppSecretsRepo::new(
|
||||
pool.clone(),
|
||||
master_key.clone(),
|
||||
));
|
||||
// v1.1.7 two-phase migration: encrypt any plaintext realtime signing
|
||||
// keys at rest. Idempotent — only touches rows not yet encrypted. The
|
||||
// plaintext column is dropped in v1.1.8.
|
||||
match app_secrets_repo.migrate_plaintext_keys().await {
|
||||
Ok(0) => {}
|
||||
Ok(n) => tracing::info!(
|
||||
migrated = n,
|
||||
"encrypted plaintext realtime signing keys at rest"
|
||||
),
|
||||
Err(e) => {
|
||||
tracing::error!(error = %e, "failed to encrypt realtime signing keys (continuing)")
|
||||
}
|
||||
}
|
||||
let realtime_authority: Arc<dyn RealtimeAuthority> = Arc::new(RealtimeAuthorityImpl::new(
|
||||
topic_repo.clone(),
|
||||
app_secrets_repo.clone(),
|
||||
|
||||
Reference in New Issue
Block a user