dotfiles/nixos/flake-modules/clan.nix
DannyDannyDanny 9921a7f9f1 feat(nix): zerotier overlay via clan inventory + mac ZT client 🕸️
Stage 4b of the clan migration. Declares a clan.inventory.instances.zerotier
instance with sunken-ship as controller and phantom-ship as peer (controller
is also listed as a peer so it joins its own network). Generates the network
ID, controller identity, and per-peer identities via `clan vars generate`;
all secrets are SOPS-encrypted to the user's age key and the per-machine
age keys.

- nixos/sops/ — clan-managed SOPS state (user + per-machine age keys).
- nixos/vars/ — shared + per-machine zerotier vars; *-identity-secret
  files are SOPS-encrypted, *.value files are plain public data.
- clan.core.networking.{targetHost,buildHost} = "danny@<host>" on both
  servers so `clan machines update` knows where to push and build.
- mac gets `zerotier-one` installed as a homebrew cask; authorization
  on the controller happens manually by node-ID in a follow-up step.

Known rough edges (to chase in later stages):
- zerotier-inventory-autoaccept.service races zerotierone.service on
  first activation (connection refused against the local API). Retrying
  the unit succeeds; clan upstream bug.
- Deployment must go through `clan machines update`, not plain
  nixos-rebuild, or the per-host SOPS age key isn't uploaded and
  zerotier-one can't decrypt its identity.
2026-04-19 14:43:29 +02:00

80 lines
2.8 KiB
Nix

# clan.lol wiring for the homelab.
#
# Declares `sunken-ship` and `phantom-ship` as clan machines. Each machine's
# `imports` list is the NixOS module set that used to live in its own
# flake-module. clan-core produces `flake.nixosConfigurations.<name>` from
# these, which is why the old per-host flake-modules were removed.
#
# The mac stays outside the clan — admin only, uses `clan machines update`
# to push to the servers.
{ config, inputs, ... }:
let
lib = inputs.nixpkgs.lib;
hmModule = { user, homeDirectory, stateVersion ? null, userImports ? [ ] }:
import ../lib/home-manager-user.nix {
inherit lib user homeDirectory stateVersion userImports;
};
in {
imports = [ inputs.clan-core.flakeModules.default ];
clan = {
meta.name = "homelab";
# Inventory machines — required for `inventory.instances` role bindings
# to resolve. Host-specific NixOS config lives under `machines.<name>`
# below.
inventory.machines.sunken-ship = { };
inventory.machines.phantom-ship = { };
# ZeroTier mesh VPN. sunken-ship is the controller (manages network
# membership); phantom-ship is a peer. The mac joins manually as an
# external ZT client and is authorized on the controller by node ID.
inventory.instances.zerotier = {
module.name = "zerotier";
module.input = "clan-core";
roles.controller.machines.sunken-ship = { };
roles.peer.machines.phantom-ship = { };
roles.peer.machines.sunken-ship = { };
};
# Preserve current network / init stack (no systemd-networkd/resolved,
# no boot.initrd.systemd, no extra debug packages). Revisit per-service
# in later stages rather than flipping this fleet-wide.
machines.sunken-ship = {
imports = [
{
clan.core.enableRecommendedDefaults = false;
clan.core.networking.targetHost = "danny@sunken-ship";
clan.core.networking.buildHost = "danny@sunken-ship";
}
../hosts/sunken-ship.nix
config.flake.nixosModules.dotfiles-rebuild
inputs.home-manager.nixosModules.home-manager
(hmModule {
user = "danny";
homeDirectory = "/home/danny";
stateVersion = "25.11";
})
];
};
machines.phantom-ship = {
imports = [
{
clan.core.enableRecommendedDefaults = false;
clan.core.networking.targetHost = "danny@phantom-ship";
clan.core.networking.buildHost = "danny@phantom-ship";
}
inputs.nix-openclaw.nixosModules.openclaw-gateway
../hosts/phantom-ship.nix
config.flake.nixosModules.dotfiles-rebuild
inputs.home-manager.nixosModules.home-manager
(hmModule {
user = "danny";
homeDirectory = "/home/danny";
stateVersion = "25.11";
})
];
};
};
}