Off-site / object-storage backup target for the SQLite DB #18
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED#18
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Optionally ship the existing timestamped SQLite backups to an off-box target (S3-compatible object storage, or a mounted remote path) so a host failure doesn't lose approved schemes and history.
Why it's valuable
v1 already creates timestamped backups before bulk operations and prunes by
BACKUP_RETAIN_COUNT, but they live on the same volume as the DB. Off-site copies make the data actually durable — important because approved schemes represent real curation effort.Sketch
BACKUP_S3_ENDPOINT/bucket/creds, orBACKUP_REMOTE_PATH).docs/troubleshooting.md.Acceptance criteria
Proposed enhancement (not in the original spec).
Done — #123 merged, CI green.
docs/troubleshooting.md)A real bug in the first draft
Remote retention originally matched on an
"iris_"filename prefix. Every daily backup is namediris_daily_..., so that prefix matches the dailies too — manual pruning would have deleted daily backups early. It now shares the exact_KIND_GLOBSpattern local pruning uses, deliberately one rule rather than two that must be kept in step.Why no boto3
S3 here is PUT, list and DELETE, against ~50MB of SDK in an image deliberately slimmed in #60. The SigV4 signature is computed on httpx directly.
That is normally a bad trade, and the reason it is acceptable here is specific: SigV4 is request authentication, not encryption. No key exchange, no confidentiality property to get subtly wrong — a mistake yields a 403 and a loudly broken feature rather than a silently insecure one.
defusedxml(~30KB) was added, for the listing response. Refusing that would have been false economy: the body is remote input and the stdlib parser expands entities, so a hostile or compromised endpoint could answer a listing with a billion-laughs payload. The response is size-bounded as well.On the signing test
Worth recording because I got it wrong twice. I first asserted a hex vector recalled from memory; it failed. I "fixed" it by matching AWS's
iamexample; it failed again, because the constant itself was fabricated. Fetching AWS's actual documentation settled it: the current docs state the derivation as an algorithm and give no worked numeric example, so there is no vendor vector to cite.The implemented chain matches the spec exactly (
AWS4+secret → date → region → service → aws4_request). The test now reimplements those four steps longhand from the specification rather than calling the module and comparing it to itself, and I confirmed it catches a region/service swap. I also removed theserviceparameter that existed only to serve the bogus test.Better to say plainly that there is no vendor vector than to leave a comment claiming an authority the test does not have.
Failure behaviour
An unreachable target never fails a backup — the local snapshot is already written and is what actually stands between the user and data loss, so a dead NAS is logged and the operation that triggered it still completes. Documented in troubleshooting, along with the corollary: check the logs rather than assuming silence means success.
Uploads to a mounted path stage under a dot-prefixed name and are renamed, so a reader never sees a half-copied backup that looks complete.