Structural repair: - 13 pages were the same document twice (each exactly 2x its HEAD size), a second frontmatter block whose closing fence had been glued onto a body line by a frontmatter rewrite that re-read its own output. Un-doubled by cutting at the second frontmatter and keeping the copy whose body is a superset; the orphan census reports 0 lost tokens against HEAD. - log.md: one entry per date, bare `## YYYY-MM-DD` headings, newest first — 66 dated headings merged into 34, titles demoted to bold leads, every content line verified present. The file had been descending at the head and ascending at the tail. Ownership moves: - New `LLM/frank-medium.md` is the locator for the retired Dankserver endpoint: era windows quoted from each era page's own banner, no numbers restated, and the two unresolved date conflicts recorded instead of silently resolved. `frank-archon.md` links to it instead of restating the pre-rename window. - The four Pipeline-Parallelism stubs (frontmatter only, no body) are gone; `Multi-Node Runtimes.md` owns runtime construction, launch, mixed-vendor verdicts and cross-split spec decode, and the four links that pointed at the stubs were repointed at it or at the drafter page. - Authentik's three stub pages fold into `Authentik.md`, with the process detail and the outpost reference split out so the parent stays under the length hint. Verified corrections (host census + live probe, 2026-10-19): - The MXFP4 checkpoint two pages called the "on-disk rollback" is gone: /home/dank/models/ holds StillDeadcode, rad and tpl.jinja. Stated once, on the era page that owns that payload; the swap page now links to it. - frank-medium-radiance-mxfp4: the 4.24/7 accept rate belongs to the DFlash2 arm measured on the FP8 target, not to the MXFP4 era; the 3-family soak run census is kept once (155 requests) instead of twice (~120 and 155). - Fleet shapes re-confirmed against the live endpoints before touching any page: 3x200000 fp8 KV, 2x200704, 2x262144. Index rows are regenerated, not hand-carved. Net -1,849 vault lines.
6.3 KiB
type, description, tags, timestamp
| type | description | tags | timestamp | |||
|---|---|---|---|---|---|---|
| reference | The Playwright suite that drives the live fleet through a real browser, its project set, and the FrankBook conditional. |
|
2026-10-18 |
E2E Suite — homepage + guard chain (Playwright)
tests/e2e/ drives the live fleet through a real browser: fresh contexts
hit https://www.franksdank.cloud, log in through the authentik two-stage
flow, and crawl every service link through the Caddy → Anubis → outpost
guard chain. It is the only test in this repo that exercises the chain as a
user sees it; the bats seams assert config shape, not behavior.
Layout
| Path | Role |
|---|---|
playwright.config.ts |
five projects (below), real-browser UA, trace on failure |
specs/00-auth.setup.ts |
setup project: two real authentik logins, saves storage states |
specs/10-homepage.spec.ts |
user panel set + open links derived from services.json (registry-derived — no hardcoded service lists) |
specs/11-admin.spec.ts |
admin project: admin grid + /admin/ RBAC (403 for standard user, members-only 200) |
specs/12-crawl.spec.ts |
every user open link reaches its real app through the guard chain |
specs/19-frankbook.spec.ts |
frankbook project: walks the six admin links that terminate on FrankBook-Pro (192.168.0.112) — conditional, see below |
specs/13-sso.spec.ts |
direct guarded-vhost visits resolve silently with ONLY the homepage session (single-login enforcement) |
specs/14-guard.spec.ts |
guest project, no storage state: unauthenticated traffic lands on the IdP login |
specs/15-unauth-fetch.spec.ts |
guest project: a wget-style client must never receive application bytes from a guarded service — it asserts on leakage (app-code markers), not on status code |
specs/16-marreta.spec.ts |
regression guard for the 2026-09-12 incident: the marreta tile resolving to the authentik login flow because the marreta-proxy ProxyProvider had no linked Application |
specs/17-hermes-vhost.spec.ts |
hermes.franksdank.cloud fronts a host-networked container, so the vhost upstream is the host LAN address; guards the 2026-09-16 loopback-bind 502 that the rest of the suite stayed green through |
specs/18-openwebui-sso.spec.ts |
ai.franksdank.cloud completes its OIDC round-trip silently with only the homepage session |
lib/login.ts |
akLogin helper (two-stage flow, shadow-DOM locators) |
lib/env.ts |
loads .env.local, exports REAL_UA |
tests/e2e_scaffold.bats |
bats seam: structure, placeholder-only example env, every spec matched by a playwright project (tests/e2e_scaffold.bats:51), and no credentials/auth state tracked. Never drives the fleet |
Projects and auth
- setup — logs in as
qa-e2eandqa-e2e-admin(fleet-managed IdP test users; the admin is ahomepage-adminsmember), writes.auth/qa-e2e{,-admin}.jsonstorage states. - standard — 10/12/13/16/17/18 with the user state
(
tests/e2e/playwright.config.ts:33). - admin — 11 with the admin state.
- guest — 14/15 with an empty state.
- frankbook — 19 with the admin state. Excluded from every default run: the
*arr/qBittorrent links terminate on FrankBook-Pro, which sleeps when idle, and a
sleeping Mac mini must never fail an unrelated deploy. The project joins only
when
E2E_INCLUDE_FRANKBOOK=1(set byscripts/deploy.shwhen a frankbook trigger path changed:tests/e2e/specs/19-frankbook.spec.ts,frankbook-pro/**,shared/homepage/services.json, or thedankclaw/Caddyfileserving the arr vhosts) or when an explicit--projectargument names it. An11-adminedit that touches the arr links must therefore be paired with a19-frankbookrun against an awake host before it is trusted live.
Credentials live in tests/e2e/.env.local (git-ignored; placeholders in
.env.local.example). The scaffold bats seam fails the commit if creds or
auth states are ever tracked.
Run
tests/e2e/run.sh # default set — frankbook project excluded
E2E_INCLUDE_FRANKBOOK=1 tests/e2e/run.sh # default set + the frankbook walk
cd tests/e2e && npx playwright test # ALL projects, frankbook included
Requires LAN reachability of www.franksdank.cloud (Pi-hole DNS or public).
The suite is not part of ./tests/run.sh — it needs the live fleet and
real IdP credentials. It is a deploy gate: scripts/deploy.sh runs it and
aborts with DEPLOY INCOMPLETE: e2e gate FAILED when it does not pass
(scripts/deploy.sh:278-285).
Gotchas (all paid for live)
- Fresh context per login. Reusing a half-finished flow poisons
authentik's
pending_userstage and the second login hangs. The setup spec runs each login in its own context. - Lit stage re-render race. authentik's flow executor can re-render the
password stage right after mount, wiping a programmatic
fill; the submit then POSTs an empty form ("This field is required").akLoginfills, verifies the value survived, submits via Enter, and retries. - Headless UA = bot. Anubis denies obvious headless UAs; the config
overrides
userAgentwith a real Chrome UA (REAL_UA). - Cold Anubis PoW. The first challenge in a fresh context can outlive default assertion budgets; title asserts carry 60s budgets.
- Solve-once cookie. Since Anubis v1.27.0 +
COOKIE_DOMAIN(see Anubis), one PoW per fresh context covers every guarded vhost — crawl and SSO specs complete in ~0.5 s per vhost instead of a PoW each. - Apps navigate too — and may leave.
12-crawlreturns to the homepage between links, and Plex Web (signed out in theqa-e2eprofile) runs its own sign-in flow: it redirects the top frame, so Chromium cancelled ourgoto("/")withnet::ERR_ABORTED(once as a 420 s timeout, once at 5 s on the same link), and the same redirect then lands the browser onapp.plex.tv— Plex's hosted surface for a session-less client — which broke thehostname == targetlaw. Two narrow answers:backHome()re-issues the navigation on that one error class, and the landing check takes an explicit hand-off list (HANDOFFS) instead of a single host. The guard contract is untouched: challenge cleared, no authentikuidField, status < 500, and every host NOT in the list still has to land on its own vhost.