From cb6396caabf329d1eeaf5c7da917d1f2a5702d7d Mon Sep 17 00:00:00 2001 From: codex Date: Sat, 5 Sep 2026 20:50:26 +0200 Subject: [PATCH] Record verified Backup account activation and consumer refresh Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883 --- WORK-RECORDS.md | 1 + ...26-0004-railiance-backup-offsite-lane.yaml | 19 +++++- docs/backup-credential-recovery.md | 33 ++++++++++ ...RPF-WP-0029-backup-account-2026-09-05.json | 60 +++++++++++++++++++ docs/forgejo-backup.md | 6 ++ ...-09-05-nextcloud-backup-account-cutover.md | 29 +++++++++ ...-0029-backup-credential-default-removal.md | 18 ++++-- 7 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json create mode 100644 history/2026-09-05-nextcloud-backup-account-cutover.md diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index d1fde2b..07035bb 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -30,6 +30,7 @@ | task | RPF-WP-0027-T06 | wait | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | | task | RPF-WP-0029-T01 | done | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | | task | RPF-WP-0029-T02 | wait | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | +| task | RPF-WP-0029-T03 | done | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | | task | RPF-WP-0035-T01 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | | task | RPF-WP-0035-T02 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | | task | RPF-WP-0035-T03 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | diff --git a/credential-change-requests/CCR-2026-0004-railiance-backup-offsite-lane.yaml b/credential-change-requests/CCR-2026-0004-railiance-backup-offsite-lane.yaml index eb6f5c9..5bca033 100644 --- a/credential-change-requests/CCR-2026-0004-railiance-backup-offsite-lane.yaml +++ b/credential-change-requests/CCR-2026-0004-railiance-backup-offsite-lane.yaml @@ -5,7 +5,7 @@ request_type: workload-kv-read title: Railiance offsite backup lane (Nextcloud WebDAV + age recovery) status: active created: '2026-07-07' -updated: '2026-07-16' +updated: '2026-09-05' requester: agent: grok reason: Move railiance-backup / forgejo-backup credentials from hardcoded script @@ -72,6 +72,12 @@ risk: classification: high notes: - NC_WEBDAV_TOKEN grants upload to the offsite backup file drop. + - Since 2026-09-05 the file drop belongs to Backup, folder /railiance-backups, + with create-only permissions and a server-enforced 10 GiB account quota. + - Operator credentials remain in KVv2 operators/nextcloud/backup, fields + BACKUP_USERNAME and BACKUP_PASSWORD; never deliver them to production. + - Existing Bernd-owned retained backups and recovery access remain separate; + historical predecessor invalidation and age-key exposure are still open. - "AGE_PRIVATE_KEY decrypts all age-encrypted backup artifacts \u2014 recovery escrow\ \ only." - Credentials must not be stored on production hosts with delete permission. @@ -117,6 +123,17 @@ verification: - 'Field presence via JSON key inventory only (NC_WEBDAV_TOKEN, NC_WEBDAV_URL, AGE_PRIVATE_KEY); lengths recorded, values not printed.' - 'EXPOSED taint set: custom_metadata exposed_at=2026-07-16T00:00:00Z exposed_version=2 (WP-0026 T05).' - 'ops-warden catalog promoted draft→active; fetch_command pinned to NC_WEBDAV_TOKEN; resolvable=true; risk=high.' + - at: '2026-09-05' + actor: codex + kind: dedicated_backup_account_cutover + result: passed + details: + - User authorized dedicated Backup account and operator custody coordinates. + - Verified 10737418240-byte quota and Backup-owned create-only share. + - Encrypted fixture PUT 201, owner download and escrow decryption passed; + runtime GET and DELETE returned 405 on the actual upload endpoint. + - CAS advanced workload KV version 2 to 3, preserving age escrow and other fields. + - Evidence docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json. lifecycle: deactivate: Disable ops-warden catalog entry and detach OIDC role policy; rotate diff --git a/docs/backup-credential-recovery.md b/docs/backup-credential-recovery.md index 3201779..7193f8a 100644 --- a/docs/backup-credential-recovery.md +++ b/docs/backup-credential-recovery.md @@ -65,3 +65,36 @@ requires HTTPS and rejects non-success responses. The Python backend streams the artifact, refuses redirects and suppresses credential-bearing exception text. These source changes are preparation; no provider rotation, upload or restore is claimed by their tests. + +## Dedicated Backup account (2026-09-05) + +The operator-provisioned account is `Backup` on `nx4069.your-storageshare.de`. +Its credentials remain in OpenBao KVv2 `operators/nextcloud/backup`, fields +`BACKUP_USERNAME` and `BACKUP_PASSWORD` (API `operators/data/nextcloud/backup`). +The UI's `secrets/` prefix is not a separate KV mount. + +Future uploads use the account's `/railiance-backups` folder through a dedicated +create-only share. Only its derived `NC_WEBDAV_TOKEN` and `NC_WEBDAV_URL` enter +the existing workload lane and activity-core ExternalSecret; the account password +is not a production credential. Recovery download and cleanup use operator +account authority. Preserve `AGE_PRIVATE_KEY` and all unrelated KV fields with CAS. + +The server-reported quota is **10 GiB (10,737,418,240 bytes)**. This is a hard +capacity limit shared by the account's files. Retention counts such as 14 daily +and four weekly backups do not guarantee they fit. Check quota before large +uploads; a quota rejection is a failed backup. Pruning retained backups requires +an explicit retention decision and operator authority; upload jobs cannot delete. +Existing Bernd-owned backups remain in their original location and require their +existing recovery access. This cutover neither migrates them nor revokes Bernd's +personal account or old share. + +`scripts/migrate_nextcloud_backup_account.py` performs the attended account, +quota, create-only share, encrypted fixture recovery and CAS checks. Run it only +through `warden access openbao-platform-admin-login --exec -- ...`, with a fresh +private receipt path and `--confirm 'MIGRATE TO Backup'`. The helper is silent; +receipts contain only status and metadata. Review a failed receipt before retrying: +a failure can leave the dedicated folder/share or an identified test fixture, and +a cleanup failure after CAS can occur after credentials have already changed. +Refresh `actcore-backup-offsite`, verify delivery by contained comparison, and +restart its three environment consumers (`actcore-api`, `actcore-event-router`, +`actcore-worker`). Record their readiness and loaded-value comparisons. diff --git a/docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json b/docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json new file mode 100644 index 0000000..7ac0860 --- /dev/null +++ b/docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json @@ -0,0 +1,60 @@ +{ + "date": "2026-09-05", + "cutover": { + "schema": "platform.nextcloud-backup-account-cutover.v1", + "status": "account_cutover_verified_pending_consumer_refresh", + "quota_before": { + "quota_bytes": 10737418240, + "used_bytes": 2506112, + "available_bytes": 10734912128 + }, + "previous_kv_version": 2, + "share_id": "19", + "observed_permissions": 4, + "owner_matches": true, + "path_matches": true, + "share_owner": "Backup", + "share_permissions": 4, + "fixture_name": "account-acceptance-ee8a5e360afd26b7dc7b9b4c.age", + "upload_http_status": 201, + "runtime_get_http_status": 405, + "runtime_delete_http_status": 405, + "encrypted_upload": true, + "owner_download": true, + "escrow_decryption": true, + "runtime_read_denied": true, + "runtime_delete_denied": true, + "kv_version": 3, + "escrow_preserved": true, + "fixture_removed": true, + "quota_after": { + "quota_bytes": 10737418240, + "used_bytes": 2506112, + "available_bytes": 10734912128 + } + }, + "consumers": { + "status": "verified", + "kv_version": 3, + "eso_delivery_matches": true, + "deployments": { + "actcore-api": { + "ready": true, + "environment_matches": true + }, + "actcore-event-router": { + "ready": true, + "environment_matches": true + }, + "actcore-worker": { + "ready": true, + "environment_matches": true + } + }, + "previous_test_fixtures_removed": [ + "account-acceptance-e820d52ac48dc28766c39eef.age", + "account-acceptance-f88f8dcc3bea4ea199aeeebd.age" + ] + }, + "status": "verified" +} diff --git a/docs/forgejo-backup.md b/docs/forgejo-backup.md index c35847b..f5dae34 100644 --- a/docs/forgejo-backup.md +++ b/docs/forgejo-backup.md @@ -40,6 +40,12 @@ Decrypt: `~/.config/age/railiance-backup.key` (same key as other Railiance backu ## Nextcloud layout +Since 2026-09-05, uploads go to user `Backup`, folder `/railiance-backups`, +through a create-only share delivered from OpenBao. The account has **10 GiB** +total capacity; the retention target below is subject to available space. +Operator credentials stay in `operators/nextcloud/backup`; workload jobs receive +only the derived upload grant. See [account and recovery procedure](backup-credential-recovery.md). + ``` forgejo/forgejo-dump-.zip.age forgejo/forgejo-db-.sql.age diff --git a/history/2026-09-05-nextcloud-backup-account-cutover.md b/history/2026-09-05-nextcloud-backup-account-cutover.md new file mode 100644 index 0000000..644986f --- /dev/null +++ b/history/2026-09-05-nextcloud-backup-account-cutover.md @@ -0,0 +1,29 @@ +# Dedicated Nextcloud Backup account activated — 2026-09-05 + +User authorized replacing Bernd's personal upload lane with the new Backup +account. Resolved UI custody notation to KVv2 operators/nextcloud/backup +(BACKUP_USERNAME/BACKUP_PASSWORD). Native WebDAV confirmed Backup and a +10,737,418,240-byte account quota. + +Created /railiance-backups and backup prefix folders, with a Backup-owned public +share granting create only (permissions 4). Account password remains operator-only. +An initial publicUpload option broadened the share; verification rejected it and +removed that share before touching workload custody. Explicit permissions alone +produced the required grant. The actual file-drop endpoint denies GET and DELETE +with HTTP 405. Three encrypted synthetic fixtures exercised transport/recovery; +all were removed using owner authority after verification. + +Uploaded with HTTP 201, downloaded as owner, and decrypted with existing age +escrow. CAS advanced the workload lane from version 2 to 3, changing only the +upload token and URL. Forced actcore-backup-offsite refresh and compared values +in protected memory. Restarted actcore-api, actcore-event-router and actcore-worker; +all ready with new values loaded. No password or token appears in source/evidence. + +10 GiB is a hard account limit, not assurance that 14 daily plus four weekly +backups fit. No retained backup deletion or automatic pruning was performed. +Existing Bernd-owned backups and recovery access remain where they were. +T03 is done; T02 remains waiting for old share invalidation and a real offsite +application restore. Upload rotation does not clear historical age-key exposure. + +Validation: 196 repository tests passed. Live evidence is in +`docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json`. diff --git a/workplans/RPF-WP-0029-backup-credential-default-removal.md b/workplans/RPF-WP-0029-backup-credential-default-removal.md index da1cf6a..e0c1f6f 100644 --- a/workplans/RPF-WP-0029-backup-credential-default-removal.md +++ b/workplans/RPF-WP-0029-backup-credential-default-removal.md @@ -4,7 +4,7 @@ type: workplan title: "Remove backup credential default and verify governed replacement" domain: financials repo: railiance-platform -status: active +status: blocked owner: codex created: "2026-09-05" updated: "2026-09-05" @@ -44,11 +44,12 @@ state_hub_task_id: "b3f3402f-890b-5781-9b3e-1c9c0d28cea8" Provider-side invalidation and replacement custody need the attended provider owner and CCR-2026-0004 lifecycle procedure. Record only non-secret invalidation, encrypted upload and restore receipts. No provider authority or replacement receipt was available; source removal alone does not close the reported exposure. Never record the predecessor value, fingerprint, length, or shape. Prepared owner execution procedure: `docs/backup-credential-recovery.md`. -Awaiting the Nextcloud share owner/account and replacement custody coordinates; -OpenBao authority is not provider share-management authority. Activity-core is +The dedicated Backup account cutover is complete under T03. Awaiting owner +authority for invalidating the old Bernd-owned share and a real offsite restore. Activity-core is also a consumer of this upload lane. Preserve AGE_PRIVATE_KEY and historical exposure evidence; upload-token rotation cannot clear recovery-key taint. -No provider mutation, backup upload or restore has been run in this continuation. +T03 proves encrypted fixture transport and decryption; full application restore +and historical predecessor invalidation remain open. ## Portfolio review — 2026-09-05 @@ -64,7 +65,7 @@ here until its evidence is accepted. No rotation was executed in this review. ```task id: RPF-WP-0029-T03 -status: progress +status: done priority: high state_hub_task_id: "f85b1b4e-9a20-56e2-9e7d-d1d6f1c77bd9" ``` @@ -78,3 +79,10 @@ for workload delivery, and preserve the existing age escrow and retained data. Prove encrypted upload/download/decryption and workload delivery. No automatic pruning or personal-account revocation is inferred from this account change. The historical predecessor invalidation obligation in T02 remains separate. + +Completed 2026-09-05: Backup-owned share permissions 4; upload HTTP 201, +owner download/decryption passed, runtime GET/DELETE denied with HTTP 405. +Workload KV CAS 2→3 preserved age escrow. ExternalSecret delivery and all three +activity-core deployments verified ready with new credentials loaded. All test +fixtures removed. Account quota is enforced by Nextcloud; no automatic pruning. +Evidence: `docs/evidence/RPF-WP-0029-backup-account-2026-09-05.json`.