Headplane Documentation

repository·main·Indexed 25 days ago

https://github.com/tale/headplane

A 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.

Tokens
19K
Snippets
40
Records
90
Agent score
83%

What's inside Headplane

  1. Overview of Headplane and Headscale

    main
    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.
  2. Overview of Headplane

    main

    Headplane 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.
  3. Understand the Headplane server-side architecture

    main

    The 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.ts as the entry point, which is consumed by the Vite plugin during react-router dev.
    • Production: Uses main.ts as the SSR build input. This file bundles into build/server/index.js and 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.
  4. Understand User Matching Strategy

    main

    Headplane links OIDC identities to Headscale users using a two-step process:

    1. Subject Match (Primary): Headplane compares the resolved OIDC sub (or configured subject_claims) against the provider_id stored in Headscale.
    2. Email Match (Fallback): If the subject doesn't match, Headplane compares the email from the OIDC userinfo endpoint 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, sub values might differ. In this case, email matching is required. Ensure your IdP provides an email claim.
  5. Key features of Headplane

    main

    Headplane 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.
  6. Add a new service to Headplane

    main

    Follow these steps to implement a new service:

    1. Define the interface in a new file under app/server/<name>/.
    2. Write the factory function that takes explicit dependencies and returns the interface. Keep state in closure variables.
    3. Add lifecycle hooks (start/stop/reload/invalidate) if the service has background work or cached state.
    4. Use Result<T, E> for operations that can fail. Define a typed error with a code field.
    5. Wire it in createAppRuntime() in server/index.ts.
    6. Write tests that create isolated instances using the factory (no module mocking required).
  7. Prerequisites for Browser SSH

    main

    To use Browser SSH in Headplane, ensure the following requirements are met:

    • Headscale Version: Use Headscale 0.28.x or 0.29.2 and newer. (Note: Versions 0.29.0 beta through 0.29.1 are broken due to a /ts2021 WebSocket 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.wasm and wasm_exec.js must be present).
  8. Override configuration using environment variables

    main

    You 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 .env file into the environment.

    Examples:

    • To override headscale.url, use HEADPLANE_HEADSCALE__URL.
    • To override server.port, use HEADPLANE_SERVER__PORT.
    • To override oidc.client_secret, use HEADPLANE_OIDC__CLIENT_SECRET.

    Warning: This is only for configuration overrides. You cannot use this mechanism for general environment variables like HEADPLANE_DEBUG_LOG or HEADPLANE_CONFIG_PATH.

  9. Run Headplane

    main

    Start Headplane using pnpm start or 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 the HEADPLANE_CONFIG_PATH environment variable.

    Login: To log in, you must provide a Headscale API key. Generate one using:

    headscale apikeys create --expiration 90d
    pnpm start
    # OR
    node build/server/index.js
  10. Configure Client IP Checks for Proxy Authentication

    main

    By default, Headplane uses the socket address connected to it to verify the proxy's identity. If allowed_cidrs is 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. When ip_header is set, Headplane follows this logic:

    1. It checks if the direct socket peer (the proxy) matches server.proxy_auth.trusted_proxy_cidrs (defaults to localhost).
    2. If it matches, it reads the specified ip_header (e.g., X-Forwarded-For).
    3. 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"
  11. Run Headplane in production

    main

    In production, the application is bundled into build/server/index.js. You can run the server using Node.js:

    node /app/build/server/index.js

    To enable TLS/HTTPS, pass a tls object containing key and cert to the startHttpServer function within the bootstrap logic.