NixOS realization root for the SourceOS Linux control-plane stack. Targets Apple Silicon M2 (Asahi Linux). Central artifact: hosts/builder-aarch64 — a NixOS host that runs a local Foreman+Katello content lifecycle stack and the sourceos-syncd daemon.
boot.loader.efi.canTouchEfiVariables = falseis MANDATORY on all Apple Silicon hosts. Modifying EFI variables bricks macOS boot.nixos-rebuild switchfor builder-aarch64 requires--impurebecausehardware-configuration.nixandenroll.nixare gitignored (device-specific) and not in the Nix store.- Never use
builtins.pathExistswith absolute paths (e.g./etc/sourceos/…) in Nix — it's an impure operation that fails in pure flake eval. Usesystemd.services.X.unitConfig.ConditionPathExistsinstead. sops.secrets.*.sopsFilepoints to/etc/sourceos/secrets.yaml(absolute, off-repo). Do NOT change it to a repo-relative path — secrets are device-specific and never committed.- All local Docker compose ports are loopback-only (
127.0.0.1:…). Never bind to0.0.0.0.
| Path | Purpose |
|---|---|
flake.nix |
Declares nixosConfigurations.builder-aarch64; inputs include sourceos-syncd-src, sourceos-boot-src (both flake = false), sops-nix, nixos-apple-silicon |
hosts/builder-aarch64/default.nix |
M2 host config: Asahi HW, Docker, harmonia, nginx, sops-nix, sourceos-syncd NixOS module |
hosts/builder-aarch64/enroll.nix |
Gitignored. Generated by scripts/enroll.sh. Device-specific: signingPublicKey, harmonia trusted-public-keys |
hosts/builder-aarch64/hardware-configuration.nix |
Gitignored. Generated by nixos-generate-config |
hosts/builder-aarch64/enroll.nix.example |
Template showing enroll.nix structure |
modules/nixos/sourceos-syncd/default.nix |
NixOS module for the sync daemon; wires systemd service + health-check timer |
packages/sourceos-syncd/default.nix |
buildPythonApplication derivation; stdlib-only runtime; jsonschema in nativeCheckInputs only |
packages/sourceos-boot/default.nix |
Same pattern for sourceos-boot |
scripts/enroll.sh |
One-shot M2 enrollment (12 steps, idempotent) |
scripts/doctor.sh |
Full stack health check; 14 components; --json flag |
scripts/promote.sh |
Promotes Katello content view dev→candidate→stable |
scripts/katello-sourceos-setup.sh |
Idempotent Katello org/product/repos/CV setup |
docs/bootstrap/M2_ENROLL.md |
Authoritative enrollment runbook |
Both sourceos-syncd and sourceos-boot use flake = false inputs (repos without their own flake.nix). They're passed as sourceos-syncd-src / sourceos-boot-src args to callPackage. In the builder-aarch64 nixosSystem they're built in a let block and passed via specialArgs.
Secrets live at /etc/sourceos/ (off-repo, device-local):
age.key— device age private key (generated by enroll.sh step 3)secrets.yaml— SOPS-encrypted Katello password (sops-nix decrypts at boot)harmonia-signing.key— Nix binary cache signing key (step 7)nix-cache.sec/nix-cache.pub— minisign key pair (step 8)
harmonia runs on 127.0.0.1:8099. nginx wraps it at :8101 and serves the nix-cache-info.minisig static file. sourceos-syncd fetches both before nix copy to verify cache identity. harmonia only starts when /etc/sourceos/harmonia-signing.key exists (ConditionPathExists).
After merging changes to sourceos-syncd or sourceos-boot:
nix flake update sourceos-syncd-src # or sourceos-boot-srcAll CI workflows in .github/workflows/ are workstation-related and run on ubuntu-latest. None try to build the builder-aarch64 NixOS config (intentional — it requires the device-specific gitignored files). Do not add CI that attempts nix build .#nixosConfigurations.builder-aarch64.config.system.build.toplevel.
| Repo | Role |
|---|---|
SourceOS-Linux/sourceos-syncd |
Katello sync daemon; emits SyncCycleReceipt |
SourceOS-Linux/sourceos-boot |
Asahi boot chain model; rollback executor |
SourceOS-Linux/sourceos-spec |
Canonical schemas (SyncCycleReceipt, BootReleaseSet, …) |
SocioProphet/agentplane |
Execution control plane; bridges bundles to Tekton+Katello |
SocioProphet/prophet-platform |
Foreman+Katello docker-compose at infra/local/docker-compose.foreman-katello.yml |