44a9124ef57916d4e592335604829ee7c56854c6
The in-app Tailscale needs one thing from the platform: a way to turn an authenticated
Officer session into a Headscale pre-auth key, so the phone registers itself instead of
someone pasting a key by hand. The VPN's control and data planes talk directly to
Headscale — never through /api — so this is not a proxy and should not become one.
Headscale itself runs on a separate host, managed manually. The platform consumes
HEADSCALE_URL (also returned as controlUrl) and HEADSCALE_API_KEY, both from the host
env. Neither is set yet, which is why an unconfigured instance answers 503 rather than
crashing — Headscale is being stood up in parallel.
The response is `{ controlUrl, authKey }` exactly, because enrollVpn() in
@officer/core/officer-net.ts reads those two fields; changing the shape means changing the
app.
Two things the spec's sketch does not do:
- The `user` field changed meaning across Headscale versions — a name on <=v0.22, a
numeric id on v0.23+ — and we cannot see which one is running from here. So it resolves
the id via /api/v1/user and tries that first, falling back to the name. Whichever the
live server accepts wins, and neither version needs a config flag.
- Upstream calls carry a 10s timeout, and upstream error bodies are logged but never
returned to the client: that is an admin API and its errors are descriptive.
The standalone app's origin follows the platform's own convention rather than the spec's
literal: every other app origin is an env var with a scope rule, so this one is
OFFICER_TAIL_ORIGIN (set in .env on this host), restricted to /api/auth + /api/vpn the way
OffVault is restricted to /api/auth + /api/vault. The OffTail tile embedded in the main
Officer app needs nothing — it reuses OFFICER_APP_ORIGIN.
Not implemented: the optional GET/DELETE /api/vpn/devices. They are not needed for a first
connection and are better written against a running Headscale.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description
No description provided
42 MiB
Languages
TypeScript
90.9%
Shell
4.7%
JavaScript
4.1%
CSS
0.2%
HTML
0.1%