Aller au contenu

Modules de service

AdGuard Home entièrement configuré pour passerelle / routeur local.

  • enable bool Active le service adguardhome local
darkone.service.adguardhome.enable = false;

Intelligence artificielle locale (open-webui + ollama + llms).

  • enable bool Activer le service d’IA local
darkone.service.ai.enable = false;

Services audio : alsa, pulse (pas jack pour le moment). Ajoute automatiquement les utilisateurs au groupe audio quand il est activé.

  • enable bool Activer le système sonore
darkone.service.audio.enable = false;

dnsmasq pré-configuré pour la passerelle / routeur local.

  • enable bool Active le service dnsmasq local
darkone.service.dnsmasq.enable = false;

Un module LaSuite Docs entièrement configuré.

  • enable bool Activer le service de documentation local
  • s3Host str Nom d’hôte du backend S3
  • s3Port port Port du backend S3
  • s3Bucket str Nom du bucket S3 pour le stockage des documents
darkone.service.docs = {
enable = false;
s3Host = "127.0.0.1";
s3Port = dnfConfig.network.ports.garage;
s3Bucket = "docs";
};

Client web Element pour le service matrix local.

  • enable bool Activer le service element local
darkone.service.element.enable = false;

Module fail2ban spécifique à DNF.

  • enable bool Activer fail2ban avec les spécificités DNF
darkone.service.fail2ban.enable = false;

Une forge git forgejo entièrement configurée.

  • enable bool Active le service forgejo local
darkone.service.forgejo.enable = false;

Un service Garage S3 local entièrement configuré.

Fournit un stockage d’objets interne compatible S3 accessible uniquement sur 127.0.0.1:3900.

  • enable bool Activer le service Garage S3 local
  • srvPort port Port de l’API S3 exposé sur l’IP interne
  • s3Region str Nom de la région S3 (doit correspondre à la config du consommateur)
  • capacity str Indication de capacité de stockage du nœud transmise à garage layout assign. Utilisée uniquement au premier démarrage pour l’initialisation du layout ; n’agit pas comme un quota strict sur le système de fichiers sous-jacent. Suffixes pris en charge : B, KB, MB, GB, TB, PB.
darkone.service.garage = {
enable = false;
srvPort = srvPort;
s3Region = s3Region;
capacity = "500GB";
};

GeneWeb — Service de généalogie puissant.

  • enable bool Activer le service de généalogie GeneWeb local
  • enablePasswords bool Activer les mots de passe sops (non recommandé)
darkone.service.geneweb = {
enable = false;
enablePasswords = false;
};

Harmonia : serveur local de cache binaire Nix (sert le /nix/store de cet hôte).

Expose les chemins du store locaux construits / réalisés en HTTP simple (port 5000), signés avec la clé de cache binaire propre au déploiement. Active par hôte depuis etc/config.yaml (services.harmonia), comme tout autre service DNF.

  • enable bool Activer un serveur de cache binaire Nix Harmonia local
darkone.service.harmonia.enable = false;

DNF headscale : headscale-audit. Doc : en-tête de ../headscale.nix.


DNF headscale : DNS pivot du tailnet. Doc : en-tête de ../headscale.nix.


DNF headscale : dnf-tailnet-enroll. Doc : en-tête de ../headscale.nix.


Un service headscale entièrement configuré pour le HCS.

La politique ACL du tailnet est générée depuis la topologie (zones, hôtes, utilisateurs) par dnfLib.mkHeadscalePolicy, validée par headscale policy check à la construction, puis rechargée au changement (SIGHUP) sans redémarrer headscale.

  • Les machines sont taguées : tag:hcs, tag:gw-<zone>, tag:admin (hôte dont groups contient admin).
  • Les machines et les LAN de zone joignent les autres. Appareils personnels : DNS et HTTPS sur le HCS, HTTPS sur les passerelles de zone (toutes les zones pour les admin, sinon leurs groupes zone-*). SSH depuis les postes d’admin et policy.adminDevices.
  • Les appareils personnels se connectent via Kanidm (OIDC), pour les membres du groupe Kanidm tailnet ; leurs clés expirent après nodeExpiry.
darkone.service.headscale.policy.adminDevices.phone-alice = "100.64.0.9";

Sous-modules (headscale/) :

  • dns.nix : unbound, le DNS pivot du tailnet et sa vue tailnet-machines ;
  • audit.nix : headscale-audit, nœuds actifs confrontés à la topologie déclarée ;
  • enroll.nix : dnf-tailnet-enroll, derrière just tailnet-enroll.

unbound sur le HCS. Les noms internes l’atteignent par le split DNS de headscale ; il relaie chaque domaine de zone vers la passerelle de zone, le reste vers Quad9 par TLS.

Vue tailnet-machines : les nœuds tagués (HCS mis à part) reçoivent l’adresse LAN de zone des services globaux qu’une zone dessert (git.<domain> sur une passerelle), tous les autres reçoivent l’adresse publique. unbound-tailnet-view liste leurs IP de tailnet depuis headscale, déclarées nulle part ailleurs.

