System modules
⚙ darkone.system.core
Section titled “⚙ darkone.system.core”The core DNF module.
Sets up the systemd-boot loader, the LTS kernel (or latest mainline via
enableLatestKernel), JetBrains Mono Nerd
Font (with kmscon for the TTY), nightly Nix store optimisation and
weekly garbage collection (30 d retention, or keep-last-N via
gcKeepGenerations on disk-constrained hosts), the firewall, suspend
policy (servers can fully disable it via disableSuspend), polkit
rules letting wheel halt/reboot, and a shared common-files
group/user for cross-service media folders.
- enable
boolDarkone framework core system (activated by default) - enableSystemdBoot
boolEnable the default boot loader - enableLatestKernel
boolUse the latest mainline kernel (linuxPackages_latest) instead of the LTS one - enableFstrim
boolSSD optimisation with fstrim - enableFirewall
boolEnable firewall (default true) - enableSops
boolEnable sops dnf module (default true) - enableFlatpak
boolEnable flatpak DNF configuration (only for graphic environments) - enableKmscon
boolEnable nerd font for TTY - gcKeepGenerations
intWhen > 0, trim the system profile to the last N generations before each garbage collection, and collect daily without age-based retention. For hosts with a tight root partition (gateways). - enableBoost
boolEnable overclocking, corectl - enableAutoSuspend
boolEnable automatic suspend (for laptops, ignored if disableSuspend is true) - disableSuspend
boolFull suspend disable (for servers) - enableCommonFilesUser
boolEnable the common-files user used by several services
darkone.system.core = { enable = true; enableSystemdBoot = true; enableLatestKernel = false; enableFstrim = true; enableFirewall = true; enableSops = true; enableFlatpak = true; enableKmscon = true; gcKeepGenerations = 5; enableBoost = false; enableAutoSuspend = false; disableSuspend = false; enableCommonFilesUser = false;};⚙ darkone.system.documentation
Section titled “⚙ darkone.system.documentation”Documentation (man) for developers and admins.
- enable
boolEnable useful technical documentation (man, nixos)
darkone.system.documentation.enable = false;⚙ darkone.system.hardware
Section titled “⚙ darkone.system.hardware”Hardware configuration improvements.
- enable
boolEnable hardware optimisations - enableIntel
boolEnable intel microcode updates - enableAmd
boolEnable amd microcode updates
darkone.system.hardware = { enable = false; enableIntel = false; enableAmd = false;};⚙ darkone.system.i18n
Section titled “⚙ darkone.system.i18n”Location and lang configuration.
- enable
boolEnable i18n with network zone configuration by default - locale
strMatching localeRegexNetwork locale, must match thexx_YY.UTF-8shape. - timeZone
strNetwork time zone
darkone.system.i18n = { enable = false; locale = "fr_FR.UTF-8"; timeZone = "Europe/Paris";};⚙ darkone.system.luks
Section titled “⚙ darkone.system.luks”LUKS passphrase policy & remote unlock: shared + per-host passphrases, initrd SSH.
Completes modules/system/yubikey.nix (FIDO2 keyslots) with the passphrase
side of the fleet LUKS policy. Every encrypted volume carries exactly two
managed passphrases, plus one keyslot per enrolled YubiKey:
| Keyslot | Secret | Audience |
|---|---|---|
| shared | sops luks-passphrase | the fleet admin. Also authorizes keyslot operations (luksAddKey / luksKillSlot) for the units below, and is the key disko formats the disk with. |
| per-host | sops luks/<host>/passphrase | the user of that one machine, without handing over the fleet. |
| FIDO2 | registry usr/secrets/yubikeys.json | one slot per key, on every encrypted host. |
Two managed passphrases and not one, because they serve different people — and because a rotation of either stays authorized by the other, so the sync unit is never left without an authorizer (it adds the new slot before killing the old one).
There is no separate “install passphrase”: just install formats the volume
through nixos-anywhere --disk-encryption-keys with the shared passphrase, so
the format keyslot is the shared keyslot.
just luks <host> provisions a host (idempotent, also called by
just configure); its pre phase runs offline and is called by just install
before the build. It records the host in the public manifest
usr/secrets/luks.json (committed), stores the per-host passphrase and the
initrd SSH host key in sops, and ensures the shared luks-passphrase exists.
just luks <host> show prints the keyslot table and audits remote unlock.
The module then keeps everything converged at each apply:
- keyslots: a oneshot service syncs the shared and per-host passphrases into every disko-declared LUKS2 header. A changed sops value rotates the corresponding keyslot (old slot killed, new one added). Keyslot operations are authorized by whichever managed passphrase still unlocks the volume, so one of them must remain valid. Slots outside its ledger (FIDO2, manual enrollments) are never touched.
- remote unlock: initrd sshd on a dedicated port (2222), own persistent
host key (
/var/lib/luks-initrd/), root login with thenixdeploy key (usr/secrets/nix.pub).just unlock <host>answers the passphrase prompt without a human (through thednf-unlockhelper shipped in the initrd);just enter <host>is the interactive path. Both fall back to the WAN IP recorded in the manifest when the VPN route died with the host. Regular hosts DHCP on wired interfaces (a laptop on Wi-Fi has no initrd network and falls back to console unlock); zone gateways replicate their production layout instead — static LAN IP on the lan0 bridge, DHCP on the WAN side — since they are themselves the DHCP server their initrd would otherwise wait on.
- enable
boolFleet LUKS passphrase policy (inert untiljust luks <host>provisions the host) - sshPort
portInitrd sshd port; distinct from 22 so the initrd host key never clashes with the system one - volumes
listOf strLUKS volumes declared by the host disko layout.
darkone.system.luks = { enable = true; sshPort = 2222; volumes = [ ];};⚙ darkone.system.security
Section titled “⚙ darkone.system.security”ANSSI BP-028 v2.0 (GNU/Linux) system hardening. (wip)
Recommended module, enabled in certain host profiles or manually depending on needs. Progressively applies ANSSI recommendations according to the chosen level and machine category. Each host profile defines its level and category:
darkone.system.security = { level = "intermediary"; # minimal | intermediary | reinforced | high category = "server"; # base | client | server};Rules incompatible with the environment are excluded by tag:
darkone.system.security.excludes = [ "needs-jit" "needs-hibernation" ];A specific rule can be bypassed with a mandatory rationale:
darkone.system.security.exceptions = { R9.rationale = "Docker rootless required during development.";};- enable
boolEnable the ANSSI BP-028 v2.0 hardening module. - level
enum [ "minimal" "intermediary" "reinforced" "high" ]Targeted ANSSI hardening level. - category
enum [ "base" "client" "server" ]Machine category that selects rule subsets. - excludes
listOf strTags that disable entire rule groups. - exceptions
attrsPer-rule exceptions with mandatory rationale. - adminMailbox
strAdministrator email address (sudo R39, MTA aliases R75). - useHardenedKernel
boolUse linuxPackages_hardened (R60, C1) instead of the default kernel. - allowedActiveUsers
listOf strHuman accounts allowed to authenticate (R30). Defaults to every user declared in config.yaml (host.users) plus the framework deploy accountnix. Disabled accounts (no password credential) and service accounts (not normal users) need not be listed.
darkone.system.security = { enable = false; level = "minimal"; category = "base"; excludes = [ ]; exceptions = { }; adminMailbox = "admin@exemple.fr"; useHardenedKernel = false; allowedActiveUsers = [ ];};⚙ darkone.system.services
Section titled “⚙ darkone.system.services”DNF Service registration and configuration.
- enable
boolEnable DNF services manager to register and expose services - service
attrsGlobal services configuration <name>- enable
boolEnable service proxy - defaultParams
submoduleTheses options are calculated by dnfLib.srv.extractServiceParams- domain
strDomain name for the service - title
strDisplay name in homepage - description
strService description for homepage - icon
strIcon name for homepage 🡕 - global
boolGlobal service is accessible on Internet - noRobots
boolPrevent robots from scanning if global is true - fqdn
strCalculated FQDN or the service before the reverse proxy - href
strCalculated URL of the service before the reverse proxy - ip
strCalculated IP to contact the service
- domain
- displayOnHomepage
boolDisplay a link on homepage - reverseProxy
boolReached through the zone gateway reverse proxy (DNS points to the gateway LAN IP) - uniquePerZone
boolAt most one instance allowed per zone (generator validation) - externalAccess
boolwww-zone service reachable from the LAN via a fixed host IP (e.g. headscale, turn) - persist.dirs
listOf strService persistant dirs - persist.files
listOf strService persistant files - persist.dbDirs
listOf strService persistant dirs with database(s) - persist.dbFiles
listOf strService database file(s) - persist.varDirs
listOf strVariable secondary files (log, cache, etc.) - persist.mediaDirs
listOf strService media dirs (pictures, videos, big files) - proxy.enable
boolWhether to create virtualHost configuration (false for services that manage their own) - proxy.isProtected
boolOauth2 protected service - proxy.allowedGroups
listOf strKanidm groups allowed on this protected service (empty = any authenticated user) - proxy.isInternal
boolBind service on internal interface only (not internet accessible) - proxy.hasReverseProxy
boolThis is a reverse proxy (or another virtualhost configuration via extraConfig) - proxy.defaultService
boolIs the default service - proxy.servicePort
nullOr portService internal port - proxy.preExtraConfig
linesExtra caddy virtualHost configuration (prefix) - proxy.extraConfig
linesExtra caddy virtualHost configuration - proxy.extraGlobalConfig
linesExtra caddy configuration - proxy.scheme
strInternal service scheme (http / https)
- enable
darkone.system.services = { enable = false; service.enable = false; service.defaultParams.domain = ""; service.defaultParams.title = ""; service.defaultParams.description = ""; service.defaultParams.icon = ""; service.defaultParams.global = false; service.defaultParams.noRobots = true; service.defaultParams.fqdn = ""; service.defaultParams.href = ""; service.defaultParams.ip = ""; service.displayOnHomepage = true; service.reverseProxy = true; service.uniquePerZone = false; service.externalAccess = false; service.persist.dirs = [ ]; service.persist.files = [ ]; service.persist.dbDirs = [ ]; service.persist.dbFiles = [ ]; service.persist.varDirs = [ ]; service.persist.mediaDirs = [ ]; service.proxy.enable = true; service.proxy.isProtected = false; service.proxy.allowedGroups = [ ]; service.proxy.isInternal = false; service.proxy.hasReverseProxy = true; service.proxy.defaultService = false; service.proxy.servicePort = null; service.proxy.preExtraConfig = ""; service.proxy.extraConfig = ""; service.proxy.extraGlobalConfig = ""; service.proxy.scheme = "https";};⚙ darkone.system.sops
Section titled “⚙ darkone.system.sops”DNF sops, passwords and secrets management.
Wires sops-nix to usr/secrets/secrets.yaml and unlocks it with the
host SSH key (ssh_host_ed25519_key) plus the dedicated infrastructure
age key (/etc/sops/age/infra.key). Declares one
user/<login>/password-hash secret per host user, with
neededForUsers = true so the hash is available before the user accounts
are created.
Nothing else is materialised here: a secret belongs on the hosts that
consume it, declared by the module that reads it. default-password-hash
stays in the encrypted file, unread by any host — it records the fleet
default password for just passwd <login>.
- enable
boolEnable sops automated configuration for DNF
darkone.system.sops.enable = false;⚙ darkone.system.srv-dirs
Section titled “⚙ darkone.system.srv-dirs”Shared directories management service.
- enable
boolEnable srv dirs, create the root dir (default /srv) - enableNfs
boolEnable nfs service paths (nfs/common, nfs/homes) - enableMedias
boolEnable media services paths (medias/[videos|music|incomming/…]) - root
strRoot dir for persistant data (/srv) - nfs
strNFS root directory (/srv/nfs) - homes
strDirectory for shared homes (/srv/nfs/homes) - common
strShared common directory (/srv/nfs/common linked to ~/Public) - medias
strMedias root dir (/srv/medias) - music
strShared music files directory (/srv/medias/music) - videos
strShared video files directory (/srv/medias/videos) - incoming
strShared incoming directory (/srv/medias/incoming write access) - incomingMusic
strShared incoming directory (/srv/medias/incoming/music write access) - incomingVideos
strShared incoming directory (/srv/medias/incoming/videos write access)
darkone.system.srv-dirs = { enable = cfg.enableNfs || cfg.enableMedias; enableNfs = false; enableMedias = false; root = "/srv"; nfs = null; homes = null; common = null; medias = null; music = null; videos = null; incoming = null; incomingMusic = null; incomingVideos = null;};⚙ darkone.system.testing
Section titled “⚙ darkone.system.testing”Standalone test mode for the NixOS Test Driver.
When enabled, neutralizes the irreducibly external/runtime bits so a node can be exercised in a VM: headscale/tailscale become no-ops and a fixed TLS cert can stub ACME. It also lets workDir-only config (nix.pub, harmonia.pub) be skipped by core/nix-cache. sops stays REAL (high fidelity).
- standalone
boolStandalone test mode — skip workDir-only config (nix.pub, harmonia.pub) and neutralize external services - tlsCert
nullOr pathSelf-signed cert (PEM) provided by the test harness to stub ACME. Never a tests/ path baked into the framework. - tlsKey
nullOr pathPrivate key (PEM) paired with tlsCert.
darkone.system.testing = { standalone = false; tlsCert = null; tlsKey = null;};⚙ darkone.system.yubikey
Section titled “⚙ darkone.system.yubikey”YubiKey strong authentication: fleet-wide PAM U2F + declarative FIDO2 LUKS.
One physical enrollment per (user, key) on the admin host — just yubikey <user> [key] [action] — feeds:
- a public registry
usr/secrets/yubikeys.jsonin the consumer workspace: pamu2fcfg credential, FIDO2 hmac-secret credential id and salt (all useless without the physical key, hence committable); - one sops secret
yubikey/<user>/<key>/luks-secret(the FIDO2-derived LUKS passphrase) plus the sharedluks-passphrase(the passphrase typed at disko install time, needed to authorize new keyslots).
The module then propagates everything declaratively (just apply), without
ever plugging the key on the target hosts:
- login / sudo / greeter: pam_u2f as a password alternative
(
sufficient): touching the key logs in, the sops password remains the automatic fallback. One credential is valid fleet-wide thanks to the fixed origin (pam://<network.domain>). - encrypted hosts: a oneshot service self-enrolls each declared key in
every disko-declared LUKS2 header (keyslot +
systemd-fido2token, the exact formatsystemd-cryptenrollwrites) and prunes revoked ones. The physical key is only ever needed at boot to unlock the disk.
- enable
boolYubiKey authentication (default: enabled when usr/secrets/yubikeys.json exists) - origin
strFixed pam_u2f origin/appid: one enrollment is valid fleet-wide - luks.enable
boolFIDO2 unlock of the host LUKS volumes (inert without disko LUKS + enrolled keys)
darkone.system.yubikey = { enable = hasRegistry; origin = "pam://${network.domain}"; luks.enable = true;};