De la merge request à une modification approuvée de SecureTransport.

Cette chaîne GitLab fonctionne sans assistant IA. Vos équipes décrivent la configuration SecureTransport en YAML sous flows/, la versionnent dans GitLab, et un pipeline l’applique après le clic d’un approbateur habilité. Le worker helixflow-ci lance lui-même le MCP HelixFlow local, qui reste la seule porte d’écriture vers SecureTransport. Cette première version crée et modifie des objets ; elle n’en supprime aucun.

1. Ce que fait la chaîne

Ce site ne se connecte ni à votre GitLab ni à votre SecureTransport : la chaîne s’exécute sur vos runners, avec votre coffre de secrets.

qualitycontrôle hors ligne de flows/, sur la merge request
<cible>:planflow_plan, résumé à relire
<cible>:applyjob manuel, flow_apply
<cible>:verifyflow_diff, aucune dérive

Sur votre runner GitLab :

  • helixflow-ci · lance le MCP
  • helixflow · seule porte d’écriture vers SecureTransport
  • OpenBao · authentification JWT et identifiants ST
La chaîne GitLab, de la merge request à la vérification de SecureTransport.

Quatre jobs s’enchaînent, dans cet ordre :

quality
Sur la merge request et sur main : contrôle hors ligne des fichiers modifiés sous flows/ (schéma, règles de modélisation, gouvernance, secrets en clair), sans profil ni connexion à SecureTransport.
<cible>:plan
Après la fusion sur main : planifie avec flow_plan chaque fichier ajouté ou modifié sous flows/<cible>/ et publie un résumé à relire. Un pipeline lancé à la main sur main (Build > Pipelines > Run pipeline) planifie tous les fichiers de chaque cible ; ceux qui n’ont pas changé donnent noop.
<cible>:apply
Job manuel : un approbateur habilité clique sur ▶ dans GitLab pour le lancer. Ce clic est l’approbation ; il n’y en a pas d’autre. Avant d’appeler flow_apply, helixflow-ci revérifie le contexte du job, l’appartenance actuelle de l’approbateur au groupe de la cible et les empreintes des fichiers.
<cible>:verify
Compare la cible au dépôt après l’application avec flow_diff ; toute dérive fait échouer le job et liste les champs différents.

Chaque job publie helixflow-report.json et helixflow-report.md en artefacts pendant 30 jours, même en échec. Le rapport ne contient jamais de valeur secrète.

2. Prérequis

  • Un GitLab autogéré. La chaîne est qualifiée sur GitLab CE 19.4.1 ; GitLab.com et les fonctions Premium ne sont pas couverts par cette première version.
  • Une machine Linux qui joint SecureTransport, avec GitLab Runner en exécuteur shell : un runner quality non protégé et sans secret (tag helixflow-quality), et un runner protégé par cible (tags helixflow-nonprod et helixflow-prod dans le modèle de pipeline). Seul Ubuntu 24.04 LTS amd64 est qualifié ; la version arm64 est fournie sans être qualifiée.
  • Un compte système dédié par cible sur cette machine (par exemple helixflow-prod) : il exécute le runner de la cible et possède son cible.yaml ainsi que ses dossiers HelixFlow de configuration, d’état et de licence. Aucun compte de cible ne lit les fichiers d’un autre.
  • Un serveur OpenBao, ou un coffre existant compatible avec l’API Vault, joignable depuis cette machine.
  • Deux projets GitLab : le projet flows, où les équipes versionnent leurs fichiers sous flows/<cible>/, et un projet d’administration séparé qui contient le fichier de pipeline.
  • Un groupe GitLab d’approbateurs par cible, et un compte de service GitLab dont le jeton read_api, rangé dans OpenBao, lit l’appartenance à ces groupes.

3. Installer les binaires sur la machine des runners

Téléchargez le binaire Linux de HelixFlow, vérifiez son empreinte SHA-256 avec la valeur publiée, puis installez-le à un emplacement fixe que cible.yaml désignera. Téléchargements

Terminal
sha256sum helixflow-linux-amd64
sudo install -d -m 0755 /opt/helixflow/bin
sudo install -m 0755 helixflow-linux-amd64 /opt/helixflow/bin/helixflow

Sur ARM64, remplacez amd64 par arm64 dans les noms de fichiers.