headscale-audit, toutes les 15 min : nœuds actifs confrontés à la topologie déclarée. Métriques pour les alertes dnf-headscale-<zone>, détails dans son journal. Ne tague ni ne supprime jamais.

dnf-tailnet-enroll, côté root de just tailnet-enroll : clés à usage unique taguées depuis la topologie déclarée ; le nœud enrôlé reçoit son nom et ses tags déclarés.

  • enable bool Activer le service DNF headscale
  • enableGRPC bool Ouvrir le port TCP GRPC
  • nodeExpiry str Expiration des clés des appareils personnels (0 : jamais). Les machines taguées n’expirent jamais.
  • policy.enforce bool ACL du tailnet en refus par défaut. false autorise tout trafic, pour une migration uniquement.
  • policy.adminDevices attrs Appareils personnels autorisés en SSH sur chaque machine et zone : nom -> IPv4 de tailnet.
  • policy.exitNodeSources listOf str Sources de politique autorisées à utiliser le HCS comme nœud de sortie.
  • policy.extraHosts attrs Alias d’hôte de politique supplémentaires (nom -> CIDR), à utiliser dans extraAcls.
  • policy.extraAcls listOf (attrsOf anything) Règles ACL ajoutées après celles générées.
  • shared raw Valeurs headscale partagées avec les sous-modules headscale/.
darkone.service.headscale = {
enable = false;
enableGRPC = false;
nodeExpiry = "180d";
policy.enforce = true;
policy.adminDevices = { };
policy.exitNodeSources = [ ];
policy.extraHosts = { };
policy.extraAcls = [ ];
shared = {
inherit
policy
auditSpec
idmUrl
hcsTailnetIpv4
;
};
};

Un Home Assistant avec certains plugins (en cours).

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

Tableau de bord de page d’accueil configuré dynamiquement pour votre réseau local.

  • enable bool Activer le tableau de bord de la page d’accueil + httpd + hôte
  • protect bool Restreindre la page d’accueil aux utilisateurs Kanidm authentifiés (oauth2-proxy forward auth)
  • localServices listOf attrs Services à ajouter dans la section Applications locales
  • globalServices listOf attrs Services communs du réseau & accessibles publiquement
  • remoteServices listOf attrs Services à ajouter dans la section Applications distantes
  • bookmarks listOf attrs Remplacer les favoris par défaut (liens)
  • widgets listOf attrs Remplacer les widgets par défaut
darkone.service.homepage = {
enable = false;
protect = true;
localServices = [ ];
globalServices = [ ];
remoteServices = [ ];
bookmarks = [ ];
widgets = [ ];
};

DNF idm : provision Kanidm. Doc : en-tête de ../idm.nix.

  • oauth2 attrs Gabarits de client OAuth2/OIDC fournis par les modules de service. Kanidm provisionne un client par entrée correspondante dans network.services, avec clientId = dnfLib.oauth2ClientName.
    • enable bool Provisionner ou non des clients OAuth2 pour ce gabarit.
    • clientName nullOr str Remplace le nom de client kanidm. Par défaut dnfLib.oauth2ClientName.
    • displayName str Nom lisible affiché sur l’écran de consentement kanidm.
    • imageFile path Icône de l’application. Renvoyée à chaque exécution de kanidm-provision.
    • redirectPaths listOf str Chemins de redirection OAuth2 (un par URL de rappel acceptée).
    • landingPath str Chemin du point d’entrée de connexion automatique sur le service.
    • enableLegacyCrypto bool Autoriser les algorithmes de signature JWT hérités (ex. RS256).
    • allowInsecureClientDisablePkce bool Désactiver PKCE sur le client (uniquement pour les clients qui ne l’implémentent pas).
    • preferShortUsername nullOr bool Utiliser le nom d’utilisateur court (sans domaine) dans la revendication preferred_username.
    • extra attrs Attributs supplémentaires fusionnés dans le client provisionné (claimMaps, etc).
darkone.service.idm.provision = {
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 = { };
};

DNF idm : réplication Kanidm. Doc : en-tête de ../idm.nix.

  • replication raw Faits de réplication de ce nœud.
darkone.service.idm.replication.replication = { inherit isMainReplica isReplConsumer; };

Service DNF Kanidm (gestionnaire d’identités).

Sous-modules (idm/) :

  • replication.nix : réplication en lecture seule multi-zones, du HCS vers les passerelles de zone ;
  • provision.nix : clients OAuth2 (registre de gabarits), groupes et personnes.

Automatique, dérivée de l’endroit où idm est déclaré :

  • idm sur le HCS seul, ou sur une passerelle sans HCS : une instance ;
  • sur le HCS et >= 1 passerelle de zone : le HCS fournit (WriteReplica), chaque passerelle idm consomme.

