diff --git a/scripts/migrate-demo-edge01.sh b/scripts/migrate-demo-edge01.sh index 5c9521e0e..af4918879 100755 --- a/scripts/migrate-demo-edge01.sh +++ b/scripts/migrate-demo-edge01.sh @@ -329,7 +329,8 @@ readonly USERS_COLS="id, username, email, password_hash, role, created_at, updat height_cm, date_of_birth, gender, timezone, codex_access_token_encrypted, codex_refresh_token_encrypted, codex_token_expires_at, codex_connected_at, codex_connection_status, - insights_privacy_mode, insights_cached_at, insights_cached_text, insights_exclude_metrics, + insights_privacy_mode, insights_cached_at, insights_cached_text, insights_cached_text_encrypted, + insights_exclude_metrics, global_excluded_injection_sites, telegram_bot_token, telegram_chat_id, telegram_enabled, withings_client_id_encrypted, withings_client_secret_encrypted, @@ -361,7 +362,8 @@ readonly USERS_SELECT="id, username, email, password_hash, NULL::timestamptz AS codex_token_expires_at, NULL::timestamptz AS codex_connected_at, 'disconnected' AS codex_connection_status, - insights_privacy_mode, insights_cached_at, insights_cached_text, insights_exclude_metrics, + insights_privacy_mode, insights_cached_at, insights_cached_text, insights_cached_text_encrypted, + insights_exclude_metrics, global_excluded_injection_sites, NULL::text AS telegram_bot_token, NULL::text AS telegram_chat_id, diff --git a/scripts/seed-demo.ts b/scripts/seed-demo.ts index 6e13de3b0..57abb04a7 100644 --- a/scripts/seed-demo.ts +++ b/scripts/seed-demo.ts @@ -88,6 +88,7 @@ import { getKeyFingerprint, } from "../src/lib/crypto"; import { ENVIRONMENT_LOCATION_AAD } from "../src/lib/crypto/encrypted-columns"; +import { sealInsightsCachedText } from "../src/lib/insights/cached-text-cipher"; import { DEFAULT_DASHBOARD_LAYOUT, serializeDashboardLayout, @@ -3343,7 +3344,8 @@ async function seed() { const bakedProvider = "anthropic"; // ── Comprehensive insight + daily briefing ── - // Stored on users.insights_cached_text (JSON). The GET read parses it and + // Stored sealed on users.insights_cached_text_encrypted (JSON). The GET + // read opens and parses it and // returns it as-is; setting insights_cached_at = NOW() keeps it inside the // 24 h freshness window for the dashboard-snapshot briefing path. The shape // is the union of insightResultSchema (summary / findings / correlations / @@ -3527,14 +3529,19 @@ async function seed() { await client.query( `UPDATE users - SET insights_cached_text = $2, + SET insights_cached_text_encrypted = $2, + insights_cached_text = NULL, insights_cached_at = NOW(), insights_snapshot_hash = $3, insights_briefing_reroll_date = $4 WHERE id = $1`, [ userId, - JSON.stringify(comprehensiveInsight), + // Sealed like every write since v1.43.7; the app never reads the + // readable column while a ciphertext is there. + Buffer.from( + sealInsightsCachedText(JSON.stringify(comprehensiveInsight)), + ), // A stable fingerprint. The 24 h cache window keeps the row fresh on // its own; this value just has to be present + non-null so a // hypothetical regeneration would treat the snapshot as known. The