The orchestrator's flake now builds the machine it shares with the cc-ci CI
server: `nixosConfigurations.cc-ci` composes cc-ci's nixosModules.cc-ci-server
(new flake input, nixpkgs + sops-nix follow ours), this repo's orchestrator
module (nix/modules/cc-ci.nix, exported as cc-ci-orchestrator, `cc-ci` kept
as an alias for notplants-nix) and the new nix/modules/orchestrator-host.nix
— the host contract those units always assumed (loops user, claude/opencode
CLIs, opencode web server + tailnet-only UI on 8443 since traefik owns
80/443, nix-ld, tool set, `ssh cc-ci` → loopback).
nix/hosts/cc-ci/{hardware,networking}.nix are PROVISIONAL copies of the old
server's layout so the flake evaluates; they get replaced by the
nixos-infect output of 195.201.88.249.
README.md is the deploy guide (Hetzner Debian → nixos-infect → this flake →
staging → data restore → cutover). archive/ holds the retired Incus/Hetzner
orchestrator host configs, the old terraform and the migration plans;
references updated. cc-ci-plan/plan-cc-ci-combined-host.md is the working
plan for the move.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
69 lines
3.3 KiB
Nix
69 lines
3.3 KiB
Nix
# cc-ci — ONE Hetzner Cloud host running both the cc-ci CI server and the cc-ci orchestrator.
|
||
#
|
||
# This file is only what is physical or identity about the machine: hardware, networking, the
|
||
# tailscale node, root SSH keys, swap, stateVersion. Everything functional comes from modules:
|
||
# cc-ci.nixosModules.cc-ci-server recipe-maintainers/cc-ci — swarm, traefik, drone,
|
||
# runner, bridge, dashboard, reports, acme-dns, harness
|
||
# self.nixosModules.cc-ci-orchestrator nix/modules/cc-ci.nix — loops, orchestrator, timers
|
||
# self.nixosModules.orchestrator-host nix/modules/orchestrator-host.nix — loops user, CLIs
|
||
# See README.md for provisioning (Hetzner Debian → nixos-infect → this flake) and staging.
|
||
{ lib, pkgs, ... }:
|
||
{
|
||
imports = [
|
||
./hardware.nix
|
||
./networking.nix
|
||
];
|
||
|
||
networking.hostName = "cc-ci";
|
||
|
||
# ---- cc-ci server identity --------------------------------------------------------------
|
||
# Public address: acme-dns binds to it and publishes it as the `ns-acme` glue record; the
|
||
# Gandi A records for ci / *.ci / ns-acme .commoninternet.net point here.
|
||
cc-ci.publicIPv4 = "195.201.88.249";
|
||
# cc-ci is a plain flake input here (no private submodule), so the sops file is the one in
|
||
# the deployed --recursive checkout the weekly sweep runs from (README "Stage the workspace").
|
||
cc-ci.sopsFile = "/etc/cc-ci/secrets/secrets.yaml";
|
||
|
||
# ---- orchestrator identity --------------------------------------------------------------
|
||
# The CI server is this very host, so `ssh cc-ci` goes to loopback (the module default).
|
||
cc-ci-orchestrator.ciSshHost = "127.0.0.1";
|
||
|
||
# ---- tailscale — auth key staged out of band at /etc/ts-auth-key -----------------------
|
||
services.tailscale = {
|
||
enable = true;
|
||
authKeyFile = "/etc/ts-auth-key";
|
||
extraUpFlags = [ "--hostname=cc-ci" ];
|
||
};
|
||
|
||
# ---- ssh ----------------------------------------------------------------------------------
|
||
services.openssh = {
|
||
enable = true;
|
||
settings.PermitRootLogin = "yes";
|
||
};
|
||
# Root keys: PUBLIC keys, tracked deliberately in ./ssh-keys (one per line, blank lines ok).
|
||
users.users.root.openssh.authorizedKeys.keys =
|
||
builtins.filter (s: s != "") (lib.splitString "\n" (builtins.readFile ./ssh-keys));
|
||
# The loops user can also be reached directly (same keys) — handy for rsync of its workspace.
|
||
users.users.loops.openssh.authorizedKeys.keys =
|
||
builtins.filter (s: s != "") (lib.splitString "\n" (builtins.readFile ./ssh-keys));
|
||
|
||
# ---- firewall -------------------------------------------------------------------------------
|
||
# 80/443 (traefik) and 53 (acme-dns) are opened by the cc-ci-server module. The tailscale
|
||
# interface is trusted, which is what makes the opencode UI on 8443 tailnet-only.
|
||
networking.firewall = {
|
||
enable = true;
|
||
trustedInterfaces = [ "tailscale0" ];
|
||
allowedTCPPorts = [ 22 ];
|
||
};
|
||
networking.nameservers = [ "1.1.1.1" "8.8.8.8" ];
|
||
|
||
# ---- memory: 8 GB RAM shared by the swarm (recipe deploys) and 3–6 agent sessions ---------
|
||
swapDevices = [ { device = "/swapfile"; size = 8192; } ];
|
||
|
||
# ssh client for root (the orchestrator's `ssh cc-ci` goes through the loops user's own config).
|
||
environment.systemPackages = [ pkgs.openssh ];
|
||
|
||
# Fresh NixOS 26.05 install (nixos-infect, 2026-09-07). Never change this on an existing host.
|
||
system.stateVersion = "26.05";
|
||
}
|