From 9649ae34905ad65eb0a368dce33df4d2a57bbd76 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 7 Jul 2026 18:07:07 +0200 Subject: [PATCH] Complete DISCTL-WP-0003-T01: seed asset and data registers Add AssetRegister.yaml and DataRegister.yaml documenting the railiance-backup tool family, backup lanes, data classes, gaps, and RTO/RPO targets. Mark T01 done. --- AssetRegister.yaml | 239 ++++++++++++++++++ DataRegister.yaml | 209 +++++++++++++++ ...CTL-WP-0003-railiance-backup-governance.md | 2 +- 3 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 AssetRegister.yaml create mode 100644 DataRegister.yaml diff --git a/AssetRegister.yaml b/AssetRegister.yaml new file mode 100644 index 0000000..034a0d5 --- /dev/null +++ b/AssetRegister.yaml @@ -0,0 +1,239 @@ +# Resilience Control Plane — asset register (initial seed) +# DISCTL-WP-0003-T01 — railiance-backup tool family and backup lanes +# Broader T0/T4 inventory (domains, Stripe, Bubble, etc.) follows in DISCTL-WP-0002-T02. + +schema_version: "0.1" +updated: "2026-07-07" +register_scope: backup_lanes + +tier_definitions: + T0: "Identity / trust / money — recoverable even after breach" + T1: "Irreplaceable source of truth — strong backups, tested restore" + T2: "Runtime platforms — rebuild from IaC + restore data" + T3: "Derived / rebuildable — recompute where possible" + T4: "External dependencies — export, monitor, document limits" + +backup_streams: + - id: backup-dev-workstation + name: Dev workstation custodian state + tool: railiance-backup + tool_path: railiance-cluster/tools/cmd/railiance-backup + owner_repo: railiance-cluster + operator: Bernd + tier: T1 + host: bnt-lap001 + backup_mechanism: >- + pg_dump from Docker container infra-postgres-1 (custodian DB); + tar of ~/.claude, ~/.claude.json, ~/.gitconfig; age encrypt; upload via curl PUT + destination: + primary: Nextcloud WebDAV file drop (off-node) + local_cache: ~/.cache/railiance/backups/ + backup_schedule: "daily 02:00 UTC (cron)" + retention: "7 local copies per artifact type" + encryption: age + recovery_method: >- + Download artifacts from Nextcloud; age -d with ~/.config/age/railiance-backup.key; + restore PostgreSQL volume; unpack config tar. See railiance-cluster/docs/backup-restore.md + rpo: 24h + rto: 4h + status: operational + gaps: [] + evidence: + restore_tested: false + last_drill: null + references: + - railiance-cluster/docs/backup-restore.md + - railiance-cluster/tools/cmd/railiance-backup + + - id: backup-s1-os-config + name: S1 OS and provisioning config + tool: railiance-backup-s1 + tool_path: railiance-infra/tools/cmd/railiance-backup-s1 + owner_repo: railiance-infra + operator: Bernd + tier: T2 + host: railiance01 + backup_mechanism: >- + Tar of Ansible-managed OS files (sshd, ufw, fail2ban, hosts, hostname, + apt sources) plus dpkg --get-selections; age encrypt + destination: + primary: /opt/backup/railiance/infra/ (on-node local only) + offsite: none + backup_schedule: manual (make backup in railiance-infra) + retention: "7 local copies per artifact type" + encryption: age (SOPS key pair) + recovery_method: >- + age -d with ~/.config/sops/age/keys.txt; restore files to /etc/ or re-apply + via Ansible playbooks + rpo: 24h + rto: 4-8h + status: operational + gaps: + - "No scheduled automation — manual make backup only" + - "No off-cluster upload — lost if host is destroyed" + evidence: + restore_tested: false + last_drill: null + references: + - railiance-infra/tools/cmd/railiance-backup-s1 + + - id: backup-s2-k8s-runtime + name: S2 Kubernetes runtime state + tool: railiance-backup-s2 + tool_path: railiance-cluster/tools/cmd/railiance-backup-s2 + owner_repo: railiance-cluster + operator: Bernd + tier: T1 + host: railiance01 + backup_mechanism: >- + sqlite3 hot backup of k3s state.db; tar of Helm release values (all namespaces); + kubeconfig copy; age encrypt + destination: + primary: /opt/backup/railiance/cluster/ (on-node local only) + offsite: none + backup_schedule: manual (make backup in railiance-cluster) + retention: "7 local copies per artifact type" + encryption: age (SOPS key pair) + recovery_method: >- + age -d; restore k3s state.db or redeploy from Helm values + kubeconfig. + Prefer rebuild-from-manifests where possible. + rpo: 24h + rto: 4-8h + status: operational + gaps: + - "No scheduled automation — manual make backup only" + - "No off-cluster upload — lost if host is destroyed" + evidence: + restore_tested: false + last_drill: null + references: + - railiance-cluster/tools/cmd/railiance-backup-s2 + - history/2026-07-04-forgejo-backup-strategy-assessment.md + + - id: backup-s3-platform + name: S3 platform services (PostgreSQL, Valkey) + tool: railiance-backup + tool_path: railiance-platform/tools/cmd/railiance-backup + owner_repo: railiance-platform + operator: Bernd + tier: T1 + host: railiance01 + backup_mechanism: >- + Intended pg_dump of platform PostgreSQL HA and Valkey state; age encrypt; + upload to Nextcloud WebDAV (same lane as dev workstation pattern) + destination: + primary: Nextcloud WebDAV file drop (intended, off-node) + local_cache: TBD + backup_schedule: TBD + retention: "14 daily + 4 weekly rotations (adopted 2026-07-09 for Forgejo lane)" + encryption: age + recovery_method: >- + Not operational — tool absent. Makefile target exists but + tools/cmd/railiance-backup was never relocated from railiance-cluster + (RAIL-PL-WP-0001-T05 cancelled). + rpo: 24h + rto: 4h + status: missing + gaps: + - "tools/cmd/railiance-backup absent from railiance-platform" + - "make backup target references missing implementation" + - "Relocation from railiance-cluster planned but never completed" + evidence: + restore_tested: false + last_drill: null + references: + - railiance-platform/Makefile + - railiance-platform/docs/postgresql-ha.md + - history/2026-07-04-forgejo-backup-strategy-assessment.md + + - id: backup-forgejo-production + name: Forgejo production (git, packages, DB) + tool: forgejo dump + CNPG pg_dump + tool_path: null + owner_repo: railiance-platform + operator: Bernd + tier: T1 + host: railiance01 + backup_mechanism: >- + Daily forgejo dump zip (repos, packages OCI/npm/generic, attachments, LFS, + avatars) plus daily pg_dump from CNPG forgejo-db; age encrypt; Nextcloud upload + destination: + primary: Nextcloud WebDAV file drop (decided 2026-07-09, not wired) + drill_archive: workstation /tmp only (T09 drill) + backup_schedule: daily (intended — not implemented) + retention: "14 daily + 4 weekly rotations" + encryption: age (platform backup public key) + recovery_method: >- + forgejo-restore-drill.sh — unzip dump, copy files, psql import, isolated Helm + deploy. Proven 2026-07-04 (RAIL-HO-WP-0005-T09); not yet from automated backup. + rpo: 24h + rto: 4h + status: not_wired + gaps: + - "No scheduled forgejo dump CronJob" + - "No CNPG ScheduledBackup CR on forgejo-db" + - "No automated upload to Nextcloud — decision #7 adopted but not implemented" + - "Promotion gate: 7 consecutive daily successes + restore from Nextcloud artifact" + evidence: + restore_tested: true + last_drill: "2026-07-04" + evidence_ref: railiance-infra/docs/forgejo-restore-drill-evidence.md + references: + - the-custodian/docs/forgejo-production-decisions.md + - railiance-infra/tools/forgejo-restore-drill.sh + - history/2026-07-04-forgejo-backup-strategy-assessment.md + + - id: backup-gitea-canonical + name: Gitea canonical source forge (pre-cutover) + tool: gitea dump / git mirror + tool_path: null + owner_repo: railiance-infra + operator: Bernd + tier: T1 + host: coulombcore + backup_mechanism: >- + Manual gitea dump before migration waves; git mirror to offsite provider (intended). + Bundled PostgreSQL + gitea-shared-storage PVC; no automated CronJob evidenced. + destination: + primary: none automated + mirror: TBD (GitHub private mirror or secondary host) + backup_schedule: pre-cutover manual only + retention: point-in-time of last manual dump + encryption: not standardized + recovery_method: gitea dump restore per Gitea docs; safety contract requires fresh + dump before each Forgejo migration wave + rpo: 24h + rto: 4-8h + status: at_risk + gaps: + - "No automated Gitea backup CronJob on coulombcore" + - "Package blob backup not evidenced" + - "disaster-control itself not yet mirrored off primary Gitea (DISCTL-WP-0002-T01)" + evidence: + restore_tested: false + last_drill: null + references: + - history/2026-07-04-forgejo-backup-strategy-assessment.md + - specs/ResilienceControlSetup.md + +related_streams: + # Operational on cluster but outside T01 minimum table — tracked for completeness. + - id: backup-adjacent-sso + name: Adjacent SSO/MFA stack + tools: + - privacyidea-backup + - authelia-backup + - lldap-backup + owner_repo: railiance-platform + tier: T2 + host: railiance01 + backup_schedule: daily CronJobs on railiance01 + status: operational + note: "Only scheduled backup CronJobs found on cluster as of 2026-07-04 assessment" + + - id: backup-apps-pg + name: apps-pg shared CloudNativePG cluster + owner_repo: railiance-platform + tier: T1 + status: not_wired + note: "Documented follow-up in railiance-platform/docs/apps-pg.md; no ScheduledBackup CR" \ No newline at end of file diff --git a/DataRegister.yaml b/DataRegister.yaml new file mode 100644 index 0000000..e789f69 --- /dev/null +++ b/DataRegister.yaml @@ -0,0 +1,209 @@ +# Resilience Control Plane — data register (initial seed) +# DISCTL-WP-0003-T01 — data classes covered by railiance-backup lanes +# Full data inventory (Bubble, Stripe exports, member data, etc.) follows in DISCTL-WP-0002-T02. + +schema_version: "0.1" +updated: "2026-07-07" +register_scope: backup_lane_data_classes + +data_classes: + - id: data-custodian-state-hub + name: Custodian State Hub database + tier: T1 + source_of_truth: Docker volume infra_pg_data (container infra-postgres-1) + description: >- + Workstreams, tasks, decisions, progress history, messages — operational + memory of the custodian control plane + backup_stream: backup-dev-workstation + backup: pg_dump via railiance-backup + recovery_method: Restore PostgreSQL from age-decrypted dump + rpo: 24h + rto: 4h + owner: Bernd + status: operational + + - id: data-operator-dev-config + name: Operator development environment config + tier: T1 + source_of_truth: ~/.claude/, ~/.claude.json, ~/.gitconfig on bnt-lap001 + description: >- + Claude project memory, MCP registration, agent settings, git identity — + not in git repos; loss slows all agent sessions + backup_stream: backup-dev-workstation + backup: config tar via railiance-backup + recovery_method: Unpack age-decrypted tar to $HOME + rpo: 24h + rto: 4h + owner: Bernd + status: operational + + - id: data-s1-os-config + name: S1 OS and firewall configuration state + tier: T2 + source_of_truth: Live /etc/ on railiance01 (Ansible-managed, may drift) + description: sshd, ufw, fail2ban, hosts, hostname, installed package selections + backup_stream: backup-s1-os-config + backup: os-config tar + packages list via railiance-backup-s1 + recovery_method: Restore files or re-apply Ansible; package list aids apt reconciliation + rpo: 24h + rto: 4-8h + owner: Bernd + status: operational + gaps: + - "On-node only — no off-cluster copy" + + - id: data-k3s-cluster-state + name: k3s cluster control plane state + tier: T1 + source_of_truth: /var/lib/rancher/k3s/server/db/state.db (SQLite mode) + description: >- + Kubernetes object state for single-node k3s — deployments, services, + secrets metadata, CRDs + backup_stream: backup-s2-k8s-runtime + backup: sqlite3 hot backup via railiance-backup-s2 + recovery_method: Restore state.db or rebuild cluster from Helm values + rpo: 24h + rto: 4-8h + owner: Bernd + status: operational + gaps: + - "On-node only — no off-cluster copy" + - "PV/PVC data not included — application data needs separate streams" + + - id: data-helm-release-values + name: Helm release configuration + tier: T2 + source_of_truth: Live Helm releases on k3s + description: Per-release values.yaml for all namespaces — redeploy baseline + backup_stream: backup-s2-k8s-runtime + backup: helm get values export via railiance-backup-s2 + recovery_method: helm upgrade --install from decrypted values files + rpo: 24h + rto: 4-8h + owner: Bernd + status: operational + gaps: + - "On-node only — no off-cluster copy" + + - id: data-kubeconfig + name: Cluster admin kubeconfig + tier: T0 + source_of_truth: /etc/rancher/k3s/k3s.yaml + description: Cluster admin credentials — trust boundary asset + backup_stream: backup-s2-k8s-runtime + backup: file copy via railiance-backup-s2 + recovery_method: Restore to /etc/rancher/k3s/ or regenerate from k3s + rpo: 24h + rto: 2h + owner: Bernd + status: operational + gaps: + - "On-node only — no off-cluster copy" + + - id: data-platform-postgresql + name: Platform PostgreSQL HA databases + tier: T1 + source_of_truth: railiance-platform PostgreSQL HA cluster + description: >- + Legacy platform relational data including services not yet on CNPG — + intended target of S3 railiance-backup + backup_stream: backup-s3-platform + backup: none + recovery_method: Not operational — awaiting railiance-backup tool restoration + rpo: 24h + rto: 4h + owner: Bernd + status: missing + gaps: + - "railiance-platform/tools/cmd/railiance-backup absent" + + - id: data-forgejo-git-repos + name: Forgejo git repositories and metadata + tier: T1 + source_of_truth: Forgejo application PVC + forgejo-db PostgreSQL + description: >- + All git repos, org structure, issues, PRs, webhooks — canonical forge + post-cutover + backup_stream: backup-forgejo-production + backup: manual forgejo dump only (automated not wired) + recovery_method: forgejo-restore-drill.sh path — proven 2026-07-04 + rpo: 24h + rto: 4h + owner: Bernd + status: manual_only + gaps: + - "No scheduled backup job" + - "RPO today is point-in-time of last manual dump" + + - id: data-forgejo-packages + name: Forgejo OCI/npm/generic packages + tier: T1 + source_of_truth: Forgejo package storage on application PVC + description: Container images, npm packages, generic artifacts published to forgejo.coulomb.social + backup_stream: backup-forgejo-production + backup: included in forgejo dump (when run); not scheduled + recovery_method: Restored as part of forgejo dump restore + rpo: 24h + rto: 4h + owner: Bernd + status: manual_only + + - id: data-forgejo-db + name: forgejo-db CNPG database + tier: T1 + source_of_truth: CloudNativePG cluster forgejo-db (databases namespace) + description: Forgejo relational metadata — users, repos index, package registry DB + backup_stream: backup-forgejo-production + backup: none automated (intended daily pg_dump per 2026-07-09 decision) + recovery_method: psql import from dump (proven in T09 drill) + rpo: 24h + rto: 4h + owner: Bernd + status: not_wired + gaps: + - "No CNPG ScheduledBackup CR" + - "Split-brain risk: DB-only restore misses git blobs on app PVC" + + - id: data-gitea-repos + name: Gitea repositories and instance data + tier: T1 + source_of_truth: coulombcore Gitea instance (bundled PG + gitea-shared-storage PVC) + description: >- + Canonical source forge until Forgejo cutover — all coulomb/* repos including + disaster-control + backup_stream: backup-gitea-canonical + backup: none automated + recovery_method: gitea dump restore; manual dump required before migration waves + rpo: 24h + rto: 4-8h + owner: Bernd + status: at_risk + gaps: + - "No automated backup" + - "Package blob backup not evidenced" + + - id: data-age-backup-keys + name: age encryption keys for backup artifacts + tier: T0 + source_of_truth: >- + Operational ~/.config/age/railiance-backup.key (dev lane); + ~/.config/sops/age/keys.txt (S1/S2 lane) + description: >- + Private keys required to decrypt all age-encrypted backups — loss makes + backups undecryptable + backup_stream: null + backup: password manager recovery copy (required); not in backup streams + recovery_method: Retrieve from password manager; regenerate only with key rotation plan + rpo: immediate + rto: 2h + owner: Bernd + status: operational + gaps: + - "Not inventoried in AssetRegister T0 section yet — DISCTL-WP-0002-T02 follow-up" + +status_legend: + operational: "Backup mechanism exists and has run successfully" + manual_only: "Restore path exists but backup is operator-triggered only" + not_wired: "Decision or design exists; automation not implemented" + missing: "No working backup mechanism" + at_risk: "Known gap with high exposure; no reliable recovery point" \ No newline at end of file diff --git a/workplans/DISCTL-WP-0003-railiance-backup-governance.md b/workplans/DISCTL-WP-0003-railiance-backup-governance.md index b73e561..bdde246 100644 --- a/workplans/DISCTL-WP-0003-railiance-backup-governance.md +++ b/workplans/DISCTL-WP-0003-railiance-backup-governance.md @@ -40,7 +40,7 @@ in `railiance-platform`, `railiance-apps`, and `railiance-infra`. ```task id: DISCTL-WP-0003-T01 -status: todo +status: done priority: high state_hub_task_id: "da37bcf1-2d7c-4f6b-88c4-08c0bad91151" ```