13 — the owner's requirements checked against the code, 2026-09-22
Agent V1. Read-only audit of holger origin/main 2333299 and private-holger-ops main fe6c0ea. The live box holger.rs was read only; it runs holger v0.6.10 (2333299). Nothing here was fixed.
The owner's words for the twin: "regarding holger we want the same behaviour" as gunnar. gunnar's list is workspace_nordisk/stinky-gunnar-requirements-2026-09-22.md.
Rule for every item: "done" means the fix is proven on the mock cloud first (mock-upcloud / mock-upcloud-kvm, SeaBIOS, the UpCloud shape), and only then on anything real.
holger.rs is one box today. A twin server costs money and is the owner's decision. The items below are about the code, so it is ready when the owner says yes.
Verdict per requirement
| # | requirement | verdict | why, in one line |
| H1 | the twin is a restore source (config + data), also after a failed reshape | PARTLY | replicate_estate (server/lib/src/replication.rs:686) and follow_once (twin.rs:396) work between two data roots on ONE host. There is no network transport, nothing on a timer, the artifact store is not moved, and there is no reshape fallback. |
| H2 | the twin only receives and stores; indexing is done on main at recharge | NOT MET | wire_holger builds a twin like a primary. The job queue opens and starts workers on every role (server/lib/src/lib.rs:3222-3242), and jobs is in ESTATE, so a twin would resume the primary's jobs. |
| H3 | backfill a missed entry, never skip a gap; main keeps entries until the twin has them | NOT MET | Replication is a full idempotent scan. There is no sequence, no watermark, no gap detection, and retention.rs knows nothing of a twin. |
| H4 | config lives in the twin too | PARTLY | holger.ron is in ESTATE. admin-token is deliberately not replicated (copied by hand, front plan step 12). Local only, with no wire. |
| H5 | one ISO; the role is elected at bootstrap and recorded | PARTLY | elect (twin.rs:621) writes holger-role.ron once. AdminService.BecomeTwin is at grpc/services.rs:1196, and the recipe refuses a twin row. The HOLGER_ROLE/HOLGER_PRIMARY environment (twin.rs:423-500, lib.rs:342-357) and the RON role: key can still make a twin that was never elected. |
| H6 | the weakest box is the twin, decided once by xtask; the keep-up floor only warns | PARTLY | The floor warns (twin.rs:247, test a_short_twin_is_named_and_never_refused). private-holger-ops xtask/src/front.rs:273-298 only prints instructions. No xtask measures the boxes, and cores/RAM are on no holger endpoint. WireGuard between the two boxes is proposed in HOLGER-PLAN §5 "W22 — a paired on-prem holger over WireGuard" (2026-09-22, design only). |
| H7 | main and twin trust each other both ways | NOT MET | There is no network link and no peer identity (02 S2-9: "a peer is any admin"). |
| H8 | WireGuard + mTLS (TLS 1.3), signed medium, audit that survives a restore | PARTLY | Signed medium: MET through korp-installer (?13). Audit: holger-audit is in ESTATE, lib test only. No WireGuard between VMs. exposed/tls.rs:116 allows TLS 1.2 and 1.3, and client certificates are optional (:129 allow_unauthenticated). |
| H9 | UpCloud trait + mock as pre-test | NOT MET on main | agent/mock-estate e565609 and agent/one-upcloud-client 7561e88 are unmerged. On them only orphans goes through upcloud_api. Terraform reaches the mock by base URL, and nothing uses mock-upcloud-kvm. |
| H10 | immutable box (?25) | MET as a ruling, NOT PROVEN | ?25 answered 2026-09-20 (HOLGER-PLAN.md:82): no self-update, and there is no UpdateService/ImportBundle in proto/holger.proto. No test pins it, and the live box has its binary swapped in place. |
| H11 | flow SVG in the console with visitor counts, no raw IPs | MET | 2333299 (2316d9c + 93df658). no_client_address_reaches_the_rendered_picture (flow.rs:2897). Verified live: /-/flow.svg shows client 5cebb6db and "consumers today", and the only addresses are 0.0.0.0/127.0.0.1. |
| H12 | the rest of HOLGER-PLAN | open | See item 9. |
Gaps, highest priority first
1. There is no network link between main and twin, and no mutual trust (H1, H4, H7).
-
Owner: "the twin is a replika for worst case scenario, when main dies , it can be feeded from twin … regarding holger we want the same bahior"; "how does twin main ui trust each other ?"
-
Evidence:
PrimaryView's only implementation isLocalPrimary(server/lib/src/twin.rs:338,354), which reads a local data root.holger-server estate --from --to(server/cli/src/main.rs:229) copies between two paths.- Nothing calls
follow_onceon a timer. - Plan rows: W11 (§3), §4 "STILL OWED" (
HOLGER-PLAN.md:2040), ?6.
-
Done means:
- A gRPC
PrimaryViewover mTLS (TLS 1.3 only, client certificate required), paired the way gunnar pairs (the twin mints its key and the primary's issuer signs it; the twin pins the issuer and verifies the primary's signed head). - A follower on a timer.
- The artifact store included.
- Proven on the mock with two boxes installed from one medium.
- A gRPC
2. The twin processes: its job queue runs on every role (H2).
- Owner: "the twin shouldnt do indexin or prcessing , only recive and save data."
- Evidence:
server/lib/src/lib.rs:3222-3242opensJobQueueand callsqueue.start(...)regardless of role, and the durablejobsqueue is inESTATE(replication.rs, around line 529). -
Done means:
- A twin starts no job workers and no indexers.
- Main re-indexes at recharge.
- A test fails if a twin starts a worker.
- Proven on the mock.
3. There is no sequence, so no gap detection and no retention guard (H3).
- Owner: "if it discovers package 11 arrives but no packate 10 exits it must fetch packege 10"; "regarding the twin it will eventualy catch up ?!" → "yes check it and add a test".
- Evidence:
replicate_estateis a full content-id scan. There is no watermark.server/lib/src/retention.rshas no twin term. -
Done means:
- Replication carries an ordered position. The twin fetches from its own position + 1 and never skips.
- Main refuses to drop what the twin has not confirmed, with a named override written to the audit trail, and raises a size alarm.
- A test holds the twin back, restarts main, releases the twin, and sees it catch up.
- Proven on the mock.
4. The role can still come from the environment or the config (H5).
-
Owner: "it MUST use the SAME iso"; the election is "a bootstrap thing from xtask".
-
Evidence:
HOLGER_ROLE/HOLGER_PRIMARYare read at boot (server/lib/src/twin.rs:423-500,lib.rs:342-357).effective_rolefalls back env → RONrole:, so a role can exist with no election record.- Stale text:
holger-recipe/src/rules.rs:685("--param role=twinon holger-installer"), thelib.rs:342comment "THE ROLE IS AN INSTALL PARAMETER", and the test namesthe_role_is_an_install_parameter_of_the_one_medium(twin.rs:831) anda_medium_with_no_role_parameter_installs_what_it_always_did(:809). - A primary never records an election.
- Plan row: ?49 in §4 (
HOLGER-PLAN.md:2047) does not mention these carriers.
-
Done means:
- The role is read only from
holger-role.ronwritten by the election or BecomeTwin. The env and RON carriers are removed or refused by name. - The primary records its side.
- Two boxes from one medium are proven on the mock.
- The role is read only from
5. There is no xtask election (H6).
-
Owner: "the weakest machine must become the twin. PERIOD"; "IT must not CHANGE role later".
-
Evidence:
- private-holger-ops
xtask/src/front.rs:273-298(step 10) prints a hand instruction. AdminService.Capacityreports disk only; cores and RAM are on no endpoint.- The test
two_boxes_from_one_medium_and_the_weaker_is_told_what_it_is(twin.rs:1059) picks the box by hand.
- private-holger-ops
-
Done means:
cargo xtask elect twin --applyin private-holger-ops measures both fresh boxes (cores, RAM,/data, disk speed), picks the weakest, calls BecomeTwin and writes the record once.- The keep-up floor stays a warning.
- Proven on the mock.
6. No WireGuard between VMs; TLS is not 1.3-only and not mutual (H8).
-
Owner: "so wireguard+mtls DO CHECK IT !!!"; "can yo upick a fast chiper for the mtls".
-
Evidence:
server/lib/src/exposed/tls.rs:116with_safe_default_protocol_versions()accepts TLS 1.2.:129allow_unauthenticated()makes client certificates optional.server/cli/src/appliance.rs:1-40is only the appliance-to-hub ceremony.- Plan: only ?13 (signed medium) exists; there is no row for WireGuard or mTLS between the VMs.
-
Done means:
- The replication listener is TLS 1.3 only, AES-128-GCM first (as gunnar
gunnar-grpc/src/lib.rs:372-404), with a client certificate required and bound to the tunnel address. - WireGuard between main and twin.
- Proven together on the mock, with byte counters showing the traffic crossed the tunnel.
- The replication listener is TLS 1.3 only, AES-128-GCM first (as gunnar
-
Proposed: HOLGER-PLAN §5 "W22 — a paired on-prem holger over WireGuard" (2026-09-22) — enrolment after gunnar's hub +
tunnel enrol, the mirror door alone bound to wg0, andvia wg0on the flow edges. Design only, no code.
7. The UpCloud trait and the mock are not on main (H9).
-
Owner: "what if we ue the fake upcload provider to test out Holgers xtask/terraform setup ?"; "the user of the trait must belive he is on real upcload , SEABIOS !!!".
-
Evidence:
- private-holger-ops
mainhas neither. Branchesagent/mock-estatee565609andagent/one-upcloud-client7561e88(+2358 lines:xtask/src/{mock,upcloud,plan_guard}.rs,xtask/tests/mock_estate.rs) are unmerged. - A mock terraform run is recorded in
/home/rickard/holger-ops-t7/terraform.mock/MOCK-RUN.txt(3 servers, 2 storages, 3 firewall rule sets). xtask/tests/terraform_gate.rsis red.
- private-holger-ops
-
Done means:
- Both branches merged. Every UpCloud call in the xtask goes through
upcloud_api::connect. - Terraform is run against the mock before any real apply, and real
--applyrefuses without a mock receipt for the same commit. - A holger medium boots on mock-upcloud-kvm (SeaBIOS).
- Both branches merged. Every UpCloud call in the xtask goes through
8. The immutable box is a ruling without a test (H10).
- Evidence: ?25 answered (
HOLGER-PLAN.md:82). No test asserts the absence of an update path. The live box is upgraded by swapping/usr/local/bin/holger-serverin place (memoryholger-rs-is-one-box). -
Done means:
- A recipe or proto test fails if an update or import RPC appears.
- The appliance medium is installed on the mock and upgraded only by a new image.
9. HOLGER-PLAN open work (H12), for dispatch.
- §1 open questions: ?3 (HIGH) metering base branch; ?41 (HIGH) two nornir defects; ?47 (HIGH) medium carrying the admin token; ?48 (HIGH) RON path
/var/lib/holgervs/data; ?49 gRPC "listening" before bind; ?28 decommission verb; ?7holger-remotein MVP orfull; ?4 apex owner; ?11 half rename; ?31/?32 rivals; ?35 musl on the public box; ?36 GLIBC 2.39 headroom; ?38Methodnewtype; ?42 ENOSPC bench needs root; ?43 Gitea rival; ?44 GPU-refuse default; ?45vector-gpuin dwarves. - Plan hygiene: ?16 and ?17 are still listed open but are answered in §4 (
:1407,:1447). The id ?49 is used twice (the bind bug in the table, and the twin role ruling at:2047). - §2: W0b rivals current; W16 held until znippy 0.9.16 is published.
- §3 blocked: W7, W7b, W8, W8b, W8c, W11.
- Also found:
audit/query.rs:355truncates a bare IPv6 address (2001:db8::42→2001:db8:), so two clients can be drawn as one puller in the flow picture. Reported 2026-09-22, not fixed. - Checkout hygiene: the local
/home/rickard/git/holgeris onmainatda800f0, 5 commits behindorigin/main, with an untracked file named,.