2 Commits
Author SHA1 Message Date
adminandClaude Fable 5.1 4727aaa5ad register: compress R-459 and R-467 to CLOSED-ITEMS (full text at ae59c31); REPORT sizes + CI
gates / gates (push) Successful in 20s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-13 10:15:46 +02:00
adminandClaude Fable 5.1 ae59c31a84 R-459 CLOSED (MariaDB converts itself, proven by harness + live), golden 0.236.0 (R-467), the golden waiver (R-468)
Operator rulings 2026-09-13, both shipped the same day:
- MariaDB finishes its own conversion (catalog eec1228/bd32830/3525e35). Harness E3/E3b `proven`
  with engine_state_after "already upgraded to 12.3.3-MariaDB [exit=1]", the skip line gone, C3
  still `failed`; landed on demo-hp through the real 15-min cycle, nothing recreated, one deliberate
  restart logged "MariaDB upgrade not required" with the app serving. Evidence:
  documentation/audits/r459-close-2026-09-13/. The engine-major rule + gate keep every engine
  inside its major until Slice 4 (R-448) — removal tracked as R-469.
- Goldens on a cadence, not per release. golden_currency_gate.py reads a dated waiver
  (documentation/tests/golden-waiver.yml, <= 14 days, row-bound): valid + BEHIND -> loud advisory,
  exit 0; expired -> red again naming the date; UNRECORDED (R-385) never covered; malformed -> 2,
  never 0. Tests cases 5-15 incl. the R-421 decoy; red-proof old-vs-new on the real behind tree.
  R-242's vouch half stays open. Cadence in RUNBOOK-manual-build.md §4.2 + the checklist.
- Golden 0.236.0 baked, round-tripped, vouched, floor raised 0.232.0 -> 0.236.0
  (documentation/tests/golden-0.236.0-2026-09-13/) — the last per-release bake; the waiver was
  issued AFTER it landed. No --no-verify anywhere in this session.

Rows: R-459 CLOSED, R-467 CLOSED, R-242 narrowed; R-468/R-469/R-470/R-471 opened. 09 §3 gains
decisions 5 and 6; STATUS items 11 and 12 closed; CONTEXT records the cadence ruling.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-13 10:14:37 +02:00
63 changed files with 2614 additions and 220 deletions

No files matched your search

