# Resilience Control Plane — asset register (initial seed) # DISCTL-WP-0003-T01 — railiance-backup tool family and backup lanes # Backup lanes: DISCTL-WP-0003-T01. Stack assets (T0/T4): DISCTL-WP-0002-T02. schema_version: "0.2" updated: "2026-07-07" register_scope: backup_lanes_and_stack 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 assets: # ── T0 Identity / trust / money ────────────────────────────────────────────── - id: asset-dns-coulomb-social name: coulomb.social DNS zone tier: T0 owner: Bernd provider: IONOS hosts: - gitea.coulomb.social - forgejo.coulomb.social - hub.coulomb.social - reuse.coulomb.social production_ips: coulombcore_legacy: "92.205.130.254" railiance01: "92.205.62.239" recovery_path: method: IONOS control panel + zone record export backup_admin: unknown mfa_recovery: unknown break_glass: unknown backup: manual export after changes status: at_risk gaps: - "Registrar lock status not inventoried" - "MFA recovery codes not recorded here" - "No automated zone export" - id: asset-registrar-ionos name: Domain registrar (IONOS) tier: T0 owner: Bernd recovery_path: method: IONOS account login backup_admin: unknown mfa_recovery: unknown break_glass: unknown status: unknown gaps: - "Backup admin contact not recorded" - "Account lock + MFA recovery not evidenced" - id: asset-email-ionos name: coulomb.social email (IONOS SMTP) tier: T0 owner: Bernd mailboxes: - forgejo@coulomb.social recovery_path: method: IONOS mail control panel backup_admin: unknown mfa_recovery: unknown status: operational gaps: - "Recovery path not fully documented" - id: asset-stripe-admin name: Stripe account (payments / subscriptions) tier: T0 owner: Bernd recovery_path: method: Stripe dashboard login backup_admin: unknown mfa_recovery: unknown break_glass: unknown backup: scheduled CSV export (not established) status: at_risk gaps: - "No scheduled export evidenced" - "Recovery contacts not inventoried" - id: asset-openbao-recovery name: OpenBao sealed recovery material tier: T0 owner: Bernd host: railiance01 recovery_path: method: OpenBao unseal keys + root token recovery procedure backup_admin: Bernd break_glass: ops-warden / railiance-platform runbooks backup: SOPS/age sealed vault (not fully evidenced for cold disaster) status: at_risk gaps: - "Cold disaster recovery of unseal material not drill-tested" references: - railiance-platform/docs/openbao-*.md - id: asset-age-backup-keys name: age encryption keys (backup artifacts) tier: T0 owner: Bernd recovery_path: method: Password manager recovery copy operational: ~/.config/age/railiance-backup.key, ~/.config/sops/age/keys.txt backup: password manager (mandatory) status: operational gaps: - "Password manager copy not verified in register drill" - id: asset-backup-offsite-lane name: Nextcloud backup file drop tier: T0 owner: Bernd recovery_path: method: Nextcloud admin read access for restore; upload via filesdrop token credentials: OpenBao platform/workloads/railiance/backup/offsite-lane backup: N/A (this is the backup destination) status: operational references: - BackupPolicy.md §4 - railiance-cluster/docs/backup-restore.md - id: asset-ssh-warden name: SSH certificate access (ops-warden) tier: T0 owner: Bernd recovery_path: method: warden sign / cert_command break_glass: ops-warden wiki status: operational references: - AGENTS.md credential routing # ── T1 Irreplaceable source of truth ───────────────────────────────────────── - id: asset-gitea-canonical name: Gitea instance (canonical forge until cutover) tier: T1 owner: Bernd host: coulombcore url: https://gitea.coulomb.social backup_stream: backup-gitea-canonical recovery_path: method: gitea dump restore; git mirror status: at_risk - id: asset-forgejo-production name: Forgejo instance (production forge) tier: T1 owner: Bernd host: railiance01 url: https://forgejo.coulomb.social backup_stream: backup-forgejo-production recovery_path: method: forgejo-restore-drill.sh status: not_wired - id: asset-git-repos name: Git repositories (all coulomb/* repos) tier: T1 owner: Bernd source_of_truth: Gitea (migrating to Forgejo) backup: git push to remote; forgejo/gitea dump for full fidelity recovery_path: method: git clone from mirror or dump restore status: operational gaps: [] - id: asset-state-hub name: Custodian State Hub tier: T1 owner: Bernd host: bnt-lap001 (dev workstation) backup_stream: backup-dev-workstation recovery_path: method: PostgreSQL restore from age-decrypted dump status: operational - id: asset-disaster-control name: disaster-control repo (this control plane) tier: T1 owner: Bernd host: gitea.coulomb.social mirror: url: https://forgejo.coulomb.social/coulomb/disaster-control host: railiance01 remote: forgejo-remote:coulomb/disaster-control.git backup: git push to origin + mirror sync recovery_path: method: git clone from Forgejo mirror; see docs/mirror-sync.md backup_schedule: after each push to origin (./tools/sync-mirror.sh) status: operational references: - docs/mirror-sync.md - tools/sync-mirror.sh # ── T2 Runtime platforms ───────────────────────────────────────────────────── - id: asset-coulombcore name: CoulombCore server tier: T2 owner: Bernd host: coulombcore ip: "92.205.130.254" recovery_path: method: Rebuild from Ansible + restore data; do not resurrect compromised host backup_stream: backup-gitea-canonical status: operational - id: asset-railiance01 name: railiance01 k3s cluster tier: T2 owner: Bernd host: railiance01 ip: "92.205.62.239" recovery_path: method: Ansible baseline + Helm redeploy + data restore from backups backup_streams: - backup-s1-os-config - backup-s2-k8s-runtime status: operational gaps: - "S1/S2 backups on-node only" # ── T4 External dependencies ───────────────────────────────────────────────── - id: asset-bubble-io name: Bubble.io legacy application tier: T4 owner: Bernd recovery_path: method: Bubble database export + documented restore workflow limits: Platform-controlled; export cadence not established backup: none automated rpo: 24h rto: 24h status: at_risk gaps: - "No scheduled export evidenced" - "Member data on platform we do not control" - id: asset-openrouter name: OpenRouter API (LLM inference) tier: T4 owner: Bernd recovery_path: method: Alternate LLM provider; API key rotation via OpenBao limits: No data RPO unless storing prompts/results backup: API keys in OpenBao rpo: N/A rto: 1-4h status: operational gaps: - "Alternate provider path not documented in runbook" 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"