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

SymptomMost likely causeJump to
Passkey completes, then HTTP 500 “internal server error”Pocket-ID account email unverified; oauth2-proxy rejects itLogin 500
Login screen shows invalid_requestapproval_prompt empty → falls back to forceinvalid_request
Login screen errors about PKCE / code_challengeclient requires PKCE, method not setPKCE
Page 502 Bad Gateway after authoauth2-proxy upstream points at its own loopback502
Logged in, but an action fails: Host key verification failed / Permission deniedOliveTin runs as olivetin, ssh key mounted at /root/.sshssh actions
TLS / cert won’t issue, or :443 conflictmissing bind, single-line tls, or reload-not-restartCaddy/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-proxy

Login: invalid_request

Set OAUTH2_PROXY_PROMPT=login in oauth2-proxy.env (empty approval_promptforce → 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 denied looks like a stale host key, but the real cause is the ssh dir being at the wrong HOME. The jamesread/olivetin image runs as USER olivetin (HOME=/home/olivetin) while the key is mounted at /root/.ssh.

In docker-compose.yml, the olivetin service needs:

  1. user: root (so /root/.ssh is the real HOME).
  2. the ssh volume mount without :ro (so accept-new host 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 true

The 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 :443 collides 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 (a reload is not enough).