Headplane Documentation
repository·main·Indexed 25 days ago
https://github.com/tale/headplaneA web-based management interface for Headscale, the self-hosted version of Tailscale. Headplane provides a graphical UI for managing Wireguard-based VPN nodes, ACLs, tagging, DNS settings, and OIDC authentication. It includes a server-side architecture built with Node.js and React Router, a background agent (hp_agent) for host information, a WASM-based SSH module (hp_ssh), and a dedicated health check tool for Docker deployments.
What's inside Headplane
- Headplane is a web-based UI designed to manage Headscale instances. While Headscale provides the self-hosted control server implementation for Tailscale-compatible VPN networks, it lacks a native web interface. Headplane bridges this gap by providing a feature-rich dashboard for administering nodes, networks, and ACLs, aiming to replicate the official Tailscale user experience.
Overview of Headplane
mainHeadplane is a feature-complete web UI for Headscale, the self-hosted version of Tailscale. It provides a graphical interface to manage Headscale instances, replicating many functionalities found in the official Tailscale dashboard.
Key features include:
- Machine management: Manage node expiry, network routing, names, and owners.
- ACL & Tagging: Configure Access Control Lists and tags for network enforcement.
- OIDC Support: Use OpenID Connect as a login provider.
- DNS & Settings: Edit DNS settings, automatically provision Headscale, and configure Headscale settings.
Understand the Headplane server-side architecture
mainThe Headplane server-side code resides in
app/server/and runs exclusively in the Node process, never in the browser. The architecture is split into two primary entry points depending on the environment:- Development: Uses
app.tsas the entry point, which is consumed by the Vite plugin duringreact-router dev. - Production: Uses
main.tsas the SSR build input. This file bundles intobuild/server/index.jsand handles HTTP(S) server binding, static asset serving, and basename redirects.
Key modules include:
config/: YAML loading and schema.db/: Drizzle SQLite client.headscale/: Headscale REST API client.web/: Authentication and RBAC.hp-agent.ts: Agent process manager.
- Development: Uses
Understand User Matching Strategy
mainHeadplane links OIDC identities to Headscale users using a two-step process:
- Subject Match (Primary): Headplane compares the resolved OIDC
sub(or configuredsubject_claims) against theprovider_idstored in Headscale. - Email Match (Fallback): If the subject doesn't match, Headplane compares the email from the OIDC
userinfoendpoint against the email on the Headscale user record.
Important Notes:
- Local Users: If Headscale uses local users (not OIDC), automatic matching won't work. Users will be prompted to manually select their Headscale user during onboarding.
- Different Clients: If Headscale and Headplane use different OIDC clients,
subvalues might differ. In this case, email matching is required. Ensure your IdP provides anemailclaim.
- Subject Match (Primary): Headplane compares the resolved OIDC
Key features of Headplane
mainHeadplane provides advanced management capabilities for your Headscale instance, including:
- Machine Management: Control node expiry, network routing, names, and ownership.
- Access Control: Configure Access Control Lists (ACLs) and tagging for network enforcement.
- Authentication: Support for Single Sign-On (SSO) via OpenID Connect (OIDC).
- DNS Management: Edit DNS settings and automatically provision Headscale.
- Headscale Configuration: Directly edit Headscale's internal settings through the UI.
- Remote Access: Web-based SSH access to managed nodes.
Add a new service to Headplane
mainFollow these steps to implement a new service:
- Define the interface in a new file under
app/server/<name>/. - Write the factory function that takes explicit dependencies and returns the interface. Keep state in closure variables.
- Add lifecycle hooks (
start/stop/reload/invalidate) if the service has background work or cached state. - Use
Result<T, E>for operations that can fail. Define a typed error with acodefield. - Wire it in
createAppRuntime()inserver/index.ts. - Write tests that create isolated instances using the factory (no module mocking required).
- Define the interface in a new file under
Prerequisites for Browser SSH
mainTo use Browser SSH in Headplane, ensure the following requirements are met:
- Headscale Version: Use Headscale
0.28.xor0.29.2and newer. (Note: Versions0.29.0beta through0.29.1are broken due to a/ts2021WebSocket routing regression). - Tailscale SSH: Target nodes must have Tailscale SSH enabled via
tailscale up --ssh. - Authentication: Users must be logged in via OIDC. API key logins are not supported for Browser SSH.
- Headplane Agent: The Headplane Agent must be enabled and configured.
- WASM Assets: Headplane must be built with WASM support (assets
hp_ssh.wasmandwasm_exec.jsmust be present).
- Headscale Version: Use Headscale
Override configuration using environment variables
mainYou can override configuration file settings using environment variables. These overrides are merged after the configuration file is loaded and take precedence.
Pattern:
HEADPLANE_<SECTION>__<KEY_NAME>Enabling Overrides: This functionality is NOT enabled by default. To enable it, you must set:
HEADPLANE_LOAD_ENV_OVERRIDES=true
Setting this also instructs Headplane to load the relative
.envfile into the environment.Examples:
- To override
headscale.url, useHEADPLANE_HEADSCALE__URL. - To override
server.port, useHEADPLANE_SERVER__PORT. - To override
oidc.client_secret, useHEADPLANE_OIDC__CLIENT_SECRET.
Warning: This is only for configuration overrides. You cannot use this mechanism for general environment variables like
HEADPLANE_DEBUG_LOGorHEADPLANE_CONFIG_PATH.Run Headplane
mainStart Headplane using
pnpm startor by running the build entrypoint directly with Node.js.Important: Ensure the
build/directory exists relative to where the command is run, otherwise frontend assets will not be found.Configuration Path: Headplane defaults to
/etc/headplane/config.yaml. To use a custom path, set theHEADPLANE_CONFIG_PATHenvironment variable.Login: To log in, you must provide a Headscale API key. Generate one using:
headscale apikeys create --expiration 90dpnpm start # OR node build/server/index.jsConfigure Client IP Checks for Proxy Authentication
mainBy default, Headplane uses the socket address connected to it to verify the proxy's identity. If
allowed_cidrsis omitted, Headplane trusts only localhost.To verify the original client IP (the user's IP) instead of the proxy's IP, you must use
ip_header. Whenip_headeris set, Headplane follows this logic:- It checks if the direct socket peer (the proxy) matches
server.proxy_auth.trusted_proxy_cidrs(defaults to localhost). - If it matches, it reads the specified
ip_header(e.g.,X-Forwarded-For). - It then checks the first IP found in that header against
server.proxy_auth.allowed_cidrs.
server: proxy_auth: enabled: true ip_header: "X-Forwarded-For" trusted_proxy_cidrs: - "127.0.0.1/32" allowed_cidrs: - "10.0.0.0/8"- It checks if the direct socket peer (the proxy) matches
Run Headplane in production
mainIn production, the application is bundled into
build/server/index.js. You can run the server using Node.js:node /app/build/server/index.jsTo enable TLS/HTTPS, pass a
tlsobject containingkeyandcertto thestartHttpServerfunction within the bootstrap logic.Deploy Headplane
mainFor detailed installation and deployment instructions, please visit the official website at https://headplane.net.