+6
View File
@@ -135,6 +135,12 @@ something, not only sessions that touch `documentation/` — which is why it is
- **Root `STATUS.md`** — at the end of every session in which something shipped, broke or was
decided. It is a **view** of `OPEN-ITEMS.md`; nothing may exist only there. One screen, written for
the operator in plain language, and deliberately **not** `CONTEXT.md`.
- **The golden, on its cadence** (operator ruling 2026-09-13): **weekly, and before ANY drill or
fresh install**, bake + vouch + raise the floor per `documentation/runbooks/RUNBOOK-manual-build.md`
§4.1. Not per release. Between bakes the dated waiver (§4.2, `documentation/tests/golden-waiver.yml`,
≤ 14 days) keeps `golden_currency_gate.py` advisory; **when it expires the gate is red and stays
red until someone bakes or renews — that is the mechanism, so do not `--no-verify` past it.** A
nightly or drill session that starts on a fresh install checks the golden FIRST.
- **The capability map** (`documentation/architecture/00-capability-map.md`), if a capability's
status changed — with its new evidence citation.
- **`python3 scripts/unproven.py --summary`** — one line per status, and the not-walked total. Run it
+27
View File
@@ -14,6 +14,33 @@
> language, one screen, no identifiers in the prose. Same subjects, different readers; merging them
> would make one of the two audiences stop reading. `STATUS.md` is also a **view of `OPEN-ITEMS.md`**
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
## Goldens move to a CADENCE, and the gate learns to read a dated waiver (2026-09-13, R-468 / R-242 / R-467)
**The ruling (operator, 2026-09-13):** *bake on a cadence — weekly, and always before any drill or
fresh install — not per release.* **The reason:** `golden_currency_gate.py` trips on every release by
design, and in August that produced **25 goldens in 26 days** plus thirteen declared `--no-verify`
bypasses (R-404/R-417). A guard bypassed that often teaches everyone to bypass it. Every release still
raises the FLOOR, so the demo boxes keep getting each release in ~20 s; only the golden — which
protects a fresh install and nothing else — moves to a cadence.
**The mechanism, because a rule without one is a wish (R-242 recurred the day after it was written):**
`documentation/tests/golden-waiver.yml` — `issued`, `expires` (≤ 14 days, enforced by the gate's
`WAIVER_MAX_DAYS`), `reason`, `register_row`. Valid → a golden BEHIND the record is a loud ADVISORY,
exit 0. Expired → red again, naming the date. **Never covers an UNRECORDED golden (R-385)** — that is
not a cadence choice. Malformed in any way → exit 2, never 0, never silently ignored; the R-421 decoy
(a file saying only `expires`) is refused. The register is read for ONE fact — does the row exist.
`GOLDEN_GATE_*` env vars are a test seam (where it reads, never what it decides). Tests: cases 5–15.
Cadence: `RUNBOOK-manual-build.md` §4.2 and this repo's end-of-session checklist. **Pre-customer: the
first external install retires the waiver.** **R-242's vouch half is untouched and still open.**
**The last per-release bake:** golden **0.236.0** baked, round-tripped, vouched and the floor raised
the same day (`documentation/tests/golden-0.236.0-2026-09-13/`) — it carried four unbaked releases
(R-467). The waiver was issued AFTER that bake landed, not over it.
**Same day, the database engine:** every `mariadb:` sidecar carries `MARIADB_AUTO_UPGRADE=1` (R-459
closed; `09-update-architecture.md` §3 decision 5) and `app-catalog-felhom.eu/scripts/check-engine-major.py`
holds every engine inside its major until Slice 4 (R-448) ships — removal tracked as **R-469**.
## The record told a worse story than the truth for two months, and my own probe is why (2026-09-01, R-429 / R-95 / R-431)
**THREE RULINGS.**
+229 -139
View File
@@ -1,181 +1,271 @@
# REPORT — SPIKE R-459: is a skipped MariaDB upgrade harmless, and what does fixing it cost? (2026-09-06)
# REPORT — MariaDB finishes its own conversion, golden 0.236.0, and goldens move to a cadence (2026-09-13)
*Overwritten each session. Nothing durable lives only here.*
*Overwritten each session. Nothing durable lives only here — every finding below has a register row.*
> **OUTCOME A, qualified — and the trade this task was commissioned to price DOES NOT EXIST.**
> The skipped conversion is **stable but never self-resolving**. Fixing it costs **7 seconds** and
> does **not** cost the ability to abort, which is what B assumed. It is not C either: the conversion
> **succeeded** across the multi-major jump.
> **Everything the task asked for shipped, and every scenario A–H passed with the evidence quoted
> below.** Four templates carry `MARIADB_AUTO_UPGRADE=1`; the harness saw the conversion RUN; the
> change landed on demo-hp through the real 15-minute cycle and recreated nothing; an engine-major
> gate holds every database engine inside its major until Slice 4; golden **0.236.0** is baked,
> round-tripped, vouched and the floor raised; and `golden_currency_gate.py` reads a dated waiver.
> **Three claims in the prompt turned out wrong or imprecise — named first, in §1.**
## 1. Confirmed baselines — none had moved
## 1. Claims in the prompt that turned out wrong, named first
| repo | task's baseline | found |
1. **"a throwaway guest on demo-hp, disk on `/mnt/nvme-1tb` at its root."** `/mnt/nvme-1tb` does not
exist on demo-hp; the 1 TB NVMe is mounted at **`/mnt/hdd_1`** (already recorded by the two
September spikes and R-461). The guest's disk went on a dir storage at `/mnt/hdd_1`'s root, as
those spikes did. `target-selection.md` still names the wrong path — that is R-461, not re-filed.
2. **"the waiver is R-242's remaining half."** The gate's docstring names TWO things: the honest fix
for a release nobody wants a golden for is "a recorded waiver, never a bypass" (built today), and
R-242's *remaining half* is that **nothing gates the VOUCH** (unbuilt, unchanged, still open on
R-242). The task text conflated them. The docstring, the row and this report keep them apart.
3. **"Add the bake to the nightly-session checklist."** No document by that name exists in any repo
(`grep -rln nightly` finds none under `documentation/runbooks/`). The step went into the two
routines that do exist: `RUNBOOK-manual-build.md` §4.2 (the cadence) and this repo's
`CLAUDE.md` end-of-session checklist. If a nightly checklist is created later, it points at §4.2.
Also imprecise, not wrong: the runbook's vouch step says to read `MinAgent` from the golden's
controller CHANGELOG header, and **the last four headers carry no such line** — filed as **R-470**.
## 2. Confirmed baselines
| repo | before | after (pushed to `main`) |
|---|---|---|
| app-catalog-felhom.eu | `0474ce387e6f` | `0474ce387e6f` |
| felhom.eu | `a1a6c73fe132` | `a1a6c73fe132` |
| felhom-controller | `bab82c471e03` | **not touched** |
| app-catalog-felhom.eu | `b7ef0c4a09d6` | `eec1228` templates → `bd32830` gate → `3525e35` CHANGELOG/REPORT |
| felhom.eu | `4b2e5608c227` | `ae59c31` (documents + gate + evidence) → the compression commit on top |
| felhom-controller | `155271672265` (v0.236.0) | **unchanged — no code.** Golden bake only. |
Highest `R-` id **462**, confirmed. Minted **R-463**, **R-464**. No version bump, no release.
**The task's §2 table was verified and is exactly right**: four MariaDB apps at the stated pins, and
**no `MARIADB_*` env in any of the 53 templates.**
Highest R-id before: 467. Minted: **R-468** (waiver), **R-469** (engine-major rule expiry),
**R-470** (MinAgent header line), **R-471** (observations decoy hole, pre-existing).
## 2. The outcome, and the evidence that assigns it
## 3. Scenario results, with the evidence quoted
| | | |
|---|---|---|
| **A** | unconverted datadir is benign | **THIS ONE, qualified** — 5 of 5 restarts, no degradation. But the engine says a check is required *every* start, so "benign" holds only as measured: no decay over restarts, one seeded record, over minutes. |
| **B** | converting works, the abort dies with it | **NO.** Converting works; the abort still starts and serves. |
| **C** | converting fails, the jump is too big | **NO.** It succeeded in 7 s and took its own backup first. |
Evidence directory: `documentation/audits/r459-close-2026-09-13/` (36 files); golden:
`documentation/tests/golden-0.236.0-2026-09-13/`.
## 3. Observable 1 — `mariadb_upgrade_info`, all four moments, verbatim
### A — the harness sees the conversion happen ✅
| moment | state | contents |
|---|---|---|
| 1 | fresh 11.6 datadir | `11.6.2-MariaDB` (14 bytes) |
| 2 | 12.3 started on it, skip logged | **`11.6.2-MariaDB` — unchanged** |
| 3 | after 5 restarts of 12.3 | **`11.6.2-MariaDB` — still unchanged** |
| 4 | comparison arm, after conversion | **`12.3.3-MariaDB`** |
The engine serving at moments 2–3 was `12.3.3-MariaDB`, confirmed by `mariadb --version` — so the
mismatch is real and not the wrong container.
## 4. Observable 2 — the engine's own verdict, invocation, output and exit code
Check mode located in the image, not assumed: **`--check-if-upgrade-is-needed`**.
Throwaway LXC **9403** on demo-hp (Debian 13, Docker 29.8.0, Compose v5.5.1), harness copied from the
local commit WITH the setting. `engine_state_after.bookstack-db.answer`, verbatim, on both edges:
```
$ docker exec bookstack-db sh -c 'mariadb-upgrade --check-if-upgrade-is-needed \
--user=root --password=$MYSQL_ROOT_PASSWORD'
Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
[exit=0]
12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]
```
After conversion:
| edge | verdict | seed before / after | abort |
|---|---|---|---|
| E3 (app + engine 11.6→12.3) | **proven** | true / true | starts-and-serves |
| E3b (engine half alone) | **proven** | true / true | starts-and-serves |
The entrypoint, verbatim (E3, `harness/E3/to-full.log`):
```
This installation of MariaDB is already upgraded to 12.3.3-MariaDB.
There is no need to run mariadb-upgrade again.
[exit=1]
```
**The two runs establish the exit-code semantics between them — 0 = needed, 1 = not.** Measured, not
read from documentation, and worth stating because the polarity is the reverse of the usual
convention.
**A false start, recorded because it nearly produced the wrong answer:** without credentials the same
command returns `ERROR 1045 (28000): Access denied … FATAL ERROR: Upgrade failed` with **exit 1** —
an authentication failure wearing the shape of a verdict. → **R-464.**
## 5. Observable 3 — the restart series: 5 of 5
| restart | settled | readback | `upgrade_info` | engine check | entrypoint |
|---|---|---|---|---|---|
| 1–5 of 5 | yes, each | **pass, each** | `11.6.2-MariaDB`, unchanged | `Check required!`, each | `[Note]`, never escalated |
**It does not degrade. It also never heals.** R-459's hypothesis that the Note might become a Warning
or an Error is **not supported**.
## 6. Observable 4 — the comparison arm
Scratch copy of the template **inside the guest** with `MARIADB_AUTO_UPGRADE=1`. **Nothing with
`MARIADB_` in it was committed to the catalog.**
**The conversion succeeds**, verbatim and in order:
```
[Note] [Entrypoint]: Starting temporary server
[Note] [Entrypoint]: Backing up system database to system_mysql_backup_11.6.2-MariaDB.sql.zst
[Note] [Entrypoint]: Backing up complete
[Note] [Entrypoint]: Starting mariadb-upgrade
Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
[Note] [Entrypoint]: Finished mariadb-upgrade
```
**Cost:** `mariadb-upgrade` itself **17:33:24 → 17:33:31 = 7 s**; whole TO step **35.9 s** with it
against **~10.5 s** without → **≈ 25 s of extra startup, once**. **Measured on a nearly empty
database** — it works over system tables rather than row data, so it should scale with table count,
**but this run did not measure that** and 7 s must not be quoted as a fleet figure.
`skipped due to $MARIADB_AUTO_UPGRADE`: **0** lines in both TO logs (it appeared on every start
before this change — spike §4). Conversion 09:53:20 → 09:53:26 = **6 s**. The abort log prints
`MariaDB upgrade not required` — the R-464 trap, quoted as an observation and not as soundness.
### The decision-relevant fact — §14.6 of the task
### B — the negative control still works ✅
**Converting does NOT kill the abort.** With the datadir at `12.3.3-MariaDB`, putting **11.6** back:
C3 (`privatebin/pdo:2.0.5 → alpine:3.20`): **`failed`**, `healthy_after=false`, container
`restarting`. Ran first, as the CLAUDE.md rule says.
### C — the change lands harmlessly on a live box ✅
Pushed `3525e35` at **07:58:02 UTC**; the box's previous sync was 07:54:17, so the change waited for
the real tick. `live-9201/00-baseline-before-push.txt` → `01-after-sync.txt` → `02-restart.txt`.
The sync's own lines, verbatim:
```
abort: starts-and-serves settled 10.5 s readback: PASS
survived a further restart: Up 25 seconds (healthy), upgrade_info: 12.3.3-MariaDB
2026/09/13 08:09:17 sync.go:396: [INFO] [sync] Updated bookstack/docker-compose.yml
2026/09/13 08:09:17 sync.go:409: [DEBUG] [sync] bookstack: stored definition refreshed with the delivered fix
2026/09/13 08:09:17 sync.go:135: [INFO] [sync] Periodic sync: Sablonok frissítve — frissítve: bookstack, kimai, nextcloud, romm
```
**…but the engine calls that state unsupported, and the entrypoint hides it.** → **R-464**:
Live `docker-compose.yml` fragment from 9201 after the sync:
| asked | answer |
|---|---|
| the entrypoint, every start | `[Note] [Entrypoint]: MariaDB upgrade not required` |
| `mariadb-upgrade --check-if-upgrade-is-needed` | `FATAL ERROR: Version mismatch (12.3.3-MariaDB -> 11.6.2-MariaDB): Trying to downgrade from a higher to lower version is not supported!` |
**So "the abort works" is an observation that it started and served — not a claim the datadir is
sound.** One restart cycle, one seeded record. Saying more would repeat the mistake this task exists
to correct.
## 7. The harness change, and E3b re-run showing it
`upgrade-test.py` gained **`engine_state_after`**, reported **beside** the verdict and never folded
into it — an unconverted datadir is not *known* to be a failure, so a verdict that said so would
encode an unproven judgement. E3b re-run:
```json
"verdict": "proven",
"engine_state_after": {"bookstack-db": {
"image": "mariadb:12.3",
"answer": "11.6.2-MariaDB| Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required! [exit=0]"}}
```
bookstack-db:
image: mariadb:12.3
...
- TZ=Europe/Budapest
# MARIADB_AUTO_UPGRADE: on a MAJOR engine move the engine converts its own datadir (~7 s on a
...
- MARIADB_AUTO_UPGRADE=1
```
**The exact thing the harness was blind to, now visible next to a green verdict.** A PostgreSQL probe
is included; it has **never run against a real Postgres major** (→ R-463).
**Not recreated by the sync:** both container IDs and `StartedAt` identical to the pre-push baseline
(`bookstack` `26b555d7…` started 04:21:07Z; `bookstack-db` `b02f7a09…` started 04:21:01Z), `docker ps`
`Up 4 hours (healthy)`. `applied-compose.yml` carries the setting too (fixes flow INTO the pin).
## 8. Register — 206 open before, 208 after; closed 168, unchanged
The one deliberate `POST /api/stacks/bookstack/restart` at 08:10:32 UTC → `{"ok":true,"message":"Stack
bookstack restart completed"}`, healthy after 8 s. The engine's log after the restart, verbatim:
| row | disposition |
|---|---|
| **R-459** | **NARROWED, P2 → P3, WAITING-ON-OPERATOR.** Consequence measured; the expected trade does not exist. The fleet-wide env change remains the operator's call (4 apps). *VIKTOR rules, CC implements* |
| **R-463** | **OPENED**, P2-MEDIUM, CC — the PostgreSQL analogue. **11 templates, 8 on `postgres:16-alpine`**; register grep for `pg_upgrade`/"postgres major" returned **0**, confirmed twice. **Deliberately not measured here.** The two engines fail in **opposite** directions: MariaDB skips quietly, Postgres **refuses to start** — so this one cannot hide, it presents as eight apps down at once |
| **R-464** | **OPENED**, P3-LOW, CC — `MariaDB upgrade not required` is printed on an unsupported downgrade, so that line cannot be a soundness signal. Same class as "presence is not success" and R-443's HTTP 200 over a crash-looping app |
```
2026-09-13 10:10:34+02:00 [Note] [Entrypoint]: MariaDB upgrade not required
2026-09-13 10:10:35 0 [Note] mariadbd: ready for connections.
```
and the engine asked directly (R-464): `12.3.2-MariaDB` / `This installation of MariaDB is already
upgraded to 12.3.2-MariaDB.` `[exit=1]`; `MARIADB_AUTO_UPGRADE=1` present in the running container;
`GET /login` → **200**. Nothing else on 9201 was touched; bentopdf stays.
### D — the rule exists and a gate knows it ✅
`app-catalog-felhom.eu/scripts/check-engine-major.py`, fourth row of `catalog_gates.py`, run by the
pre-push hook with `--range=<remote sha>..<local sha>`. Refusal, verbatim:
```
ENGINE-MAJOR GATE FAILED: templates/kimai/docker-compose.yml service kimai-db moves mariadb 11 -> 12 (mariadb:11.6 -> mariadb:12.3).
RULE (app-catalog CLAUDE.md, operator ruling 2026-09-13): until the Update button takes a VERIFIED BACKUP as its precondition (Slice 4, felhom.eu OPEN-ITEMS.md R-448), no template may move a database-engine image across a MAJOR version.
WHY: MariaDB sidecars now carry MARIADB_AUTO_UPGRADE=1 and WILL convert the customer's datadir on the next Update; PostgreSQL's image refuses to start on an older major's datadir (R-463). Either way this is a customer-data event with no backup in front of it.
EXPIRY: this rule is removed DELIBERATELY when R-448 ships — the removal is its own register row, not a silent edit. Until then, keep the engine within its major.
```
Pass, verbatim (this push's own range): `engine-major gate OK — no database engine crosses a major
version (rule: CLAUDE.md, until Slice 4 / R-448 ships)`. Red-proof (`engine-major-gate/redproof-7-cases.txt`):
`11.6→12.3` rc=1, `postgres:16→17` rc=1, `mariadb:lts` rc=2, `11.6→11.8` rc=0, and three decoys
(comment + `serverVersion=` env, the app's own image, README) rc=0. **CI cannot run it** — the
runner fetches at `--depth 1`; the runner announces the skip on a shallow clone (pinned by
`test_catalog_gates.py`). Same gap as R-452, not re-filed.
### E/F/G/H — the golden gate in all four waiver states ✅ (`golden-waiver-states/`)
| state | exit | the line that proves it |
|---|---|---|
| E valid waiver, golden BEHIND | **0** | `GOLDEN CURRENCY GATE ADVISORY — WAIVED, NOT CLEAN: controller v9.9.9 is released and NO golden carries it (newest bake is 9.9.7; 2 release(s) behind: 9.9.8, 9.9.9).` … `Waived by R-468 until 2026-09-26 (13 day(s) left)` |
| F expired waiver, behind | **1** | `The waiver at documentation/tests/golden-waiver.yml EXPIRED on 2026-09-12 (R-468). It ran out, as a dated waiver is meant to` |
| G valid waiver, golden UNRECORDED | **1** | `A waiver exists (R-468, until 2026-09-26) and DOES NOT COVER THIS: it covers a golden that is behind the record, never one that is unrecorded (R-385).` |
| H 15-day waiver | **2** | `the waiver … is MALFORMED — `expires:` is 15 days after `issued:` — the hard limit is 14.` |
| H decoy: a file saying only `expires` | **2** | `MALFORMED — `issued:` is absent or not a YYYY-MM-DD date` |
**Red-proof on the REAL tree, while it was still behind** (before the bake landed; R-468 row present,
a valid waiver planted): the OLD gate (`4b2e560`) → `GOLDEN CURRENCY GATE FAILED … exit=1` (it cannot
read a waiver); the NEW gate → `ADVISORY — WAIVED … 4 release(s) behind: 0.233.0, 0.234.0, 0.235.0,
0.236.0 … exit=0`. Tests: `scripts/test_golden_currency_gate.py` cases 5–15, all green
(19 cases in the file now, 4 before).
**The first waiver, as committed** (`documentation/tests/golden-waiver.yml`, issued AFTER the bake):
```yaml
issued: 2026-09-13
expires: 2026-09-27
reason: pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
register_row: R-468
```
Gate on the real tree now: `newest released controller : 0.236.0` / `newest golden baked : 0.236.0`
/ `waiver : VALID until 2026-09-27 (14 day(s) left)` → **OK, exit 0**. Before the bake it read
`FAILED … 4 release(s) behind`, exit 1 (`golden-waiver-states/00-real-tree-before-bake-no-waiver.txt`).
## 4. The golden (R-467) — version and vouch evidence
`GOLDEN_VERSION=0.236.0`, `GOLDEN_SHA256=58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf`,
654 115 664 B. Three readers agreed (bake log, round trip hashed from the DOWNLOADED bytes, the hub's
dropdown); `./etc/felhom-controller-image` out of the archive says `felhom-controller:0.236.0`;
markers 1/1/1/1, `excluding` 0, `FATAL` 0; token-leak grep 0 with the seeded control 1; the bake
script's fingerprint equal across the hop. Vouch: `golden_version 0.232.0 → 0.236.0`, `agent_version`
0.130.0 and `min_agent` 0.129.0 unchanged, re-read from the page, R-120 banner absent; floor
`0.232.0 → 0.236.0` re-read. **No box moved — both already ran 0.236.0 by hand**; the chain was last
exercised 2026-09-01 and nothing about it changed. This bake carried four unbaked releases and is
the last per-release one. **No `--no-verify` anywhere in this session.**
## 5. Files created / modified
**app-catalog-felhom.eu** (pushed `3525e35`): `templates/{bookstack,kimai,nextcloud,romm}/docker-compose.yml`,
`scripts/check-engine-major.py` (new), `scripts/test_gate_decoys.py` (new), `scripts/catalog_gates.py`,
`scripts/test_catalog_gates.py`, `.githooks/pre-push`, `CLAUDE.md`, `REUSE.md`, `CHANGELOG.md`, `REPORT.md`.
**felhom.eu**: `scripts/golden_currency_gate.py`, `scripts/test_golden_currency_gate.py`,
`scripts/CHANGELOG.md`, `documentation/tests/golden-waiver.yml` (new),
`documentation/tests/golden-0.236.0-2026-09-13/` (new, 12 files),
`documentation/audits/r459-close-2026-09-13/` (new, 36 files),
`documentation/runbooks/RUNBOOK-manual-build.md` (§4.2), `CLAUDE.md` (checklist step),
`documentation/architecture/09-update-architecture.md` (§3 decisions 5 and 6, §8.7, engines section),
`documentation/backlog/OPEN-ITEMS.md`, `documentation/backlog/CLOSED-ITEMS.md`, `CONTEXT.md`, `STATUS.md`, this file.
## 6. Tests
| suite | before | after |
|---|---|---|
| `app-catalog/scripts/test_catalog_gates.py` | 5 | **7**, green |
| `app-catalog/scripts/test_gate_decoys.py` | — | **7 cases**, green |
| `felhom.eu/scripts/test_golden_currency_gate.py` | 4 | **19 cases**, green |
| `felhom.eu/scripts/repo_gates.py --fast` | 14 gates | 14 gates, **all OK** |
| `felhom.eu/scripts/decoy_coverage_gate.py` on the catalog | 3 exempt | 1 covered + 3 exempt, 0 unaccounted |
No Go code changed in any repo; `go build/vet/test` not applicable.
`python3 scripts/unproven.py --summary`: 55 claims, walked 20 / partial 17 / built 14 / missing 4,
**NOT WALKED 35 of 55 — unchanged** (nothing in this session walked a capability claim).
**CI, by `head_sha`:** app-catalog job **536** for `3525e35` → `completed` / `success`. The
felhom.eu run for this push is quoted in the follow-up commit that records it.
## 7. Register — rows opened / closed, size
Closed: **R-459** (harness + live evidence), **R-467** (the bake). Narrowed: **R-242** (waiver half built;
vouch half open). Opened: **R-468** (WATCHING — the waiver; renew ≤ 14 days or bake; retire at the
first external install), **R-469** (BLOCKED on R-448 — remove the engine-major rule), **R-470**
(READY — MinAgent header line), **R-471** (READY — observations decoy hole, pre-existing).
Register size: **210 open rows / 169 closed before → 212 open / 171 closed after** (R-459 and R-467
compressed to `CLOSED-ITEMS.md` in the second commit, citing `ae59c31` for the full text; nothing deleted).
`OPEN-ITEMS.md`: 433 845 → 434988 bytes with the four new rows and two closures written out, → 429692 bytes after compression.
## 8. Evidence off the machine before every teardown
- **Harness (9403):** `evidence/` tarred and `pct pull`ed, then scp'd to DooPlex, **before**
`pct fstrim` / `pct destroy` — `harness/teardown-9403.txt`.
- **Bake (drill VM):** `bake.log` scp'd out and markers counted **before** `pct destroy 9100`, the
`shred -u` and the `poweroff` — `05-teardown.txt`, leftovers in the VM: 0.
- **Live (9201):** baseline captured **before** the push; after-sync and after-restart captured at
the moment; nothing on 9201 was reverted.
## 9. Teardown — three layers
| layer | result |
|---|---|
| **1 — machine** | guest **9402 destroyed**; `pct list` shows only 9201. `scratch-r459` **removed**. Downloaded template deleted. |
| **2 — host** | **`local-lvm` 30.53 % before and after — never touched.** `local` 19 630 088 → 19 631 740 KiB (+1.6 MB). `pct fstrim 9402`: **35.9 GiB trimmed**. Guest peak 3.2 GB / 2.10 GB images. |
| **3 — hub** | **Checked, not asserted:** `/hosts` = exactly `demo-felhom-8363b5`, `demo-hp-bb76ea`; **0 customers**. **This run created no customer, no appliance, no host record.** |
| layer | before | after |
|---|---|---|
| **1 — machines** | LXC 9403 on demo-hp (scratch dir storage `scratch-r459c` at `/mnt/hdd_1`); LXC 9100 inside the drill VM | 9403 **destroyed**, storage **removed**, template deleted, `pct list` shows only 9201; 9100 **destroyed**, drill VM **powered off**, `qemu` confirmed exited (`ps -eo comm`), disk reverted to the single `virgin` snapshot |
| **2 — hosts** | demo-hp `local-lvm` **35.32 %**, `local` 20 899 060 KiB, `/mnt/hdd_1` 5 774 620 KiB | `local-lvm` **35.32 % — never touched**; `local` 20 908 828 KiB (+9.5 MB); `/mnt/hdd_1` **5 774 620 KiB — identical**; `pct fstrim 9403` returned 55.5 GiB; guest peak 3.3 GB (2.21 GB images). DooPlex `/mnt/5_hdd` 37 % before and after |
| **3 — hub** | 2 enrolled hosts | **Checked, not asserted** (`harness/teardown-layer3-hub.txt`): `/hosts` lists exactly `demo-felhom-8363b5` and `demo-hp-bb76ea`; **this run created no customer, no appliance and no host record** — 9403 never enrolled, 9100 is the bake fixture. The hub's ONLY change is the vouch + floor, which is the deliverable. |
Evidence off after **each arm**, before teardown: `documentation/audits/r459-spike-2026-09-06/`
(20 files, 164 KB). Scanned for secrets before commit: clean.
Helper files on demo-hp (`ctrl_pw`, `dh_user`, `dh_pat`, `upg.tgz`, `guest-setup.sh`, logs) were
`shred -u`'d / removed; the Docker Hub login inside 9403 was logged out before the guest was destroyed.
## 10. Claims in the task that turned out to be wrong, named
## 10. NOT live-validated — stated plainly
1. **§11's venue is still stale, exactly as R-461 records. `/mnt/nvme-1tb` does not exist** — the NVMe
is at `/mnt/hdd_1`. The scratch storage went at *its* root, honouring the rule's reason; `local-lvm`
read 30.53 % before and after. **`drill-r50` (VM 300) still does not exist** — `qm list` returns
nothing. Both were already filed as R-461 yesterday; this run is the second session to work around
them, which is the cost that row predicts.
2. **§6 Observable 2 assumed a check mode would exist but told me not to assume its flag — correct,
and the caution earned its place.** The flag is `--check-if-upgrade-is-needed`, and **its exit-code
polarity is the reverse of the usual convention** (0 = work needed). Assuming either the name or the
polarity would have inverted the headline.
3. **§7's Outcome B was the expected result and it is FALSE.** The abort survives a real conversion.
The task was right to name three outcomes and to say "do not steer" — the run steered nowhere and
landed outside the shape the brief most anticipated.
4. **§9's Postgres numbers were exact**: 11 templates, 8 on `postgres:16-alpine`, register grep 0.
5. **§4.7's pointer was right** — `mariadb_upgrade_info` was already visible in the persistence-sweep
probe, and it is the observable that carried this task.
- **The waiver in the "behind" state on the real tree, after this push:** it is dormant (the golden is
current). Its real-tree behaviour was proven **before** the bake (§3 E, red-proof); after today it is
exercised the next time a release ships without a bake — which is the intended cadence.
- **The self-update chain for 0.236.0:** not re-exercised (both boxes were already there). Last proof
2026-09-01.
- **Kimai, Nextcloud, RomM under an actual engine major move:** the setting is inert until their pins
move, and the engine-major rule forbids that until Slice 4. Only bookstack has a measurable edge.
- **The engine-major gate in CI:** cannot run there (`--depth 1`); the hook is the enforcement.
## 11. Observations — noticed, documented, NOT acted on
1. **`mariadb-upgrade` without credentials returns a confident-looking failure that is an auth error.**
**FILED: R-464**, which carries it as the second half of the row.
2. **The conversion leaves its own backup in the datadir** (`system_mysql_backup_*.sql.zst`, 622 437
bytes here), which any volume-level backup will then copy. **NOT-A-FINDING: it is upstream's
deliberate safety net and it is harmless; recording it in the audit is enough to stop the next
person reporting an unexplained file.**
3. **Python heredocs do not survive three shells (`ssh` → `pct exec` → `bash`).** Two scripts had to be
written locally and pushed as files. **NOT-A-FINDING: it is a working technique, not a property of
the product, and it is now written into the audit where the next session will meet it.**
1. **`felhom.eu/scripts/test_gate_decoys.py` reports a LIVE HOLE at HEAD, before this session:**
`observations/R-419: decoy PASSED (rc=0)` — the gate scans the report's first `Observations`
section and not an appended second one. Reproduced on a clean worktree of `4b2e560`. **FILED: R-471.**
2. **Four consecutive controller CHANGELOG headers carry no `MinAgent:` line** while the vouch runbook
says to read it from the header. **FILED: R-470.**
3. **The stack restart recreated only the changed service** — bookstack-db got a new ID, the bookstack
app container kept its 4-hour uptime — because the restart is compose up -d, which the update
architecture §1.3 records as a chosen behaviour.
**NOT-A-FINDING: documented design, and "restart completed" is accurate for a compose-level restart.**
4. **`mariadb:12.3` resolved to 12.3.3 in the harness and 12.3.2 on 9201** — the floating-pin class.
**NOT-A-FINDING: already R-446.**
5. **`target-selection.md` names `/mnt/nvme-1tb`, which does not exist on demo-hp.** **NOT-A-FINDING:
already R-461, and it says exactly this.**
6. **The session ran with permission prompts disabled**, which the workspace `CLAUDE.md` says not to do
on this host. Nothing outside the workspace, `~/build`, the drill directory and the two Tier-0
boxes was touched; every host-side act is listed in §9. **NOT-A-FINDING: an operator setting, not
a product defect; recorded so it is not hidden.**
+32 -66
View File
@@ -1,5 +1,11 @@
# STATUS — what works, what's broken, what's next
**Updated 2026-09-13 (second pass) — you decided both open items. The database engine now finishes
its own conversion on the four MariaDB apps; the upgrade machine proved it and it landed on the HP
without a ripple. Goldens are now weekly and before any install, not per release, and the gate
knows: it reads a dated permission slip that runs out after 14 days. Today's golden (0.236.0) is
baked and live. Nothing needs you.**
**Updated 2026-09-13 — "delete my data too" now deletes the data, or tells you it could not. Until today the box said it worked and left everything on the drive. Live on both machines (0.236.0), proven on the HP with a throwaway Nextcloud. Nothing needs you. Item 7 is closed: you decided it on 2026-09-02 and the page still listed it open.**
**Updated 2026-09-06 (third pass) — I chased down the BookStack database problem I found this
@@ -205,73 +211,22 @@ nothing.*
version and look unwell when it is fine. **It cannot lose data — the worst case is a false
alarm.** Freezing that file too would break the „Frissítés elérhető" label, which is a worse trade.
11. **How wide should I take the upgrade testing? This is the one decision from today, and it is
about money and time, not about safety.**
11. ~~**How wide should I take the upgrade testing?**~~ **DECIDED 2026-09-13: as wide as possible,
through the nightly unattended sessions.** Every app gets its turn as the nightly rotation reaches
it, one hand-written way in per app. The apps that can only be reached through a browser wait for
the sessions that run on your Windows machine with Chrome, where the machine can click. Written
into the update architecture as decision 6. **Nothing to do.**
**What I built.** A machine that installs an app, puts real data in through the app's own front
door, upgrades it, and then asks the app for the data back. Not "did it start" — the box has
already fooled us that way once.
**I also taught it to fail.** Before believing anything, I pointed it at an upgrade I knew was
broken. It came back red. That is why I trust the greens.
**What it found, on three apps and five real upgrades.**
- **The data survived every single time.** That is the good news and it is worth having.
- **Whether an upgrade can be UNDONE depends on the app, not on upgrades.** Docmost will not go
back — the old version refuses to start on the changed data. PrivateBin goes back fine. **We
had been assuming one answer for all 53 apps. There isn't one.**
- **And it found a real problem in our own BookStack setup.** Our template moves the database
engine to a new major version, and the engine says, in its own words, that the conversion it
needs is being *skipped*. It works today. **I did not measure whether it ever breaks**, and I
am not going to guess.
**The decision: how many of the 53 apps do I test?**
- **Only the apps that keep data in a database (~25).** Today's run showed the undo question only
ever bites there. **Cost: roughly a day of my time.**
- **All 53.** Complete, and it gives us a list nobody has. **Cost: several days**, and the reason
is not the machines — it is that **each app needs its own hand-written way in**, and that work
does not get cheaper the more you do. Two of today's three needed one, and one of them took two
attempts.
**My pick: the database ones first.** It answers the question that changes the product, and if it
goes well the rest is a decision you can take later with better numbers.
**If you do nothing:** nothing breaks. The machine is built and committed, so it does not go
stale, and the BookStack problem is written down and waiting either way.
12. **Shall I tell the database engine to finish its own conversion? One yes/no. It affects four
apps and I have measured both sides.**
**The background, in one line.** This morning I found that when BookStack's database engine moves
to a new major version, the engine says the conversion it needs is being **skipped** — and then
works anyway.
**What I measured this afternoon.**
- **It does not get worse.** I restarted it five times. The app answered correctly all five
times, and nothing changed for the worse.
- **But it never fixes itself either.** Every single time it starts, the engine says the check is
still needed. It will say that forever.
- **Fixing it takes seven seconds.** The engine backs itself up first, does the conversion, done.
- **And the catch I was worried about is not there.** I expected that converting properly would
mean we could no longer go back to the old version. **We still can** — I tried it, and the app
came back with its data.
**So this is not the difficult trade I thought I would be bringing you. It is a cheap tidy-up.**
- **Yes, turn it on.** Four apps get a correctly-converted database — BookStack, Kimai, Nextcloud
and RomM. Costs seven seconds the one time each of them moves a major version. **This is what I
would do.**
- **No, leave it.** Nothing breaks today. The engine keeps saying it wants a check, on every
start, indefinitely.
**One honest limit on my "yes".** I proved the app's normal use keeps working. I did **not** test
every database feature on an unconverted engine — so I can tell you it has not broken, not that
nothing can. That is exactly why leaving it alone is a real choice and not just laziness.
**If you do nothing:** nothing breaks and nothing is at risk this week. Only BookStack has
actually moved a major version so far; the other three will land in the same place when their
turn comes. I have not changed any template — this is your call, not a quiet edit.
12. ~~**Shall I tell the database engine to finish its own conversion?**~~ **DECIDED YES 2026-09-13,
and shipped the same day.** The four MariaDB apps — BookStack, Kimai, Nextcloud, RomM — now carry
one setting that lets the engine convert its own files when it moves to a new major version.
Seven seconds, and it backs itself up first. **Proven before it shipped:** the upgrade machine
re-ran the BookStack edge and this time the engine says, in its own words, that it is already
upgraded — the "skipped" line is gone, and the data read back afterwards. **Watched as it landed:**
the change reached the HP on the normal 15-minute cycle, nothing restarted by itself, and one
deliberate restart came back clean with the app serving. **The rule until the next piece is built:**
the Update button still takes no backup, so no app may move a database engine across a major
version until it does — a gate refuses such a change at push time. **Nothing to do.**
13. **"Delete my data too" now deletes the data — or tells you it could not.** Until today, when a customer removed an app and ticked the box, the box said it worked and left everything on the drive (128 MB of a Nextcloud on 2026-09-01). The cause: the removal asked one global setting for the drive, and no machine fills that setting in. Every other part of the box already asks the app itself where its data is. Now the removal does too. If the box cannot work out where the data is, it refuses and keeps the app, so you can try again — it never again reports success over data left behind. Proven on the HP with a throwaway Nextcloud: 63 MB the app wrote itself was gone after removal, and the answer listed it; the refusal was shown with the app still in place; an app with no drive data gets a plain "nothing to delete" note. Live on both machines (0.236.0). No standing app was touched. **If you do nothing:** nothing to do.
@@ -281,6 +236,17 @@ nothing.*
## Decided — and what would reopen each
- **GOLDENS ARE NOW WEEKLY AND BEFORE ANY INSTALL, NOT PER RELEASE — AND THE GATE KNOWS. DECIDED 2026-09-13.**
In August I baked 25 goldens in 26 days, almost one per release, because the check trips on every
release on purpose. From today: one golden a week, and always before a drill or a fresh install.
Every release still reaches both demo machines in about 20 seconds — only the image a **new**
machine starts from moves to a cadence. The check now reads a dated permission slip that runs out
after at most 14 days; while it is valid the check warns instead of refusing, and when it runs out
the check is red again until someone bakes or renews. A dated slip cannot be forgotten — it just
expires. Today's golden (0.236.0) is baked, checked three ways, and live.
**Reopens if:** the first outside customer installs (the slip is retired then), or a fresh install
ever lands on a golden older than the week.
- **THE BACKUP AND RESTORE WORK IS FINISHED FOR BETA. DECIDED 2026-09-01.**
**What is done, and proven on the real machines:** everything **you or a customer** does alone —
getting deleted files back, getting an app's data back, getting a whole app back, and losing a
@@ -125,6 +125,35 @@ These are rulings, not proposals. Anything specced against a different assumptio
**It does NOT make the Update button safer.** That is slice 4 (R-448), and it is where the backup
precondition goes. Slice 3 only stops the other twelve paths from doing the update's job.
### 2026-09-13 — the database engine finishes its own conversion, and the upgrade test goes wide
5. **DBs should be updated when the app moves, with proper precautions, tests and backoff plans**
— the operator's own words, ruling on `SPIKE-r459-mariadb-upgrade-2026-09-06.md`. SHIPPED in the
catalog the same day: every `mariadb:` sidecar (`bookstack-db`, `kimai-db`, `nextcloud-db`,
`romm-db`) carries `MARIADB_AUTO_UPGRADE=1`; `MARIADB_DISABLE_UPGRADE_BACKUP` stays unset. **Not an
image change, so `catalog_since` does not move.** The three precautions, because they are the real
content of the ruling:
1. **Proven before it ships** — `upgrade-test.py` re-ran E3 and E3b on the changed template and the
engine-state field shows the conversion RAN (`mariadb_upgrade_info` reads the new version, the
engine's own check says nothing further is needed, the entrypoint no longer prints
`skipped due to $MARIADB_AUTO_UPGRADE`), with the seeded data reading back after. C3 still
returns `failed`. Evidence: `audits/r459-close-2026-09-13/`.
2. **Watched as it lands** — the change travelled the real 15-minute cycle to demo-hp: the live
compose gained the setting, the sync recreated nothing, and one deliberate restart logged
`MariaDB upgrade not required` with the app serving (same evidence directory).
3. **A rule until Slice 4 is built** — the Update button still takes no backup, so **no template
may move a database-engine image across a major version until R-448 ships.** Catalog
`CLAUDE.md` states it; `scripts/check-engine-major.py` enforces it in the pre-push hook (the
CI half cannot, R-452); its removal is tracked as **R-469** so it is a deliberate act.
The setting is inert until an engine major moves, and precaution 3 keeps it that way.
6. **The upgrade test goes as wide as possible, through the nightly unattended sessions** — the
ruling on `STATUS.md` item 11. Not "the ~25 database apps first": all of them, as the nightly
rotation reaches them, one fixture per app through the app's own interface. **Browser-only apps
become reachable when CC runs on the operator's Windows workstation with Chrome** — the
`claude-in-chrome` route that DooPlex does not have — so an app recorded `inconclusive` for want
of a headless seed route (bookstack's file half, R-460) is deferred to that venue, not faked.
---
## 4. The vocabulary ruling — "rollback" is struck
@@ -314,6 +343,10 @@ app-half edge cannot produce and which no amount of readability would have surfa
restarts, no degradation, and the engine says `Check required!` every time, forever). Converting
properly **succeeds**, costs **7 s**, takes its own system-database backup, and **does not** cost the
ability to abort. **The trade that was expected here does not exist.**
- **2026-09-13 — the setting is in the catalog.** All four `mariadb:` sidecars carry
`MARIADB_AUTO_UPGRADE=1` (operator ruling, §3 decision 5), and `upgrade-test.py`'s engine-state field
now shows the conversion RUNNING on the bookstack edges. **And a gate holds the engines inside their
major until Slice 4:** `app-catalog-felhom.eu/scripts/check-engine-major.py` (R-469).
**Two rules for anything this arc builds around a database engine:**
@@ -428,10 +461,12 @@ Version strings stay in the logs, the API and the hub.
6. **The Update button is still unguarded.** It takes no backup, has no rollback, and can still
attempt a multi-major jump the app will refuse (R-40). **Slice 3 did not change that and must not
be read as having done so** — the precondition is slice 4 (R-448).
7. **An engine major can be applied without its datadir upgrade, and nothing notices.** Measured
2026-09-06: the catalog's own bookstack transition starts MariaDB 12.3 on an 11.6 datadir, and the
image logs that the required upgrade was **skipped** because the template sets no
`MARIADB_AUTO_UPGRADE`. The app serves. Whether that ever breaks is **not** established. **R-459.**
7. ~~**An engine major can be applied without its datadir upgrade, and nothing notices.**~~ **CLOSED
2026-09-13 for MariaDB (R-459):** every `mariadb:` sidecar carries `MARIADB_AUTO_UPGRADE=1`, and the
harness shows the conversion running on the E3/E3b edges (§3 decision 5). **What stays true:** the
PostgreSQL half (R-463) has no equivalent — the image performs no `pg_upgrade` — and the
engine-major rule (§3 precaution 3, R-469) is what keeps both engines inside their major until
Slice 4 gives the Update button a backup.
8. **Only three of 53 apps have ever had an upgrade measured**, and one of them (bookstack) can only
be half-proven headlessly (**R-460**). The widening is **R-462**, costed with real numbers.
9. **The hub does not record image tags at all.** Its report's container payload carries name, state,
@@ -0,0 +1,16 @@
ok FACT: kimai-db mariadb:11.6 -> 12.3 (cross-major) rc=1 (expected 1)
engine-major gate — range HEAD~1..HEAD: 1 compose file(s) changed, 1 engine pin(s) compared
ENGINE-MAJOR GATE FAILED: templates/kimai/docker-compose.yml service kimai-db moves mariadb 11 -> 12 (mariadb:11.6 -> mariadb:12.3).
RULE (app-catalog CLAUDE.md, operator ruling 2026-09-13): until the Update button takes a VERIFIED BACKUP as its precondition (Slice 4, felhom.eu OPEN-ITEMS.md R-448), no template may move a database-engine image across a MAJOR version.
WHY: MariaDB sidecars now carry MARIADB_AUTO_UPGRADE=1 and WILL convert the customer's datadir on the next Update; PostgreSQL's image refuses to start on an older major's datadir (R-463). Either way this is a customer-data event with no backup in front of it.
EXPIRY: this rule is removed DELIBERATELY when R-448 ships — the removal is its own register row, not a silent edit. Until then, keep the engine within its major.
ok FACT: docmost-postgres postgres:16-alpine -> 17-alpine rc=1 (expected 1)
ok FACT: kimai-db mariadb:11.6 -> mariadb:lts (major unreadable) rc=2 (expected 2)
ok GENUINE: kimai-db mariadb:11.6 -> 11.8 (within major) rc=0 (expected 0)
ok DECOY: major moves only in a comment + serverVersion env rc=0 (expected 0)
ok DECOY: the APP image crosses a major (kimai 2.57 -> 3.0) rc=0 (expected 0)
ok DECOY: 'mariadb:12.3' lands in README.md, not a template rc=0 (expected 0)
catalog gate decoys OK — 7 case(s), every label judged on its fact (R-421)
@@ -0,0 +1,8 @@
newest released controller : 0.236.0 (## v0.236.0 — "delete my data too" deletes the data, or says that it could not (2026-09-13)
newest golden baked : 0.232.0 (documentation/tests/golden-0.232.0-2026-09-01 [sha 5f8a53ed5b19…])
GOLDEN CURRENCY GATE FAILED: controller v0.236.0 is released and NO golden carries it (newest bake is 0.232.0; 4 release(s) behind).
A machine installed right now would receive v0.232.0 — the release is written, tested and pushed, and NOT delivered.
Fix: bake a golden per documentation/runbooks/RUNBOOK-manual-build.md §4.1, then vouch it (a THREE-field change: golden_version + agent_version + min_agent).
If the cadence ruling covers this release, issue a DATED waiver at documentation/tests/golden-waiver.yml (RUNBOOK-manual-build.md §4.2) — never a bypass.
rc=1
@@ -0,0 +1,12 @@
=== NEW gate (scripts/golden_currency_gate.py) ===
newest released controller : 9.9.9 (## v9.9.9 — synthetic)
newest golden baked : 9.9.7 (documentation/tests/golden-9.9.7-2026-01-01 [sha 9287f7cef5f1…])
waiver : VALID until 2026-09-26 (13 day(s) left) — R-468, reason: pre-customer development; goldens weekly (operator ruling 2026-09-13)
GOLDEN CURRENCY GATE ADVISORY — WAIVED, NOT CLEAN: controller v9.9.9 is released and NO golden carries it (newest bake is 9.9.7; 2 release(s) behind: 9.9.8, 9.9.9).
A machine installed right now would receive v9.9.7 and reach v9.9.9 by self-update.
Waived by R-468 until 2026-09-26 (13 day(s) left): pre-customer development; goldens weekly (operator ruling 2026-09-13)
This is the operator's 2026-09-13 cadence ruling, not a pass: bake weekly and before ANY drill or fresh install (RUNBOOK-manual-build.md §4.2). When the waiver expires this gate is red again.
golden currency gate OK (WAIVED) — the newest released controller has NO golden; a valid waiver covers it (NOTE: this checks the BAKE, not the vouch)
exit=0
@@ -0,0 +1,12 @@
=== NEW gate (scripts/golden_currency_gate.py) ===
newest released controller : 9.9.9 (## v9.9.9 — synthetic)
newest golden baked : 9.9.7 (documentation/tests/golden-9.9.7-2026-01-01 [sha 9287f7cef5f1…])
waiver : EXPIRED on 2026-09-12 (R-468)
GOLDEN CURRENCY GATE FAILED: controller v9.9.9 is released and NO golden carries it (newest bake is 9.9.7; 2 release(s) behind).
The waiver at documentation/tests/golden-waiver.yml EXPIRED on 2026-09-12 (R-468). It ran out, as a dated waiver is meant to: bake a golden, or renew it with a new dated commit (at most 14 days).
A machine installed right now would receive v9.9.7 — the release is written, tested and pushed, and NOT delivered.
Fix: bake a golden per documentation/runbooks/RUNBOOK-manual-build.md §4.1, then vouch it (a THREE-field change: golden_version + agent_version + min_agent).
If the cadence ruling covers this release, issue a DATED waiver at documentation/tests/golden-waiver.yml (RUNBOOK-manual-build.md §4.2) — never a bypass.
exit=1
@@ -0,0 +1,12 @@
=== NEW gate (scripts/golden_currency_gate.py) ===
newest released controller : 9.9.9 (## v9.9.9 — synthetic)
newest golden baked : 9.9.8 (documentation/tests/golden-9.9.8-2026-01-01 [sha 9287f7cef5f1…])
waiver : VALID until 2026-09-26 (13 day(s) left) — R-468, reason: pre-customer development; goldens weekly (operator ruling 2026-09-13)
GOLDEN CURRENCY GATE FAILED: golden 9.9.8 is baked but UNRECORDED — the controller CHANGELOG has no '## v9.9.8' heading.
A waiver exists (R-468, until 2026-09-26) and DOES NOT COVER THIS: it covers a golden that is behind the record, never one that is unrecorded (R-385).
The newest heading is 9.9.9. A golden ahead of the record was built from a version nobody wrote down, so no one can read what the fleet is running.
Fix: give v9.9.8 its own '## v9.9.8 — <what changed>' heading in felhom-controller/CHANGELOG.md, above the entries it supersedes. If its fix is currently described inside another version's entry, MOVE that text — do not duplicate it, and do not delete the reasoning.
If this bake was a throwaway that must never be delivered, delete its documentation/tests/golden-<VER>-<DATE>/ directory — never leave it to read as shipped.
exit=1
@@ -0,0 +1,8 @@
=== NEW gate (scripts/golden_currency_gate.py) ===
newest released controller : 9.9.9 (## v9.9.9 — synthetic)
newest golden baked : 9.9.7 (documentation/tests/golden-9.9.7-2026-01-01 [sha 9287f7cef5f1…])
GOLDEN CURRENCY GATE INCONCLUSIVE: the waiver at ../../../../../tmp/tmpiiph1vyo/golden-waiver.yml is MALFORMED — `expires:` is 15 days after `issued:` — the hard limit is 14. A waiver that tries to be permanent is refused; renew it with a new dated commit instead.
A malformed waiver is neither cover nor absence. Fix it (four lines: issued, expires <= 14 days later, reason, register_row) or delete it.
exit=2
@@ -0,0 +1,8 @@
=== NEW gate (scripts/golden_currency_gate.py) ===
newest released controller : 9.9.9 (## v9.9.9 — synthetic)
newest golden baked : 9.9.7 (documentation/tests/golden-9.9.7-2026-01-01 [sha 9287f7cef5f1…])
GOLDEN CURRENCY GATE INCONCLUSIVE: the waiver at ../../../../../tmp/tmp04cax9by/golden-waiver.yml is MALFORMED — `issued:` is absent or not a YYYY-MM-DD date (got '')
A malformed waiver is neither cover nor absence. Fix it (four lines: issued, expires <= 14 days later, reason, register_row) or delete it.
exit=2
@@ -0,0 +1,23 @@
=== REAL TREE (0.236.0 released, newest golden 0.232.0 — BEHIND), valid waiver planted, R-468 exists ===
--- OLD gate (HEAD before this task) ---
newest released controller : 0.236.0 (## v0.236.0 — "delete my data too" deletes the data, or says that it could not (2026-09-13)
newest golden baked : 0.232.0 (documentation/tests/golden-0.232.0-2026-09-01 [sha 5f8a53ed5b19…])
GOLDEN CURRENCY GATE FAILED: controller v0.236.0 is released and NO golden carries it (newest bake is 0.232.0).
A machine installed right now would receive v0.232.0 — the release is written, tested and pushed, and NOT delivered.
Fix: bake a golden per documentation/runbooks/RUNBOOK-manual-build.md §4.1, then vouch it (a THREE-field change: golden_version + agent_version + min_agent).
If this release deliberately needs no golden, record a waiver in documentation/backlog/OPEN-ITEMS.md — never a bypass.
exit=1
--- NEW gate ---
newest released controller : 0.236.0 (## v0.236.0 — "delete my data too" deletes the data, or says that it could not (2026-09-13)
newest golden baked : 0.232.0 (documentation/tests/golden-0.232.0-2026-09-01 [sha 5f8a53ed5b19…])
waiver : VALID until 2026-09-27 (14 day(s) left) — R-468, reason: pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
GOLDEN CURRENCY GATE ADVISORY — WAIVED, NOT CLEAN: controller v0.236.0 is released and NO golden carries it (newest bake is 0.232.0; 4 release(s) behind: 0.233.0, 0.234.0, 0.235.0, 0.236.0).
A machine installed right now would receive v0.232.0 and reach v0.236.0 by self-update.
Waived by R-468 until 2026-09-27 (14 day(s) left): pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
This is the operator's 2026-09-13 cadence ruling, not a pass: bake weekly and before ANY drill or fresh install (RUNBOOK-manual-build.md §4.2). When the waiver expires this gate is red again.
golden currency gate OK (WAIVED) — the newest released controller has NO golden; a valid waiver covers it (NOTE: this checks the BAKE, not the vouch)
exit=0
@@ -0,0 +1,8 @@
{
"privatebin": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,5 @@
privatebin | [13-Sep-2026 09:52:04] NOTICE: fpm is running, pid 11
privatebin | [13-Sep-2026 09:52:04] NOTICE: ready to handle connections
privatebin | 127.0.0.1 - - [13/Sep/2026:09:52:09 +0200] "GET / HTTP/1.1" 200 15569 "-" "Wget" "-"
privatebin | 172.18.0.1 - - [13/Sep/2026:09:52:09 +0200] "GET / HTTP/1.1" 200 22914 "-" "curl/8.14.1" "-"
privatebin | 172.18.0.1 - - [13/Sep/2026:09:52:09 +0200] "GET /?pasteid=95d75dc1cf5feaba HTTP/1.1" 200 311 "-" "curl/8.14.1" "-"
Whitespace-only changes.
@@ -0,0 +1,14 @@
[07:42:55] C3: deploying privatebin at FROM {'privatebin': 'privatebin/pdo:2.0.5'}
[07:43:01] FROM settled=True in 5.2s :: {"privatebin": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:43:01] privatebin: seeded paste id=95d75dc1cf5feaba
[07:43:01] privatebin: readback http=200 marker_present=True
[07:43:01] C1 (seed reads back BEFORE): True
[07:43:01] C3: swapping to TO {'privatebin': 'alpine:3.20'}
[07:43:02] TO up -d rc=0
[07:50:03] TO settled=False in 421.1s :: {"privatebin": {"status": "restarting", "health": "unhealthy", "restarts": 0, "exit": 0}}
[07:50:03] migration lines observed: 0
[07:52:03] app never answered on http://invalid:8080/ (last rc=6 code=000)
[07:52:03] RESULT (seed reads back AFTER): False
[07:52:03] C3: ABORT — putting the FROM images back
[07:52:09] privatebin: readback http=200 marker_present=True
[07:52:09] ABORT: app came back in 5.2s; data present=True
Whitespace-only changes.
@@ -0,0 +1,8 @@
{
"privatebin": {
"status": "restarting",
"health": "unhealthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,24 @@
{
"harness_version": 1,
"edge": "C3",
"app": "privatebin",
"note": "NEGATIVE control: the TO image starts and exits immediately",
"from": {
"privatebin": "privatebin/pdo:2.0.5"
},
"to": {
"privatebin": "alpine:3.20"
},
"verdict": "failed",
"seed_read_before": true,
"seed_read_after": false,
"healthy_after": false,
"migration_observed": null,
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"duration_s": 421.1,
"measured_at": "2026-09-13T07:52:09Z",
"evidence": "evidence/C3",
"total_s": 553.9
}
@@ -0,0 +1,14 @@
{
"bookstack": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
},
"bookstack-db": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,67 @@
bookstack | [migrations] started
bookstack | [migrations] 01-nginx-site-confs-default: skipped
bookstack | [migrations] 02-default-location: skipped
bookstack | [migrations] done
bookstack | ───────────────────────────────────────
bookstack |
bookstack | ██╗ ███████╗██╗ ██████╗
bookstack | ██║ ██╔════╝██║██╔═══██╗
bookstack | ██║ ███████╗██║██║ ██║
bookstack | ██║ ╚════██║██║██║ ██║
bookstack | ███████╗███████║██║╚██████╔╝
bookstack | ╚══════╝╚══════╝╚═╝ ╚═════╝
bookstack |
bookstack | Brought to you by linuxserver.io
bookstack | ───────────────────────────────────────
bookstack |
bookstack | To support LSIO projects visit:
bookstack | https://www.linuxserver.io/donate/
bookstack |
bookstack | ───────────────────────────────────────
bookstack | GID/UID
bookstack | ───────────────────────────────────────
bookstack-db | 2026-09-13 09:53:49+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.6.2+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:53:49+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
bookstack-db | 2026-09-13 09:53:49+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
bookstack-db | 2026-09-13 09:53:49+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.6.2+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:53:50+02:00 [Note] [Entrypoint]: MariaDB upgrade not required
bookstack-db | 2026-09-13 9:53:50 0 [Note] Starting MariaDB 11.6.2-MariaDB-ubu2404 source revision d8dad8c3b54cd09fefce7bc3b9749f427eed9709 server_uid NqHiasVU3XvSSnAvQ8Ghzed+a14= as process 1
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Compressed tables use zlib 1.3
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Number of transaction pools: 1
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
bookstack-db | 2026-09-13 9:53:50 0 [Warning] mariadbd: io_uring_queue_init() failed with errno 0
bookstack-db | 2026-09-13 9:53:50 0 [Warning] InnoDB: liburing disabled: falling back to innodb_use_native_aio=OFF
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Initializing buffer pool, total size = 128.000MiB, chunk size = 2.000MiB
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Completed initialization of buffer pool
bookstack |
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: End of log at LSN=2253291
bookstack | User UID: 1000
bookstack | User GID: 1000
bookstack | ───────────────────────────────────────
bookstack | Linuxserver.io version: v25.02.2-ls202
bookstack | Build-date: 2025-04-14T18:32:57+00:00
bookstack | ───────────────────────────────────────
bookstack |
bookstack | using keys found in /config/keys
bookstack | Waiting for DB to be available
bookstack |
bookstack | INFO Nothing to migrate.
bookstack |
bookstack | [custom-init] No custom files found, skipping...
bookstack | [ls.io-init] done.
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Opened 3 undo tablespaces
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: log sequence number 2253291; transaction id 2925
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
bookstack-db | 2026-09-13 9:53:50 0 [Note] Plugin 'FEEDBACK' is disabled.
bookstack-db | 2026-09-13 9:53:50 0 [Note] Plugin 'wsrep-provider' is disabled.
bookstack-db | 2026-09-13 9:53:50 0 [Note] InnoDB: Buffer pool(s) load completed at 260913 9:53:50
bookstack-db | 2026-09-13 9:53:54 0 [Note] Server socket created on IP: '0.0.0.0'.
bookstack-db | 2026-09-13 9:53:54 0 [Note] Server socket created on IP: '::'.
bookstack-db | 2026-09-13 9:53:54 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
bookstack-db | 2026-09-13 9:53:54 0 [Note] mariadbd: ready for connections.
bookstack-db | Version: '11.6.2-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
bookstack-db | 2026-09-13 9:53:56 5 [Warning] Aborted connection 5 to db: 'unconnected' user: 'unauthenticated' host: '172.19.0.3' (This connection closed normally without authentication)
@@ -0,0 +1,8 @@
{
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
}
@@ -0,0 +1,6 @@
bookstack | [migrations] started
bookstack | [migrations] 01-nginx-site-confs-default: skipped
bookstack | [migrations] 02-default-location: skipped
bookstack | [migrations] done
bookstack | INFO Running migrations.
bookstack | 2025_09_15_134701_migrate_entity_data ......................... 11.76ms DONE
@@ -0,0 +1,15 @@
[07:52:10] E3: deploying bookstack at FROM {'bookstack': 'lscr.io/linuxserver/bookstack:25.02.2', 'bookstack-db': 'mariadb:11.6'}
[07:52:56] FROM settled=True in 15.7s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:52:57] bookstack: artisan create-admin rc=0 :: Admin account with email "spike-e4414904@gate.invalid" successfully created!
[07:52:58] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-db3feb - Email: spike
[07:52:58] C1 (seed reads back BEFORE): True
[07:52:58] E3: swapping to TO {'bookstack': 'lscr.io/linuxserver/bookstack:26.05.2', 'bookstack-db': 'mariadb:12.3'}
[07:53:33] TO up -d rc=0
[07:53:43] TO settled=True in 10.5s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:53:43] engine state bookstack-db: 12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]
[07:53:43] migration lines observed: 6
[07:53:44] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-db3feb - Email: spike
[07:53:44] RESULT (seed reads back AFTER): True
[07:53:44] E3: ABORT — putting the FROM images back
[07:54:06] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-db3feb - Email: spike
[07:54:06] ABORT: app came back in 10.5s; data present=True
@@ -0,0 +1,171 @@
bookstack-db | 2026-09-13 09:53:17+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.3+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:53:17+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
bookstack-db | 2026-09-13 09:53:17+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
bookstack-db | 2026-09-13 09:53:17+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.3+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:53:17+02:00 [Note] [Entrypoint]: Starting temporary server
bookstack-db | 2026-09-13 09:53:17+02:00 [Note] [Entrypoint]: Waiting for server startup
bookstack-db | 2026-09-13 9:53:17 0 [Note] Starting MariaDB 12.3.3-MariaDB-ubu2404 source revision 83e909fc2a0dbc394b4b683fb3fa2d7dcf26cc5e server_uid uYRfNEQQC8vM+0dejC8iUUzoFC4= as process 56
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Compressed tables use zlib 1.3
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Number of transaction pools: 1
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
bookstack-db | 2026-09-13 9:53:17 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
bookstack-db | create_uring failed: falling back to libaio
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Using Linux native AIO
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Completed initialization of buffer pool
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: End of log at LSN=1173728
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Opened 3 undo tablespaces
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
bookstack-db | 2026-09-13 9:53:17 0 [Note] InnoDB: log sequence number 1173728; transaction id 2278
bookstack-db | 2026-09-13 9:53:17 0 [Note] Plugin 'FEEDBACK' is disabled.
bookstack-db | 2026-09-13 9:53:17 0 [Note] Plugin 'wsrep-provider' is disabled.
bookstack-db | 2026-09-13 9:53:19 0 [Note] Replication not automatically started: --skip-slave-start was specified
bookstack-db | 2026-09-13 9:53:19 0 [Note] mariadbd: ready for connections.
bookstack-db | Version: '12.3.3-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 0 mariadb.org binary distribution
bookstack-db | 2026-09-13 09:53:19+02:00 [Note] [Entrypoint]: Temporary server started.
bookstack-db | 2026-09-13 09:53:19+02:00 [Note] [Entrypoint]: Backing up system database to system_mysql_backup_11.6.2-MariaDB.sql.zst
bookstack-db | 2026-09-13 09:53:20+02:00 [Note] [Entrypoint]: Backing up complete
bookstack-db | 2026-09-13 09:53:20+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade
bookstack-db | The --upgrade-system-tables option was used, user tables won't be touched.
bookstack-db | Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
bookstack-db | Phase 1/8: Checking and upgrading mysql database
bookstack-db | Processing databases
bookstack-db | mysql
bookstack-db | mysql.column_stats OK
bookstack-db | mysql.columns_priv OK
bookstack-db | mysql.db OK
bookstack-db | mysql.event OK
bookstack-db | mysql.func OK
bookstack-db | mysql.global_priv OK
bookstack-db | mysql.gtid_slave_pos OK
bookstack-db | mysql.help_category OK
bookstack-db | mysql.help_keyword OK
bookstack-db | mysql.help_relation OK
bookstack-db | mysql.help_topic OK
bookstack-db | mysql.index_stats OK
bookstack-db | mysql.innodb_index_stats OK
bookstack-db | mysql.innodb_table_stats OK
bookstack-db | mysql.plugin OK
bookstack-db | mysql.proc OK
bookstack-db | mysql.procs_priv OK
bookstack-db | mysql.proxies_priv OK
bookstack-db | mysql.roles_mapping OK
bookstack-db | mysql.servers OK
bookstack-db | mysql.table_stats OK
bookstack-db | mysql.tables_priv OK
bookstack-db | mysql.time_zone OK
bookstack-db | mysql.time_zone_leap_second OK
bookstack-db | mysql.time_zone_name OK
bookstack-db | mysql.time_zone_transition OK
bookstack-db | mysql.time_zone_transition_type OK
bookstack-db | mysql.transaction_registry OK
bookstack-db | Phase 2/8: Installing used storage engines... Skipped
bookstack-db | Phase 3/8: Running 'mysql_fix_privilege_tables'
bookstack-db | Phase 4/8: Fixing views... Skipped
bookstack-db | Phase 5/8: Fixing table and database names ... Skipped
bookstack-db | Phase 6/8: Checking and upgrading tables... Skipped
bookstack-db | Phase 7/8: uninstalling plugins
bookstack-db | Phase 8/8: Running 'FLUSH PRIVILEGES'
bookstack-db | OK
bookstack-db | 2026-09-13 09:53:26+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade
bookstack-db | 2026-09-13 09:53:26+02:00 [Note] [Entrypoint]: Stopping temporary server
bookstack-db | 2026-09-13 9:53:26 0 [Note] mariadbd (initiated by: unknown): Normal shutdown
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: FTS optimize thread exiting.
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Starting shutdown...
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Removed temporary tablespace data file: "./ibtmp1"
bookstack-db | 2026-09-13 9:53:26 0 [Note] Shutdown completed; log sequence number 1173728; transaction id 2282
bookstack-db | 2026-09-13 9:53:26 0 [Note] mariadbd: Shutdown complete
bookstack-db | 2026-09-13 09:53:26+02:00 [Note] [Entrypoint]: Temporary server stopped
bookstack-db | 2026-09-13 9:53:26 0 [Note] Starting MariaDB 12.3.3-MariaDB-ubu2404 source revision 83e909fc2a0dbc394b4b683fb3fa2d7dcf26cc5e server_uid uYRfNEQQC8vM+0dejC8iUUzoFC4= as process 1
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Compressed tables use zlib 1.3
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Number of transaction pools: 1
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
bookstack-db | 2026-09-13 9:53:26 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
bookstack-db | create_uring failed: falling back to libaio
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Using Linux native AIO
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Completed initialization of buffer pool
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: End of log at LSN=1173728
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Opened 3 undo tablespaces
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: log sequence number 1173728; transaction id 2278
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
bookstack-db | 2026-09-13 9:53:26 0 [Note] Plugin 'FEEDBACK' is disabled.
bookstack-db | 2026-09-13 9:53:26 0 [Note] Plugin 'wsrep-provider' is disabled.
bookstack-db | 2026-09-13 9:53:26 0 [Note] InnoDB: Buffer pool(s) load completed at 260913 9:53:26
bookstack-db | 2026-09-13 9:53:28 0 [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
bookstack-db | 2026-09-13 9:53:28 0 [Note] Server socket created on IP: '::', port: '3306'.
bookstack-db | 2026-09-13 9:53:28 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
bookstack-db | 2026-09-13 9:53:28 0 [Note] mariadbd: ready for connections.
bookstack-db | Version: '12.3.3-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
bookstack-db | 2026-09-13 9:53:34 5 [Warning] Aborted connection 5 to db: 'unconnected' user: 'unauthenticated' host: '172.19.0.3' (This connection closed normally without authentication)
bookstack | [migrations] started
bookstack | [migrations] 01-nginx-site-confs-default: skipped
bookstack | [migrations] 02-default-location: skipped
bookstack | [migrations] done
bookstack | ───────────────────────────────────────
bookstack |
bookstack | ██╗ ███████╗██╗ ██████╗
bookstack | ██║ ██╔════╝██║██╔═══██╗
bookstack | ██║ ███████╗██║██║ ██║
bookstack | ██║ ╚════██║██║██║ ██║
bookstack | ███████╗███████║██║╚██████╔╝
bookstack | ╚══════╝╚══════╝╚═╝ ╚═════╝
bookstack |
bookstack | Brought to you by linuxserver.io
bookstack | ───────────────────────────────────────
bookstack |
bookstack | To support the app dev(s) visit:
bookstack | Bookstack: https://www.bookstackapp.com/donate/
bookstack |
bookstack | To support LSIO projects visit:
bookstack | https://www.linuxserver.io/donate/
bookstack |
bookstack | ───────────────────────────────────────
bookstack | GID/UID
bookstack | ───────────────────────────────────────
bookstack |
bookstack | User UID: 1000
bookstack | User GID: 1000
bookstack | ───────────────────────────────────────
bookstack | Linuxserver.io version: v26.05.2-ls276
bookstack | Build-date: 2026-07-27T19:41:43+00:00
bookstack | ───────────────────────────────────────
bookstack |
bookstack | using keys found in /config/keys
bookstack | **** The following active confs have different version dates than the samples that are shipped. ****
bookstack | **** This may be due to user customization or an update to the samples. ****
bookstack | **** You should compare the following files to the samples in the same folder and update them. ****
bookstack | **** Use the link at the top of the file to view the changelog. ****
bookstack | ┌────────────┬────────────┬────────────────────────────────────────────────────────────────────────┐
bookstack | │ old date │ new date │ path │
bookstack | ├────────────┼────────────┼────────────────────────────────────────────────────────────────────────┤
bookstack | │ 2024-07-16 │ 2026-06-27 │ /config/nginx/site-confs/default.conf │
bookstack | │ 2024-12-17 │ 2025-12-26 │ /config/nginx/nginx.conf │
bookstack | │ 2024-12-06 │ 2026-06-27 │ /config/nginx/ssl.conf │
bookstack | └────────────┴────────────┴────────────────────────────────────────────────────────────────────────┘
bookstack | Waiting for DB to be available
bookstack |
bookstack | INFO Running migrations.
bookstack |
bookstack | 2025_04_18_215145_add_content_refs_and_archived_to_comments ... 85.17ms DONE
bookstack | 2025_09_02_111542_remove_unused_columns ....................... 82.04ms DONE
bookstack | 2025_09_15_132850_create_entities_table ...................... 307.42ms DONE
bookstack | 2025_09_15_134701_migrate_entity_data ......................... 11.76ms DONE
bookstack | 2025_09_15_134751_update_entity_relation_columns ............. 888.10ms DONE
bookstack | 2025_09_15_134813_drop_old_entity_tables ...................... 48.37ms DONE
bookstack | 2025_10_18_163331_clean_user_id_references ................... 461.62ms DONE
bookstack | 2025_10_22_134507_update_comments_relation_field_names ........ 42.49ms DONE
bookstack | 2025_11_23_161812_create_slug_history_table .................. 132.11ms DONE
bookstack | 2025_12_15_140219_create_mention_history_table ................ 67.89ms DONE
bookstack | 2025_12_19_103417_add_views_viewable_type_index ............... 30.42ms DONE
bookstack | 2026_04_19_141616_add_revision_view_all_permission ............. 4.70ms DONE
bookstack |
bookstack | [custom-init] No custom files found, skipping...
bookstack | [ls.io-init] done.
@@ -0,0 +1,14 @@
{
"bookstack": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
},
"bookstack-db": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,33 @@
{
"harness_version": 1,
"edge": "E3",
"app": "bookstack",
"note": "catalog transition 0b73e5e: app AND engine together",
"from": {
"bookstack": "lscr.io/linuxserver/bookstack:25.02.2",
"bookstack-db": "mariadb:11.6"
},
"to": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:12.3"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "bookstack | [migrations] started",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": {
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
},
"duration_s": 10.5,
"measured_at": "2026-09-13T07:54:06Z",
"evidence": "evidence/E3",
"total_s": 116.3
}
@@ -0,0 +1,14 @@
{
"bookstack": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
},
"bookstack-db": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,183 @@
bookstack-db | 2026-09-13 09:55:08+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.6.2+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:55:08+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
bookstack-db | 2026-09-13 09:55:08+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
bookstack-db | 2026-09-13 09:55:08+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.6.2+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:55:08+02:00 [Note] [Entrypoint]: MariaDB upgrade not required
bookstack-db | 2026-09-13 9:55:08 0 [Note] Starting MariaDB 11.6.2-MariaDB-ubu2404 source revision d8dad8c3b54cd09fefce7bc3b9749f427eed9709 server_uid osbkHDTZ9yfxr2UZyu83QV8Ztoc= as process 1
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Compressed tables use zlib 1.3
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Number of transaction pools: 1
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
bookstack-db | 2026-09-13 9:55:08 0 [Warning] mariadbd: io_uring_queue_init() failed with errno 0
bookstack-db | 2026-09-13 9:55:08 0 [Warning] InnoDB: liburing disabled: falling back to innodb_use_native_aio=OFF
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Initializing buffer pool, total size = 128.000MiB, chunk size = 2.000MiB
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Completed initialization of buffer pool
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: End of log at LSN=2025029
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Opened 3 undo tablespaces
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: log sequence number 2025029; transaction id 2873
bookstack-db | 2026-09-13 9:55:08 0 [Note] Plugin 'FEEDBACK' is disabled.
bookstack-db | 2026-09-13 9:55:08 0 [Note] Plugin 'wsrep-provider' is disabled.
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
bookstack-db | 2026-09-13 9:55:08 0 [Note] InnoDB: Buffer pool(s) load completed at 260913 9:55:08
bookstack-db | 2026-09-13 9:55:09 0 [Note] Server socket created on IP: '0.0.0.0'.
bookstack-db | 2026-09-13 9:55:09 0 [Note] Server socket created on IP: '::'.
bookstack | [migrations] started
bookstack | [migrations] 01-nginx-site-confs-default: executing...
bookstack | [migrations] 01-nginx-site-confs-default: succeeded
bookstack | [migrations] 02-default-location: executing...
bookstack | [migrations] 02-default-location: succeeded
bookstack | [migrations] done
bookstack | ───────────────────────────────────────
bookstack |
bookstack | ██╗ ███████╗██╗ ██████╗
bookstack | ██║ ██╔════╝██║██╔═══██╗
bookstack | ██║ ███████╗██║██║ ██║
bookstack | ██║ ╚════██║██║██║ ██║
bookstack | ███████╗███████║██║╚██████╔╝
bookstack | ╚══════╝╚══════╝╚═╝ ╚═════╝
bookstack |
bookstack | Brought to you by linuxserver.io
bookstack | ───────────────────────────────────────
bookstack |
bookstack | To support the app dev(s) visit:
bookstack | Bookstack: https://www.bookstackapp.com/donate/
bookstack |
bookstack | To support LSIO projects visit:
bookstack | https://www.linuxserver.io/donate/
bookstack |
bookstack | ───────────────────────────────────────
bookstack | GID/UID
bookstack | ───────────────────────────────────────
bookstack |
bookstack | User UID: 1000
bookstack | User GID: 1000
bookstack | ───────────────────────────────────────
bookstack | Linuxserver.io version: v26.05.2-ls276
bookstack | Build-date: 2026-07-27T19:41:43+00:00
bookstack | ───────────────────────────────────────
bookstack |
bookstack | Setting resolver to 127.0.0.11
bookstack | Setting worker_processes to 4
bookstack | generating self-signed keys in /config/keys, you can replace these with your own keys if required
bookstack | .+...+.+.....+.+...........+...+.+........+.+++++++++++++++++++++++++++++++++++++++*...+......+........+.+.....+.+..+...+.......+...+...............+.....+.........+......+....+......+.....+...+.+.....+...+.+......+...........+...+++++++++++++++++++++++++++++++++++++++*...............+.+...........+.........+.+...+...+........+....+.....+............+.........+.+......+....................+..........+...+..+....+.....+......+.......+...+...+......+.....+..........+......+..+.+......+.....+.+..............+...............+.+............+..+....+.........+.....+.+.....+......+...............+......+....+.....+......+...+....++++++
bookstack | ...+......+...+.....+......+.+...+...........+++++++++++++++++++++++++++++++++++++++*..+...+...+..+..........+.....+....+...............+...........+.........+.........+...................+...+.........+.....+......+...+......+.+..+..........+...............+.....+..........+.....+......+.+........+...+...+.+...+........+++++++++++++++++++++++++++++++++++++++*.......+......+........+.+.........+..+...+......+....+..+.+...+..+.........+......+....+...+......+..+...+.+......+........+....+...+..................+..+..........+........+....+...........+......+.+...+..+....+........+.+.....+.........+...+...+.+.....+...+.......+......+.........+........+.............+...+.....+......+......+.......+......+.........+.....+..........+...+...+............+..+.+......+...........+.......+....................+.+..+.........+......+...+..........+......+...+...+..+.+...........+.+........+....+..+.+.....+.........+.........+....+...+..+.......+.....+................+.....+.........+...+.+......+.....+.........+.+..................+......+..+..........+..+................+.........+...+...+..+...+......+...+.+......+.....+......+...+.+.....+...+............+....+..+...+....+.....+.+..+.......+......+.....+.......+........+.......+.........+.....+...+.+..+....+...+............+...........+.......+...+...+......+...+.....+...............+.+..+.............+.....+.+.....+...+.+.....+.+..............+.+...+...+...............+..+...+.........+..........++++++
bookstack | -----
bookstack | Waiting for DB to be available
bookstack |
bookstack | INFO Preparing database.
bookstack |
bookstack | Creating migration table ...................................... 21.36ms DONE
bookstack |
bookstack | INFO Running migrations.
bookstack |
bookstack | 2014_10_12_000000_create_users_table ......................... 279.20ms DONE
bookstack | 2014_10_12_100000_create_password_resets_table ................ 60.91ms DONE
bookstack | 2015_07_12_114933_create_books_table .......................... 15.20ms DONE
bookstack | 2015_07_12_190027_create_pages_table .......................... 13.81ms DONE
bookstack-db | 2026-09-13 9:55:09 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
bookstack-db | 2026-09-13 9:55:09 0 [Note] mariadbd: ready for connections.
bookstack-db | Version: '11.6.2-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
bookstack | 2015_07_13_172121_create_images_table ......................... 11.55ms DONE
bookstack | 2015_07_27_172342_create_chapters_table ....................... 17.39ms DONE
bookstack | 2015_08_08_200447_add_users_to_entities ...................... 146.31ms DONE
bookstack | 2015_08_09_093534_create_page_revisions_table ................. 14.90ms DONE
bookstack | 2015_08_16_142133_create_activities_table ..................... 11.43ms DONE
bookstack | 2015_08_29_105422_add_roles_and_permissions .................. 311.76ms DONE
bookstack | 2015_08_30_125859_create_settings_table ....................... 17.18ms DONE
bookstack | 2015_08_31_175240_add_search_indexes ........................... 0.08ms DONE
bookstack | 2015_09_04_165821_create_social_accounts_table ................ 61.62ms DONE
bookstack | 2015_09_05_164707_add_email_confirmation_table ................ 79.45ms DONE
bookstack | 2015_11_21_145609_create_views_table .......................... 12.53ms DONE
bookstack | 2015_11_26_221857_add_entity_indexes ......................... 487.07ms DONE
bookstack | 2015_12_05_145049_fulltext_weighting ........................... 0.06ms DONE
bookstack | 2015_12_07_195238_add_image_upload_types ...................... 77.75ms DONE
bookstack | 2015_12_09_195748_add_user_avatars ............................ 19.71ms DONE
bookstack | 2016_01_11_210908_add_external_auth_to_users .................. 47.16ms DONE
bookstack | 2016_02_25_184030_add_slug_to_revisions ....................... 83.43ms DONE
bookstack | 2016_02_27_120329_update_permissions_and_roles ............... 159.05ms DONE
bookstack | 2016_02_28_084200_add_entity_access_controls ................. 259.17ms DONE
bookstack | 2016_03_09_203143_add_page_revision_types ..................... 42.78ms DONE
bookstack | 2016_03_13_082138_add_page_drafts ............................. 43.51ms DONE
bookstack | 2016_03_25_123157_add_markdown_support ........................ 38.64ms DONE
bookstack | 2016_04_09_100730_add_view_permissions_to_roles ............... 43.26ms DONE
bookstack | 2016_04_20_192649_create_joint_permissions_table ............. 300.32ms DONE
bookstack | 2016_05_06_185215_create_tags_table .......................... 113.02ms DONE
bookstack | 2016_07_07_181521_add_summary_to_page_revisions ............... 17.06ms DONE
bookstack | 2016_09_29_101449_remove_hidden_roles ......................... 76.98ms DONE
bookstack | 2016_10_09_142037_create_attachments_table .................... 67.83ms DONE
bookstack | 2017_01_21_163556_create_cache_table .......................... 42.59ms DONE
bookstack | 2017_01_21_163602_create_sessions_table ....................... 42.74ms DONE
bookstack | 2017_03_19_091553_create_search_index_table .................. 115.70ms DONE
bookstack | 2017_04_20_185112_add_revision_counts ......................... 62.91ms DONE
bookstack | 2017_07_02_152834_update_db_encoding_to_ut8mb4 ................. 0.07ms DONE
bookstack | 2017_08_01_130541_create_comments_table ....................... 86.66ms DONE
bookstack | 2017_08_29_102650_add_cover_image_display ..................... 16.83ms DONE
bookstack | 2018_07_15_173514_add_role_external_auth_id ................... 48.22ms DONE
bookstack | 2018_08_04_115700_create_bookshelves_table ................... 384.06ms DONE
bookstack | 2019_07_07_112515_add_template_support ........................ 48.61ms DONE
bookstack | 2019_08_17_140214_add_user_invites_table ...................... 62.14ms DONE
bookstack | 2019_12_29_120917_add_api_auth ................................ 88.92ms DONE
bookstack | 2020_08_04_111754_drop_joint_permissions_id .................. 105.72ms DONE
bookstack | 2020_08_04_131052_remove_role_name_field ...................... 16.66ms DONE
bookstack | 2020_09_19_094251_add_activity_indexes ........................ 49.34ms DONE
bookstack | 2020_09_27_210059_add_entity_soft_deletes ..................... 78.73ms DONE
bookstack | 2020_09_27_210528_create_deletions_table ...................... 92.71ms DONE
bookstack | 2020_11_07_232321_simplify_activities_table .................. 141.84ms DONE
bookstack | 2020_12_30_173528_add_owned_by_field_to_entities ............. 200.24ms DONE
bookstack | 2021_01_30_225441_add_settings_type_column .................... 21.09ms DONE
bookstack | 2021_03_08_215138_add_user_slug ............................... 49.08ms DONE
bookstack | 2021_05_15_173110_create_favourites_table ..................... 66.80ms DONE
bookstack | 2021_06_30_173111_create_mfa_values_table ..................... 62.79ms DONE
bookstack | 2021_07_03_085038_add_mfa_enforced_to_roles_table ............. 17.85ms DONE
bookstack | 2021_08_28_161743_add_export_role_permission ................... 6.47ms DONE
bookstack | 2021_09_26_044614_add_activities_ip_column .................... 20.29ms DONE
bookstack | 2021_11_26_070438_add_index_for_user_ip ....................... 27.34ms DONE
bookstack | 2021_12_07_111343_create_webhooks_table ...................... 119.99ms DONE
bookstack | 2021_12_13_152024_create_jobs_table ........................... 38.72ms DONE
bookstack | 2021_12_13_152120_create_failed_jobs_table .................... 37.85ms DONE
bookstack | 2022_01_03_154041_add_webhooks_timeout_error_columns .......... 70.20ms DONE
bookstack | 2022_04_17_101741_add_editor_change_field_and_permission ...... 27.06ms DONE
bookstack | 2022_04_25_140741_update_polymorphic_types .................... 16.24ms DONE
bookstack | 2022_07_16_170051_drop_joint_permission_type ................. 123.83ms DONE
bookstack | 2022_08_17_092941_create_references_table .................... 112.81ms DONE
bookstack | 2022_09_02_082910_fix_shelf_cover_image_types .................. 0.71ms DONE
bookstack | 2022_10_07_091406_flatten_entity_permissions_table ............ 90.68ms DONE
bookstack | 2022_10_08_104202_drop_entity_restricted_field ................ 93.44ms DONE
bookstack | 2023_01_24_104625_refactor_joint_permissions_storage ......... 142.21ms DONE
bookstack | 2023_01_28_141230_copy_color_settings_for_dark_mode ............ 1.09ms DONE
bookstack | 2023_02_20_093655_increase_attachments_path_length ............ 36.15ms DONE
bookstack | 2023_02_23_200227_add_updated_at_index_to_pages ............... 23.45ms DONE
bookstack | 2023_06_10_071823_remove_guest_user_secondary_roles ............ 1.97ms DONE
bookstack | 2023_06_25_181952_remove_bookshelf_create_entity_permissions ... 0.06ms DONE
bookstack | 2023_07_25_124945_add_receive_notifications_role_permissions ... 6.28ms DONE
bookstack | 2023_07_31_104430_create_watches_table ........................ 89.12ms DONE
bookstack | 2023_08_21_174248_increase_cache_size ......................... 30.81ms DONE
bookstack | 2023_12_02_104541_add_default_template_to_books ............... 20.41ms DONE
bookstack | 2023_12_17_140913_add_description_html_to_entities ............ 73.97ms DONE
bookstack | 2024_01_01_104542_add_default_template_to_chapters ............ 20.92ms DONE
bookstack | 2024_02_04_141358_add_views_updated_index ..................... 26.40ms DONE
bookstack | 2024_05_04_154409_rename_activity_relation_columns ............ 39.24ms DONE
bookstack | 2024_09_29_140340_ensure_editor_value_set ...................... 2.58ms DONE
bookstack | 2024_10_29_114420_add_import_role_permission ................... 5.16ms DONE
bookstack | 2024_11_02_160700_create_imports_table ........................ 36.28ms DONE
bookstack | 2024_11_27_171039_add_instance_id_setting ..................... 10.40ms DONE
bookstack | 2025_01_29_180933_create_sort_rules_table ..................... 11.84ms DONE
bookstack | 2025_02_05_150842_add_sort_rule_id_to_books ................... 20.57ms DONE
bookstack | 2025_04_18_215145_add_content_refs_and_archived_to_comments ... 66.35ms DONE
bookstack | 2025_09_02_111542_remove_unused_columns ....................... 69.84ms DONE
bookstack | 2025_09_15_132850_create_entities_table ...................... 281.26ms DONE
bookstack | 2025_09_15_134701_migrate_entity_data .......................... 9.73ms DONE
bookstack | 2025_09_15_134751_update_entity_relation_columns ............. 794.71ms DONE
bookstack | 2025_09_15_134813_drop_old_entity_tables ...................... 49.70ms DONE
bookstack | 2025_10_18_163331_clean_user_id_references ................... 457.39ms DONE
bookstack | 2025_10_22_134507_update_comments_relation_field_names ........ 40.71ms DONE
bookstack | 2025_11_23_161812_create_slug_history_table .................. 112.42ms DONE
bookstack | 2025_12_15_140219_create_mention_history_table ................ 67.72ms DONE
bookstack | 2025_12_19_103417_add_views_viewable_type_index ............... 22.39ms DONE
bookstack | 2026_04_19_141616_add_revision_view_all_permission ............. 4.69ms DONE
bookstack |
bookstack | [custom-init] No custom files found, skipping...
bookstack | [ls.io-init] done.
@@ -0,0 +1,8 @@
{
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
}
@@ -0,0 +1,6 @@
bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade
bookstack-db | The --upgrade-system-tables option was used, user tables won't be touched.
bookstack-db | Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
bookstack-db | Phase 1/8: Checking and upgrading mysql database
bookstack-db | Phase 6/8: Checking and upgrading tables... Skipped
bookstack-db | 2026-09-13 09:54:59+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade
@@ -0,0 +1,15 @@
[07:54:10] E3b: deploying bookstack at FROM {'bookstack': 'lscr.io/linuxserver/bookstack:26.05.2', 'bookstack-db': 'mariadb:11.6'}
[07:54:47] FROM settled=True in 20.9s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:54:48] bookstack: artisan create-admin rc=0 :: Admin account with email "spike-87a54b85@gate.invalid" successfully created!
[07:54:49] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-7b31eb - Email: spike
[07:54:49] C1 (seed reads back BEFORE): True
[07:54:49] E3b: swapping to TO {'bookstack': 'lscr.io/linuxserver/bookstack:26.05.2', 'bookstack-db': 'mariadb:12.3'}
[07:55:06] TO up -d rc=0
[07:55:06] TO settled=True in 0.2s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:55:06] engine state bookstack-db: 12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]
[07:55:06] migration lines observed: 6
[07:55:07] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-7b31eb - Email: spike
[07:55:07] RESULT (seed reads back AFTER): True
[07:55:07] E3b: ABORT — putting the FROM images back
[07:55:14] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-7b31eb - Email: spike
[07:55:14] ABORT: app came back in 0.2s; data present=True
@@ -0,0 +1,260 @@
bookstack-db | 2026-09-13 09:54:50+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.3+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:54:51+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
bookstack-db | 2026-09-13 09:54:51+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
bookstack-db | 2026-09-13 09:54:51+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.3+maria~ubu2404 started.
bookstack-db | 2026-09-13 09:54:51+02:00 [Note] [Entrypoint]: Starting temporary server
bookstack-db | 2026-09-13 09:54:51+02:00 [Note] [Entrypoint]: Waiting for server startup
bookstack | [migrations] started
bookstack-db | 2026-09-13 9:54:51 0 [Note] Starting MariaDB 12.3.3-MariaDB-ubu2404 source revision 83e909fc2a0dbc394b4b683fb3fa2d7dcf26cc5e server_uid bR6d3VLG8Cdw7s40Fw1l0qseMmo= as process 56
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Compressed tables use zlib 1.3
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Number of transaction pools: 1
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
bookstack-db | 2026-09-13 9:54:51 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
bookstack-db | create_uring failed: falling back to libaio
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Using Linux native AIO
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Completed initialization of buffer pool
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: End of log at LSN=2025029
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Opened 3 undo tablespaces
bookstack | [migrations] 01-nginx-site-confs-default: executing...
bookstack | [migrations] 01-nginx-site-confs-default: succeeded
bookstack | [migrations] 02-default-location: executing...
bookstack | [migrations] 02-default-location: succeeded
bookstack | [migrations] done
bookstack | ───────────────────────────────────────
bookstack |
bookstack | ██╗ ███████╗██╗ ██████╗
bookstack | ██║ ██╔════╝██║██╔═══██╗
bookstack | ██║ ███████╗██║██║ ██║
bookstack | ██║ ╚════██║██║██║ ██║
bookstack | ███████╗███████║██║╚██████╔╝
bookstack | ╚══════╝╚══════╝╚═╝ ╚═════╝
bookstack |
bookstack | Brought to you by linuxserver.io
bookstack | ───────────────────────────────────────
bookstack |
bookstack | To support the app dev(s) visit:
bookstack | Bookstack: https://www.bookstackapp.com/donate/
bookstack |
bookstack | To support LSIO projects visit:
bookstack | https://www.linuxserver.io/donate/
bookstack |
bookstack | ───────────────────────────────────────
bookstack | GID/UID
bookstack | ───────────────────────────────────────
bookstack |
bookstack | User UID: 1000
bookstack | User GID: 1000
bookstack | ───────────────────────────────────────
bookstack | Linuxserver.io version: v26.05.2-ls276
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
bookstack | Build-date: 2026-07-27T19:41:43+00:00
bookstack | ───────────────────────────────────────
bookstack |
bookstack | Setting resolver to 127.0.0.11
bookstack | Setting worker_processes to 4
bookstack | generating self-signed keys in /config/keys, you can replace these with your own keys if required
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
bookstack | .+...+.+.....+.+...........+...+.+........+.+++++++++++++++++++++++++++++++++++++++*...+......+........+.+.....+.+..+...+.......+...+...............+.....+.........+......+....+......+.....+...+.+.....+...+.+......+...........+...+++++++++++++++++++++++++++++++++++++++*...............+.+...........+.........+.+...+...+........+....+.....+............+.........+.+......+....................+..........+...+..+....+.....+......+.......+...+...+......+.....+..........+......+..+.+......+.....+.+..............+...............+.+............+..+....+.........+.....+.+.....+......+...............+......+....+.....+......+...+....++++++
bookstack | ...+......+...+.....+......+.+...+...........+++++++++++++++++++++++++++++++++++++++*..+...+...+..+..........+.....+....+...............+...........+.........+.........+...................+...+.........+.....+......+...+......+.+..+..........+...............+.....+..........+.....+......+.+........+...+...+.+...+........+++++++++++++++++++++++++++++++++++++++*.......+......+........+.+.........+..+...+......+....+..+.+...+..+.........+......+....+...+......+..+...+.+......+........+....+...+..................+..+..........+........+....+...........+......+.+...+..+....+........+.+.....+.........+...+...+.+.....+...+.......+......+.........+........+.............+...+.....+......+......+.......+......+.........+.....+..........+...+...+............+..+.+......+...........+.......+....................+.+..+.........+......+...+..........+......+...+...+..+.+...........+.+........+....+..+.+.....+.........+.........+....+...+..+.......+.....+................+.....+.........+...+.+......+.....+.........+.+..................+......+..+..........+..+................+.........+...+...+..+...+......+...+.+......+.....+......+...+.+.....+...+............+....+..+...+....+.....+.+..+.......+......+.....+.......+........+.......+.........+.....+...+.+..+....+...+............+...........+.......+...+...+......+...+.....+...............+.+..+.............+.....+.+.....+...+.+.....+.+..............+.+...+...+...............+..+...+.........+..........++++++
bookstack | -----
bookstack | Waiting for DB to be available
bookstack |
bookstack-db | 2026-09-13 9:54:51 0 [Note] InnoDB: log sequence number 2025029; transaction id 2873
bookstack | INFO Preparing database.
bookstack-db | 2026-09-13 9:54:51 0 [Note] Plugin 'FEEDBACK' is disabled.
bookstack-db | 2026-09-13 9:54:51 0 [Note] Plugin 'wsrep-provider' is disabled.
bookstack-db | 2026-09-13 9:54:52 0 [Note] Replication not automatically started: --skip-slave-start was specified
bookstack-db | 2026-09-13 9:54:52 0 [Note] mariadbd: ready for connections.
bookstack-db | Version: '12.3.3-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 0 mariadb.org binary distribution
bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Temporary server started.
bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Backing up system database to system_mysql_backup_11.6.2-MariaDB.sql.zst
bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Backing up complete
bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade
bookstack-db | The --upgrade-system-tables option was used, user tables won't be touched.
bookstack-db | Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
bookstack-db | Phase 1/8: Checking and upgrading mysql database
bookstack-db | Processing databases
bookstack-db | mysql
bookstack-db | mysql.column_stats OK
bookstack-db | mysql.columns_priv OK
bookstack |
bookstack-db | mysql.db OK
bookstack | Creating migration table ...................................... 21.36ms DONE
bookstack |
bookstack | INFO Running migrations.
bookstack |
bookstack | 2014_10_12_000000_create_users_table ......................... 279.20ms DONE
bookstack | 2014_10_12_100000_create_password_resets_table ................ 60.91ms DONE
bookstack | 2015_07_12_114933_create_books_table .......................... 15.20ms DONE
bookstack | 2015_07_12_190027_create_pages_table .......................... 13.81ms DONE
bookstack | 2015_07_13_172121_create_images_table ......................... 11.55ms DONE
bookstack | 2015_07_27_172342_create_chapters_table ....................... 17.39ms DONE
bookstack | 2015_08_08_200447_add_users_to_entities ...................... 146.31ms DONE
bookstack | 2015_08_09_093534_create_page_revisions_table ................. 14.90ms DONE
bookstack | 2015_08_16_142133_create_activities_table ..................... 11.43ms DONE
bookstack | 2015_08_29_105422_add_roles_and_permissions .................. 311.76ms DONE
bookstack | 2015_08_30_125859_create_settings_table ....................... 17.18ms DONE
bookstack | 2015_08_31_175240_add_search_indexes ........................... 0.08ms DONE
bookstack | 2015_09_04_165821_create_social_accounts_table ................ 61.62ms DONE
bookstack | 2015_09_05_164707_add_email_confirmation_table ................ 79.45ms DONE
bookstack | 2015_11_21_145609_create_views_table .......................... 12.53ms DONE
bookstack | 2015_11_26_221857_add_entity_indexes ......................... 487.07ms DONE
bookstack | 2015_12_05_145049_fulltext_weighting ........................... 0.06ms DONE
bookstack | 2015_12_07_195238_add_image_upload_types ...................... 77.75ms DONE
bookstack | 2015_12_09_195748_add_user_avatars ............................ 19.71ms DONE
bookstack | 2016_01_11_210908_add_external_auth_to_users .................. 47.16ms DONE
bookstack | 2016_02_25_184030_add_slug_to_revisions ....................... 83.43ms DONE
bookstack | 2016_02_27_120329_update_permissions_and_roles ............... 159.05ms DONE
bookstack | 2016_02_28_084200_add_entity_access_controls ................. 259.17ms DONE
bookstack | 2016_03_09_203143_add_page_revision_types ..................... 42.78ms DONE
bookstack | 2016_03_13_082138_add_page_drafts ............................. 43.51ms DONE
bookstack-db | mysql.event OK
bookstack-db | mysql.func OK
bookstack-db | mysql.global_priv OK
bookstack-db | mysql.gtid_slave_pos OK
bookstack-db | mysql.help_category OK
bookstack-db | mysql.help_keyword OK
bookstack-db | mysql.help_relation OK
bookstack-db | mysql.help_topic OK
bookstack-db | mysql.index_stats OK
bookstack-db | mysql.innodb_index_stats OK
bookstack-db | mysql.innodb_table_stats OK
bookstack-db | mysql.plugin OK
bookstack | 2016_03_25_123157_add_markdown_support ........................ 38.64ms DONE
bookstack | 2016_04_09_100730_add_view_permissions_to_roles ............... 43.26ms DONE
bookstack | 2016_04_20_192649_create_joint_permissions_table ............. 300.32ms DONE
bookstack-db | mysql.proc OK
bookstack-db | mysql.procs_priv OK
bookstack-db | mysql.proxies_priv OK
bookstack-db | mysql.roles_mapping OK
bookstack-db | mysql.servers OK
bookstack-db | mysql.table_stats OK
bookstack-db | mysql.tables_priv OK
bookstack-db | mysql.time_zone OK
bookstack-db | mysql.time_zone_leap_second OK
bookstack-db | mysql.time_zone_name OK
bookstack-db | mysql.time_zone_transition OK
bookstack-db | mysql.time_zone_transition_type OK
bookstack-db | mysql.transaction_registry OK
bookstack-db | Phase 2/8: Installing used storage engines... Skipped
bookstack-db | Phase 3/8: Running 'mysql_fix_privilege_tables'
bookstack-db | Phase 4/8: Fixing views... Skipped
bookstack-db | Phase 5/8: Fixing table and database names ... Skipped
bookstack-db | Phase 6/8: Checking and upgrading tables... Skipped
bookstack-db | Phase 7/8: uninstalling plugins
bookstack-db | Phase 8/8: Running 'FLUSH PRIVILEGES'
bookstack-db | OK
bookstack-db | 2026-09-13 09:54:59+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade
bookstack-db | 2026-09-13 09:54:59+02:00 [Note] [Entrypoint]: Stopping temporary server
bookstack-db | 2026-09-13 9:54:59 0 [Note] mariadbd (initiated by: unknown): Normal shutdown
bookstack-db | 2026-09-13 9:54:59 0 [Note] InnoDB: FTS optimize thread exiting.
bookstack-db | 2026-09-13 9:54:59 0 [Note] InnoDB: Starting shutdown...
bookstack-db | 2026-09-13 9:54:59 0 [Note] InnoDB: Removed temporary tablespace data file: "./ibtmp1"
bookstack-db | 2026-09-13 9:54:59 0 [Note] Shutdown completed; log sequence number 2025029; transaction id 2877
bookstack | 2016_05_06_185215_create_tags_table .......................... 113.02ms DONE
bookstack | 2016_07_07_181521_add_summary_to_page_revisions ............... 17.06ms DONE
bookstack | 2016_09_29_101449_remove_hidden_roles ......................... 76.98ms DONE
bookstack | 2016_10_09_142037_create_attachments_table .................... 67.83ms DONE
bookstack | 2017_01_21_163556_create_cache_table .......................... 42.59ms DONE
bookstack | 2017_01_21_163602_create_sessions_table ....................... 42.74ms DONE
bookstack | 2017_03_19_091553_create_search_index_table .................. 115.70ms DONE
bookstack | 2017_04_20_185112_add_revision_counts ......................... 62.91ms DONE
bookstack | 2017_07_02_152834_update_db_encoding_to_ut8mb4 ................. 0.07ms DONE
bookstack | 2017_08_01_130541_create_comments_table ....................... 86.66ms DONE
bookstack | 2017_08_29_102650_add_cover_image_display ..................... 16.83ms DONE
bookstack | 2018_07_15_173514_add_role_external_auth_id ................... 48.22ms DONE
bookstack | 2018_08_04_115700_create_bookshelves_table ................... 384.06ms DONE
bookstack | 2019_07_07_112515_add_template_support ........................ 48.61ms DONE
bookstack | 2019_08_17_140214_add_user_invites_table ...................... 62.14ms DONE
bookstack | 2019_12_29_120917_add_api_auth ................................ 88.92ms DONE
bookstack | 2020_08_04_111754_drop_joint_permissions_id .................. 105.72ms DONE
bookstack | 2020_08_04_131052_remove_role_name_field ...................... 16.66ms DONE
bookstack | 2020_09_19_094251_add_activity_indexes ........................ 49.34ms DONE
bookstack | 2020_09_27_210059_add_entity_soft_deletes ..................... 78.73ms DONE
bookstack | 2020_09_27_210528_create_deletions_table ...................... 92.71ms DONE
bookstack | 2020_11_07_232321_simplify_activities_table .................. 141.84ms DONE
bookstack | 2020_12_30_173528_add_owned_by_field_to_entities ............. 200.24ms DONE
bookstack | 2021_01_30_225441_add_settings_type_column .................... 21.09ms DONE
bookstack | 2021_03_08_215138_add_user_slug ............................... 49.08ms DONE
bookstack | 2021_05_15_173110_create_favourites_table ..................... 66.80ms DONE
bookstack-db | 2026-09-13 9:54:59 0 [Note] mariadbd: Shutdown complete
bookstack-db | 2026-09-13 09:54:59+02:00 [Note] [Entrypoint]: Temporary server stopped
bookstack-db | 2026-09-13 9:55:00 0 [Note] Starting MariaDB 12.3.3-MariaDB-ubu2404 source revision 83e909fc2a0dbc394b4b683fb3fa2d7dcf26cc5e server_uid bR6d3VLG8Cdw7s40Fw1l0qseMmo= as process 1
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Compressed tables use zlib 1.3
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Number of transaction pools: 1
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
bookstack-db | 2026-09-13 9:55:00 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
bookstack-db | create_uring failed: falling back to libaio
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Using Linux native AIO
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Completed initialization of buffer pool
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: End of log at LSN=2025029
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Opened 3 undo tablespaces
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
bookstack | 2021_06_30_173111_create_mfa_values_table ..................... 62.79ms DONE
bookstack | 2021_07_03_085038_add_mfa_enforced_to_roles_table ............. 17.85ms DONE
bookstack | 2021_08_28_161743_add_export_role_permission ................... 6.47ms DONE
bookstack | 2021_09_26_044614_add_activities_ip_column .................... 20.29ms DONE
bookstack | 2021_11_26_070438_add_index_for_user_ip ....................... 27.34ms DONE
bookstack | 2021_12_07_111343_create_webhooks_table ...................... 119.99ms DONE
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
bookstack | 2021_12_13_152024_create_jobs_table ........................... 38.72ms DONE
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: log sequence number 2025029; transaction id 2873
bookstack-db | 2026-09-13 9:55:00 0 [Note] Plugin 'FEEDBACK' is disabled.
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
bookstack-db | 2026-09-13 9:55:00 0 [Note] Plugin 'wsrep-provider' is disabled.
bookstack-db | 2026-09-13 9:55:00 0 [Note] InnoDB: Buffer pool(s) load completed at 260913 9:55:00
bookstack-db | 2026-09-13 9:55:00 0 [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
bookstack-db | 2026-09-13 9:55:00 0 [Note] Server socket created on IP: '::', port: '3306'.
bookstack-db | 2026-09-13 9:55:00 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
bookstack-db | 2026-09-13 9:55:00 0 [Note] mariadbd: ready for connections.
bookstack-db | Version: '12.3.3-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
bookstack | 2021_12_13_152120_create_failed_jobs_table .................... 37.85ms DONE
bookstack | 2022_01_03_154041_add_webhooks_timeout_error_columns .......... 70.20ms DONE
bookstack | 2022_04_17_101741_add_editor_change_field_and_permission ...... 27.06ms DONE
bookstack | 2022_04_25_140741_update_polymorphic_types .................... 16.24ms DONE
bookstack | 2022_07_16_170051_drop_joint_permission_type ................. 123.83ms DONE
bookstack | 2022_08_17_092941_create_references_table .................... 112.81ms DONE
bookstack | 2022_09_02_082910_fix_shelf_cover_image_types .................. 0.71ms DONE
bookstack | 2022_10_07_091406_flatten_entity_permissions_table ............ 90.68ms DONE
bookstack | 2022_10_08_104202_drop_entity_restricted_field ................ 93.44ms DONE
bookstack | 2023_01_24_104625_refactor_joint_permissions_storage ......... 142.21ms DONE
bookstack | 2023_01_28_141230_copy_color_settings_for_dark_mode ............ 1.09ms DONE
bookstack | 2023_02_20_093655_increase_attachments_path_length ............ 36.15ms DONE
bookstack | 2023_02_23_200227_add_updated_at_index_to_pages ............... 23.45ms DONE
bookstack | 2023_06_10_071823_remove_guest_user_secondary_roles ............ 1.97ms DONE
bookstack | 2023_06_25_181952_remove_bookshelf_create_entity_permissions ... 0.06ms DONE
bookstack | 2023_07_25_124945_add_receive_notifications_role_permissions ... 6.28ms DONE
bookstack | 2023_07_31_104430_create_watches_table ........................ 89.12ms DONE
bookstack | 2023_08_21_174248_increase_cache_size ......................... 30.81ms DONE
bookstack | 2023_12_02_104541_add_default_template_to_books ............... 20.41ms DONE
bookstack | 2023_12_17_140913_add_description_html_to_entities ............ 73.97ms DONE
bookstack | 2024_01_01_104542_add_default_template_to_chapters ............ 20.92ms DONE
bookstack | 2024_02_04_141358_add_views_updated_index ..................... 26.40ms DONE
bookstack | 2024_05_04_154409_rename_activity_relation_columns ............ 39.24ms DONE
bookstack | 2024_09_29_140340_ensure_editor_value_set ...................... 2.58ms DONE
bookstack | 2024_10_29_114420_add_import_role_permission ................... 5.16ms DONE
bookstack | 2024_11_02_160700_create_imports_table ........................ 36.28ms DONE
bookstack | 2024_11_27_171039_add_instance_id_setting ..................... 10.40ms DONE
bookstack | 2025_01_29_180933_create_sort_rules_table ..................... 11.84ms DONE
bookstack | 2025_02_05_150842_add_sort_rule_id_to_books ................... 20.57ms DONE
bookstack | 2025_04_18_215145_add_content_refs_and_archived_to_comments ... 66.35ms DONE
bookstack | 2025_09_02_111542_remove_unused_columns ....................... 69.84ms DONE
bookstack | 2025_09_15_132850_create_entities_table ...................... 281.26ms DONE
bookstack | 2025_09_15_134701_migrate_entity_data .......................... 9.73ms DONE
bookstack | 2025_09_15_134751_update_entity_relation_columns ............. 794.71ms DONE
bookstack | 2025_09_15_134813_drop_old_entity_tables ...................... 49.70ms DONE
bookstack | 2025_10_18_163331_clean_user_id_references ................... 457.39ms DONE
bookstack | 2025_10_22_134507_update_comments_relation_field_names ........ 40.71ms DONE
bookstack | 2025_11_23_161812_create_slug_history_table .................. 112.42ms DONE
bookstack | 2025_12_15_140219_create_mention_history_table ................ 67.72ms DONE
bookstack | 2025_12_19_103417_add_views_viewable_type_index ............... 22.39ms DONE
bookstack | 2026_04_19_141616_add_revision_view_all_permission ............. 4.69ms DONE
bookstack |
bookstack | [custom-init] No custom files found, skipping...
bookstack | [ls.io-init] done.
@@ -0,0 +1,14 @@
{
"bookstack": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
},
"bookstack-db": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,33 @@
{
"harness_version": 1,
"edge": "E3b",
"app": "bookstack",
"note": "AUTHORED step: engine half alone",
"from": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:11.6"
},
"to": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:12.3"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": {
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
},
"duration_s": 0.2,
"measured_at": "2026-09-13T07:55:14Z",
"evidence": "evidence/E3b",
"total_s": 64.0
}
@@ -0,0 +1,135 @@
[07:42:55] C3: deploying privatebin at FROM {'privatebin': 'privatebin/pdo:2.0.5'}
[07:43:01] FROM settled=True in 5.2s :: {"privatebin": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:43:01] privatebin: seeded paste id=95d75dc1cf5feaba
[07:43:01] privatebin: readback http=200 marker_present=True
[07:43:01] C1 (seed reads back BEFORE): True
[07:43:01] C3: swapping to TO {'privatebin': 'alpine:3.20'}
[07:43:02] TO up -d rc=0
[07:50:03] TO settled=False in 421.1s :: {"privatebin": {"status": "restarting", "health": "unhealthy", "restarts": 0, "exit": 0}}
[07:50:03] migration lines observed: 0
[07:52:03] app never answered on http://invalid:8080/ (last rc=6 code=000)
[07:52:03] RESULT (seed reads back AFTER): False
[07:52:03] C3: ABORT — putting the FROM images back
[07:52:09] privatebin: readback http=200 marker_present=True
[07:52:09] ABORT: app came back in 5.2s; data present=True
{
"harness_version": 1,
"edge": "C3",
"app": "privatebin",
"note": "NEGATIVE control: the TO image starts and exits immediately",
"from": {
"privatebin": "privatebin/pdo:2.0.5"
},
"to": {
"privatebin": "alpine:3.20"
},
"verdict": "failed",
"seed_read_before": true,
"seed_read_after": false,
"healthy_after": false,
"migration_observed": null,
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"duration_s": 421.1,
"measured_at": "2026-09-13T07:52:09Z",
"evidence": "evidence/C3",
"total_s": 553.9
}
[07:52:10] E3: deploying bookstack at FROM {'bookstack': 'lscr.io/linuxserver/bookstack:25.02.2', 'bookstack-db': 'mariadb:11.6'}
[07:52:56] FROM settled=True in 15.7s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:52:57] bookstack: artisan create-admin rc=0 :: Admin account with email "spike-e4414904@gate.invalid" successfully created!
[07:52:58] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-db3feb - Email: spike
[07:52:58] C1 (seed reads back BEFORE): True
[07:52:58] E3: swapping to TO {'bookstack': 'lscr.io/linuxserver/bookstack:26.05.2', 'bookstack-db': 'mariadb:12.3'}
[07:53:33] TO up -d rc=0
[07:53:43] TO settled=True in 10.5s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:53:43] engine state bookstack-db: 12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]
[07:53:43] migration lines observed: 6
[07:53:44] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-db3feb - Email: spike
[07:53:44] RESULT (seed reads back AFTER): True
[07:53:44] E3: ABORT — putting the FROM images back
[07:54:06] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-db3feb - Email: spike
[07:54:06] ABORT: app came back in 10.5s; data present=True
{
"harness_version": 1,
"edge": "E3",
"app": "bookstack",
"note": "catalog transition 0b73e5e: app AND engine together",
"from": {
"bookstack": "lscr.io/linuxserver/bookstack:25.02.2",
"bookstack-db": "mariadb:11.6"
},
"to": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:12.3"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "bookstack | [migrations] started",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": {
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
},
"duration_s": 10.5,
"measured_at": "2026-09-13T07:54:06Z",
"evidence": "evidence/E3",
"total_s": 116.3
}
[07:54:10] E3b: deploying bookstack at FROM {'bookstack': 'lscr.io/linuxserver/bookstack:26.05.2', 'bookstack-db': 'mariadb:11.6'}
[07:54:47] FROM settled=True in 20.9s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:54:48] bookstack: artisan create-admin rc=0 :: Admin account with email "spike-87a54b85@gate.invalid" successfully created!
[07:54:49] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-7b31eb - Email: spike
[07:54:49] C1 (seed reads back BEFORE): True
[07:54:49] E3b: swapping to TO {'bookstack': 'lscr.io/linuxserver/bookstack:26.05.2', 'bookstack-db': 'mariadb:12.3'}
[07:55:06] TO up -d rc=0
[07:55:06] TO settled=True in 0.2s :: {"bookstack": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "bookstack-db": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[07:55:06] engine state bookstack-db: 12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]
[07:55:06] migration lines observed: 6
[07:55:07] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-7b31eb - Email: spike
[07:55:07] RESULT (seed reads back AFTER): True
[07:55:07] E3b: ABORT — putting the FROM images back
[07:55:14] bookstack: readback of the seeded account found=True :: This will delete any configure multi-factor authentication methods for user: - ID: 3 - Name: spike-7b31eb - Email: spike
[07:55:14] ABORT: app came back in 0.2s; data present=True
{
"harness_version": 1,
"edge": "E3b",
"app": "bookstack",
"note": "AUTHORED step: engine half alone",
"from": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:11.6"
},
"to": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:12.3"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": {
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
},
"duration_s": 0.2,
"measured_at": "2026-09-13T07:55:14Z",
"evidence": "evidence/E3b",
"total_s": 64.0
}
rc=0
@@ -0,0 +1,92 @@
[
{
"harness_version": 1,
"edge": "C3",
"app": "privatebin",
"note": "NEGATIVE control: the TO image starts and exits immediately",
"from": {
"privatebin": "privatebin/pdo:2.0.5"
},
"to": {
"privatebin": "alpine:3.20"
},
"verdict": "failed",
"seed_read_before": true,
"seed_read_after": false,
"healthy_after": false,
"migration_observed": null,
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"duration_s": 421.1,
"measured_at": "2026-09-13T07:52:09Z",
"evidence": "evidence/C3",
"total_s": 553.9
},
{
"harness_version": 1,
"edge": "E3",
"app": "bookstack",
"note": "catalog transition 0b73e5e: app AND engine together",
"from": {
"bookstack": "lscr.io/linuxserver/bookstack:25.02.2",
"bookstack-db": "mariadb:11.6"
},
"to": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:12.3"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "bookstack | [migrations] started",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": {
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
},
"duration_s": 10.5,
"measured_at": "2026-09-13T07:54:06Z",
"evidence": "evidence/E3",
"total_s": 116.3
},
{
"harness_version": 1,
"edge": "E3b",
"app": "bookstack",
"note": "AUTHORED step: engine half alone",
"from": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:11.6"
},
"to": {
"bookstack": "lscr.io/linuxserver/bookstack:26.05.2",
"bookstack-db": "mariadb:12.3"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "bookstack-db | 2026-09-13 09:54:53+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": {
"bookstack-db": {
"image": "mariadb:12.3",
"probe": "datadir version | the engine's own upgrade verdict",
"answer": "12.3.3-MariaDB | This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]",
"probe_rc": 0
}
},
"duration_s": 0.2,
"measured_at": "2026-09-13T07:55:14Z",
"evidence": "evidence/E3b",
"total_s": 64.0
}
]
@@ -0,0 +1,24 @@
=== layer 1+2 BEFORE ===
Sun Sep 13 07:56:14 UTC 2026
local dir active 40453376 21035832 17330428 52.00%
local-lvm lvmthin active 56487936 19951538 36536397 35.32%
scratch-r459c dir active 983379700 9632244 923720844 0.98%
VMID Status Lock Name
9201 running demo-hp
9403 running r459-close
/dev/nvme0n1 983379700 9632244 923720844 2% /mnt/hdd_1
guest peak: /dev/loop0 59G 3.3G 53G 6% /
images: Images 2.212GB;Containers 0B;
0
=== fstrim ===
/var/lib/lxc/9403/rootfs/: 55.5 GiB (59547074560 bytes) trimmed
=== destroy ===
purging CT 9403 from related configurations..
storage scratch-r459c removed
template deleted
=== AFTER ===
local dir active 40453376 20908828 17457432 51.69%
local-lvm lvmthin active 56487936 19951538 36536397 35.32%
VMID Status Lock Name
9201 running demo-hp
/dev/nvme0n1 983379700 5774620 927578468 1% /mnt/hdd_1
@@ -0,0 +1,5 @@
=== LAYER 3 — the hub gained nothing 2026-09-13T08:11:07Z ===
host: demo-felhom-8363b5
host: demo-hp-bb76ea
customers: 0
(this run created no customer, no appliance and no host record — guest 9403 never enrolled; the bake guest 9100 lives inside the drill VM)
@@ -0,0 +1,22 @@
=== BASELINE 2026-09-13T07:57:15Z guest 9201, BEFORE the catalog push ===
--- live compose, bookstack-db environment block ---
bookstack-db:
image: mariadb:12.3
container_name: bookstack-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=${DB_PASSWORD}
- TZ=Europe/Budapest
volumes:
--- containers (ID, image, created, started) ---
/bookstack 26b555d71e1cf4ea60cd8fc72e34faa6d97ea8a71430d54dc5894cad5a03915d created=2026-09-13T04:21:01.357420686Z started=2026-09-13T04:21:07.210437498Z lscr.io/linuxserver/bookstack:26.05.2
/bookstack-db b02f7a091aebc962c7e15a1337ceb2b1c87c3f03ade7103d4bfddc2dcfc87ce2 created=2026-09-13T04:21:01.27514214Z started=2026-09-13T04:21:01.47728414Z mariadb:12.3
--- controller: last sync lines ---
2026/09/13 07:54:17 sync.go:416: [DEBUG] [sync] zipline/docker-compose.yml: hash match, skipped
2026/09/13 07:54:17 sync.go:416: [DEBUG] [sync] zipline/.felhom.yml: hash match, skipped
2026/09/13 07:54:17 sync.go:263: [INFO] [sync] Catalog sync complete
2026/09/13 07:54:17 sync.go:135: [INFO] [sync] Periodic sync: Sablonok naprakészek — nincs változás
--- git cache HEAD the box has ---
@@ -0,0 +1,33 @@
=== AFTER THE SYNC 2026-09-13T08:09:44Z — guest 9201, nothing touched by hand ===
--- the sync, in its own words (bookstack lines + the cycle summary) ---
2026/09/13 08:09:17 sync.go:396: [INFO] [sync] Updated bookstack/docker-compose.yml
2026/09/13 08:09:17 sync.go:409: [DEBUG] [sync] bookstack: stored definition refreshed with the delivered fix
2026/09/13 08:09:17 sync.go:541: [DEBUG] [sync] bookstack/docker-compose.yml: src=d45643865f5cf66e, dst=d45643865f5cf66e (changed)
2026/09/13 08:09:17 sync.go:244: [DEBUG] [sync] Post-sync hook: triggering missing field injection for 4 stack(s): [bookstack kimai nextcloud romm]
2026/09/13 08:09:17 sync.go:263: [INFO] [sync] Catalog sync complete
2026/09/13 08:09:17 sync.go:135: [INFO] [sync] Periodic sync: Sablonok frissítve — frissítve: bookstack, kimai, nextcloud, romm
--- live compose, bookstack-db environment block ---
bookstack-db:
image: mariadb:12.3
container_name: bookstack-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=${DB_PASSWORD}
- TZ=Europe/Budapest
# MARIADB_AUTO_UPGRADE: on a MAJOR engine move the engine converts its own datadir (~7 s on a
# small DB, backs its system tables up first). Operator ruling 2026-09-13 on
# felhom.eu/documentation/audits/SPIKE-r459-mariadb-upgrade-2026-09-06.md. Inert until a
# major moves — and none may, until Slice 4 (R-448) ships: see CLAUDE.md, engine-major rule.
- MARIADB_AUTO_UPGRADE=1
volumes:
--- containers: same IDs and StartedAt as the baseline = NOT recreated by the sync ---
/bookstack 26b555d71e1cf4ea60cd8fc72e34faa6d97ea8a71430d54dc5894cad5a03915d started=2026-09-13T04:21:07.210437498Z restarts=0
/bookstack-db b02f7a091aebc962c7e15a1337ceb2b1c87c3f03ade7103d4bfddc2dcfc87ce2 started=2026-09-13T04:21:01.47728414Z restarts=0
--- docker ps ---
bookstack|lscr.io/linuxserver/bookstack:26.05.2|Up 4 hours (healthy)
bookstack-db|mariadb:12.3|Up 4 hours (healthy)
--- the applied definition also carries it (fixes flow INTO the pin) ---
2
@@ -0,0 +1,31 @@
=== THE ONE DELIBERATE RESTART — POST /api/stacks/bookstack/restart 2026-09-13T08:10:32Z ===
login http=302
session cookie: 79 chars
csrf token: 64 chars (must be 64)
{"ok":true,"message":"Stack bookstack restart completed"}
restart http=200
--- waiting for both containers healthy ---
healthy after 8s
--- containers after: NEW ids and StartedAt = the restart recreated them (compose up -d) ---
/bookstack 26b555d71e1cf4ea60cd8fc72e34faa6d97ea8a71430d54dc5894cad5a03915d started=2026-09-13T04:21:07.210437498Z
/bookstack-db 16205e01bdbe56e5849da0c06d5d50e1eb5a70e2e92efaf59d0e09758272d573 started=2026-09-13T08:10:33.353161195Z
--- the ENGINE log after the restart, verbatim (last start) ---
2026-09-13 10:10:33+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.2+maria~ubu2404 started.
2026-09-13 10:10:33+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
2026-09-13 10:10:33+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2026-09-13 10:10:33+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.2+maria~ubu2404 started.
2026-09-13 10:10:34+02:00 [Note] [Entrypoint]: MariaDB upgrade not required
2026-09-13 10:10:35 0 [Note] mariadbd: ready for connections.
Version: '12.3.2-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
--- the engine asked directly (R-464: not the log) ---
12.3.2-MariaDB
This installation of MariaDB is already upgraded to 12.3.2-MariaDB.
There is no need to run mariadb-upgrade again.
[exit=1]
--- the env is IN the running container ---
MARIADB_AUTO_UPGRADE=1
--- the app serves ---
GET /login http=200
bookstack-db|mariadb:12.3|Up 11 seconds (healthy)
bookstack|lscr.io/linuxserver/bookstack:26.05.2|Up 4 hours (healthy)
+2
View File
@@ -256,3 +256,5 @@ Compressed here to title, shipping version, evidence, and the sentences that sta
| **R-408** | **`RestoreOffboxScratch` took no single-writer flag while a comment asserted every off-site operation did.** Shipped in controller **v0.232.0**. **Reasoning kept:** *the real deliverable is the WALK, not the acquire* — the sentence was false for months and nothing checked it, the ninth instance of this project's most-repeated class; *it is an AST pass and not `strings.Contains`, because a commented-out call still contains the string*; *adding a line to `offsiteExempt` is a deliberate act and belongs in the commit that adds it*; *the R-87 proof's exemption is kept HONEST by a second test that fails if that path ever gains `unlockStale`, routes through `resticStep`, or loses `--no-lock`*. | **CLOSED — SHIPPED** (controller v0.232.0, 2026-09-01) | full text: `git show 22e1c95:documentation/backlog/OPEN-ITEMS.md` |
| **R-411** | **A background job deleted the lock of a live customer restore and logged it as a crash that did not happen.** Shipped in controller **v0.232.0**. Evidence: `audits/DRILL-soak-2026-08-31/phase1-lock-collision/`, `audits/R411-R414-2026-09-01/`. **Reasoning kept:** *`restic stats` TAKES a repository lock* — the fact nobody had, and the one that made the chain reachable; *`restic check` takes one too, `restic snapshots` and `restic list` do not*; *the fix was wider than the row — FOUR entry points were unflagged, three of them found by R-408's walk rather than by the report*; *the escalation in `resticStep` was NOT removed — real stale locks exist and it clears them; the defect was that a sibling could be live*. | **CLOSED — SHIPPED + PROVEN-LIVE** (controller v0.232.0, 2026-09-01) | full text: `git show 22e1c95:documentation/backlog/OPEN-ITEMS.md` |
| **R-403** | **A poorer copy deleted a richer one: an EMPTY recovery unit on the primary drive was mirrored over a COMPLETE copy on the second drive, with `--delete`.** Shipped in controller **v0.230.0**. **MEASURED before it was fixed** — on the shipped v0.229.0, on demo-hp: 120 082 104 B (4 database dumps + 3 volume tars) -> 7 036 B (none of either) in one nightly run, recorded as a success. Evidence: `audits/DRILL-r403-tier2-delete-2026-08-31/`. **Reasoning kept:** *hollowness is a MANIFEST question, never a size question* - a unit with a fat compose capture and no dumps is the dangerous shape and a 360-byte unit for a tiny app is healthy; absent or unparseable manifest counts as hollow, fail closed. *The guard fences ONE shape and not shrinking* - `07` §8 row 5's derived-copy rebuild is a DESIGN DECISION, `--delete` stays, the data legs are untouched, and only source-hollow-over-destination-complete is refused (§8.2 records the exception beside the rule so nobody 'fixes' it back). *The rehydrate happens INSIDE the restore* - the hollow manifest was written two seconds later by the 5-minute capture job, so any follow-up job races it; and *the capture is deliberately NOT guarded*, because a capture describing an empty drive as empty is correct and guarding it would make the manifest lie. *A warning that fires on everything costs the same as the comforting lie it replaces* - the first draft flagged 'package older than the run', which is true of every healthy app, and four healthy apps on the box would have been warned. | **CLOSED 2026-08-31 - controller v0.230.0, PROVEN-LIVE both ways** (the loss reproduced on v0.229.0, then the same state preserved on v0.230.0 with all 7 files sha256-identical) | full text: `git show 66156c619fd2:documentation/backlog/OPEN-ITEMS.md` |
| **R-459** | **The skipped MariaDB conversion is STABLE but never self-resolving; converting costs 7 s and keeps the abort — RULED YES and shipped 2026-09-13: `MARIADB_AUTO_UPGRADE=1` on `bookstack-db`, `kimai-db`, `nextcloud-db`, `romm-db`** (catalog `eec1228`/`bd32830`/`3525e35`; no image moved, `catalog_since` untouched). Measured `audits/SPIKE-r459-mariadb-upgrade-2026-09-06.md`; proven `audits/r459-close-2026-09-13/` — harness E3/E3b `proven` with `engine_state_after` = `already upgraded to 12.3.3-MariaDB [exit=1]`, `skipped due to $MARIADB_AUTO_UPGRADE` 0 lines, C3 still `failed`; landed on demo-hp via the real 15-min cycle with both container IDs unchanged, one deliberate restart → `MariaDB upgrade not required`, `/login` 200. **Reasoning kept:** *ask the engine, not the log* — the entrypoint prints `MariaDB upgrade not required` on an unsupported downgrade too (R-464); `mariadb-upgrade --check-if-upgrade-is-needed` exit 0 = needed, 1 = not. *Not established, unchanged:* whether any MariaDB feature misbehaves on an UNCONVERTED datadir. *The precaution that keeps the setting inert until Slice 4:* the engine-major rule + gate, removal tracked as R-469. | **CLOSED 2026-09-13 — shipped in the catalog, PROVEN by harness and live** | full text: `git show ae59c31:documentation/backlog/OPEN-ITEMS.md` |
| **R-467** | **Controller v0.236.0 owed a golden — PAID 2026-09-13:** golden `0.236.0` baked (`GOLDEN_SHA256=58a3cc24…958bf`, 654 115 664 B), round-tripped from the DOWNLOADED bytes, `./etc/felhom-controller-image` says `felhom-controller:0.236.0`, hub dropdown agreed, three-field vouch re-read (`0.236.0` / `agent 0.130.0` / `min_agent 0.129.0`, not the R-216 shape), floor raised 0.232.0 → 0.236.0. Evidence `documentation/tests/golden-0.236.0-2026-09-13/`. **Reasoning kept:** *this bake carried FOUR unbaked releases and is the LAST per-release bake* — goldens are weekly and before any install from today (R-468); *the MinAgent line was missing from four headers* (R-470). | **CLOSED 2026-09-13 — baked, vouched, floor raised** | full text: `git show ae59c31:documentation/backlog/OPEN-ITEMS.md` |
+7 -3
View File
@@ -217,7 +217,7 @@ the fault was real. Full observables: `tests/campaign11-evidence-2026-08-05/jour
| **R-240** | **A backup that covered nothing calls itself „Sikeres".** On a configured box with no app selected for off-site backup, a run reports status `ok` with the warning „Sikeres — nincs mentésre jelölt alkalmazás" — *successful* immediately beside *nothing is selected*. Measured as T4 on the final walk, 2026-08-07; flagged once before (2026-08-06) and deliberately not touched then, because the task that noticed it forbade changing that path. **It is the same rhetorical shape the project has spent a fortnight removing** — R-203's *a warning beside a success is read as a success*, R-234's *„✓ Rendben" over an app that was skipped*, R-225's *unknown rendered as zero* — one notch weaker each time, and this is the weakest and last of them. The state itself is honest and must stay `ok`: an unconfigured box reporting `incomplete` forever is its own defect, pinned by a test. **The defect is the word „Sikeres", not the verdict.** Wording such as „Nincs mentésre jelölt alkalmazás — ez a futás semmit nem mentett" says the same thing without congratulating the customer on it. | **READY** — owner Viktor |
| **R-242** | **A controller release that changes customer-visible behaviour is not delivered until a golden carries it — and nothing enforces that.** R-239 is the symptom; this is the mechanism, recorded **2026-08-07** and deliberately **NOT built** (the task that found it scoped it as a record-only item). **Two releases went out without a golden and the gap was invisible until a walk measured it from the customer's side**: v0.204.0 (R-237) and v0.205.0 (R-234) were written, tested, pushed, and CHANGELOG'd, and every one of those steps passed while a machine installed that night received neither. The register said CLOSED; the fleet said otherwise. **Nothing in the release path knows a golden exists.** The version bump, the image push, the CHANGELOG entry and the register closure are all repo-local; the manifest's `golden_version` is edited by a separate operator act, in a different repo, with no link back. **Proposed shapes, cheapest first — the choice is the operator's and is not taken here.** (a) **A release-path checklist step** — one line in the controller's end-of-session checklist: *a release that changes customer-visible behaviour is not finished until a golden carries it or a register row says why not.* Costs nothing, catches nothing mechanically. (b) **A gate in `repo_gates.py`** comparing the manifest's `golden_version` against the newest released controller and FAILING (or warning) past a tolerance of one minor. Mechanical, runs on every push, and would have fired the morning after v0.204.0. (c) **A hub-side checker** — the hub already knows every box's running controller version from `/hosts` and the vouched golden from the manifest; a periodic comparison against the newest published image would catch drift the repo cannot see, including a vouch that was made and then rolled back. **Earliest catch: (b).** It fires on the push that creates the gap, before any box is installed, and it needs no live fleet. **(c) catches strictly more but only after boxes exist.** (a) is worth doing regardless because it is free. **Not built. No gate was written this session.** **⚠ IT RECURRED WITHIN A DAY, WHICH IS THE ARGUMENT FOR BUILDING IT.** Controller **v0.206.0** shipped the R-241 fixes on 2026-08-07 while the vouched golden still carried **0.205.0** — so a machine installed on the morning of 2026-08-08 would have received neither. Third occurrence of the shape in three days (R-111/R-115/R-120 are the older family). **✅ SHAPE (b) BUILT 2026-08-08 — `scripts/golden_currency_gate.py`, registered in `repo_gates.py` as gate 7.** **It was shown FAILING against that exact state before anything was baked**, which is its red-proof and the reason its own introducing push needed `--no-verify` (stated in the session report rather than worked around): `newest released controller : 0.206.0 / newest golden baked : 0.205.0 → CONVICTED`. **IT IS `--fast`, AND THAT FORCED ITS DESIGN:** both `.githooks/pre-push` AND CI run `repo_gates.py --fast`, so a non-fast gate would run in NEITHER — the R-29 census failure this runner exists to end. **THEREFORE IT CHECKS THE BAKE, NOT THE VOUCH**, because the vouched version lives only in the hub's `hub_settings` with no copy in git, and putting a copy there would create a second source of truth that can drift — a green gate over a false claim being the worst outcome available. **A bake without a vouch still passes: that half is NOT closed and stays on this row.** It also compares versions rather than behaviour, so a release changing nothing customer-visible trips it too — accepted deliberately, because judging that by hand is what failed three times and the cost of a false trip is one bake; a waiver belongs here, never in a habit of bypassing. **VOUCHED 2026-08-08 with the operator's approval** — golden `0.206.0` / sha `c85230b4…108e`; `agent_version` and `min_agent` both stayed `0.127.0`, and `wrapper_sha256` was carried through explicitly because the handler clears it when omitted. **The gate was CONVICTED before the bake and OK after it** — red→green on the same command, which is its proof that it measures something real. **⚠ THE GATE FIRED FOR REAL, 2026-08-08 — and it was right.** Controller **v0.207.0** (R-249/R-252/R-253) is released, tested and pushed, and **no golden carries it** — the newest bake is 0.206.0 — so `golden_currency_gate.py` FAILED, saying exactly the true thing: *a machine installed right now would receive v0.206.0*. **The `felhom.eu` push therefore used `git push --no-verify`, declared here, in the commit message and in the session report.** A bypass and NOT a waiver, deliberately: the gate offers a waiver only for a release that *deliberately needs no golden*, and this one needs one. **Owed: bake golden 0.207.0 and vouch it** (`RUNBOOK-manual-build.md` §4.1; the vouch is a three-field change). **This row's own remaining half is unchanged — nothing gates the VOUCH itself.** **✅ THE OWED BAKE IS DONE, SAME DAY — Line truncated
| **R-242** | **A controller release that changes customer-visible behaviour is not delivered until a golden carries it — and nothing enforces that.** R-239 is the symptom; this is the mechanism, recorded **2026-08-07** and deliberately **NOT built** (the task that found it scoped it as a record-only item). **Two releases went out without a golden and the gap was invisible until a walk measured it from the customer's side**: v0.204.0 (R-237) and v0.205.0 (R-234) were written, tested, pushed, and CHANGELOG'd, and every one of those steps passed while a machine installed that night received neither. The register said CLOSED; the fleet said otherwise. **Nothing in the release path knows a golden exists.** The version bump, the image push, the CHANGELOG entry and the register closure are all repo-local; the manifest's `golden_version` is edited by a separate operator act, in a different repo, with no link back. **Proposed shapes, cheapest first — the choice is the operator's and is not taken here.** (a) **A release-path checklist step** — one line in the controller's end-of-session checklist: *a release that changes customer-visible behaviour is not finished until a golden carries it or a register row says why not.* Costs nothing, catches nothing mechanically. (b) **A gate in `repo_gates.py`** comparing the manifest's `golden_version` against the newest released controller and FAILING (or warning) past a tolerance of one minor. Mechanical, runs on every push, and would have fired the morning after v0.204.0. (c) **A hub-side checker** — the hub already knows every box's running controller version from `/hosts` and the vouched golden from the manifest; a periodic comparison against the newest published image would catch drift the repo cannot see, including a vouch that was made and then rolled back. **Earliest catch: (b).** It fires on the push that creates the gap, before any box is installed, and it needs no live fleet. **(c) catches strictly more but only after boxes exist.** (a) is worth doing regardless because it is free. **Not built. No gate was written this session.** **⚠ IT RECURRED WITHIN A DAY, WHICH IS THE ARGUMENT FOR BUILDING IT.** Controller **v0.206.0** shipped the R-241 fixes on 2026-08-07 while the vouched golden still carried **0.205.0** — so a machine installed on the morning of 2026-08-08 would have received neither. Third occurrence of the shape in three days (R-111/R-115/R-120 are the older family). **✅ SHAPE (b) BUILT 2026-08-08 — `scripts/golden_currency_gate.py`, registered in `repo_gates.py` as gate 7.** **It was shown FAILING against that exact state before anything was baked**, which is its red-proof and the reason its own introducing push needed `--no-verify` (stated in the session report rather than worked around): `newest released controller : 0.206.0 / newest golden baked : 0.205.0 → CONVICTED`. **IT IS `--fast`, AND THAT FORCED ITS DESIGN:** both `.githooks/pre-push` AND CI run `repo_gates.py --fast`, so a non-fast gate would run in NEITHER — the R-29 census failure this runner exists to end. **THEREFORE IT CHECKS THE BAKE, NOT THE VOUCH**, because the vouched version lives only in the hub's `hub_settings` with no copy in git, and putting a copy there would create a second source of truth that can drift — a green gate over a false claim being the worst outcome available. **A bake without a vouch still passes: that half is NOT closed and stays on this row.** It also compares versions rather than behaviour, so a release changing nothing customer-visible trips it too — accepted deliberately, because judging that by hand is what failed three times and the cost of a false trip is one bake; a waiver belongs here, never in a habit of bypassing. **VOUCHED 2026-08-08 with the operator's approval** — golden `0.206.0` / sha `c85230b4…108e`; `agent_version` and `min_agent` both stayed `0.127.0`, and `wrapper_sha256` was carried through explicitly because the handler clears it when omitted. **The gate was CONVICTED before the bake and OK after it** — red→green on the same command, which is its proof that it measures something real. **⚠ THE GATE FIRED FOR REAL, 2026-08-08 — and it was right.** Controller **v0.207.0** (R-249/R-252/R-253) is released, tested and pushed, and **no golden carries it** — the newest bake is 0.206.0 — so `golden_currency_gate.py` FAILED, saying exactly the true thing: *a machine installed right now would receive v0.206.0*. **The `felhom.eu` push therefore used `git push --no-verify`, declared here, in the commit message and in the session report.** A bypass and NOT a waiver, deliberately: the gate offers a waiver only for a release that *deliberately needs no golden*, and this one needs one. **Owed: bake golden 0.207.0 and vouch it** (`RUNBOOK-manual-build.md` §4.1; the vouch is a three-field change). **This row's own remaining half is unchanged — nothing gates the VOUCH itself.** **✅ THE OWED BAKE IS DONE, SAME DAY — Line truncated
| **R-243** | **A box in the R-241 state silently stops backing up off-site, and NO ALARM OF ANY KIND FIRES.** Found by the R-241 spike (2026-08-07) as a by-product; **not part of the walk's finding and not previously filed.** The R-241 state is self-locking in a second, worse way than the recovery-journey dead end: `escrow_state` is stuck `pending` forever (the auto-confirm flips only on a hash match, and the hash cannot match a key the box minted itself), and `runOffboxBackup` returns at the escrow gate (`offbox.go:743`) before touching anything. **So off-site backups never run again — and the hub never notices.** All three signals that could catch it are excluded, each for its own individually-correct reason, verified in the hub this session: `offsite_stale` — `isStale` (`monitor/offsite.go:135`) returns false unless `EscrowState == "escrowed"`, and its own comment reads *"Pending/disabled = normal onboarding, never stale"*, so the box is classified as **still being set up, forever**; `offsite_delivery_stuck` — `monitor/offsite_delivery.go:91` skips the `applied` shape, and delivery genuinely IS applied (the credential was consumed and the target is in every report); `backup_failed` — never fires, because nothing fails: the run returns `nil` before it starts. **Three correct exclusions leaving one state unobserved.** This is the same class as the workspace `CLAUDE.md` "presence is not success" rule, one level up: **the absence of a failure is being read as the presence of a working tier.** **Partly subsumed by R-241's fix** — a box that recovers leaves this state — but **not for a box that does not**, and the alarm gap is what makes "does not" survivable indefinitely. **Not fixed; no code written.** **⚠ UPDATED 2026-08-07 (v0.206.0) — the STATE this row describes can no longer be entered, but the ALARM GAP is untouched and the row stays open.** R-241's mint guard means a box no longer mints a key over a sealed package, so it no longer arrives in the "escrow stuck pending against a self-minted key" state by itself. **What replaces it is a state that is VISIBLE rather than silent:** the box declares `offsite.state=awaiting_recovery_key` and the customer is offered the recovery screen. **But the hub still raises nothing for it**, and for the same three reasons: `isStale` needs `escrowed`, the delivery checker skips the `applied` shape, and `backup_failed` needs a run that never happens. **So a box whose customer never acts still stops backing up off-site with no operator signal** — the difference is that the customer can now see it and act, where before nobody could. **The remaining work is an operator-side signal for a box held in `awaiting_recovery_key` past some age**, and it is deliberately not bundled into R-241's fix. **⚠ MEASURED ON A REBUILD, 2026-08-07 (fifth walk) — the gap is real for the state this row describes, and NOT for the state a rebuild produces.** 88 seconds after the walk5 guest was destroyed and rebuilt, the hub emitted `offsite_delivery_stuck` (**warning**) and wrote an **operator-channel** `notification_log` row recording `offsite_credential_restaged` / status **REFUSED** with an accurate reason — *"the credential was applied and worked; the target was lost afterwards … a guest rebuild does, R-193"*. So on the **regressed-apply** shape the operator IS told, promptly and correctly, and this row's *"skips the applied shape"* does not apply. The gap stands for a box that reaches the held state **without** a prior working tier in its report history. **Recorded so the row is not read wider than it measures.** | **READY** — owner Viktor |
| **R-244** | **The customer DELETE cascade leaves `app_log_issues` behind, and it is systematic across every venue ever torn down.** Found **2026-08-07** while verifying the `finalwalk` teardown with a **full census** (every table, every column) rather than a per-table query. After a cascade that logged `COMPLETE … full teardown`, **61 rows still matched `finalwalk`**. Four of the five sources are **deliberate and correct** — the cascade's own header states *"Provenance/events are NEVER wiped — audit outlives every tier"*: `events` 16, `notification_log` 14, `host_deletions` 1, `customer_resets` 1. **The fifth is a gap:** `app_log_issues` 29 rows, which the residue purge does not touch (its logged leg covers `reports`/`app_telemetry`/`app_log_tails`/`log_tail_requests`/`notif_prefs`/`selfbind_tokens`/`appliance_registrations` — not this table). **It is not a `finalwalk` quirk:** rows still reference **`c11` 40, `rewalk` 20, `part4` 24** — all three torn down 2026-08-06, whose ledger recorded *"0 occurrences"*. **That prior claim was measured with a narrower query and does not survive a full census; the correction is recorded rather than the measurement quietly redone.** **Why it was probably never written, established rather than assumed:** the table is a **fleet-wide aggregate** keyed on `app_name`+`fingerprint` with an `affected_customers` JSON list — of the 29 `finalwalk` rows, **12 reference only `finalwalk`** (orphans, safely deletable) and **17 are shared with LIVE customers** (`demo-felhom`, `peti-felhom`, …) and **must not be deleted, only de-referenced.** A naive `DELETE … WHERE customer LIKE` would destroy a live customer's issue history — which is very likely why the leg does not exist, and is the reason this is not a one-line fix. **Severity is LOW and stated plainly: no secret material is involved** — app name, fingerprint, message text, counts, timestamps. What survives is a deleted customer's *identifier* inside an aggregate row. **Proposed shape:** a residue leg that (a) removes the customer id from `affected_customers`/`context_customer`, and (b) deletes rows whose `affected_customers` becomes empty; plus a one-off sweep for the four already-torn-down venues. **The general lesson is the reusable part:** *a per-table absence query is not a census.* The teardown verification is now a full-schema sweep, and that is what found this. **Not fixed** — a cascade change needs its own red-proof and this session was scoped as a spike plus two operations. Evidence: `tests/teardown-finalwalk-2026-08-07.md`. **⚠ STILL OWED, AND NOW MEASURED RATHER THAN ESTIMATED (2026-08-08 census, read-only, no truncation).** `app_log_issues` holds **1309 rows**; **71 reference a torn-down venue** (`finalwalk`, `c11`, `rewalk`, `part4`); of those **44 are ORPHANS** — they name only torn-down customers and are safely deletable — and **27 are SHARED with a live customer** (`demo-felhom`, `peti-felhom`, …) and **must be de-referenced, never deleted**. 1238 rows are untouched. **The 27 are exactly why the leg was never written**, and why a `DELETE … WHERE customer LIKE` would destroy a live customer's issue history. **What it needs, precisely:** a cascade leg that (a) removes the customer id from `affected_customers` / `context_customer`, and (b) deletes only rows whose `affected_customers` becomes empty; plus a one-off sweep for the four venues already gone. **Why it was NOT done on 2026-08-08:** the fix is hub code, and that session's scope forbade a hub version bump; a hand-run SQL mutation over 71 rows — 27 of them needing surgical de-referencing — with no tested code path and no red-proof is precisely the shape that goes wrong on a live database. **It accumulates one venue at a time, so the next walk adds to it**; the numbers above mean the next session starts from data rather than a guess. **⚠ IT GREW AGAIN, AS PREDICTED — walk5 teardown, 2026-08-08.** The fifth walk's venue was torn down with a full-schema census taken **before and after**: **168 rows → 67**. Of the 67, **37 are by design** (`events` 21, `notification_log` 14, `host_deletions` 1, `customer_resets` 1) and **30 are `app_log_issues`** — this row's gap, and the count was **predicted in the pre-run enumeration rather than discovered afterwards**, which is the difference from the ledger that once recorded *"0 occurrences"* from a narrower query. **The running total across torn-down venues therefore rises from 71 to ~101 rows** (`finalwalk`, `c11`, `rewalk`, `part4`, now `walk5`) — the shared-with-a-live-customer subset must still be **de-referenced, never deleted**. **It accumulates one venue at a time and it did so again.** Evidence: `tests/walk5-r201-2026-08-07/teardown-walk5-2026-08-08.md`. | **READY** — owner Viktor |
@@ -690,7 +690,6 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-456** | **[P3-LOW] A partly-dead stack is not a boot orphan, and that is written down nowhere.** MEASURED 2026-09-02 on demo-hp while validating v0.233.0: `docker rm -f bookstack` (leaving `bookstack-db` running) then a controller restart produced `Boot reconciliation: 1 boot-orphaned app(s) found: [bentopdf]` — **bookstack was NOT selected**, although the app container was gone and `desired_state: running` was recorded. Removing `bookstack-db` as well made the whole stack orphaned and the very next pass repaired it in 6.3 s. **So `bootrecon.isBootOrphan` requires the stack as a WHOLE to be down; one live member is enough to make it invisible to the reconciler.** **NOT called a defect, and the reason is part of the row:** `StateDegraded` IS in `IsDownState`, and the crash-loop/dead-app alarm path (`classifyRunStates`) does count a degraded stack as down — so the customer IS told; it is the automatic REPAIR that does not fire, and there may be a good reason (repairing half a stack while its DB is live is not obviously safe). **What is certain is that nobody has written the rule down**, so the next session re-derives it the same way this one did — by watching a reconciliation not happen, which is an absent observable and the weakest possible evidence. Either state the rule in `02-controller-module-map.md` with a test pinning it, or change it. Owner: **CC.** `tests/VALIDATION-update-slice12-2026-09-02.md` §2.2 | **READY — rank P3-LOW; owner: CC** |
| **R-457** | **[P3-LOW] A test that hardcodes a date AND asserts an age derived from it is green on the day it is written and red the next morning — one instance PROVEN, six candidate files named.** MEASURED 2026-09-03: `TestGroupD_BadgeRendersOnBothSurfaces` (shipped the previous day in v0.233.0) pinned a fixture `catalog_since: "2026-07-18"` and asserted the rendered string `"Frissítés elérhető — 46 napja"`. **The pure badge tests inject a clock; the RENDER test does not and cannot** — it goes through the production templates, which call the funcmap entry `updateBadge`, which reads `time.Now()`. The suite was green on 2026-09-02 and **FAILED on 2026-09-03** with *"the behind badge is missing"* on both surfaces, because the true answer had become 47. **Fixed by DERIVING the fixture** — `catalog_since` is computed as *today minus 46 days*, so the test asserts the real number through the real clock and cannot rot. **THE CLASS, which is why this is a row and not just a fix:** a clock-reading test that also carries a date LITERAL is a bomb with a fuse of unknown length, and the suite being green is not evidence it is defused — it is evidence the fuse has not burned down yet. **NAMED AS UNCHECKED CANDIDATES, NOT ACCUSED** — six other test files contain both a `20xx-xx-xx` literal and `time.Now()`: `internal/backup/offbox_test.go`, `internal/web/handler_export_upload_test.go`, `internal/web/r103_tier2_action_test.go`, `internal/web/dashboard_backup_card_test.go`, `internal/web/async_restore_test.go`, `internal/stacks/installed_test.go`. Mixing the two is not itself a defect — it is one only where a literal feeds an assertion evaluated against the real clock — so each needs reading, which is a sweep and not this session. **The instrument that would end the class:** run the suite once under a faked future date in CI and see what turns red. Owner: **CC.** `felhom-controller` v0.234.0 CHANGELOG | **READY — rank P3-LOW; owner: CC** |
| **R-458** | **[P3-LOW] `.felhom.yml` keeps flowing to an app whose compose file is FROZEN, so a frozen app can receive a health check written for a version it is not running.** The v0.235.0 render freezes `docker-compose.yml` for a pinned app once the catalog moves past its version, but copies `.felhom.yml` **verbatim in every case** (`Syncer.copyTemplates`). **The asymmetry is deliberate and both directions were considered:** `.felhom.yml` carries no image, and it carries `catalog_since` — the single input the update badge uses to say *„Frissítés elérhető — N napja"* — so freezing it would silently withhold the one number that tells a customer they are behind, i.e. it would break slice 2 to protect slice 3. **What it costs:** the file also carries the controller-side `healthcheck:` block and resource hints, so a template updated for a newer version can hand a frozen app a probe written for software it is not running. **THE FAILURE DIRECTION IS A FALSE ALARM, NEVER DATA LOSS** — the app keeps running; at worst it renders as degraded and, if it persisted, could reach the dead-app alarm path. That is the same class as R-330's false e-mails, which is why this is a row and not a footnote. **Not fixed now, and the reason is that the cheap fix is wrong:** freezing the whole file breaks the badge, and freezing only the `healthcheck:` key means the syncer would have to parse and re-assemble a customer-facing metadata file — new surface on the one path that touches every app on every box every 15 minutes. **What would settle it:** whether any catalog `healthcheck:` has ever been changed in the same commit as an `image:` line (measurable from the catalog's own history, no box needed). If the answer is "never", the exposure is theoretical and the row can be closed by measurement instead of by code. Owner: **CC.** `architecture/09-update-architecture.md` §5.4, §8.5 | **READY — rank P3-LOW; owner: CC** |
| **R-459** | **[P3-LOW, was P2] MEASURED 2026-09-06 — the skipped MariaDB conversion is STABLE but never self-resolving, and fixing it costs 7 SECONDS and does NOT cost the ability to go back.** The consequence R-459 deliberately left unmeasured is now measured: `audits/SPIKE-r459-mariadb-upgrade-2026-09-06.md`. **(1) It does not degrade: 5 of 5 restarts of 12.3 on the 11.6 datadir, readback passed every time, `mariadb_upgrade_info` unchanged at `11.6.2-MariaDB`, and the entrypoint's line never escalated past `[Note]`.** **(2) It never heals either** — the engine answers `Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!` on every start, and will forever. **(3) THE TRADE THIS ROW WAS EXPECTED TO PRODUCE DOES NOT EXIST.** The fear was that converting properly would end the ability to abort. Measured: with `MARIADB_AUTO_UPGRADE=1` the conversion **succeeds** across the multi-major jump (so it is not a stepping problem either), takes **7 s**, takes its own system-database backup first (`system_mysql_backup_11.6.2-MariaDB.sql.zst`), and **putting 11.6 back afterwards still starts and serves the data**. **So the choice is no longer a trade; it is a cheap correction.** **WHAT REMAINS OPEN IS THE DECISION, NOT THE MEASUREMENT:** setting `MARIADB_AUTO_UPGRADE` changes how **four** apps upgrade — bookstack (the only one that has moved a major), kimai, nextcloud, romm — and R-459's original owner line reserves a fleet-wide env change for the operator. **Nothing was committed to any template**; the comparison arm used a scratch copy inside a throwaway guest. **NOT ESTABLISHED, and not smuggled in: whether any specific MariaDB FEATURE misbehaves on unconverted system tables.** This run exercised BookStack's normal read/write path only, over minutes, with one seeded record. **Rank dropped P2 → P3** because the failure mode is now bounded by measurement rather than unknown. Raised in `STATUS.md`. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: VIKTOR rules on the fleet-wide env, CC implements** |
| **R-460** | **[P3-LOW] BookStack's FILE half cannot be seeded or verified without a browser, so its upgrades can only ever be auto-proven for the DATABASE.** MEASURED 2026-09-06 while building the R-449 harness. BookStack's API needs a token that is only mintable through its web UI, and its HTTP login is unusable headlessly for a second, independent reason: `APP_URL` comes from the template as `https://${SUBDOMAIN}.${DOMAIN}`, so the app marks its session and XSRF cookies **`secure`**; curl over plain http stores neither and **every login POST returns 419 Page Expired**, which looks exactly like a wrong password. The container serves no TLS. **The database half IS provable** — the harness seeds with `php artisan bookstack:create-admin` and reads back with a DIFFERENT artisan command that must find the record, carrying its own negative control on every call. **What is unprovable is an uploaded image or attachment**, i.e. exactly the half a customer would notice. **THIS IS A FACT ABOUT THE APP, NOT A DEFECT IN THE HARNESS**, and it is recorded because Slice 6 needs to know which apps can be auto-verified and which can only be partly verified — nobody had that list before. **Deliberately NOT worked around:** planting a file in the volume would make the test pass while proving nothing, which is R-156's exact failure. **What would remove it:** a headless token route (upstream), or accepting a browser-driven step for this app alone, which DooPlex cannot run. Owner: **CC.** `audits/SPIKE-upgrade-test-2026-09-06.md` §6 | **READY — rank P3-LOW; owner: CC** |
| **R-461** | **[P3-LOW] `runbooks/target-selection.md` names a venue that does not exist and fences a fixture that is gone.** MEASURED 2026-09-06 on demo-hp while siting the R-449 guest. (a) The runbook says to put VM disks on a dir storage at **`/mnt/nvme-1tb`, at its root**. **There is no `/mnt/nvme-1tb`** — the 1 TB NVMe is mounted at **`/mnt/hdd_1`**, which is the enrolled user-data drive and the `felhom-backup` target, i.e. the same disk under a different path. The instruction's REASON is still exactly right (`local-lvm` is an over-subscribed thin pool backing the live guest 9201, and this run kept off it — `local-lvm` read **30.50 % before and after**), so the fence held; only its address is stale. (b) The runbook forbids destroying **`drill-r50` (VM 300)**, "the only drift fixture (R-93)". **`qm list` returns nothing on demo-hp** — there are no VMs at all. **The fence currently protects nothing, and R-93's premise that a drift fixture exists is false.** **Why it is a row and not a quiet edit:** a runbook that names a path nobody can find is one a session works around, and working around a safety instruction is how the instruction stops being followed. Both halves need checking against the box before the text is changed — (b) in particular may mean R-93 should be closed or reopened as "the drift fixture is gone", which is a different fact from "do not destroy it". Owner: **CC.** `audits/SPIKE-upgrade-test-2026-09-06.md` §7 | **READY — rank P3-LOW; owner: CC** |
| **R-462** | **[P2-MEDIUM] Widen the upgrade harness beyond three apps — and the cost is dominated by FIXTURES, not by machine time.** The R-449 harness works and is proven by a red negative control (`audits/SPIKE-upgrade-test-2026-09-06.md` §1). **Costed with this run's REAL numbers rather than an estimate:** a successful edge takes **6.4 s – 305.1 s, median 71.8 s**; a FAILING edge takes **556 s**, roughly **8×**, because a negative is only honest if it waits out the full settle window; 3 apps / 11 images cost **5.07 GB**, so 53 apps naively extrapolate to **~90 GB** and, at the median, about an hour of harness time for one edge each. **THAT EXTRAPOLATION UNDERSTATES THE REAL COST BY AN ORDER OF MAGNITUDE, and that is the point of this row.** Two of the three apps needed a bespoke non-browser seed route; one needed two attempts and a discarded approach; one (bookstack) can only ever be half-proven (R-460). **Fixture time scales with apps and does not amortise.** **The decision this row is really asking for is scope, not schedule:** all 53, or only the apps a customer would lose data from, or only apps whose catalog transition is a MAJOR. **Recommended shape, NOT a design — the operator picks:** start with the apps that carry a database, because §3 measured that the abort question only ever bites there. Owner: **VIKTOR rules on scope, CC implements.** `audits/SPIKE-upgrade-test-2026-09-06.md` §5 | **READY — rank P2-MEDIUM; owner: VIKTOR rules on scope, CC implements** |
@@ -698,7 +697,12 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-464** | **[P3-LOW] MariaDB's entrypoint prints `MariaDB upgrade not required` on an UNSUPPORTED DOWNGRADE, so that line cannot be used as a soundness signal.** MEASURED 2026-09-06. After converting a datadir to `12.3.3-MariaDB` and then starting **11.6** on it, the entrypoint logs, on every start: **`[Note] [Entrypoint]: MariaDB upgrade not required`**. Asked properly, the same engine answers **`FATAL ERROR: Version mismatch (12.3.3-MariaDB -> 11.6.2-MariaDB): Trying to downgrade from a higher to lower version is not supported!`** **The entrypoint compares the datadir's recorded version against its own and concludes there is nothing to DO. That is true, and it is not a statement that the state is sound.** **THIS IS THIS PROJECT'S MOST-REPEATED CLASS, in a new costume** — the same shape as `CLAUDE.md`'s "presence is not success" and as R-443's HTTP 200 over a crash-looping app: a reassuring sentence that answers a narrower question than the one a reader will take it for. **Why it is worth a row rather than a footnote: the obvious cheap instrument for R-459 is to grep container logs for that exact line**, and such an instrument would report "fine" for an unsupported downgrade. **The correct probe is `mariadb-upgrade --check-if-upgrade-is-needed`**, which is what `upgrade-test.py`'s `engine_state_after` now uses. **Also recorded, because it nearly produced a wrong answer here: run without credentials that command returns `ERROR 1045 … FATAL ERROR: Upgrade failed` with exit 1** — an authentication failure wearing the shape of a verdict. Owner: **CC.** `audits/SPIKE-r459-mariadb-upgrade-2026-09-06.md` §5.4 | **READY — rank P3-LOW; owner: CC** |
| **R-465** | **[P3-LOW] `cfg.Paths.HDDPath` — the global that R-442 proved is set on NO box (demo-hp AND demo-felhom: 0 `hdd_path`, 0 `FELHOM_PATHS_*`) — still has SIX readers, each reading an always-empty value:** `report/builder.go:69`, `monitor/healthcheck.go:35`, `api/router.go` (system-info), `web/server.go:740`, `cmd/controller/main.go` (auto-discovery seed + metrics HDD path). Removal was silently inert for months on the very same read. Whether any of these is inert the same way — a report field that is always empty, a health check that never fires, a metric never collected — is a one-hour audit: for each reader, name the POSITIVE observable that must appear when it works and check it on the box. R-442 §5 said "do not delete it here"; this row is the audit it deferred. Owner: **CC.** `felhom-controller/REPORT.md` (v0.236.0, Observations 1) | **READY — rank P3-LOW; owner: CC** |
| **R-466** | **[P3-LOW] Removing an app with „Mentési adatok törlése" ticked leaves its recovery unit's `compose/` + `manifest.json` on the drive.** The router passes only `backup.AppDBDumpPath(nsRoot, name)` to `RemoveStack`, so `backups/primary/<app>/db-dumps/` goes and the unit root keeps `compose/` (the app's `app.yaml` with the portable secret class at 0600) and `manifest.json`. MEASURED 2026-09-13 on demo-hp after the R-442 Scenario A removal — `audits/R442-2026-09-13/teardown-and-log.txt`, residue check: `backups/primary/nextcloud` still listed with the app gone; removed by hand at teardown. A customer who asked for the backups to go is left with the app's definition and a manifest. **Decide:** the button means the WHOLE unit (pass the unit root, under the same `backups/`-prefix guard) or stays db-dumps-only (then the modal must say so). Owner: **CC.** | **READY — rank P3-LOW; owner: CC** |
| **R-467** | **[P3-LOW] Controller v0.236.0 owes a golden** (golden-notice gate, 2026-09-13): newest golden baked is **0.232.0**, so a machine installed now receives 0.232.0 and reaches 0.236.0 only by self-update. Bake + vouch per `runbooks/RUNBOOK-manual-build.md` §4.1 (the THREE-field vouch: `golden_version` + `agent_version` + `min_agent`); bake record to `documentation/tests/golden-<ver>-<date>/`. If the train deliberately skips this version, this row is the waiver the gate asks for. Owner: **CC.** | **READY — rank P3-LOW; owner: CC** |
| **R-468** | **[P3-LOW] THE GOLDEN WAIVER — goldens on a cadence, not per release (operator ruling 2026-09-13).** 25 goldens in 26 days in August, almost one per release, because `golden_currency_gate.py` trips on every release by design and the only honest ways past it were a bake or a declared `--no-verify` (thirteen by 2026-09-01, R-404/R-417). **The ruling: bake WEEKLY, and always before any drill or fresh install.** Every release still raises the FLOOR, so both demo boxes keep getting each release in ~20 s; only the golden — which protects a fresh install and nothing else — moves to a cadence. **The mechanism (built 2026-09-13):** `documentation/tests/golden-waiver.yml`, four lines (`issued`, `expires`, `reason`, `register_row: R-468`), read by the gate. While valid, a golden BEHIND the record makes the gate print a loud ADVISORY and exit 0; when it expires the gate is red again until someone bakes or renews. **The 14-day cap is enforced by the gate, not the runbook** — a longer, undated, unparseable, reason-less or row-less waiver is INCONCLUSIVE (exit 2), never 0 and never silently ignored. **It never covers a golden that is UNRECORDED (R-385)** — that is not a cadence choice. **A dated waiver cannot be forgotten; it just expires** — the difference from R-242's original rule, which recurred the day after it was written. Tests: `scripts/test_golden_currency_gate.py` cases 5–15 (E/F/G/H, a 15-day, absent, unparseable, bad-row and empty-reason waiver each 2; the R-421 decoy — a file saying only `expires` — 2). **This is a PRE-CUSTOMER arrangement: the first external install retires it** (delete the file in that commit). Cadence written into `RUNBOOK-manual-build.md` §4.2 and the `felhom.eu` end-of-session checklist. **Does NOT touch R-242's open half (nothing gates the VOUCH).** | **WATCHING — rank P3-LOW; owner: CC (renew ≤ 14 days or bake); retire at the first external install** |
| **R-469** | **[P3-LOW] REMOVE THE ENGINE-MAJOR RULE when Slice 4 (R-448) ships — a tracked act, not a lapse.** Since 2026-09-13 `app-catalog-felhom.eu` `CLAUDE.md` rules that *until the Update button takes a verified backup as its precondition, no template may move a database-engine image across a major version* (four MariaDB, eleven PostgreSQL services), and `scripts/check-engine-major.py` (fourth row of `catalog_gates.py`, run by `.githooks/pre-push` with the push range) refuses one, naming the rule and this expiry. **Why the rule:** every `mariadb:` sidecar now carries `MARIADB_AUTO_UPGRADE=1` (R-459), so a MariaDB major move CONVERTS the customer's datadir on the next Update; PostgreSQL converts nothing and refuses to start (R-463). Either way a customer-data event with no backup in front of it. **Honest limit, not re-filed:** the gate needs a parent commit and CI fetches at `--depth 1` — the R-452 gap — so on a shallow clone the runner skips it out loud and only the hook bites. **When R-448 ships:** delete the CLAUDE.md rule, the gate's row and the gate, in one commit that cites this row; then close this. | **BLOCKED — on R-448; rank P3-LOW; owner: CC** |
| **R-470** | **[P3-LOW] Four consecutive controller CHANGELOG headers (v0.233.0 … v0.236.0) carry NO `MinAgent:` line, and `RUNBOOK-manual-build.md` §4.1 step 5 tells the vouch to read it from the header.** Measured 2026-09-13 while baking golden 0.236.0: `sed -n 1,349p CHANGELOG.md | grep -c MinAgent` → **0**; the newest statement is v0.232.0's `**MinAgent: 0.129.0** (unchanged)`. The vouch used 0.129.0 on the ground that no later entry declares a change and `internal/agentapi/features.go` gates per feature, not per release — but a reader following the runbook literally finds nothing to read, which is the R-233 shape (a document pointing at a string that is not there). **Fix:** either every release header carries the line (the v0.232.0 convention), or the runbook says where MinAgent actually lives when a header omits it. One of the two, not both. | **READY — rank P3-LOW; owner: CC** |
| **R-471** | **[P3-LOW] `observations_gate.py` reads the FIRST `Observations` section of `REPORT.md` and nothing after it, so an appended second section with an unmarked item passes — and the decoy that proves it has been reading "LIVE HOLE" at HEAD, unnoticed, because the decoy suite is run by hand.** MEASURED 2026-09-13 on a clean worktree of `4b2e560`: `python3 scripts/test_gate_decoys.py` → `FAIL: observations/R-419: decoy PASSED - LIVE HOLE (rc=0)`; the gate's own output shows it scanned `## 11. Observations — noticed, documented, NOT acted on` (3 items, all marked) and never reached the appended `## Observations` block the decoy planted. Whether the cause is "first heading wins" or a heading-shape filter is NOT established — only that the planted unmarked item was not seen. **Consequence:** a report with two observation sections gets the second one unchecked. **Two fixes, both owed:** scan every section whose heading contains `Observations`, and put `test_gate_decoys.py` where something runs it (it is the instrument for R-421 and nothing in `repo_gates.py` invokes it). | **READY — rank P3-LOW; owner: CC** |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.
@@ -202,8 +202,51 @@ bake's own acceptance check. The real guard was never weak: the script's own
back is a form submission, not a rebuild.
A golden is only needed when a publish train wants fresh installs current — demo deploys never need it.
**Since 2026-09-13 the cadence is §4.2: weekly, and before any drill or fresh install.**
The full 0.188.0 run, with the observables: `documentation/audits/tester-gate-golden-0.188.0-2026-07-31.md`.
### 4.2 Cadence — goldens are WEEKLY and before any install, not per release (operator ruling 2026-09-13)
**What was measured before the ruling:** 25 goldens in 26 days in August, almost one per release,
because `golden_currency_gate.py` trips on every release by design and the only honest ways past it
were a bake or a declared `--no-verify` (thirteen of those by 2026-09-01 — R-404/R-417). **The
ruling:** bake on a cadence. Every release still raises the FLOOR (§4.1 step 5), so both demo boxes
keep getting each release in ~20 s; only the golden — which protects a **fresh install** and nothing
else — moves to a cadence.
**The cadence, and it is a step in a routine, not a memory:**
1. **Weekly.** Bake + vouch + raise the floor per §4.1, one golden carrying whatever is newest.
2. **Before ANY drill or fresh install**, whether or not the week is up. A drill on a fresh install
with a stale golden measures the wrong controller (that is how R-120 surfaced).
3. **The first external install retires the arrangement.** The waiver's own register row says so.
**The gate reads a dated waiver** — `documentation/tests/golden-waiver.yml`, four lines:
```yaml
# documentation/tests/golden-waiver.yml — read by scripts/golden_currency_gate.py
issued: 2026-09-13
expires: 2026-09-27 # HARD LIMIT: at most 14 days after issued, or the gate refuses the waiver
reason: pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
register_row: R-468 # must exist as a `**R-468**` row in OPEN-ITEMS.md
```
While it is valid, a golden BEHIND the record makes the gate print a loud **ADVISORY** and exit 0;
when it expires the gate is **red again** until someone bakes or renews. **It never covers a golden
that is UNRECORDED** (no `## vX.Y.Z` heading — R-385): that is not a cadence choice. A waiver longer
than 14 days, with a missing or unparseable date, an empty reason, or a row that does not exist, is
**INCONCLUSIVE (exit 2)** — refused, and refused out loud. The cap lives in the gate
(`WAIVER_MAX_DAYS`), not here, because a cap in prose is what failed the first time (R-242).
**To issue or renew:** edit the two dates (issue = today, expiry ≤ 14 days later), keep the row,
commit it **on its own** with a message saying why the bake is deferred, and push. Renewal is a diff
someone can see — that is the point. **Do not issue a waiver over a bake that could be done today**;
that is the habit the gate's docstring warns about. **To retire it:** delete the file in the commit
that records the first external install.
**What the waiver does NOT do:** it does not touch the vouch half of R-242 (still open — nothing
gates the vouch), it does not change what the gate checks, and it does not stop the floor.
## 5. Hub (felhom.eu/hub → k3s, GitOps)
**The manifest is the truth** — a built image deploys NOTHING until `manifests/hub.yaml`'s `image:`
@@ -0,0 +1 @@
pve-manager/9.2.2/b9984c6d90a4bd80 (running kernel: 7.0.2-6-pve)
@@ -0,0 +1,20 @@
=== preconditions 2026-09-13T07:46:03Z ===
/dev/sda1 9.1T 3.1T 5.5T 37% /mnt/5_hdd
/dev/sdb1 445G 214G 209G 51% /
felhom-controller clean=[] HEAD=1552716 origin=1552716
felhom.eu clean=[] HEAD=4b2e560 origin=4b2e560
felhom-agent clean=[?? scripts/__pycache__/;] HEAD=4586f0f origin=4586f0f
controller CHANGELOG top: ## v0.236.0 — "delete my data too" deletes the data, or says that it could not (2026-09-
MinAgent lines in the top FOUR entries (v0.233.0..v0.236.0): 0 <- none; newest MinAgent statement is v0.232.0's: **MinAgent: 0.129.0** (unchanged)
controller image 0.236.0 in registry: HTTP 200
404 pre-gate on the golden package: HTTP 404
control — the 0.232.0 package that DOES exist: HTTP 200
=== drill VM ===
Snapshot list:
ID TAG VM_SIZE DATE VM_CLOCK ICOUNT
1 virgin 0 B 2026-07-03 14:12:15 0000:00:00.000 0
no qemu running
=== bake script fingerprint on DooPlex (compared across the hop later) ===
7b0fb5cf082fa3301a45578d1624e375d205b4e538d602e7d198a015e573b6a1 felhom-agent/configs/build-golden.sh
@@ -0,0 +1,28 @@
=== pveam update (the virgin snapshot's INDEX is stale too) ===
update successful
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = "UTF-8",
LC_NUMERIC = (unset),
LC_COLLATE = (unset),
LC_TIME = (unset),
LC_MESSAGES = (unset),
LC_MONETARY = (unset),
LC_ADDRESS = (unset),
LC_IDENTIFICATION = (unset),
LC_MEASUREMENT = (unset),
LC_PAPER = (unset),
LC_TELEPHONE = (unset),
LC_NAME = (unset),
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to a fallback locale ("en_US.UTF-8").
debian-13-standard_13.6-1_amd64.tar.zst
calculating checksum...OK, checksum verified
download of 'http://download.proxmox.com/images/system/debian-13-standard_13.6-1_amd64.tar.zst' to '/var/lib/vz/template/cache/debian-13-standard_13.6-1_amd64.tar.zst' finished
=== bake script + token, file -> file ===
in the VM: 7b0fb5cf082fa3301a45578d1624e375d205b4e538d602e7d198a015e573b6a1
DooPlex : 7b0fb5cf082fa3301a45578d1624e375d205b4e538d602e7d198a015e573b6a1
@@ -0,0 +1,6 @@
Running as unit: golden-bake.service; invocation ID: c011a9b879364141982db8d7654bf3fe
=== token-leak check on the unit properties (must be 0) ===
0
=== and the grep is PROVEN to work (must be 1) ===
1
active
@@ -0,0 +1,18 @@
=== acceptance markers, counted on the COMMITTED log ===
docker OK (overlay2 : 1
including mount point :
316:INFO: including mount point rootfs ('/') in backup
317:INFO: including mount point mp0 ('/var/lib/felhom') in backup
upload OK (HTTP 201) : 1
MUST BE ZERO - excluding : 0
MUST BE ZERO - FATAL : 0
=== the result ===
GOLDEN_VERSION=0.236.0
GOLDEN_SHA256=58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf
archive volid: local:backup/vzdump-lxc-9100-2026_09_13-09_51_44.tar.zst
script: build-golden.sh v3.0.0
=== token-leak grep on the COMMITTED log (must be 0), and the grep PROVEN to work ===
bake.log: 0
seeded throwaway copy (must be 1): 1
@@ -0,0 +1,5 @@
Logical volume "vm-9100-disk-1" successfully removed.
purging CT 9100 from related configurations..
leftovers in the VM: 0
qemu exited
1 virgin 0 B 2026-07-03 14:12:15 0000:00:00.000 0
@@ -0,0 +1,8 @@
=== READER 2: the ROUND TRIP — hash the DOWNLOADED bytes, not the bake's printout ===
http=200 bytes=654115664
downloaded sha256: 58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf
bake printed : 58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf
=== THE DELIVERED ARTIFACT MUST NAME ITS OWN CONTROLLER ===
gitea.dooplex.hu/admin/felhom-controller:0.236.0
docker store entries in the archive: 19382
@@ -0,0 +1,11 @@
=== READER 3: the hub's Day-0 dropdown, reading Gitea on a different code path ===
agent_version — none — sha=
agent_version 0.130.0 sha=a56a92a7bd68f5b46736eaec <-- currently SELECTED
agent_version 0.129.0 sha=53a54f0620afbd6d4a1b8660
agent_version 0.128.0 sha=c6eba73bf9b9ad6980cfef57
golden_version — none — sha=
golden_version 0.236.0 sha=58a3cc24c61dc2271f2cb508
golden_version 0.232.0 sha=5f8a53ed5b19a6cb2006298c <-- currently SELECTED
golden_version 0.230.0 sha=9287f7cef5f13166276e8406
min_controller_version = 0.232.0
min_agent = 0.129.0
@@ -0,0 +1,10 @@
=== VOUCH: the THREE fields, moved together ===
golden_version 0.232.0 -> 0.236.0 (this bake carries FOUR releases: 0.233.0..0.236.0 were never baked)
agent_version 0.130.0 -> 0.130.0 UNCHANGED, already >= MinAgent
min_agent 0.129.0 -> 0.129.0 UNCHANGED — no header since v0.232.0 declares a change (R-470 filed for the missing line)
min_agent (0.129.0) <= agent_version (0.130.0): NOT the R-216 shape
golden_sha256 sent = 58a3cc24c61dc227… (must equal the bake's 58a3cc24c61dc227…)
POST http=303
Location: /configuration?flash=artifacts_set
(read from -D, never %{redirect_url} — R-132)
@@ -0,0 +1,10 @@
=== RE-READ FROM THE PAGE — a 303 and a success flash are not proof ===
agent_version SELECTED = 0.130.0 sha=a56a92a7bd68f5b46736eaec...
golden_version SELECTED = 0.236.0 sha=58a3cc24c61dc2271f2cb508...
min_agent = 0.129.0
golden_sha256 = 58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf
R-120 refusal banner present: False
=== raise the fleet floor 0.232.0 -> 0.236.0 (both demo guests already run 0.236.0 by hand — the floor moves nothing today; it makes a fresh install land current) ===
POST http=303
Location: /configuration?flash=floor_set
after: name="min_controller_version" value="0.236.0"
@@ -0,0 +1,74 @@
# Golden bake 0.236.0 — 2026-09-13
Baked, published, round-trip verified, **vouched**, and the fleet floor raised 0.232.0 → 0.236.0.
**This bake carries FOUR releases: 0.233.0, 0.234.0, 0.235.0 and 0.236.0 were never baked** (R-467).
It is the last per-release bake: from today goldens are on a **weekly cadence, and before any drill or
fresh install** (operator ruling 2026-09-13, R-468 — `RUNBOOK-manual-build.md` §4.2).
| | |
|---|---|
| `GOLDEN_SHA256` | `58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf` |
| size | **654 115 664 B** |
| baked controller | `gitea.dooplex.hu/admin/felhom-controller:0.236.0` |
| `MinAgent` | **0.129.0** — the newest header that STATES one is v0.232.0's; v0.233.0…v0.236.0 carry no line (**R-470**) |
| script | `build-golden.sh v3.0.0`, sha `7b0fb5cf…73b6a1`, **compared across the hop** (`02-template.txt`) |
| template | `debian-13-standard_13.6-1_amd64.tar.zst`, after `pveam update` |
| archive volid | `local:backup/vzdump-lxc-9100-2026_09_13-09_51_44.tar.zst` |
## Acceptance markers — counted on the COMMITTED log (`04-markers.txt`)
```
docker OK (overlay2 : 1
including mount point rootfs : 1 (line 316)
including mount point mp0 : 1 (line 317) <- no mp1 since v3.0.0 (R-165/R-233)
upload OK (HTTP 201) : 1
--- must be ZERO ---
excluding : 0
FATAL : 0
```
## Three independent readers agreed before anything was vouched
1. **The bake** printed `GOLDEN_SHA256=58a3cc24…958bf`.
2. **The round trip** (`07-roundtrip.txt`) — the published bytes downloaded back: `HTTP 200`,
**654 115 664 B**, sha `58a3cc24…958bf`, hashed from the **downloaded** bytes.
3. **The hub's Day-0 dropdown** (`08-hub-before-vouch.txt`), a different code path reading Gitea:
`0.236.0 sha=58a3cc24c61dc2271f2cb508`.
The delivered artifact names the controller it will start, read out of the downloaded archive:
`./etc/felhom-controller-image` → `gitea.dooplex.hu/admin/felhom-controller:0.236.0`, with **19 382**
entries under `var/lib/felhom/docker/`.
## Pre-gates, each proven able to see something first (`01-preconditions.txt`, `03-bake-launch.txt`)
| gate | result | the control that makes it believable |
|---|---|---|
| 404 pre-gate | `HTTP 404` for 0.236.0 before the bake | the 0.232.0 package returns `HTTP 200` on the same URL shape |
| token-leak grep on the committed log | **0** | the token appended to a throwaway copy greps **1**; the copy was `shred -u`'d |
| token off every command line | unit properties grep **0** | same seeded control returns **1** |
## The vouch — three fields, and only one moved (`09-vouch.txt`, `11-vouch-verified.txt`)
| field | before | after | why |
|---|---|---|---|
| `golden_version` | 0.232.0 | **0.236.0** | the new bake |
| `agent_version` | 0.130.0 | 0.130.0 | unchanged — already ≥ MinAgent |
| `min_agent` | 0.129.0 | 0.129.0 | unchanged — no header since v0.232.0 declares a change (R-470) |
`POST /configuration/artifacts` → `303 flash=artifacts_set`; the page was **re-read** (golden
`0.236.0` selected, sha `58a3cc24…`), the R-120 refusal banner **absent**. Floor
`POST /configuration/global-floor` → `303 flash=floor_set`, re-read `0.236.0`.
## The floor, and why no box moved
**Both demo guests were already on 0.236.0 by hand** (proven during R-442's live validation the same
morning), so the floor raise changes nothing on the fleet today. It clears `golden_currency_gate.py`
and makes a fresh install land on the current release — which the nightly drills need. The chain
itself was exercised on 2026-09-01 (`golden-0.232.0-2026-09-01/12-selfupdate.txt`) and is not
re-proven here; nothing about it changed.
## Teardown (`05-teardown.txt`)
`pct destroy 9100 --purge`, `shred -u` of the token, runner, script and log **after** the log was
copied out (leftovers in `/root`: 0), `poweroff`, qemu confirmed exited with `ps -eo comm`, and
`qemu-img snapshot -a virgin`. The disk carries the single `virgin` snapshot.
@@ -0,0 +1,327 @@
[golden] build-golden.sh v3.0.0 — baking controller gitea.dooplex.hu/admin/felhom-controller:0.236.0
[golden] creating build LXC 9100 (nesting=1,keyctl=1, unprivileged; rootfs 32G + ONE data volume 24G @ /var/lib/felhom, backup=1) …
Logical volume "vm-9100-disk-0" created.
Logical volume pve/vm-9100-disk-0 changed.
Creating filesystem with 8388608 4k blocks and 2097152 inodes
Filesystem UUID: 2f235e82-2a88-4be0-b7b7-75778991837c
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624
Logical volume "vm-9100-disk-1" created.
Logical volume pve/vm-9100-disk-1 changed.
Creating filesystem with 6291456 4k blocks and 1572864 inodes
Filesystem UUID: 39948d21-08ac-4cde-b5d3-997c54b68f36
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
extracting archive '/var/lib/vz/template/cache/debian-13-standard_13.6-1_amd64.tar.zst'
Total bytes read: 553512960 (528MiB, 94MiB/s)
Detected container architecture: amd64
Creating SSH host key 'ssh_host_ed25519_key' - this may take some time ...
done: SHA256:e56AH6TbE0URIv25xegROJSnKt2yI9lK+kjeinRAeIE root@felhom-golden
Creating SSH host key 'ssh_host_ecdsa_key' - this may take some time ...
done: SHA256:VUZj3qbCDwZzz7pZsp5QlPisPDJ5KwjVShkoQEQuAZ4 root@felhom-golden
Creating SSH host key 'ssh_host_rsa_key' - this may take some time ...
done: SHA256:7n/yEy8Nfqo8I4pX5L2b3Kg3d2K2sfgYYAZeH9XqWaI root@felhom-golden
[golden] starting + installing Docker (official repo, trixie channel) …
apt-listchanges: Can't set locale; make sure $LC_* and $LANG are correct!
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = (unset),
LC_NUMERIC = (unset),
LC_COLLATE = (unset),
LC_TIME = (unset),
LC_MESSAGES = (unset),
LC_MONETARY = (unset),
LC_ADDRESS = (unset),
LC_IDENTIFICATION = (unset),
LC_MEASUREMENT = (unset),
LC_PAPER = (unset),
LC_TELEPHONE = (unset),
LC_NAME = (unset),
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
apt-listchanges: Can't set locale; make sure $LC_* and $LANG are correct!
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_CTYPE = (unset),
LC_NUMERIC = (unset),
LC_COLLATE = (unset),
LC_TIME = (unset),
LC_MESSAGES = (unset),
LC_MONETARY = (unset),
LC_ADDRESS = (unset),
LC_IDENTIFICATION = (unset),
LC_MEASUREMENT = (unset),
LC_PAPER = (unset),
LC_TELEPHONE = (unset),
LC_NAME = (unset),
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
[golden] baking daemon.json: classic overlay2 driver (containerd-snapshotter OFF) + log rotation …
[golden] wiring the single data volume (R-165 variant V-c): /var/lib/felhom/{docker,sys_drive} -> binds …
[golden] verifying Docker works in the build guest (storage driver should be overlay2 on the ext4 data volume) …
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4f55086f7dd0: Pulling fs layer
4f55086f7dd0: Verifying Checksum
4f55086f7dd0: Download complete
4f55086f7dd0: Pull complete
Digest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe895f9cc8f8
Status: Downloaded newer image for hello-world:latest
docker OK (overlay2; data-root /var/lib/docker)
/var/lib/docker is a real mount: /dev/mapper/pve-vm--9100--disk--1[/docker] ext4
/mnt/sys_drive is a real mount: /dev/mapper/pve-vm--9100--disk--1[/sys_drive] ext4
both paths are ONE filesystem: /dev/mapper/pve-vm--9100--disk--1 23317576
[golden] baking the in-guest controller image gitea.dooplex.hu/admin/felhom-controller:0.236.0 (no registry cred at deploy) …
WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/
0.236.0: Pulling from admin/felhom-controller
a8ac7f6c67ab: Pulling fs layer
bf30769d36e7: Pulling fs layer
044b66fbe46c: Pulling fs layer
b5c41a28e83f: Pulling fs layer
4051fe1934c3: Pulling fs layer
7879c9030fd8: Pulling fs layer
b5c41a28e83f: Waiting
4051fe1934c3: Waiting
7879c9030fd8: Waiting
a8ac7f6c67ab: Verifying Checksum
a8ac7f6c67ab: Download complete
044b66fbe46c: Verifying Checksum
044b66fbe46c: Download complete
b5c41a28e83f: Verifying Checksum
b5c41a28e83f: Download complete
4051fe1934c3: Verifying Checksum
4051fe1934c3: Download complete
7879c9030fd8: Verifying Checksum
7879c9030fd8: Download complete
bf30769d36e7: Verifying Checksum
bf30769d36e7: Download complete
a8ac7f6c67ab: Pull complete
bf30769d36e7: Pull complete
044b66fbe46c: Pull complete
b5c41a28e83f: Pull complete
4051fe1934c3: Pull complete
7879c9030fd8: Pull complete
Digest: sha256:ec94c3298040ae9061555c1dbdd0fc6221d64ba42f7154ce71739ec1d8f09176
Status: Downloaded newer image for gitea.dooplex.hu/admin/felhom-controller:0.236.0
gitea.dooplex.hu/admin/felhom-controller:0.236.0
[golden] asking the controller which infra images it manages …
[golden] baking infra images (4): traefik:v3.6.7 cloudflare/cloudflared:2026.6.0 gtstef/filebrowser:1.3.3-stable gitea.dooplex.hu/admin/felhom-samba:1.1.0 …
v3.6.7: Pulling from library/traefik
589002ba0eae: Pulling fs layer
ef63511ea6cc: Pulling fs layer
0738e5cb835e: Pulling fs layer
3e6813f70c64: Pulling fs layer
3e6813f70c64: Waiting
ef63511ea6cc: Verifying Checksum
ef63511ea6cc: Download complete
589002ba0eae: Verifying Checksum
589002ba0eae: Download complete
3e6813f70c64: Verifying Checksum
3e6813f70c64: Download complete
0738e5cb835e: Verifying Checksum
0738e5cb835e: Download complete
589002ba0eae: Pull complete
ef63511ea6cc: Pull complete
0738e5cb835e: Pull complete
3e6813f70c64: Pull complete
Digest: sha256:a9890c898f379c1905ee5b28342f6b408dc863f08db2dab20e46c267d1ff463a
Status: Downloaded newer image for traefik:v3.6.7
docker.io/library/traefik:v3.6.7
2026.6.0: Pulling from cloudflare/cloudflared
47de5dd0b812: Pulling fs layer
c172f21841df: Pulling fs layer
99515e7b4d35: Pulling fs layer
99ba982a9142: Pulling fs layer
d6b1b89eccac: Pulling fs layer
2780920e5dbf: Pulling fs layer
7c12895b777b: Pulling fs layer
3214acf345c0: Pulling fs layer
52630fc75a18: Pulling fs layer
dd64bf2dd177: Pulling fs layer
b839dfae01f6: Pulling fs layer
ebddc55facdc: Pulling fs layer
bdfd7f7e5bf6: Pulling fs layer
2d4d7adf6272: Pulling fs layer
40008157d8d2: Pulling fs layer
bd8962e29291: Pulling fs layer
cac2ae0193cb: Pulling fs layer
74d1dac84ecc: Pulling fs layer
b839dfae01f6: Waiting
ebddc55facdc: Waiting
bdfd7f7e5bf6: Waiting
2d4d7adf6272: Waiting
40008157d8d2: Waiting
bd8962e29291: Waiting
cac2ae0193cb: Waiting
74d1dac84ecc: Waiting
99ba982a9142: Waiting
d6b1b89eccac: Waiting
2780920e5dbf: Waiting
7c12895b777b: Waiting
3214acf345c0: Waiting
52630fc75a18: Waiting
dd64bf2dd177: Waiting
47de5dd0b812: Download complete
c172f21841df: Verifying Checksum
c172f21841df: Download complete
47de5dd0b812: Pull complete
99515e7b4d35: Verifying Checksum
99515e7b4d35: Download complete
99ba982a9142: Verifying Checksum
99ba982a9142: Download complete
d6b1b89eccac: Verifying Checksum
d6b1b89eccac: Download complete
2780920e5dbf: Verifying Checksum
2780920e5dbf: Download complete
7c12895b777b: Verifying Checksum
7c12895b777b: Download complete
c172f21841df: Pull complete
3214acf345c0: Verifying Checksum
3214acf345c0: Download complete
52630fc75a18: Verifying Checksum
52630fc75a18: Download complete
dd64bf2dd177: Verifying Checksum
dd64bf2dd177: Download complete
b839dfae01f6: Verifying Checksum
b839dfae01f6: Download complete
ebddc55facdc: Verifying Checksum
ebddc55facdc: Download complete
bdfd7f7e5bf6: Verifying Checksum
bdfd7f7e5bf6: Download complete
40008157d8d2: Verifying Checksum
40008157d8d2: Download complete
bd8962e29291: Verifying Checksum
bd8962e29291: Download complete
2d4d7adf6272: Verifying Checksum
2d4d7adf6272: Download complete
cac2ae0193cb: Verifying Checksum
cac2ae0193cb: Download complete
99515e7b4d35: Pull complete
74d1dac84ecc: Verifying Checksum
74d1dac84ecc: Download complete
99ba982a9142: Pull complete
d6b1b89eccac: Pull complete
2780920e5dbf: Pull complete
7c12895b777b: Pull complete
3214acf345c0: Pull complete
52630fc75a18: Pull complete
dd64bf2dd177: Pull complete
b839dfae01f6: Pull complete
ebddc55facdc: Pull complete
bdfd7f7e5bf6: Pull complete
2d4d7adf6272: Pull complete
40008157d8d2: Pull complete
bd8962e29291: Pull complete
cac2ae0193cb: Pull complete
74d1dac84ecc: Pull complete
Digest: sha256:ba461b8aa9c042156dbd39c38657fe7431bafa063220eab8d5330a523863da9f
Status: Downloaded newer image for cloudflare/cloudflared:2026.6.0
docker.io/cloudflare/cloudflared:2026.6.0
1.3.3-stable: Pulling from gtstef/filebrowser
6a0ac1617861: Pulling fs layer
ef8806083e82: Pulling fs layer
b74107c861c7: Pulling fs layer
adc935def003: Pulling fs layer
4f4fb700ef54: Pulling fs layer
18695ccc900a: Pulling fs layer
45d119d5c397: Pulling fs layer
dac52db4fc51: Pulling fs layer
6d598f86b2f2: Pulling fs layer
8aa349c8396c: Pulling fs layer
dac52db4fc51: Waiting
6d598f86b2f2: Waiting
8aa349c8396c: Waiting
4f4fb700ef54: Waiting
18695ccc900a: Waiting
45d119d5c397: Waiting
adc935def003: Waiting
6a0ac1617861: Verifying Checksum
6a0ac1617861: Download complete
adc935def003: Verifying Checksum
adc935def003: Download complete
6a0ac1617861: Pull complete
4f4fb700ef54: Verifying Checksum
4f4fb700ef54: Download complete
b74107c861c7: Verifying Checksum
b74107c861c7: Download complete
ef8806083e82: Verifying Checksum
ef8806083e82: Download complete
45d119d5c397: Verifying Checksum
45d119d5c397: Download complete
18695ccc900a: Verifying Checksum
18695ccc900a: Download complete
dac52db4fc51: Verifying Checksum
dac52db4fc51: Download complete
6d598f86b2f2: Verifying Checksum
6d598f86b2f2: Download complete
8aa349c8396c: Verifying Checksum
8aa349c8396c: Download complete
ef8806083e82: Pull complete
b74107c861c7: Pull complete
adc935def003: Pull complete
4f4fb700ef54: Pull complete
18695ccc900a: Pull complete
45d119d5c397: Pull complete
dac52db4fc51: Pull complete
6d598f86b2f2: Pull complete
8aa349c8396c: Pull complete
Digest: sha256:eb3733681db8757412632c61a99ad656f0d94ed6781bb2ea114b4d70babab78c
Status: Downloaded newer image for gtstef/filebrowser:1.3.3-stable
docker.io/gtstef/filebrowser:1.3.3-stable
1.1.0: Pulling from admin/felhom-samba
897d797d2723: Pulling fs layer
3051591aa250: Pulling fs layer
ce57a3f93416: Pulling fs layer
fb94eeec2fe1: Pulling fs layer
fb94eeec2fe1: Waiting
ce57a3f93416: Verifying Checksum
ce57a3f93416: Download complete
fb94eeec2fe1: Verifying Checksum
fb94eeec2fe1: Download complete
897d797d2723: Download complete
3051591aa250: Verifying Checksum
3051591aa250: Download complete
897d797d2723: Pull complete
3051591aa250: Pull complete
ce57a3f93416: Pull complete
fb94eeec2fe1: Pull complete
Digest: sha256:1c17c09422bec0366d7cf0e0fcfc1486ba6c90334a0a5d5c851073a9342f8f10
Status: Downloaded newer image for gitea.dooplex.hu/admin/felhom-samba:1.1.0
gitea.dooplex.hu/admin/felhom-samba:1.1.0
[golden] baking the controller-bootstrap unit (deploys the BAKED controller from the config mount) …
Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-controller-bootstrap.service' → '/etc/systemd/system/felhom-controller-bootstrap.service'.
[golden] baking the controller-bootstrap PATH unit (starts the service on bootstrap-mount hot-plug — B1) …
Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-controller-bootstrap.path' → '/etc/systemd/system/felhom-controller-bootstrap.path'.
[golden] baking the first-boot SSH host-key regeneration unit (F3) …
Created symlink '/etc/systemd/system/multi-user.target.wants/felhom-regen-hostkeys.service' → '/etc/systemd/system/felhom-regen-hostkeys.service'.
[golden] identity-clean + minimize …
[golden] stop + archive …
INFO: including mount point rootfs ('/') in backup
INFO: including mount point mp0 ('/var/lib/felhom') in backup
INFO: archive file size: 623MB
INFO: Finished Backup of VM 9100 (00:00:43)
[golden] DONE. golden archive volid: local:backup/vzdump-lxc-9100-2026_09_13-09_51_44.tar.zst (rootfs 32G + ONE data volume 24G @ /var/lib/felhom, all in the archive)
[golden] publishing golden (654115664 bytes, sha256 58a3cc24c61dc227…) → https://gitea.dooplex.hu/api/packages/admin/generic/felhom-golden/0.236.0/golden.tar.zst
[golden] pre-delete existing: HTTP 404 (404/204 expected)
[golden] upload OK (HTTP 201)
GOLDEN_VERSION=0.236.0
GOLDEN_SHA256=58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf
[golden] Record in the hub operator UI (Configs → Day-0 artifacts): golden 0.236.0 / 58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf
[golden] (the build guest 9100 is stopped; destroy it with: pct destroy 9100 --purge)
+11
View File
@@ -0,0 +1,11 @@
# documentation/tests/golden-waiver.yml — read by scripts/golden_currency_gate.py
#
# Goldens are on a CADENCE, not per release (operator ruling 2026-09-13, R-468): weekly, and always
# before any drill or fresh install. While this waiver is valid a golden BEHIND the record makes the
# gate ADVISORY instead of red. It never covers a golden that is UNRECORDED (R-385). At most 14 days
# after `issued`, or the gate refuses it. Renew with a new dated commit; delete it at the first
# external install. Issued AFTER the 0.236.0 bake landed — not over a bake that could have been done.
issued: 2026-09-13
expires: 2026-09-27
reason: pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
register_row: R-468
+28
View File
@@ -1,3 +1,31 @@
## the golden waiver — goldens on a cadence, not per release (2026-09-13, R-468 / R-242) — NOT A RELEASE
**No product code, no version bump, no image.** A scripts change is not a release.
`golden_currency_gate.py` now reads a dated waiver, `documentation/tests/golden-waiver.yml` (`issued`,
`expires`, `reason`, `register_row`). Operator ruling 2026-09-13: **bake on a cadence — weekly, and
always before any drill or fresh install — not per release.** The gate had tripped on every release
by design and the only honest ways past it were a bake or a declared `--no-verify`; August measured
25 goldens in 26 days and thirteen bypasses.
- **Valid waiver + golden BEHIND the record** → a loud ADVISORY naming the waiver, its expiry and how
many releases the golden lags; exit 0. A waived conviction stays visible on every push.
- **Expired waiver** → exit 1 exactly as before, and the message says it EXPIRED on `<date>`.
- **Unrecorded golden (R-385)** → exit 1 regardless; the waiver is named and declared NOT to cover
it. **The asymmetry is the design:** behind is a cadence choice, unrecorded is the fleet running
something nobody wrote down.
- **Malformed waiver** — more than 14 days, absent or unparseable date, empty reason, a row that does
not exist in `OPEN-ITEMS.md` — → exit 2 INCONCLUSIVE, never 0, never silently ignored. The 14-day
cap lives in the gate (`WAIVER_MAX_DAYS`), not in prose. The register is read for ONE fact (does
the row exist), never for meaning (R-421).
- `GOLDEN_GATE_*` environment variables are a **test seam**: they move where the gate reads, never
what it decides. `test_golden_currency_gate.py` gains cases 5–15 (Scenarios E/F/G/H, each with its
wrong direction; the R-421 decoy — a file saying only `expires` — returns 2). **Red-proof:** the
old gate on the real tree with a valid waiver planted → exit 1 (it cannot read one); the new gate on
synthetic "behind" trees → 0 with the advisory present.
- The docstring's *"honest fix is a recorded waiver, never a habit of bypassing"* is now a mechanism.
**R-242's OTHER half — nothing gates the VOUCH — is unchanged and still open.**
## the decoy sweep — can this gate be fooled by a label? (2026-09-01, R-421) — NOT A RELEASE
**No product code, no version bump, no image, no golden.** A scripts change is not a release.
+184 -7
View File
@@ -37,7 +37,8 @@ That limit is forced, not chosen, and the reasoning is recorded so nobody re-der
and is therefore the step this repo can see; the vouch is an operator act against the hub and needs a
different mechanism. That gap is real and is recorded as R-242's remaining half, NOT papered over
here. In practice the two are minutes apart in the same session, and the recurrence this gate is
built for was a missing BAKE.
built for was a missing BAKE. **That vouch half is STILL open after 2026-09-13** — the waiver below
covers a different thing, and this sentence is kept so the two are not confused.
WHY VERSION AND NOT BEHAVIOUR. It compares version numbers, so a controller release that changed
nothing a customer can see also trips it. That is accepted deliberately: deciding "customer-visible"
@@ -45,7 +46,8 @@ mechanically is not possible, judging it by hand is what already failed twice, a
false trip is one bake — which is the operation the project wants to be routine anyway. **A gate that
cries wolf is one people learn to bypass, and `--no-verify` exists**, so the tolerance is stated
rather than assumed: if this ever fires on a release nobody wants a golden for, the honest fix is a
recorded waiver in the register, never a habit of bypassing.
recorded waiver in the register, never a habit of bypassing. **That waiver is now BUILT — see
"THE WAIVER" below (2026-09-13).**
FAIL-CLOSED, BUT HONEST ABOUT NOT KNOWING. An absent controller clone, or a CHANGELOG whose top
header cannot be parsed, exits **2 (INCONCLUSIVE)** — never 0. The runner reports 2 distinctly for
@@ -72,7 +74,47 @@ it is absent. An unrecorded golden is convicted (exit 1) exactly like a stale on
**Why membership and not `baked > released`.** A comparison against the newest heading alone would go
green again the moment ANY later entry was written, leaving 0.221.1 permanently unrecorded and the
gate permanently silent about it. Membership cannot be satisfied by an unrelated later release.
── THE WAIVER — goldens on a CADENCE, not per release (added 2026-09-13, operator ruling) ────────
What happened between 2026-08-07 and 2026-09-01: **25 goldens in 26 days**, almost one per release,
because this gate trips on every release by design (see WHY VERSION AND NOT BEHAVIOUR) and the only
honest ways past it were a bake or a `--no-verify`. Thirteen bypasses were counted by 2026-09-01
(R-404/R-417). The operator ruled on 2026-09-13: **bake on a cadence — weekly, and always before any
drill or fresh install — not per release.** Every release still raises the FLOOR, so the fleet keeps
getting each release in ~20 s; only the golden, which protects a fresh install and nothing else,
moves to a cadence.
The mechanism is a small tracked file, `documentation/tests/golden-waiver.yml`:
issued: 2026-09-13
expires: 2026-09-27 # at most 14 days after issued, or the gate REFUSES the waiver
reason: pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
register_row: R-468 # must exist as a `**R-468**` row in OPEN-ITEMS.md
While the waiver is VALID, the "behind" conviction becomes a **loud ADVISORY** (exit 0) that names
the waiver, its expiry and how many releases the golden lags. When it runs out, the gate is red
again until someone bakes or renews. **A dated waiver cannot be forgotten — it just expires.** That
is what makes it different from R-242's original rule, which recurred the day after it was written:
renewal is a new commit with a diff, a deliberate act someone can see.
THE ASYMMETRY, stated because it is the whole design: **the waiver covers a golden that is BEHIND the
record. It never covers a golden that is UNRECORDED (R-385).** The first is a cadence choice; the
second is the fleet running something nobody wrote down, and no schedule makes that acceptable.
WHAT REFUSES THE WAIVER (exit 2, INCONCLUSIVE — never 0, never silently ignored): an expiry more
than 14 days after issue; an absent or unparseable date; a missing or empty reason; a register row
that is absent or whose `**R-n**` row does not exist. The 14-day cap is enforced HERE, not in the
runbook, because a cap in prose is the thing that failed. A file at the right path containing only
the word `expires` is the R-421 decoy and is refused like any other malformed waiver.
The register is read for exactly ONE fact — does the named row exist — never for meaning. Reading
prose for meaning is the R-421 class.
WHAT IT DOES NOT COVER: the vouch (still R-242's open half); an unrecorded golden (R-385); anything
after the first external install — the waiver's own row says it is a pre-customer arrangement.
"""
import datetime
import io
import os
import re
@@ -81,8 +123,21 @@ import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# The controller clone sits beside this one. The same sibling assumption reuse_refs_check.py and
# instructions_gate.py already make — an absent sibling is INCONCLUSIVE, never a silent pass.
CONTROLLER_CHANGELOG = os.path.join(os.path.dirname(ROOT), "felhom-controller", "CHANGELOG.md")
EVIDENCE_DIR = os.path.join(ROOT, "documentation", "tests")
#
# The GOLDEN_GATE_* environment variables are a TEST SEAM (2026-09-13): they move WHERE the gate
# reads, never WHAT it decides, so test_golden_currency_gate.py can build a "behind" or an
# "unrecorded" tree without depending on the real controller's release history. Unset in every real
# run — the hook and CI export nothing.
CONTROLLER_CHANGELOG = os.environ.get(
"GOLDEN_GATE_CHANGELOG",
os.path.join(os.path.dirname(ROOT), "felhom-controller", "CHANGELOG.md"))
EVIDENCE_DIR = os.environ.get("GOLDEN_GATE_EVIDENCE_DIR", os.path.join(ROOT, "documentation", "tests"))
WAIVER_PATH = os.environ.get("GOLDEN_GATE_WAIVER",
os.path.join(ROOT, "documentation", "tests", "golden-waiver.yml"))
REGISTER_PATH = os.environ.get("GOLDEN_GATE_REGISTER",
os.path.join(ROOT, "documentation", "backlog", "OPEN-ITEMS.md"))
# The HARD LIMIT on a waiver's life. Enforced here and nowhere else — see the module docstring.
WAIVER_MAX_DAYS = 14
# `## v0.206.0 — …` on the FIRST such line: the CHANGELOG is newest-first by convention.
RELEASED_RE = re.compile(r"^##\s+v(\d+)\.(\d+)\.(\d+)\b")
@@ -182,6 +237,82 @@ def newest_baked():
return found[-1]
WAIVER_KEY_RE = re.compile(r"^\s*([a-z_]+)\s*:\s*(.*?)\s*$")
ROW_RE = re.compile(r"^R-\d+$")
def read_waiver(path=None, register=None, today=None):
"""(state, info) — state is 'absent', 'valid', 'expired' or 'malformed'.
The file is four `key: value` lines; comments (`# …`) and blank lines are ignored. It is parsed by
hand so the gate stays stdlib-only and `--fast`. Every way the file can be wrong returns
'malformed' with the reason in info["why"] — the caller turns that into exit 2, never into 0 and
never into a silent 'absent'. A decoy — the word `expires` with no date — lands here too.
"""
path = path or WAIVER_PATH
register = register or REGISTER_PATH
today = today or datetime.datetime.now(datetime.timezone.utc).date()
if not os.path.isfile(path):
return "absent", {"path": path}
try:
text = io.open(path, encoding="utf-8").read()
except OSError as e:
return "malformed", {"path": path, "why": "cannot be read (%s)" % e}
fields = {}
for line in text.splitlines():
line = line.split("#", 1)[0]
m = WAIVER_KEY_RE.match(line)
if m and m.group(2):
fields[m.group(1)] = m.group(2).strip().strip("'\"")
info = {"path": path, "fields": fields}
def date_of(key):
v = fields.get(key, "")
try:
return datetime.date.fromisoformat(v)
except ValueError:
return None
issued, expires = date_of("issued"), date_of("expires")
if issued is None:
info["why"] = "`issued:` is absent or not a YYYY-MM-DD date (got %r)" % fields.get("issued", "")
return "malformed", info
if expires is None:
info["why"] = "`expires:` is absent or not a YYYY-MM-DD date (got %r)" % fields.get("expires", "")
return "malformed", info
if expires <= issued:
info["why"] = "`expires:` (%s) is not after `issued:` (%s)" % (expires, issued)
return "malformed", info
span = (expires - issued).days
if span > WAIVER_MAX_DAYS:
info["why"] = ("`expires:` is %d days after `issued:` — the hard limit is %d. A waiver that "
"tries to be permanent is refused; renew it with a new dated commit instead."
% (span, WAIVER_MAX_DAYS))
return "malformed", info
if not fields.get("reason"):
info["why"] = "`reason:` is absent or empty"
return "malformed", info
row = fields.get("register_row", "")
if not ROW_RE.match(row):
info["why"] = "`register_row:` is absent or not of the form R-<n> (got %r)" % row
return "malformed", info
# The register is read for ONE fact — does the row exist — never for meaning (R-421).
try:
reg = io.open(register, encoding="utf-8").read()
except OSError as e:
info["why"] = "register %s cannot be read (%s)" % (register, e)
return "malformed", info
if ("**%s**" % row) not in reg:
info["why"] = "`register_row: %s` names a row that does not exist in %s" % (
row, os.path.relpath(register, ROOT) if register.startswith(ROOT) else register)
return "malformed", info
info.update({"issued": issued, "expires": expires, "row": row, "reason": fields["reason"],
"days_left": (expires - today).days})
if today >= expires:
return "expired", info
return "valid", info
def vstr(v):
return ".".join(str(p) for p in v)
@@ -201,6 +332,23 @@ def main():
print(" newest released controller : %s (%s)" % (vstr(released), rel_note))
print(" newest golden baked : %s (documentation/tests/%s)" % (vstr(baked), bake_note))
# The waiver is read BEFORE any verdict, because a malformed one is a result of its own (exit 2)
# whatever the golden's state — a file that says `expires` and means nothing must not lie there
# looking like cover.
wstate, winfo = read_waiver()
if wstate == "malformed":
print("")
print("GOLDEN CURRENCY GATE INCONCLUSIVE: the waiver at %s is MALFORMED — %s"
% (os.path.relpath(winfo["path"], ROOT), winfo["why"]))
print("A malformed waiver is neither cover nor absence. Fix it (four lines: issued, expires "
"<= %d days later, reason, register_row) or delete it." % WAIVER_MAX_DAYS)
sys.exit(2)
if wstate == "valid":
print(" waiver : VALID until %s (%d day(s) left) — %s, reason: %s"
% (winfo["expires"], winfo["days_left"], winfo["row"], winfo["reason"]))
elif wstate == "expired":
print(" waiver : EXPIRED on %s (%s)" % (winfo["expires"], winfo["row"]))
# R-385 — UNRECORDED, checked before "behind". A golden whose version has no heading of its own
# was built from something never written down, and that is a different (worse) fault than a
# forgotten bake: there is nothing to read to find out what the fleet is running.
@@ -208,6 +356,12 @@ def main():
print("")
print("GOLDEN CURRENCY GATE FAILED: golden %s is baked but UNRECORDED — the controller "
"CHANGELOG has no '## v%s' heading." % (vstr(baked), vstr(baked)))
if wstate == "valid":
# THE ASYMMETRY. A waiver is a cadence choice about a golden that is BEHIND; it says
# nothing about a golden nobody wrote down, and must not be read as if it did.
print("A waiver exists (%s, until %s) and DOES NOT COVER THIS: it covers a golden that is "
"behind the record, never one that is unrecorded (R-385)."
% (winfo["row"], winfo["expires"]))
print("The newest heading is %s. A golden ahead of the record was built from a version "
"nobody wrote down, so no one can read what the fleet is running." % vstr(released))
print("Fix: give v%s its own '## v%s — <what changed>' heading in "
@@ -220,17 +374,40 @@ def main():
sys.exit(1)
if released > baked:
lag = sorted(v for v in every_released if v > baked)
if wstate == "valid":
print("")
print("GOLDEN CURRENCY GATE ADVISORY — WAIVED, NOT CLEAN: controller v%s is released and "
"NO golden carries it (newest bake is %s; %d release(s) behind: %s)."
% (vstr(released), vstr(baked), len(lag), ", ".join(vstr(v) for v in lag)))
print("A machine installed right now would receive v%s and reach v%s by self-update."
% (vstr(baked), vstr(released)))
print("Waived by %s until %s (%d day(s) left): %s"
% (winfo["row"], winfo["expires"], winfo["days_left"], winfo["reason"]))
print("This is the operator's 2026-09-13 cadence ruling, not a pass: bake weekly and "
"before ANY drill or fresh install (RUNBOOK-manual-build.md §4.2). When the waiver "
"expires this gate is red again.")
print("golden currency gate OK (WAIVED) — the newest released controller has NO golden; "
"a valid waiver covers it (NOTE: this checks the BAKE, not the vouch)")
return
print("")
print("GOLDEN CURRENCY GATE FAILED: controller v%s is released and NO golden carries it "
"(newest bake is %s)." % (vstr(released), vstr(baked)))
"(newest bake is %s; %d release(s) behind)." % (vstr(released), vstr(baked), len(lag)))
if wstate == "expired":
print("The waiver at documentation/tests/golden-waiver.yml EXPIRED on %s (%s). It ran "
"out, as a dated waiver is meant to: bake a golden, or renew it with a new dated "
"commit (at most %d days)." % (winfo["expires"], winfo["row"], WAIVER_MAX_DAYS))
print("A machine installed right now would receive v%s — the release is written, tested and "
"pushed, and NOT delivered." % vstr(baked))
print("Fix: bake a golden per documentation/runbooks/RUNBOOK-manual-build.md §4.1, then vouch "
"it (a THREE-field change: golden_version + agent_version + min_agent).")
print("If this release deliberately needs no golden, record a waiver in "
"documentation/backlog/OPEN-ITEMS.md — never a bypass.")
print("If the cadence ruling covers this release, issue a DATED waiver at "
"documentation/tests/golden-waiver.yml (RUNBOOK-manual-build.md §4.2) — never a bypass.")
sys.exit(1)
if wstate == "expired":
print(" NOTE: the waiver expired on %s and covers nothing today (the golden is current). "
"Renew or delete it so it does not read as cover." % winfo["expires"])
print("golden currency gate OK — the newest released controller has a golden "
"(NOTE: this checks the BAKE, not the vouch — see the module docstring)")
+126 -1
View File
@@ -18,6 +18,12 @@ satisfied by a gate that fails on everything.
Run: python3 scripts/test_golden_currency_gate.py
Exit 0 all pass · 1 a case failed.
2026-09-13: the WAIVER (Scenarios E-H of the task that built it) is tested below in `waiver_cases`,
against a synthetic tree through the gate's GOLDEN_GATE_* seam — a valid waiver passes with the
advisory PRESENT; an expired one convicts and NAMES the date; a valid one does NOT save an
unrecorded golden (R-385); a 15-day, absent-expiry, unparseable, bad-row and empty-reason waiver
each return 2; and the R-421 decoy (a file saying only `expires`) returns 2.
"""
import io
import os
@@ -111,13 +117,132 @@ def main():
else:
print("CASE 4 ok: the tree is unchanged; the gate's verdict is the same as the baseline")
fails += waiver_cases()
if fails:
print()
for f in fails:
print("FAIL: %s" % f)
return 1
print("\ngolden-currency gate self-test OK — a directory name alone cannot satisfy it (R-410)")
print("\ngolden-currency gate self-test OK — a directory name alone cannot satisfy it (R-410), "
"and the waiver is judged on its dates, its row and its direction (2026-09-13)")
return 0
# ── THE WAIVER (2026-09-13) — Scenarios E, F, G, H, each with its red-proof ─────────────────────
#
# These run the gate against a SYNTHETIC tree through the GOLDEN_GATE_* seam (a fake controller
# CHANGELOG, a fake evidence dir with a real-shaped bake log, a fake register, a waiver file), so the
# verdicts do not depend on what the real controller happens to have released this week. The seam
# moves where the gate reads, never what it decides.
import datetime
import tempfile
def run_gate_env(env):
e = dict(os.environ)
e.update(env)
p = subprocess.run([sys.executable, GATE], capture_output=True, text=True, env=e)
return p.returncode, p.stdout + p.stderr
def synthetic_tree(released, baked, waiver_text, register_rows=("R-468",)):
"""Build a tree where the CHANGELOG lists `released` (newest first) and one golden `baked` has a
real-shaped bake log. Returns (env, tmpdir)."""
tmp = tempfile.mkdtemp(prefix="golden-waiver-")
ch = os.path.join(tmp, "CHANGELOG.md")
with io.open(ch, "w", encoding="utf-8") as fh:
for v in released:
fh.write("## v%s — synthetic (2026-01-01)\n\nbody\n\n" % v)
ev = os.path.join(tmp, "tests")
os.makedirs(os.path.join(ev, "golden-%s-2026-01-01" % baked))
with io.open(os.path.join(ev, "golden-%s-2026-01-01" % baked, "bake.log"), "w",
encoding="utf-8") as fh:
fh.write("[golden] upload OK (HTTP 201)\nGOLDEN_VERSION=%s\nGOLDEN_SHA256=%s\n" % (baked, REAL_SHA))
reg = os.path.join(tmp, "OPEN-ITEMS.md")
with io.open(reg, "w", encoding="utf-8") as fh:
for r in register_rows:
fh.write("| **%s** | synthetic row | READY | CC |\n" % r)
wv = os.path.join(tmp, "golden-waiver.yml")
if waiver_text is not None:
with io.open(wv, "w", encoding="utf-8") as fh:
fh.write(waiver_text)
env = {"GOLDEN_GATE_CHANGELOG": ch, "GOLDEN_GATE_EVIDENCE_DIR": ev,
"GOLDEN_GATE_WAIVER": wv, "GOLDEN_GATE_REGISTER": reg}
return env, tmp
def waiver(issued, expires, reason="pre-customer development; weekly cadence", row="R-468"):
lines = ["# synthetic waiver"]
if issued is not None:
lines.append("issued: %s" % issued)
if expires is not None:
lines.append("expires: %s" % expires)
if reason is not None:
lines.append("reason: %s" % reason)
if row is not None:
lines.append("register_row: %s" % row)
return "\n".join(lines) + "\n"
def waiver_cases():
fails = []
today = datetime.date.today()
d = lambda n: (today + datetime.timedelta(days=n)).isoformat()
BEHIND = (["9.9.9", "9.9.8", "9.9.7"], "9.9.7") # released 9.9.9, golden 9.9.7: two behind
UNRECORDED = (["9.9.9", "9.9.7"], "9.9.8") # golden 9.9.8 has no heading (R-385)
def check(label, released_baked, wtext, want_rc, must=(), must_not=(), rows=("R-468",)):
env, tmp = synthetic_tree(released_baked[0], released_baked[1], wtext, rows)
try:
rc, out = run_gate_env(env)
finally:
shutil.rmtree(tmp, ignore_errors=True)
bad = [m for m in must if m not in out] + ["NOT expected: " + m for m in must_not if m in out]
if rc != want_rc or bad:
fails.append("%s: rc=%d (wanted %d) %s\n%s" % (label, rc, want_rc, bad, out[-900:]))
else:
print("%s ok" % label)
# BASELINE for the synthetic tree — behind, no waiver: convicted exactly as before this change.
check("CASE 5 (baseline, behind, no waiver -> 1)", BEHIND, None, 1,
must=("GOLDEN CURRENCY GATE FAILED", "2 release(s) behind"))
# E — a valid waiver turns the conviction into a LOUD advisory, exit 0.
check("CASE 6 (E: valid waiver, behind -> ADVISORY, 0)", BEHIND, waiver(d(0), d(13)), 0,
must=("ADVISORY", "WAIVED", "R-468", d(13), "2 release(s) behind", "OK (WAIVED)"),
must_not=("GOLDEN CURRENCY GATE FAILED",))
# F — the same waiver, expired: red again, and the reader learns it RAN OUT.
check("CASE 7 (F: expired waiver, behind -> 1, names the date)", BEHIND,
waiver(d(-10), d(0)), 1, must=("GOLDEN CURRENCY GATE FAILED", "EXPIRED on %s" % d(0)))
check("CASE 7b (F: waiver expired yesterday -> 1)", BEHIND, waiver(d(-10), d(-1)), 1,
must=("EXPIRED on %s" % d(-1),))
# G — a valid waiver never saves an UNRECORDED golden (R-385).
check("CASE 8 (G: valid waiver, UNRECORDED golden -> 1)", UNRECORDED, waiver(d(0), d(13)), 1,
must=("UNRECORDED", "DOES NOT COVER THIS", "R-385"))
# H — a waiver that tries to be permanent, or is malformed, is INCONCLUSIVE — never 0.
check("CASE 9 (H: 15-day waiver -> 2)", BEHIND, waiver(d(0), d(15)), 2,
must=("MALFORMED", "hard limit is 14"))
check("CASE 9b (H: exactly 14 days is the limit and PASSES)", BEHIND, waiver(d(0), d(14)), 0,
must=("ADVISORY",))
check("CASE 10 (H: absent expiry -> 2)", BEHIND, waiver(d(0), None), 2,
must=("MALFORMED", "expires"))
check("CASE 11 (H: unparseable expiry -> 2)", BEHIND, waiver(d(0), "next week"), 2,
must=("MALFORMED", "expires"))
check("CASE 12 (H: register row does not exist -> 2)", BEHIND, waiver(d(0), d(13), row="R-999999"),
2, must=("MALFORMED", "R-999999", "does not exist"))
check("CASE 12b (H: empty reason -> 2)", BEHIND, waiver(d(0), d(13), reason=""), 2,
must=("MALFORMED", "reason"))
# THE DECOY (R-421): a file at the right path saying only `expires` must not pass.
check("CASE 13 (DECOY: a file containing only the word `expires` -> 2)", BEHIND, "expires\n", 2,
must=("MALFORMED",), must_not=("ADVISORY", "OK (WAIVED)"))
# A malformed waiver is refused EVEN WHEN the golden is current — it must not lie there as cover.
check("CASE 14 (malformed waiver, golden CURRENT -> still 2)", (["9.9.9"], "9.9.9"),
waiver(d(0), d(30)), 2, must=("MALFORMED",))
# An expired waiver with a current golden: OK, but the expiry is NAMED.
check("CASE 15 (expired waiver, golden current -> 0 with a note)", (["9.9.9"], "9.9.9"),
waiver(d(-10), d(-1)), 0, must=("expired on %s" % d(-1), "golden currency gate OK"))
return fails
sys.exit(main())