--- id: RAIL-BS-WP-0004 type: workplan title: "Current-Environment Safety Net" domain: railiance repo: railiance-cluster status: active owner: tegwick topic_slug: railiance state_hub_workstream_id: "7e8b0c20-51eb-40c9-9e3b-85dd380d7625" created: "2026-02-25" updated: "2026-03-10" --- # Current-Environment Safety Net ## Goal Ensure backup and disaster recovery for the current single-server environment is operational and tested before any ThreePhoenix infrastructure migration work begins. Aligned to OAS Stack S2 (railiance-cluster owns backup tooling). ## Context The backup toolchain lives in `tools/cmd/railiance-backup` and `tools/cmd/railiance-preflight`, dispatched via `bin/railiance`. It protects: | Asset | Method | Risk without backup | |---|---|---| | Custodian State Hub DB | pg_dump → age → Nextcloud | Total loss of workstreams, decisions, history | | Claude config + memory | tar → age → Nextcloud | Loss of MCP registration, project memory | | Git repos | Gitea remotes | SPOF: Gitea runs on the same server being migrated | Decision D2: Nextcloud upload-only file drop as backup destination. ## OAS Alignment Per ADR-003, backup tooling lives in **S2 (railiance-cluster)**. The preflight check covers all five OAS stack repos: | Repo | OAS Layer | |---|---| | railiance-infra | S1 — OS & Provisioning | | railiance-cluster | S2 — Kubernetes Runtime | | railiance-platform | S3 — Platform Services | | railiance-enablement | S4 — Developer Tooling | | railiance-apps | S5 — Workloads & Endpoints | Plus cross-domain repos: the-custodian, markitect_project, activity-core, net-kingdom, issue-facade, binect-js, kaizen-agentic. ## Boundary Backup execution: this repo (`bin/railiance backup`). Backup destination: Nextcloud file drop (URL in `~/.config/railiance/nc-upload-url` or hardcoded). Restore procedure: `docs/backup-restore.md`. --- ## Tasks ### T01 — Update preflight repo list to OAS 5-repo layout ```task id: T01 status: done priority: high state_hub_task_id: "4526a842-ea31-4874-9231-92ab556cfe7b" ``` Update `tools/cmd/railiance-preflight` REPOS array: remove `railiance-bootstrap`, add `railiance-infra`, `railiance-cluster`, `railiance-platform`, `railiance-enablement`, `railiance-apps`. Add all active project repos. **Done when:** `bin/railiance preflight` checks all current repos. --- ### T02 — Fix stale repo references in backup-restore.md ```task id: T02 status: done priority: medium state_hub_task_id: "a6313e06-1976-46a7-8e31-df4eb2eca880" ``` Update restore procedure: `railiance-bootstrap` → `railiance-cluster`, `railiance-hosts` → `railiance-infra`, add the three new OAS repos. **Done when:** doc accurately reflects the current 5-repo OAS stack. --- ### T03 — Add make backup and make preflight targets ```task id: T03 status: done priority: medium state_hub_task_id: "05d42a55-921f-4aa7-bb76-e8af9c7e0ac3" ``` Add to root Makefile so the safety net is discoverable from `make help`. **Done when:** `make backup` and `make preflight` both work. --- ### T04 — Run current backup and verify upload ```task id: T04 status: done priority: high state_hub_task_id: "08233868-d522-4117-bc4e-6c0f52545665" ``` Run `bin/railiance backup` and confirm both DB and config files appear in the Nextcloud file drop. **Done when:** backup completes without error and `.last-backup` stamp is fresh. --- ### T05 — Server backup: Gitea data and Zulip chat ```task id: T05 status: todo priority: medium state_hub_task_id: "2d5acff7-4a4e-4ddd-ad06-08237ad3dac8" ``` **Scope correction (2026-03-10):** The original task assumed the `railiance-backup` script in `tools/cmd/railiance-backup` applied here. It does not — that script is for a developer workstation (custodian DB in Docker + Claude config) and is unrelated to the server. The server's safety net must protect: | Asset | Method | |---|---| | Gitea repositories + DB | `k3s kubectl exec` into gitea pod → `gitea dump` | | Zulip chat data | Zulip's built-in export or volume snapshot | This work belongs in **railiance-infra** (S1 — OS & Provisioning layer) as an Ansible role or playbook, not here. A cron job on the server should call that script once it exists. **Do not** wire up a cron job that calls the existing `bin/railiance backup` — that script targets Docker containers that do not exist on this server. **Done when:** 1. A backup playbook/role exists in `railiance-infra` covering Gitea + Zulip 2. It is deployed via Ansible and a cron job on the server calls it daily 3. At least one successful backup run is verified in the log --- ### T06 — Run restore drill ```task id: T06 status: todo priority: medium state_hub_task_id: "f8e4a094-c367-40eb-b895-da17bc144b07" ``` Run the minimal restore drill from `docs/backup-restore.md` against the current backup. Record completion in `~/.cache/railiance/restore-drill.log`. **Done when:** drill exits 0 and log entry is written. --- ## References - Decision D2: Nextcloud as backup destination (`DECISIONS.md`) - Backup tooling: `tools/cmd/railiance-backup`, `tools/cmd/railiance-preflight` - Restore procedure: `docs/backup-restore.md` - Extension points: EP-RAIL-003 (git bare mirrors), EP-RAIL-004 (secondary offsite copy)