package settings // ── Decision 50 (2026-09-30, R-720): a new app is in the off-site copy by default ────────────────────── // // The 2026-09-16 ruling turned the off-site tier ON for every new customer, but each app's own switch // (`app_backup[].offbox`) started OFF and only the backups page's „Bekapcsolás" set it. Measured on a // fresh box 2026-09-29: three apps installed, every one „3. mentés Kikapcsolva", the night's off-site run // „nincs kijelölt alkalmazás". The household was never told. // // DefaultOffboxOnForNewApp is called by the deploy-done hook (the one place a fresh install ends). It switches // the app ON when, and only when: // - an off-site target exists and is enabled (the customer has off-site — hub-provisioned or the // household's own NAS), and // - the app has NO per-app backup record yet. A record means someone already chose: an app removed // WITHOUT deleting its backups keeps its record (R-474), so a reinstall keeps the household's earlier // OFF. An app removed WITH its backups had its record deleted, so it is a new app again. // // It never turns anything OFF and never touches an app that is already installed (a release switches // nothing by itself — the 2026-09-29 Part 0 rule). // Pinned by internal/settings/offbox_default_test.go. func (s *Settings) DefaultOffboxOnForNewApp(stackName string) (bool, string) { s.mu.Lock() defer s.mu.Unlock() if s.Offbox == nil || !s.Offbox.Enabled || s.Offbox.Host == "" { return false, "no off-site target" } if _, chosen := s.AppBackup[stackName]; chosen { return false, "the app already has a backup choice" } if s.AppBackup == nil { s.AppBackup = make(map[string]AppBackupPrefs) } s.AppBackup[stackName] = AppBackupPrefs{Offbox: true} if err := s.save(); err != nil { delete(s.AppBackup, stackName) return false, "save failed: " + err.Error() } return true, "" } // AppsWithoutOffbox lists the given installed apps that are NOT in the off-site copy, when an off-site target // exists (decision 50: the one-press offer for apps installed before the release). Empty when there is no // target, or every app is on. func (s *Settings) AppsWithoutOffbox(installed []string) []string { s.mu.RLock() defer s.mu.RUnlock() if s.Offbox == nil || !s.Offbox.Enabled || s.Offbox.Host == "" { return nil } var out []string for _, n := range installed { if !s.AppBackup[n].Offbox { out = append(out, n) } } return out } // EnableOffboxFor switches off-site ON for each named app in one save (the one-press offer). func (s *Settings) EnableOffboxFor(apps []string) error { s.mu.Lock() defer s.mu.Unlock() if s.AppBackup == nil { s.AppBackup = make(map[string]AppBackupPrefs) } for _, a := range apps { p := s.AppBackup[a] p.Offbox = true s.AppBackup[a] = p } s.OffboxOfferDismissed = true // answered return s.save() } // DismissOffboxOffer records the household's „Nem most". func (s *Settings) DismissOffboxOffer() error { s.mu.Lock() defer s.mu.Unlock() s.OffboxOfferDismissed = true return s.save() } // OffboxOfferDismissedValue reports whether the offer was answered. func (s *Settings) OffboxOfferDismissedValue() bool { s.mu.RLock() defer s.mu.RUnlock() return s.OffboxOfferDismissed }