feat: nix: add a new mise task and nix flake output to generate NixOS docs

Adds a new Nix flake output `headplane-nixos-docs` which generates a
Markdown page with all NixOS `services.headplane.settings.*` options.

Replaces existing handwritten options documentation.
This commit is contained in:
Igor Ramazanov 2025-07-29 15:22:28 +00:00 committed by Aarnav Tale
parent cdcb38f11e
commit fdbfe584c5
7 changed files with 1331 additions and 482 deletions

View File

@ -2,9 +2,8 @@
[flake.nix](../flake.nix) provided:
```
$ nix flake show . --all-systems
warning: Git tree '/home/erikp/headplane' is dirty
git+file:///home/erikp/headplane
$ nix flake show github:tale/headplane --all-systems
github:tale/headplane/ec6d455461955242393b60d9ce60c5123fa9784f?narHash=sha256-CM/vXzUiOed7i1Pp15KyV4FuIvumRlXnpF33dSWZZH4%3D
├───checks
│ ├───aarch64-darwin
│ │ └───default: derivation 'headplane-with-agent'
@ -29,183 +28,107 @@ git+file:///home/erikp/headplane
│ └───default: Nixpkgs overlay
└───packages
├───aarch64-darwin
│ ├───headplane: package 'headplane-0.5.10'
│ └───headplane-agent: package 'hp_agent-0.5.10'
│ ├───headplane: package 'headplane-0.6.1'
│ ├───headplane-agent: package 'hp_agent-0.6.1'
│ ├───headplane-nixos-docs: package 'headplane-nixos-docs.md'
│ └───headplane-ssh-wasm: package 'headplane-ssh-wasm-0.6.1'
├───x86_64-darwin
│ ├───headplane: package 'headplane-0.5.10'
│ └───headplane-agent: package 'hp_agent-0.5.10'
│ ├───headplane: package 'headplane-0.6.1'
│ ├───headplane-agent: package 'hp_agent-0.6.1'
│ ├───headplane-nixos-docs: package 'headplane-nixos-docs.md'
│ └───headplane-ssh-wasm: package 'headplane-ssh-wasm-0.6.1'
└───x86_64-linux
├───headplane: package 'headplane-0.5.10'
└───headplane-agent: package 'hp_agent-0.5.10'
├───headplane: package 'headplane-0.6.1'
├───headplane-agent: package 'hp_agent-0.6.1'
├───headplane-nixos-docs: package 'headplane-nixos-docs.md'
└───headplane-ssh-wasm: package 'headplane-ssh-wasm-0.6.1'
```
## NixOS module options
The Headplane NixOS module provides the following options under `services.headplane`:
### Top-level options
- **enable** (`bool`, default: `false`)
> Whether to enable the Headplane service.
- **package** (`package`, default: `pkgs.headplane`)
> The Headplane package to use.
- **settings** (submodule)
> Headplane configuration options. Generates a YAML config file. See: https://github.com/tale/headplane/blob/main/config.example.yaml
#### settings.server
- **host** (`string`, default: `"0.0.0.0"`)
> The host address to bind to. Example: `"127.0.0.1"`
- **port** (`port`, default: `3000`)
> The port to listen on.
- **cookie_secret** (`null or string`, default: `null`)
> The secret used to encode and decode web sessions. Ensure that this is exactly 32 characters long. Can be either a direct string or a path to a file containing the secret. Example: `config.sops.secrets.headplane.path`
- **cookie_secret_path** (`null or path`, default: `null`)
> Path to a file containing the cookie secret. The secret must be exactly 32 characters long. Can be used instead of cookie_secret. Example: `config.sops.secrets.headplane_cookie.path`
- **cookie_secure** (`bool`, default: `true`)
> Should the cookies only work over HTTPS? Set to false if running via HTTP without a proxy. Recommended to be true in production.
- **agent** (submodule)
> Agent configuration for the Headplane agent.
- **authkey** (`null or string`, default: `null`)
> The auth key used to authenticate the agent with Headplane. Can be either a direct string or a path to a file containing the key. Example: `config.sops.secrets.agent_authkey.path`
- **authkey_path** (`null or path`, default: `null`)
> Path to a file containing the agent auth key. Can be used instead of authkey. Example: `config.sops.secrets.agent_authkey.path`
- **ttl** (`int`, default: `180000`)
> How long to cache agent information (in milliseconds).
- **cache_path** (`string`, default: `"/var/lib/headplane/agent_cache.json"`)
> Where to store the agent cache.
#### settings.headscale
- **url** (`string`, default: `"http://127.0.0.1:8080"`)
> The URL to your Headscale instance. All API requests are routed through this URL. THIS IS NOT the gRPC endpoint, but the HTTP endpoint. IMPORTANT: If you are using TLS this MUST be set to `https://`. Example: `https://headscale.example.com`
- **tls_cert** (`null or string`, default: `null`)
> TLS certificate for HTTPS connections. Can be either a direct string or a path to a file containing the certificate. Example: `config.sops.secrets.tls_cert.path`
- **tls_cert_path** (`null or path`, default: `null`)
> Path to a file containing the TLS certificate. Can be used instead of tls_cert. Example: `config.sops.secrets.tls_cert.path`
- **tls_key** (`null or string`, default: `null`)
> TLS private key for HTTPS connections. Can be either a direct string or a path to a file containing the key. Example: `config.sops.secrets.tls_key.path`
- **tls_key_path** (`null or path`, default: `null`)
> Path to a file containing the TLS private key. Can be used instead of tls_key. Example: `config.sops.secrets.tls_key.path`
- **config_path** (`null or path`, default: `null`)
> Path to the Headscale configuration file. This is optional, but HIGHLY recommended for the best experience. If this is read only, Headplane will show your configuration settings in the Web UI, but they cannot be changed. Example: `/etc/headscale/config.yaml`
- **config_strict** (`bool`, default: `true`)
> Headplane internally validates the Headscale configuration to ensure that it changes the configuration in a safe way. If you want to disable this validation, set this to false.
#### settings.integration
- **proc** (submodule)
> Native process integration settings.
- **enabled** (`bool`, default: `false`)
> Enable "Native" integration that works when Headscale and Headplane are running outside of a container. There is no additional configuration, but you need to ensure that the Headplane process can terminate the Headscale process.
#### settings.oidc
- **issuer** (`string`, default: `""`)
> URL to OpenID issuer. Example: `https://authentik.parawell.cloud/application/o/test-headscale/`
- **client_id** (`string`, default: `""`)
> The client ID for the OIDC client. Example: `your-client-id`
- **client_secret** (`null or string`, default: `null`)
> The client secret for the OIDC client. Example: `config.sops.secrets.oidc_secret.path`
- **client_secret_path** (`null or path`, default: `null`)
> Path to a file containing the OIDC client secret. Can be used instead of client_secret. Example: `config.sops.secrets.oidc_client_secret.path`
- **disable_api_key_login** (`bool`, default: `false`)
> Whether to disable API key login.
- **token_endpoint_auth_method** (`enum ["client_secret_post", "client_secret_basic"]`, default: `"client_secret_post"`)
> The token endpoint authentication method.
- **headscale_api_key** (`null or string`, default: `null`)
> If you are using OIDC, you need to generate an API key that can be used to authenticate other sessions when signing in. This can be done with `headscale apikeys create --expiration 999d`. Example: `config.sops.secrets.headscale_api_key.path`
- **headscale_api_key_path** (`null or path`, default: `null`)
> Path to a file containing the Headscale API key. Can be used instead of headscale_api_key. Example: `config.sops.secrets.headscale_api_key.path`
- **redirect_uri** (`string`)
> This should point to your publicly accessible URL for your Headplane instance with /admin/oidc/callback. Example: `https://headscale.example.com/admin/oidc/callback`
- **agent** (submodule)
> Standalone agent configuration.
- **enable** (`bool`, default: `false`)
> Whether to enable the Headplane agent.
- **package** (`package`, default: `pkgs.headplane-agent`)
> The Headplane agent package to use.
- **settings** (`attrsOf string or bool`, default: `{}`)
> Headplane agent env vars config. See: https://github.com/tale/headplane/blob/main/docs/Headplane-Agent.md
Defined as `services.headplane.*`, check the `./nix/` directory for details.\
The full (generated by `mise run nixos-docs`) list of `services.headplane.settings.*` options: [./NixOS-options.md](./NixOS-options.md)
## Usage
1. Add the `github:tale/headplane` flake input.
2. Import a default overlay to add `pkgs.headplane`.
3. Import NixOS module for `services.headplane.*`.
1. Add the Headplane flake input to your `flake.nix`:
```nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
headplane = {
url = "github:tale/headplane";
inputs.nixpkgs.follows = "nixpkgs";
};
};
```nix
# Your flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
headplane = {
url = "github:tale/headplane";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, headplane, ... }: {
nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; # or your system architecture
modules = [
# Import the Headplane NixOS module to get access to services.headplane.* options
headplane.nixosModules.headplane
# Add the Headplane overlay to make pkgs.headplane and pkgs.headplane-agent available
{
nixpkgs.overlays = [ headplane.overlays.default ];
}
outputs = {
nixpkgs,
headplane,
...
}: {
nixosConfigurations.MY_MACHINE = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# provides `services.headplane.*` NixOS options.
headplane.nixosModules.headplane
{
# provides `pkgs.headplane`
nixpkgs.overlays = [ headplane.overlays.default ];
}
{
{config, pkgs, ...}:
let
format = pkgs.formats.yaml {};
# Your Headplane configuration
({ config, pkgs, lib, ... }: {
services.headplane = {
enable = true;
# A workaround generate a valid Headscale config accepted by Headplane when `config_strict == true`.
settings = lib.recursiveUpdate config.services.headscale.settings {
tls_cert_path = "/dev/null";
tls_key_path = "/dev/null";
policy.path = "/dev/null";
};
# Configure the standalone Headplane Agent
agent = {
enable = false; # Set to true if you want to run the agent (not ready yet as of writing this doc)
};
headscaleConfig = format.generate "headscale.yml" settings;
in {
services.headplane = {
enable = true;
settings = {
server = {
host = "127.0.0.1";
port = 3000;
# Using `sops-nix` as an example, can be a path to any file with a secret.
cookie_secret_path = config.sops.secrets."headplane/serverCookieSecret".path;
};
headscale = {
url = "https://example.com";
config_path = "${headscaleConfig}";
};
integration.agent = {
enabled = true;
# Using `sops-nix` as an example, can be a path to any file with a secret.
pre_authkey_path = config.sops.secrets."headplane/integrationAgentPreAuthkeyPath".path;
};
oidc = {
issuer = "https://oidc.example.com";
client_id = "headplane";
# Using `sops-nix` as an example, can be a path to any file with a secret.
client_secret_path = config.sops.secrets."headplane/oidcClientSecret".path;
disable_api_key_login = true;
# Might needed when integrating with Authelia.
token_endpoint_auth_method = "client_secret_basic";
# Using `sops-nix` as an example, can be a path to any file with a secret.
headscale_api_key_path = config.sops.secrets."headplane/oidcHeadscaleApiKey".path;
redirect_uri = "https://oidc.example.com/admin/oidc/callback";
};
};
};
}
}
];
};
};
}
```
# Configure the Headplane server application
settings = {
server = {
# host = "127.0.0.1";
# port = 3000;
cookie_secret_path = config.sops.secrets.headplane_cookie_secret.path;
cookie_secure = true;
agent = {
enabled = false;
# authkey_path = config.sops.secrets.headplane_server_agent_authkey.path;
# ttl = 180000; # milliseconds
# cache_path = "/var/lib/headplane/agent_cache.json";
};
};
headscale = {
# url = "http://127.0.0.1:8080";
config_path = headscaleConfig; # Use the generated config file
# config_strict = true;
# tls_cert = "your-tls-cert"; # Alternative to tls_cert_path
# tls_cert_path = config.sops.secrets.headplane_tls_cert.path;
# tls_key = "your-tls-key"; # Alternative to tls_key_path
# tls_key_path = config.sops.secrets.headplane_tls_key.path;
};
oidc = {
# issuer = "";
# client_id = "";
# client_secret_path = config.sops.secrets.headplane_oidc_client_secret.path;
# client_secret = "your_oidc_client_secret"; # Alternative to client_secret_path
# disable_api_key_login = false;
# token_endpoint_auth_method = "client_secret_post";
# headscale_api_key_path = config.sops.secrets.headplane_headscale_api_key.path;
# headscale_api_key = "your_headscale_api_key"; # Alternative to headscale_api_key_path
# redirect_uri = "";
};
};
};
})
];
};
};
}
```
For more details about each option, refer to the options documentation above.

881
docs/NixOS-options.md Normal file
View File

@ -0,0 +1,881 @@
## services\.headplane\.enable
Whether to enable headplane\.
*Type:*
boolean
*Default:*
` false `
*Example:*
` true `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.package
The headplane package to use\.
*Type:*
package
*Default:*
` pkgs.headplane `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.debug
Enable debug logging
*Type:*
boolean
*Default:*
` false `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings
Headplane configuration options\. Generates a YAML config file\.
See: https://github\.com/tale/headplane/blob/main/config\.example\.yaml
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale
Headscale specific settings for Headplane integration\.
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale\.config_path
Path to the Headscale configuration file\.
This is optional, but HIGHLY recommended for the best experience\.
If this is read only, Headplane will show your configuration settings
in the Web UI, but they cannot be changed\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "/etc/headscale/config.yaml" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale\.config_strict
Headplane internally validates the Headscale configuration
to ensure that it changes the configuration in a safe way\.
If you want to disable this validation, set this to false\.
*Type:*
boolean
*Default:*
` true `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale\.dns_records_path
If you are using ` dns.extra_records_path ` in your Headscale configuration, you need to set this to the path for Headplane to be able to read the DNS records\.
Ensure that the file is both readable and writable by the Headplane process\.
When using this, Headplane will no longer need to automatically restart Headscale for DNS record changes\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "/var/lib/headplane/extra_records.json" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale\.public_url
Public URL if differrent\. This affects certain parts of the web UI\.
*Type:*
null or string
*Default:*
` null `
*Example:*
` "https://headscale.example.com" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale\.tls_cert_path
Path to a file containing the TLS certificate\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "config.sops.secrets.tls_cert.path" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.headscale\.url
The URL to your Headscale instance\.
All API requests are routed through this URL\.
THIS IS NOT the gRPC endpoint, but the HTTP endpoint\.
IMPORTANT: If you are using TLS this MUST be set to ` https:// `\.
*Type:*
string
*Default:*
` "http://127.0.0.1:8080" `
*Example:*
` "https://headscale.example.com" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration
Integration configurations for Headplane to interact with Headscale\.
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent
Agent configuration for the Headplane agent\.
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.enabled
The Headplane agent allows retrieving information about nodes\.
This allows the UI to display version, OS, and connectivity data\.
You will see the Headplane agent in your Tailnet as a node when it connects\.
*Type:*
boolean
*Default:*
` false `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.package
The headplane-agent package to use\.
*Type:*
package
*Default:*
` pkgs.headplane-agent `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.cache_path
Where to store the agent cache\.
*Type:*
absolute path
*Default:*
` "/var/lib/headplane/agent_cache.json" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.cache_ttl
How long to cache agent information (in milliseconds)\.
If you want data to update faster, reduce the TTL, but this will increase the frequency of requests to Headscale\.
*Type:*
signed integer
*Default:*
` 180000 `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.host_name
Optionally change the name of the agent in the Tailnet
*Type:*
string
*Default:*
` "headplane-agent" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.pre_authkey_path
Path to a file containing the agent preauth key\.
To connect to your Tailnet, you need to generate a pre-auth key\.
This can be done via the web UI or through the ` headscale ` CLI\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "config.sops.secrets.agent_pre_authkey.path" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.agent\.work_dir
Do not change this unless you are running a custom deployment\.
The work_dir represents where the agent will store its data to be able to automatically reauthenticate with your Tailnet\.
It needs to be writable by the user running the Headplane process\.
*Type:*
absolute path
*Default:*
` "/var/lib/headplane/agent" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.proc
Native process integration settings\.
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.integration\.proc\.enabled
Enable “Native” integration that works when Headscale and
Headplane are running outside of a container\. There is no additional
configuration, but you need to ensure that the Headplane process
can terminate the Headscale process\.
*Type:*
boolean
*Default:*
` true `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc
OIDC Configuration for authentication\.
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.client_id
The client ID for the OIDC client\.
*Type:*
string
*Default:*
` "" `
*Example:*
` "your-client-id" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.client_secret_path
Path to a file containing the OIDC client secret\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "config.sops.secrets.oidc_client_secret.path" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.disable_api_key_login
Whether to disable API key login\.
*Type:*
boolean
*Default:*
` false `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.headscale_api_key_path
Path to a file containing the Headscale API key\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "config.sops.secrets.headscale_api_key.path" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.issuer
URL to OpenID issuer\.
*Type:*
string
*Default:*
` "" `
*Example:*
` "https://provider.example.com/issuer-url" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.redirect_uri
This should point to your publicly accessible URL
for your Headplane instance with /admin/oidc/callback\.
*Type:*
string
*Default:*
` "" `
*Example:*
` "https://headscale.example.com/admin/oidc/callback" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.token_endpoint_auth_method
The token endpoint authentication method\.
*Type:*
one of “client_secret_post”, “client_secret_basic”, “client_secret_jwt”
*Default:*
` "client_secret_post" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.oidc\.user_storage_file
Path to a file containing the users and their permissions for Headplane\.
*Type:*
absolute path
*Default:*
` "/var/lib/headplane/users.json" `
*Example:*
` "/var/lib/headplane/users.json" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.server
Server configuration for Headplane web application\.
*Type:*
submodule
*Default:*
` { } `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.server\.cookie_secret_path
Path to a file containing the cookie secret\.
The secret must be exactly 32 characters long\.
*Type:*
null or absolute path
*Default:*
` null `
*Example:*
` "config.sops.secrets.headplane_cookie.path" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.server\.cookie_secure
Should the cookies only work over HTTPS?
Set to false if running via HTTP without a proxy\.
Recommended to be true in production\.
*Type:*
boolean
*Default:*
` true `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.server\.data_path
The path to persist Headplane specific data\.
All data going forward is stored in this directory, including the internal database and any cache related files\.
Data formats prior to 0\.6\.1 will automatically be migrated\.
*Type:*
absolute path
*Default:*
` "/var/lib/headplane" `
*Example:*
` "/var/lib/headplane" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.server\.host
The host address to bind to\.
*Type:*
string
*Default:*
` "127.0.0.1" `
*Example:*
` "0.0.0.0" `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)
## services\.headplane\.settings\.server\.port
The port to listen on\.
*Type:*
16 bit unsigned integer; between 0 and 65535 (both inclusive)
*Default:*
` 3000 `
*Declared by:*
- [/nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options\.nix](file:///nix/store/k8cbmm2xy0s8gk2kggksxy6zffzry4jg-source/nix/options.nix)

View File

@ -31,6 +31,7 @@ rec {
packages = {
headplane = pkgs.callPackage ./nix/package.nix {headplane-ssh-wasm = packages.headplane-ssh-wasm;};
headplane-agent = pkgs.callPackage ./nix/agent.nix {};
headplane-nixos-docs = pkgs.callPackage ./nix/docs.nix {};
headplane-ssh-wasm = pkgs.callPackage ./nix/ssh-wasm.nix {};
};
checks.default = pkgs.symlinkJoin {
@ -64,6 +65,7 @@ rec {
overlays.default = final: prev: {
headplane = final.callPackage ./nix/package.nix {headplane-ssh-wasm = final.headplane-ssh-wasm;};
headplane-agent = final.callPackage ./nix/agent.nix {};
headplane-nixos-docs = final.callPackage ./nix/docs.nix {};
headplane-ssh-wasm = final.callPackage ./nix/ssh-wasm.nix {};
};
nixosModules.headplane = import ./nix/module.nix;

View File

@ -32,6 +32,15 @@ run = [
'go build -ldflags="-s -w -X main.imageTag=$IMAGE_TAG" -o build/sh ./cmd/fake_sh'
]
[tasks.build-nixos-docs]
alias = ["nixos-docs"]
description = "Builds NixOS module documentation"
run = [
'nix build .#headplane-nixos-docs',
'cp --dereference --force ./result ./docs/NixOS-options.md',
'sed -i "s;\[/nix.*;\[nix/options.nix\](https://github.com/tale/headplane/blob/main/nix/options.nix);" ./docs/NixOS-options.md'
]
[tasks.generate-caddy-certs]
alias = ["mkcert"]
dir = "{{cwd}}/test/caddy/certs"

24
nix/docs.nix Normal file
View File

@ -0,0 +1,24 @@
{
lib,
nixosOptionsDoc,
runCommand,
...
}: let
eval = lib.evalModules {
modules = [./options.nix];
};
transformOptions = opt:
if (lib.hasPrefix "_" opt.name)
then
opt
// {
internal = true;
visible = false;
}
else opt;
optionsDoc = nixosOptionsDoc {
inherit (eval) options;
inherit transformOptions;
};
in
runCommand "headplane-nixos-docs.md" {} "cat ${optionsDoc.optionsCommonMark} > $out"

View File

@ -8,12 +8,8 @@
(lib)
filterAttrs
filterAttrsRecursive
mkEnableOption
mkIf
mkOption
mkPackageOption
recursiveUpdate
types
updateManyAttrsByPath
;
cfg = config.services.headplane;
@ -32,313 +28,7 @@
settingsWithoutNulls = filterAttrsRecursive (key: value: value != null) settingsWithoutAgentPackage;
settingsFile = settingsFormat.generate "headplane-config.yaml" settingsWithoutNulls;
in {
options.services.headplane = {
enable = mkEnableOption "headplane";
package = mkPackageOption pkgs "headplane" {};
debug = mkOption {
type = types.bool;
default = false;
description = "Enable debug logging";
};
settings = mkOption {
description = ''
Headplane configuration options. Generates a YAML config file.
See: https://github.com/tale/headplane/blob/main/config.example.yaml
'';
type = types.submodule {
options = {
server = mkOption {
type = types.submodule {
options = {
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "The host address to bind to.";
example = "0.0.0.0";
};
port = mkOption {
type = types.port;
default = 3000;
description = "The port to listen on.";
};
cookie_secret_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the cookie secret.
The secret must be exactly 32 characters long.
'';
example = "config.sops.secrets.headplane_cookie.path";
};
cookie_secure = mkOption {
type = types.bool;
default = true;
description = ''
Should the cookies only work over HTTPS?
Set to false if running via HTTP without a proxy.
Recommended to be true in production.
'';
};
data_path = mkOption {
type = types.path;
default = "/var/lib/headplane";
description = ''
The path to persist Headplane specific data.
All data going forward is stored in this directory, including the internal database and any cache related files.
Data formats prior to 0.6.1 will automatically be migrated.
'';
example = "/var/lib/headplane";
};
};
};
default = {};
description = "Server configuration for Headplane web application.";
};
headscale = mkOption {
type = types.submodule {
options = {
url = mkOption {
type = types.str;
default = "http://127.0.0.1:8080";
description = ''
The URL to your Headscale instance.
All API requests are routed through this URL.
THIS IS NOT the gRPC endpoint, but the HTTP endpoint.
IMPORTANT: If you are using TLS this MUST be set to `https://`.
'';
example = "https://headscale.example.com";
};
tls_cert_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the TLS certificate.
'';
example = "config.sops.secrets.tls_cert.path";
};
public_url = mkOption {
type = types.nullOr types.str;
default = null;
description = "Public URL if differrent. This affects certain parts of the web UI.";
example = "https://headscale.example.com";
};
config_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to the Headscale configuration file.
This is optional, but HIGHLY recommended for the best experience.
If this is read only, Headplane will show your configuration settings
in the Web UI, but they cannot be changed.
'';
example = "/etc/headscale/config.yaml";
};
config_strict = mkOption {
type = types.bool;
default = true;
description = ''
Headplane internally validates the Headscale configuration
to ensure that it changes the configuration in a safe way.
If you want to disable this validation, set this to false.
'';
};
dns_records_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
If you are using `dns.extra_records_path` in your Headscale configuration, you need to set this to the path for Headplane to be able to read the DNS records.
Ensure that the file is both readable and writable by the Headplane process.
When using this, Headplane will no longer need to automatically restart Headscale for DNS record changes.
'';
example = "/var/lib/headplane/extra_records.json";
};
};
};
default = {};
description = "Headscale specific settings for Headplane integration.";
};
integration = mkOption {
type = types.submodule {
options = {
agent = mkOption {
type = types.submodule {
options = {
enabled = mkOption {
type = types.bool;
default = false;
description = ''
The Headplane agent allows retrieving information about nodes.
This allows the UI to display version, OS, and connectivity data.
You will see the Headplane agent in your Tailnet as a node when it connects.
'';
};
pre_authkey_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the agent preauth key.
To connect to your Tailnet, you need to generate a pre-auth key.
This can be done via the web UI or through the `headscale` CLI.
'';
example = "config.sops.secrets.agent_pre_authkey.path";
};
host_name = mkOption {
type = types.str;
default = "headplane-agent";
description = "Optionally change the name of the agent in the Tailnet";
};
cache_ttl = mkOption {
type = types.int;
default = 180000;
description = ''
How long to cache agent information (in milliseconds).
If you want data to update faster, reduce the TTL, but this will increase the frequency of requests to Headscale.
'';
};
cache_path = mkOption {
type = types.path;
default = "/var/lib/headplane/agent_cache.json";
description = "Where to store the agent cache.";
};
work_dir = mkOption {
type = types.path;
default = "/var/lib/headplane/agent";
description = ''
Do not change this unless you are running a custom deployment.
The work_dir represents where the agent will store its data to be able to automatically reauthenticate with your Tailnet.
It needs to be writable by the user running the Headplane process.
'';
};
package = mkPackageOption pkgs "headplane-agent" {};
};
};
default = {};
description = "Agent configuration for the Headplane agent.";
};
proc = mkOption {
type = types.submodule {
options = {
enabled = mkOption {
type = types.bool;
default = true;
description = ''
Enable "Native" integration that works when Headscale and
Headplane are running outside of a container. There is no additional
configuration, but you need to ensure that the Headplane process
can terminate the Headscale process.
'';
};
};
};
default = {};
description = "Native process integration settings.";
};
};
};
default = {};
description = "Integration configurations for Headplane to interact with Headscale.";
};
oidc = mkOption {
type = types.submodule {
options = {
issuer = mkOption {
type = types.str;
default = "";
description = "URL to OpenID issuer.";
example = "https://provider.example.com/issuer-url";
};
client_id = mkOption {
type = types.str;
default = "";
description = "The client ID for the OIDC client.";
example = "your-client-id";
};
client_secret_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the OIDC client secret.
'';
example = "config.sops.secrets.oidc_client_secret.path";
};
disable_api_key_login = mkOption {
type = types.bool;
default = false;
description = "Whether to disable API key login.";
};
token_endpoint_auth_method = mkOption {
type = types.enum [
"client_secret_post"
"client_secret_basic"
"client_secret_jwt"
];
default = "client_secret_post";
description = "The token endpoint authentication method.";
};
headscale_api_key_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the Headscale API key.
'';
example = "config.sops.secrets.headscale_api_key.path";
};
redirect_uri = mkOption {
type = types.str;
default = "";
description = ''
This should point to your publicly accessible URL
for your Headplane instance with /admin/oidc/callback.
'';
example = "https://headscale.example.com/admin/oidc/callback";
};
user_storage_file = mkOption {
type = types.path;
default = "/var/lib/headplane/users.json";
description = ''
Path to a file containing the users and their permissions for Headplane.
'';
example = "/var/lib/headplane/users.json";
};
};
};
default = {};
description = "OIDC Configuration for authentication.";
};
};
};
default = {};
};
};
imports = [./options.nix];
config = mkIf cfg.enable {
environment = {
systemPackages = [cfg.package];

320
nix/options.nix Normal file
View File

@ -0,0 +1,320 @@
{
lib,
pkgs,
...
}: let
inherit
(lib)
mkEnableOption
mkOption
mkPackageOption
types
;
in {
options.services.headplane = {
enable = mkEnableOption "headplane";
package = mkPackageOption pkgs "headplane" {};
debug = mkOption {
type = types.bool;
default = false;
description = "Enable debug logging";
};
settings = mkOption {
description = ''
Headplane configuration options. Generates a YAML config file.
See: https://github.com/tale/headplane/blob/main/config.example.yaml
'';
type = types.submodule {
options = {
server = mkOption {
type = types.submodule {
options = {
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "The host address to bind to.";
example = "0.0.0.0";
};
port = mkOption {
type = types.port;
default = 3000;
description = "The port to listen on.";
};
cookie_secret_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the cookie secret.
The secret must be exactly 32 characters long.
'';
example = "config.sops.secrets.headplane_cookie.path";
};
cookie_secure = mkOption {
type = types.bool;
default = true;
description = ''
Should the cookies only work over HTTPS?
Set to false if running via HTTP without a proxy.
Recommended to be true in production.
'';
};
data_path = mkOption {
type = types.path;
default = "/var/lib/headplane";
description = ''
The path to persist Headplane specific data.
All data going forward is stored in this directory, including the internal database and any cache related files.
Data formats prior to 0.6.1 will automatically be migrated.
'';
example = "/var/lib/headplane";
};
};
};
default = {};
description = "Server configuration for Headplane web application.";
};
headscale = mkOption {
type = types.submodule {
options = {
url = mkOption {
type = types.str;
default = "http://127.0.0.1:8080";
description = ''
The URL to your Headscale instance.
All API requests are routed through this URL.
THIS IS NOT the gRPC endpoint, but the HTTP endpoint.
IMPORTANT: If you are using TLS this MUST be set to `https://`.
'';
example = "https://headscale.example.com";
};
tls_cert_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the TLS certificate.
'';
example = "config.sops.secrets.tls_cert.path";
};
public_url = mkOption {
type = types.nullOr types.str;
default = null;
description = "Public URL if differrent. This affects certain parts of the web UI.";
example = "https://headscale.example.com";
};
config_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to the Headscale configuration file.
This is optional, but HIGHLY recommended for the best experience.
If this is read only, Headplane will show your configuration settings
in the Web UI, but they cannot be changed.
'';
example = "/etc/headscale/config.yaml";
};
config_strict = mkOption {
type = types.bool;
default = true;
description = ''
Headplane internally validates the Headscale configuration
to ensure that it changes the configuration in a safe way.
If you want to disable this validation, set this to false.
'';
};
dns_records_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
If you are using `dns.extra_records_path` in your Headscale configuration, you need to set this to the path for Headplane to be able to read the DNS records.
Ensure that the file is both readable and writable by the Headplane process.
When using this, Headplane will no longer need to automatically restart Headscale for DNS record changes.
'';
example = "/var/lib/headplane/extra_records.json";
};
};
};
default = {};
description = "Headscale specific settings for Headplane integration.";
};
integration = mkOption {
type = types.submodule {
options = {
agent = mkOption {
type = types.submodule {
options = {
enabled = mkOption {
type = types.bool;
default = false;
description = ''
The Headplane agent allows retrieving information about nodes.
This allows the UI to display version, OS, and connectivity data.
You will see the Headplane agent in your Tailnet as a node when it connects.
'';
};
pre_authkey_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the agent preauth key.
To connect to your Tailnet, you need to generate a pre-auth key.
This can be done via the web UI or through the `headscale` CLI.
'';
example = "config.sops.secrets.agent_pre_authkey.path";
};
host_name = mkOption {
type = types.str;
default = "headplane-agent";
description = "Optionally change the name of the agent in the Tailnet";
};
cache_ttl = mkOption {
type = types.int;
default = 180000;
description = ''
How long to cache agent information (in milliseconds).
If you want data to update faster, reduce the TTL, but this will increase the frequency of requests to Headscale.
'';
};
cache_path = mkOption {
type = types.path;
default = "/var/lib/headplane/agent_cache.json";
description = "Where to store the agent cache.";
};
work_dir = mkOption {
type = types.path;
default = "/var/lib/headplane/agent";
description = ''
Do not change this unless you are running a custom deployment.
The work_dir represents where the agent will store its data to be able to automatically reauthenticate with your Tailnet.
It needs to be writable by the user running the Headplane process.
'';
};
package = mkPackageOption pkgs "headplane-agent" {};
};
};
default = {};
description = "Agent configuration for the Headplane agent.";
};
proc = mkOption {
type = types.submodule {
options = {
enabled = mkOption {
type = types.bool;
default = true;
description = ''
Enable "Native" integration that works when Headscale and
Headplane are running outside of a container. There is no additional
configuration, but you need to ensure that the Headplane process
can terminate the Headscale process.
'';
};
};
};
default = {};
description = "Native process integration settings.";
};
};
};
default = {};
description = "Integration configurations for Headplane to interact with Headscale.";
};
oidc = mkOption {
type = types.submodule {
options = {
issuer = mkOption {
type = types.str;
default = "";
description = "URL to OpenID issuer.";
example = "https://provider.example.com/issuer-url";
};
client_id = mkOption {
type = types.str;
default = "";
description = "The client ID for the OIDC client.";
example = "your-client-id";
};
client_secret_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the OIDC client secret.
'';
example = "config.sops.secrets.oidc_client_secret.path";
};
disable_api_key_login = mkOption {
type = types.bool;
default = false;
description = "Whether to disable API key login.";
};
token_endpoint_auth_method = mkOption {
type = types.enum [
"client_secret_post"
"client_secret_basic"
"client_secret_jwt"
];
default = "client_secret_post";
description = "The token endpoint authentication method.";
};
headscale_api_key_path = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
Path to a file containing the Headscale API key.
'';
example = "config.sops.secrets.headscale_api_key.path";
};
redirect_uri = mkOption {
type = types.str;
default = "";
description = ''
This should point to your publicly accessible URL
for your Headplane instance with /admin/oidc/callback.
'';
example = "https://headscale.example.com/admin/oidc/callback";
};
user_storage_file = mkOption {
type = types.path;
default = "/var/lib/headplane/users.json";
description = ''
Path to a file containing the users and their permissions for Headplane.
'';
example = "/var/lib/headplane/users.json";
};
};
};
default = {};
description = "OIDC Configuration for authentication.";
};
};
};
default = {};
};
};
}