Remote Access & Security
Reaching it from outside your LAN, safely.
The dashboard defaults to LAN-only, no auth — correct for a trusted home network with a REST API that otherwise has zero authentication. This covers what to do if you want more than that.
Recommended: Tailscale exercised, not just recommended
Tailscale creates a private mesh VPN between your devices. Your phone can reach the dashboard as if it were on your home LAN, from anywhere, without opening any port on your router or exposing anything to the public internet. Install it on the dashboard machine and your phone, sign into the same account on both, and you're done — Tailscale's own device auth is the access control.
This path is now real, not just documented: the dashboard is exposed via tailscale serve on a tailnet-only HTTPS route (real cert, no browser warnings, no port forwarding, no Funnel/public-internet exposure) — verified end-to-end with the PIN gate also enabled, so the tailnet acts as the network-level control and the PIN gate as the application-level one.
# One-time, per machine: expose a local port over the tailnet only (not Funnel)
tailscale serve --bg --https=8502 http://127.0.0.1:8502
tailscale serve status # confirm it's listed as "(tailnet only)", not "Funnel on"
If you want actual public access: DuckDNS + port forward
A materially different risk profile — your router forwards a port straight to this dashboard. Anyone on the internet who finds that hostname:port can reach the API.
Do not do this without the PIN gate enabled. Enable it first (Settings → Remote Access, or the API below), use a real PIN — not a placeholder like the ones used during this project's own testing — and pick a non-default external port.
curl -X POST http://localhost:8500/api/system/remote-auth/enable \
-H "Content-Type: application/json" -d '{"pin": "your-real-pin-here"}'
What the PIN gate actually protects against
- tested Blocks casual/automated discovery from controlling bulbs.
- tested Locks out an IP for 5 minutes after 5 wrong attempts — including a subsequently-correct PIN.
- tested Session expiry is enforced server-side, not just relying on the client's cookie expiry.
- tested v0.3.0: per-IP login rate limiting trips before the lockout counter, blunting slow distributed attempts that spread guesses across many PINs-per-IP under the lockout's radar.
- tested v0.3.0: every auth event — login success/failure, lockout, session revocation — is appended to a real audit log (
backend/data/auth_audit.log), one JSON line per event, never containing the PIN or a raw token. - tested v0.3.0: sessions can be listed and revoked individually or all at once (
/api/auth/sessions) — a stolen or forgotten session doesn't have to just expire on its own.
What it still doesn't
- Encrypt traffic on its own — plain HTTP if forwarded publicly without a reverse proxy. Tailscale Serve (above) sidesteps this entirely by giving you a real HTTPS cert for free; a DuckDNS+port-forward setup still needs Caddy or similar in front.
- Defend against an attacker who can read the PIN off the wire directly.
- Replace a real multi-user login system — one shared PIN. The new audit log gives a trail of what happened, not which person did it.
Still planned: a dedicated adversarial security-test phase
This round added real hardening (rate limiting, audit logging, session revocation) and a live 21-check verification pass against the actual running server — but that's a same-machine verification, not an adversarial one. The roadmap still has a phase where a separate agent actively attempts to break a real, publicly-exposed instance from the outside — brute-force timing analysis, session-token forgery, replay attacks, port-scan discovery timing, lockout-bypass attempts. Still scoped as its own phase since it needs a real deployed, exposed instance to attack meaningfully.
Full threat-model writeup and setup walkthrough: the complete doc. What was actually tested when this was built: iteration 004.