Amorçage en deux temps : just apply génère tous les certificats de réplication (les passerelles restent WriteReplicaNoUI), puis just idm-sync-certs + just apply ajoute les partenaires et fait basculer les passerelles en ReadOnlyReplica.

Chaque module de service compatible OIDC contribue un modèle de client à darkone.service.idm.oauth2 ; un client est provisionné par (modèle, instance), fusionné par clientId à travers les zones. Jamais sur un consommateur de réplication : il reflète le HCS.

  • enable bool Activer le SSO local avec Kanidm
darkone.service.idm.enable = false;

Service Immich (gestion de photos) entièrement configuré.

  • enable bool Activer le service immich local
  • enableMachineLearning bool Activer les fonctionnalités d’apprentissage automatique (reconnaissance faciale, détection d’objets)
  • enableRedis bool Activer Redis pour la mise en cache (recommandé pour les performances)
darkone.service.immich = {
enable = false;
enableMachineLearning = false;
enableRedis = false;
};

Un serveur jellyfin entièrement configuré.

  • enable bool Activer le service jellyfin
darkone.service.jellyfin.enable = false;

Un service jitsi-meet entièrement configuré.

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

Loki + Alloy, statistiques HTTP avec Grafana.

  • enable bool Déploie le serveur Loki + le datasource Grafana (co-localisé avec Grafana).
  • isClient bool Déploie Alloy pour collecter les logs d’accès Caddy locaux.
  • retentionTime str Durée de rétention des logs dans Loki (30 jours par défaut).
darkone.service.loki = {
enable = config.darkone.service.monitoring.enable;
isClient = config.services.caddy.enable && hasMonitoringHost;
retentionTime = "720h";
};

DNF matrix : ponts mautrix. Doc : en-tête de ../matrix.nix.


DNF matrix : authentification déléguée à MAS. Doc : en-tête de ../matrix.nix.


DNF matrix : appels MatrixRTC (LiveKit). Doc : en-tête de ../matrix.nix.


Service matrix (synapse) DNF avec les ponts mautrix.

Les ponts (whatsapp, signal, telegram, messenger, discord) sont utilisables par tous les comptes locaux : chaque utilisateur lie son propre compte distant en dialoguant avec le bot du pont (@whatsappbot, @signalbot, … puis login) ; les sessions sont isolées par utilisateur. Les network.matrix.admins déclarés administrent les ponts et le serveur (cf. « Administrateurs » ci-dessous).