La licence est liée à la machine, pas au compte : demandez-en une seule pour cette machine, comme à la section 2 de ce guide, depuis une session de l’un des comptes de cible. 2. Activer la licence machine

Créer request.json
export HELIXFLOW_LICENSE_DIR=/var/lib/helixflow-prod/licenses
/opt/helixflow/bin/helixflow license request --output request.json

Après émission, copiez le même helixflow.lic chez chaque compte de cible (hors de son license_dir, avec ce compte pour propriétaire), importez-le sous ce compte (l’import l’écrit dans son license_dir), vérifiez-le, puis supprimez la copie. Répétez pour chaque cible (par exemple helixflow-nonprod) :

Importer et vérifier la licence
sudo install -o helixflow-prod -g helixflow-prod -m 0600 helixflow.lic /var/lib/helixflow-prod/helixflow.lic
sudo -u helixflow-prod env HELIXFLOW_LICENSE_DIR=/var/lib/helixflow-prod/licenses /opt/helixflow/bin/helixflow license import /var/lib/helixflow-prod/helixflow.lic
sudo rm /var/lib/helixflow-prod/helixflow.lic
sudo -u helixflow-prod env HELIXFLOW_LICENSE_DIR=/var/lib/helixflow-prod/licenses /opt/helixflow/bin/helixflow license status

helixflow-ci n’est pas publié sur la page Téléchargements : il est fourni par l’éditeur sur demande, pour Linux amd64 ou arm64, avec son empreinte SHA-256 et le modèle de pipeline pipeline.yml. Demandez la version qui correspond au helixflow installé. Vérifiez l’empreinte, puis placez-le dans un dossier du PATH des comptes des runners : le pipeline l’appelle par son nom. Demander helixflow-ci

Terminal
sha256sum helixflow-ci-linux-amd64
sudo install -m 0755 helixflow-ci-linux-amd64 /usr/local/bin/helixflow-ci

helixflow-ci ne demande pas de licence ; le MCP qu’il lance utilise celle du compte de la cible. helixflow-ci refuse un MCP qui n’annonce pas les capacités attendues : mettez toujours à jour les deux ensemble.

4. Régler GitLab

Ces réglages sont obligatoires. La mention « vérifié par doctor » signale ceux que helixflow-ci doctor contrôle par l’API (étape 8 de cette section) ; contrôlez les autres dans GitLab.

  • Les équipes sont Developer dans le groupe du projet flows et peuvent fusionner sur main. Le rôle Maintainer est réservé aux administrateurs : un Maintainer peut définir des variables CI (BASH_ENV, LD_PRELOAD…) exécutées avant le script du job, dans le compte qui obtient les secrets.
  • Rôle minimum pour surcharger les variables de pipeline : personne. Vérifié par doctor.
  • main protégée : aucun push direct, fusion par merge request uniquement. Vérifié par doctor.
  • Settings > Merge requests : « Pipelines must succeed » coché. Sans lui, une merge request dont quality échoue reste fusionnable. Vérifié par doctor.
  • Même page : « Skipped pipelines are considered successful » décoché. Sinon, une merge request dont le pipeline est ignoré (commit [skip ci]) se fusionne sans que quality ait tourné. Vérifié par doctor.
  • Settings > CI/CD > CI/CD configuration file du projet flows : le fichier de pipeline du projet d’administration (étape 7 de cette section). Vérifié par doctor.
  • Le projet d’administration est partagé avec le groupe du projet flows au niveau Reporter, exactement. En dessous, chaque pipeline échoue en config_error sans message ; au-dessus, les équipes pourraient agir sur le projet d’administration. Vérifié par doctor quand pipeline_config est renseigné.
  • Un groupe approbateur par cible ; c’est lui que le job apply de la cible vérifie au clic. Vérifié par doctor (existence du groupe).
  • Le compte de service du jeton read_api est Guest de chaque groupe approbateur et absent du groupe des flux : il lit l’appartenance des approbateurs et ne joue aucun job.
  • Runners de cible protégés, verrouillés sur le projet flows, avec leurs tags dédiés et l’exécuteur shell ; runner quality non protégé, sans accès aux fichiers ni au rôle OpenBao d’une cible. Non vérifié par doctor.

5. Préparer OpenBao

