Decisions 149-150 recorded; instruction files kept true; R-890, R-891, R-644 closed, R-469 narrowed (142 -> 139)
gates / gates (push) Successful in 2m38s

Decision 150 (operator 13:25): sessions correct stale facts in instruction files themselves,
naming each edit; never loosen a rule. Added to unprompted-work.md §5 (all copies) and
PROMPT-TEMPLATE.md §9. CLAUDE.md gates paragraph (R-891), architecture pointer, docs/website
rules, the doubled R-286 sentence in the workspace CLAUDE.md.
Decision 149: vaultwarden's step proven on bench 9401 and box 9202 (evidence here).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-06 13:52:28 +02:00
parent 7d0dffcf34
commit a29ee9dd33
31 changed files with 1244 additions and 18 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ repo. Sibling repos point here; this is where the pointed-at thing must actually
| Fact | Home |
|---|---|
| the locked design | `architecture/01..05-*.md` |
| the locked design | `architecture/01..11-*.md` |
| capability status + its evidence | `architecture/00-capability-map.md` |
| host addresses, routes, node names, break-glass, what is provisioned | `operations/nodes.md` |
| Tailscale topology, accept-dns/accept-routes | `operations/tailscale.md` |
+12 -1
View File
@@ -6,7 +6,8 @@ unconditional: true
> Goal sessions, nightly sessions, "work the register" sessions. **A session that starts from
> `/goal` or a standing brief inherits these rules exactly as it inherits the gates.** They are the
> part of `PROMPT-TEMPLATE.md` that a task file used to carry and a goal does not. Same wording lives
> in all three repos' `.claude/rules/`; change it in all three or in none.
> in `felhom.eu`, `felhom-controller` and `app-catalog-felhom.eu` `.claude/rules/`, and in the workspace root's
> unversioned `.claude/rules/`; change all four or none.
## 1. What you may pick up on your own
@@ -56,3 +57,13 @@ One screen, plain language, in this order: **decisions you took** (§2) first; w
what broke and whether you fixed it; rows opened and closed with the register size before and after;
what needs the operator, each with what happens if they do nothing. No file paths, no function
names, no row numbers as the subject of a sentence.
## 5. Instruction files
**Instruction files (`CLAUDE.md`, `.claude/rules/*`) are kept true by the session that finds them wrong**
(operator ruling 2026-10-06, `09` §3 decision 150). A session MAY, without asking: correct a stale fact (a command, a
count, a version, a path, a description of what a gate does), add a fact it proved, and remove a reference to something
that no longer exists. Each edit is named in the report (file, line, before, after, why). A session MAY NOT, without the
operator's word: loosen a safety rule, a fence, a „never", a protected machine, a secret rule, or a review step; or
remove a rule. When in doubt, it is a rule change, and it goes to the operator. If Claude Code's own permission check
asks before such an edit, wait for the operator's click; if it refuses, record that and file the exact line.
+2 -2
View File
@@ -18,8 +18,8 @@ tag-based publishing is a separate mechanism — see the R-110 fence in the repo
Website changes go through `python3 scripts/repo_gates.py`, which runs `site_gates.py`. **A new page
must be added to that gate's `PAGES` list** or it is unchecked.
`site_gates.py` asserts: BOM, emoji, nav/footer consistency, analytics, CDN, design tokens, and
cache-busting.
`site_gates.py` asserts: BOM, emoji, nav/footer consistency, analytics, CDN, design tokens, no
embedded `<style>` blocks, and cache-busting.
## The encoding fences
+7 -7
View File
@@ -34,7 +34,7 @@ Four surfaces in one repo, plus the design home for the whole system:
| host addresses, break-glass, node facts | `documentation/operations/nodes.md` — never restate them |
| which box may I break | `documentation/runbooks/target-selection.md` |
| what version is live anywhere | ask the hub (`/hosts`, `/configs`) or the box — **never a doc** |
| the authoritative design | `documentation/architecture/01..05-*.md` |
| the authoritative design | `documentation/architecture/01..11-*.md` (00 = the capability map) |
## Code quality
@@ -81,12 +81,12 @@ box**.
## Gates — ONE entry point
**Run `python3 scripts/repo_gates.py` after ANY change in this repo.** It runs every gate —
`site_gates.py`, `hostinstall_gates.py`, `hub_confirm_gate.py`, `manifest_bearer_gate.py`,
`reuse_refs_check.py`, `instructions_gate.py`, `golden_currency_gate.py`, `wire_contract_gate.py`,
`hub_copy_gate.py` and `due_checks_gate.py` — streaming each gate's own output and exiting
non-zero if any fails. `--fast` selects the gates that touch no network and no container runtime;
today that is all of them. **A missing gate script is a FAILURE, never a skip.**
**Run `python3 scripts/repo_gates.py` after ANY change in this repo.** It runs every gate in its
`GATES` table — **that table is the list**; this file does not restate it (a copy here read ten
gates while the table held nineteen) — streaming each gate's own output and exiting non-zero if any
fails. `--fast` selects the gates that touch no network and no container runtime, and **skips the
full-run-only gates, naming them** (today: `iso-bootstrap`, R-502 — it needs docker and an image).
**A missing gate script is a FAILURE, never a skip.**
`due_checks_gate.py` refuses the push when a dated check in `OPEN-ITEMS.md`'s `DUE-CHECKS` block has
come due (R-341). **It is not a scheduler** — it fires on the next push, not on the date.
+9
View File
@@ -16,6 +16,15 @@
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
> **2026-10-06 (afternoon) — instruction files kept true; vaultwarden on the ladder (`09` §3 149–150).** Decision 150:
> a session corrects a stale fact in `CLAUDE.md` / `.claude/rules/*` itself and names the edit (file, before, after, why);
> it never loosens a rule, fence or „never" without the operator — rule text in `.claude/rules/unprompted-work.md` §5
> (four identical copies: felhom.eu, controller, catalog, the workspace root) and `PROMPT-TEMPLATE.md` §9 rule 9.
> Decision 149: the box walk may seed vaultwarden through its admin invite on scratch 9202 (`box_admin_seed_allowed`,
> five conditions; the invite runs inside the box, the token never leaves it); the Tester 1 box is allowed but the walk
> has no route there. Vaultwarden's first ladder step 1.36.0-alpine → 1.37.4-alpine is live in the catalog (`ecb8552`).
> R-469 re-read: NOT removable — the engine gate is now decision 35's permanent per-app check (operator: close or keep).
> **2026-10-06 (midday) — the operator's ten answers (`09` §3 139–148, „A" for all; CC's own pick differed on 140, 146,
> 147).** Register 150 → 142 (2 opened: R-890 vaultwarden ladder, R-891 a stale CLAUDE.md line; 10 closed). Releases:
> agent v0.149.0 (tag = `f277e61`, sha `6bcae9c2…`, bundle `e182c82d…` — new sudo rule `FELHOM_FSTRIM`; weekly
+7
View File
@@ -272,6 +272,13 @@ Then: [exact refusal — HTTP status, error, and the proven non-effect, e.g. "m
shippable change; **overwrite** REPORT.md.
8. **Coding standards:** follow `<repo>/CLAUDE.md` (cross-cutting) and the feature doc / README for
domain patterns.
9. **Instruction files (`CLAUDE.md`, `.claude/rules/*`) are kept true by the session that finds them wrong**
(operator ruling 2026-10-06, `09` §3 decision 150). A session MAY, without asking: correct a stale fact (a command, a
count, a version, a path, a description of what a gate does), add a fact it proved, and remove a reference to something
that no longer exists. Each edit is named in the report (file, line, before, after, why). A session MAY NOT, without the
operator's word: loosen a safety rule, a fence, a „never", a protected machine, a secret rule, or a review step; or
remove a rule. When in doubt, it is a rule change, and it goes to the operator. If Claude Code's own permission check
asks before such an edit, wait for the operator's click; if it refuses, record that and file the exact line.
---
@@ -905,6 +905,20 @@ its length, and both fixes cost something the household would notice — operato
127. **The agent's three by-design abilities (`03` §3.1) stay for now**; revisited before the first paying customer.
*Operator ruling 2026-10-05.* (R-861)
### 2026-10-06 (13:25) — two operator rulings (recorded before the work)
149. **R-890 — the admin seed is allowed on the test boxes** (scratch 9202 and the disposable Tester 1 box) as well as
on the test bench; it widens decision 146. It is never used on a household box or on a demo box's household apps.
The admin secret is minted per run, never leaves that box, and is never written to a repo, log or report.
*Operator ruling 2026-10-06 13:25* (option A of the row).
150. **Instruction files are kept true by the session that finds them wrong** (R-891). A session may, without asking,
correct a stale fact in a `CLAUDE.md` or `.claude/rules/*` file, add a fact it proved, and remove a reference to
something that no longer exists, naming each edit in its report. It may not, without the operator, loosen a safety
rule, a fence, a „never", a protected machine, a secret rule or a review step, or remove a rule. *Operator ruling
2026-10-06 13:25.* **The reviewer's error, recorded:** the briefs from the night of 2026-10-05 on carried „Do not
edit instruction files", which turned one-line factual corrections into operator rows (R-891, R-469's block). Its
intent was to stop a session loosening its own fences, not to stop it keeping facts true.
### 2026-10-06 (10:41) — ten operator rulings: „A" for all ten (STATUS's ten questions; recorded before the work)
The operator chose option A for all ten. **That differs from CC's own pick on three: 2 (R-99, CC picked B — leave the
@@ -0,0 +1,11 @@
Tue Oct 6 11:47:10 UTC 2026
== all containers
filebrowser Up 5 hours (healthy)
felhom-controller Up 6 minutes (healthy)
paperless-webserver Up 11 hours (healthy)
paperless-postgres Up 11 hours (healthy)
paperless-redis Up 11 hours (healthy)
traefik Up 28 hours
== gokapi
0
0
@@ -0,0 +1,23 @@
# 2026-10-06 (afternoon) — instruction files kept true; vaultwarden on the update ladder (R-890, R-891)
Rulings: `09` §3 decisions 149 (the admin seed on the test boxes) and 150 (instruction files kept true by the session).
## Part B — vaultwarden 1.36.0-alpine → 1.37.4-alpine, both venues
| venue | result | file |
|---|---|---|
| bench LXC 9401 (demo-hp), harness v5, `FELHOM_BENCH_ADMIN_SEED=1`, 600 s memory watch | **proven**: seed read back before and after, healthy in 30.9 s, anon peak 16.3 % (cgroup 22.3 %), 0 kills, 0 restarts, no file changed | `bench/R890-vw.log`, `bench/evidence/MV-vaultwarden/verdict.json` |
| scratch guest 9202 (drill catalog), `FELHOM_BOX_ADMIN_SEED=1` | **proven**: a fresh install; self-registration 400 (closed by design); the admin sign-in and invite INSIDE the box → (200, session yes, 200); invited registration 200; seed read back; the guarded Update backing-up → pulling → copying → verifying → done in 12.3 s; seed read back; badge „Frissítés elérhető — 80 napja" before, „Naprakész" after; removed through the product (no container, no volume left) | `box/vaultwarden/step.txt`, `box/vaultwarden/box-verdict-vaultwarden.json`, `box/run.out` |
Written to the live catalog by `upgrade-test.py --write-ladder` (catalog `ecb8552`). The tool: `tools/vwstep.py` (one
process, so the install and the seed are the same run — the guard requires that). 9202 pointed at the drill:
`box/C1-repoint-drill.txt`.
**Secrets:** the admin token was never on DooPlex — the box read it from the container's environment and handed it to
curl on stdin; only HTTP codes came back. Scan of `bench/` and `box/` for 48+ hex characters, `VW_ADMIN=` and
`ADMIN_TOKEN=` values: 0 hits; control: a fresh 64-hex line → 1 hit. The bench's run `.env` is gone (shredded by the
harness); the bench has 0 containers and is stopped.
## Part C
- R-644: `C/R-644-9202-live.txt` — no gokapi on 9202 any more.
@@ -0,0 +1,115 @@
[11:37:33] scratch drive folders cleared before FROM (R-656): none existed
[11:37:33] MV-vaultwarden: deploying vaultwarden at FROM {'vaultwarden': 'vaultwarden/server:1.36.0-alpine'}
[11:38:04] FROM settled=True in 30.9s :: {"vaultwarden": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[11:38:04] fixture: the BOX walk's own (Vaultwarden), through upgrade_boxport
[11:38:04] vaultwarden: self-registration http=400 (closed by design, R-512 — 400 expected)
[11:38:04] vaultwarden: bench admin sign-in http=200 session=yes
[11:38:04] vaultwarden: bench admin invite http=200
[11:38:05] vaultwarden: invited registration http=200
[11:38:05] vaultwarden: token for the seeded account http=200 ok=True
[11:38:05] C1 (seed reads back BEFORE): True
[11:38:05] MV-vaultwarden: swapping to TO {'vaultwarden': 'vaultwarden/server:1.37.4-alpine'}
[11:38:12] TO up -d rc=0
[11:38:43] TO settled=True in 30.9s :: {"vaultwarden": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[11:38:43] migration lines observed: 0
[11:38:44] vaultwarden: token for the seeded account http=200 ok=True
[11:38:44] RESULT (seed reads back AFTER): True
[11:38:44] memory watch: 600s, 4 callers on 1 path(s) at 172.18.0.2:80
[11:38:59] + 15s vaultwarden=44M/256M peak=45M kills=0 rs=0 reqs=300
[11:39:14] + 30s vaultwarden=44M/256M peak=46M kills=0 rs=0 reqs=600
[11:39:29] + 45s vaultwarden=45M/256M peak=46M kills=0 rs=0 reqs=900
[11:39:45] + 60s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=1200
[11:40:00] + 76s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=1500
[11:40:15] + 91s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=1800
[11:40:30] + 106s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2100
[11:40:45] + 121s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2400
[11:41:00] + 136s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2700
[11:41:15] + 151s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2996
[11:41:30] + 166s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=3296
[11:41:46] + 182s vaultwarden=47M/256M peak=50M kills=0 rs=0 reqs=3596
[11:42:01] + 197s vaultwarden=47M/256M peak=50M kills=0 rs=0 reqs=3896
[11:42:16] + 212s vaultwarden=48M/256M peak=51M kills=0 rs=0 reqs=4196
[11:42:31] + 227s vaultwarden=49M/256M peak=51M kills=0 rs=0 reqs=4496
[11:42:46] + 242s vaultwarden=50M/256M peak=51M kills=0 rs=0 reqs=4796
[11:43:01] + 257s vaultwarden=49M/256M peak=51M kills=0 rs=0 reqs=5096
[11:43:16] + 272s vaultwarden=50M/256M peak=51M kills=0 rs=0 reqs=5396
[11:43:31] + 287s vaultwarden=49M/256M peak=51M kills=0 rs=0 reqs=5696
[11:43:47] + 303s vaultwarden=52M/256M peak=54M kills=0 rs=0 reqs=5996
[11:44:02] + 318s vaultwarden=51M/256M peak=54M kills=0 rs=0 reqs=6292
[11:44:17] + 333s vaultwarden=51M/256M peak=54M kills=0 rs=0 reqs=6592
[11:44:32] + 348s vaultwarden=52M/256M peak=54M kills=0 rs=0 reqs=6892
[11:44:47] + 363s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=7192
[11:45:02] + 378s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=7492
[11:45:17] + 393s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=7792
[11:45:32] + 408s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=8092
[11:45:48] + 424s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=8392
[11:46:03] + 439s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=8692
[11:46:18] + 454s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=8992
[11:46:33] + 469s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=9292
[11:46:48] + 484s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=9589
[11:47:03] + 499s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=9889
[11:47:18] + 514s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=10189
[11:47:34] + 530s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=10489
[11:47:49] + 545s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=10789
[11:48:04] + 560s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=11089
[11:48:19] + 575s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=11389
[11:48:34] + 590s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=11689
[11:48:49] + 605s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=11989
[11:48:50] memory watch: killed=False tight=[] requests=11989 codes={'200': 11989}
[11:48:50] MV-vaultwarden: ABORT — putting the FROM images back
[11:49:21] vaultwarden: token for the seeded account http=200 ok=True
[11:49:21] ABORT: app came back in 30.9s; data present=True
{
"harness_version": 5,
"edge": "MV-vaultwarden",
"app": "vaultwarden",
"note": "night 2026-09-23 within-a-major move: vaultwarden=vaultwarden/server:1.37.4-alpine",
"from": {
"vaultwarden": "vaultwarden/server:1.36.0-alpine"
},
"to": {
"vaultwarden": "vaultwarden/server:1.37.4-alpine"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": null,
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"memory": {
"soak_s": 605.5,
"requested_s": 600,
"requests": 11989,
"codes": {
"200": 11989
},
"first_kill": null,
"containers": {
"vaultwarden": {
"limit": 268435456,
"peak": 59731968,
"peak_pct": 0.223,
"anon_peak_sampled": 43720704,
"anon_peak_pct": 0.163,
"oom_kills": 0,
"restarts": 0,
"oomkilled_flag": false,
"measured": true
}
},
"unmeasured": [],
"load": "reached"
},
"marks": [],
"bench_overrides": null,
"duration_s": 30.9,
"measured_at": "2026-10-06T11:49:21Z",
"evidence": "evidence/MV-vaultwarden",
"scratch_cleared": [],
"files_changed": [],
"files_changed_detail": [],
"files_ignored": [],
"total_s": 708.8
}
@@ -0,0 +1,8 @@
{
"vaultwarden": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,26 @@
vaultwarden | /--------------------------------------------------------------------\
vaultwarden | | Starting Vaultwarden |
vaultwarden | | Version 1.36.0 |
vaultwarden | |--------------------------------------------------------------------|
vaultwarden | | This is an *unofficial* Bitwarden implementation, DO NOT use the |
vaultwarden | | official channels to report bugs/features, regardless of client. |
vaultwarden | | Send usage/configuration questions or feature requests to: |
vaultwarden | | https://github.com/dani-garcia/vaultwarden/discussions or |
vaultwarden | | https://vaultwarden.discourse.group/ |
vaultwarden | | Report suspected bugs/issues in the software itself at: |
vaultwarden | | https://github.com/dani-garcia/vaultwarden/issues/new |
vaultwarden | \--------------------------------------------------------------------/
vaultwarden |
vaultwarden | [NOTICE] You are using a plain text `ADMIN_TOKEN` which is insecure.
vaultwarden | Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.
vaultwarden | See: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token
vaultwarden |
vaultwarden | [2026-10-06 13:48:50.671][start][INFO] Rocket has launched from http://0.0.0.0:80
vaultwarden | [2026-10-06 13:49:21.529][request][INFO] POST /identity/connect/token
vaultwarden | [2026-10-06 13:49:21.529][auth][ERROR] Unauthorized Error: No Bitwarden-Client-Version header provided
vaultwarden | [2026-10-06 13:49:21.689][vaultwarden::api::identity][ERROR] Username or password is incorrect. Try again. IP: 172.18.0.1. Username: drill-d12fe8d2@gate.invalid.
vaultwarden | [2026-10-06 13:49:21.689][response][INFO] (login) POST /identity/connect/token => 400 Bad Request
vaultwarden | [2026-10-06 13:49:21.724][request][INFO] POST /identity/connect/token
vaultwarden | [2026-10-06 13:49:21.724][auth][ERROR] Unauthorized Error: No Bitwarden-Client-Version header provided
vaultwarden | [2026-10-06 13:49:21.879][vaultwarden::api::identity][INFO] User drill logged in successfully. IP: 172.18.0.1
vaultwarden | [2026-10-06 13:49:21.879][response][INFO] (login) POST /identity/connect/token => 200 OK
@@ -0,0 +1,682 @@
[
{
"t": 15.1,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 46456832,
"peak": 47407104,
"anon": 38027264,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 300
},
{
"t": 30.3,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 46735360,
"peak": 49123328,
"anon": 38473728,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 600
},
{
"t": 45.4,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 47403008,
"peak": 49123328,
"anon": 38641664,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 900
},
{
"t": 60.5,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49934336,
"peak": 52183040,
"anon": 40976384,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 1200
},
{
"t": 75.7,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49467392,
"peak": 52183040,
"anon": 40087552,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 1500
},
{
"t": 90.8,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49885184,
"peak": 52183040,
"anon": 40316928,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 1800
},
{
"t": 105.9,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49991680,
"peak": 52183040,
"anon": 40300544,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 2100
},
{
"t": 121.1,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49860608,
"peak": 52183040,
"anon": 40488960,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 2400
},
{
"t": 136.2,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49836032,
"peak": 52183040,
"anon": 39841792,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 2700
},
{
"t": 151.3,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 50278400,
"peak": 52183040,
"anon": 40030208,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 2996
},
{
"t": 166.4,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49954816,
"peak": 52183040,
"anon": 39927808,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 3296
},
{
"t": 181.6,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 50135040,
"peak": 52711424,
"anon": 40226816,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 3596
},
{
"t": 196.7,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 49827840,
"peak": 52711424,
"anon": 39620608,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 3896
},
{
"t": 211.8,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 51314688,
"peak": 54259712,
"anon": 40148992,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 4196
},
{
"t": 226.9,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 51630080,
"peak": 54259712,
"anon": 40075264,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 4496
},
{
"t": 242.1,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 52629504,
"peak": 54259712,
"anon": 40218624,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 4796
},
{
"t": 257.2,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 52166656,
"peak": 54259712,
"anon": 39854080,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 5096
},
{
"t": 272.3,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 52514816,
"peak": 54259712,
"anon": 39993344,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 5396
},
{
"t": 287.4,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 51990528,
"peak": 54259712,
"anon": 40095744,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 5696
},
{
"t": 302.6,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 54804480,
"peak": 57143296,
"anon": 42119168,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 5996
},
{
"t": 317.7,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 53960704,
"peak": 57143296,
"anon": 42205184,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 6292
},
{
"t": 332.8,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 53817344,
"peak": 57143296,
"anon": 41398272,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 6592
},
{
"t": 347.9,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 54542336,
"peak": 57143296,
"anon": 41426944,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 6892
},
{
"t": 363.1,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56356864,
"peak": 58667008,
"anon": 43720704,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 7192
},
{
"t": 378.2,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56516608,
"peak": 58667008,
"anon": 43200512,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 7492
},
{
"t": 393.3,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56545280,
"peak": 58667008,
"anon": 43233280,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 7792
},
{
"t": 408.4,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56168448,
"peak": 58667008,
"anon": 42938368,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 8092
},
{
"t": 423.6,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56717312,
"peak": 58896384,
"anon": 43270144,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 8392
},
{
"t": 438.7,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57171968,
"peak": 58896384,
"anon": 42594304,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 8692
},
{
"t": 453.9,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57262080,
"peak": 58896384,
"anon": 42614784,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 8992
},
{
"t": 469.0,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57131008,
"peak": 58896384,
"anon": 42680320,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 9292
},
{
"t": 484.1,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57974784,
"peak": 59424768,
"anon": 42905600,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 9589
},
{
"t": 499.3,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57352192,
"peak": 59424768,
"anon": 42680320,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 9889
},
{
"t": 514.4,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56872960,
"peak": 59424768,
"anon": 42700800,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 10189
},
{
"t": 529.5,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57950208,
"peak": 59424768,
"anon": 42795008,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 10489
},
{
"t": 544.7,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57360384,
"peak": 59424768,
"anon": 43061248,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 10789
},
{
"t": 559.8,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57270272,
"peak": 59424768,
"anon": 42733568,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 11089
},
{
"t": 574.9,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 57892864,
"peak": 59731968,
"anon": 42790912,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 11389
},
{
"t": 590.1,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 56963072,
"peak": 59731968,
"anon": 42803200,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 11689
},
{
"t": 605.2,
"containers": {
"vaultwarden": {
"limit": 268435456,
"current": 58019840,
"peak": 59731968,
"anon": 43212800,
"oom_kill": 0,
"restarts": 0,
"oomkilled_flag": false,
"status": "running",
"cgroup": true
}
},
"requests": 11989
}
]
@@ -0,0 +1,61 @@
[11:37:33] scratch drive folders cleared before FROM (R-656): none existed
[11:37:33] MV-vaultwarden: deploying vaultwarden at FROM {'vaultwarden': 'vaultwarden/server:1.36.0-alpine'}
[11:38:04] FROM settled=True in 30.9s :: {"vaultwarden": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[11:38:04] fixture: the BOX walk's own (Vaultwarden), through upgrade_boxport
[11:38:04] vaultwarden: self-registration http=400 (closed by design, R-512 — 400 expected)
[11:38:04] vaultwarden: bench admin sign-in http=200 session=yes
[11:38:04] vaultwarden: bench admin invite http=200
[11:38:05] vaultwarden: invited registration http=200
[11:38:05] vaultwarden: token for the seeded account http=200 ok=True
[11:38:05] C1 (seed reads back BEFORE): True
[11:38:05] MV-vaultwarden: swapping to TO {'vaultwarden': 'vaultwarden/server:1.37.4-alpine'}
[11:38:12] TO up -d rc=0
[11:38:43] TO settled=True in 30.9s :: {"vaultwarden": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[11:38:43] migration lines observed: 0
[11:38:44] vaultwarden: token for the seeded account http=200 ok=True
[11:38:44] RESULT (seed reads back AFTER): True
[11:38:44] memory watch: 600s, 4 callers on 1 path(s) at 172.18.0.2:80
[11:38:59] + 15s vaultwarden=44M/256M peak=45M kills=0 rs=0 reqs=300
[11:39:14] + 30s vaultwarden=44M/256M peak=46M kills=0 rs=0 reqs=600
[11:39:29] + 45s vaultwarden=45M/256M peak=46M kills=0 rs=0 reqs=900
[11:39:45] + 60s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=1200
[11:40:00] + 76s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=1500
[11:40:15] + 91s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=1800
[11:40:30] + 106s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2100
[11:40:45] + 121s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2400
[11:41:00] + 136s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2700
[11:41:15] + 151s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=2996
[11:41:30] + 166s vaultwarden=47M/256M peak=49M kills=0 rs=0 reqs=3296
[11:41:46] + 182s vaultwarden=47M/256M peak=50M kills=0 rs=0 reqs=3596
[11:42:01] + 197s vaultwarden=47M/256M peak=50M kills=0 rs=0 reqs=3896
[11:42:16] + 212s vaultwarden=48M/256M peak=51M kills=0 rs=0 reqs=4196
[11:42:31] + 227s vaultwarden=49M/256M peak=51M kills=0 rs=0 reqs=4496
[11:42:46] + 242s vaultwarden=50M/256M peak=51M kills=0 rs=0 reqs=4796
[11:43:01] + 257s vaultwarden=49M/256M peak=51M kills=0 rs=0 reqs=5096
[11:43:16] + 272s vaultwarden=50M/256M peak=51M kills=0 rs=0 reqs=5396
[11:43:31] + 287s vaultwarden=49M/256M peak=51M kills=0 rs=0 reqs=5696
[11:43:47] + 303s vaultwarden=52M/256M peak=54M kills=0 rs=0 reqs=5996
[11:44:02] + 318s vaultwarden=51M/256M peak=54M kills=0 rs=0 reqs=6292
[11:44:17] + 333s vaultwarden=51M/256M peak=54M kills=0 rs=0 reqs=6592
[11:44:32] + 348s vaultwarden=52M/256M peak=54M kills=0 rs=0 reqs=6892
[11:44:47] + 363s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=7192
[11:45:02] + 378s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=7492
[11:45:17] + 393s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=7792
[11:45:32] + 408s vaultwarden=53M/256M peak=55M kills=0 rs=0 reqs=8092
[11:45:48] + 424s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=8392
[11:46:03] + 439s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=8692
[11:46:18] + 454s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=8992
[11:46:33] + 469s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=9292
[11:46:48] + 484s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=9589
[11:47:03] + 499s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=9889
[11:47:18] + 514s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=10189
[11:47:34] + 530s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=10489
[11:47:49] + 545s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=10789
[11:48:04] + 560s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=11089
[11:48:19] + 575s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=11389
[11:48:34] + 590s vaultwarden=54M/256M peak=56M kills=0 rs=0 reqs=11689
[11:48:49] + 605s vaultwarden=55M/256M peak=56M kills=0 rs=0 reqs=11989
[11:48:50] memory watch: killed=False tight=[] requests=11989 codes={'200': 11989}
[11:48:50] MV-vaultwarden: ABORT — putting the FROM images back
[11:49:21] vaultwarden: token for the seeded account http=200 ok=True
[11:49:21] ABORT: app came back in 30.9s; data present=True
@@ -0,0 +1,18 @@
vaultwarden | /--------------------------------------------------------------------\
vaultwarden | | Starting Vaultwarden |
vaultwarden | | Version 1.37.4 |
vaultwarden | |--------------------------------------------------------------------|
vaultwarden | | This is an *unofficial* Bitwarden implementation, DO NOT use the |
vaultwarden | | official channels to report bugs/features, regardless of client. |
vaultwarden | | Send usage/configuration questions or feature requests to: |
vaultwarden | | https://github.com/dani-garcia/vaultwarden/discussions or |
vaultwarden | | https://vaultwarden.discourse.group/ |
vaultwarden | | Report suspected bugs/issues in the software itself at: |
vaultwarden | | https://github.com/dani-garcia/vaultwarden/issues/new |
vaultwarden | \--------------------------------------------------------------------/
vaultwarden |
vaultwarden | [NOTICE] You are using a plain text `ADMIN_TOKEN` which is insecure.
vaultwarden | Please generate a secure Argon2 PHC string by using `vaultwarden hash` or `argon2`.
vaultwarden | See: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page#secure-the-admin_token
vaultwarden |
vaultwarden | [2026-10-06 13:38:12.635][start][INFO] Rocket has launched from http://0.0.0.0:80
@@ -0,0 +1,8 @@
{
"vaultwarden": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,54 @@
{
"harness_version": 5,
"edge": "MV-vaultwarden",
"app": "vaultwarden",
"note": "night 2026-09-23 within-a-major move: vaultwarden=vaultwarden/server:1.37.4-alpine",
"from": {
"vaultwarden": "vaultwarden/server:1.36.0-alpine"
},
"to": {
"vaultwarden": "vaultwarden/server:1.37.4-alpine"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": null,
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"memory": {
"soak_s": 605.5,
"requested_s": 600,
"requests": 11989,
"codes": {
"200": 11989
},
"first_kill": null,
"containers": {
"vaultwarden": {
"limit": 268435456,
"peak": 59731968,
"peak_pct": 0.223,
"anon_peak_sampled": 43720704,
"anon_peak_pct": 0.163,
"oom_kills": 0,
"restarts": 0,
"oomkilled_flag": false,
"measured": true
}
},
"unmeasured": [],
"load": "reached"
},
"marks": [],
"bench_overrides": null,
"duration_s": 30.9,
"measured_at": "2026-10-06T11:49:21Z",
"evidence": "evidence/MV-vaultwarden",
"scratch_cleared": [],
"files_changed": [],
"files_changed_detail": [],
"files_ignored": [],
"total_s": 708.8
}
@@ -0,0 +1,2 @@
26: repo_url: https://gitea.dooplex.hu/admin/app-catalog-drill.git
@@ -0,0 +1,18 @@
{
"app": "vaultwarden",
"venue": "box 9202 (drill catalog), the product's guarded Update; seeded through the admin invite inside the box (R-890)",
"from": {
"vaultwarden": "vaultwarden/server:1.36.0-alpine"
},
"to": {
"vaultwarden": "vaultwarden/server:1.37.4-alpine"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"duration_s": 12.3,
"final_phase": "done",
"measured_at": "2026-10-06T11:41:48Z",
"evidence": "felhom.eu/documentation/audits/r890-instructions-2026-10-06/box/vaultwarden/step.txt"
}
@@ -0,0 +1,37 @@
vaultwarden: self-registration http=400 (closed by design, R-512 — 400 expected)
vaultwarden: test-box admin sign-in and invite (inside the box) -> ('200', 'yes', '200')
vaultwarden: invited registration http=200
vaultwarden: token for the seeded account http=200 ok=True
C1 seed reads back BEFORE: True
before: pinned={'vaultwarden': 'vaultwarden/server:1.36.0-alpine'}
drill: a603087 DRILL vaultwarden: vaultwarden/server:1.36.0-alpine -> vaultwarden/server:1.37.4-alpine (box proof, R-890)
badge before: {'hu': [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — 80 napja'}], 'en': [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — 80 days ago'}]}
phase +0.0s backing-up | err=None
phase +2.1s pulling | err=None
phase +6.2s copying | err=None
phase +7.2s verifying | err=None
phase +12.3s done | err=None
vaultwarden: token for the seeded account http=200 ok=True
2026/10/06 11:41:50 update.go:537: [INFO] [stacks] update vaultwarden: accepted — guarded update started
2026/10/06 11:41:50 update.go:1377: [INFO] [stacks] update vaultwarden: phase checking
2026/10/06 11:41:50 update.go:795: [INFO] [stacks] update vaultwarden: ladder — the last step (1 of 1) — the catalog's current definition
2026/10/06 11:41:50 update.go:825: [INFO] [stacks] update vaultwarden: no usable copy on any tier — younger than 24h0m0s and not older than this install's deploy (2026-10-06T11:41:17Z) (found: none) — backing up first
2026/10/06 11:41:50 update.go:1377: [INFO] [stacks] update vaultwarden: phase backing-up
2026/10/06 11:41:51 update.go:840: [INFO] [stacks] update vaultwarden: precondition met after the backup — Tier 2 (second drive) copy from 2026-10-06T11:41:50Z
2026/10/06 11:41:51 update.go:1377: [INFO] [stacks] update vaultwarden: phase safety-dump
2026/10/06 11:41:51 update.go:855: [INFO] [stacks] update vaultwarden: safety dump done (0 file(s)) []
2026/10/06 11:41:52 undo.go:287: [INFO] [stacks] update vaultwarden: the undo copy will hold 1 named volume(s), 0.3 MiB
2026/10/06 11:41:52 update.go:1377: [INFO] [stacks] update vaultwarden: phase pinning
2026/10/06 11:41:52 pin.go:373: [INFO] [stacks] update vaultwarden: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/vaultwarden/docker-compose.yml (vaultwarden=vaultwarden/server:1.37.4-alpine)
2026/10/06 11:41:52 update.go:1377: [INFO] [stacks] update vaultwarden: phase pulling
2026/10/06 11:41:55 update.go:1377: [INFO] [stacks] update vaultwarden: phase copying
2026/10/06 11:41:56 update.go:1377: [INFO] [stacks] update vaultwarden: phase copying
2026/10/06 11:41:56 undo.go:437: [INFO] [stacks] update vaultwarden: copied vaultwarden_vaultwarden_data → vaultwarden_vaultwarden_data.pre-update-20261006T114156Z in 461ms
2026/10/06 11:41:56 update.go:1377: [INFO] [stacks] update vaultwarden: phase starting
2026/10/06 11:41:56 update.go:1377: [INFO] [stacks] update vaultwarden: phase verifying
2026/10/06 11:42:02 update.go:1006: [INFO] [stacks] update vaultwarden: healthy after 5s (the app's health check passed)
2026/10/06 11:42:02 update.go:1042: [INFO] [stacks] update vaultwarden: DONE in 12s
badge after: {'hu': [{'title': 'Ez az alkalmazás a legfrissebb elérhető változatot futtatja.', 'text': 'Naprakész'}], 'en': [{'title': 'This app is running the newest version available.', 'text': 'Up to date'}]}
RESULT final_phase=done after={'vaultwarden': 'vaultwarden/server:1.37.4-alpine'} seed_after=True verdict=proven (12.3 s)
remove -> 200
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Point 9202 at the drill catalog, or put the saved controller.yaml back. `09` §6.5."""
import io, os, re, sys
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
VOL = "/var/lib/docker/volumes/felhom-controller-data/_data"
SAVE = f"{VOL}/controller.yaml.pre-r890"
DRILL = "https://gitea.dooplex.hu/admin/app-catalog-drill.git"
def creds():
for l in io.open(os.path.expanduser("~/.git-credentials")).read().split("\n"):
m = re.match(r"https://(admin):([^@]+)@gitea\.dooplex\.hu", l)
if m: return m.group(1), m.group(2)
sys.exit("no admin credential")
if sys.argv[1] == "drill":
u, t = creds()
out = w.guest(f"""set -e
test -f {SAVE} || cp -p {VOL}/controller.yaml {SAVE}
python3 - <<'PY'
import re
p = "{VOL}/controller.yaml"; s = open(p).read()
s = re.sub(r'(^\\s+repo_url: ).*$', r'\\g<1>{DRILL}', s, count=1, flags=re.M)
s = re.sub(r'(^git:(?:\\n\\s+.*)*?\\n\\s+token: ).*$', r'\\g<1>"{t}"', s, count=1, flags=re.M)
s = re.sub(r'(^git:(?:\\n\\s+.*)*?\\n\\s+username: ).*$', r'\\g<1>"{u}"', s, count=1, flags=re.M)
open(p, "w").write(s)
PY
rm -rf {VOL}/catalog-cache {VOL}/data/catalog-cache
docker restart felhom-controller >/dev/null
grep -n 'repo_url' {VOL}/controller.yaml
""")
print(out.replace(t, "<token>"))
elif sys.argv[1] == "restore":
print(w.guest(f"""set -e
cp -p {SAVE} {VOL}/controller.yaml
rm -rf {VOL}/catalog-cache {VOL}/data/catalog-cache
docker restart felhom-controller >/dev/null
grep -n 'repo_url' {VOL}/controller.yaml; grep -c 'token: ""' {VOL}/controller.yaml || true
cmp {SAVE} {VOL}/controller.yaml && echo RESTORED-IDENTICAL"""))
@@ -0,0 +1,76 @@
"""vwstep.py <to-ref> — R-890: vaultwarden's step on scratch 9202 (drill catalog), ONE process, through the product.
1 install vaultwarden fresh at the live pin (this run installs it — the admin seed refuses otherwise);
2 seed through the household's door, the invite through the admin page INSIDE the box
(FELHOM_BOX_ADMIN_SEED=1, upgrade_fixtures_box.box_admin_seed_allowed); read it back (C1);
3 a DRILL-only commit moves the image and adds a ladder entry; sync, rescan;
4 the product's guarded Update; the seed read back; box verdict JSON;
5 remove through the product.
Evidence: ../box/vaultwarden/step.txt + box-verdict-vaultwarden.json. The live entry is written ONLY by
`upgrade-test.py --write-ladder` from both verdicts."""
import json, os, re, subprocess, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
import upgrade_fixtures_box as fixtures
APP, SUB, SVC = "vaultwarden", "vault", "vaultwarden"
to = sys.argv[1]
HERE = os.path.dirname(os.path.abspath(__file__))
EVD = os.path.join(HERE, "..", "box", APP); os.makedirs(EVD, exist_ok=True)
log = open(f"{EVD}/step.txt", "a", buffering=1)
D = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill"
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
fx = fixtures.FIXTURES[APP]
w.login()
if w.stack(APP).get("deployed"):
say("vaultwarden already installed on 9202 — removing it first (scratch box)")
w.remove(APP)
w.sync_rescan()
if not w.deploy(APP, SUB):
sys.exit(say("RESULT the install did not complete") or 1)
tok = fx.seed(w, SUB, say)
if tok is None or not fx.verify(w, SUB, tok, say):
say(f"RESULT C1 failed: {getattr(fx, 'tried', '')}")
w.remove(APP); sys.exit(1)
say("C1 seed reads back BEFORE: True")
before = (w.stack(APP).get("app_config") or {}).get("pinned_images")
say(f"before: pinned={before}")
subprocess.run(["git", "-C", D, "pull", "-q", "--rebase", "origin", "main"], check=True)
comp, fy = f"{D}/templates/{APP}/docker-compose.yml", f"{D}/templates/{APP}/.felhom.yml"
s = open(comp).read()
frm = re.search(r"^\s+image:\s*(\S+)", s, re.M).group(1)
open(comp, "w").write(s.replace("image: " + frm, "image: " + to, 1))
entry = {"from": {SVC: frm}, "to": {SVC: to}, "verdict": "proven", "tested_at": "DRILL", "harness_version": 5,
"evidence": "DRILL (box proof in progress)", "marks": {"files_may_change": False, "needs_person": None, "memory_tight": False}}
f = open(fy).read()
f = (f.rstrip("\n") + "\n - " + json.dumps(entry) + "\n") if "update_ladder:" in f else (f.rstrip("\n") + "\nupdate_ladder:\n - " + json.dumps(entry) + "\n")
open(fy, "w").write(f)
subprocess.run(["git", "-C", D, "commit", "-q", "-am", f"DRILL {APP}: {frm} -> {to} (box proof, R-890)"], check=True)
subprocess.run(["git", "-C", D, "push", "-q", "origin", "main"], check=True, capture_output=True)
say("drill:", subprocess.run(["git", "-C", D, "log", "--oneline", "-1"], capture_output=True, text=True).stdout.strip())
w.sync_rescan(APP, to)
say(f"badge before: {w.badges(APP)}")
since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
res = w.press_update(APP, poll=1, cap_s=1800)
for p in res.get("phases", []):
log.write(f" phase +{p['t']}s {p['phase']} | err={p['error']}\n")
time.sleep(10)
read = fx.verify(w, SUB, tok, say)
lines = w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'update {APP}' | grep -v DEBUG | cut -c1-400")
log.write(lines + "\n")
st = w.stack(APP); after = (st.get("app_config") or {}).get("pinned_images")
verdict = {"app": APP, "venue": "box 9202 (drill catalog), the product's guarded Update; seeded through the admin invite inside the box (R-890)",
"from": before, "to": after,
"verdict": "proven" if (res.get("final_phase") == "done" and read and (after or {}).get(SVC) == to) else "failed",
"seed_read_before": True, "seed_read_after": read, "healthy_after": st.get("state") == "running",
"duration_s": res.get("duration_s"), "final_phase": res.get("final_phase"), "measured_at": since,
"evidence": "felhom.eu/documentation/audits/r890-instructions-2026-10-06/box/vaultwarden/step.txt"}
json.dump(verdict, open(f"{EVD}/box-verdict-{APP}.json", "w"), indent=2)
say(f"badge after: {w.badges(APP)}")
say(f"RESULT final_phase={res.get('final_phase')} after={after} seed_after={read} verdict={verdict['verdict']} ({res.get('duration_s')} s)")
say(f"remove -> {w.remove(APP)}")
+10
View File
@@ -26,6 +26,16 @@
---
## 2026-10-06 (afternoon) — instruction files kept true; vaultwarden on the ladder
The full text of every row below: `git show 7d0dffcf34:documentation/backlog/OPEN-ITEMS.md`.
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-644** | **[P3-LOW] `gokapi` on scratch guest 9202 is crash-looping — 329 restarts by 2026-09-23 07:51 UTC, *password does not appear to be a SHA-1 hash* — and the controller still lists it deployed.** (P4) | CLOSED 2026-10-06 — NO LONGER PRESENT (live read) | 9202 at 11:47Z: no gokapi container and no gokapi volume (control: the same listing shows the six running containers); the controller lists only paperless-ngx and privatebin deployed — `audits/r890-instructions-2026-10-06/C/R-644-9202-live.txt`. |
| **R-890** | **A vaultwarden update step still cannot be written to the ladder: the ladder writer needs the step proven on BOTH the bench and the box, and decision 146 keeps the admin seed on the bench only.** (P4) | CLOSED 2026-10-06 — BUILT AND PROVEN (decision 149): the test box seeds vaultwarden through its admin invite inside the box; the first vaultwarden step is in the ladder | catalog `6b4877d` (box_admin_seed_allowed, five conditions; BoxAdminSeedGuard red-proved) and `ecb8552` (vaultwarden 1.36.0-alpine -> 1.37.4-alpine written by --write-ladder); bench 9401 proven (anon peak 16.3 %, seed read back), box 9202 proven (guarded Update done 12.3 s, seed read back) — `audits/r890-instructions-2026-10-06/`. The Tester 1 box is allowed by the ruling but the walk has no route to it. |
| **R-891** | **felhom.eu `CLAUDE.md` says `--fast` selects „all of them" — no longer true since the full-run-only `iso-bootstrap` gate (R-502, 2026-10-06).** (P4) | CLOSED 2026-10-06 — FIXED (decision 150: instruction files are kept true by the session) | felhom.eu `CLAUDE.md` „Gates — ONE entry point" now says the `GATES` table is the list (nineteen gates; the old copy named ten) and that `--fast` skips the full-run-only gates, naming them (today `iso-bootstrap`); `repo_gates.py` read: 19 gates, one not fast. |
## 2026-10-06 (midday) — R-444 live
The full text of every row below: `git show bfdea832:documentation/backlog/OPEN-ITEMS.md`.
File diff suppressed because one or more lines are too long
@@ -57,9 +57,6 @@ roles. **A file being open in the editor is NOT an instruction. If no task is st
"working" and "stopped entirely". **And the control must come from a DIFFERENT channel than the measurement** (R-286):
same query, same snapshot, same API or same clock all share the defect they are meant to catch — a
stale hub snapshot once "confirmed" itself (2 events all day) while the operator's mailbox held eight
alarms. A hub-state check copies `hub.db-wal` too, or asks the running pod. **And the control must come from a DIFFERENT channel than the measurement** (R-286):
same query, same snapshot, same API or same clock all share the defect they are meant to catch — a
stale hub snapshot once "confirmed" itself (2 events all day) while the operator's mailbox held eight
alarms. A hub-state check copies `hub.db-wal` too, or asks the running pod.
4. **A recommendation that is not followed gets one line saying why.** Silence reads as agreement and
the disagreement is lost.