Merge branch 'fix/backup-chunked-storage' into release/v1.39.2

This commit is contained in:
Marc-André Bombeck committed 2026-09-26 01:03:25 +02:00
commit d5a39603ca
43 files changed
+1912 -607

No files matched your search

+5
View File
@@ -292,6 +292,11 @@
"name": "TRUST_PROXY_HOPS",
"purpose": "Trusted reverse-proxy hop count; the client IP is read that many entries from the right of X-Forwarded-For.",
"example": "1"
},
{
"name": "BACKUP_MAX_STORED_MB",
"purpose": "Largest stored backup of one account the app keeps, in megabytes of encrypted copy (2048 by default). A storage limit, independent of the app's memory: a backup past it fails with a message naming this variable, and the previous copy stays.",
"example": "2048"
}
]
}
+15 -2
View File
@@ -863,14 +863,27 @@ async function main() {
// operator that zero meant safe to drop the old key, and every stored backup
// became undecryptable. Walked in bounded id-cursor batches because one row
// is an entire compressed account, and re-sealed WITHOUT reading the
// plaintext, so both stored envelopes (the plain backup JSON and the
// `HLZ1:` gzip form) — and any later one — rotate unchanged.
// plaintext, so every single-value envelope (the plain backup JSON, the
// `HLZ1:` gzip form and the v1.39.1 `~hlgcm1.` stream) rotates unchanged.
results.push(
await rotateRegistryColumn("DataBackup", "data", {
dataBackup: prisma.dataBackup,
} as unknown as CorpusClient),
);
// ───── Whole-account backup in pieces (Bytes, binary2, batched) ─────
// From v1.39.2 a backup is stored as sealed pieces in "data_backup_chunks",
// one `encryptBytes()` value each, and the "data" column above holds only
// copies written before that. Walked in id-cursor batches of a few pieces
// (each is about a megabyte) and re-sealed without reading the backup: the
// header that ties a piece to its copy and position is inside the
// ciphertext, so it comes through rotation unchanged.
results.push(
await rotateRegistryColumn("DataBackupChunk", "data", {
dataBackupChunk: prisma.dataBackupChunk,
} as unknown as CorpusClient),
);
// ───── Idempotent-replay response cache (String, disposable) ─────
// `IdempotencyKey.responseBody` is the cached response body, encrypted
// because the PHI-returning creates echo their own decrypted DTO. Rotating