Chaque job de cible déclare un jeton OIDC GitLab, HELIXFLOW_CI_ID_TOKEN, d’audience helixflow-ci. helixflow-ci l’échange contre un jeton de coffre valable quelques minutes, par la méthode d’authentification JWT d’OpenBao. Le job quality ne déclare aucun jeton et ne parle jamais au coffre.

Méthode JWT
Activée sur le montage auth_mount de cible.yaml (jwt-gitlab dans l’exemple) et configurée pour accepter les jetons émis par votre GitLab.
Rôle de la cible
Un rôle par cible (role de cible.yaml), avec bound_audiences: helixflow-ci et des bound_claims qui doivent tous correspondre : project_id du projet flows, ref: main, ref_type: branch, ref_protected: "true", environment égal au nom de la cible, et ci_config_ref_uri. GitLab construit ce dernier à partir du projet flows et de son réglage CI/CD configuration file, par exemple :
ci_config_ref_uri
gitlab.example.test/teams/flows//pipeline.yml@helixflow-admin/admin@refs/heads/main

Changer le fichier de configuration CI/CD du projet flows ferme donc l’accès au coffre tant que le rôle n’est pas mis à jour.

Politique
Lecture seule sur les secrets de la cible, jamais sur ceux d’une autre cible.
Politique OpenBao · prod
path "helixflow/data/prod/*" { capabilities = ["read"] }
Chemins KV v2
Sous le montage kv_mount (helixflow dans l’exemple) :
  • helixflow/<cible>/st — identifiants SecureTransport (champs login et password) ;
  • helixflow/<cible>/gitlab — jeton read_api du compte de service (champ token) ;
  • helixflow/<cible>/flows/ — secrets référencés par les flux (étape 9 de cette section).

Saisissez vous-même ces secrets dans OpenBao. Ne placez jamais un identifiant SecureTransport, un jeton ou un mot de passe dans le dépôt, dans une variable CI/CD GitLab, dans cible.yaml, sur ce site ou dans une conversation.

Après un redémarrage, OpenBao démarre scellé : un opérateur doit le desceller avant que la chaîne reprenne. En attendant, chaque job de cible échoue en indiquant que le coffre est scellé.

6. Décrire la cible : cible.yaml

Un fichier par cible, propriété du compte système de la cible et en mode 0600 (refusé sinon). Il ne contient aucun secret, seulement des références et des chemins absolus.

Emplacement par défaut, et option qui en désigne un autre : /etc/helixflow-ci/<cible>/cible.yaml · --config

/etc/helixflow-ci/prod/cible.yaml
target: prod
flows_dir: flows/prod
gitlab:
  url: https://gitlab.example.test
  ca_path: /etc/helixflow-ci/prod/gitlab-ca.pem
  branch: main
  flows_project_id: 42
  approver_group_id: 7
  pipeline_config: pipeline.yml@helixflow-admin/admin
helixflow:
  binary: /opt/helixflow/bin/helixflow
  profile: st-prod
  config_dir: /var/lib/helixflow-prod/config
  state_dir: /var/lib/helixflow-prod/state
  license_dir: /var/lib/helixflow-prod/licenses
approval_window: 60m
openbao:
  address: https://openbao.example.test:8200
  ca_path: /etc/helixflow-ci/prod/openbao-ca.pem
  auth_mount: jwt-gitlab
  role: helixflow-prod
  kv_mount: helixflow
target · flows_dir
Nom de la cible et son dossier dans le projet flows, qui doit valoir flows/<nom de la cible>. Le nom commence par une lettre minuscule, suivie de lettres minuscules, de chiffres ou de tirets, 32 caractères au plus. C’est la valeur passée à l’option : --target
gitlab.url · gitlab.ca_path · gitlab.branch
Adresse HTTPS de votre GitLab ; ca_path (facultatif) désigne l’autorité qui a signé son certificat ; branch est la branche protégée, main par défaut.
flows_project_id · approver_group_id
Identifiants numériques du projet flows et du groupe approbateur de la cible.
pipeline_config
Facultatif : valeur attendue du réglage CI/CD configuration file. doctor la compare au réglage réel et en déduit le projet d’administration.
helixflow.binary · helixflow.profile
Chemin du binaire helixflow installé à l’étape 3 de cette section et nom du profil HelixFlow de la cible.
config_dir · state_dir · license_dir
Dossiers HelixFlow de configuration, d’état et de licence du compte de la cible. Les jeux de plans en attente d’approbation sont conservés sous state_dir.
approval_window
Délai accordé à l’approbation après le plan : entre 5 minutes et 24 heures, 60 minutes par défaut.
openbao
Adresse HTTPS et autorité du coffre, montage de l’authentification JWT, rôle de la cible et montage KV v2 de ses secrets (étape 5 de cette section).
Profil HelixFlow de la cible

