fix(recovery): the database dumps count in the keys the backups still need

The recovery kit, "Keys the backups still need" and safeToDropRetiredKeys
read only the stored copies and the off-host account objects. After a
rotation the page called the old key safe to drop while every dump taken
before it, the pre-migration dumps that are the way back from an update
among them, could only be restored with that key: db-restore refuses a dump
whose active key is missing.

A dump now counts for the key its rows were written under, the manifest's
active key: a dump on this server while it is kept (read from the manifests),
an off-host one until the bucket's rule for database/ expires it, or
indefinitely when that rule cannot be read. The nightly run records the key
on its ledger row (migration 0489, database_backup_runs.active_key_id). The
encryption view and the kit list the dumps per key, in every locale, and the
rotation and backup runbooks say so.
This commit is contained in:
Marc-André Bombeck committed 2026-10-25 17:20:16 +01:00
1 parent 40c310f0ad
commit 26a9efdf52
24 files changed
+422 -45

No files matched your search

+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Tabelle ausblenden",
"columnsShowAll": "Alle {count} Spalten anzeigen",
"columnsShowWithRows": "Nur Spalten mit Zeilen",
"columnsNoneWithRows": "Noch keine verschlüsselte Spalte enthält Zeilen."
"columnsNoneWithRows": "Noch keine verschlüsselte Spalte enthält Zeilen.",
"backupsDumpLocalLine": "Schlüssel {key}: {count} Datenbank-Dumps auf diesem Server wurden unter ihm geschrieben; er wird gebraucht, bis die Aufbewahrung sie entfernt.",
"backupsDumpOffhostLine": "Schlüssel {key}: Der neueste Datenbank-Dump, der unter ihm geschrieben wurde, kam am {when} in den Bucket; dort gebraucht bis {until}",
"backupsDumpOffhostLineUnknown": "Schlüssel {key}: Der neueste Datenbank-Dump, der unter ihm geschrieben wurde, kam am {when} in den Bucket. Wie lange der Bucket database/ behält, ist unbekannt; behalte den Schlüssel, bis dieser Dump sicher weg ist."
},
"maintenance": {
"title": "Pflege der Messwert-Tabelle",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Wird von externen Kopien gebraucht, solange der Bucket sie aufbewahrt.",
"backupActive": "Versiegelt jede neue externe Kopie.",
"backupPrevious": "Öffnet externe Kopien, die vor dem letzten Schlüsselwechsel geschrieben wurden.",
"token": "Signiert Sitzungen, Tokens und Freigabelinks. Ohne ihn werden alle abgemeldet, Daten gehen aber nicht verloren."
"token": "Signiert Sitzungen, Tokens und Freigabelinks. Ohne ihn werden alle abgemeldet, Daten gehen aber nicht verloren.",
"localDumps": "Wird von {count} Datenbank-Dumps auf diesem Server gebraucht.",
"offhostDumpsUntil": "Wird von externen Datenbank-Dumps bis {date} gebraucht.",
"offhostDumpsUnknown": "Wird von externen Datenbank-Dumps gebraucht, solange der Bucket sie aufbewahrt."
},
"source": "Gesetzt in {variable}.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Hide the table",
"columnsShowAll": "Show all {count} columns",
"columnsShowWithRows": "Only columns with rows",
"columnsNoneWithRows": "No encrypted column holds rows yet."
"columnsNoneWithRows": "No encrypted column holds rows yet.",
"backupsDumpLocalLine": "Key {key}: {count} database dumps on this server were written under it; it is needed until retention removes them.",
"backupsDumpOffhostLine": "Key {key}: the newest database dump written under it went into the bucket on {when}; needed there until {until}",
"backupsDumpOffhostLineUnknown": "Key {key}: the newest database dump written under it went into the bucket on {when}. How long the bucket keeps database/ is unknown, so keep the key until you know that dump is gone."
},
"maintenance": {
"title": "Measurement table maintenance",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Needed by off-host copies for as long as the bucket keeps them.",
"backupActive": "Seals every new off-host copy.",
"backupPrevious": "Opens off-host copies written before the last key change.",
"token": "Signs sessions, tokens and share links. Without it everyone is signed out, but no data is lost."
"token": "Signs sessions, tokens and share links. Without it everyone is signed out, but no data is lost.",
"localDumps": "Needed by {count} database dumps on this server.",
"offhostDumpsUntil": "Needed by off-host database dumps until {date}.",
"offhostDumpsUnknown": "Needed by off-host database dumps for as long as the bucket keeps them."
},
"source": "Set in {variable}.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Ocultar la tabla",
"columnsShowAll": "Mostrar las {count} columnas",
"columnsShowWithRows": "Solo columnas con filas",
"columnsNoneWithRows": "Ninguna columna cifrada contiene filas todavía."
"columnsNoneWithRows": "Ninguna columna cifrada contiene filas todavía.",
"backupsDumpLocalLine": "Clave {key}: {count} volcados de la base de datos en este servidor se escribieron con ella; se necesita hasta que la retención los elimine.",
"backupsDumpOffhostLine": "Clave {key}: el volcado de la base de datos más reciente escrito con ella entró en el bucket el {when}; se necesita allí hasta el {until}",
"backupsDumpOffhostLineUnknown": "Clave {key}: el volcado de la base de datos más reciente escrito con ella entró en el bucket el {when}. No se sabe cuánto tiempo conserva el bucket database/, así que conserva la clave hasta saber que ese volcado ya no está."
},
"maintenance": {
"title": "Mantenimiento de la tabla de mediciones",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "La necesitan las copias fuera del host mientras el bucket las conserve.",
"backupActive": "Sella cada nueva copia fuera del host.",
"backupPrevious": "Abre las copias fuera del host escritas antes del último cambio de clave.",
"token": "Firma sesiones, tokens y enlaces para compartir. Sin ella se cierra la sesión de todo el mundo, pero no se pierden datos."
"token": "Firma sesiones, tokens y enlaces para compartir. Sin ella se cierra la sesión de todo el mundo, pero no se pierden datos.",
"localDumps": "La necesitan {count} volcados de la base de datos en este servidor.",
"offhostDumpsUntil": "La necesitan los volcados de la base de datos fuera del host hasta el {date}.",
"offhostDumpsUnknown": "La necesitan los volcados de la base de datos fuera del host mientras el bucket los conserve."
},
"source": "Definida en {variable}.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Masquer le tableau",
"columnsShowAll": "Afficher les {count} colonnes",
"columnsShowWithRows": "Seulement les colonnes avec des lignes",
"columnsNoneWithRows": "Aucune colonne chiffrée ne contient encore de lignes."
"columnsNoneWithRows": "Aucune colonne chiffrée ne contient encore de lignes.",
"backupsDumpLocalLine": "Clé {key} : {count} dumps de la base de données sur ce serveur ont été écrits avec elle ; elle est nécessaire jusqu'à ce que la rétention les supprime.",
"backupsDumpOffhostLine": "Clé {key} : le dump de la base de données le plus récent écrit avec elle est arrivé dans le bucket le {when} ; nécessaire jusqu'au {until}",
"backupsDumpOffhostLineUnknown": "Clé {key} : le dump de la base de données le plus récent écrit avec elle est arrivé dans le bucket le {when}. La durée de conservation de database/ dans le bucket est inconnue : gardez la clé jusqu'à être sûr que ce dump a disparu."
},
"maintenance": {
"title": "Maintenance de la table des mesures",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Nécessaire aux copies hors hôte tant que le bucket les conserve.",
"backupActive": "Scelle chaque nouvelle copie hors hôte.",
"backupPrevious": "Ouvre les copies hors hôte écrites avant le dernier changement de clé.",
"token": "Signe les sessions, les jetons et les liens de partage. Sans elle, tout le monde est déconnecté, mais aucune donnée n'est perdue."
"token": "Signe les sessions, les jetons et les liens de partage. Sans elle, tout le monde est déconnecté, mais aucune donnée n'est perdue.",
"localDumps": "Nécessaire à {count} dumps de la base de données sur ce serveur.",
"offhostDumpsUntil": "Nécessaire aux dumps de la base de données hors hôte jusqu'au {date}.",
"offhostDumpsUnknown": "Nécessaire aux dumps de la base de données hors hôte tant que le bucket les conserve."
},
"source": "Définie dans {variable}.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Nascondi la tabella",
"columnsShowAll": "Mostra tutte le {count} colonne",
"columnsShowWithRows": "Solo colonne con righe",
"columnsNoneWithRows": "Nessuna colonna cifrata contiene ancora righe."
"columnsNoneWithRows": "Nessuna colonna cifrata contiene ancora righe.",
"backupsDumpLocalLine": "Chiave {key}: {count} dump del database su questo server sono stati scritti con essa; serve finché la conservazione non li rimuove.",
"backupsDumpOffhostLine": "Chiave {key}: il dump del database più recente scritto con essa è entrato nel bucket il {when}; serve lì fino al {until}",
"backupsDumpOffhostLineUnknown": "Chiave {key}: il dump del database più recente scritto con essa è entrato nel bucket il {when}. Non si sa per quanto il bucket conservi database/, quindi conserva la chiave finché non sai che quel dump non c'è più."
},
"maintenance": {
"title": "Manutenzione della tabella delle misurazioni",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Serve alle copie fuori host finché il bucket le conserva.",
"backupActive": "Sigilla ogni nuova copia fuori host.",
"backupPrevious": "Apre le copie fuori host scritte prima dell'ultimo cambio di chiave.",
"token": "Firma sessioni, token e link di condivisione. Senza di essa tutti vengono disconnessi, ma nessun dato va perso."
"token": "Firma sessioni, token e link di condivisione. Senza di essa tutti vengono disconnessi, ma nessun dato va perso.",
"localDumps": "Serve a {count} dump del database su questo server.",
"offhostDumpsUntil": "Serve ai dump del database fuori host fino al {date}.",
"offhostDumpsUnknown": "Serve ai dump del database fuori host finché il bucket li conserva."
},
"source": "Impostata in {variable}.",
"status": {
+8 -2
View File
@@ -8393,7 +8393,10 @@
"columnsHide": "표 숨기기",
"columnsShowAll": "열 {count}개 모두 보기",
"columnsShowWithRows": "행이 있는 열만",
"columnsNoneWithRows": "아직 행이 있는 암호화 열이 없어요."
"columnsNoneWithRows": "아직 행이 있는 암호화 열이 없어요.",
"backupsDumpLocalLine": "키 {key}: 이 서버의 데이터베이스 덤프 {count}개가 이 키로 작성되었습니다. 보존 정책이 덤프를 지울 때까지 필요합니다.",
"backupsDumpOffhostLine": "키 {key}: 이 키로 작성된 최신 데이터베이스 덤프가 {when}에 버킷에 저장되었습니다. {until}까지 필요합니다",
"backupsDumpOffhostLineUnknown": "키 {key}: 이 키로 작성된 최신 데이터베이스 덤프가 {when}에 버킷에 저장되었습니다. 버킷이 database/를 얼마나 보관하는지 알 수 없으므로 그 덤프가 사라진 것을 확인할 때까지 키를 유지하세요."
},
"maintenance": {
"title": "측정값 테이블 유지 관리",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "버킷이 외부 보관 사본을 보관하는 동안 필요합니다.",
"backupActive": "새 외부 보관 사본을 모두 봉인합니다.",
"backupPrevious": "마지막 키 변경 전에 기록된 외부 보관 사본을 엽니다.",
"token": "세션, 토큰, 공유 링크에 서명합니다. 이 키가 없으면 모두 로그아웃되지만 데이터는 잃지 않습니다."
"token": "세션, 토큰, 공유 링크에 서명합니다. 이 키가 없으면 모두 로그아웃되지만 데이터는 잃지 않습니다.",
"localDumps": "이 서버의 데이터베이스 덤프 {count}개에 필요합니다.",
"offhostDumpsUntil": "{date}까지 외부 보관 데이터베이스 덤프에 필요합니다.",
"offhostDumpsUnknown": "버킷이 외부 보관 데이터베이스 덤프를 보관하는 동안 필요합니다."
},
"source": "{variable}에 설정됨.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Tabel verbergen",
"columnsShowAll": "Alle {count} kolommen tonen",
"columnsShowWithRows": "Alleen kolommen met rijen",
"columnsNoneWithRows": "Nog geen enkele versleutelde kolom bevat rijen."
"columnsNoneWithRows": "Nog geen enkele versleutelde kolom bevat rijen.",
"backupsDumpLocalLine": "Sleutel {key}: {count} databasedumps op deze server zijn onder deze sleutel geschreven; hij is nodig tot de bewaartermijn ze verwijdert.",
"backupsDumpOffhostLine": "Sleutel {key}: de nieuwste databasedump onder deze sleutel is op {when} in de bucket gezet; daar nodig tot {until}",
"backupsDumpOffhostLineUnknown": "Sleutel {key}: de nieuwste databasedump onder deze sleutel is op {when} in de bucket gezet. Hoe lang de bucket database/ bewaart, is onbekend, dus bewaar de sleutel tot je zeker weet dat die dump weg is."
},
"maintenance": {
"title": "Onderhoud van de metingentabel",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Nodig voor externe kopieën zolang de bucket ze bewaart.",
"backupActive": "Verzegelt elke nieuwe externe kopie.",
"backupPrevious": "Opent externe kopieën die vóór de laatste sleutelwissel zijn geschreven.",
"token": "Ondertekent sessies, tokens en deellinks. Zonder deze sleutel wordt iedereen afgemeld, maar gaan er geen gegevens verloren."
"token": "Ondertekent sessies, tokens en deellinks. Zonder deze sleutel wordt iedereen afgemeld, maar gaan er geen gegevens verloren.",
"localDumps": "Nodig voor {count} databasedumps op deze server.",
"offhostDumpsUntil": "Nodig voor externe databasedumps tot {date}.",
"offhostDumpsUnknown": "Nodig voor externe databasedumps zolang de bucket ze bewaart."
},
"source": "Ingesteld in {variable}.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Ukryj tabelę",
"columnsShowAll": "Pokaż wszystkie kolumny ({count})",
"columnsShowWithRows": "Tylko kolumny z wierszami",
"columnsNoneWithRows": "Żadna zaszyfrowana kolumna nie zawiera jeszcze wierszy."
"columnsNoneWithRows": "Żadna zaszyfrowana kolumna nie zawiera jeszcze wierszy.",
"backupsDumpLocalLine": "Klucz {key}: {count} zrzutów bazy danych na tym serwerze zapisano pod nim; jest potrzebny, dopóki retencja ich nie usunie.",
"backupsDumpOffhostLine": "Klucz {key}: najnowszy zrzut bazy danych zapisany pod nim trafił do bucketu {when}; potrzebny tam do {until}",
"backupsDumpOffhostLineUnknown": "Klucz {key}: najnowszy zrzut bazy danych zapisany pod nim trafił do bucketu {when}. Nie wiadomo, jak długo bucket przechowuje database/, więc zachowaj klucz, dopóki nie masz pewności, że tego zrzutu już nie ma."
},
"maintenance": {
"title": "Konserwacja tabeli pomiarów",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Potrzebny kopiom poza hostem, dopóki bucket je przechowuje.",
"backupActive": "Pieczętuje każdą nową kopię poza hostem.",
"backupPrevious": "Otwiera kopie poza hostem zapisane przed ostatnią zmianą klucza.",
"token": "Podpisuje sesje, tokeny i linki udostępniania. Bez niego wszyscy zostaną wylogowani, ale żadne dane nie przepadną."
"token": "Podpisuje sesje, tokeny i linki udostępniania. Bez niego wszyscy zostaną wylogowani, ale żadne dane nie przepadną.",
"localDumps": "Potrzebny zrzutom bazy danych na tym serwerze: {count}.",
"offhostDumpsUntil": "Potrzebny zrzutom bazy danych poza hostem do {date}.",
"offhostDumpsUnknown": "Potrzebny zrzutom bazy danych poza hostem, dopóki bucket je przechowuje."
},
"source": "Ustawiony w {variable}.",
"status": {
+8 -2
View File
@@ -8418,7 +8418,10 @@
"columnsHide": "Ocultar a tabela",
"columnsShowAll": "Mostrar todas as {count} colunas",
"columnsShowWithRows": "Somente colunas com linhas",
"columnsNoneWithRows": "Nenhuma coluna criptografada contém linhas ainda."
"columnsNoneWithRows": "Nenhuma coluna criptografada contém linhas ainda.",
"backupsDumpLocalLine": "Chave {key}: {count} dumps do banco de dados neste servidor foram gravados com ela; ela é necessária até que a retenção os remova.",
"backupsDumpOffhostLine": "Chave {key}: o dump do banco de dados mais recente gravado com ela foi para o bucket em {when}; necessária lá até {until}",
"backupsDumpOffhostLineUnknown": "Chave {key}: o dump do banco de dados mais recente gravado com ela foi para o bucket em {when}. Não se sabe por quanto tempo o bucket mantém database/, então mantenha a chave até saber que aquele dump se foi."
},
"maintenance": {
"title": "Manutenção da tabela de medições",
@@ -8604,7 +8607,10 @@
"offhostUnknown": "Necessária para as cópias fora do host enquanto o bucket as mantiver.",
"backupActive": "Sela cada nova cópia fora do host.",
"backupPrevious": "Abre as cópias fora do host gravadas antes da última troca de chave.",
"token": "Assina sessões, tokens e links de compartilhamento. Sem ela, todos são desconectados, mas nenhum dado é perdido."
"token": "Assina sessões, tokens e links de compartilhamento. Sem ela, todos são desconectados, mas nenhum dado é perdido.",
"localDumps": "Necessária para {count} dumps do banco de dados neste servidor.",
"offhostDumpsUntil": "Necessária para os dumps do banco de dados fora do host até {date}.",
"offhostDumpsUnknown": "Necessária para os dumps do banco de dados fora do host enquanto o bucket os mantiver."
},
"source": "Definida em {variable}.",
"status": {