Record verified Backup account activation and consumer refresh
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
codex 2026-09-05 20:50:26 +02:00
parent b6d648bfbe
commit cb6396caab
7 changed files with 160 additions and 6 deletions

View file

@ -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 |

View file

@ -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

View file

@ -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.

View file

@ -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"
}

View file

@ -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-<timestamp>.zip.age
forgejo/forgejo-db-<timestamp>.sql.age

View file

@ -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`.

View file

@ -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`.