bootstrap.js no longer creates host_theta-proxy / host_theta-jump as synthetic kind:'host' resources. Proxy and jump-host are containers running on the one real stack host, not machines of their own -- and jump-host resolves its SSH-reachable-hosts list from exactly kind:'host', so the mistake wasn't just conceptual, it could offer unreachable SSH targets. Their services now parent directly onto the stack host, like every other component. Installs seeded between 2026-08-05 and this release self-heal on the next ./setup.sh run: existing children are re-parented off the synthetic hosts and the now-empty synthetic hosts are removed. Validated live against a running instance carrying the exact bad state. Bumps submodules to sso-manager-node v1.30.2, proxy v1.35.1, jump-host v1.19.1. Also: README's architecture diagram + repo layout were stale (2-service view predating jump-host/OpenBao, 2 of 5 submodules listed); new docs/fixtures.md + docs/screenshots.md + bootstrap/seed-demo-users.sh for consistent, repeatable demo data and screenshot passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0113gCdnfSCuZr6xvPDxTo3D
8.2 KiB
title
| title |
|---|
| Canonical demo fixtures |
Canonical demo fixtures
The exact users, groups, and hosts that should exist on a stack used for screenshots or demos, so every future pass seeds the same data and a screenshot diff only shows what actually changed in the UI — not incidental differences in who/what happened to exist that day.
Persona: a single admin/power-user running theta42 across a big homelab and a small business — mix of self-hosted infra (Proxmox, Pi-hole, Plex) and office-y apps (invoicing, helpdesk, wiki) with real department structure.
Domain: laptop-dev.vm42.us (real public DNS pointing at this machine — see
"Domain" below). Update this doc if the domain ever changes again.
Users
| uid | Name | Department | Password | Notes |
|---|---|---|---|---|
schen |
Sarah Chen | Engineering | DemoPass123! |
DevOps lead |
dkim |
David Kim | Engineering | DemoPass123! |
Backend developer |
ppatel |
Priya Patel | Engineering | DemoPass123! |
Frontend developer |
mjohnson |
Marcus Johnson | Finance | DemoPass123! |
Finance manager |
lnguyen |
Linda Nguyen | Finance | DemoPass123! |
Bookkeeper |
erodriguez |
Emily Rodriguez | Support | DemoPass123! |
Support lead |
tbaker |
Tom Baker | Support | DemoPass123! |
Support tech |
jwilson |
James Wilson | Management | DemoPass123! |
Owner |
svc-monitoring |
— | service account | ServiceAcct!2024 |
Grafana/Prometheus scraping |
svc-backup |
— | service account | ServiceAcct!2024 |
Backup automation |
uidNumbers 5000–5009 in that order. Mail is <first>.<last>@laptop-dev.vm42.us
(service accounts use their uid, e.g. monitoring@laptop-dev.vm42.us).
Groups
groupOfNames, owned by cn=admin,..., member of the department's users:
engineering— schen, dkim, ppatelfinance— mjohnson, lnguyensupport— erodriguez, tbakermanagement— jwilsonapp_sso_service_account(built-in) — svc-monitoring, svc-backup
Seeding users + groups
cd theta-env
docker cp bootstrap/seed-demo-users.sh sso-manager:/tmp/seed-demo-users.sh
docker compose exec -T sso-manager bash /tmp/seed-demo-users.sh
Idempotent — re-running skips anything that already exists. If you add a
fixture below, add it to bootstrap/seed-demo-users.sh too and keep the two
in sync.
Proxy hosts
All under *.laptop-dev.vm42.us. setup.sh itself creates the first two
(sso, proxy) — everything else below is added by hand through Hosts → Add
host (Proxy UI, currently no seed script — see note at the bottom).
| Host | Target | Auth | Notes |
|---|---|---|---|
sso |
sso-manager:3001 |
— | created by setup.sh |
proxy |
127.0.0.1:3000 |
— | created by setup.sh |
jump |
jump-host:3002 |
— | created by setup.sh |
proxmox |
10.0.10.5:8006 (HTTPS) |
Basic — realm "Proxmox VE", users dkim, schen |
|
pbs |
10.0.10.6:8007 (HTTPS) |
Basic — realm "Proxmox Backup Server", user dkim |
|
grafana |
10.0.10.12:3000 + LB target 10.0.10.13:3000 |
SSO — group engineering |
load-balancing example |
nextcloud |
10.0.10.20:80 |
SSO — any authenticated user | empty allow-lists |
ha |
10.0.10.30:8123 |
Basic — realm "Home Assistant", user jwilson |
|
jenkins |
10.0.10.40:8080 |
SSO — group engineering |
|
gitea |
10.0.10.41:3000 |
Off (public) | has its own login |
plex |
10.0.10.50:32400 |
Off (public) | has its own login |
nas |
10.0.10.60:5001 (HTTPS) |
Basic — realm "Synology NAS", user jwilson |
|
pihole |
10.0.10.61:80 |
Basic — realm "Pi-hole Admin", user dkim |
|
wiki |
10.0.10.70:3000 |
SSO — any authenticated user | |
invoices |
10.0.10.80:8000 |
SSO — group finance |
small-business flavor |
helpdesk |
10.0.10.81:3000 |
SSO — group support |
small-business flavor |
Basic-auth passwords used: dkim:HomeLab!2024, schen:Engineering!24,
jwilson:HomeOwner!24.
Domain
CFG_DOMAIN=laptop-dev.vm42.us in setup.env, real public DNS (CNAME
through 718it.biz) that resolves back to this machine. CFG_LDAPS_HOST
is pinned to the LAN IP of the interface holding the default route
(ip route get 1.1.1.1), not just any active interface — this machine had
two (wifi + USB ethernet) and only one was actually externally reachable
through the existing port-forward/prod-proxy setup.
A production reverse proxy in front of this host handles TLS/ACME for
*.718it.biz-family domains (to avoid hitting Let's Encrypt's rate limits
re-provisioning a cert every time this dev stack rebuilds) — if a fresh
rebuild's Host records don't resolve correctly from the public domain right
after setup.sh, that's the layer to check, not this stack's own nginx/lua
routing. curl -sk -D - https://sso.laptop-dev.vm42.us/ from the host
machine is the fastest way to confirm whether the issue is server-side.
Known-good login shortcuts
Skip SSO's self-signed-cert dance entirely for admin/screenshot work — every app ships a local anti-lockout admin account for exactly this:
# SSO Manager admin (bootstrap account, uid "admin")
node -e "console.log(require('./config/sso-secrets.js').bootstrap.adminPass)"
# Proxy — username proxyadmin2
node -e "console.log(require('./config/proxy-secrets.js').auth.localAdminPass)"
# Jump-host — username jumpadmin
node -e "console.log(require('./config/jump-secrets.js').auth.localAdminPass)"
Ports (from setup.env — check it, these are operator-configurable):
SSO 3001, Proxy management UI 3010 (MGMT_PORT), Jump-host 3002.
A freshly-bootstrapped admin account hits the onboarding flow (accept ToS,
enter a DOB) before the rest of the UI is usable — expect that on a stack
that was just rebuilt from scratch.
Jump-host access (SSO Directory resource)
Jump-host's dashboard ("Hosts you can reach") is not driven by Proxy's
Host records — it resolves access via the SSO Manager's own Directory
(kind: host resources), filtered by the logged-in user's LDAP group
membership. This is a completely separate system from Proxy's HTTP-routing
hosts above; a Proxy host existing does not make it SSH-reachable through
jump-host.
For a dkim-can-reach-something screenshot, one Directory host resource was
added:
- Directory → Add Resource: name
proxmox-node, kindHost, IP10.0.10.5, parent resourcelocal (site_local). - Associated LDAP Groups →
site_local_host_proxmox-node_access→ Members → Add member →dkim(added the individual user directly, not theengineeringgroup — the resource's own auto-generated_accessgroup's member picker only offers individual users).
To reproduce: repeat those two steps for proxmox-node if it's missing, or
add more Directory host resources the same way for a richer "Hosts you can
reach" list.
To screenshot as a real fixture user (not the jumpadmin local
anti-lockout admin, whose "My hosts" list is always non-empty by virtue of
infra ownership, not a real access grant): log out, click "Log in with
Jump" on the login page, and sign in as dkim / DemoPass123! through the
real SSO flow. This exercises the actual OIDC redirect through
sso.laptop-dev.vm42.us — by this point in the session it worked cleanly in
the browser; if it doesn't (stale cookies/redirect loop from an earlier bad
state), see docs/screenshots.md §2 for the fallback.
What's not yet automated
Proxy hosts are still added by hand (no seed-demo-hosts.sh equivalent) —
the Proxy UI has no simple LDIF-style bulk-import path the way LDAP does, and
scripting it means either driving the browser or reverse-engineering the
session-cookie login flow for curl. If this list changes often enough to be
annoying, that's the next thing worth building — a small node script run via
docker compose exec proxy node ... calling the Host model directly,
mirroring how setup.sh's own step 7 registers the sso/proxy hosts.
Screenshot workflow
See docs/screenshots.md for the full screenshot-capture workflow
(save-to-disk, where each doc image lives, the app.modal.js browser-cache
gotcha). Once fixtures match this doc, only re-screenshot pages whose UI
actually changed since the last pass — the data itself shouldn't be the
reason a screenshot looks different.