Compare commits

...

1 Commits

Author SHA1 Message Date
Johannes Kirschbauer
be4cd657c9 POC 2025-09-17 14:32:46 +02:00
79 changed files with 19525 additions and 88 deletions

View File

@@ -34,7 +34,6 @@ from typing import Any
from clan_lib.errors import ClanError from clan_lib.errors import ClanError
from clan_lib.services.modules import ( from clan_lib.services.modules import (
CategoryInfo, CategoryInfo,
ModuleManifest,
) )
# Get environment variables # Get environment variables
@@ -103,7 +102,7 @@ def render_option(
read_only = option.get("readOnly") read_only = option.get("readOnly")
res = f""" res = f"""
{"#" * level} {sanitize(name) if short_head is None else sanitize(short_head)} {"{: #" + sanitize_anchor(name) + "}" if level > 1 else ""} {"#" * level} {sanitize(name) if short_head is None else sanitize(short_head)}
""" """
@@ -130,14 +129,12 @@ def render_option(
""" """
example = option.get("example", {}).get("text") example = option.get("example", {}).get("text")
if example: if example:
example_indented = join_lines_with_indentation(example.split("\n"))
res += f""" res += f"""
???+ example ```nix title="example"
{example}
```nix
{example_indented}
``` ```
""" """
if option.get("relatedPackages"): if option.get("relatedPackages"):
res += f""" res += f"""
@@ -154,7 +151,7 @@ def render_option(
source_path = source_path.split(",")[0] source_path = source_path.split(",")[0]
res += f""" res += f"""
:simple-git: Declared in: [{name}]({source_path}) 📃 Declared in: [{name}]({source_path})
""" """
res += "\n\n" res += "\n\n"
@@ -224,7 +221,7 @@ def produce_clan_core_docs() -> None:
indexfile = f"{module_name}/index.md" indexfile = f"{module_name}/index.md"
core_outputs[indexfile] = module_header(module_name) + clan_core_descr core_outputs[indexfile] = module_header(module_name) + clan_core_descr
core_outputs[indexfile] += """!!! info "Submodules"\n""" core_outputs[indexfile] += """:::note[Submodules]"\n"""
for submodule_name, split_options in split.items(): for submodule_name, split_options in split.items():
root = options_to_tree(split_options) root = options_to_tree(split_options)
@@ -320,10 +317,11 @@ Each `clanService`:
* Can define **roles** (e.g., `client`, `server`) * Can define **roles** (e.g., `client`, `server`)
* Uses **`inventory.instances`** to configure where and how it is deployed * Uses **`inventory.instances`** to configure where and how it is deployed
!!! Note :::note
`clanServices` are part of Clan's next-generation service model and are intended to replace `clanModules`. `clanServices` are part of Clan's next-generation service model and are intended to replace `clanModules`.
See [Migration Guide](../../guides/migrations/migrate-inventory-services.md) for help on migrating. See [Migration Guide](../../guides/migrations/migrate-inventory-services.md) for help on migrating.
:::
Learn how to use `clanServices` in practice in the [Using clanServices guide](../../guides/inventory/clanServices.md). Learn how to use `clanServices` in practice in the [Using clanServices guide](../../guides/inventory/clanServices.md).
""" """
@@ -339,15 +337,20 @@ Learn how to use `clanServices` in practice in the [Using clanServices guide](..
if module_name in ["internet", "tor"]: if module_name in ["internet", "tor"]:
continue continue
output = f"# {module_name}\n\n" output = f"""---
title: {module_name}
description: {module_info["manifest"]["description"]}
---
"""
# output += f"`clan.modules.{module_name}`\n" # output += f"`clan.modules.{module_name}`\n"
output += f"*{module_info['manifest']['description']}*\n" output += f"*{module_info['manifest']['description']}*\n"
# output += "## Categories\n\n" # output += "## Categories\n\n"
output += render_categories( # output += render_categories(
module_info["manifest"]["categories"], # module_info["manifest"]["categories"],
ModuleManifest.categories_info(), # ModuleManifest.categories_info(),
) # )
output += f"{module_info['manifest']['readme']}\n" output += f"{module_info['manifest']['readme']}\n"
@@ -417,10 +420,11 @@ This document describes the structure and configurable attributes of a `clan.ser
Typically needed by module authors to define roles, behavior and metadata for distributed services. Typically needed by module authors to define roles, behavior and metadata for distributed services.
!!! Note :::note
This is not a user-facing documentation, but rather meant as a reference for *module authors* This is not a user-facing documentation, but rather meant as a reference for *module authors*
See: [clanService Authoring Guide](../../guides/services/community.md) See: [clanService Authoring Guide](../../guides/services/community.md)
:::
""" """
# Inventory options are already included under the clan attribute # Inventory options are already included under the clan attribute
# We just omitted them in the clan docs, because we want a separate output for the inventory model # We just omitted them in the clan docs, because we want a separate output for the inventory model

View File

@@ -1,8 +1,10 @@
Services provided by the community ---
title: Services provided by the community
!!! tip "Add your own!" ---
:::tip[Add your own!]
Have you built a service or a tool for? Open a PR adding a link to this page! Have you built a service or a tool for? Open a PR adding a link to this page!
:::
<div class="grid cards" markdown> <div class="grid cards" markdown>

View File

@@ -64,6 +64,7 @@ Just like with Nix you could specify a remote url or path to the flake containin
`clan flakes create --template=github:owner/repo#foo` `clan flakes create --template=github:owner/repo#foo`
!!! Note "Implementation Note" :::note
Not all features of Nix's attribute selection are currently matched. Not all features of Nix's attribute selection are currently matched.
There are minor differences in case of unexpected behavior please create an [issue](https://git.clan.lol/clan/clan-core/issues/new) There are minor differences in case of unexpected behavior please create an [issue](https://git.clan.lol/clan/clan-core/issues/new)
:::

View File

@@ -6,8 +6,9 @@ This section contains the architecture decisions that have been reviewed and gen
> An architecture decision record (ADR) is a document that captures an important architecture decision made along with its context and consequences. > An architecture decision record (ADR) is a document that captures an important architecture decision made along with its context and consequences.
!!! Note :::note
For further reading about adr's we recommend [architecture-decision-record](https://github.com/joelparkerhenderson/architecture-decision-record) For further reading about adr's we recommend [architecture-decision-record](https://github.com/joelparkerhenderson/architecture-decision-record)
:::
## Crafting a new ADR ## Crafting a new ADR

View File

@@ -5,7 +5,7 @@ Here are some methods for debugging and testing the clan-cli
To streamline your development process, I suggest not installing `clan-cli`. Instead, clone the `clan-core` repository and add `clan-core/pkgs/clan-cli/bin` to your PATH to use the checked-out version directly. To streamline your development process, I suggest not installing `clan-cli`. Instead, clone the `clan-core` repository and add `clan-core/pkgs/clan-cli/bin` to your PATH to use the checked-out version directly.
!!! Note :::Note
After cloning, navigate to `clan-core/pkgs/clan-cli` and execute `direnv allow` to activate the devshell. This will set up a symlink to nixpkgs at a specific location; without it, `clan-cli` won't function correctly. After cloning, navigate to `clan-core/pkgs/clan-cli` and execute `direnv allow` to activate the devshell. This will set up a symlink to nixpkgs at a specific location; without it, `clan-cli` won't function correctly.
With this setup, you can easily use [breakpoint()](https://docs.python.org/3/library/pdb.html) to inspect the application's internal state as needed. With this setup, you can easily use [breakpoint()](https://docs.python.org/3/library/pdb.html) to inspect the application's internal state as needed.

View File

@@ -1,10 +1,10 @@
This guide provides an example setup for a single-disk ZFS system with native encryption, accessible for decryption remotely. This guide provides an example setup for a single-disk ZFS system with native encryption, accessible for decryption remotely.
!!! Warning :::warning
This configuration only applies to `systemd-boot` enabled systems and **requires** UEFI booting. This configuration only applies to `systemd-boot` enabled systems and **requires** UEFI booting.
!!! Info "Secure Boot" :::Info "Secure Boot"
This guide is compatible with systems that have [secure boot disabled](./secure-boot.md). If you encounter boot issues, check if secure boot needs to be disabled in your UEFI settings. This guide is compatible with systems that have [secure boot disabled](./secure-boot.md). If you encounter boot issues, check if secure boot needs to be disabled in your UEFI settings.
Replace the highlighted lines with your own disk-id. Replace the highlighted lines with your own disk-id.

View File

@@ -1,5 +1,5 @@
!!! Danger ":fontawesome-solid-road-barrier: Under Construction :fontawesome-solid-road-barrier:" :::Danger ":fontawesome-solid-road-barrier: Under Construction :fontawesome-solid-road-barrier:"
Currently under construction use with caution Currently under construction use with caution
:fontawesome-solid-road-barrier: :fontawesome-solid-road-barrier: :fontawesome-solid-road-barrier: :fontawesome-solid-road-barrier: :fontawesome-solid-road-barrier: :fontawesome-solid-road-barrier:

View File

@@ -41,7 +41,7 @@ See the complete [list](../inventory/autoincludes.md) of auto-loaded files.
### Configuring a machine ### Configuring a machine
!!! Note :::Note
The option: `inventory.machines.<name>` is used to define metadata about the machine The option: `inventory.machines.<name>` is used to define metadata about the machine
That includes for example `deploy.targethost` `machineClass` or `tags` That includes for example `deploy.targethost` `machineClass` or `tags`

View File

@@ -12,7 +12,7 @@ In Clan Services are multi-Host & role-based:
To learn more: [Guide about clanService](../inventory/clanServices.md) To learn more: [Guide about clanService](../inventory/clanServices.md)
!!! Important :::Important
It is recommended to add at least one networking service such as `zerotier` that allows to reach all your clan machines from your setup computer across the globe. It is recommended to add at least one networking service such as `zerotier` that allows to reach all your clan machines from your setup computer across the globe.
## Configure a Zerotier Network (recommended) ## Configure a Zerotier Network (recommended)

View File

@@ -1,6 +1,6 @@
# How to add users # How to add users
!!! Note "Under construction" :::Note "Under construction"
The users concept of clan is not done yet. This guide outlines some solutions from our community. The users concept of clan is not done yet. This guide outlines some solutions from our community.
Defining users can be done in many different ways. We want to highlight two approaches: Defining users can be done in many different ways. We want to highlight two approaches:
@@ -109,7 +109,7 @@ We can use this property of clan services to bind a nixosModule to the user, whi
1. Type `path` or `string`: Must point to a separate file. Inlining a module is not possible 1. Type `path` or `string`: Must point to a separate file. Inlining a module is not possible
!!! Note "This is inspiration" :::Note "This is inspiration"
Our community might come up with better solutions soon. Our community might come up with better solutions soon.
We are seeking contributions to improve this pattern if you have a nicer solution in mind. We are seeking contributions to improve this pattern if you have a nicer solution in mind.

View File

@@ -25,7 +25,7 @@ Available 'machine' templates
For this guide we will select the `single-disk` template, that uses `A simple ext4 disk with a single partition`. For this guide we will select the `single-disk` template, that uses `A simple ext4 disk with a single partition`.
!!! tip :::tip
For advanced partitioning, see [Disko templates](https://github.com/nix-community/disko-templates) or [Disko examples](https://github.com/nix-community/disko/tree/master/example). For advanced partitioning, see [Disko templates](https://github.com/nix-community/disko-templates) or [Disko examples](https://github.com/nix-community/disko/tree/master/example).
You can also [contribute a disk template to clan core](https://docs.clan.lol/guides/disko-templates/community/) You can also [contribute a disk template to clan core](https://docs.clan.lol/guides/disko-templates/community/)
@@ -58,7 +58,7 @@ Applied disk template 'single-disk' to machine 'jon'
A disko.nix file should be created in `machines/jon` A disko.nix file should be created in `machines/jon`
You can have a look and customize it if needed. You can have a look and customize it if needed.
!!! Danger :::Danger
Don't change the `disko.nix` after the machine is installed for the first time, unless you really know what you are doing. Don't change the `disko.nix` after the machine is installed for the first time, unless you really know what you are doing.
Changing disko configuration requires wiping and reinstalling the machine. Changing disko configuration requires wiping and reinstalling the machine.

View File

@@ -2,7 +2,7 @@
This guide will help you convert your existing NixOS configurations into a Clan. This guide will help you convert your existing NixOS configurations into a Clan.
!!! Warning :::Warning
Migrating instead of starting new can be trickier and might lead to bugs or Migrating instead of starting new can be trickier and might lead to bugs or
unexpected issues. We recommend reading the [Getting Started](./index.md) guide first. unexpected issues. We recommend reading the [Getting Started](./index.md) guide first.

View File

@@ -2,13 +2,13 @@
To install Clan on physical machines, you need to use our custom installer image. This is necessary for proper installation and operation. To install Clan on physical machines, you need to use our custom installer image. This is necessary for proper installation and operation.
!!! note "Deploying to a Virtual Machine?" :::note "Deploying to a Virtual Machine?"
If you're deploying to a virtual machine (VM), you can skip this section and go directly to the [Deploy Virtual Machine](./hardware-report-virtual.md) step. In this scenario, we automatically use [nixos-anywhere](https://github.com/nix-community/nixos-anywhere) to replace the kernel during runtime. If you're deploying to a virtual machine (VM), you can skip this section and go directly to the [Deploy Virtual Machine](./hardware-report-virtual.md) step. In this scenario, we automatically use [nixos-anywhere](https://github.com/nix-community/nixos-anywhere) to replace the kernel during runtime.
??? info "Why nixos-anywhere Doesn't Work on Physical Hardware?" :::info "Why nixos-anywhere Doesn't Work on Physical Hardware?"
nixos-anywhere relies on [kexec](https://wiki.archlinux.org/title/Kexec) to replace the running kernel with our custom one. This method often has compatibility issues with real hardware, especially systems with dedicated graphics cards like laptops and servers, leading to crashes and black screens. nixos-anywhere relies on [kexec](https://wiki.archlinux.org/title/Kexec) to replace the running kernel with our custom one. This method often has compatibility issues with real hardware, especially systems with dedicated graphics cards like laptops and servers, leading to crashes and black screens.
??? info "Reasons for a Custom Install Image" :::info "Reasons for a Custom Install Image"
Our custom install images are built to include essential tools like [nixos-facter](https://github.com/nix-community/nixos-facter) and support for [ZFS](https://wiki.archlinux.org/title/ZFS). They're also optimized to run on systems with as little as 1 GB of RAM, ensuring efficient performance even on lower-end hardware. Our custom install images are built to include essential tools like [nixos-facter](https://github.com/nix-community/nixos-facter) and support for [ZFS](https://wiki.archlinux.org/title/ZFS). They're also optimized to run on systems with as little as 1 GB of RAM, ensuring efficient performance even on lower-end hardware.
## Prerequisites ## Prerequisites
@@ -36,7 +36,7 @@ To install Clan on physical machines, you need to use our custom installer image
└─luks-f7600028-9d83-4967-84bc-dd2f498bc486 254:0 0 1,8T 0 crypt /nix/store └─luks-f7600028-9d83-4967-84bc-dd2f498bc486 254:0 0 1,8T 0 crypt /nix/store
``` ```
!!! Info "In this case the USB device is `sdb`" :::Info "In this case the USB device is `sdb`"
3. Ensure all partitions on the drive are unmounted. Replace `sdb1` in the command below with your device identifier (like `sdc1`, etc.): 3. Ensure all partitions on the drive are unmounted. Replace `sdb1` in the command below with your device identifier (like `sdc1`, etc.):
@@ -62,11 +62,11 @@ sudo umount /dev/sdb1
--disk main /dev/sd<X> \ --disk main /dev/sd<X> \
flash-installer flash-installer
``` ```
!!! Note :::Note
Replace `$HOME/.ssh/id_ed25519.pub` with a path to your SSH public key. Replace `$HOME/.ssh/id_ed25519.pub` with a path to your SSH public key.
Replace `/dev/sd<X>` with the drive path you want to flash Replace `/dev/sd<X>` with the drive path you want to flash
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss." :::Danger "Specifying the wrong device can lead to unrecoverable data loss."
The `clan flash` utility will erase the disk. Make sure to specify the correct device The `clan flash` utility will erase the disk. Make sure to specify the correct device
@@ -114,12 +114,12 @@ sudo umount /dev/sdb1
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-aarch64-linux.iso wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-aarch64-linux.iso
``` ```
!!! Note :::Note
If you don't have `wget` installed, you can use `curl --progress-bar -OL <url>` instead. If you don't have `wget` installed, you can use `curl --progress-bar -OL <url>` instead.
## Flash the Installer to the USB Drive ## Flash the Installer to the USB Drive
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss." :::Danger "Specifying the wrong device can lead to unrecoverable data loss."
The `dd` utility will erase the disk. Make sure to specify the correct device (`of=...`) The `dd` utility will erase the disk. Make sure to specify the correct device (`of=...`)
@@ -197,7 +197,7 @@ IPv4 address 192.168.188.50 (Your new local ip)
Press ++ctrl+d++ to exit `IWD`. Press ++ctrl+d++ to exit `IWD`.
!!! Important :::Important
Press ++ctrl+d++ **again** to update the displayed QR code and connection information. Press ++ctrl+d++ **again** to update the displayed QR code and connection information.
You're all set up You're all set up

View File

@@ -22,7 +22,7 @@ nix flake check
This command helps ensure that your system configuration is correct and free from errors. This command helps ensure that your system configuration is correct and free from errors.
!!! Tip :::Tip
You can integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase. You can integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase.

View File

@@ -70,7 +70,7 @@ This is an example of the booted installer.
This password is autogenerated and meant to be easily typeable. This password is autogenerated and meant to be easily typeable.
3. See how to connect the installer medium to wlan [here](./create-installer.md). 3. See how to connect the installer medium to wlan [here](./create-installer.md).
!!!tip :::tip
For easy sharing of deployment information via QR code, we highly recommend using [KDE Connect](https://apps.kde.org/de/kdeconnect/). For easy sharing of deployment information via QR code, we highly recommend using [KDE Connect](https://apps.kde.org/de/kdeconnect/).
There are two ways to deploy your machine: There are two ways to deploy your machine:

View File

@@ -12,7 +12,7 @@ Now that you have created a machine, added some services, and set up secrets, th
Clan supports any cloud machine if it is reachable via SSH and supports `kexec`. Clan supports any cloud machine if it is reachable via SSH and supports `kexec`.
??? tip "NixOS can cause strange issues when booting in certain cloud environments." :::tip "NixOS can cause strange issues when booting in certain cloud environments."
If on Linode: Make sure that the system uses "Direct Disk boot kernel" (found in the configuration panel) If on Linode: Make sure that the system uses "Direct Disk boot kernel" (found in the configuration panel)
@@ -26,5 +26,5 @@ clan machines install [MACHINE] \
--target-host myuser@<IP> --target-host myuser@<IP>
``` ```
!!! Warning :::Warning
After running the above command, be aware that the SSH login user changes from `myuser` to `root`. For subsequent SSH connections to the target machine, use `root` as the login user. This change occurs because the system switches to the NixOS kernel using `kexec`. After running the above command, be aware that the SSH login user changes from `myuser` to `root`. For subsequent SSH connections to the target machine, use `root` as the login user. This change occurs because the system switches to the NixOS kernel using `kexec`.

View File

@@ -13,7 +13,7 @@ Make sure you have the following:
* 💻 **Administration Machine**: Run the setup commands from this machine. * 💻 **Administration Machine**: Run the setup commands from this machine.
* 🛠️ **Nix**: The Nix package manager, installed on your administration machine. * 🛠️ **Nix**: The Nix package manager, installed on your administration machine.
??? info "**How to install Nix (Linux / MacOS / NixOS)**" :::info "**How to install Nix (Linux / MacOS / NixOS)**"
**On Linux or macOS:** **On Linux or macOS:**
@@ -75,7 +75,7 @@ my-clan/
└── sops/ └── sops/
``` ```
!!! note "Templates" :::note "Templates"
This is the structure for the `default` template. This is the structure for the `default` template.
Use `clan templates list` and `clan templates --help` for available templates & more. Keep in mind that the exact files may change as templates evolve. Use `clan templates list` and `clan templates --help` for available templates & more. Keep in mind that the exact files may change as templates evolve.

View File

@@ -60,7 +60,7 @@ If the machine does not have enough resources to run the NixOS **evaluation** or
it is also possible to specify a `buildHost` instead. it is also possible to specify a `buildHost` instead.
During an update, clan will ssh into the `buildHost` and run `nixos-rebuild` from there. During an update, clan will ssh into the `buildHost` and run `nixos-rebuild` from there.
!!! Note :::Note
The `buildHost` option should be set directly within your machines Nix configuration, **not** under `inventory.machines`. The `buildHost` option should be set directly within your machines Nix configuration, **not** under `inventory.machines`.
@@ -89,7 +89,7 @@ clan machines update jon --build-host root@192.168.1.10
clan machines update jon --build-host local clan machines update jon --build-host local
``` ```
!!! Note :::Note
Make sure the CPU architecture of the `buildHost` matches that of the `targetHost` Make sure the CPU architecture of the `buildHost` matches that of the `targetHost`
For example, if deploying to a macOS machine with an ARM64-Darwin architecture, you need a second macOS machine with the same architecture to build it. For example, if deploying to a macOS machine with an ARM64-Darwin architecture, you need a second macOS machine with the same architecture to build it.

View File

@@ -6,7 +6,7 @@ Clan automatically imports specific files from each machine directory and regist
Every folder under `machines/{machineName}` is automatically registered as a Clan machine. Every folder under `machines/{machineName}` is automatically registered as a Clan machine.
!!! info "Files loaded automatically for each machine" :::info "Files loaded automatically for each machine"
The following files are detected and imported for every Clan machine: The following files are detected and imported for every Clan machine:

View File

@@ -9,7 +9,7 @@ The inventory logic will automatically derive the modules and configurations to
The following tutorial will walk through setting up a Backup service where the terms `Service` and `Role` will become more clear. The following tutorial will walk through setting up a Backup service where the terms `Service` and `Role` will become more clear.
!!! example "Experimental status" :::example "Experimental status"
The inventory implementation is not considered stable yet. The inventory implementation is not considered stable yet.
We are actively soliciting feedback from users. We are actively soliciting feedback from users.
@@ -36,7 +36,7 @@ Each service can still be customized and configured according to the modules opt
### Setting up the Backup Service ### Setting up the Backup Service
!!! Example "Borgbackup Example" :::Example "Borgbackup Example"
To configure a service it needs to be added to the machine. To configure a service it needs to be added to the machine.
It is required to assign the service (`borgbackup`) an arbitrary instance name. (`instance_1`) It is required to assign the service (`borgbackup`) an arbitrary instance name. (`instance_1`)
@@ -64,7 +64,7 @@ The inventory allows machines to set Tags
It is possible to add services to multiple machines via tags as shown It is possible to add services to multiple machines via tags as shown
!!! Example "Tags Example" :::Example "Tags Example"
```{.nix hl_lines="5 8 18"} ```{.nix hl_lines="5 8 18"}
{ {
@@ -93,11 +93,11 @@ It is possible to add services to multiple machines via tags as shown
### Multiple Service Instances ### Multiple Service Instances
!!! danger "Important" :::danger "Important"
Not all modules implement support for multiple instances yet. Not all modules implement support for multiple instances yet.
Multiple instance usage could create complexity, refer to each modules documentation, for intended usage. Multiple instance usage could create complexity, refer to each modules documentation, for intended usage.
!!! Example :::Example
In this example `backup_server` has role `client` and `server` in different instances. In this example `backup_server` has role `client` and `server` in different instances.

View File

@@ -290,7 +290,7 @@ The following table shows the migration status of each deprecated clanModule:
--- ---
!!! Warning :::Warning
* Old `clanModules` (`class = "nixos"`) are deprecated and will be removed in the near future. * Old `clanModules` (`class = "nixos"`) are deprecated and will be removed in the near future.
* `inventory.services` is no longer recommended; use `inventory.instances` instead. * `inventory.services` is no longer recommended; use `inventory.instances` instead.
* Module authors should begin exporting service modules under the `clan.modules` attribute of their flake. * Module authors should begin exporting service modules under the `clan.modules` attribute of their flake.

View File

@@ -98,7 +98,7 @@ clan network overview
## Option 2: Manual targetHost (Bypasses Fallback!) ## Option 2: Manual targetHost (Bypasses Fallback!)
!!! warning :::warning
Setting `targetHost` directly **disables all automatic networking and fallback**. Only use this if you need complete control and don't want Clan's intelligent connection management. Setting `targetHost` directly **disables all automatic networking and fallback**. Only use this if you need complete control and don't want Clan's intelligent connection management.
### Using Inventory (For Static Addresses) ### Using Inventory (For Static Addresses)
@@ -155,7 +155,7 @@ Use machine-level `targetHost` when you need to **interpolate values from the Ni
- Building from multiple config values: `"${config.users.users.deploy.name}@${config.networking.hostName}"` - Building from multiple config values: `"${config.users.users.deploy.name}@${config.networking.hostName}"`
- Any address that depends on evaluated NixOS configuration - Any address that depends on evaluated NixOS configuration
!!! info "Key Difference" :::info "Key Difference"
**Inventory-level** (`deploy.targetHost`) is evaluated immediately and works with static strings. **Inventory-level** (`deploy.targetHost`) is evaluated immediately and works with static strings.
**Machine-level** (`clan.core.networking.targetHost`) is evaluated after NixOS configuration and can access `config.*` values. **Machine-level** (`clan.core.networking.targetHost`) is evaluated after NixOS configuration and can access `config.*` values.

View File

@@ -7,10 +7,10 @@ However, there are important considerations when using `nixos-rebuild` directly
## Important Considerations ## Important Considerations
!!! warning "Vars Must Be Uploaded First" :::warning "Vars Must Be Uploaded First"
If your configuration uses clan vars, failing to run `clan vars upload` before `nixos-rebuild` will result in missing secrets and potentially broken services. If your configuration uses clan vars, failing to run `clan vars upload` before `nixos-rebuild` will result in missing secrets and potentially broken services.
!!! info "Build Host Configuration" :::info "Build Host Configuration"
Clan automatically handles build host configuration based on your machine settings. Clan automatically handles build host configuration based on your machine settings.
When using `nixos-rebuild` manually, you need to specify `--build-host` and `--target-host` options yourself. When using `nixos-rebuild` manually, you need to specify `--build-host` and `--target-host` options yourself.

View File

@@ -17,7 +17,7 @@ Clan can also be configured to be used with other secret store [backends](../ref
To get started, you'll need to create **your admin keypair**. To get started, you'll need to create **your admin keypair**.
!!! info :::info
Don't worry — if you've already made one before, this step won't change or overwrite it. Don't worry — if you've already made one before, this step won't change or overwrite it.
```bash ```bash
@@ -33,7 +33,7 @@ Generated age private key at '/home/joerg/.config/sops/age/keys.txt' for your us
Also add your age public key to the repository with 'clan secrets users add YOUR_USER age1wkth7uhpkl555g40t8hjsysr20drq286netu8zptw50lmqz7j95sw2t3l7' (replace YOUR_USER with your actual username) Also add your age public key to the repository with 'clan secrets users add YOUR_USER age1wkth7uhpkl555g40t8hjsysr20drq286netu8zptw50lmqz7j95sw2t3l7' (replace YOUR_USER with your actual username)
``` ```
!!! warning :::warning
Make sure to keep a safe backup of the private key you've just created. Make sure to keep a safe backup of the private key you've just created.
If it's lost, you won't be able to get to your secrets anymore because they all need the admin key to be unlocked. If it's lost, you won't be able to get to your secrets anymore because they all need the admin key to be unlocked.
@@ -47,7 +47,7 @@ Alternatively, you can provide your [age] secret key as an environment variable
using `SOPS_AGE_KEY_FILE`. using `SOPS_AGE_KEY_FILE`.
For more information see the [SOPS] guide on [encrypting with age]. For more information see the [SOPS] guide on [encrypting with age].
!!! note :::note
It's safe to add any secrets created by the clan CLI and placed in your repository to version control systems like `git`. It's safe to add any secrets created by the clan CLI and placed in your repository to version control systems like `git`.
## Add Your Public Key(s) ## Add Your Public Key(s)
@@ -68,7 +68,7 @@ sops/
``` ```
If you followed the quickstart tutorial all necessary secrets are initialized at this point. If you followed the quickstart tutorial all necessary secrets are initialized at this point.
!!! note :::note
You can add multiple age keys for a user by providing multiple `--age-key <your_public_key>` flags: You can add multiple age keys for a user by providing multiple `--age-key <your_public_key>` flags:
```console ```console
@@ -260,7 +260,7 @@ The secrets system conceptually knows two different entities:
**A machine** Can decrypt secrets that where encrypted specifically for that machine. **A machine** Can decrypt secrets that where encrypted specifically for that machine.
!!! Danger :::Danger
**Always make sure at least one _User_ has access to a secret**. Otherwise you could lock yourself out from accessing the secret. **Always make sure at least one _User_ has access to a secret**. Otherwise you could lock yourself out from accessing the secret.
### Inherited implications ### Inherited implications
@@ -275,8 +275,9 @@ By default clan uses [sops](https://github.com/getsops/sops) through [sops-nix](
- **Machine key-pairs are auto-generated**: When a machine is created **no user-interaction is required** to setup public/private key-pairs. - **Machine key-pairs are auto-generated**: When a machine is created **no user-interaction is required** to setup public/private key-pairs.
- **secrets are re-encrypted**: In case machines, users or groups are modified secrets get re-encrypted on demand. - **secrets are re-encrypted**: In case machines, users or groups are modified secrets get re-encrypted on demand.
!!! Important :::caution
After revoking access to a secret you should also change the underlying secret. i.e. change the API key, or the password. After revoking access to a secret you should also change the underlying secret. i.e. change the API key, or the password.
:::
--- ---

View File

@@ -8,7 +8,7 @@ At the moment, NixOS/Clan does not support [Secure Boot](https://wiki.gentoo.org
- Restart your computer. - Restart your computer.
- As your computer restarts, press the appropriate key to enter the UEFI/BIOS settings. - As your computer restarts, press the appropriate key to enter the UEFI/BIOS settings.
??? tip "The key depends on your laptop or motherboard manufacturer. Click to see a reference list:" :::tip "The key depends on your laptop or motherboard manufacturer. Click to see a reference list:"
| Manufacturer | UEFI/BIOS Key(s) | | Manufacturer | UEFI/BIOS Key(s) |
|--------------------|---------------------------| |--------------------|---------------------------|
@@ -31,8 +31,9 @@ At the moment, NixOS/Clan does not support [Secure Boot](https://wiki.gentoo.org
| EVGA | `Del` | | EVGA | `Del` |
| Origin PC | `F2`, `Delete` | | Origin PC | `F2`, `Delete` |
!!! Note :::note
Pressing the key quickly and repeatedly is sometimes necessary to access the UEFI/BIOS menu, as the window to enter this mode is brief. Pressing the key quickly and repeatedly is sometimes necessary to access the UEFI/BIOS menu, as the window to enter this mode is brief.
:::
## Access Advanced Mode (Optional) ## Access Advanced Mode (Optional)

View File

@@ -145,7 +145,7 @@ Next we need to define the settings and the behavior of these distinct roles.
## Using values from a NixOS machine inside the module ## Using values from a NixOS machine inside the module
!!! Example "Experimental Status" :::Example "Experimental Status"
This feature is experimental and should be used with care. This feature is experimental and should be used with care.
Sometimes a settings value depends on something within a machines `config`. Sometimes a settings value depends on something within a machines `config`.
@@ -174,7 +174,7 @@ The following example shows how to create a local instance of machine specific s
} }
``` ```
!!! Danger :::Danger
`localSettings` are a local attribute. Other machines cannot access it. `localSettings` are a local attribute. Other machines cannot access it.
If calling extendSettings is done that doesn't change the original `settings` this means if a different machine tries to access i.e `roles.client.settings` it would *NOT* contain your changes. If calling extendSettings is done that doesn't change the original `settings` this means if a different machine tries to access i.e `roles.client.settings` it would *NOT* contain your changes.

View File

@@ -29,7 +29,7 @@ Below is a [list of popular `age` plugins](https://github.com/FiloSottile/awesom
If you want to use `fido2 tokens` to encrypt your secret instead of the normal age secret key then you need to prefix your age secret key with the corresponding plugin name. In our case we want to use the `age-plugin-fido2-hmac` plugin so we replace `AGE-SECRET-KEY` with `AGE-PLUGIN-FIDO2-HMAC`. If you want to use `fido2 tokens` to encrypt your secret instead of the normal age secret key then you need to prefix your age secret key with the corresponding plugin name. In our case we want to use the `age-plugin-fido2-hmac` plugin so we replace `AGE-SECRET-KEY` with `AGE-PLUGIN-FIDO2-HMAC`.
??? tip :::tip
- On Linux the age secret key is located at `~/.config/sops/age/keys.txt` - On Linux the age secret key is located at `~/.config/sops/age/keys.txt`
- On macOS it is located at `/Users/admin/Library/Application Support/sops/age/keys.txt` - On macOS it is located at `/Users/admin/Library/Application Support/sops/age/keys.txt`

21
tested-trappist/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

View File

@@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
tested-trappist/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

49
tested-trappist/README.md Normal file
View File

@@ -0,0 +1,49 @@
# Starlight Starter Kit: Basics
[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build)
```
npm create astro@latest -- --template starlight
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro + Starlight project, you'll see the following folders and files:
```
.
├── public/
├── src/
│ ├── assets/
│ ├── content/
│ │ └── docs/
│ └── content.config.ts
├── astro.config.mjs
├── package.json
└── tsconfig.json
```
Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
Images can be added to `src/assets/` and embedded in Markdown with a relative link.
Static assets, like favicons, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Check out [Starlights docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).

View File

@@ -0,0 +1,58 @@
// @ts-check
import { defineConfig } from "astro/config";
import starlightOpenAPI, { openAPISidebarGroups } from "starlight-openapi";
import starlight from "@astrojs/starlight";
// https://astro.build/config
export default defineConfig({
integrations: [
starlight({
title: "Clan",
favicon: "/public/favicon.svg",
// logo: { src: "/public/logo.svg", alt: "Clan Logo" },
social: [
{
icon: "seti:git",
label: "Git",
href: "https://git.clan.lol/clan/clan-core",
},
],
plugins: [
// Generate the OpenAPI documentation pages.
starlightOpenAPI([
{
base: "api",
schema: "openapi.json",
sidebar: {
collapsed: true,
label: "Internal API",
},
},
]),
],
sidebar: [
{
label: "Getting Started",
autogenerate: { directory: "getting-started" },
collapsed: false,
},
{
label: "Guides",
autogenerate: { directory: "guides" },
collapsed: false,
},
{
label: "Reference",
items: [
{
label: "Clan Services",
autogenerate: { directory: "reference/clanServices" },
collapsed: false,
},
...openAPISidebarGroups,
],
},
],
}),
],
});

6754
tested-trappist/openapi.json Normal file

File diff suppressed because it is too large Load Diff

6571
tested-trappist/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
{
"name": "tested-trappist",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.35.3",
"astro": "^5.6.1",
"sharp": "^0.34.2",
"starlight-openapi": "^0.20.0"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="89" fill="currentColor"><g clip-path="url(#a)"><path d="M57.709 20.105H68.62c1.157 0 2.099-.94 2.099-2.095V9.632a2.1 2.1 0 0 0-2.099-2.095h-3.439c-1.111 0-2.014-.9-2.014-2.01V2.095A2.1 2.1 0 0 0 61.07 0H30.02a2.1 2.1 0 0 0-2.098 2.095v3.432c0 1.11-.903 2.01-2.014 2.01H22.47c-1.157 0-2.098.94-2.098 2.095v3.432c0 1.11-.903 2.01-2.014 2.01h-3.439c-1.157 0-2.099.94-2.099 2.094 0 0-.503-1.272-.503 22.493 0 21.247.503 19.38.503 19.38 0 1.156.942 2.096 2.1 2.096h3.438c1.111 0 2.014.9 2.014 2.01v3.517c0 1.109.902 2.01 2.013 2.01h3.524c1.111 0 2.014.9 2.014 2.01v3.432a2.1 2.1 0 0 0 2.098 2.094h30.211c1.157 0 2.099-.94 2.099-2.094v-3.433c0-1.11.902-2.01 2.013-2.01h5.557c1.158 0 2.099-.94 2.099-2.094v-9.984a2.1 2.1 0 0 0-2.099-2.095h-13.03c-1.157 0-2.098.94-2.098 2.095v5.044c0 1.11-.902 2.01-2.014 2.01H37.488c-1.111 0-2.013-.9-2.013-2.01v-5.11a2.1 2.1 0 0 0-2.099-2.094h-5.119c-1.111 0-1.739.163-2.014-2.01-.085-.698-.13-1.553-.196-2.695-.163-2.878-.307-1.723-.307-10.369 0-12.085.314-15.563.503-17.24.19-1.677.903-2.01 2.014-2.01h5.12c1.156 0 2.098-.94 2.098-2.094v-3.433c0-1.109.902-2.01 2.013-2.01h16.116c1.111 0 2.014.901 2.014 2.01v3.433c0 1.155.94 2.094 2.098 2.094zM18.626 73.757h-2.478a.87.87 0 0 1-.87-.868v-2.473c0-.96-.777-1.743-1.745-1.743H6.838c-.96 0-1.745.777-1.745 1.743v2.473a.87.87 0 0 1-.87.868H1.746c-.961 0-1.746.776-1.746 1.742v6.682c0 .96.778 1.742 1.746 1.742h2.477c.484 0 .87.392.87.868v2.473c0 .96.778 1.743 1.745 1.743h6.695c.961 0 1.746-.777 1.746-1.743v-2.473c0-.483.392-.868.87-.868h2.477c.961 0 1.746-.776 1.746-1.742v-6.682c0-.96-.778-1.742-1.746-1.742"/></g><defs><clipPath id="a"><path d="M0 0h72v89H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="223" height="89" fill="currentColor"><g clip-path="url(#a)"><path d="M55.503 18.696h10.104a1.946 1.946 0 0 0 1.943-1.948v-7.79c0-1.075-.87-1.947-1.943-1.947h-3.186a1.863 1.863 0 0 1-1.866-1.87V1.947C60.555.872 59.685 0 58.612 0h-27.98a1.946 1.946 0 0 0-1.944 1.947v3.194c0 1.036-.832 1.87-1.865 1.87h-3.187a1.946 1.946 0 0 0-1.943 1.947v3.194c0 1.036-.832 1.87-1.866 1.87h-3.186a1.946 1.946 0 0 0-1.943 1.947s-.467 1.153-.467 23.253c0 19.763.467 21.913.467 21.913 0 1.075.87 1.948 1.943 1.948h3.186c1.034 0 1.866.833 1.866 1.87v3.271c0 1.036.831 1.87 1.865 1.87h3.265c1.033 0 1.865.833 1.865 1.87v3.193c0 1.075.87 1.948 1.943 1.948h27.981a1.946 1.946 0 0 0 1.943-1.948v-3.194c0-1.036.832-1.87 1.866-1.87h5.145a1.946 1.946 0 0 0 1.943-1.947v-9.285c0-1.075-.87-1.948-1.943-1.948H55.503a1.946 1.946 0 0 0-1.943 1.948v4.69c0 1.035-.832 1.869-1.866 1.869H37.55a1.863 1.863 0 0 1-1.866-1.87v-4.752c0-1.075-.87-1.947-1.943-1.947H29c-1.034 0-1.609.148-1.865-1.87-.078-.646-.125-1.44-.18-2.508-.147-2.68-.287-5.5-.287-13.539 0-11.24.288-16.81.466-18.369.18-1.558.832-1.87 1.866-1.87h4.741a1.946 1.946 0 0 0 1.943-1.947v-3.193c0-1.037.832-1.87 1.866-1.87h14.145c1.034 0 1.866.833 1.866 1.87v3.193c0 1.075.87 1.948 1.943 1.948M20.247 74.822h-2.293a.814.814 0 0 1-.808-.81v-2.298c0-.896-.723-1.62-1.617-1.62H9.327c-.894 0-1.617.724-1.617 1.62v2.298c0 .444-.365.81-.808.81H4.609c-.894 0-1.617.725-1.617 1.62v6.217c0 .896.723 1.62 1.617 1.62h2.293c.443 0 .808.366.808.81v2.299c0 .895.723 1.62 1.617 1.62h6.202c.894 0 1.617-.725 1.617-1.62v-2.299c0-.444.365-.81.808-.81h2.293c.894 0 1.617-.724 1.617-1.62v-6.216c0-.896-.723-1.62-1.617-1.62M221.135 35.04h-1.71a1.863 1.863 0 0 1-1.866-1.87v-3.272c0-1.036-.831-1.87-1.865-1.87h-3.265a1.863 1.863 0 0 1-1.865-1.87v-3.271c0-1.036-.832-1.87-1.865-1.87h-20.971a1.863 1.863 0 0 0-1.865 1.87v3.965c0 .514-.42.935-.933.935h-3.559c-.513 0-.84-.32-.933-.935l-.622-3.918c-.148-1.099-.676-1.777-1.788-1.777l-3.653-.14h-2.052a3.736 3.736 0 0 0-3.73 3.74V61.68a3.736 3.736 0 0 1-3.731 3.739h-8.394a1.863 1.863 0 0 1-1.866-1.87V36.714c0-11.825-7.461-18.813-22.556-18.813-13.718 0-20.325 5.04-21.203 14.443-.109 1.153.552 1.815 1.702 1.815l7.757.569c1.143.1 1.594-.554 1.811-1.652.77-3.74 4.174-5.827 9.933-5.827 7.081 0 10.042 3.358 10.042 9.076v3.014c0 1.036-.831 1.87-1.865 1.87l-.342-.024h-9.715c-15.421 0-22.984 5.983-22.984 17.956 0 3.802.778 7.058 2.254 9.738h-.59c-1.765-1.27-2.457-2.236-3.055-2.93-.256-.295-.653-.537-1.345-.537h-1.717l-5.993.008h-3.264a3.736 3.736 0 0 1-3.731-3.74V1.769C89.74.654 89.072 0 87.969 0H79.55c-1.034 0-1.865.732-1.865 1.768l-.024 54.304v13.554c0 4.13 3.343 7.479 7.462 7.479h50.84c8.448-.429 8.604-3.42 9.436-4.542.645 3.56 1.865 4.347 4.71 4.518 8.137.117 18.343.032 18.49.024h4.975c4.119 0 6.684-3.35 6.684-7.479l.777-27.264c0-1.036.832-1.87 1.866-1.87h2.021a1.56 1.56 0 0 0 1.554-1.558v-3.583c0-1.036.832-1.87 1.866-1.87h11.868a3.37 3.37 0 0 1 3.366 3.373v3.249c0 1.075.87 1.947 1.943 1.947h4.119c.513 0 .933.42.933.935v32.25c0 1.036.831 1.87 1.865 1.87h6.84a3.736 3.736 0 0 0 3.731-3.74V36.91c0-1.036-.832-1.87-1.866-1.87zM142.64 54.225c0 8.927-6.132 14.715-15.335 14.715-6.606 0-9.793-2.953-9.793-8.748 0-6.442 3.832-9.636 11.62-9.636h13.508v3.669"/></g><defs><clipPath id="a"><path d="M0 0h223v89H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -0,0 +1,7 @@
import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { docsSchema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
};

View File

@@ -0,0 +1,128 @@
---
title: How to add machines
---
Machines can be added using the following methods
- Create a file `machines/{machine_name}/configuration.nix` (See: [File Autoincludes](../inventory/autoincludes.md))
- Imperative via cli command: `clan machines create`
- Editing nix expressions in flake.nix See [`clan-core.lib.clan`](/options/?scope=Flake Options (clan.nix file))
See the complete [list](../inventory/autoincludes.md) of auto-loaded files.
## Create a machine
=== "clan.nix (declarative)"
```{.nix hl_lines="3-4"}
{
inventory.machines = {
# Define a machine
jon = { };
};
# Additional NixOS configuration can be added here.
# machines/jon/configuration.nix will be automatically imported.
# See: https://docs.clan.lol/guides/more-machines/#automatic-registration
machines = {
# jon = { config, ... }: {
# environment.systemPackages = [ pkgs.asciinema ];
# };
};
}
```
=== "CLI (imperative)"
```sh
clan machines create jon
```
The imperative command might create a machine folder in `machines/jon`
And might persist information in `inventory.json`
### Configuring a machine
:::note
The option: `inventory.machines.<name>` is used to define metadata about the machine
That includes for example `deploy.targethost` `machineClass` or `tags`
The option: `machines.<name>` is used to add extra *nixosConfiguration* to a machine
:::
Add the following to your `clan.nix` file for each machine.
This example demonstrates what is needed based on a machine called `jon`:
```nix title="clan.nix" {3-6, 15-19}
{
inventory.machines = {
jon = {
# Define tags here (optional)
tags = [ ]; # (1)
};
sara = {
deploy.targetHost = "root@sara";
tags = [ ];
};
};
# Define additional nixosConfiguration here
# Or in /machines/jon/configuration.nix (autoloaded)
machines = {
jon = { config, pkgs, ... }: {
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC..." # elided (2)
];
};
};
}
```
1. Tags can be used to automatically add this machine to services later on. - You dont need to set this now.
2. Add your *ssh key* here - That will ensure you can always login to your machine via *ssh* in case something goes wrong.
### (Optional) Create a `configuration.nix`
```nix title="./machines/jon/configuration.nix"
{
imports = [
# enables GNOME desktop (optional)
../../modules/gnome.nix
];
# Set nixosOptions here
# Or import your own modules via 'imports'
# ...
}
```
### (Optional) Renaming a Machine
Older templates included static machine folders like `jon` and `sara`.
If your setup still uses such static machines, you can rename a machine folder to match your own machine name:
```bash
git mv ./machines/jon ./machines/<your-machine-name>
```
Since your Clan configuration lives inside a Git repository, remember:
* Only files tracked by Git (`git add`) are recognized.
* Whenever you add, rename, or remove files, run:
```bash
git add ./machines/<your-machine-name>
```
to stage the changes.
---
### (Optional) Removing a Machine
If you want to work with a single machine for now, you can remove other machine entries both from your `flake.nix` and from the `machines` directory. For example, to remove the machine `sara`:
```bash
git rm -rf ./machines/sara
```
Make sure to also remove or update any references to that machine in your `nix files` or `inventory.json` if you have any of that

View File

@@ -0,0 +1,80 @@
---
title: How to add services
---
A service in clan is a self-contained, reusable unit of system configuration that provides a specific piece of functionality across one or more machines.
Think of it as a recipe for running a tool — like automatic backups, VPN networking, monitoring, etc.
In Clan Services are multi-Host & role-based:
- Roles map machines to logical service responsibilities, enabling structured, clean deployments.
- You can use tags instead of explicit machine names.
To learn more: [Guide about clanService](../inventory/clanServices.md)
:::caution
It is recommended to add at least one networking service such as `zerotier` that allows to reach all your clan machines from your setup computer across the globe.
:::
## Configure a Zerotier Network (recommended)
```{.nix title="clan.nix" hl_lines="8-16"}
{
inventory.machines = {
jon = { };
sara = { };
};
inventory.instances = {
zerotier = { # (1)
# Replace with the name (string) of your machine that you will use as zerotier-controller
# See: https://docs.zerotier.com/controller/
# Deploy this machine first to create the network secrets
roles.controller.machines."jon" = { }; # (2)
# Peers of the network
# this line means 'all' clan machines will be 'peers'
roles.peer.tags.all = { }; # (3)
};
};
# ...
# elided
}
```
1. See [reference/clanServices](../../reference/clanServices/index.md) for all available services and how to configure them.
Or read [authoring/clanServices](../services/community.md) if you want to bring your own
2. Replace `__YOUR_CONTROLLER_` with the *name* of your machine.
3. This line will add all machines of your clan as `peer` to zerotier
## Adding more recommended defaults
Adding the following services is recommended for most users:
```{.nix title="clan.nix" hl_lines="7-14"}
{
inventory.machines = {
jon = { };
sara = { };
};
inventory.instances = {
admin = { # (1)
roles.default.tags.all = { };
roles.default.settings = {
allowedKeys = {
"my-user" = "ssh-ed25519 AAAAC3N..."; # (2)
};
};
};
# ...
# elided
};
}
```
1. The `admin` service will generate a **root-password** and **add your ssh-key** that allows for convienient administration.
2. Equivalent to directly setting `authorizedKeys` like in [configuring a machine](./add-machines.md#configuring-a-machine)
3. Adds `user = jon` as a user on all machines. Will create a `home` directory, and prompt for a password before deployment.

View File

@@ -0,0 +1,131 @@
---
title: How to add users
---
:::note[Under construction]
The users concept of clan is not done yet. This guide outlines some solutions from our community.
Defining users can be done in many different ways. We want to highlight two approaches:
- Using clan's [users](../../reference/clanServices/users.md) service.
- Using a custom approach.
:::
## Adding Users using the [users](../../reference/clanServices/users.md) service
To add a first *user* this guide will be leveraging two things:
- [clanServices](../../reference/clanServices/index.md): Allows to bind arbitrary logic to something we call an `ìnstance`.
- [clanServices/users](../../reference/clanServices/users.md): Implements logic for adding a single user perInstance.
The example shows how to add a user called `jon`:
```{.nix title="clan.nix" hl_lines="7-21"}
{
inventory.machines = {
jon = { };
sara = { };
};
inventory.instances = {
jon-user = { # (1)
module.name = "users";
roles.default.tags.all = { }; # (2)
roles.default.settings = {
user = "jon"; # (3)
groups = [
"wheel" # Allow using 'sudo'
"networkmanager" # Allows to manage network connections.
"video" # Allows to access video devices.
"input" # Allows to access input devices.
];
};
};
# ...
# elided
};
}
```
1. Add `user = jon` as a user on all machines. Will create a `home` directory, and prompt for a password before deployment.
2. Add this user to `all` machines
3. Define the `name` of the user to be `jon`
The `users` service creates a `/home/jon` directory, allows `jon` to sign in and will take care of the user's password.
For more information see [clanService/users](../../reference/clanServices/users.md)
## Using a custom approach
Some people like to define a `users` folder in their repository root.
That allows to bind all user specific logic to a single place (`default.nix`)
Which can be imported into individual machines to make the user available on that machine.
```bash
.
├── machines
│   ├── jon
# ......
├── users
│   ├── jon
│ │ └── default.nix # <- a NixOS module; sets some options
# ... ... ...
```
## using [home-manager](https://github.com/nix-community/home-manager)
When using clan's `users` service it is possible to define extraModules.
In fact this is always possible when using clan's services.
We can use this property of clan services to bind a nixosModule to the user, which configures home-manager.
```{.nix title="clan.nix" hl_lines="22"}
{
inventory.machines = {
jon = { };
sara = { };
};
inventory.instances = {
jon-user = {
module.name = "users";
roles.default.tags.all = { };
roles.default.settings = {
user = "jon",
groups = [
"wheel"
"networkmanager"
"video"
"input"
];
};
roles.default.extraModules = [ ./users/jon/home.nix ]; # (1)
};
# ...
# elided
};
}
```
1. Type `path` or `string`: Must point to a separate file. Inlining a module is not possible
:::Note[This is inspiration]
Our community might come up with better solutions soon.
We are seeking contributions to improve this pattern if you have a nicer solution in mind.
:::
```nix title="users/jon/home.nix"
# NixOS module to import home-manager and the home-manager configuration of 'jon'
{ self, ...}:
{
imports = [ self.inputs.home-manager.nixosModules.default ];
home-manager.users.jon = {
imports = [
./home-configuration.nix
];
};
}
```

View File

@@ -0,0 +1,79 @@
---
title: Configure Disk Config
---
By default clan uses [disko](https://github.com/nix-community/disko) which allows for declarative disk partitioning.
To see what disk templates are available run:
```{.shellSession hl_lines="10" .no-copy}
$ clan templates list
Available 'clan' template
├── <builtin>
│ ├── default: Initialize a new clan flake
│ ├── flake-parts: Flake-parts
│ └── minimal: for clans managed via (G)UI
Available 'disko' templates
├── <builtin>
│ └── single-disk: A simple ext4 disk with a single partition
Available 'machine' templates
├── <builtin>
│ ├── demo-template: Demo machine for the CLAN project
│ ├── flash-installer: Initialize a new flash-installer machine
│ ├── new-machine: Initialize a new machine
│ └── test-morph-template: Morph a machine
```
For this guide we will select the `single-disk` template, that uses `A simple ext4 disk with a single partition`.
:::tip
For advanced partitioning, see [Disko templates](https://github.com/nix-community/disko-templates) or [Disko examples](https://github.com/nix-community/disko/tree/master/example).
You can also [contribute a disk template to clan core](https://docs.clan.lol/guides/disko-templates/community/)
:::
To setup a disk schema for a machine run
```bash
clan templates apply disk single-disk jon --set mainDisk ""
```
Which should fail and give the valid options for the specific hardware:
```shellSession
Invalid value for placeholder mainDisk - Valid options:
/dev/disk/by-id/nvme-WD_PC_SN740_SDDQNQD-512G-1201_232557804368
```
Re-run the command with the correct disk:
```bash
clan templates apply disk single-disk jon --set mainDisk "/dev/disk/by-id/nvme-WD_PC_SN740_SDDQNQD-512G-1201_232557804368"
```
Should now be successful
```shellSession
Applied disk template 'single-disk' to machine 'jon'
```
A disko.nix file should be created in `machines/jon`
You can have a look and customize it if needed.
:::danger
Don't change the `disko.nix` after the machine is installed for the first time, unless you really know what you are doing.
Changing disko configuration requires wiping and reinstalling the machine.
:::
## Deploy the machine
**Finally deployment time!**
This command is destructive and will format your disk and install NixOS on it! It is equivalent to appending `--phases kexec,disko,install,reboot`.
```bash
clan machines install [MACHINE] --target-host root@<IP>
```

View File

@@ -0,0 +1,183 @@
---
title: Convert existing NixOS configurations
---
This guide will help you convert your existing NixOS configurations into a Clan.
:::caution
Migrating instead of starting new can be trickier and might lead to bugs or
unexpected issues. We recommend reading the [Getting Started](./index.md) guide first.
Once you have a working setup and understand the concepts transfering your NixOS configurations over is easy.
:::
## Back up your existing configuration
Before you start, it is strongly recommended to back up your existing
configuration in any form you see fit. If you use version control to manage
your configuration changes, it is also a good idea to follow the migration
guide in a separte branch until everything works as expected.
## Starting Point
We assume you are already using NixOS flakes to manage your configuration. If
not, migrate to a flake-based setup following the official [NixOS
documentation](https://nix.dev/manual/nix/2.25/command-ref/new-cli/nix3-flake.html).
The snippet below shows a common Nix flake. For this example we will assume you
have have two hosts: **berlin** and **cologne**.
```nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs, ... }: {
nixosConfigurations = {
berlin = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./machines/berlin/configuration.nix ];
};
cologne = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./machines/cologne/configuration.nix ];
};
};
};
}
```
## 1. Add `clan-core` to `inputs`
Add `clan-core` to your flake as input.
```nix
inputs.clan-core = {
url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
# Don't do this if your machines are on nixpkgs stable.
inputs.nixpkgs.follows = "nixpkgs";
}
```
## 2. Update Outputs
To be able to access our newly added dependency, it has to be added to the
output parameters.
```diff
- outputs = { self, nixpkgs, ... }:
+ outputs = { self, nixpkgs, clan-core }:
```
The existing `nixosConfigurations` output of your flake will be created by
clan. In addition, a new `clanInternals` output will be added. Since both of
these are provided by the output of `clan-core.lib.clan`, a common syntax is to use a
`let...in` statement to create your clan and access it's parameters in the flake
outputs.
For the provide flake example, your flake should now look like this:
```nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
inputs.clan-core = {
url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, clan-core, ... }:
let
clan = clan-core.lib.clan {
self = self; # this needs to point at the repository root
specialArgs = {};
meta.name = throw "Change me to something unique";
machines = {
berlin = {
nixpkgs.hostPlatform = "x86_64-linux";
imports = [ ./machines/berlin/configuration.nix ];
};
cologne = {
nixpkgs.hostPlatform = "x86_64-linux";
imports = [ ./machines/cologne/configuration.nix ];
};
};
};
in
{
inherit (clan.config) nixosConfigurations nixosModules clanInternals;
clan = clan.config;
};
}
```
✅ Et voilà! Your existing hosts are now part of a clan.
Existing Nix tooling
should still work as normal. To check that you didn't make any errors, run `nix
flake show` and verify both hosts are still recognized as if nothing had
changed. You should also see the new `clan` output.
```
nix flake show
git+file:///my-nixos-config
├───clan: unknown
└───nixosConfigurations
├───berlin: NixOS configuration
└───cologne: NixOS configuration
```
Of course you can also rebuild your configuration using `nixos-rebuild` and
veryify everything still works.
## 3. Add `clan-cli` to your `devShells`
At this point Clan is set up, but you can't use the CLI yet. To do so, it is
recommended to expose it via a `devShell` in your flake. It is also possible to
install it any other way you would install a package in Nix, but using a
developtment shell ensures the CLI's version will always be in sync with your
configuration.
A minimal example is provided below, add it to your flake outputs.
```nix
devShells."x86_64-linux".default = nixpkgs.legacyPackages."x86_64-linux".mkShell {
packages = [ clan-core.packages."x86_64-linux".clan-cli ];
}
```
To use the CLI, execute `nix develop` in the directory of your flake. The
resulting shell, provides you with the `clan` CLI tool. Since you will be using
it every time you interact with Clan, it is recommended to set up
[direnv](https://direnv.net/).
Verify everything works as expected by running `clan machines list`.
```
nix develop
[user@host:~/my-nixos-config]$ clan machines list
berlin
cologne
```
## Specify Targets
Clan needs to know where it can reach your hosts. For testing purpose set
`clan.core.networking.targetHost` to the machines adress or hostname.
```nix
# machines/berlin/configuration.nix
{
clan.core.networking.targetHost = "123.4.56.78";
}
```
See our guide on for properly [configuring machines networking](../networking/networking.md)
## Next Steps
You are now fully set up. Use the CLI to manage your hosts or proceed to
configure further services. At this point you should be able to run commands
like `clan machines update berlin` to deploy a host.

View File

@@ -0,0 +1,217 @@
---
title: USB Installer Image for Physical Machines
---
To install Clan on physical machines, you need to use our custom installer image. This is necessary for proper installation and operation.
:::note[Deploying to a Virtual Machine?]
If you're deploying to a virtual machine (VM), you can skip this section and go directly to the [Deploy Virtual Machine](./hardware-report-virtual.md) step. In this scenario, we automatically use [nixos-anywhere](https://github.com/nix-community/nixos-anywhere) to replace the kernel during runtime.
:::
:::tip[Why nixos-anywhere Doesn't Work on Physical Hardware?]
nixos-anywhere relies on [kexec](https://wiki.archlinux.org/title/Kexec) to replace the running kernel with our custom one. This method often has compatibility issues with real hardware, especially systems with dedicated graphics cards like laptops and servers, leading to crashes and black screens.
:::
:::tip[Reasons for a Custom Install Image]
Our custom install images are built to include essential tools like [nixos-facter](https://github.com/nix-community/nixos-facter) and support for [ZFS](https://wiki.archlinux.org/title/ZFS). They're also optimized to run on systems with as little as 1 GB of RAM, ensuring efficient performance even on lower-end hardware.
:::
## Prerequisites
- [x] A free USB Drive with at least 1.5GB (All data on it will be lost)
- [x] Linux/NixOS Machine with Internet
## Identify the USB Flash Drive
1. Insert your USB flash drive into your computer.
2. Identify your flash drive with `lsblk`:
```shellSession
lsblk
```
```{.shellSession hl_lines="2" .no-copy}
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb 8:0 1 117,2G 0 disk
└─sdb1 8:1 1 117,2G 0 part /run/media/qubasa/INTENSO
nvme0n1 259:0 0 1,8T 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot
└─nvme0n1p2 259:2 0 1,8T 0 part
└─luks-f7600028-9d83-4967-84bc-dd2f498bc486 254:0 0 1,8T 0 crypt /nix/store
```
:::tip
In this case the USB device is `sdb`
:::
3. Ensure all partitions on the drive are unmounted. Replace `sdb1` in the command below with your device identifier (like `sdc1`, etc.):
```shellSession
sudo umount /dev/sdb1
```
## Installer
=== "**Linux OS**"
**Create a Custom Installer**
We recommend to build your own installer because of the following reasons:
- Include your ssh public keys into the image that allows passwordless ssh connection later on.
- Set your preferred language and keymap
```bash
clan flash write --flake https://git.clan.lol/clan/clan-core/archive/main.tar.gz \
--ssh-pubkey $HOME/.ssh/id_ed25519.pub \
--keymap us \
--language en_US.UTF-8 \
--disk main /dev/sd<X> \
flash-installer
```
:::note
Replace `$HOME/.ssh/id_ed25519.pub` with a path to your SSH public key.
Replace `/dev/sd<X>` with the drive path you want to flash
:::
:::danger
Specifying the wrong device can lead to unrecoverable data loss.
The `clan flash` utility will erase the disk. Make sure to specify the correct device
:::
- **SSH-Pubkey Option**
To add an ssh public key into the installer image append the option:
```
--ssh-pubkey <pubkey_path>
```
If you do not have an ssh key yet, you can generate one with `ssh-keygen -t ed25519` command.
This ssh key will be installed into the root user.
- **Connect to the installer**
On boot, the installer will display on-screen the IP address it received from the network.
If you need to configure Wi-Fi first, refer to the next section.
If Multicast-DNS (Avahi) is enabled on your own machine, you can also access the installer using the `flash-installer.local` address.
- **List Keymaps**
You can get a list of all keymaps with the following command:
```
clan flash list keymaps
```
- **List Languages**
You can get a list of all languages with the following command:
```
clan flash list languages
```
=== "**Other OS**"
**Download Generic Installer**
For x86_64:
```shellSession
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso
```
For generic arm64 / aarch64 (probably does not work on raspberry pi...)
```shellSession
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-aarch64-linux.iso
```
:::note
If you don't have `wget` installed, you can use `curl --progress-bar -OL <url>` instead.
:::
## Flash the Installer to the USB Drive
:::danger
Specifying the wrong device can lead to unrecoverable data loss.
The `dd` utility will erase the disk. Make sure to specify the correct device (`of=...`)
For example if the USB device is `sdb` use `of=/dev/sdb` (on macOS it will look more like /dev/disk1)
:::
On Linux, you can use the `lsblk` utility to identify the correct disko
```
lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
```
On macos use `diskutil`:
```
diskutil list
```
Use the `dd` utility to write the NixOS installer image to your USB drive.
Replace `/dev/sd<X>` with your external drive from above.
```shellSession
sudo dd bs=4M conv=fsync status=progress if=./nixos-installer-x86_64-linux.iso of=/dev/sd<X>
```
- **Connect to the installer
On boot, the installer will display on-screen the IP address it received from the network.
If you need to configure Wi-Fi first, refer to the next section.
If Multicast-DNS (Avahi) is enabled on your own machine, you can also access the installer using the `nixos-installer.local` address.
## Boot From USB Stick
- To use, boot from the Clan USB drive with **secure boot turned off**. For step by step instructions go to [Disabling Secure Boot](../secure-boot.md)
## (Optional) Connect to Wifi Manually
If you don't have access via LAN the Installer offers support for connecting via Wifi.
```shellSession
iwctl
```
This will enter `iwd`
```{.console, .no-copy}
[iwd]#
```
Now run the following command to connect to your Wifi:
```{.shellSession .no-copy}
# Identify your network device.
device list
# Replace 'wlan0' with your wireless device name
# Find your Wifi SSID.
station wlan0 scan
station wlan0 get-networks
# Replace your_ssid with the Wifi SSID
# Connect to your network.
station wlan0 connect your_ssid
# Verify you are connected
station wlan0 show
```
If the connection was successful you should see something like this:
```{.console, .no-copy}
State connected
Connected network FRITZ!Box (Your router device)
IPv4 address 192.168.188.50 (Your new local ip)
```
Press ++ctrl+d++ to exit `IWD`.
:::caution
Press ++ctrl+d++ **again** to update the displayed QR code and connection information.
:::
You're all set up

View File

@@ -0,0 +1,31 @@
---
title: Generate Facts and Vars
---
Typically, this step is handled automatically when a machine is deployed. However, to enable the use of `nix flake check` with your configuration, it must be completed manually beforehand.
Currently, generating all the necessary facts requires two separate commands. This is due to the coexistence of two parallel secret management solutions:
the newer, recommended version (`clan vars`) and the older version (`clan facts`) that we are slowly phasing out.
To generate both facts and vars, execute the following commands:
```sh
clan facts generate && clan vars generate
```
### Check Configuration
Validate your configuration by running:
```bash
nix flake check
```
This command helps ensure that your system configuration is correct and free from errors.
:::tip
You can integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase.
:::

View File

@@ -0,0 +1,120 @@
---
title: Installing a Physical Machine
next: { link: "/getting-started/choose-disk", label: Configure Disk }
---
Now that you have created a machine, added some services, and set up secrets, this guide will walk you through how to deploy it.
### Prerequisites
- [x] RAM > 2GB
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
- [x] **Machine configuration**: See our basic [adding and configuring machine guide](./add-machines.md)
- [x] **Initialized secrets**: See [secrets](../secrets.md) for how to initialize your secrets.
- [x] **USB Flash Drive**: See [Clan Installer](./create-installer.md)
### Image Installer
This method makes use of the [image installers](./create-installer.md).
The installer will randomly generate a password and local addresses on boot, then run a SSH server with these preconfigured.
The installer shows its deployment relevant information in two formats, a text form, as well as a QR code.
This is an example of the booted installer.
```{ .bash .annotate .no-copy .nohighlight}
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ ┌───────────────────────────┐ │
│ │███████████████████████████│ # This is the QR Code (1) │
│ │██ ▄▄▄▄▄ █▀▄█▀█▀▄█ ▄▄▄▄▄ ██│ │
│ │██ █ █ █▀▄▄▄█ ▀█ █ █ ██│ │
│ │██ █▄▄▄█ █▀▄ ▀▄▄▄█ █▄▄▄█ ██│ │
│ │██▄▄▄▄▄▄▄█▄▀ ▀▄▀▄█▄▄▄▄▄▄▄██│ │
│ │███▀▀▀ █▄▄█ ▀▄ ▄▀▄█ ███│ │
│ │██▄██▄▄█▄▄▀▀██▄▀ ▄▄▄ ▄▀█▀██│ │
│ │██ ▄▄▄▄▄ █▄▄▄▄ █ █▄█ █▀ ███│ │
│ │██ █ █ █ █ █ ▄▄▄ ▄▀▀ ██│ │
│ │██ █▄▄▄█ █ ▄ ▄ ▄ ▀█ ▄███│ │
│ │██▄▄▄▄▄▄▄█▄▄▄▄▄▄█▄▄▄▄▄█▄███│ │
│ │███████████████████████████│ │
│ └───────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────────────┐ │
│ │Root password: cheesy-capital-unwell # password (2) │ │
│ │Local network addresses: │ │
│ │enp1s0 UP 192.168.178.169/24 metric 1024 fe80::21e:6ff:fe45:3c92/64 │ │
│ │enp2s0 DOWN │ │
│ │wlan0 DOWN # connect to wlan (3) │ │
│ │Onion address: 6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion │ │
│ │Multicast DNS: nixos-installer.local │ │
│ └─────────────────────────────────────────────────────────────────────────────────┘ │
│ Press 'Ctrl-C' for console access │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
1. This is not an actual QR code, because it is displayed rather poorly on text sites.
This would be the actual content of this specific QR code prettified:
```json
{
"pass": "cheesy-capital-unwell",
"tor": "6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion",
"addrs": [
"2001:9e8:347:ca00:21e:6ff:fe45:3c92"
]
}
```
To generate the actual QR code, that would be displayed use:
```shellSession
echo '{"pass":"cheesy-capital-unwell","tor":"6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion","addrs":["2001:9e8:347:ca00:21e:6ff:fe45:3c92"]}' | nix run nixpkgs#qrencode -- -s 2 -m 2 -t utf8
```
2. The root password for the installer medium.
This password is autogenerated and meant to be easily typeable.
3. See how to connect the installer medium to wlan [here](./create-installer.md).
:::tip
For easy sharing of deployment information via QR code, we highly recommend using [KDE Connect](https://apps.kde.org/de/kdeconnect/).
:::
There are two ways to deploy your machine:
### Generating a Hardware Report
The following command will generate a hardware report with [nixos-facter](https://github.com/nix-community/nixos-facter) and writes it back into your machine folder. The `--phases kexec` flag makes sure we are not yet formatting anything, instead if the target system is not a NixOS machine it will use [kexec](https://wiki.archlinux.org/title/Kexec) to switch to a NixOS kernel.
=== "Password"
**Password**
```terminal
clan machines install [MACHINE] \
--update-hardware-config nixos-facter \
--phases kexec \
--target-host root@192.168.178.169
```
=== "QR Code"
**QR Code**
**Using a JSON String or File Path**:
Copy the JSON string contained in the QR Code and provide its path or paste it directly:
```terminal
clan machines install [MACHINE] --json [JSON] \
--update-hardware-config nixos-facter \
--phases kexec
```
**Using an Image Containing the QR Code**:
Provide the path to an image file containing the QR code displayed by the installer:
```terminal
clan machines install [MACHINE] --png [PATH] \
--update-hardware-config nixos-facter \
--phases kexec
```
If you are using our template `[MACHINE]` would be `jon`
[Next Step (Choose Disk Format)](./choose-disk.md){ .md-button .md-button--primary }

View File

@@ -0,0 +1,36 @@
---
title: Generate a VM Hardware Report
---
Now that you have created a machine, added some services, and set up secrets, this guide will walk you through how to deploy it.
## Prerequisites
- [x] RAM > 2GB
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
- [x] **Machine configuration**: See our basic [adding and configuring machine guide](./add-machines.md)
Clan supports any cloud machine if it is reachable via SSH and supports `kexec`.
:::tip
NixOS can cause strange issues when booting in certain cloud environments.
If on Linode: Make sure that the system uses "Direct Disk boot kernel" (found in the configuration panel)
:::
The following command will generate a hardware report with [nixos-facter](https://github.com/nix-community/nixos-facter) and writes it back into your machine folder. The `--phases kexec` flag makes sure we are not yet formatting anything, instead if the target system is not a NixOS machine it will use [kexec](https://wiki.archlinux.org/title/Kexec) to switch to a NixOS kernel.
```terminal
clan machines install [MACHINE] \
--update-hardware-config nixos-facter \
--phases kexec \
--target-host myuser@<IP>
```
:::caution
After running the above command, be aware that the SSH login user changes from `myuser` to `root`. For subsequent SSH connections to the target machine, use `root` as the login user. This change occurs because the system switches to the NixOS kernel using `kexec`.
:::

View File

@@ -0,0 +1,133 @@
---
title: Update Machines
---
The Clan command line interface enables you to update machines remotely over SSH.
In this guide we will teach you how to set a `targetHost` in Nix,
and how to define a remote builder for your machine closures.
## Setting `targetHost`
Set the machines `targetHost` to the reachable IP address of the new machine.
This eliminates the need to specify `--target-host` in CLI commands.
```{.nix title="clan.nix" hl_lines="9"}
{
# Ensure this is unique among all clans you want to use.
meta.name = "my-clan";
inventory.machines = {
# Define machines here.
# The machine name will be used as the hostname.
jon = {
deploy.targetHost = "root@192.168.192.4"; # (1)
};
};
# [...]
}
```
The use of `root@` in the target address implies SSH access as the `root` user.
Ensure that the root login is secured and only used when necessary.
## Multiple Target Hosts
You can now experiment with a new interface that allows you to define multiple `targetHost` addresses for different VPNs. Learn more and try it out in our [networking guide](../networking/networking.md).
## Updating Machine Configurations
Execute the following command to update the specified machine:
```bash
clan machines update jon
```
All machines can be updated simultaneously by omitting the machine name:
```bash
clan machines update
```
---
## Advanced Usage
The following options are only needed for special cases, such as limited resources, mixed environments, or private flakes.
### Setting `buildHost`
If the machine does not have enough resources to run the NixOS **evaluation** or **build** itself,
it is also possible to specify a `buildHost` instead.
During an update, clan will ssh into the `buildHost` and run `nixos-rebuild` from there.
:::note
The `buildHost` option should be set directly within your machines Nix configuration, **not** under `inventory.machines`.
:::
```{.nix hl_lines="5" .no-copy}
clan {
# ...
machines = {
"jon" = {
clan.core.networking.buildHost = "root@<host_or_ip>";
};
};
};
```
### Overriding configuration with CLI flags
`buildHost` / `targetHost`, and other network settings can be temporarily overridden for a single command:
For the full list of flags refer to the [Clan CLI](../../reference/cli/index.md)
```bash
# Build on a remote host
clan machines update jon --build-host root@192.168.1.10
# Build locally (useful for testing or when the target has limited resources)
clan machines update jon --build-host local
```
:::note
Make sure the CPU architecture of the `buildHost` matches that of the `targetHost`
For example, if deploying to a macOS machine with an ARM64-Darwin architecture, you need a second macOS machine with the same architecture to build it.
:::
### Excluding a machine from `clan machine update`
To exclude machines from being updated when running `clan machines update` without any machines specified,
one can set the `clan.deployment.requireExplicitUpdate` option to true:
```{.nix hl_lines="5" .no-copy}
clan {
# ...
machines = {
"jon" = {
clan.deployment.requireExplicitUpdate = true;
};
};
};
```
This is useful for machines that are not always online or are not part of the regular update cycle.
### Uploading Flake Inputs
When updating remote machines, flake inputs are usually fetched by the build host.
However, if flake inputs require authentication (e.g., private repositories),
Use the `--upload-inputs` flag to upload all inputs from your local machine:
```bash
clan machines update jon --upload-inputs
```
This is particularly useful when:
- The flake references private Git repositories
- Authentication credentials are only available on local machine
- The build host doesn't have access to certain network resources

View File

@@ -0,0 +1,89 @@
---
title: Using Age Plugins with Clan Vars
sidebar:
order: 99
---
This guide explains how to set up YubiKey and other plugins for `clan vars` secrets.
By default the `clan vars` subcommand uses the `age` encryption tool, which supports various plugins.
---
## Supported Age Plugins
Below is a [list of popular `age` plugins](https://github.com/FiloSottile/awesome-age?tab=readme-ov-file#plugins) you can use with Clan. (Last updated: **September 12, 2025**)
- ⭐️ [**age-plugin-yubikey**](https://github.com/str4d/age-plugin-yubikey): YubiKey (and other PIV tokens) plugin.
- [**age-plugin-se**](https://github.com/remko/age-plugin-se): Apple Secure Enclave plugin.
- 🧪 [**age-plugin-tpm**](https://github.com/Foxboron/age-plugin-tpm): TPM 2.0 plugin.
- 🧪 [**age-plugin-tkey**](https://github.com/quite/age-plugin-tkey): Tillitis TKey plugin.
[**age-plugin-trezor**](https://github.com/romanz/trezor-agent/blob/master/doc/README-age.md): Hardware wallet plugin (TREZOR, Ledger, etc.).
- 🧪 [**age-plugin-sntrup761x25519**](https://github.com/keisentraut/age-plugin-sntrup761x25519): Post-quantum hybrid plugin (NTRU Prime + X25519).
- 🧪 [**age-plugin-fido**](https://github.com/riastradh/age-plugin-fido): Prototype symmetric encryption plugin for FIDO2 keys.
- 🧪 [**age-plugin-fido2-hmac**](https://github.com/olastor/age-plugin-fido2-hmac): FIDO2 plugin with PIN support.
- 🧪 [**age-plugin-sss**](https://github.com/olastor/age-plugin-sss): Shamir's Secret Sharing (SSS) plugin.
- 🧪 [**age-plugin-amnesia**](https://github.com/cedws/amnesia/blob/master/README.md#age-plugin-experimental): Adds Q&A-based identity wrapping.
> **Note:** Plugins marked with 🧪 are experimental. Plugins marked with ⭐️ are official.
---
## Using Plugin-Generated Keys
If you want to use `fido2 tokens` to encrypt your secret instead of the normal age secret key then you need to prefix your age secret key with the corresponding plugin name. In our case we want to use the `age-plugin-fido2-hmac` plugin so we replace `AGE-SECRET-KEY` with `AGE-PLUGIN-FIDO2-HMAC`.
:::tip
- On Linux the age secret key is located at `~/.config/sops/age/keys.txt`
- On macOS it is located at `/Users/admin/Library/Application Support/sops/age/keys.txt`
**Before**:
```hl_lines="2"
# public key: age1zdy49ek6z60q9r34vf5mmzkx6u43pr9haqdh5lqdg7fh5tpwlfwqea356l
AGE-SECRET-KEY-1QQPQZRFR7ZZ2WCV...
```
**After**:
```hl_lines="2"
# public key: age1zdy49ek6z60q9r34vf5mmzkx6u43pr9haqdh5lqdg7fh5tpwlfwqea356l
AGE-PLUGIN-FIDO2-HMAC-1QQPQZRFR7ZZ2WCV...
```
## Configuring Plugins in `flake.nix`
To use `age` plugins with Clan, you need to configure them in your `flake.nix` file. Heres an example:
```nix title="flake.nix"
{
inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
inputs.nixpkgs.follows = "clan-core/nixpkgs";
outputs = { self, clan-core, ... }:
let
# Define Clan configuration
clan = clan-core.lib.clan {
inherit self;
meta.name = "myclan";
# Add YubiKey and FIDO2 HMAC plugins
# Note: Plugins must be available in nixpkgs.
secrets.age.plugins = [
"age-plugin-yubikey"
"age-plugin-fido2-hmac"
];
machines = {
# Machine configurations (elided for brevity)
};
};
in
{
inherit (clan) nixosConfigurations nixosModules clanInternals;
# Additional configurations (elided for brevity)
};
}
```

View File

@@ -0,0 +1,292 @@
---
title: Advanced Vars Examples
---
This guide demonstrates complex, real-world patterns for the vars system.
## Certificate Authority with Intermediate Certificates
This example shows how to create a complete certificate authority with root and intermediate certificates using dependencies.
```nix
{
# Generate root CA (not deployed to machines)
clan.core.vars.generators.root-ca = {
files."ca.key" = {
secret = true;
deploy = false; # Keep root key offline
};
files."ca.crt".secret = false;
runtimeInputs = [ pkgs.step-cli ];
script = ''
step certificate create "My Root CA" \
$out/ca.crt $out/ca.key \
--profile root-ca \
--no-password \
--not-after 87600h
'';
};
# Generate intermediate key
clan.core.vars.generators.intermediate-key = {
files."intermediate.key" = {
secret = true;
deploy = true;
};
runtimeInputs = [ pkgs.step-cli ];
script = ''
step crypto keypair \
$out/intermediate.pub \
$out/intermediate.key \
--no-password
'';
};
# Generate intermediate certificate signed by root
clan.core.vars.generators.intermediate-cert = {
files."intermediate.crt".secret = false;
dependencies = [
"root-ca"
"intermediate-key"
];
runtimeInputs = [ pkgs.step-cli ];
script = ''
step certificate create "My Intermediate CA" \
$out/intermediate.crt \
$in/intermediate-key/intermediate.key \
--ca $in/root-ca/ca.crt \
--ca-key $in/root-ca/ca.key \
--profile intermediate-ca \
--not-after 8760h \
--no-password
'';
};
# Use the certificates in services
services.nginx.virtualHosts."example.com" = {
sslCertificate = config.clan.core.vars.generators.intermediate-cert.files."intermediate.crt".value;
sslCertificateKey = config.clan.core.vars.generators.intermediate-key.files."intermediate.key".path;
};
}
```
## Multi-Service Secret Sharing
Generate secrets that multiple services can use:
```nix
{
# Generate database credentials
clan.core.vars.generators.database = {
share = true; # Share across machines
files."password" = { };
files."connection-string" = { };
prompts.dbname = {
description = "Database name";
type = "line";
};
script = ''
# Generate password
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32 > $out/password
# Create connection string
echo "postgresql://app:$(cat $out/password)@localhost/$prompts/dbname" \
> $out/connection-string
'';
};
# PostgreSQL uses the password
services.postgresql = {
enable = true;
initialScript = pkgs.writeText "init.sql" ''
CREATE USER app WITH PASSWORD '${
builtins.readFile config.clan.core.vars.generators.database.files."password".path
}';
'';
};
# Application uses the connection string
systemd.services.myapp = {
serviceConfig.EnvironmentFile =
config.clan.core.vars.generators.database.files."connection-string".path;
};
}
```
## SSH Host Keys with Certificates
Generate SSH host keys and sign them with a CA:
```nix
{
# SSH Certificate Authority (shared)
clan.core.vars.generators.ssh-ca = {
share = true;
files."ca" = { secret = true; deploy = false; };
files."ca.pub" = { secret = false; };
runtimeInputs = [ pkgs.openssh ];
script = ''
ssh-keygen -t ed25519 -N "" -f $out/ca
mv $out/ca.pub $out/ca.pub
'';
};
# Host-specific SSH keys
clan.core.vars.generators.ssh-host = {
files."ssh_host_ed25519_key" = {
secret = true;
owner = "root";
group = "root";
mode = "0600";
};
files."ssh_host_ed25519_key.pub" = { secret = false; };
files."ssh_host_ed25519_key-cert.pub" = { secret = false; };
dependencies = [ "ssh-ca" ];
runtimeInputs = [ pkgs.openssh ];
script = ''
# Generate host key
ssh-keygen -t ed25519 -N "" -f $out/ssh_host_ed25519_key
# Sign with CA
ssh-keygen -s $in/ssh-ca/ca \
-I "host:${config.networking.hostName}" \
-h \
-V -5m:+365d \
$out/ssh_host_ed25519_key.pub
'';
};
# Configure SSH to use the generated keys
services.openssh = {
hostKeys = [{
path = config.clan.core.vars.generators.ssh-host.files."ssh_host_ed25519_key".path;
type = "ed25519";
}];
};
}
```
## WireGuard Mesh Network
Create a WireGuard configuration with pre-shared keys:
```nix
{
# Generate WireGuard keys for this host
clan.core.vars.generators.wireguard = {
files."privatekey" = {
secret = true;
owner = "systemd-network";
mode = "0400";
};
files."publickey" = { secret = false; };
files."preshared" = { secret = true; };
runtimeInputs = [ pkgs.wireguard-tools ];
script = ''
# Generate key pair
wg genkey > $out/privatekey
wg pubkey < $out/privatekey > $out/publickey
# Generate pre-shared key
wg genpsk > $out/preshared
'';
};
# Configure WireGuard
networking.wireguard.interfaces.wg0 = {
privateKeyFile = config.clan.core.vars.generators.wireguard.files."privatekey".path;
peers = [{
publicKey = "peer-public-key-here";
presharedKeyFile = config.clan.core.vars.generators.wireguard.files."preshared".path;
allowedIPs = [ "10.0.0.2/32" ];
}];
};
}
```
## Conditional Generation Based on Machine Role
Generate different secrets based on machine configuration:
```nix
{
clan.core.vars.generators = lib.mkMerge [
# All machines get basic auth
{
basic-auth = {
files."htpasswd" = { };
prompts.username = {
description = "Username for basic auth";
type = "line";
};
prompts.password = {
description = "Password for basic auth";
type = "hidden";
};
runtimeInputs = [ pkgs.apacheHttpd ];
script = ''
htpasswd -nbB "$prompts/username" "$prompts/password" > $out/htpasswd
'';
};
}
# Only servers get API tokens
(lib.mkIf config.services.myapi.enable {
api-tokens = {
files."admin-token" = { };
files."readonly-token" = { };
runtimeInputs = [ pkgs.openssl ];
script = ''
openssl rand -hex 32 > $out/admin-token
openssl rand -hex 16 > $out/readonly-token
'';
};
})
];
}
```
## Backup Encryption Keys
Generate and manage backup encryption keys:
```nix
{
clan.core.vars.generators.backup = {
share = true; # Same key for all backup sources
files."encryption.key" = {
secret = true;
deploy = true;
};
files."encryption.pub" = { secret = false; };
runtimeInputs = [ pkgs.age ];
script = ''
# Generate age key pair
age-keygen -o $out/encryption.key 2>/dev/null
# Extract public key
grep "public key:" $out/encryption.key | cut -d: -f2 | tr -d ' ' \
> $out/encryption.pub
'';
};
# Use in backup service
services.borgbackup.jobs.system = {
encryption = {
mode = "repokey-blake2";
passCommand = "cat ${config.clan.core.vars.generators.backup.files."encryption.key".path}";
};
};
}
```
## Tips and Best Practices
1. **Use dependencies** to build complex multi-stage generations
2. **Share generators** when the same secret is needed across machines
3. **Set appropriate permissions** for service-specific secrets
4. **Use prompts** for user-specific values that shouldn't be generated
5. **Combine secret and non-secret files** in the same generator when they're related
6. **Use conditional generation** with `lib.mkIf` for role-specific secrets

View File

@@ -0,0 +1,142 @@
---
title: Diving deeper
---
The `clan vars` subcommand is a powerful tool for managing machine-specific variables in a declarative and reproducible way. This guide will walk you through its usage, from setting up a generator to sharing and updating variables across machines.
For a detailed API reference, see the [vars module documentation](../../reference/clan.core/vars.md).
In this guide, you will learn how to:
1. Declare a `generator` in the machine's NixOS configuration.
2. Inspect the status of variables using the Clan CLI.
3. Generate variables interactively.
4. Observe the changes made to your repository.
5. Update the machine configuration.
6. Share the root password between multiple machines.
7. Change the root password when needed.
By the end of this guide, you will have a clear understanding of how to use `clan vars` to manage sensitive data, such as passwords, in a secure and efficient manner.
## Declare the generator
In this example, a `vars` `generator` is used to:
- prompt the user for the password
- run the required `mkpasswd` command to generate the hash
- store the hash in a file
- expose the file path to the nixos configuration
Create a new nix file `root-password.nix` with the following content and import it into your `configuration.nix`
```nix
{config, pkgs, ...}: {
clan.core.vars.generators.root-password = {
# prompt the user for a password
# (`password-input` being an arbitrary name)
prompts.password-input.description = "the root user's password";
prompts.password-input.type = "hidden";
# don't store the prompted password itself
prompts.password-input.persist = false;
# define an output file for storing the hash
files.password-hash.secret = false;
# define the logic for generating the hash
script = ''
cat $prompts/password-input | mkpasswd -m sha-512 > $out/password-hash
'';
# the tools required by the script
runtimeInputs = [ pkgs.mkpasswd ];
};
# ensure users are immutable (otherwise the following config might be ignored)
users.mutableUsers = false;
# set the root password to the file containing the hash
users.users.root.hashedPasswordFile =
# clan will make sure, this path exists
config.clan.core.vars.generators.root-password.files.password-hash.path;
}
```
## Inspect the status
Executing `clan vars list`, you should see the following:
```shellSession
$ clan vars list my_machine
root-password/password-hash: <not set>
```
...indicating that the value `password-hash` for the generator `root-password` is not set yet.
## Generate the values
This step is not strictly necessary, as deploying the machine via `clan machines update` would trigger the generator as well.
To run the generator, execute `clan vars generate` for your machine
```shellSession
$ clan vars generate my_machine
Enter the value for root-password/password-input (hidden):
```
After entering the value, the updated status is reported:
```shellSession
Updated var root-password/password-hash
old: <not set>
new: $6$RMats/YMeypFtcYX$DUi...
```
## Observe the changes
With the last step, a new file was created in your repository:
`vars/per-machine/my-machine/root-password/password-hash/value`
If the repository is a git repository, a commit was created automatically:
```shellSession
$ git log -n1
commit ... (HEAD -> master)
Author: ...
Date: ...
Update vars via generator root-password for machine grmpf-nix
```
## Update the machine
```shell
clan machines update my_machine
```
## Share root password between machines
If we just imported the `root-password.nix` from above into more machines, clan would ask for a new password for each additional machine.
If the root password instead should only be entered once and shared across all machines, the generator defined above needs to be declared as `shared`, by adding `share = true` to it:
```nix
{config, pkgs, ...}: {
clan.core.vars.generators.root-password = {
share = true;
# ...
}
}
```
Importing that shared generator into each machine, will ensure that the password is only asked once the first machine gets updated and then re-used for all subsequent machines.
## Change the root password
Changing the password can be done via this command.
Replace `my-machine` with your machine.
If the password is shared, just pick any machine that has the generator declared.
```shellSession
$ clan vars generate my-machine --generator root-password --regenerate
...
Enter the value for root-password/password-input (hidden):
Input received. Processing...
...
Updated var root-password/password-hash
old: $6$tb27m6EOdff.X9TM$19N...
new: $6$OyoQtDVzeemgh8EQ$zRK...
```

View File

@@ -0,0 +1,125 @@
---
title: Understanding Clan Vars
---
This guide explains the architecture and design principles behind the vars system.
## Architecture Overview
The vars system provides a declarative, reproducible way to manage generated files (especially secrets) in NixOS configurations.
## Data Flow
```mermaid
graph LR
A[Generator Script] --> B[Output Files]
C[User Prompts] --> A
D[Dependencies] --> A
B --> E[Secret Storage<br/>sops/password-store]
B --> F[Nix Store<br/>public files]
E --> G[Machine Deployment]
F --> G
```
## Key Design Principles
### 1. Declarative Generation
Unlike imperative secret management, vars are declared in your NixOS configuration and generated deterministically. This ensures reproducibility across deployments.
### 2. Separation of Concerns
- **Generation logic**: Defined in generator scripts
- **Storage**: Handled by pluggable backends (sops, password-store, etc.)
- **Deployment**: Managed by NixOS activation scripts
- **Access control**: Enforced through file permissions and ownership
### 3. Composability Through Dependencies
Generators can depend on outputs from other generators, enabling complex workflows:
```nix
# Dependencies create a directed acyclic graph (DAG)
A B C
D
```
This allows building sophisticated systems like certificate authorities where intermediate certificates depend on root certificates.
### 4. Type Safety
The vars system distinguishes between:
- **Secret files**: Only accessible via `.path`, deployed to `/run/secrets/`
- **Public files**: Accessible via `.value`, stored in nix store
This prevents accidental exposure of secrets in the nix store.
## Storage Backend Architecture
The vars system uses pluggable storage backends:
- **sops** (default): Integrates with clan's existing sops encryption
- **password-store**: For users already using pass
Each backend handles encryption/decryption transparently, allowing the same generator definitions to work across different security models.
## Timing and Lifecycle
### Generation Phases
1. **Pre-deployment**: `clan vars generate` creates vars before deployment
2. **During deployment**: Missing vars are generated automatically
3. **Regeneration**: Explicit regeneration with `--regenerate` flag
### The `neededFor` Option
Control when vars are available during system activation:
```nix
files."early-secret" = {
secret = true;
neededFor = [ "users" "groups" ]; # Available early in activation
};
```
## Advanced Patterns
### Multi-Machine Coordination
The `share` option enables cross-machine secret sharing:
```mermaid
graph LR
A[Shared Generator] --> B[Machine 1]
A --> C[Machine 2]
A --> D[Machine 3]
```
This is useful for:
- Shared certificate authorities
- Mesh VPN pre-shared keys
- Cluster join tokens
### Generator Composition
Complex systems can be built by composing simple generators:
```
root-ca → intermediate-ca → service-cert
ocsp-responder
```
Each generator focuses on one task, making the system modular and testable.
## Key Advantages
Compared to manual secret management, vars provides:
- **Declarative configuration**: Define once, generate consistently
- **Dependency management**: Build complex systems with generator dependencies
- **Type safety**: Separate handling of secret and public files
- **User prompts**: Gather input when needed
- **Easy regeneration**: Update secrets with a single command

View File

@@ -0,0 +1,147 @@
---
title: Introduction to Vars
---
The vars system is clan's declarative solution for managing generated files, secrets, and dynamic configuration in your NixOS deployments. It eliminates the manual steps of generating credentials, certificates, and other dynamic values by automating these processes within your infrastructure-as-code workflow.
## What Problems Does Vars Solve?
### Before Vars: Manual Secret Management
Traditional NixOS deployments require manual steps for secrets and generated files:
```bash
# Generate password hash manually
mkpasswd -m sha-512 > /tmp/root-password-hash
# Copy hash into configuration
users.users.root.hashedPasswordFile = "/tmp/root-password-hash";
```
This approach has several problems:
- **Not reproducible**: Manual steps vary between team members
- **Hard to maintain**: Updating secrets requires remembering manual commands
- **Deployment friction**: Secrets must be managed outside of your configuration
- **Team collaboration issues**: Sharing credentials securely is complex
### After Vars: Declarative Generation
With vars, the same process becomes declarative and automated:
```nix
clan.core.vars.generators.root-password = {
prompts.password.description = "Root password";
prompts.password.type = "hidden";
files.hash.secret = false;
script = "mkpasswd -m sha-512 < $prompts/password > $out/hash";
runtimeInputs = [ pkgs.mkpasswd ];
};
users.users.root.hashedPasswordFile =
config.clan.core.vars.generators.root-password.files.hash.path;
```
## Core Benefits
- **🔄 Reproducible**: Same inputs always produce the same outputs
- **📝 Declarative**: Defined alongside your NixOS configuration
- **🔐 Secure**: Automatic secret storage and encrypted deployment
- **👥 Collaborative**: Built-in sharing for team environments
- **🚀 Automated**: No manual intervention required for deployments
- **🔗 Integrated**: Works seamlessly with clan's deployment workflow
## How It Works
```mermaid
graph TB
A[Generator Declaration] --> B[clan vars generate]
B --> C{Prompts User}
C --> D[Execute Script]
D --> E[Output Files]
E --> F{Secret?}
F -->|Yes| G[Encrypted Storage]
F -->|No| H[Git Repository]
G --> I[Deploy to Machine]
H --> I
I --> J[Available in NixOS]
```
1. **Declare generators** in your NixOS configuration
2. **Generate values** using `clan vars generate` (or automatically during deployment)
3. **Store securely** in encrypted backends or version control
4. **Deploy seamlessly** to your machines where they're accessible as file paths
## Common Use Cases
| Use Case | What Gets Generated | Benefits |
|----------|-------------------|----------|
| **User passwords** | Password hashes | No plaintext in config |
| **SSH keys** | Host/user keypairs | Automated key rotation |
| **TLS certificates** | Certificates + private keys | Automated PKI |
| **Database credentials** | Passwords + connection strings | Secure service communication |
| **API tokens** | Random tokens | Service authentication |
| **Configuration files** | Complex configs with secrets | Dynamic config generation |
## Architecture Overview
The vars system has three main components:
### 1. **Generators**
Define how to create files from inputs:
- **Prompts**: Values requested from users
- **Scripts**: Generation logic
- **Dependencies**: Other generators this depends on
- **Outputs**: Files that get created
### 2. **Storage Backends**
Handle secret storage and deployment:
- **sops**: Encrypted files in git (recommended)
- **password-store**: GPG/age-based secret storage
## Quick Start Example
Here's a complete example showing password generation and usage:
```nix
# generator.nix
{ config, pkgs, ... }: {
clan.core.vars.generators.user-password = {
prompts.password = {
description = "User password";
type = "hidden";
};
files.hash = { secret = false; };
script = ''
mkpasswd -m sha-512 < $prompts/password > $out/hash
'';
runtimeInputs = [ pkgs.mkpasswd ];
};
users.users.myuser = {
hashedPasswordFile =
config.clan.core.vars.generators.user-password.files.hash.path;
};
}
```
```bash
# Generate the password
clan vars generate my-machine
# Deploy to machine
clan machines update my-machine
```
## Migration from Facts
If you're currently using the legacy facts system, see our [Migration Guide](../migrations/migration-facts-vars.md) for step-by-step instructions on upgrading to vars.

View File

@@ -0,0 +1,274 @@
---
title: Troubleshooting Vars
---
Quick reference for diagnosing and fixing vars issues.
## Common Issues
### Generator Script Fails
**Symptom**: Error during `clan vars generate` or deployment
**Possible causes and solutions**:
1. **Missing runtime inputs**
```nix
# Wrong - missing required tool
runtimeInputs = [ ];
script = ''
openssl rand -hex 32 > $out/secret # openssl not found!
'';
# Correct
runtimeInputs = [ pkgs.openssl ];
```
2. **Wrong output path**
```nix
# Wrong - must use $out
script = ''
echo "secret" > ./myfile
'';
# Correct
script = ''
echo "secret" > $out/myfile
'';
```
3. **Missing declared files**
```nix
files."config" = { };
files."key" = { };
script = ''
# Wrong - only generates one file
echo "data" > $out/config
'';
# Correct - must generate all declared files
script = ''
echo "data" > $out/config
echo "key" > $out/key
'';
```
### Cannot Access Generated Files
**Symptom**: "attribute 'value' missing" or file not found
**Solutions**:
1. **Secret files don't have `.value`**
```nix
# Wrong - secret files can't use .value
files."secret" = { secret = true; };
# ...
environment.etc."app.conf".text =
config.clan.core.vars.generators.app.files."secret".value;
# Correct - use .path for secrets
environment.etc."app.conf".source =
config.clan.core.vars.generators.app.files."secret".path;
```
2. **Public files should use `.value`**
```nix
# Better for non-secrets
files."cert.pem" = { secret = false; };
# ...
sslCertificate =
config.clan.core.vars.generators.ca.files."cert.pem".value;
```
### Dependencies Not Available
**Symptom**: "No such file or directory" when accessing `$in/...`
**Solution**: Declare dependencies correctly
```nix
clan.core.vars.generators.child = {
# Wrong - missing dependency
script = ''
cat $in/parent/file > $out/newfile
'';
# Correct
dependencies = [ "parent" ];
script = ''
cat $in/parent/file > $out/newfile
'';
};
```
### Permission Denied
**Symptom**: Service cannot read generated secret file
**Solution**: Set correct ownership and permissions
```nix
files."service.key" = {
secret = true;
owner = "myservice"; # Match service user
group = "myservice";
mode = "0400"; # Read-only for owner
};
```
### Vars Not Regenerating
**Symptom**: Changes to generator script don't trigger regeneration
**Solution**: Use `--regenerate` flag
```bash
clan vars generate my-machine --generator my-generator --regenerate
```
### Prompts Not Working
**Symptom**: Script fails with "No such file or directory" for prompts
**Solution**: Access prompts correctly
```nix
# Wrong
script = ''
echo $password > $out/file
'';
# Correct
prompts.password.type = "hidden";
script = ''
cat $prompts/password > $out/file
'';
```
## Debugging Techniques
### 1. Check Generator Status
See what vars are set:
```bash
clan vars list my-machine
```
### 2. Inspect Generated Files
For shared vars:
```bash
ls -la vars/shared/my-generator/
```
For per-machine vars:
```bash
ls -la vars/per-machine/my-machine/my-generator/
```
### 3. Test Generators Locally
Create a test script to debug:
```nix
# test-generator.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation {
name = "test-generator";
buildInputs = [ pkgs.openssl ]; # Your runtime inputs
buildCommand = ''
# Your generator script here
mkdir -p $out
openssl rand -hex 32 > $out/secret
ls -la $out/
'';
}
```
Run with:
```bash
nix-build test-generator.nix
```
### 4. Enable Debug Logging
Set debug mode:
```bash
clan --debug vars generate my-machine
```
### 5. Check File Permissions
Verify generated secret permissions:
```bash
# On the target machine
ls -la /run/secrets/
```
## Recovery Procedures
### Regenerate All Vars
If vars are corrupted or need refresh:
```bash
# Regenerate all for a machine
clan vars generate my-machine --regenerate
# Regenerate specific generator
clan vars generate my-machine --generator my-generator --regenerate
```
### Manual Secret Injection
For recovery or testing:
```bash
# Set a var manually (bypass generator)
echo "temporary-secret" | clan vars set my-machine my-generator/my-file
```
### Restore from Backup
Vars are stored in the repository:
```bash
# Restore previous version
git checkout HEAD~1 -- vars/
# Check and regenerate if needed
clan vars list my-machine
```
## Storage Backend Issues
### SOPS Decryption Fails
**Symptom**: "Failed to decrypt" or permission errors
**Solution**: Ensure your user/machine has the correct age keys configured. Clan manages encryption keys automatically based on the configured users and machines in your flake.
Check that:
1. Your machine is properly configured in the flake
2. Your user has access to the machine's secrets
3. The age key is available in the expected location
### Password Store Issues
**Symptom**: "pass: store not initialized"
**Solution**: Initialize password store:
```bash
export PASSWORD_STORE_DIR=/path/to/clan/vars
pass init your-gpg-key
```
## Getting Help
If these solutions don't resolve your issue:
1. Check the [clan-core issue tracker](https://git.clan.lol/clan/clan-core/issues)
2. Ask in the Clan community channels
3. Provide:
- The generator configuration
- The exact error message
- Output of `clan --debug vars generate`

View File

@@ -0,0 +1,148 @@
---
title: Welcome to Clan
---
## What is Clan?
<style>
.clamp-wrap {
--lines: 5; /* visible lines when collapsed */
--fade-height: 2.5rem;/* fade size */
font: inherit;
color: inherit;
position: relative;
}
/* Accessible, visually hidden checkbox */
.clamp-toggle {
position: absolute;
width: 1px; height: 1px;
margin: -1px; border: 0; padding: 0;
clip: rect(0 0 0 0); clip-path: inset(50%);
overflow: hidden; white-space: nowrap;
}
.clamp-content {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: var(--lines);
overflow: hidden;
/* Fade via mask (no overlay needed) */
-webkit-mask-image: linear-gradient(
to bottom,
black,
black calc(100% - var(--fade-height)),
transparent
);
mask-image: linear-gradient(
to bottom,
black,
black calc(100% - var(--fade-height)),
transparent
);
}
/* Right-aligned Read more/less */
.clamp-more {
display: block;
width: max-content;
/* margin-left: auto; */
margin-top: 0.5rem;
cursor: pointer;
color: #0057d9;
text-decoration: underline;
user-select: none;
}
.clamp-more:hover,
.clamp-more:focus { text-decoration: none; }
/* Expanded state */
.clamp-toggle:checked ~ .clamp-content {
-webkit-line-clamp: initial;
display: block;
-webkit-mask-image: none;
mask-image: none;
}
/* Dynamic label text */
.clamp-more::after { content: "Read more"; }
.clamp-toggle:checked ~ .clamp-more::after { content: "Read less"; }
</style>
<div class="clamp-wrap" style="--lines: 3;">
<input type="checkbox" id="clan-readmore" class="clamp-toggle" />
<div class="clamp-content">
<p><a href="https://clan.lol/">Clan</a> is a peer-to-peer computer management framework that empowers you to selfhost in a reliable and scalable way</strong>.</p>
<p>Built on NixOS, Clan provides a declarative interface for managing machines</strong> with automated <a href="./guides/secrets.md">secret management</a>, easy <a href="./guides/mesh-vpn.md">mesh VPN connectivity</a>, and <a href="./guides/backups.md">automated backups</a>.</p>
<p>Whether you're running a homelab or maintaining critical computing infrastructure, Clan will help reduce maintenance burden</strong> by allowing a git repository to define your whole network</strong> of computers.</p>
<p>In combination with <a href="https://github.com/Mic92/sops-nix">sops-nix</a>, <a href="https://github.com/nix-community/nixos-anywhere">nixos-anywhere</a> and <a href="https://github.com/nix-community/disko">disko</a>, Clan makes it possible to have collaborative infrastructure</strong>.</p>
<p>At the heart of Clan are <a href="./reference/clanServices/index.md">Clan Services</a> - the core concept that enables you to add functionality across multiple machines in your network. While Clan ships with essential core services, you can <a href="./guides/inventory/clanServices.md">create custom services</a> tailored to your specific needs.</p>
</div>
<label class="clamp-more" for="clan-readmore"></label>
</div>
---
[Get started](./guides/getting-started/index.md)
[View on Gitea](https://git.clan.lol/clan/clan-core)
## Guides
<div class="grid cards" markdown>
- [Inventory](./guides/inventory/inventory.md)
***
Learn how about inventory
- [Vars](./guides/vars/vars-overview.md)
***
Learn how to use vars
- [macOS](./guides/macos.md)
***
Using Clan to manage your macOS machines
</div>
## Reference
<div class="grid cards" markdown>
- [CLI](./reference/cli/index.md)
***
command line interface
- [Clan Options](/options)
***
Search all options
- [Services](./reference/clanServices/index.md)
***
Discover services
</div>
## Blog
<div class="grid cards" markdown>
- [Clan Blog](https://clan.lol/blog/)
***
For the latest updates, tutorials, and community stories.
</div>

View File

@@ -0,0 +1,107 @@
---
title: admin
description: Convenient Administration for the Clan App
---
*Convenient Administration for the Clan App*
---
## Roles
The admin module has the following roles:
- default
## Options for the `default` role
#### allowedKeys
The allowed public keys for ssh access to the admin user
**Type**: `attribute set of string`
**Default**:
```nix
{ }
```
```nix title="example"
{
key_1 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
}
```
📃 Declared in: [clanServices/admin/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/admin/default.nix)
#### certificateSearchDomains
List of domains to include in the certificate.
This option will prepend the machine name in front of each domain before adding it to the certificate.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
[
"mydomain.com"
]
```
📃 Declared in: [clanServices/admin/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/admin/default.nix)
#### rsaHostKey.enable
Whether to enable Generate RSA host key.
**Type**: `boolean`
**Default**:
```nix
false
```
```nix title="example"
true
```
📃 Declared in: [clanServices/admin/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/admin/default.nix)

View File

@@ -0,0 +1,218 @@
---
title: borgbackup
description: Efficient, deduplicating backup program with optional compression and secure encryption.
---
*Efficient, deduplicating backup program with optional compression and secure encryption.*
## Usage
```nix
inventory.instances = {
borgbackup = {
module = {
name = "borgbackup";
input = "clan-core";
};
roles.client.machines."jon".settings = {
destinations."storagebox" = {
repo = "username@$hostname:/./borgbackup";
rsh = ''ssh -oPort=23 -i /run/secrets/vars/borgbackup/borgbackup.ssh'';
};
};
roles.server.machines = { };
};
};
```
The input should be named according to your flake input. Jon is configured as a
client machine with a destination pointing to a Hetzner Storage Box.
## Overview
This guide explains how to set up and manage
[BorgBackup](https://borgbackup.readthedocs.io/) for secure, efficient backups
in a clan network. BorgBackup provides:
- Space efficient storage of backups with deduplication
- Secure, authenticated encryption
- Compression: lz4, zstd, zlib, lzma or none
- Mountable backups with FUSE
- Easy installation on multiple platforms: Linux, macOS, BSD, …
- Free software (BSD license).
- Backed by a large and active open-source community.
## Roles
### 1. Client
Clients are machines that create and send backups to various destinations. Each
client can have multiple backup destinations configured.
### 2. Server
Servers act as backup repositories, receiving and storing backups from client
machines. They can be dedicated backup servers within your clan network.
## Backup destinations
This service allows you to perform backups to multiple `destinations`.
Destinations can be:
- **Local**: Local disk storage
- **Server**: Your own borgbackup server (using the `server` role)
- **Third-party services**: Such as Hetzner's Storage Box
For a more comprehensive guide on backups look into the guide section.
---
## Roles
The borgbackup module has the following roles:
- client
- server
## Options for the `client` role
#### destinations
external destinations where the machine should be backuped to
**Type**: `attribute set of (submodule)`
**Default**:
```nix
{ }
```
📃 Declared in: [clanServices/borgbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/borgbackup/default.nix)
#### destinations.<name\>.name
the name of the backup job
**Type**: `string matching the pattern ^[a-zA-Z0-9._-]+$`
**Default**:
```nix
"name"
```
📃 Declared in: [clanServices/borgbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/borgbackup/default.nix)
#### destinations.<name\>.repo
the borgbackup repository to backup to
**Type**: `string`
📃 Declared in: [clanServices/borgbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/borgbackup/default.nix)
#### destinations.<name\>.rsh
the rsh to use for the backup
**Type**: `string`
**Default**:
```nix
"ssh -i \${config.clan.core.vars.generators.borgbackup.files.\"borgbackup.ssh\".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
```
📃 Declared in: [clanServices/borgbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/borgbackup/default.nix)
#### exclude
Directories/Files to exclude from the backup.
Use * as a wildcard.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
[
"*.pyc"
]
```
📃 Declared in: [clanServices/borgbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/borgbackup/default.nix)
## Options for the `server` role
#### directory
The directory where the borgbackup repositories are stored.
**Type**: `string`
**Default**:
```nix
"/var/lib/borgbackup"
```
📃 Declared in: [clanServices/borgbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/borgbackup/default.nix)

View File

@@ -0,0 +1,145 @@
---
title: certificates
description: Sets up a certificates internal to your Clan
---
*Sets up a certificates internal to your Clan*
This service sets up a certificate authority (CA) that can issue certificates to
other machines in your clan. For this the `ca` role is used.
It additionally provides a `default` role, that can be applied to all machines
in your clan and will make sure they trust your CA.
## Example Usage
The following configuration would add a CA for the top level domain `.foo`. If
the machine `server` now hosts a webservice at `https://something.foo`, it will
get a certificate from `ca` which is valid inside your clan. The machine
`client` will trust this certificate if it makes a request to
`https://something.foo`.
This clan service can be combined with the `coredns` service for easy to deploy,
SSL secured clan-internal service hosting.
```nix
inventory = {
machines.ca = { };
machines.client = { };
machines.server = { };
instances."certificates" = {
module.name = "certificates";
module.input = "self";
roles.ca.machines.ca.settings.tlds = [ "foo" ];
roles.default.machines.client = { };
roles.default.machines.server = { };
};
};
```
---
## Roles
The certificates module has the following roles:
- ca
- default
## Options for the `ca` role
#### acmeEmail
Email address for account creation and correspondence from the CA.
It is recommended to use the same email for all certs to avoid account
creation limits.
**Type**: `string`
**Default**:
```nix
"none@none.tld"
```
📃 Declared in: [clanServices/certificates/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/certificates/default.nix)
#### expire
When the certificate should expire.
**Type**: `null or string`
**Default**:
```nix
"8760h"
```
```nix title="example"
"8760h"
```
📃 Declared in: [clanServices/certificates/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/certificates/default.nix)
#### tlds
Top level domain for this CA. Certificates will be issued and trusted for *.<tld>
**Type**: `list of string`
📃 Declared in: [clanServices/certificates/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/certificates/default.nix)
## Options for the `default` role
#### acmeEmail
Email address for account creation and correspondence from the CA.
It is recommended to use the same email for all certs to avoid account
creation limits.
**Type**: `string`
**Default**:
```nix
"none@none.tld"
```
📃 Declared in: [clanServices/certificates/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/certificates/default.nix)

View File

@@ -0,0 +1,213 @@
---
title: coredns
description: Clan-internal DNS and service exposure
---
*Clan-internal DNS and service exposure*
This module enables hosting clan-internal services easily, which can be resolved
inside your VPN. This allows defining a custom top-level domain (e.g. `.clan`)
and exposing endpoints from a machine to others, which will be
accessible under `http://<service>.clan` in your browser.
The service consists of two roles:
- A `server` role: This is the DNS-server that will be queried when trying to
resolve clan-internal services. It defines the top-level domain.
- A `default` role: This does two things. First, it sets up the nameservers so
that clan-internal queries are resolved via the `server` machine, while
external queries are resolved as normal via DHCP. Second, it allows exposing
services (see example below).
## Example Usage
Here the machine `dnsserver` is designated as internal DNS-server for the TLD
`.foo`. `server01` will host an application that shall be reachable at
`http://one.foo` and `server02` is going to be reachable at `http://two.foo`.
`client` is any other machine that is part of the clan but does not host any
services.
When `client` tries to resolve `http://one.foo`, the DNS query will be
routed to `dnsserver`, which will answer with `192.168.1.3`. If it tries to
resolve some external domain (e.g. `https://clan.lol`), the query will not be
routed to `dnsserver` but resolved as before, via the nameservers advertised by
DHCP.
```nix
inventory = {
machines = {
dnsserver = { }; # 192.168.1.2
server01 = { }; # 192.168.1.3
server02 = { }; # 192.168.1.4
client = { }; # 192.168.1.5
};
instances = {
coredns = {
module.name = "@clan/coredns";
module.input = "self";
# Add the default role to all machines, including `client`
roles.default.tags.all = { };
# DNS server
roles.server.machines."dnsserver".settings = {
ip = "192.168.1.2";
tld = "foo";
};
# First service
roles.default.machines."server01".settings = {
ip = "192.168.1.3";
services = [ "one" ];
};
# Second service
roles.default.machines."server02".settings = {
ip = "192.168.1.4";
services = [ "two" ];
};
};
};
};
```
---
## Roles
The coredns module has the following roles:
- default
- server
## Options for the `default` role
#### dnsPort
Port of the clan-internal DNS server
**Type**: `signed integer`
**Default**:
```nix
1053
```
📃 Declared in: [clanServices/coredns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/coredns/default.nix)
#### ip
IP on which the services will listen
**Type**: `string`
📃 Declared in: [clanServices/coredns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/coredns/default.nix)
#### services
Service endpoints this host exposes (without TLD). Each entry will
be resolved to <entry>.<tld> using the configured top-level domain.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
📃 Declared in: [clanServices/coredns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/coredns/default.nix)
## Options for the `server` role
#### dnsPort
Port of the clan-internal DNS server
**Type**: `signed integer`
**Default**:
```nix
1053
```
📃 Declared in: [clanServices/coredns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/coredns/default.nix)
#### ip
IP for the DNS to listen on
**Type**: `string`
📃 Declared in: [clanServices/coredns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/coredns/default.nix)
#### tld
Top-level domain for this instance. All services below this will be
resolved internally.
**Type**: `string`
**Default**:
```nix
"clan"
```
📃 Declared in: [clanServices/coredns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/coredns/default.nix)

View File

@@ -0,0 +1,337 @@
---
title: data-mesher
description: Set up data-mesher
---
*Set up data-mesher*
This service will set up data-mesher.
## Usage
```nix
inventory.instances = {
data-mesher = {
module = {
name = "data-mesher";
input = "clan-core";
};
roles.admin.machines.server0 = {
settings = {
bootstrapNodes = {
node1 = "192.168.1.1:7946";
node2 = "192.168.1.2:7946";
};
network = {
hostTTL = "24h";
interface = "tailscale0";
};
};
};
roles.peer.machines.server1 = { };
roles.signer.machines.server2 = { };
};
}
```
---
## Roles
The data-mesher module has the following roles:
- admin
- peer
- signer
## Options for the `admin` role
#### bootstrapNodes
A list of bootstrap nodes that act as an initial gateway when joining
the cluster.
**Type**: `null or (list of string)`
```nix title="example"
[
"192.168.1.1:7946"
"192.168.1.2:7946"
]
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.hostTTL
The TTL for hosts in the network, in the form of a Go time.Duration
**Type**: `string`
**Default**:
```nix
"672h"
```
```nix title="example"
"24h"
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.interface
The interface over which cluster communication should be performed.
All the ip addresses associate with this interface will be part of
our host claim, including both ipv4 and ipv6.
This should be set to an internal/VPN interface.
**Type**: `string`
```nix title="example"
"tailscale0"
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.port
Port to listen on for cluster communication.
**Type**: `16 bit unsigned integer; between 0 and 65535 (both inclusive)`
**Default**:
```nix
7946
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.tld
Top level domain to use for the network
**Type**: `string`
**Default**:
```nix
"clan"
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
## Options for the `peer` role
#### bootstrapNodes
A list of bootstrap nodes that act as an initial gateway when joining
the cluster.
**Type**: `null or (list of string)`
```nix title="example"
[
"192.168.1.1:7946"
"192.168.1.2:7946"
]
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.interface
The interface over which cluster communication should be performed.
All the ip addresses associate with this interface will be part of
our host claim, including both ipv4 and ipv6.
This should be set to an internal/VPN interface.
**Type**: `string`
```nix title="example"
"tailscale0"
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.port
Port to listen on for cluster communication.
**Type**: `16 bit unsigned integer; between 0 and 65535 (both inclusive)`
**Default**:
```nix
7946
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
## Options for the `signer` role
#### bootstrapNodes
A list of bootstrap nodes that act as an initial gateway when joining
the cluster.
**Type**: `null or (list of string)`
```nix title="example"
[
"192.168.1.1:7946"
"192.168.1.2:7946"
]
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.interface
The interface over which cluster communication should be performed.
All the ip addresses associate with this interface will be part of
our host claim, including both ipv4 and ipv6.
This should be set to an internal/VPN interface.
**Type**: `string`
```nix title="example"
"tailscale0"
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)
#### network.port
Port to listen on for cluster communication.
**Type**: `16 bit unsigned integer; between 0 and 65535 (both inclusive)`
**Default**:
```nix
7946
```
📃 Declared in: [clanServices/data-mesher/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/data-mesher/default.nix)

View File

@@ -0,0 +1,335 @@
---
title: dyndns
description: A dynamic DNS service to update domain IPs
---
*A dynamic DNS service to update domain IPs*
A Dynamic-DNS (DDNS) service continuously keeps one or more DNS records in sync with the current public IP address of your machine.
In *clan* this service is backed by [qdm12/ddns-updater](https://github.com/qdm12/ddns-updater).
> Info
> ddns-updater itself is **heavily opinionated and version-specific**. Whenever you need the exhaustive list of flags or
> provider-specific fields refer to its *versioned* documentation **not** the GitHub README
---
# 1. Configuration model
Internally ddns-updater consumes a single file named `config.json`.
A minimal configuration for the registrar *Namecheap* looks like:
```json
{
"settings": [
{
"provider": "namecheap",
"domain": "sub.example.com",
"password": "e5322165c1d74692bfa6d807100c0310"
}
]
}
```
Another example for *Porkbun*:
```json
{
"settings": [
{
"provider": "porkbun",
"domain": "domain.com",
"api_key": "sk1_…",
"secret_api_key": "pk1_…",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
When you write a `clan.nix` the **common** fields (`provider`, `domain`, `period`, …) are already exposed as typed
*Nix options*.
Registrar-specific or very new keys can be passed through an open attribute set called **extraSettings**.
---
# 2. Full Porkbun example
Manage three records `@`, `home` and `test` of the domain
`jon.blog` and refresh them every 15 minutes:
```nix title="clan.nix" hl_lines="10-11"
inventory.instances = {
dyndns = {
roles.default.machines."jon" = { };
roles.default.settings = {
period = 15; # minutes
settings = {
"all-jon-blog" = {
provider = "porkbun";
domain = "jon.blog";
# (1) tell the secret-manager which key we are going to store
secret_field_name = "secret_api_key";
# everything below is copied verbatim into config.json
extraSettings = {
host = "@,home,test"; # (2) comma-separated list of sub-domains
ip_version = "ipv4";
ipv6_suffix = "";
api_key = "pk1_4bb2b231275a02fdc23b7e6f3552s01S213S"; # (3) public safe to commit
};
};
};
};
};
};
```
1. `secret_field_name` tells the *vars-generator* to store the entered secret under the specified JSON field name in the configuration.
2. ddns-updater allows multiple hosts by separating them with a comma.
3. The `api_key` above is *public*; the corresponding **private key** is retrieved through `secret_field_name`.
---
## Roles
The dyndns module has the following roles:
- default
## Options for the `default` role
#### period
Domain update period in minutes
**Type**: `signed integer`
**Default**:
```nix
5
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### server.acmeEmail
Email address for account creation and correspondence from the CA.
It is recommended to use the same email for all certs to avoid account
creation limits.
**Type**: `string`
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### server.domain
Domain to serve the webservice on
**Type**: `string`
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### server.enable
Whether to enable dyndns webserver.
**Type**: `boolean`
**Default**:
```nix
false
```
```nix title="example"
true
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### server.port
Port to listen on
**Type**: `signed integer`
**Default**:
```nix
54805
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### settings
Configuration for which domains to update
**Type**: `attribute set of (submodule)`
**Default**:
```nix
{ }
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### settings.<name\>.domain
The top level domain to update.
**Type**: `string`
```nix title="example"
"example.com"
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### settings.<name\>.extraSettings
Extra settings for the provider.
Provider specific settings: https://github.com/qdm12/ddns-updater#configuration
**Type**: `attribute set of string`
**Default**:
```nix
{ }
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### settings.<name\>.provider
The dyndns provider to use
**Type**: `string`
```nix title="example"
"namecheap"
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)
#### settings.<name\>.secret_field_name
The field name for the secret
**Type**: `one of "password", "token", "api_key", "secret_api_key"`
**Default**:
```nix
"password"
```
```nix title="example"
"api_key"
```
📃 Declared in: [clanServices/dyndns/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/dyndns/default.nix)

View File

@@ -0,0 +1,31 @@
---
title: emergency-access
description: Set recovery password for emergency access to machine
---
*Set recovery password for emergency access to machine*
This service will automatically set the emergency access password if your system fails to boot.
## Usage
```nix
inventory.instances = {
emergency-access = {
module = {
name = "emergency-access";
input = "clan-core";
};
roles.default.tags.nixos = { };
};
}
```
---
## Roles
The emergency-access module has the following roles:
- default
This role has no configuration

View File

@@ -0,0 +1,15 @@
---
title: garage
description: S3-compatible object store for small self-hosted geo-distributed deployments
---
*S3-compatible object store for small self-hosted geo-distributed deployments*
---
## Roles
The garage module has the following roles:
- default
This role has no configuration

View File

@@ -0,0 +1,61 @@
---
title: hello-world
description: This is a test
---
*This is a test*
---
## Roles
The hello-world module has the following roles:
- evening
- morning
## Options for the `evening` role
#### greeting
The greeting to use
**Type**: `string`
**Default**:
```nix
"Good evening"
```
📃 Declared in: [clanServices/hello-world/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/hello-world/default.nix)
## Options for the `morning` role
#### greeting
The greeting to use
**Type**: `string`
**Default**:
```nix
"Good morning"
```
📃 Declared in: [clanServices/hello-world/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/hello-world/default.nix)

View File

@@ -0,0 +1,41 @@
---
title: importer
description: Convenient, structured module imports for hosts.
---
*Convenient, structured module imports for hosts.*
The importer module allows users to configure importing modules in a flexible and structured way.
It exposes the `extraModules` functionality of the inventory, without any added configuration.
## Usage
```nix
inventory.instances = {
zone1 = {
module.name = "@clan/importer";
roles.default.tags.zone1 = {};
roles.default.extraModules = [ "modules/zone1.nix" ];
};
base = {
module.name = "@clan/importer";
roles.default.tags.all = {};
roles.default.extraModules = [ "modules/base.nix" ];
};
};
```
This will import the module `modules/base.nix` to all machines that have the `all` tag,
which by default is every machine managed by the clan.
And also import for all machines tagged with `zone1` the module at `modules/zone1.nix`.
---
## Roles
The importer module has the following roles:
- default
This role has no configuration

View File

@@ -0,0 +1,290 @@
---
title: localbackup
description: Automatically backups current machine to local directory.
---
*Automatically backups current machine to local directory.*
## Features
- Creates incremental snapshots using rsnapshot
- Supports multiple backup targets
- Mount/unmount hooks for external storage
- Pre/post backup hooks for custom scripts
- Configurable snapshot retention
- Automatic state folder detection
## Usage
Enable the localbackup service and configure backup targets:
```nix
instances = {
localbackup = {
module.name = "@clan/localbackup";
module.input = "self";
roles.default.machines."machine".settings = {
targets.external= {
directory = "/mnt/backup";
mountpoint = "/mnt/backup";
};
};
};
};
```
## Commands
The service provides these commands:
- `localbackup-create`: Create a new backup
- `localbackup-list`: List available backups
- `localbackup-restore`: Restore from backup (requires NAME and FOLDERS environment variables)
---
## Roles
The localbackup module has the following roles:
- default
## Options for the `default` role
#### snapshots
Number of snapshots to keep
**Type**: `signed integer`
**Default**:
```nix
20
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets
List of directories where backups are stored
**Type**: `attribute set of (submodule)`
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.directory
the directory to backup
**Type**: `string`
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.mountpoint
mountpoint of the directory to backup. If set, the directory will be mounted before the backup and unmounted afterwards
**Type**: `null or string`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.name
the name of the backup job
**Type**: `string matching the pattern ^[a-zA-Z0-9._-]+$`
**Default**:
```nix
"name"
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.postBackupHook
Shell commands to run after the backup
**Type**: `null or strings concatenated with "\n"`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.postMountHook
Shell commands to run after the directory is mounted
**Type**: `null or strings concatenated with "\n"`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.postUnmountHook
Shell commands to run after the directory is unmounted
**Type**: `null or strings concatenated with "\n"`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.preBackupHook
Shell commands to run before the backup
**Type**: `null or strings concatenated with "\n"`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.preMountHook
Shell commands to run before the directory is mounted
**Type**: `null or strings concatenated with "\n"`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)
#### targets.<name\>.preUnmountHook
Shell commands to run before the directory is unmounted
**Type**: `null or strings concatenated with "\n"`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/localbackup/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/localbackup/default.nix)

View File

@@ -0,0 +1,176 @@
---
title: matrix-synapse
description: A federated messaging server with end-to-end encryption.
---
*A federated messaging server with end-to-end encryption.*
---
## Roles
The matrix-synapse module has the following roles:
- default
## Options for the `default` role
#### acmeEmail
Email address for account creation and correspondence from the CA.
It is recommended to use the same email for all certs to avoid account
creation limits.
**Type**: `string`
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)
#### app_domain
The matrix server hostname also serves the element client
**Type**: `string`
```nix title="example"
"matrix.example.com"
```
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)
#### server_tld
The address that is suffixed after your username i.e @alice:example.com
**Type**: `string`
```nix title="example"
"example.com"
```
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)
#### services.matrix-synapse.package
Package to use for matrix-synapse
**Type**: `unspecified value`
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)
#### users
A list of users. Not that only new users will be created and existing ones are not modified.
**Type**: `attribute set of (submodule)`
**Default**:
```nix
{ }
```
```nix title="example"
{
alice = {
admin = true;
};
}
```
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)
#### users.<name\>.admin
Whether the user should be an admin
**Type**: `boolean`
**Default**:
```nix
false
```
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)
#### users.<name\>.name
The name of the user
**Type**: `string`
**Default**:
```nix
"name"
```
📃 Declared in: [clanServices/matrix-synapse/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/matrix-synapse/default.nix)

View File

@@ -0,0 +1,79 @@
---
title: monitoring
description: Monitoring service for the nodes in your clan
---
*Monitoring service for the nodes in your clan*
## Usage
```
inventory.instances = {
monitoring = {
module.name = "monitoring";
roles.telegraf.tags.all = {
settings.interfaces = [ "wg-clan" ];
};
};
};
```
This service will eventually set up a monitoring stack for your clan. For now,
only a telegraf role is implemented, which exposes the currently deployed
version of your configuration, so it can be used to check for required updates.
---
## Roles
The monitoring module has the following roles:
- telegraf
## Options for the `telegraf` role
#### allowAllInterfaces
If true, Telegraf will listen on all interfaces. Otherwise, it will only listen on the interfaces specified in `interfaces`
**Type**: `boolean`
**Default**:
```nix
false
```
📃 Declared in: [clanServices/monitoring/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/monitoring/default.nix)
#### interfaces
List of interfaces to expose the metrics to
**Type**: `list of string`
**Default**:
```nix
[
"zt+"
]
```
📃 Declared in: [clanServices/monitoring/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/monitoring/default.nix)

View File

@@ -0,0 +1,60 @@
---
title: mycelium
description: End-2-end encrypted IPv6 overlay network
---
*End-2-end encrypted IPv6 overlay network*
---
## Roles
The mycelium module has the following roles:
- peer
## Options for the `peer` role
#### addHostedPublicNodes
Add hosted Public nodes
**Type**: `boolean`
**Default**:
```nix
true
```
📃 Declared in: [clanServices/mycelium/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/mycelium/default.nix)
#### openFirewall
Open the firewall for mycelium
**Type**: `boolean`
**Default**:
```nix
true
```
📃 Declared in: [clanServices/mycelium/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/mycelium/default.nix)

View File

@@ -0,0 +1,45 @@
---
title: packages
description: Define package sets from nixpkgs and install them on one or more machines
---
*Define package sets from nixpkgs and install them on one or more machines*
---
## Roles
The packages module has the following roles:
- default
## Options for the `default` role
#### packages
The packages to install on the machine
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
[
"cowsay"
]
```
📃 Declared in: [clanServices/packages/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/packages/default.nix)

View File

@@ -0,0 +1,148 @@
---
title: sshd
description: Enables secure remote access to the machine over SSH
---
*Enables secure remote access to the machine over SSH*
The `sshd` Clan service manages SSH to make it easy to securely access your machines over the internet. The service uses `vars` to store the SSH host keys for each machine to ensure they remain stable across deployments.
`sshd` also generates SSH certificates for both servers and clients allowing for certificate-based authentication for SSH.
The service also disables password-based authentication over SSH, to access your machines you'll need to use public key authentication or certificate-based authentication.
## Usage
```nix
{
inventory.instances = {
# By default this service only generates ed25519 host keys
sshd-basic = {
module = {
name = "sshd";
input = "clan-core";
};
roles.server.tags.all = { };
roles.client.tags.all = { };
};
# Also generate RSA host keys for all servers
sshd-with-rsa = {
module = {
name = "sshd";
input = "clan-core";
};
roles.server.tags.all = { };
roles.server.settings = {
hostKeys.rsa.enable = true;
};
roles.client.tags.all = { };
};
};
}
```
---
## Roles
The sshd module has the following roles:
- client
- server
## Options for the `client` role
#### certificate.searchDomains
List of domains to include in the certificate.
This option will prepend the machine name in front of each domain
before adding it to the certificate.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
[
"mydomain.com"
]
```
📃 Declared in: [clanServices/sshd/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/sshd/default.nix)
## Options for the `server` role
#### certificate.searchDomains
List of domains to include in the certificate. This option will
prepend the machine name in front of each domain before adding
it to the certificate.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
[
"mydomain.com"
]
```
📃 Declared in: [clanServices/sshd/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/sshd/default.nix)
#### hostKeys.rsa.enable
Whether to enable Generate RSA host key.
**Type**: `boolean`
**Default**:
```nix
false
```
```nix title="example"
true
```
📃 Declared in: [clanServices/sshd/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/sshd/default.nix)

View File

@@ -0,0 +1,369 @@
---
title: syncthing
description: Syncthing is a continuous file synchronization program with automatic peer discovery
---
*Syncthing is a continuous file synchronization program with automatic peer discovery*
## Usage
```nix
{
instances.syncthing = {
roles.peer.tags.all = { };
roles.peer.settings.folders = {
documents = {
path = "~/syncthing/documents";
};
};
};
}
```
Now the folder `~/syncthing/documents` will be shared with all your machines.
## Documentation
Extensive documentation is available on the [Syncthing](https://docs.syncthing.net/) website.
---
## Roles
The syncthing module has the following roles:
- peer
## Options for the `peer` role
#### extraDevices
External syncthing devices not managed by clan (e.g., mobile phones)
**Type**: `attribute set of (submodule)`
**Default**:
```nix
{ }
```
```nix title="example"
{
phone = {
id = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2";
name = "My Phone";
addresses = [ "dynamic" ];
};
}
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### extraDevices.<name\>.addresses
List of addresses for the device
**Type**: `list of string`
**Default**:
```nix
[
"dynamic"
]
```
```nix title="example"
[
"dynamic"
"tcp://192.168.1.100:22000"
]
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### extraDevices.<name\>.id
Device ID of the external syncthing device
**Type**: `string`
```nix title="example"
"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### extraDevices.<name\>.name
Human readable name for the device
**Type**: `string`
**Default**:
```nix
""
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders
Folders to synchronize between all peers
**Type**: `attribute set of (submodule)`
**Default**:
```nix
{ }
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.devices
List of device names to share this folder with. Empty list means all peers and extraDevices.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.ignorePerms
Ignore permission changes
**Type**: `boolean`
**Default**:
```nix
false
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.path
Path to the folder to sync
**Type**: `string`
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.rescanIntervalS
Rescan interval in seconds
**Type**: `signed integer`
**Default**:
```nix
3600
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.type
Folder type
**Type**: `one of "sendreceive", "sendonly", "receiveonly"`
**Default**:
```nix
"sendreceive"
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.versioning
Versioning configuration
**Type**: `null or (submodule)`
**Default**:
```nix
null
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.versioning.params
Versioning parameters
**Type**: `attribute set of string`
**Default**:
```nix
{ }
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### folders.<name\>.versioning.type
Versioning type
**Type**: `one of "external", "simple", "staggered", "trashcan"`
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)
#### openDefaultPorts
Whether to open the default syncthing ports in the firewall.
**Type**: `boolean`
**Default**:
```nix
true
```
📃 Declared in: [clanServices/syncthing/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/syncthing/default.nix)

View File

@@ -0,0 +1,30 @@
---
title: trusted-nix-caches
description: This module sets the `clan.lol` and `nix-community` cache up as a trusted cache.
---
*This module sets the `clan.lol` and `nix-community` cache up as a trusted cache.*
Sets up nix to trust and use the clan cache
## Usage
```nix
inventory.instances = {
clan-cache = {
module = {
name = "trusted-nix-caches";
input = "clan-core";
};
roles.default.machines.draper = { };
};
}
```
---
## Roles
The trusted-nix-caches module has the following roles:
- default
This role has no configuration

View File

@@ -0,0 +1,242 @@
---
title: users
description: An instance of this module will create a user account on the added machines, along with a generated password that is constant across machines and user settings.
---
*An instance of this module will create a user account on the added machines,
along with a generated password that is constant across machines and user settings.
*
## Usage
```nix
{
inventory.instances = {
# Deploy user alice on all machines. Don't prompt for password (will be
# auto-generated).
user-alice = {
module = {
name = "users";
input = "clan-core";
};
roles.default.tags.all = { };
roles.default.settings = {
user = "alice";
prompt = false;
};
};
# Deploy user Carol on all machines. Prompt only once and use the
# same password on all machines. (`share = true`)
user-carol = {
module = {
name = "users";
input = "clan";
};
roles.default.tags.all = { };
roles.default.settings = {
user = "carol";
share = true;
};
};
# Deploy user bob only on his laptop. Prompt for a password.
user-bob = {
module = {
name = "users";
input = "clan-core";
};
roles.default.machines.bobs-laptop = { };
roles.default.settings.user = "bob";
};
};
}
```
## Migration from `root-password` module
The deprecated `clan.root-password` module has been replaced by the `users` module. Here's how to migrate:
### 1. Update your flake configuration
Replace the `root-password` module import with a `users` service instance:
```nix
# OLD - Remove this from your nixosModules:
imports = [
self.inputs.clan-core.clanModules.root-password
];
# NEW - Add to inventory.instances or machines/flake-module.nix:
instances = {
users-root = {
module.name = "users";
module.input = "clan-core";
roles.default.tags.nixos = { };
roles.default.settings = {
user = "root";
prompt = false; # Set to true if you want to be prompted
groups = [ ];
};
};
};
```
### 2. Migrate vars
The vars structure has changed from `root-password` to `user-password-root`:
```bash
# For each machine, rename the vars directories:
cd vars/per-machine/<machine-name>/
mv root-password user-password-root
mv user-password-root/password-hash user-password-root/user-password-hash
mv user-password-root/password user-password-root/user-password
```
---
## Roles
The users module has the following roles:
- default
## Options for the `default` role
#### groups
Additional groups the user should be added to.
You can add any group that exists on your system.
Make sure these group exists on all machines where the user is enabled.
Commonly used groups:
- "wheel" - Allows the user to run commands as root using `sudo`.
- "networkmanager" - Allows the user to manage network connections.
- "video" - Allows the user to access video devices.
- "input" - Allows the user to access input devices.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
[
"wheel"
"networkmanager"
"video"
"input"
]
```
📃 Declared in: [clanServices/users/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/users/default.nix)
#### prompt
Whether the user should be prompted for a password.
Effects:
- *enabled* (`true`) - Prompt for a password during the machine installation or update workflow.
- *disabled* (`false`) - Generate a password during the machine installation or update workflow.
The password can be shown in two steps:
- `clan vars list <machine-name>`
- `clan vars get <machine-name> <name-of-password-variable>`
**Type**: `boolean`
**Default**:
```nix
true
```
```nix title="example"
false
```
📃 Declared in: [clanServices/users/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/users/default.nix)
#### share
Weather the user should have the same password on all machines.
By default, you will be prompted for a new password for every host.
Unless `generate` is set to `true`.
**Type**: `boolean`
**Default**:
```nix
false
```
```nix title="example"
true
```
📃 Declared in: [clanServices/users/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/users/default.nix)
#### user
The user the password should be generated for.
**Type**: `string`
```nix title="example"
"alice"
```
📃 Declared in: [clanServices/users/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/users/default.nix)

View File

@@ -0,0 +1,109 @@
---
title: wifi
description: No description
---
*No description*
---
## Roles
The wifi module has the following roles:
- default
## Options for the `default` role
#### networks
Wifi networks to predefine
**Type**: `attribute set of (submodule)`
**Default**:
```nix
{ }
```
📃 Declared in: [clanServices/wifi/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wifi/default.nix)
#### networks.<name\>.autoConnect
Automatically try to join this wifi network
**Type**: `boolean`
**Default**:
```nix
true
```
📃 Declared in: [clanServices/wifi/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wifi/default.nix)
#### networks.<name\>.enable
Enable this wifi network
**Type**: `boolean`
**Default**:
```nix
true
```
📃 Declared in: [clanServices/wifi/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wifi/default.nix)
#### networks.<name\>.keyMgmt
Key management used for the connection.
One of "none" (WEP or no password protection), "ieee8021x" (Dynamic WEP), "owe" (Opportunistic Wireless Encryption), "wpa-psk" (WPA2 + WPA3 personal),
"sae" (WPA3 personal only), "wpa-eap" (WPA2 + WPA3 enterprise) or "wpa-eap-suite-b-192" (WPA3 enterprise only).
**Type**: `string`
**Default**:
```nix
"wpa-psk"
```
📃 Declared in: [clanServices/wifi/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wifi/default.nix)

View File

@@ -0,0 +1,390 @@
---
title: wireguard
description: Wireguard-based VPN mesh network with automatic IPv6 address allocation
---
*Wireguard-based VPN mesh network with automatic IPv6 address allocation*
# Wireguard VPN Service
This service provides a Wireguard-based VPN mesh network with automatic IPv6 address allocation and routing between clan machines.
## Overview
The wireguard service creates a secure mesh network between clan machines using two roles:
- **Controllers**: Machines with public endpoints that act as connection points and routers
- **Peers**: Machines that connect through controllers to access the network
## Requirements
- Controllers must have a publicly accessible endpoint (domain name or static IP)
- Peers must be in networks where UDP traffic is not blocked (uses port 51820 by default, configurable)
## Features
- Automatic IPv6 address allocation using ULA (Unique Local Address) prefixes
- Full mesh connectivity between all machines
- Automatic key generation and distribution
- IPv6 forwarding on controllers for inter-peer communication
- Support for multiple controllers for redundancy
## Network Architecture
### IPv6 Address Allocation
- Base network: `/40` ULA prefix (deterministically generated from instance name)
- Controllers: Each gets a `/56` subnet from the base `/40`
- Peers: Each gets a unique 64-bit host suffix that is used in ALL controller subnets
### Addressing Design
- Each peer generates a unique host suffix (e.g., `:8750:a09b:0:1`)
- This suffix is appended to each controller's `/56` prefix to create unique addresses
- Example: peer1 with suffix `:8750:a09b:0:1` gets:
- `fd51:19c1:3b:f700:8750:a09b:0:1` in controller1's subnet
- `fd51:19c1:c1:aa00:8750:a09b:0:1` in controller2's subnet
- Controllers allow each peer's `/96` subnet for routing flexibility
### Connectivity
- Peers use a single WireGuard interface with multiple IPs (one per controller subnet)
- Controllers connect to ALL other controllers and ALL peers on a single interface
- Controllers have IPv6 forwarding enabled to route traffic between peers
- All traffic between peers flows through controllers
- Symmetric routing is maintained as each peer has consistent IPs across all controllers
### Example Network Topology
```mermaid
graph TB
subgraph Controllers
C1[controller1<br/>endpoint: vpn1.example.com<br/>fd51:19c1:3b:f700::/56]
C2[controller2<br/>endpoint: vpn2.example.com<br/>fd51:19c1:c1:aa00::/56]
end
subgraph Peers
P1[peer1<br/>designated: controller1]
P2[peer2<br/>designated: controller2]
P3[peer3<br/>designated: controller1]
end
%% Controllers connect to each other
C1 <--> C2
%% All peers connect to all controllers
P1 <--> C1
P1 <--> C2
P2 <--> C1
P2 <--> C2
P3 <--> C1
P3 <--> C2
%% Peer-to-peer traffic flows through controllers
P1 -.->|via controllers| P3
P1 -.->|via controllers| P2
P2 -.->|via controllers| P3
classDef controller fill:#f9f,stroke:#333,stroke-width:4px
classDef peer fill:#bbf,stroke:#333,stroke-width:2px
class C1,C2 controller
class P1,P2,P3 peer
```
## Configuration
### Basic Setup with Single Controller
```nix
# In your flake.nix or inventory
{
services.wireguard.server1 = {
roles.controller = {
# Public endpoint where this controller can be reached
endpoint = "vpn.example.com";
# Optional: Change the UDP port (default: 51820)
port = 51820;
};
};
services.wireguard.laptop1 = {
roles.peer = {
# No configuration needed if only one controller exists
};
};
}
```
### Multiple Controllers Setup
```nix
{
services.wireguard.server1 = {
roles.controller = {
endpoint = "vpn1.example.com";
};
};
services.wireguard.server2 = {
roles.controller = {
endpoint = "vpn2.example.com";
};
};
services.wireguard.laptop1 = {
roles.peer = {
# Must specify which controller subnet is exposed as the default in /etc/hosts, when multiple controllers exist
controller = "server1";
};
};
}
```
### Advanced Options
### Automatic Hostname Resolution
The wireguard service automatically adds entries to `/etc/hosts` for all machines in the network. Each machine is accessible via its hostname in the format `<machine-name>.<instance-name>`.
For example, with an instance named `vpn`:
- `server1.vpn` - resolves to server1's IPv6 address
- `laptop1.vpn` - resolves to laptop1's IPv6 address
This allows machines to communicate using hostnames instead of IPv6 addresses:
```bash
# Ping another machine by hostname
ping6 server1.vpn
# SSH to another machine
ssh user@laptop1.vpn
```
## Troubleshooting
### Check Wireguard Status
```bash
sudo wg show
```
### Verify IP Addresses
```bash
ip addr show dev <instance-name>
```
### Check Routing
```bash
ip -6 route show dev <instance-name>
```
### Interface Fails to Start: "Address already in use"
If you see this error in your logs:
```
wireguard: Could not bring up interface, ignoring: Address already in use
```
This means the configured port (default: 51820) is already in use by another service or wireguard instance. Solutions:
1. **Check for conflicting wireguard instances:**
```bash
sudo wg show
sudo ss -ulnp | grep 51820
```
2. **Use a different port:**
```nix
services.wireguard.myinstance = {
roles.controller = {
endpoint = "vpn.example.com";
port = 51821; # Use a different port
};
};
```
3. **Ensure unique ports across multiple instances:**
If you have multiple wireguard instances on the same machine, each must use a different port.
### Key Management
Keys are automatically generated and stored in the clan vars system. To regenerate keys:
```bash
# Regenerate keys for a specific machine and instance
clan vars generate --service wireguard-keys-<instance-name> --regenerate --machine <machine-name>
# Apply the new keys
clan machines update <machine-name>
```
## Security Considerations
- All traffic is encrypted using Wireguard's modern cryptography
- Private keys never leave the machines they're generated on
- Public keys are distributed through the clan vars system
- Controllers must have publicly accessible endpoints
- Firewall rules are automatically configured for the Wireguard ports
---
## Roles
The wireguard module has the following roles:
- controller
- peer
## Options for the `controller` role
#### domain
Domain suffix to use for hostnames in /etc/hosts.
Defaults to the instance name.
**Type**: `null or string`
**Default**:
```nix
instanceName
```
📃 Declared in: [clanServices/wireguard/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wireguard/default.nix)
#### endpoint
Endpoint where the controller can be reached
**Type**: `string`
```nix title="example"
"vpn.clan.lol"
```
📃 Declared in: [clanServices/wireguard/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wireguard/default.nix)
#### port
Port for the wireguard interface
**Type**: `signed integer`
**Default**:
```nix
51820
```
```nix title="example"
51820
```
📃 Declared in: [clanServices/wireguard/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wireguard/default.nix)
## Options for the `peer` role
#### controller
Machinename of the controller to attach to
**Type**: `string`
```nix title="example"
"controller1"
```
📃 Declared in: [clanServices/wireguard/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wireguard/default.nix)
#### domain
Domain suffix to use for hostnames in /etc/hosts.
Defaults to the instance name.
**Type**: `null or string`
**Default**:
```nix
instanceName
```
📃 Declared in: [clanServices/wireguard/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wireguard/default.nix)
#### port
Port for the wireguard interface
**Type**: `signed integer`
**Default**:
```nix
51820
```
```nix title="example"
51820
```
📃 Declared in: [clanServices/wireguard/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/wireguard/default.nix)

View File

@@ -0,0 +1,131 @@
---
title: zerotier
description: Configuration of the secure and efficient Zerotier VPN
---
*Configuration of the secure and efficient Zerotier VPN*
## Usage
```
inventory.instances = {
zerotier = {
module = {
name = "zerotier";
input = "clan-core";
};
roles.peer.tags.all = { };
roles.controller.machines.jon = { };
roles.moon.machines.sara.settings.stableEndpoints = [ "77.52.165.46" ];
};
```
The input should be named according to your flake input.
All machines will be peers and connected to the zerotier network.
Jon is the controller machine, which will will accept other machines into the network.
Sara is a moon and sets the `stableEndpoint` setting with a publicly reachable IP, the moon is optional.
## Overview
This guide explains how to set up and manage a [ZeroTier VPN](https://zerotier.com) for a clan network. Each VPN requires a single controller and can support multiple peers and optional moons for better connectivity.
## Roles
### 1. Controller
The [Controller](https://docs.zerotier.com/controller/) manages network membership and is responsible for admitting new peers.
When a new node is added to the clan, the controller must be updated to ensure it has the latest member list.
- **Key Points:**
- Must be online to admit new machines to the VPN.
- Existing nodes can continue to communicate even when the controller is offline.
### 2. Moons
[Moons](https://docs.zerotier.com/roots) act as relay nodes,
providing direct connectivity to peers via their public IP addresses.
They enable devices that are not publicly reachable to join the VPN by routing through these nodes.
- **Configuration Notes:**
- Each moon must define its public IP address.
- Ensures connectivity for devices behind NAT or restrictive firewalls.
### 3. Peers
Peers are standard nodes in the VPN.
They connect to other peers, moons, and the controller as needed.
- **Purpose:**
- General role for all machines that are neither controllers nor moons.
- Ideal for most clan members devices.
---
## Roles
The zerotier module has the following roles:
- controller
- moon
- peer
## Options for the `controller` role
#### allowedIps
Extra machines by their zerotier ip that the zerotier controller
should accept. These could be external machines.
**Type**: `list of string`
**Default**:
```nix
[ ]
```
```nix title="example"
''
[ "fd5d:bbe3:cbc5:fe6b:f699:935d:bbe3:cbc5" ]
''
```
📃 Declared in: [clanServices/zerotier/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/zerotier/default.nix)
## Options for the `moon` role
#### stableEndpoints
Make this machine a moon.
Other machines can join this moon by adding this moon in their config.
It will be reachable under the given stable endpoints.
**Type**: `list of string`
```nix title="example"
''
[ "1.2.3.4" "10.0.0.3/9993" "2001:abcd:abcd::3/9993" ]
''
```
📃 Declared in: [clanServices/zerotier/default.nix](https://git.clan.lol/clan/clan-core/src/branch/main//clanServices/zerotier/default.nix)
This role has no configuration

View File

@@ -0,0 +1,5 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}