Le profil désigné par profile se trouve dans le config_dir du compte de la cible. La chaîne est stricte sur toutes les cibles : classe prod pour la production, preprod pour une cible hors production ; un profil d’une autre classe est refusé au plan. admin_actors nomme le compte système de la cible, et human_confirmation: false, car l’approbation est le clic GitLab.

/var/lib/helixflow-prod/config/profiles/st-prod.yaml
base_url: https://st-prod.example.test:8444
env_class: prod
tls:
  mode: ca
  ca_path: /var/lib/helixflow-prod/config/ca/st-ca.pem
credentials:
  source: env
admin:
  production:
    enabled: true
    admin_actors: [helixflow-prod]
    strict_stop: true
    human_confirmation: false
    write:
      actions: [create, modify]
      families: [account, application, certificate, route, site, subscription, transfer-profile, transfer-site]

Avec credentials: source: env, helixflow-ci transmet au MCP les identifiants lus dans OpenBao ; ils n’apparaissent dans aucun fichier.

7. Brancher le pipeline

Le projet flows ne contient pas de .gitlab-ci.yml. Le modèle pipeline.yml, fourni sur demande avec helixflow-ci, est déposé dans le projet d’administration, et les équipes ne le modifient pas. Le projet flows le désigne par son réglage CI/CD configuration file, sous la forme pipeline.yml@<groupe admin>/<projet admin> :

CI/CD configuration file
pipeline.yml@helixflow-admin/admin

Le modèle déclare deux cibles, nonprod (dossier flows/nonprod, classe preprod) et prod (dossier flows/prod, classe prod). Il ne se déclenche que sur une merge request, un push sur main ou un pipeline lancé à la main sur main. Ces lignes du modèle portent les garanties décrites ci-dessous :

pipeline.yml · extrait
variables:
  GIT_DEPTH: "0"

.target:
  id_tokens:
    HELIXFLOW_CI_ID_TOKEN:
      aud: helixflow-ci

.prod:
  extends: .target
  tags: [helixflow-prod]
  resource_group: helixflow-prod
  variables: { HF_TARGET: prod }
  • GIT_DEPTH: "0" demande l’historique complet. Avec la profondeur limitée par défaut de GitLab, une base de comparaison trop ancienne (push de plusieurs commits, merge request longue) ferait échouer quality et plan (code 2).
  • resource_group: helixflow-<cible> empêche deux pipelines d’écrire en même temps sur la même cible.
  • id_tokens fournit à chaque job de cible le jeton OIDC échangé contre un jeton OpenBao. Les jobs plan, apply et verify déclarent aussi l’environnement de leur cible, que vérifie la réclamation environment du rôle.

8. Vérifier l’installation avec doctor

Après chaque installation ou modification, lancez doctor hors job, dans une session du compte de la cible. Il ne fait que des lectures et n’écrit pas de rapport.

Terminal
helixflow-ci doctor --target prod --gitlab-token-file ./gitlab-read-token

Sans jeton GitLab, il vérifie :

  • que le binaire helixflow existe et que la licence est valide ;
  • que le MCP démarre et annonce les capacités attendues ;
  • que le profil est strict, sans question humaine, et que flow_apply y est permis pour le compte de la cible (un refus est nommé par sa policy_key) ;
  • qu’OpenBao répond et n’est pas scellé.

Pour vérifier aussi les réglages GitLab marqués « vérifié par doctor » à l’étape 4 de cette section, ajoutez l’option : --gitlab-token-file

Le fichier, en mode 0600, contient un jeton read_api qui doit pouvoir lire le projet flows, le projet d’administration et le groupe approbateur ; le jeton du compte de service rangé dans OpenBao ne suffit pas, et le rôle GitLab minimal qui suffit n’a pas été mesuré. Supprimez le fichier après doctor, ou utilisez un jeton de courte durée et révoquez-le. Sans cette option, doctor l’indique et saute ces contrôles.

