// hubdb-check — open a RESTORED copy of hub.db with the seal key and report, as counts only, whether every vaulted // console password opens (R-173, runbooks/RUNBOOK-hub-db-offsite-backup.md §3, step 2). // // Usage: hubdb-check // // Run it on a scratch COPY, never on the live database: opening runs the store's migrations. It prints no secret — // only the number of hosts and of console passwords that opened, failed or are absent. Exit 0 only when every // vaulted console password opened, at least one host exists and at least one password was vaulted; a wrong key // fails every row (the seal is AES-GCM, authenticated). Pinned by main_test.go. package main import ( "fmt" "io" "log" "os" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) type result struct{ hosts, opened, failed, absent int } func check(dbPath, keyFile string) (result, error) { var r result raw, err := os.ReadFile(keyFile) if err != nil { return r, fmt.Errorf("reading the key file: %w", err) } key, err := store.ParseOffsiteSecretKey(string(raw)) if err != nil { return r, fmt.Errorf("the key file does not hold a valid OFFSITE_SECRET_KEY: %w", err) } s, err := store.New(dbPath, log.New(io.Discard, "", 0)) if err != nil { return r, err } defer s.Close() if err := s.SetOffsiteSecretKey(key); err != nil { return r, err } hosts, err := s.ListHosts() if err != nil { return r, err } r.hosts = len(hosts) for _, h := range hosts { c, err := s.GetHostRecoveryCredential(h.HostID) switch { case err != nil: r.failed++ case c == nil: r.absent++ default: r.opened++ } } return r, nil } func main() { if len(os.Args) != 3 { fmt.Fprintln(os.Stderr, "usage: hubdb-check ") os.Exit(2) } r, err := check(os.Args[1], os.Args[2]) if err != nil { fmt.Fprintln(os.Stderr, "hubdb-check: FAILED:", err) os.Exit(1) } fmt.Printf("hosts=%d console_passwords_opened=%d failed=%d absent=%d\n", r.hosts, r.opened, r.failed, r.absent) if r.hosts == 0 || r.opened == 0 || r.failed > 0 { fmt.Fprintln(os.Stderr, "hubdb-check: FAILED: not every console password opened with this key") os.Exit(1) } }