Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
155 lines
6.7 KiB
Markdown
155 lines
6.7 KiB
Markdown
---
|
||
id: RISK-F-0006
|
||
type: finding
|
||
title: "apps-pg has no backup configured at all: R0 means no recovery"
|
||
status: fixed
|
||
owner: risk-nexus
|
||
reported_by: railiance-platform
|
||
reported_via: flex-auth
|
||
routed_by: risk-nexus
|
||
date_reported: "2026-08-17"
|
||
date_filed: "2026-08-19"
|
||
system: railiance-platform
|
||
environment: production
|
||
fix_owner: railiance-platform
|
||
fix_tracking: RPF-WP-0019 (finished)
|
||
related: [RISK-F-0001]
|
||
# Graded by risk-nexus 2026-08-19 — docs/rulings/2026-08-19-second-grading.md
|
||
severity: high
|
||
severity_at_production: critical
|
||
impact: I4
|
||
likelihood: L2
|
||
fidelity_modifier: false
|
||
production_rescore: true
|
||
disclosure: public
|
||
publication: pending-handover
|
||
publication_id: risk-f-0006-apps-pg-no-backup
|
||
publication_path: "findings/apps-pg-no-backup/v1/index.html"
|
||
publication_subtitle: "A shared production database had no backup or recovery path; encrypted off-host backup and a demonstrated restore closed the gap."
|
||
revision: "fixed-1"
|
||
last_reviewed: "2026-08-21"
|
||
review_interval: 6m
|
||
embargo_lifted: "2026-08-20 — backup live, restore demonstrated with matching row counts"
|
||
embargo_was_since: "2026-08-19"
|
||
escalation: answered
|
||
escalation_trigger: 3
|
||
escalation_status: answered
|
||
date_fixed: "2026-08-20"
|
||
escalation_answered: "2026-08-19"
|
||
escalation_answered_by: the-custodian
|
||
escalation_act: approve
|
||
decision: "spend for apps-pg backup storage approved; no ceiling stated"
|
||
last_checked: "2026-08-21T07:32:09Z"
|
||
next_check: "2026-08-21T07:32:09Z"
|
||
cadence: instant
|
||
clean_streak: 0
|
||
graded_by: risk-nexus
|
||
ruling: RISK-RULING-2026-08-19-B
|
||
checked_by: "risk-nexus"
|
||
---
|
||
|
||
# RISK-F-0006 — the platform database cannot be recovered
|
||
|
||
## What is true, as reported
|
||
|
||
`apps-pg` (`railiance-platform`) has no backup configured at all: no
|
||
`barmanObjectStore`, no retention policy, and `BestEffort` QoS on the pod.
|
||
Reported in the same round as `RISK-F-0001`, whose author noted that `R0` here
|
||
means **no recovery**, not merely no erasure policy.
|
||
|
||
Not verified by this repo. `railiance-platform` owns it.
|
||
|
||
## Why it is graded highest of the round despite being the least exciting
|
||
|
||
The other findings in this round are about someone reaching something. This one
|
||
is about nothing coming back. There is no attacker in it and no clever step —
|
||
an ordinary node failure, a lost volume, a mistaken `DROP`, and the data that
|
||
was in `apps-pg` is gone.
|
||
|
||
`BestEffort` QoS makes the pod the first candidate for eviction under memory
|
||
pressure. Eviction alone is an availability event and recoverable. What is not
|
||
recoverable is anything that damages the volume, and that is the case with no
|
||
answer today.
|
||
|
||
## Register ruling — 2026-08-19
|
||
|
||
`high` today (`I4` × `L2`, non-adversarial reading), `critical` at production,
|
||
embargoed, and **escalated on trigger 3 (spend)**.
|
||
|
||
`I4`: `apps-pg` is shared platform infrastructure, so the loss is not confined
|
||
to one system, and it is unrecoverable rather than degraded. `L2` on the
|
||
non-adversarial reading the scale gained for this finding: irrecoverable loss
|
||
needs a volume or corruption event, which is an ordinary failure the estate
|
||
has seen the shape of but not an expected weekly occurrence.
|
||
|
||
`production_rescore: true`: at production this is `L3`, because the surface
|
||
area of ordinary operational events grows with real traffic and real operators.
|
||
|
||
**Escalation, trigger 3.** Object storage for backups is recurring spend that
|
||
is not currently committed, and no repo can authorise it for itself. The ask
|
||
is narrow: approve a backup target and its cost, or state that the estate
|
||
accepts running `apps-pg` with no recovery and for how long. The second is a
|
||
legitimate answer in build mode and needs saying out loud rather than
|
||
happening by default.
|
||
|
||
The disclosure condition deliberately includes a demonstrated restore. A
|
||
backup nobody has restored from is a claim, not a control — the same class of
|
||
error as the gate in `RISK-F-0002`.
|
||
|
||
## Reviews
|
||
|
||
- **2026-08-19** — filed and graded from `RISK-F-0001`'s unfiled list.
|
||
Open at review: has the spend been ruled on; is a backup configured; has a
|
||
restore been demonstrated; does `railiance-platform` track it anywhere.
|
||
|
||
## Operator decision — 2026-08-19: approved
|
||
|
||
The spend is approved. `railiance-platform` may provision backup storage for
|
||
`apps-pg` without returning for authorisation.
|
||
|
||
No ceiling was stated, so none is recorded. The register asks
|
||
`railiance-platform` to report the actual target and its monthly cost once
|
||
chosen; a figure materially above the trigger-3 band (recurring €50/month)
|
||
comes back for confirmation rather than being assumed covered. That is the
|
||
register being careful with an open approval, not a condition on it.
|
||
|
||
**What is now blocking is work, not permission.** The finding stays `open` at
|
||
`high`, and the embargo condition is unchanged: a backup exists **and** a
|
||
restore has been demonstrated once. A backup nobody has restored from is a
|
||
claim, not a control.
|
||
|
||
`fix_tracking` is still `unset` and is now `railiance-platform`'s to open.
|
||
|
||
## Reviews
|
||
|
||
- **2026-08-19** — escalation answered, spend approved. Open at review: is a
|
||
backup configured; has a restore been demonstrated; what does it cost.
|
||
- **2026-08-20** — clean check: checked against the inbox and the owner's record; nothing moved. Cadence instant → 1h (1 clean in a row); next check 2026-08-20 11:02Z.
|
||
|
||
## Check — 2026-08-21: fixed, and the evidence was read rather than taken
|
||
|
||
`railiance-platform` finished `RPF-WP-0019` on 2026-08-20. This register read
|
||
their evidence file rather than accepting the report:
|
||
`railiance-platform/docs/evidence/RPF-WP-0019-backup-restore-2026-08-20.md`.
|
||
|
||
- Governed target live at `s3://railiance-platform-pg-backup/platform-pg/apps-pg/`,
|
||
30-day retention, continuous WAL.
|
||
- `apps-pg-daily-20260820204148` completed in 8s, `LastBackupSucceeded=True`.
|
||
- **A separately named scratch cluster restored both consumer databases in 56s,
|
||
with exact database sizes and all 13 `coulomb_social` user-table row counts
|
||
matching.** The scratch namespace was deleted; production stayed `Ready`.
|
||
|
||
**The embargo condition required a demonstrated restore and got one.** That
|
||
condition existed because a backup nobody has restored from is a claim, not a
|
||
control — and the demonstration is the difference between this finding closing
|
||
and merely appearing to.
|
||
|
||
They also fixed something the grading had cited but not asked for: `apps-pg-1`
|
||
moved from `BestEffort` to `Burstable` QoS, with requests and limits, which was
|
||
part of why `L2` rather than `L1`. And their evidence records a failed first WAL
|
||
attempt against the wrong prefix rather than only the successful one, which is
|
||
the standard this register keeps asking of reporters.
|
||
|
||
`fixed`, `public`, escalation closed. The spend approved on 2026-08-19 has a
|
||
real target behind it.
|
||
- **2026-08-21** — not clean: owner replied; see the dated check section Cadence 1h → instant; checked again immediately.
|