Le double puppeting utilise la méthode officielle appservice (https://docs.mau.fi/bridges/general/double-puppeting.html 🡕) : un token appservice doublepuppet partagé permet aux ponts d’envoyer les messages d’origine distante sous le vrai compte matrix de l’utilisateur. Secrets sops requis : mautrix-doublepuppet-as-token et mautrix-doublepuppet-hs-token (openssl rand -hex 32 chacun).

Les tokens as/hs de chaque pont sont fournis par sops plutôt que générés automatiquement : le fichier de registration devient une pure fonction des secrets, donc réinitialiser le répertoire d’état d’un pont n’invalide jamais la registration chargée par synapse (sinon il faudrait purger la registration + redémarrer synapse, ce qui produirait des erreurs « as_token was not accepted »).

DNF matrix (synapse) homeserver.

Sous-modules (matrix/) :

  • mas.nix : authentification, déléguée au Matrix Authentication Service ;
  • bridges.nix : ponts mautrix, utilisables par tout compte local ;
  • rtc.nix : backend MatrixRTC (LiveKit) pour Element Call.

La fédération est configurable via darkone.service.matrix.federation :

  • enable = false : bloque toute la fédération (liste blanche de domaines vide).
  • enable = true; whitelist = [ ] : fédération ouverte avec tous les serveurs.
  • enable = true; whitelist = [ "ami.org" ] : liste blanche (entrante + sortante).

La découverte reste verrouillée quoi qu’il arrive (salons absents des répertoires distants, profils privés sur la fédération), si bien que le réseau est joignable mais pas consultable.

L’auto-inscription des amis (friendRegistration.enable) ouvre des comptes mot de passe locaux conditionnés par un jeton, en parallèle des utilisateurs OIDC Kanidm. Fabriquer un jeton sur l’hôte avec sudo dnf-mas manage issue-user-registration-token.

network.matrix.admins (parties locales) est la source déclarative unique des deux administrations, appliquée à la reconstruction sans écriture en base :

  • ponts : niveau admin dans chaque carte permissions mautrix ;
  • serveur : policy.data.admin_users, que la politique MAS fournie transforme en scopes urn:mas:admin et urn:synapse:admin:* (API admin MAS, API admin synapse, connexion à l’UI matrix-admin).

Toute l’authentification passe toujours par le Matrix Authentication Service : Element X, la connexion par QR code et le portail d’auto-service /account n’existent que là, et l’auth propre à synapse est dépréciée en amont (MSC3861). Kanidm reste la source d’identité : MAS est le client OIDC à la place de synapse, et synapse ne fait qu’interroger MAS pour introspecter les jetons.

Même vhost : MAS détient les points d’auth racine + compat, synapse garde /_matrix/* et /_synapse/*. La découverte des clients est automatique (synapse sert lui-même auth_metadata), aucun changement de well-known.

Secrets sops requis, tous créés par just configure-admin-host : mas-encryption-secret, mas-synapse-secret et mas-rsa-private-key.

dnf-mas (root) enveloppe mas-cli pour l’administration côté hôte : jetons d’inscription, promote-admin, register-user, syn2mas. MAS tourne avec DynamicUser et lit ses secrets depuis les credentials systemd, absents hors de l’unité en cours ; le wrapper reconstruit un répertoire de credentials équivalent à partir des fichiers sops, donc fonctionne que le service soit en service ou non (syn2mas l’exige à l’arrêt).

mas-cli n’agit que, jamais de listage : activer le service matrix-admin pour le côté lecture (comptes, appareils, sessions), il parle les deux API admin.

Ponts mautrix : WhatsApp, Signal, Telegram, Messenger, Discord. Chaque utilisateur lie son propre compte distant en parlant au bot du pont (@whatsappbot, @signalbot, … puis login) ; les sessions sont isolées par utilisateur. Administrateurs des ponts : cf. « Administrateurs » ci-dessus.

Double puppeting : méthode officielle appservice (https://docs.mau.fi/bridges/general/double-puppeting.html 🡕). Un jeton appservice doublepuppet partagé permet aux ponts d’envoyer des messages à origine distante sous le vrai compte matrix de l’utilisateur. Secrets sops requis : mautrix-doublepuppet-as-token et mautrix-doublepuppet-hs-token (générés par just configure-admin-host).

Les jetons as/hs appservice de chaque pont proviennent de sops, jamais auto-générés : le fichier de registration est une fonction pure des secrets, si bien qu’effacer le répertoire d’état d’un pont n’invalide jamais la registration chargée par synapse (« as_token was not accepted », effacement de la registration + redémarrage de synapse).

Deux stacks coexistent, car aucune seule ne couvre tous les clients :

  • WebRTC 1:1 historique, négocié via synapse et relayé par coturn (darkone.service.turn). La seule chose que parle Element Classic.
  • MatrixRTC (matrixRtc.enable) : un SFU LiveKit plus son service d’autorisation, pour les appels de groupe et Element Call. Element X ne parle que celui-là et affiche « call is not supported » (MISSING_MATRIX_RTC_TRANSPORT) sans lui ; Element Web/Desktop intègrent aussi Element Call et en gagnent les appels de groupe.

Les deux sont servis depuis le vhost matrix, à côté de synapse et MAS : le websocket du SFU sur /livekit/sfu, le service JWT sur /livekit/jwt. Les clients le découvrent via matrix_rtc.transports (synapse, MSC4143) et, en repli pour les plus anciens, via rtc_foci dans le well-known.

Secret sops requis : livekit-secret (just configure-admin-host), partagé par le SFU et le service JWT.

  • enable bool Activer le service matrix (synapse)
  • federation.enable bool Autoriser la fédération serveur-à-serveur. False bloque toute la fédération.
  • federation.whitelist listOf str Vide = fédérer avec tous les serveurs ; non vide = seulement ces domaines (entrant + sortant).
  • matrixRtc.enable bool Activer le backend MatrixRTC (SFU LiveKit) pour les appels de groupe Element Call.
  • friendRegistration.enable bool Autoriser les amis à s’auto-inscrire avec un jeton d’invitation (conditionné par jeton).
  • bridges.whatsapp.enable bool Pont Mautrix WhatsApp (connexion par QR code).
  • bridges.signal.enable bool Pont Mautrix Signal (connexion par QR code).
  • bridges.telegram.enable bool Pont Mautrix Telegram (connexion par numéro de téléphone).
  • bridges.messenger.enable bool Pont Mautrix Facebook Messenger (connexion par cookies).
  • bridges.discord.enable bool Pont Mautrix Discord (connexion par QR code, expérimental).
  • shared raw Valeurs matrix partagées avec les sous-modules matrix/.
darkone.service.matrix = {
enable = false;
federation.enable = false;
federation.whitelist = [ ];
matrixRtc.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;
shared = {
inherit
params
masKanidmUlid
livekitJwtUrl
matrixAdmins
;
};
};

Gestion de recettes Mealie

  • enable bool Activer le service mealie
darkone.service.mealie.enable = false;

Un service MinIO S3 local entièrement configuré. (wip)

Fournit un backend de stockage d’objets compatible S3 interne accessible uniquement sur 127.0.0.1:9000. L’interface Web de la console s’exécute sur 127.0.0.1:9001 pour l’administration et le débogage.

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

Supervision DNF : montages rw rendus en lecture seule par le noyau. Doc : en-tête de ../monitoring.nix.


Module de supervision : tableaux de bord Grafana + exporteur par nœud.

Surveillance en lecture seule (monitoring/fs-readonly.nix) : chaque nœud exporte dnf_filesystem_forced_readonly, ses montages rw dont le noyau a rendu le système de fichiers en lecture seule, derrière FilesystemReadOnly. Les montages ro et liaisons prévus n’y apparaissent jamais ; le fichier vit sur tmpfs, il survit donc au disque défaillant.

  • enable bool Activer la supervision avec prometheus, grafana et l’exporteur de nœud
  • isNode bool S’agit-il d’un nœud de supervision
  • kioskTarget str Cible (relative, sans /) de la redirection automatique vers Grafana depuis la racine du domaine de supervision. Surchargée par le module Loki lorsqu’il est actif pour pointer vers un tableau de bord multi-sources.
darkone.service.monitoring = {
enable = false;
isNode = lib.hasAttrByPath [ "features" "monitoring-node" ] host;
kioskTarget = "d/dnf-monitoring-home/home?kiosk";
};

Service Nextcloud entièrement configuré.

  • enable bool Activer le service nextcloud local

  • adminUser str Nom d’utilisateur administrateur pour Nextcloud

  • enableSsoRedirect bool Envoyer les utilisateurs directement vers Kanidm au lieu d’afficher le formulaire de connexion Nextcloud (user_oidc allow_multiple_user_backends). Économise un clic à chaque connexion, y compris celle du client de bureau via Login Flow v2.

    Sans effet si Kanidm est absent du réseau.

  • plugins listOf (enum (appstoreApps ++ shippedToggleableApps)) Applications Nextcloud à activer. Seuls calendar, contacts et tasks sont activés par défaut ; Talk (spreed), le tableau de bord, le flux d’activité, les photos et toutes les autres applications restent désactivées tant qu’elles ne sont pas listées ici.

    user_oidc (SSO Kanidm) et notify_push (push client) sont toujours activés, quelle que soit cette liste.

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

Serveur + client NFS pour les partages domestiques.

  • enable bool Active le serveur NFS DNF (éviter l’activation manuelle)
  • serverDomain str FQDN du serveur NFS
darkone.service.nfs = {
enable = hasServer && (isServer || isClient);
serverDomain = "nfs";
};

Proxy de cache binaire Nix par zone (nginx proxy_cache).

Met en cache le cache Nix public (cache.nixos.org) sur la passerelle de zone, afin qu’un déploiement à l’échelle de la flotte récupère chaque fermeture depuis le WAN une seule fois, puis la serve sur le LAN à tous les autres hôtes. Les chemins compilés localement sont servis directement depuis harmonia (voir harmonia.nix) ; ce proxy ne sert jamais de façade à harmonia.

  • enable bool Le proxy de cache binaire Nix par zone (nginx devant cache.nixos.org)
  • maxSize str Taille maximale du cache sur disque ; nginx évince au-delà les entrées les moins récemment utilisées.
  • roaming bool Hôte nomade (ordinateur portable déplacé entre zones) : remplacer les substituteurs épinglés à la zone par les noms neutres de zone que le DNS de chaque zone résout vers ses propres services de cache. Hors de toute zone DNF, ces noms répondent NXDOMAIN immédiatement et Nix se rabat sur cache.nixos.org. Déclaré par la fonctionnalité roaming dans config.yaml ; à définir à la main pour un déplacement temporaire.
darkone.service.nix-cache = {
enable = false;
maxSize = "40g";
roaming = (host.features or { }) ? "roaming";
};

Assistant OpenClaw d’un espace IA (passerelle + interface de gestion, derrière le SSO de la zone).

Une instance par hôte ai-space, déclarée comme n’importe quel service :

- hostname: "ia-pro"
profile: "ai-space"
users: ["alice"] # membres : groupe Kanidm `space-ia-pro`
features: ["monitoring-node"] # alertes : espace injoignable, unité de la passerelle en échec
services: { openclaw: {} }
  • accès : forward auth Caddy, membres de space-<host> uniquement (proxy.spaceMembersOnly) ; OpenClaw fait confiance à la passerelle de la zone en tant que proxy (trusted-proxy) et lit l’identité dans x-auth-request-email ;
  • appareils : le navigateur d’un membre est appairé à la première visite, sans admin de l’opérateur (accordé uniquement depuis le CLI de l’hôte) ;
  • port : joignable depuis la passerelle de la zone uniquement ;
  • CLI hôte : openclaw, relancé en tant qu’utilisateur du service, au-delà de trusted-proxy avec le mot de passe de la passerelle (openclaw-gateway-password, just space-init) ;
  • état : /var/lib/openclaw (connexions aux fournisseurs, sessions, espace de travail, clés E2EE Matrix) ;
  • propriétaire (owner, par défaut : l’unique membre) : son abonnement fait tourner l’agent, il reçoit les demandes d’approbation et les messages proactifs ;
  • Matrix : canal de DM avec le bot @<host>:<domain>, membres uniquement, salons ignorés, une session par membre quand il y en a plusieurs, quand le réseau dispose de Matrix et que just space-init <host> a créé le bot (marqueur var/generated/spaces/<host>.nix, jeton dans le fichier sops de l’espace) ; approbations d’exec demandées au propriétaire en DM ;
  • outillage de l’agent : MCP, skills, Claude Code / Codex et packages de l’espace (darkone.host.ai-space), partagés avec les homes des membres ;
  • identité (identity : nom, emoji, avatar) : déclarée, la configuration étant en lecture seule (openclaw agents set-identity échoue) ;
  • espace de travail (/var/lib/openclaw/workspace) : fichiers workspace (AGENTS.md, SOUL.md…) remplacés à chaque démarrage, les autres laissés à l’agent ; projects clonés une fois en projects/<name> ;
  • notes vocales : whisper.cpp local (transcription, modèle ggml small).
darkone.service.openclaw = {
workspace."AGENTS.md" = ./agents/AGENTS.md;
projects.notes = "git@forge.example.com:alice/notes.git";
};
  • enable bool Activer l’assistant OpenClaw d’un espace IA

  • owner nullOr str Membre propriétaire de l’espace : son abonnement connecte l’agent (model), il reçoit les demandes d’approbation et les messages proactifs. Requis avec plusieurs membres (l’ordre de users n’est pas conservé).

  • model str Modèle principal. Runtime d’abonnement de son fournisseur : anthropic/* via le CLI claude (avec darkone.host.ai-space.enableClaude), openai/* via le plugin codex (OAuth ChatGPT).

  • exec enum [ "full" "auto" "ask" "allowlist" "deny" ] Politique de commandes de l’agent (OpenClaw tools.exec.mode) :

    • full : n’importe quelle commande, non approuvée (défaut d’OpenClaw) ;
    • ask : commandes de la liste blanche exécutées, toute autre interroge le propriétaire ;
    • auto : un modèle examine les manques, le propriétaire décide en cas de doute ;
    • allowlist, deny : commandes de la liste blanche uniquement, aucune.

    Liste blanche : openclaw approvals allowlist add --agent main &lt;path&gt;. Avec le runtime claude-cli, les pipes, redirections et substitutions sont refusés avant toute invite hors full.

  • identity.name nullOr str Nom de l’assistant, affiché dans l’interface.

  • identity.emoji nullOr str Emoji de l’assistant, affiché dans l’interface.

  • identity.avatar nullOr str Image d’avatar (URL ou chemin relatif à l’espace de travail).

  • workspace attrs Fichiers de l’espace de travail de l’agent, par chemin relatif : contenu ou fichier. Remplacés à chaque démarrage de la passerelle ; les fichiers non déclarés appartiennent à l’agent.

  • projects attrs Dépôts git clonés une fois dans workspace/projects/&lt;name&gt;, jamais tirés. Les remotes SSH utilisent la clé d’openclaw (/var/lib/openclaw/.ssh, créée à la main) ; la clé du serveur est acceptée au premier clonage.

  • transcription.enable bool Transcrire les notes vocales en local (whisper.cpp, CPU).

  • transcription.model path Modèle ggml de whisper.cpp.

  • transcription.language str Langue parlée (auto : détectée par note).

darkone.service.openclaw = {
enable = false;
owner = "alice";
model = "openai/gpt-6-astra";
exec = "ask";
identity.name = "Jarvis";
identity.emoji = "🦉";
identity.avatar = null;
workspace = { };
projects = { };
transcription.enable = true;
transcription.model = pkgs.fetchurl {
url = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin";
sha256 = "0ywqxbziyp2bv72riyjpw4brk9v46d4cfbjfwqvvjrrq0srakqqv";
};
transcription.language = "fr";
};

Un wiki outline complètement configuré.

  • enable bool Activer le service outline local
darkone.service.outline.enable = false;

OxiCloud — Fast Sovereign Cloud (stockage de fichiers, WebDAV, CalDAV et CardDAV).

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

Relais SMTP Postfix.

  • enable bool Activer le relais SMTP Postfix
darkone.service.postfix.enable = false;

Imprimantes et scanners.

  • enable bool Paquets utiles par défaut
  • loadAll bool Imprimantes et scanners complets
  • enableScanners bool Activer les scanners
  • enableHpPrinters bool Imprimantes HP uniquement
  • enableManualInstall bool Installation manuelle des pilotes
darkone.service.printing = {
enable = false;
loadAll = false;
enableScanners = false;
enableHpPrinters = false;
enableManualInstall = false;
};

Serveur de supervision Prometheus avec escalade déclarative via Alertmanager.

  • enable bool Activer le serveur de métriques + d’alerting Prometheus

  • retentionTime str Durée de rétention des métriques Prometheus

  • alerting.enable bool Activer Alertmanager (activé par défaut une fois les salons Matrix d’alerte provisionnés)

  • alerting.matrix.enable bool Diffuser les alertes dans les salons Matrix via le bot matrix-alertmanager

  • alerting.matrix.userId str ID utilisateur Matrix du bot d’alerte (par défaut depuis network.matrix.bot)

  • alerting.matrix.warningsRoom str ID du salon Matrix pour les avertissements (par défaut depuis network.matrix.warningsRoom)

  • alerting.matrix.incidentsRoom str ID du salon Matrix pour les incidents (par défaut depuis network.matrix.incidentsRoom)

  • alerting.email.enable bool Envoyer aussi les alertes critiques par mail (via le relais Postfix local)

  • alerting.email.to str Destinataire des mails d’alerte critique

  • alerting.network.enable bool Sonder l’atteignabilité réseau (passerelle/tailnet/DNS) avec blackbox_exporter

  • alerting.network.internetProbeTargets listOf str Adresses IP externes que la passerelle de zone ping pour détecter une coupure WAN. Si toutes échouent, ZoneInternetDown se déclenche et inhibe les alertes de down (erronées) des hôtes accessibles uniquement via le WAN (par ex. le HCS). Liste vide = désactivé.

  • alerting.network.httpProbeUrls listOf str URLs HTTP(S) sondées pour la vivacité (ServiceEndpointDown) et l’expiration du certificat TLS (CertificateExpiringSoon/Critical). Vide par défaut : lister les endpoints web publics à surveiller. Les redirections vers un login SSO (3xx) sont comptées comme up ; l’expiration du certificat est lue depuis la poignée de main TLS quelle que soit la réponse HTTP.

  • alerting.silenceOnRebuild bool Émettre le flag dnf-maintenance (textfile collector) afin que les reconstructions inhibent les alertes d’un nœud

  • alerting.thresholds attrs Remplacer les seuils d’alerte de ressources (voir les valeurs par défaut dans dnf/lib/alerts.nix)

  • alerting.silences listOf submodule Alertes connues et acceptées qui ne doivent jamais notifier. Chaque entrée devient une route Alertmanager vers le récepteur null : l’alerte se déclenche toujours et reste visible dans les interfaces Prometheus/Alertmanager/Grafana, elle ne parvient simplement plus à Matrix ni par mail.

    Déclaré sur l’hôte qui fait tourner Prometheus, car c’est là que les règles et routes de la zone sont générées. Un module consommateur à l’échelle de la flotte (usr/modules/alerts.nix) est l’endroit pratique : il est importé partout et ne prend effet que là où Prometheus tourne.

    • alert str Nom de l’alerte à mettre en sourdine (label alertname). Toujours restreint, afin que les niveaux de gravité voisins restent armés.
    • host nullOr str Restreindre le silence à un seul hôte. Null met en sourdine l’alerte pour toute la flotte.
    • matchers attrs Étiquettes de correspondance exacte supplémentaires, combinées en ET avec les autres (par ex. le point de montage ou le device visé par l’alerte).
    • reason str Pourquoi cette alerte est acceptée, et ce qui rendrait le silence supprimable.
darkone.service.prometheus = {
enable = false;
retentionTime = "30d";
alerting.enable = roomsReady;
alerting.matrix.enable = true;
alerting.matrix.userId = "@alertbot:domain.tld";
alerting.matrix.warningsRoom = "!warnings:domain.tld";
alerting.matrix.incidentsRoom = "!incidents:domain.tld";
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 = { };
alerting.silences = [ ];
alerting.silences.alert = "DiskSpaceLow";
alerting.silences.host = "srv-main";
alerting.silences.matchers = { };
alerting.silences.reason = null;
};

DNF restic : métriques de fraîcheur des sauvegardes. Doc : en-tête de ../restic.nix.


DNF restic : le serveur REST. Doc : en-tête de ../restic.nix.


Module de sauvegarde restic : serveur REST + cibles de sauvegarde par hôte.

Exemple (configuration machine) :

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

Disposition des dépôts par cible : un dépôt par catégorie, nommé d’après elle.

<root>/<hostname>/system <- catégorie "system" (/ moins les exclusions)
<root>/<hostname>/nfs <- catégorie "nfs" (/srv/nfs/<...>)
<root>/<hostname>/medias <- catégorie "medias" (/srv/medias/<...>)

Sous-modules (restic/) :

  • server.nix : le serveur REST, un compte par hôte de la flotte ;
  • metrics.nix : métriques de fraîcheur des sauvegardes pour les alertes restic.

Un compte par hôte de la flotte (restic/<hostname>/rest-password), vérifié contre un htpasswd assemblé au démarrage ; privateRepos confine chaque hôte à son propre préfixe <hostname>/.

Nœuds monitorés uniquement (fonction monitoring-node) : chaque tâche horodate sa dernière réussite dans le collecteur textfile, et restic-declared liste les tâches déclarées pour qu’une tâche qui ne réussit jamais vieillisse quand même en alerte dnf-restic-<zone>.

  • enable bool Activer le client de sauvegarde restic
  • enableDryRun bool Mode simulation (dry run)
  • enableWaitRemoteFs bool Exécuter les sauvegardes seulement après remote-fs.target
  • enableServer bool Activer le serveur REST restic
  • listenAll bool Lier le serveur REST sur 0.0.0.0 (clients hors site)
  • serverDataDir str Racine de stockage locale du serveur REST (dépôts de tous les hôtes)
  • targets listOf submodule Destinations de sauvegarde pour cet hôte (chemin local ou URL REST)
    • name str Identifiant de cible, utilisé dans les noms de sauvegarde/d’unité (<category>-<name>)
    • root str Racine du dépôt : chemin local ou URL REST
    • zone str Zone sélectionnant la phrase secrète du dépôt (restic-password-<zone>)
    • categories listOf ( enum [ "system" "nfs" "medias" ] ) Ce qu’il faut sauvegarder vers cette cible
  • nfsPaths listOf str Répertoires NFS (/srv/nfs/<xxx>) inclus dans la catégorie ‘nfs’
  • mediasPaths listOf str Répertoires médias (/srv/medias/<xxx>) inclus dans la catégorie ‘medias’
  • shared raw Valeurs restic partagées avec les sous-modules restic/.
darkone.service.restic = {
enable = false;
enableDryRun = false;
enableWaitRemoteFs = false;
enableServer = false;
listenAll = 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 = [ ];
shared = { inherit params; };
};

Un moteur de recherche durci et entièrement configuré.

  • enable bool Activer le proxy de recherche local
darkone.service.searx.enable = false;

Pistes partagées SuperTuxKart, servies via NFS.

  • enable bool Exporter les pistes partagées SuperTuxKart (éviter d’activer manuellement)
  • enableClient bool Monter les pistes partagées SuperTuxKart de la zone
  • serverName nullOr str Hostname du serveur hébergeant les pistes, par défaut l’hôte stk de la zone
darkone.service.stk = {
enable = stk.isServer;
enableClient = false;
serverName = stk.server;
};

DNF tailscale : mise en pause automatique lors des déplacements. Doc : en-tête de ../tailscale.nix.


DNF tailscale : synchronisation des certificats HCS. Doc : en-tête de ../tailscale.nix.


DNF tailscale : chien de garde d’auto-réparation. Doc : en-tête de ../tailscale.nix.


Client tailscale du tailnet DNF (headscale sur le HCS).

Rejoint le HCS, les passerelles de zone (isGateway : sous-réseau de zone annoncé, routes acceptées, propre résolveur conservé) et les hôtes en déplacement.

Sous-modules (tailscale/) :

  • cert-sync.nix : les passerelles de zone récupèrent les certificats Caddy du HCS ;
  • selfheal.nix : chien de garde redémarrant un tailscaled déconnecté ;
  • autopause.nix : les clients en déplacement mettent tailscale en pause sur une LAN de zone.

Les certificats publics sont émis sur le HCS uniquement ; chaque passerelle de zone récupère tout le stockage Caddy par le tailnet (rsync en tant que nix, élevé à caddy côté HCS), puis le publie sur son propre Caddy.

Chaque minute, vérifie que le backend tourne et que le plan de contrôle voit le nœud en ligne ; redémarre tailscaled après une perte prolongée. Exporte les métriques dnf_tailscale_* pour les alertes TailscaleFlapping/ TailscaleUnhealthy.

Clients en déplacement uniquement. Sur une LAN de zone DNF, --accept-dns hijackerait resolv.conf (dnsmasq/AdGuard local) et --accept-routes entrerait en conflit avec le sous-réseau de zone connecté : un dispatcheur NetworkManager met tailscale en pause sur place et le réactive ailleurs.

  • enable bool Activer le client tailscale pour se connecter au HCS
  • isGateway bool Ce nœud tailscale est une passerelle de sous-réseau
  • isExitNode bool Configurer ce client comme nœud de sortie
  • selfHeal.enable bool Chien de garde : détecter la déconnexion d’headscale et redémarrer tailscaled.
  • autoPauseOnLan.enable bool Client en déplacement : mettre tailscale en pause (down) branché sur une LAN de zone DNF connue, le réactiver ailleurs. Clients NetworkManager non passerelle uniquement.
  • shared raw Valeurs tailscale partagées avec les sous-modules tailscale/.
darkone.service.tailscale = {
enable = false;
isGateway = false;
isExitNode = false;
selfHeal.enable = true;
autoPauseOnLan.enable = false;
shared = {
inherit
tsBin
isHcsSubnetGateway
selfHealEnable
selfHealStateDir
autoPauseEnable
autoPauseStateFile
;
};
};

Serveur Coturn (matrix).

Ajouter des entrées DNS pour optimiser :

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 Activer le service turn local (visio)
darkone.service.turn.enable = false;

Un serveur vaultwarden entièrement configuré.

  • enable bool Activer le service Vaultwarden local
  • enableSmtp bool Activer SMTP pour l’envoi d’e-mails (recommandé, nécessite network.smtp)
darkone.service.vaultwarden = {
enable = false;
enableSmtp = true;
};