SOFRA · Plateforme IA
Architecture

Déploiement Nix / NixOS

Chaque connecteur et service est un flake ; chaque serveur une config NixOS versionnée, déployée via le même pattern que nos sites docs.

Tout ce qui n'est pas le chat acheté est packagé en flake Nix et déployé sur NixOS de façon reproductible (principe 5). On réutilise le pattern de déploiement déjà en place (deploy-fumadocs, nixos-deployment).

Le modèle

flowchart LR
  subgraph REPOS [Dépôts flake]
    A[mcp-sage]
    B[mcp-yooz]
    C[referentiel-cleaner]
    D[paperasse-mcp]
  end
  subgraph HOST [Serveur NixOS]
    direction TB
    CFG[configuration.nix<br/>versionnée]
    HUB[(svc: hub MCP)]
    S1[(svc: mcp-sage)]
    S2[(svc: mcp-yooz)]
    S3[(svc: referentiel-cleaner)]
  end
  A & B & C & D --> CFG
  CFG --> HUB & S1 & S2 & S3
  HUB --> S1 & S2 & S3
  • Un flake par composant, exposant un packages.default (le binaire) et un nixosModules.default (le service systemd). C'est déjà le cas de paperasse-mcp (packages.default + overlays.default).
  • Un dépôt de config serveur (type nixos-deployment) qui importe ces modules, pose les options (ports, périmètre, secrets) et décrit la machine.
  • Déploiement = nixos-rebuild switch --flake .#sofra-ai. Rollback = sélection d'une génération précédente. Aucune configuration manuelle.

Module NixOS d'un connecteur

nixosModules.default (mcp-sage)
{ config, lib, pkgs, ... }:
let cfg = config.services.mcp-sage;
in {
  options.services.mcp-sage = {
    enable = lib.mkEnableOption "Connecteur MCP Sage 100";
    port = lib.mkOption { type = lib.types.port; default = 7001; };
    environmentFile = lib.mkOption { type = lib.types.path; };  # secrets hors du store
  };
  config = lib.mkIf cfg.enable {
    systemd.services.mcp-sage = {
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = "${pkgs.mcp-sage}/bin/mcp-sage --http --port ${toString cfg.port}";
        EnvironmentFile = cfg.environmentFile;   # SAGE_API_KEY, etc.
        DynamicUser = true;
        # durcissement
        ProtectSystem = "strict";
        ProtectHome = true;
        NoNewPrivileges = true;
      };
    };
  };
}

Assemblage côté serveur

configuration.nix (extrait)
services.mcp-hub = {
  enable = true;
  port = 7000;
  connectors = [ "mcp-sage" "mcp-yooz" "mcp-bazimo" "mcp-pms" "mcp-sirene" "paperasse-mcp" ];
  environmentFile = "/run/secrets/hub.env";
};
services.mcp-sage = { enable = true; environmentFile = "/run/secrets/sage.env"; };
services.mcp-yooz = { enable = true; environmentFile = "/run/secrets/yooz.env"; };
# … un bloc par connecteur/service

Secrets

  • Jamais dans le store Nix ni dans Git. Les secrets sont des EnvironmentFile hors store (/run/secrets/*), gérés par sops-nix ou agenix.
  • Référencés par les modules, injectés à l'exécution dans systemd.
  • Voir Sécurité & données.

Topologie

  • Un serveur suffit au démarrage (hub + connecteurs + services colocalisés).
  • Réseau privé : les connecteurs n'écoutent que sur l'interface du hub ; seul le hub est joignable par le chat (HTTPS + auth).
  • Sauvegarde : la config est dans Git ; l'état (données extraites, journaux d'audit) est sauvegardé séparément.

Réutilisation

Le pattern deploy-fumadocs (submodule deploy, detect.sh, .envproject-config.nix, nixos-rebuild) s'applique tel quel. Le présent site de documentation se déploie d'ailleurs par ce même mécanisme.