fix(actual-budget): make the finance backup unable to succeed quietly #202

Merged
kblack0610 merged 1 commit from docs/finance-backup-restore into master 2026-08-25 14:47:00 +00:00
Owner

Yesterday's categorisation work on finance.kblab.me prompted the question of whether it is backed up. It is: verified end to end (see below). This PR fixes what I found while verifying.

Two ways the job could report success without a usable backup

  1. It tarred whatever was at /data without checking. An unmounted PVC would have written a small successful archive, and 30 days of that rotates the good local copies away. The job now refuses to run unless server-files/account.sqlite and at least one user-files/*.sqlite exist, and refuses to upload an archive under 20KB.

  2. smbclient -c does not exit non-zero when a command inside it fails - it prints NT_STATUS_* and carries on - so the unchecked && printed NAS copy OK for an upload that had gone to the share root or nowhere. This adopts the checked smb() wrapper already used in apps/zomboid/backup/backup.sh and adds a post-upload size check against the share.

The off-box copy stays best-effort by design (the local copy is the success condition, per the runbook). What changes is that the log now tells the truth about it. The guards in (1) exit non-zero, so KubeJobFailed pages.

Verification

Ran the real script in a container with a stubbed smbclient, against a real copy of the budget data:

Case Result
/data empty (PVC not mounted) exit 1, refuses
account.sqlite present, no budget file exit 1, refuses
happy path, real budget data exit 0, uploads and verifies 559596 bytes
cd fails, file lands in share root WARN (old code: NAS copy OK)
upload truncated WARN + size mismatch (old code: NAS copy OK)

And the current backup itself, independently of this change: today's 03:00 archive on the NAS unpacks clean, both SQLite files pass integrity_check, the budget file is registered, and it holds 483 category assignments with the newest at 2026-08-24T19:43 local - yesterday evening's session.

Docs

actual-budget was absent from the backup runbook entirely, so restoring finance data was undocumented. Adds:

  • the restore procedure, including that the budget is a CRDT sync log, so restoring account.sqlite alone gets a working login and an empty budget
  • a Verify a backup without restoring section (the checks above, reusable for any SQLite-backed app here)
  • the six CronJobs missing from the schedule table. karakeep-backup is local-only with no off-box copy, which the table now says.

Not addressed here

  • No offsite tier. Every copy in the table is in the house. Noted in the doc, not fixed.
  • The same unchecked smbclient pattern is still in the home-assistant, immich, litellm, vaultwarden and tax-export jobs. Left alone rather than changing five jobs' reporting in a PR about finance.
Yesterday's categorisation work on finance.kblab.me prompted the question of whether it is backed up. It is: verified end to end (see below). This PR fixes what I found while verifying. ## Two ways the job could report success without a usable backup 1. It tarred whatever was at `/data` without checking. An unmounted PVC would have written a small successful archive, and 30 days of that rotates the good local copies away. The job now refuses to run unless `server-files/account.sqlite` and at least one `user-files/*.sqlite` exist, and refuses to upload an archive under 20KB. 2. `smbclient -c` does not exit non-zero when a command inside it fails - it prints `NT_STATUS_*` and carries on - so the unchecked `&&` printed `NAS copy OK` for an upload that had gone to the share root or nowhere. This adopts the checked `smb()` wrapper already used in `apps/zomboid/backup/backup.sh` and adds a post-upload size check against the share. The off-box copy stays best-effort by design (the local copy is the success condition, per the runbook). What changes is that the log now tells the truth about it. The guards in (1) exit non-zero, so `KubeJobFailed` pages. ## Verification Ran the real script in a container with a stubbed `smbclient`, against a real copy of the budget data: | Case | Result | |---|---| | `/data` empty (PVC not mounted) | exit 1, refuses | | `account.sqlite` present, no budget file | exit 1, refuses | | happy path, real budget data | exit 0, uploads and verifies 559596 bytes | | `cd` fails, file lands in share root | `WARN` (old code: `NAS copy OK`) | | upload truncated | `WARN` + size mismatch (old code: `NAS copy OK`) | And the current backup itself, independently of this change: today's 03:00 archive on the NAS unpacks clean, both SQLite files pass `integrity_check`, the budget file is registered, and it holds 483 category assignments with the newest at 2026-08-24T19:43 local - yesterday evening's session. ## Docs `actual-budget` was absent from the backup runbook entirely, so restoring finance data was undocumented. Adds: - the restore procedure, including that the budget is a CRDT sync log, so restoring `account.sqlite` alone gets a working login and an empty budget - a *Verify a backup without restoring* section (the checks above, reusable for any SQLite-backed app here) - the six CronJobs missing from the schedule table. `karakeep-backup` is local-only with no off-box copy, which the table now says. ## Not addressed here - **No offsite tier.** Every copy in the table is in the house. Noted in the doc, not fixed. - **The same unchecked `smbclient` pattern** is still in the home-assistant, immich, litellm, vaultwarden and tax-export jobs. Left alone rather than changing five jobs' reporting in a PR about finance.
fix(actual-budget): make the finance backup unable to succeed quietly
All checks were successful
checks / fleet-roster (pull_request) Successful in 6s
checks / kustomize-builds (pull_request) Successful in 7s
02dd1bc7fb
The nightly job could report success in two situations where the finance
data was not actually backed up.

1. It tarred whatever was at /data without looking. An unmounted PVC would
   have produced a small "successful" archive and, over 30 days, rotated the
   good local copies out from under it. Now the job refuses to run unless
   server-files/account.sqlite and at least one user-files/*.sqlite are
   present, and refuses to upload an archive under 20KB.

2. `smbclient -c` does not exit non-zero when a command inside it fails - it
   prints NT_STATUS_* and carries on - so the unchecked `&&` printed
   "NAS copy OK" for an upload that had landed in the share root or nowhere.
   The same trap is already documented in apps/zomboid/backup/backup.sh; this
   adopts that script's checked smb() wrapper and adds a post-upload size
   check against the share.

The off-box copy stays best-effort by design (local copy is the success
condition, per the runbook); what changes is that the log now tells the truth
about it. Guards 1 and 2's local half exit non-zero, so KubeJobFailed pages.

Verified against the real budget data in a container with a stubbed smbclient:
empty /data and missing-budget both exit 1; the happy path uploads and
verifies 559596 bytes; a failed `cd` and a truncated upload both report WARN
where the old code reported "NAS copy OK".

docs: actual-budget was absent from the backup runbook entirely, so restoring
finance data was undocumented. Adds the restore procedure (including that the
budget is a CRDT sync log, so account.sqlite alone restores an empty budget),
a verify-a-backup-without-restoring section, and the six CronJobs missing from
the schedule table (karakeep is local-only, which the table now says).
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kblack0610/home-config!202
No description provided.