Vérification CI pour les livrables d'agents Module
Ce que ça fait — et ce que ça ne fait pas
Un agent qui produit un travail rapporte aussi s'il a vérifié ce travail. Laissé à lui-même, cela revient à de l'auto-déclaration : l'agent corrige sa propre copie. La vérification CI remplace l'affirmation de l'agent par une preuve indépendante, signée cryptographiquement, émise par le système qui a réellement exécuté le build.
- Vaks PM ne sollicite jamais l'extérieur. Il n'a aucun accès sortant vers votre CI. C'est au contraire votre CI qui pousse un résultat signé vers un webhook, et Vaks PM en vérifie la signature. Rien ici n'ouvre de chemin sortant depuis votre instance.
- Le verdict signé prime sur l'agent. Lorsqu'un livrable déclare la méthode
ci_signed, le statut rapporté par l'agent est ignoré ; c'est le résultat CI apparié qui est utilisé à la place, à la fois lorsque le livrable est soumis et de nouveau à la porte de revue. - Il corrobore, il ne récupère pas. Vaks PM ne détient aucun log de build et n'exécute rien. Il vérifie seulement qu'un succès/échec signé est bien arrivé pour le bon commit.
- Tout rejet est silencieux pour l'appelant. Le point de terminaison répond toujours
202, même pour une signature invalide ou un tenant inconnu — délibérément, afin qu'il ne puisse pas servir à sonder votre instance ni à déclencher des tempêtes de réessais. Le seul endroit où un rejet apparaît est la ligne de statut du connecteur et le journal d'audit.
Prérequis
| Côté | Ce qu'il vous faut |
|---|---|
| Votre CI | GitHub Actions, ou toute CI capable de POSTer un corps JSON avec une signature HMAC. Elle doit pouvoir joindre votre instance Vaks PM — c'est un appel entrant, donc une instance entièrement privée exige que la CI soit sur le même réseau ou joignable via votre propre passerelle. |
| Vaks PM | La permission agent:manage — un administrateur de l'organisation, ou un utilisateur détenant le grant d'administrateur IA. C'est la même permission que celle qui gouverne les agents eux-mêmes : quiconque pilote votre programme IA peut donc la configurer sans être administrateur complet. |
| Un secret partagé | Une chaîne que vous générez, posée des deux côtés. Elle ne circule jamais ; elle sert uniquement à signer et à vérifier. |
agent:manage ne peut jamais être détenue par un agent, aucun agent ne peut créer ni modifier le connecteur qui juge son propre travail.
Étape 1 — Créer le connecteur dans Vaks PM
Ouvrez Admin → Intégrations → Vérification CI et pressez Ajouter un connecteur. Choisissez le type de signataire :
- GitHub Actions — il lit les événements de webhook natifs de GitHub et son en-tête
X-Hub-Signature-256. - CI générique (HMAC) — pour tout le reste ; il lit un corps JSON plat et un en-tête
X-Vaks-CI-Signature.
Renseignez :
| Champ | Quoi renseigner |
|---|---|
| Libellé | Texte libre, pour la liste des connecteurs. |
| Secret HMAC partagé | Le secret que vous poserez aussi sur le webhook CI. Chiffré au repos, jamais réaffiché — laissez vide lors d'une modification pour conserver celui déjà enregistré. |
| Activé (accepter les résultats entrants) | Cochez-le. Un connecteur désactivé rejette chaque résultat comme s'il n'existait pas. |
Enregistrez, puis pressez Copier l'URL sur la carte du connecteur. C'est l'adresse vers laquelle votre CI poste :
https://<your-host>/api/v1/webhooks/ci/<provider>
où <provider> vaut github ou generic. Il n'existe qu'un seul connecteur de chaque type par organisation.
202 et rien ne se passerait. Si vous exploitez plusieurs organisations, chacune a son propre connecteur, secret et URL.
Étape 2 — Dans GitHub
Ajoutez un webhook de dépôt (ou d'organisation) : Settings → Webhooks → Add webhook.
- Payload URL : l'URL que vous avez copiée, se terminant par
/github. - Content type :
application/json. C'est important — tout autre type de contenu signifie que le corps brut n'est jamais capturé et que la signature ne correspondra jamais. - Secret : le secret partagé exact de l'étape 1.
- Events : choisissez les événements individuels et sélectionnez Workflow runs, Check suites ou Statuses — selon ce que votre pipeline émet. Vaks PM lit les trois.
GitHub signe le corps avec X-Hub-Signature-256 automatiquement dès que le secret est posé ; il n'y a rien à configurer sur la signature elle-même. Les exécutions en cours et les événements ping sont acceptés et ignorés — seule une exécution terminée porte un verdict.
Étape 2b — Un signataire CI générique
Si vous n'êtes pas sur GitHub, faites POSTer par votre pipeline un corps JSON plat vers l'URL /generic, signé avec le HMAC-SHA256 du corps brut, dans l'en-tête X-Vaks-CI-Signature sous la forme sha256=<hex>.
# Corps
{ "commitSha": "<full-sha>", "conclusion": "success", "runUrl": "https://ci.example.com/run/123" }
# En-tête
X-Vaks-CI-Signature: sha256=<hmac-sha256 hex of the raw body, keyed with the shared secret>
Un signataire minimal, en shell :
BODY='{"commitSha":"'"$GIT_SHA"'","conclusion":"'"$RESULT"'"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -sS -X POST "$VAKS_CI_URL" \
-H 'Content-Type: application/json' \
-H "X-Vaks-CI-Signature: sha256=$SIG" \
--data-raw "$BODY"
conclusion égal à success (insensible à la casse). Toute autre valeur — failure, cancelled, timed_out, n'importe laquelle — est un échec. Le champ peut aussi s'appeler status ou state, et le commit peut être commitSha, commit_sha ou sha. Un commit absent ou un verdict null signifie que le résultat est ignoré, pas échoué.
Étape 3 — L'exiger sur un projet
Un connecteur seul se contente d'enregistrer les résultats signés. Pour qu'un résultat fasse réellement barrage à un livrable, deux choses doivent s'aligner :
- Le livrable déclare la méthode de vérification
ci_signed— l'agent le fait au moment de la soumission, en y joignant le commit qu'il a compilé. - La politique de vérification du projet exige ce niveau de preuve. Cela se règle dans l'onglet IA du projet, où vous choisissez aussi entre report only (la porte observe et enregistre mais ne bloque pas) et enforce (la porte peut retenir ou rejeter le livrable).
Commencez en report only pour voir la corroboration circuler sans affecter les revues, puis basculez en enforce une fois que vous faites confiance au signal.
Étape 4 — Vérifier la configuration
Déclenchez une exécution CI pour qu'un résultat signé soit envoyé, puis vérifiez la carte du connecteur dans Admin → Intégrations → Vérification CI :
- Dernier événement affiche un horodatage récent et aucune erreur — le résultat est arrivé et la signature a été vérifiée.
- Une pastille Dernier événement rejeté avec un motif signifie que la signature a échoué ou que le connecteur est mal configuré. C'est le seul signal visible, puisque le point de terminaison répond toujours
202.
Parcourez ensuite une fois le chemin réel : faites soumettre par un agent un livrable avec le commit correspondant, et vérifiez dans l'écran de revue que le verdict CI est affiché à côté — provider, conclusion et un lien vers l'exécution.
Comment les résultats sont appariés à un livrable
Comprendre ceci lève l'essentiel de la confusion :
- L'appariement se fait par SHA de commit, à l'exact. Le résultat est apparié au livrable dont le commit est égal à celui qui a été signé. Un SHA court face à un SHA complet ne correspondra pas — envoyez le SHA complet.
- Un résultat signé est mis en tampon pendant 24 heures. La CI peut signer avant même que le livrable existe ; c'est tout l'intérêt. Mais si le livrable est revu plus de 24 heures après l'exécution, le résultat en tampon a expiré et la porte se rabat sur l'attente.
- Rien n'est consommé. Un livrable peut être réévalué autant de fois qu'on veut au regard du même résultat en tampon ; le résultat le plus récent non expiré pour le commit l'emporte.
- Le verdict est revérifié à la revue. Un résultat qui arrive après la soumission de l'agent est tout de même pris en compte lorsque le relecteur ouvre la porte — le statut figé à la soumission n'est jamais tenu pour vrai.
Dépannage
Le point de terminaison répond 202 quoi qu'il arrive : la carte du connecteur et le journal d'audit sont donc vos seules fenêtres. Les motifs de rejet apparaissent sur la ligne Dernier événement de la carte.
| Symptôme | Cause & correctif |
|---|---|
| Rien n'arrive, la carte ne se met jamais à jour | Mauvais nom d'hôte (posté vers une IP ou un hôte partagé → aucun tenant), ou le mauvais chemin de provider. Vérifiez que l'URL se termine par /github ou /generic et utilise le domaine propre au tenant. |
| Dernier événement rejeté · signature | Le secret diffère entre les deux côtés, ou — le classique — le type de contenu n'est pas application/json, de sorte que le corps brut n'a jamais été capturé. Reposez le secret et corrigez le type de contenu. |
| Dernier événement rejeté · no connector | Le connecteur est désactivé, n'a pas de secret, ou son secret stocké est devenu illisible après une rotation de clé de chiffrement. Ressaisissez le secret et activez-le. |
| Les résultats arrivent mais la porte reste en attente | Le commit ne correspond pas : SHA court contre SHA complet, ou le livrable porte un commit différent. Cela peut aussi signifier que la revue a eu lieu plus de 24 heures après l'exécution — le tampon a expiré. |
| GitHub indique le webhook livré, Vaks PM l'a ignoré | L'événement était un ping ou une exécution en cours — accepté mais pas un verdict. Seules les exécutions terminées portent une conclusion. |
Ce qui est journalisé
Sous Admin → Sécurité & Conformité → Journal d'audit : la création, la modification et la suppression d'un connecteur en sévérité critique ; chaque résultat signé accepté sous ci_result.received ; chaque rejet sous ci_webhook.rejected avec le motif. Lorsqu'un livrable est évalué, l'entrée enregistre si la CI l'a corroboré, la conclusion, et — si un agent a sur-déclaré — la rétrogradation de confiance. Le secret partagé n'est jamais écrit dans le journal.
À lire aussi : annuaires d'agents pour les identités gouvernées · webhooks sortants, la direction inverse · toutes les intégrations.