Chaque ligne commence par OK, KO ou -- (contrôle non effectué). Un seul KO fait sortir doctor en code 1. doctor ne lit pas les identifiants SecureTransport du coffre : le premier job plan les éprouve.

9. Travailler au quotidien

  1. Écrivez ou modifiez un document de flux YAML sur une branche, directement dans flows/<cible>/ (sans sous-dossier), avec l’extension .yaml ou .yml, puis ouvrez une merge request. Passer en production consiste à copier le fichier vers flows/prod/ par une merge request.
  2. quality contrôle la merge request hors ligne. En cas d’échec, la merge request reste bloquée et le rapport nomme le fichier, le champ et la règle.
  3. Après la fusion sur main, le job plan de la cible publie son résumé : une ligne par objet et par action, les compteurs et l’heure limite d’approbation. Lisez-le avant d’approuver ; « rien à appliquer » signifie que tout est noop.
  4. Pour approuver, un membre du groupe approbateur clique sur ▶ du job apply de la cible. Le clic approuve le jeu de plans du dernier job plan réussi de ce pipeline : relancer plan le remplace, relisez alors le rapport le plus récent.
  5. Le job verify compare ensuite la cible au dépôt ; une dérive le fait échouer et liste les champs différents.

Un plan non approuvé expire à l’heure limite fixée par approval_window (au plus 24 heures). apply refuse aussi un plan périmé ou déjà consommé, ou dont le commit, un fichier ou un secret a changé. Dans ces cas, rien n’est écrit : relancez le job plan.

Référencer un secret dans un flux

Un secret ne s’écrit jamais en clair dans un flux. Placez une référence ${secret:<chemin>#<champ>} sous une clé secrète (nom contenant password, secret, token…) ; le chemin est relatif à helixflow/<cible>/flows/ dans le coffre.

flows/prod/partner-a.yaml · extrait
partnerPassword: ${secret:pesit/partner-a#password}

quality refuse une valeur en clair sous une clé secrète, et une référence sous une autre clé. Les références sont résolues au plan puis à nouveau à l’apply, en mémoire : le document rendu n’est jamais écrit sur disque, et aucune valeur résolue n’apparaît dans un rapport ou un journal.

10. Résoudre un problème de la chaîne

Code 0 · succès
Tout est appliqué, ou le jeu de plans est entièrement noop.
Code 1 · refus ou échec connu
Par exemple : politique refusée au plan, profil non strict, approbateur hors groupe ou compte inactif, plan expiré ou périmé, environnement dangereux, MCP ou coffre injoignable. Le message donne la cause ; quand il demande de relancer le job plan, rien n’a été écrit.
Code 2 · configuration invalide
cible.yaml illisible, mal formé, pas en 0600 ou avec un chemin relatif ; option manquante ou mal formée ; autorité TLS illisible ; dépôt Git illisible.
Code 3 · issue incertaine
helixflow-ci ne sait pas ce qui a été écrit sur la cible (réponse du MCP perdue, exécution non confirmée). Le MCP bloque la cible (execution.pending_reconciliation) et rien n’est rejoué automatiquement.
Échec d’un fichier parmi plusieurs
apply s’arrête au premier échec ; le rapport distingue les fichiers appliqués, en échec et non tentés. Relancez (Retry) le job plan puis cliquez sur apply dans le même pipeline : les fichiers déjà appliqués donnent noop. Un pipeline lancé à la main sur main convient aussi.

Rapprocher après une issue incertaine

Après un code 3, un opérateur, et non le pipeline, rapproche la cible :

  1. Relevez l’execution_ref du fichier concerné dans helixflow-report.md.
  2. Lisez execution_status sur le MCP, avec votre assistant habituel ou un client MCP direct.
  3. Vérifiez l’état réel dans SecureTransport, puis appelez execution_reconcile pour débloquer la cible.
  4. Seulement ensuite, lancez un nouveau pipeline pour planifier et appliquer.

Cliquer de nouveau sur apply sans ce rapprochement retombe sur le même jeu de plans bloqué. Si execution_status ne montre aucune exécution pour ce plan, rien n’a été écrit : relancez le job plan, puis approuvez à nouveau.