feat(523): backup destinations - a folder or an S3 bucket, with the S3 secret sealed and kept out of backups
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 36s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 36s
services/backup_s3.py is FabledSteward's SigV4 client ported (put + list, never delete, so an append-only key is enough), with its tests against AWS's published signatures. services/backup_destination.py holds the admin config (backup_config JSON) and delivers a dated tar to a mounted folder or a bucket, and checks either without sending a backup. services/secrets.py seals the S3 secret with Fernet under a key derived from SECRET_KEY; backup._SETTINGS_NOT_EXPORTED keeps it out of every backup. cryptography becomes a direct dependency (already in the image through pyjwt). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -772,8 +772,18 @@ def _rule_version_rows(rows) -> list[dict]:
|
||||
]
|
||||
|
||||
|
||||
# Setting KEYS no backup carries. The backup destination's S3 secret is sealed
|
||||
# under SECRET_KEY, but a backup is exactly the thing copied off-site — it must
|
||||
# not carry the key that writes to the place it is copied to. A restore leaves
|
||||
# the destination configured and asks for the secret again.
|
||||
_SETTINGS_NOT_EXPORTED = frozenset({"backup_s3_secret"})
|
||||
|
||||
|
||||
def _setting_rows(rows) -> list[dict]:
|
||||
return [{"user_id": s.user_id, "key": s.key, "value": s.value} for s in rows]
|
||||
return [
|
||||
{"user_id": s.user_id, "key": s.key, "value": s.value}
|
||||
for s in rows if s.key not in _SETTINGS_NOT_EXPORTED
|
||||
]
|
||||
|
||||
|
||||
def _rulebook_rows(rows) -> list[dict]:
|
||||
|
||||
Reference in New Issue
Block a user