Backups: write-only off-site credentials, redaction that doesn't depend on rclone's wording, and the audio archive's path #795

Closed
opened 2026-09-24 01:57:15 +00:00 by claude-bot · 1 comment
Collaborator

Follow-ups from #790 (#753), which fixed the read-back and two worse bugs. Owner, 2026-09-24 (R3): lane 9 builds this (#735), server change first, as the build plan says, since lane 9 rebuilds the admin backups surface anyway.

1. Off-site credentials go back to the browser

GET /api/admin/backup/config returns the raw remote so the admin form can round-trip it. If the remote carries inline credentials (:s3,access_key_id=…,secret_access_key=…:bucket, or a named remote with parameters), they reach the admin's browser on every load.

Fix: make them write-only, as the per-campaign AI keys already are.

  • The read returns the remote with its parameter block redacted, plus a credentials_set flag.
  • A save that leaves the redacted form untouched keeps what's stored.
  • "Remove it" clears it.

backup_offsite_service.redact_remote (fixed in #790) already produces the redacted form.

2. Redaction depends on rclone repeating the remote verbatim

_redact_text scrubs the remote from rclone's output only when rclone echoes it exactly as configured. If rclone prints the parameters split up, re-quoted or reordered, they get through into the log row and error text.

Fix: redact by known secret values as well as by the remote's shape. Pull each parameter value out of the configured remote, and scrub any occurrence of any of them from whatever rclone printed. Test it with an rclone error message that quotes a parameter on its own.

3. The log doesn't record where the audio archive went

A backup row records the dump's local path (and, since #790, the remote). The audio archive written alongside it can only be found by deriving its name from the dump's timestamp. A restore helper, or ADMIN-14's instance health, would have to know that.

Fix: record the archive's path on the row too (a column and a migration), and read it back the way verify reads the dump.

None of this is urgent. Only an admin sees the form, and the admin typed the credentials. But (1) and (2) are credential hygiene and belong in the rebuild rather than after it.

🤖 Generated with Claude Code

Follow-ups from #790 (#753), which fixed the read-back and two worse bugs. **Owner, 2026-09-24 (R3): lane 9 builds this** (#735), server change first, as the build plan says, since lane 9 rebuilds the admin backups surface anyway. ### 1. Off-site credentials go back to the browser `GET /api/admin/backup/config` returns the raw `remote` so the admin form can round-trip it. If the remote carries **inline credentials** (`:s3,access_key_id=…,secret_access_key=…:bucket`, or a named remote with parameters), they reach the admin's browser on every load. **Fix:** make them **write-only**, as the per-campaign AI keys already are. - The read returns the remote with its parameter block redacted, plus a `credentials_set` flag. - A save that leaves the redacted form untouched keeps what's stored. - "Remove it" clears it. `backup_offsite_service.redact_remote` (fixed in #790) already produces the redacted form. ### 2. Redaction depends on rclone repeating the remote verbatim `_redact_text` scrubs the remote from rclone's output only when rclone echoes it exactly as configured. If rclone prints the parameters split up, re-quoted or reordered, they get through into the log row and error text. **Fix:** redact by **known secret values** as well as by the remote's shape. Pull each parameter value out of the configured remote, and scrub any occurrence of any of them from whatever rclone printed. Test it with an rclone error message that quotes a parameter on its own. ### 3. The log doesn't record where the audio archive went A backup row records the dump's local path (and, since #790, the remote). The audio archive written alongside it can only be found by deriving its name from the dump's timestamp. A restore helper, or ADMIN-14's instance health, would have to know that. **Fix:** record the archive's path on the row too (a column and a migration), and read it back the way verify reads the dump. None of this is urgent. Only an admin sees the form, and the admin typed the credentials. But (1) and (2) are credential hygiene and belong in the rebuild rather than after it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Done in #848, merged 2026-09-26.

  1. The backup destination's credentials are write-only. The read returns the destination (remote) redacted, plus credentials_set. Sending the redacted form back unchanged keeps the stored credentials, and "" clears them. The config's audit event carries no destination value.
  2. rclone output is redacted by value. Every configured parameter value longer than 4 characters is scrubbed wherever it appears, as well as by the destination's shape.
  3. Each backup row records its audio archive's path. Migration a2c4e6f8b0d1 adds backup_logs.audio_archive_path. Verify reads the archive back from that path, or from a path derived from the dump's name for older rows, with the same containment check the dump gets.

Uploaded images in the backup (IS-396) wait for the owner's lane 9 A6.

Done in #848, merged 2026-09-26. 1. **The backup destination's credentials are write-only.** The read returns the destination (`remote`) redacted, plus `credentials_set`. Sending the redacted form back unchanged keeps the stored credentials, and `""` clears them. The config's audit event carries no destination value. 2. **rclone output is redacted by value.** Every configured parameter value longer than 4 characters is scrubbed wherever it appears, as well as by the destination's shape. 3. **Each backup row records its audio archive's path.** Migration `a2c4e6f8b0d1` adds `backup_logs.audio_archive_path`. Verify reads the archive back from that path, or from a path derived from the dump's name for older rows, with the same containment check the dump gets. Uploaded images in the backup (IS-396) wait for the owner's lane 9 A6.
Sign in to join this conversation.
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
rbrooks/Quest-Board#795
No description provided.