Skip to content

Service modules

Full-configured AdGuard Home for local gateway / router.

  • enable bool Enable local adguardhome service
darkone.service.adguardhome.enable = false;

Local Artifical Intelligence (open-webui + ollama + llms).

  • enable bool Enable local AI service
darkone.service.ai.enable = false;

Audio services: alsa, pulse (not jack for the moment). Automatically adds users to the audio group when enabled.

  • enable bool Enable sound system
darkone.service.audio.enable = false;

Pre-configured dnsmasq for local gateway / router.

  • enable bool Enable local dnsmasq service
darkone.service.dnsmasq.enable = false;

A full-configured LaSuite Docs module.

  • enable bool Enable local docs service
  • s3Host str S3 backend hostname
  • s3Port port S3 backend port
  • s3Bucket str S3 bucket name for document storage
darkone.service.docs = {
enable = false;
s3Host = "127.0.0.1";
s3Port = dnfConfig.network.ports.garage;
s3Bucket = "docs";
};

Element web client for local matrix service.

  • enable bool Enable local element service
darkone.service.element.enable = false;

Fail2ban DNF specific module.

  • enable bool Enable fail2ban with DNF specificities
darkone.service.fail2ban.enable = false;

A full-configured forgejo git forge.

  • enable bool Enable local forgejo service
darkone.service.forgejo.enable = false;

A full-configured local Garage S3 service.

Provides an internal S3-compatible object storage backend accessible only on 127.0.0.1:3900.

  • enable bool Enable local Garage S3 service
  • srvPort port S3 API port exposed on the internal IP
  • s3Region str S3 region name (must match consumer config)
  • capacity str Node storage capacity hint passed to garage layout assign. Used only at first boot for layout initialization; does not act as a hard quota on the underlying filesystem. Supported suffixes: B, KB, MB, GB, TB, PB.
darkone.service.garage = {
enable = false;
srvPort = srvPort;
s3Region = s3Region;
capacity = "500GB";
};

GeneWeb — Powerful Genealogy Service.

  • enable bool Enable local GeneWeb genealogy service
  • enablePasswords bool Enable sops passwords (not recommanded)
darkone.service.geneweb = {
enable = false;
enablePasswords = false;
};

Harmonia: local Nix binary cache server (serves this host’s /nix/store).

Exposes locally built / realised store paths over plain HTTP (port 5000), signed with the deployment-wide binary-cache key. Enable per host from usr/config.yaml (services.harmonia), like any other DNF service.

  • enable bool Enable a local Harmonia Nix binary cache server
darkone.service.harmonia.enable = false;

A full-configured headscale service for HCS.

  • enable bool Enable headscale DNF service
  • enableGRPC bool Open GRPC TCP port
darkone.service.headscale = {
enable = false;
enableGRPC = false;
};

A Home Assistant with some plugins (wip).

  • enable bool Enable home assitant
darkone.service.home-assistant.enable = false;

Dynamically configured homepage dashboard for your local network.

  • enable bool Enable homepage dashboard + httpd + host
  • protect bool Restrict homepage to authenticated Kanidm users (oauth2-proxy forward auth)
  • localServices listOf attrs Services to add in Local Applications section
  • globalServices listOf attrs Full network common & public-accessible services
  • remoteServices listOf attrs Services to add in Remote Applications section
  • bookmarks listOf attrs Replace default bookmarks (links)
  • widgets listOf attrs Replace default widgets
darkone.service.homepage = {
enable = false;
protect = true;
localServices = [ ];
globalServices = [ ];
remoteServices = [ ];
bookmarks = [ ];
widgets = [ ];
};

