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
This commit is contained in:
2026-09-13 10:14:37 +02:00
parent 4b2e5608c2
commit ae59c31a84
62 changed files with 2607 additions and 220 deletions
+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.**
+222 -139
View File
@@ -1,181 +1,264 @@
# 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` | this push (two commits: documents + gate, then the register compression) |
| 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.
## 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`; nothing deleted). OPEN-ITEMS.md bytes: see the compression commit.
## 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. **`POST /api/stacks/<app>/restart` recreated only the changed service** (`bookstack-db` got a new
ID; the `bookstack` app container kept its 4-hour uptime) — it is `compose up -d`, as
`09-update-architecture.md` §1.3 records as a chosen behaviour. **NOT-A-FINDING: documented
design; the message "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" "-"
@@ -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
@@ -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)
File diff suppressed because one or more lines are too long
@@ -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())