v0.275.0: a backup's data and its version travel together (R-696, 07 §6.6, D4 option A); R-695, R-691, R-694
gates / gates (push) Successful in 23s
gates / gates (push) Successful in 23s
The unit's data files are stamped with the versions that wrote them; the capture keeps the definition the data belongs to; a restore never starts data under another version's definition (unit restores refuse a mismatch; the off-site restore writes the snapshot's definition); every tier's time is its data's; the conversion-copy release needs a dump on the new engine. File-browser sync single-flight + no empty kept folder (R-695); the kept view joins the folder's owning group, language switch resyncs (R-691); a restore-generated login is not shown as the password (R-694). Red-proofs in felhom.eu/documentation/audits/version-travel-2026-09-26/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -183,6 +183,12 @@ type Settings struct {
|
||||
// Per-app backup preferences
|
||||
AppBackup map[string]AppBackupPrefs `json:"app_backup,omitempty"`
|
||||
|
||||
// OffsiteDataAt (v0.275.0, R-696) — per app, the DATA time of the recovery unit this box last pushed
|
||||
// off-site, and when. An off-site snapshot's own time is when it was TAKEN; a run whose dump leg failed
|
||||
// still pushes the older dumps, so the snapshot time can overstate its data. The update's precondition
|
||||
// caps the off-site copy's age with this record.
|
||||
OffsiteDataAt map[string]OffsiteDataRecord `json:"offsite_data_at,omitempty"`
|
||||
|
||||
// Customer-configurable backup-window start "HH:MM" (v0.168.0). "" = use controller.yaml
|
||||
// db_dump_schedule (then the "02:30" default). Every nightly leg derives from this at fixed
|
||||
// offsets; overrides yaml when a valid value is present (mirrors PasswordHash precedence).
|
||||
@@ -1202,6 +1208,31 @@ func (s *Settings) SetClaimConsumedGeneration(gen int) error {
|
||||
return s.save()
|
||||
}
|
||||
|
||||
// OffsiteDataRecord is one app's entry in Settings.OffsiteDataAt. RFC3339 UTC both.
|
||||
type OffsiteDataRecord struct {
|
||||
PushedAt string `json:"pushed_at"`
|
||||
DataAt string `json:"data_at"`
|
||||
}
|
||||
|
||||
// GetOffsiteDataAt returns the app's record, false when none was written.
|
||||
func (s *Settings) GetOffsiteDataAt(app string) (OffsiteDataRecord, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
r, ok := s.OffsiteDataAt[app]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// SetOffsiteDataAt records the app's pushed data time and persists to disk.
|
||||
func (s *Settings) SetOffsiteDataAt(app string, r OffsiteDataRecord) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.OffsiteDataAt == nil {
|
||||
s.OffsiteDataAt = make(map[string]OffsiteDataRecord)
|
||||
}
|
||||
s.OffsiteDataAt[app] = r
|
||||
return s.save()
|
||||
}
|
||||
|
||||
// GetDBValidations returns a copy of the cached DB validations.
|
||||
func (s *Settings) GetDBValidations() map[string]DBValidationCache {
|
||||
s.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user