Kanidm (identity manager) DNF Service.

  • enable bool Enable local SSO with Kanidm
  • oauth2 attrs OAuth2/OIDC client templates contributed by service modules. Kanidm provisions one client per matching entry in network.services, with clientId = dnfLib.oauth2ClientName.
    • enable bool Whether to provision OAuth2 clients for this template.
    • clientName nullOr str Override the kanidm client name. Defaults to dnfLib.oauth2ClientName.
    • displayName str Human-readable name shown on the kanidm consent screen.
    • imageFile path Application icon. Re-uploaded on every kanidm-provision run.
    • redirectPaths listOf str OAuth2 redirect paths (one per accepted callback URL).
    • landingPath str Auto-connect entry point path on the service.
    • enableLegacyCrypto bool Allow legacy JWT signing algorithms (eg. RS256).
    • allowInsecureClientDisablePkce bool Disable PKCE on the client (only for clients that do not implement it).
    • preferShortUsername nullOr bool Use the short username (no domain) in the preferred_username claim.
    • extra attrs Extra attributes merged into the provisioned client (claimMaps, etc).
darkone.service.idm = {
enable = false;
oauth2.enable = true;
oauth2.clientName = null;
oauth2.displayName = null;
oauth2.imageFile = null;
oauth2.redirectPaths = [ ];
oauth2.landingPath = "/";
oauth2.enableLegacyCrypto = false;
oauth2.allowInsecureClientDisablePkce = false;
oauth2.preferShortUsername = null;
oauth2.extra = { };
};

Immich (photo management) full-configured service.

  • enable bool Enable local immich service
  • enableMachineLearning bool Enable machine learning features (face recognition, object detection)
  • enableRedis bool Enable Redis for caching (recommended for performance)
darkone.service.immich = {
enable = false;
enableMachineLearning = false;
enableRedis = false;
};

A full-configured jellyfin server.

  • enable bool Enable jellyfin service
darkone.service.jellyfin.enable = false;

A full-configured jitsi-meet service.

  • enable bool Enable local jitsi-meet service
darkone.service.jitsi-meet.enable = false;

Loki + Alloy, http stats with grafana.

  • enable bool Deploys the Loki server + Grafana datasource (colocated with Grafana).
  • isClient bool Deploys Alloy to collect local Caddy access logs.
  • retentionTime str Log retention duration in Loki (30 days by default).
darkone.service.loki = {
enable = config.darkone.service.monitoring.enable;
isClient = config.services.caddy.enable;
retentionTime = "720h";
};

DNF matrix (synapse) server with mautrix bridges.

Bridges (whatsapp, signal, telegram, messenger, discord) are usable by every local account: each user links its own remote account by talking to the bridge bot (@whatsappbot, @signalbot, … then login); sessions are isolated per user. The declared network.matrix.admin administrates the bridges.

