The passkey admin panel (admin.levandor.io) won’t log in, or logs in but the tap-to-run actions fail. Chain: Caddy → oauth2-proxy → OliveTin → ssh. Full architecture: 2026-08-15-admin-portal-passkey-olivetin.
Where it lives
All on telep-mainframe (
192.168.1.123). Stack:/home/levander/admin-portal/(docker compose). Caddy is a host service (systemctl), oauth2-proxy + OliveTin + Pocket-ID are containers.
Symptoms → cause
| Symptom | Most likely cause | Jump to |
|---|---|---|
| Passkey completes, then HTTP 500 “internal server error” | Pocket-ID account email unverified; oauth2-proxy rejects it | Login 500 |
Login screen shows invalid_request | approval_prompt empty → falls back to force | invalid_request |
| Login screen errors about PKCE / code_challenge | client requires PKCE, method not set | PKCE |
| Page 502 Bad Gateway after auth | oauth2-proxy upstream points at its own loopback | 502 |
Logged in, but an action fails: Host key verification failed / Permission denied | OliveTin runs as olivetin, ssh key mounted at /root/.ssh | ssh actions |
TLS / cert won’t issue, or :443 conflict | missing bind, single-line tls, or reload-not-restart | Caddy/TLS |
Login: 500 / unverified email
docker compose logs oauth2-proxy shows email in id_token (...) isn't verified. A self-hosted Pocket-ID account has email_verified=0.
cd /home/levander/admin-portal
# ensure this line is in oauth2-proxy.env:
# OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL=true
docker compose up -d --force-recreate oauth2-proxyLogin: invalid_request
Set OAUTH2_PROXY_PROMPT=login in oauth2-proxy.env (empty approval_prompt → force → Pocket-ID invalid_request), then docker compose up -d --force-recreate oauth2-proxy.
Login: PKCE
Pocket-ID’s OIDC client requires PKCE. Set OAUTH2_PROXY_CODE_CHALLENGE_METHOD=S256 in oauth2-proxy.env and recreate the container.
502 after auth
The upstream must be the compose service name, not loopback (loopback = oauth2-proxy’s own container):
grep OAUTH2_PROXY_UPSTREAMS /home/levander/admin-portal/oauth2-proxy.env
# must be: http://olivetin:1337 (NOT http://127.0.0.1:1337)ssh actions fail
Misleading symptom
Host key verification failed/Permission deniedlooks like a stale host key, but the real cause is the ssh dir being at the wrong HOME. Thejamesread/olivetinimage runs asUSER olivetin(HOME=/home/olivetin) while the key is mounted at/root/.ssh.
In docker-compose.yml, the olivetin service needs:
user: root(so/root/.sshis the real HOME).- the ssh volume mount without
:ro(soaccept-newhost keys persist).
cd /home/levander/admin-portal
docker compose up -d --force-recreate olivetin
# verify from inside the container:
docker compose exec olivetin ssh -o StrictHostKeyChecking=accept-new 192.168.1.123 trueThe pubkey olivetin/ssh/id_ed25519.pub is already authorized on mainframe + router (root@100.69.112.32). It is NOT yet on the Pi (192.168.1.200) — actions targeting the Pi will fail until the Kraken rig is back online and the key is added (2026-08-12-krakensdr-doa-rig).
Caddy/TLS issues
Host service, /etc/caddy/Caddyfile, admin.levandor.io block. Three footguns:
systemctl status caddy
caddy validate --config /etc/caddy/Caddyfile- Block must have
bind 192.168.1.123— else:443collides with Tailscale (100.115.209.87:443). - TLS must be multi-line (
tls {⏎dns cloudflare {env.CF_API_TOKEN}⏎}); single-line fails to parse. - Cloudflare token in
/etc/caddy/cloudflare.env(root, 600), loaded via systemd drop-in. - After cert/env changes:
systemctl restart caddy(areloadis not enough).
Deeper causes / links
- Full architecture, env table, and the two gotchas in depth: 2026-08-15-admin-portal-passkey-olivetin.
- The Pi target being offline: host-offline · krakensdr-df.
- Do not confuse this Caddy with the
*.telep.lansplit-horizon Caddy on the same box (:80,auto_https off): 2026-08-15-telep-lan-split-horizon-caddy · service-unreachable.
Related
- telep-mainframe · telep-router
- 2026-08-15-admin-portal-passkey-olivetin — the design/debugging note
- runbooks-index — all homelab runbooks