Double puppeting uses the official appservice method (https://docs.mau.fi/bridges/general/double-puppeting.html 🡕): a shared doublepuppet appservice token lets bridges send remote-originated messages as the user’s real matrix account. Required sops secrets: mautrix-doublepuppet-as-token and mautrix-doublepuppet-hs-token (openssl rand -hex 32 each).

Every bridge’s appservice as/hs tokens are sops-provided rather than auto-generated: the registration file becomes a pure function of the secrets, so resetting a bridge’s state dir never invalidates the registration synapse has loaded (which would otherwise require a registration wipe + synapse restart and produce “as_token was not accepted” errors).

Federation is configurable via darkone.service.matrix.federation:

  • enable = false: blocks all federation (empty domain whitelist).
  • enable = true; whitelist = [ ]: open federation with every server.
  • enable = true; whitelist = [ "ami.org" ]: allowlist (inbound + outbound).

Discovery stays locked regardless (rooms absent from remote directories, profiles private over federation), so the network is reachable but not searchable.

Friend self-registration (friendRegistration.enable) opens token-gated local password accounts alongside Kanidm OIDC users. Minting a token needs a server admin (Synapse admin API / Synapse-Admin UI); with MAS enabled, mint with mas-cli manage issue-user-registration-token on the host instead.

mas.enable delegates all authentication to Matrix Authentication Service (required by Element X, QR login, /account self-service portal). Kanidm stays the identity source: MAS becomes the OIDC client instead of synapse, and synapse only asks MAS to introspect tokens. Served on the same vhost: MAS owns the root + compat auth endpoints, synapse keeps /_matrix/* and /_synapse/*; client discovery is automatic (synapse serves auth_metadata itself), so no well-known change.

Required sops secrets (openssl rand -hex 32 unless stated): mas-encryption-secret, mas-synapse-secret, and mas-rsa-private-key (openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096).

  • enable bool Enable matrix (synapse) service
  • federation.enable bool Allow server-to-server federation. False blocks all federation.
  • federation.whitelist listOf str Empty = federate with all servers; non-empty = only these domains (inbound + outbound).
  • mas.enable bool Delegate all authentication to Matrix Authentication Service (Element X support).
  • friendRegistration.enable bool Allow friends to self-register with an invite token (token-gated).
  • bridges.whatsapp.enable bool Mautrix WhatsApp bridge (login by QR code).
  • bridges.signal.enable bool Mautrix Signal bridge (login by QR code).
  • bridges.telegram.enable bool Mautrix Telegram bridge (login by phone number).
  • bridges.messenger.enable bool Mautrix Facebook Messenger bridge (login by cookies).
  • bridges.discord.enable bool Mautrix Discord bridge (login by QR code, experimental).
darkone.service.matrix = {
enable = false;
federation.enable = true;
federation.whitelist = [ ];
mas.enable = false;
friendRegistration.enable = false;
bridges.whatsapp.enable = true;
bridges.signal.enable = true;
bridges.telegram.enable = true;
bridges.messenger.enable = true;
bridges.discord.enable = false;
};

Mealie Recipe Management

  • enable bool Enable mealie service
darkone.service.mealie.enable = false;

A full-configured local MinIO S3 service. (wip)

Provides an internal S3-compatible object storage backend accessible only on 127.0.0.1:9000. The console web UI runs on 127.0.0.1:9001 for administration and debugging.

  • enable bool Enable local MinIO S3 service
darkone.service.minio.enable = false;

Supervision module: Grafana dashboards + per-node exporter.

  • enable bool Enable monitoring with prometheus, grafana and node exporter
  • isNode bool Is a monitoring node
  • kioskTarget str Target (relative, no /) for the automatic redirect to Grafana from the monitoring domain root. Overridden by the Loki module when active to point to a multi-source home dashboard.
darkone.service.monitoring = {
enable = false;
isNode = lib.hasAttrByPath [ "features" "monitoring-node" ] host;
kioskTarget = "d/dnf-monitoring-home/home?kiosk";
};

Nextcloud full-configured service.

  • enable bool Enable local nextcloud service
  • adminUser str Admin username for Nextcloud
  • plugins listOf (enum (appstoreApps ++ shippedToggleableApps)) Nextcloud apps to enable. Only calendar and contacts are on by default; Talk (spreed), the dashboard, activity feed, photos, and every other app stay disabled until listed here.

user_oidc is required for Kanidm SSO and is force-included regardless of this list.

darkone.service.nextcloud = {
enable = false;
adminUser = "admin";
plugins = [ ];
};

NFS server + client for home shares.

  • enable bool Enable NFS DNF server (avoid enable manually)
  • serverDomain str NFS Server FQDN
darkone.service.nfs = {
enable = hasServer && (isServer || isClient);
serverDomain = "nfs";
};

Per-zone Nix binary-cache proxy (nginx proxy_cache).

Caches the public Nix cache (cache.nixos.org) on the zone gateway so a fleet-wide deploy pulls each closure from the WAN once, then serves it over the LAN to every other host. Locally built paths are served straight from harmonia (see harmonia.nix); this proxy never fronts harmonia.

  • enable bool the per-zone Nix binary-cache proxy (nginx in front of cache.nixos.org)
  • maxSize str Maximum on-disk cache size; nginx evicts least-recently-used entries beyond it.
  • roaming bool Nomadic host (laptop moved between zones): replace the zone-pinned substituters with the zone-neutral names every zone’s DNS resolves to its own cache services. Outside any DNF zone the names NXDOMAIN instantly and Nix falls back to cache.nixos.org.
darkone.service.nix-cache = {
enable = false;
maxSize = "40g";
roaming = false;
};

A full-configured outline wiki.

  • enable bool Enable local outline service
darkone.service.outline.enable = false;

OxiCloud — Fast Sovereign Cloud (file storage, WebDAV, CalDAV & CardDAV).

  • enable bool Enable local OxiCloud service
darkone.service.oxicloud.enable = false;

Postfix SMTP Relay.

  • enable bool Enable Postfix SMTP Relay
darkone.service.postfix.enable = false;

Printers and scanners.

  • enable bool Default useful packages
  • loadAll bool Full printers and scanners
  • enableScanners bool Enable scanners
  • enableHpPrinters bool HP printers only
  • enableManualInstall bool Manual drivers installation
darkone.service.printing = {
enable = false;
loadAll = false;
enableScanners = false;
enableHpPrinters = false;
enableManualInstall = false;
};

Prometheus monitoring server with declarative Alertmanager escalation.

  • enable bool Enable the Prometheus metrics + alerting server
  • retentionTime str Prometheus metrics retention duration
  • alerting.enable bool Enable Alertmanager (defaults on once the Matrix alert rooms are provisioned)
  • alerting.matrix.enable bool Deliver alerts to Matrix rooms via the matrix-alertmanager bot
  • alerting.matrix.userId str Matrix user ID of the alert bot (defaults from network.matrix.bot)
  • alerting.matrix.warningsRoom str Matrix room ID for warnings (defaults from network.matrix.warningsRoom)
  • alerting.matrix.incidentsRoom str Matrix room ID for incidents (defaults from network.matrix.incidentsRoom)
  • alerting.email.enable bool Also send critical alerts by mail (through the local Postfix relay)
  • alerting.email.to str Recipient of critical alert mails
  • alerting.network.enable bool Probe network reachability (gateway/tailnet/DNS) with blackbox_exporter
  • alerting.network.internetProbeTargets listOf str External IPs the zone gateway pings to detect a WAN outage. When all fail, ZoneInternetDown fires and inhibits the (false) down-alerts of hosts reachable only across the WAN (e.g. the HCS). Empty disables it.
  • alerting.network.httpProbeUrls listOf str HTTP(S) URLs probed for liveness (ServiceEndpointDown) and TLS certificate expiry (CertificateExpiringSoon/Critical). Empty by default: list the public web endpoints worth watching. Redirects to an SSO login (3xx) count as up; cert expiry is read from the TLS handshake regardless of HTTP status.
  • alerting.silenceOnRebuild bool Ship the dnf-maintenance flag (textfile collector) so rebuilds inhibit a node’s alerts
  • alerting.thresholds attrs Override resource alert thresholds (see dnf/lib/alerts.nix defaults)
darkone.service.prometheus = {
enable = false;
retentionTime = "30d";
alerting.enable = roomsReady;
alerting.matrix.enable = true;
alerting.matrix.userId = "@alertbot:poncon.fr";
alerting.matrix.warningsRoom = "!warnings:poncon.fr";
alerting.matrix.incidentsRoom = "!incidents:poncon.fr";
alerting.email.enable = true;
alerting.email.to = "admin@${network.domain}";
alerting.network.enable = true;
alerting.network.internetProbeTargets = [ ];
alerting.network.httpProbeUrls = [ ];
alerting.silenceOnRebuild = true;
alerting.thresholds = { };
};

Restic backup module: REST server + per-host backup targets.

Example (machine config):

darkone.service.restic = {
enable = true;
targets = [
{ name = "main"; root = "rest:http://restic.my-zone.my-domain.tld:8888";
zone = "ag"; categories = [ "system" "nfs" ]; }
];
};

Repository layout per target:

<root>/<hostname>/system <- "system" category (/ minus excludes)
<root>/<hostname>/srv/nfs <- "nfs" category (/srv/nfs/<...>)
<root>/<hostname>/srv/medias <- "medias" category (/srv/medias/<...>)
  • enable bool Enable restic backup client
  • enableDryRun bool Dry Run mode
  • enableWaitRemoteFs bool Run backups only after remote-fs.target
  • enableServer bool Enable restic REST server
  • serverDataDir str Local storage root of the REST server (all hosts’ repos)
  • targets listOf ( submodule { options = { name = lib.mkOption { type = str; default = "main"; description = "Target id, used in backup/unit names (<category>-<name>)"; }; root = lib.mkOption { type = str; default = "/mnt/backup/restic"; example = "rest:http://restic.${zone.domain}:${toString srvPort}"; description = "Repository root: local path or REST URL"; }; zone = lib.mkOption { type = str; default = zone.name; description = "Zone selecting the repo passphrase (restic-password-<zone>)"; }; categories = lib.mkOption { type = listOf ( enum [ "system" "nfs" "medias" ] ); default = [ "system" ]; description = "What to back up to this target"; }; }; } ) Backup destinations for this host (local path or REST URL)
    • name str Target id, used in backup/unit names (<category>-<name>)
    • root str Repository root: local path or REST URL
    • zone str Zone selecting the repo passphrase (restic-password-<zone>)
    • categories listOf ( enum [ "system" "nfs" "medias" ] ) What to back up to this target
  • nfsPaths listOf str NFS dirs (/srv/nfs/<xxx>) included in the ‘nfs’ category
  • mediasPaths listOf str Medias dirs (/srv/medias/<xxx>) included in the ‘medias’ category
darkone.service.restic = {
enable = false;
enableDryRun = false;
enableWaitRemoteFs = false;
enableServer = false;
serverDataDir = "/mnt/backup/restic";
targets = [ ];
targets.name = "main";
targets.root = "rest:http://restic.${zone.domain}:${toString srvPort}";
targets.zone = zone.name;
targets.categories = [ ];
nfsPaths = [ ];
mediasPaths = [ ];
};

A full-configured hardened search engine.

  • enable bool Enable local search proxy
darkone.service.searx.enable = false;

Tailscale client service for HCS.

  • enable bool Enable tailscale client to connect HCS
  • isGateway bool This tailscale node is a subnet gateway
  • isExitNode bool Configure this client as exit node
  • selfHeal.enable bool Watchdog: detect headscale disconnection and restart tailscaled.
  • autoPauseOnLan.enable bool Roaming client: pause tailscale (down) while plugged into a known DNF zone LAN, resume it elsewhere. Non-gateway NetworkManager clients only.
darkone.service.tailscale = {
enable = false;
isGateway = false;
isExitNode = false;
selfHeal.enable = true;
autoPauseOnLan.enable = false;
};

Coturn server (matrix).

Add DNS entries to optimize :

Type,Name,Priority,Pds,Port,Target
SRV,_stun._udp,0,0,3478,turn.mydomain.tld
SRV,_stun._tcp,0,0,3478,turn.mydomain.tld
SRV,_turn._udp,0,0,3478,turn.mydomain.tld
SRV,_turn._tcp,0,0,3478,turn.mydomain.tld
SRV,_turns._tcp,0,0,5349,turn.mydomain.tld
  • enable bool Enable local turn service (visio)
darkone.service.turn.enable = false;

A full-configured vaultwarden server (wip).

  • enable bool Enable local Vaultwarden service
  • enableSmtp bool Enable SMTP to send emails (recommended)
darkone.service.vaultwarden = {
enable = false;
enableSmtp = true;
};