# Comment approuver des builds locaux non signés ?

Un build local non signé n'est pas automatiquement suspect. Il ne fournit pas non plus d'identité reconnaissable. Ces deux faits doivent rester vrais en même temps, sinon votre processus d'approbation tombe dans l'une de deux mauvaises habitudes : les développeurs approuvent chaque processus anonyme parce qu'ils doivent travailler, ou ils renoncent et exécutent les agents avec des identifiants directs.

La bonne question est plus précise : quels éléments permettent à une personne d'approuver *cette exécution particulière* d'un agent compilé localement sans prétendre que tous les processus provenant d'un dossier familier méritent le même niveau de confiance ? La réponse repose sur un processus construit autour des limites de session, de la provenance du lancement, de la portée des identifiants et de la volonté de refuser les demandes qui ne peuvent pas s'expliquer.

## Non signé signifie qu'une déclaration manque, pas qu'un niveau de risque est établi

Un code non signé n'a pas formulé d'affirmation d'identité soutenue par un éditeur que vous pourriez vérifier au moyen d'une chaîne de certificats. Cela ne dit rien, à lui seul, sur le caractère malveillant du code, sur son éventuelle revue, sur une modification locale ou sur le fait qu'il vient d'être compilé depuis le dépôt ouvert devant vous. Une seule catégorie d'éléments de preuve disparaît.

Les équipes se trompent dans les deux sens. Certains traitent tout exécutable non signé comme hostile, ce qui pousse le développement normal vers des canaux parallèles et des clés API personnelles. D'autres utilisent « non signé » comme synonyme de « mon code local », ce qui crée une catégorie d'approbation trop large qu'un processus sans rapport peut exploiter.

Gardez ces distinctions séparées :

- **L'identité de l'éditeur** répond à la question de savoir qui a signé un build distribué.
- **La provenance du build** indique quelles sources, quelle révision, quelle machine et quelle commande ont produit cet exécutable.
- **La provenance de l'exécution** indique ce qui a lancé le processus qui demande un accès à cet instant.
- **L'autorité d'action** indique quel identifiant ou quel accès distant ce processus peut utiliser.

Une signature peut aider à répondre à la première question. Un processus local propre doit répondre aux trois autres. Si un développeur compile un client d'agent depuis un worktree, sa confiance vient généralement de l'état du dépôt et de la commande qu'il vient d'exécuter, pas d'un certificat public. L'expérience d'approbation doit présenter ces éléments au lieu de traiter l'absence de certificat comme un verdict.

La documentation de signature de code d'Apple établit la même limite de manière plus formelle. Une exigence désignée identifie une instance de code signé selon la politique qui la vérifie ; elle ne prouve pas que ce code convient à chaque ressource ni à chaque action future. Apple précise également que chaque sous-système de macOS applique sa propre politique de confiance. Une passerelle d'action doit donc éviter de transformer l'état de signature du code en décision universelle d'autorisation.

## Un parcours dédié aux builds locaux doit fournir des éléments qu'un autre processus ne peut pas emprunter

Donnez aux clients d'agent compilés localement leur propre parcours d'approbation. Ne les rangez pas dans une catégorie appelée « non signé », « développement » ou « terminal ». Ces étiquettes décrivent trop de processus pour être utiles.

Le parcours doit demander quatre éléments avant qu'un développeur approuve une session :

1. Le chemin de l'exécutable doit se trouver dans un dépôt contrôlé par le développeur ou dans un répertoire de sortie de build.
2. Le processus parent doit être un lanceur attendu, généralement un terminal, une tâche d'IDE ou un script intermédiaire appartenant à l'équipe.
3. L'état du dépôt et la commande de build doivent être faciles à examiner, sans devoir reconstituer la journée de mémoire.
4. La destination demandée et l'identifiant doivent correspondre au travail en cours.

Aucun de ces signaux n'est parfait isolément. Ensemble, ils rendent plus difficile le passage d'un téléchargement aléatoire, d'un assistant en arrière-plan ou d'une dépendance compromise pour une exécution locale habituelle.

C'est ici que les équipes se tournent souvent vers un moteur de règles : autoriser les chemins sous un répertoire, accepter les commandes dont le nom commence par `agent`, exempter tout ce qui est lancé par un IDE donné. Ne faites pas cela. Une règle statique est séduisante parce qu'elle supprime les interruptions, mais elle transforme une décision facile à examiner en exception durable. Tout processus capable de préparer le bon chemin ou la bonne chaîne de parents hérite de l'exception.

Utilisez plutôt une approbation humaine à la limite de la session. La carte d'approbation doit afficher l'autorité de signature du processus lorsqu'il en possède une, mais un build non signé a besoin d'autres informations à côté de ce champ : chemin de l'exécutable, commande parent, répertoire de travail lorsqu'il est disponible et cible qu'il souhaite atteindre. Le développeur peut alors répondre à une vraie question : « Est-ce bien le client que je viens de compiler pour cette tâche ? »

L'autorisation par session de Sallyport convient à ce parcours, car elle demande une approbation pour un nouveau processus d'agent et la conserve uniquement jusqu'à la fin du processus. L'unité de décision est l'identité du processus, pas une catégorie vague de logiciels non signés.

## Un chemin familier est un élément de preuve, pas une identité

Un chemin comme `~/src/agent-client/dist/agent` semble rassurant parce qu'il raconte une histoire plausible. Ce n'est pas une frontière d'identité. Un processus malveillant peut s'exécuter depuis ce chemin s'il peut y écrire, remplacer une sortie, modifier un lien symbolique ou convaincre un lanceur de résoudre un autre exécutable.

Commencez par rendre l'histoire légitime simple et répétable. Chaque développeur devrait posséder un emplacement de dépôt habituel. Les sorties de build doivent rester dans ce dépôt ou dans un répertoire prévisible accessible en écriture uniquement par le compte du développeur. Évitez les répertoires de build partagés, les téléchargements, les répertoires temporaires, les dossiers synchronisés et les racines de projet où les scripts de paquets réécrivent régulièrement les exécutables.

Un contrat de lancement pratique peut être aussi court que celui-ci :

```sh
#!/bin/zsh
set -eu

repo="$HOME/src/agent-client"
cd "$repo"

git status --short
git rev-parse --short HEAD
exec ./build/agent-client --mcp
```

L'intérêt ne réside pas dans la syntaxe shell. Il réside dans les éléments de preuve laissés derrière. Le shell parent possède un chemin de script connu, le répertoire de travail pointe vers un dépôt, la révision Git s'affiche avant le démarrage du client et `exec` remplace le shell par le programme attendu au lieu de laisser une chaîne de processus difficile à interpréter.

Ne laissez pas ce script télécharger une version, choisir une branche depuis une variable d'environnement, exécuter un hook de gestionnaire de paquets ou consulter un fichier de configuration modifiable situé hors du dépôt. Ces facilités rendent le contrat de lancement moins utile, car la personne qui examine l'approbation ne peut plus déterminer ce que le script a réellement sélectionné.

Si le client de l'agent a besoin de code généré, générez-le dans le cadre de la commande de build explicite. S'il a besoin d'un fichier de configuration local, indiquez visiblement son chemin et ne le gardez hors du dépôt que lorsqu'il contient des paramètres propres à la machine. N'y dissimulez pas d'identifiants. La passerelle existe précisément pour que le client n'en ait pas besoin.

## Examinez le processus avant d'approuver la session

Une bonne décision d'approbation prend quelques secondes, mais elle ne devrait pas reposer sur la mémoire. Lorsqu'un nouveau processus local demande à appeler une API ou à ouvrir une connexion SSH, examinez son exécutable et sa chaîne de parents avant de cliquer sur « Approuver ».

Sur macOS, ces commandes fournissent un premier examen utile. Remplacez le PID d'exemple par celui affiché par votre outil de visualisation des processus ou votre terminal :

```sh
pid=48271

ps -o pid=,ppid=,user=,lstart=,command= -p "$pid"
ps -o pid=,ppid=,command= -p "$(ps -o ppid= -p "$pid" | tr -d ' ')"
lsof -a -p "$pid" -d cwd -Fn
```

La sortie devrait avoir cette forme :

```text
48271 47990 alex Tue Jul 22 10:14:03 2026 /Users/alex/src/agent-client/build/agent-client --mcp
47990 23112 /bin/zsh /Users/alex/src/agent-client/scripts/run-local-agent
n/Users/alex/src/agent-client
```

Vous vérifiez une chaîne, vous ne collectionnez pas des détails isolés. Le binaire doit se trouver dans l'emplacement de build attendu. Le parent doit être votre lanceur connu. Le répertoire actuel doit correspondre au dépôt. Un processus provenant de `/private/var/folders`, de `~/Downloads`, d'un cache de paquets inconnu ou d'un script intermédiaire inattendu échoue à l'examen, même si son nom de commande semble correct.

Examinez ensuite l'exécutable lui-même :

```sh
client="$HOME/src/agent-client/build/agent-client"
file "$client"
codesign --display --verbose=4 "$client" 2>&1 | sed -n '1,18p'
shasum -a 256 "$client"
```

Un binaire non signé peut amener `codesign` à signaler qu'il n'est pas signé du tout. Un binaire signé ad hoc indique une signature sans autorité publique de signature. Cette information reste utile, mais ne l'interprétez pas comme une identité d'équipe. Apple décrit la signature ad hoc comme « Sign to Run Locally » et explique que son exigence désignée est liée à cette version précise du code. Une recompilation modifie les éléments de preuve, ce qui explique précisément pourquoi l'approbation de session ne doit pas survivre au remplacement du processus.

Le hachage est un outil de comparaison, pas un signal de confiance. Il aide lorsque deux développeurs doivent établir qu'ils ont exécuté la même sortie issue de la même révision. Il ne rend pas un binaire sûr parce que 64 caractères hexadécimaux apparaissent à côté de lui.

Un développeur doit refuser la demande si un élément quelconque de la chaîne est surprenant. N'approuvez pas d'abord pour enquêter après l'appel API. L'approbation est le moment où l'incertitude doit coûter quelques minutes, pas une analyse d'incident.

## Utilisez la session comme frontière entre deux builds

La limite de session résout un problème que les listes d'autorisation par chemin ne peuvent pas résoudre : le code local change constamment. Un développeur peut recompiler le client dix fois en une heure. Chaque sortie peut avoir un graphe de dépendances différent, une gestion différente des commandes ou une branche de débogage temporaire qui envoie des requêtes vers une destination inattendue.

Rendez le processus de lancement jetable. Démarrez le client de l'agent pour une tâche, approuvez ce processus après examen et laissez l'approbation expirer lorsqu'il se termine. Lorsque le développeur recompile, change de branche, modifie le script intermédiaire ou redémarre le client, il obtient un nouveau processus et une nouvelle décision.

Cela ne semble gênant que lorsque la session est mal définie. Si un client d'agent démarre et s'arrête pour chaque appel d'outil, corrigez son cycle de vie ou utilisez un superviseur local conçu à cet effet, qui reste transparent dans l'arbre des processus. Ne résolvez pas cette multiplication des demandes en rendant l'approbation permanente. Un processus de courte durée est censé le rester.

Le processus suivant fonctionne bien pour un développeur qui exécute un agent de programmation compilé localement contre un environnement de test :

1. Compilez depuis le dépôt et affichez la révision ainsi que les éventuelles modifications non validées.
2. Lancez l'agent avec le script intermédiaire versionné dans le dépôt.
3. Lors de la première demande d'autorisation, vérifiez le chemin affiché du processus, la commande parent et le service cible.
4. Approuvez la session si ces éléments correspondent à la tâche.
5. Quittez le client à la fin de la tâche, puis recompilez ou relancez-le pour la tâche distincte suivante.

Ce processus donne aux développeurs une voie de récupération claire. Si un build semble douteux, arrêtez-le. Il n'y a aucune autorisation cachée à démêler ni aucun fichier de règles qui aurait accumulé silencieusement des exceptions pour la moitié de l'équipe.

Ne confondez pas l'approbation d'une session avec l'approbation d'un dépôt. Un dépôt peut être propre alors que la commande de lancement est incorrecte. Une commande de lancement peut être correcte alors que la branche est expérimentale. Une décision de session signifie que ce processus, dans ce contexte, peut effectuer les appels ordinaires de l'exécution en cours.

## Réservez l'approbation par appel aux conséquences irréversibles

L'approbation de session est le bon choix par défaut pour le trafic de développement répétable : lire des métadonnées d'incidents, interroger une API de bac à sable, récupérer un dépôt via SSH ou mettre à jour un enregistrement de test jetable. Exiger un geste humain pour chacun de ces appels apprend aux gens à approuver sans lire.

Certains identifiants doivent néanmoins nécessiter une approbation à chaque utilisation. Choisissez-les selon la conséquence de l'action distante, et non selon la mise en scène autour de la sensibilité du secret lui-même.

Utilisez une approbation par appel pour les identifiants qui peuvent :

- écrire dans un environnement de production ;
- publier un paquet, une version ou un artefact de déploiement ;
- accéder à une exportation de données client ou à un autre ensemble concentré de données sensibles ;
- modifier l'appartenance à l'organisation, les paramètres d'authentification ou les mécanismes de récupération ;
- atteindre un hôte SSH de production.

Ne marquez pas ainsi chaque jeton d'API de développement. Vous créeriez une fatigue d'approbation, et cette fatigue transforme un examinateur attentif en personne qui clique machinalement. La passerelle doit rendre les moments dangereux suffisamment distincts pour qu'ils soient remarqués.

Le détail de la demande compte autant que la seconde confirmation. Pour HTTP, affichez la méthode et la destination, puis une partie suffisante du chemin pour identifier l'action sans déverser le contenu sensible de la requête dans l'écran d'approbation. `GET /v1/test-runs/123` et `DELETE /v1/projects/123` ne devraient jamais sembler interchangeables. Pour SSH, affichez l'hôte et le compte et demandez au développeur de confirmer pourquoi cet hôte appartient à la tâche en cours.

L'exigence par appel constitue aussi un frein utile face à un client modifié localement. Vous pouvez accepter une session pour un nouveau build expérimental qui lit des données dans un service de préproduction. Vous devriez toutefois hésiter avant d'autoriser ce même build à publier une version de production. Cette hésitation est précisément le but.

## Considérez les recompilations, changements de branche et scripts intermédiaires comme de nouveaux éléments de preuve

Les développeurs prennent souvent une décision d'approbation le matin, puis changent les faits toute la journée. Ils récupèrent une branche, exécutent un générateur de code, mettent à jour des dépendances, modifient un fichier de consignes, ajoutent un alias shell ou remplacent un script intermédiaire. Le binaire peut conserver le même nom et le même chemin alors que son comportement a changé de manière importante.

Adoptez une règle simple : si l'exécutable, son lanceur ou son environnement prévu change, terminez la session et relancez l'agent. Il n'est pas nécessaire d'organiser une cérémonie pour chaque modification. Il faut éviter de transporter le contexte d'hier dans un nouveau build.

Trois événements doivent déclencher automatiquement une pause dans votre processus :

- Vous avez changé de branche, de commit, de fichier de verrouillage des dépendances ou de sortie générée.
- Vous avez modifié le script qui lance le client ou les variables d'environnement qui influencent son comportement.
- L'agent veut désormais utiliser un autre hôte API, un autre hôte SSH ou un identifiant aux conséquences plus larges.

Un changement de branche est souvent sous-estimé parce que le nom du binaire reste stable. Pourtant, une branche de fonctionnalité peut contenir une intégration expérimentale, un manifeste d'outils modifié ou un point de terminaison de débogage. La bonne réponse n'est pas d'interdire les branches. Il faut rendre visible et examinable la première requête de la nouvelle exécution.

Les variables d'environnement méritent la même méfiance que les arguments de l'exécutable. Un client démarré avec `API_BASE_URL`, `SSH_AUTH_SOCK`, `PATH`, `DYLD_*` ou un emplacement de configuration personnalisé peut se comporter différemment du code source que vous avez examiné. Votre script intermédiaire ne doit définir que les variables nécessaires, afficher les valeurs non secrètes qui influencent le routage et refuser les valeurs manquantes au lieu de charger un profil shell volumineux.

Par exemple, ceci est facile à examiner :

```sh
export AGENT_ENVIRONMENT=staging
export AGENT_API_ORIGIN=https://staging.example.internal
exec ./build/agent-client --mcp
```

Ceci ne l'est pas :

```sh
source "$HOME/.agent-env"
eval "$(tooling configure-agent)"
exec "$AGENT_BIN" "$@"
```

La seconde forme peut être pratique, mais elle dissimule l'exécutable, la source de configuration, les arguments et les effets de bord. Elle oblige la personne qui approuve le processus à faire confiance à un ensemble d'indirections. Le développement local comporte déjà suffisamment de pièces mobiles.

## Gardez les identifiants hors du client, même lorsque le client vous appartient

Un client compilé localement est plus facile à approuver lorsqu'il ne reçoit jamais l'identifiant qu'il souhaite utiliser. Si le client lit un jeton API dans une variable d'environnement ou une clé SSH dans un fichier, il peut l'afficher, le transmettre, le mettre en cache, l'inclure dans un rapport d'erreur ou le remettre à un sous-processus. La confiance du développeur dans son propre build ne change rien à cette exposition.

Placez le secret dans la passerelle d'action et laissez le client demander une action par référence. Le client doit fournir le contexte de la requête HTTP ou de la commande SSH nécessaire à l'opération. La passerelle injecte l'identifiant, exécute l'appel et renvoie le résultat. La décision d'approbation porte ainsi sur l'action, et non sur l'autorisation donnée au client de conserver indéfiniment un jeton porteur.

Pour HTTP, prévoyez des identifiants distincts pour les environnements et les usages distincts. Un jeton de préproduction ne doit pas atteindre la production simplement parce que le client a fourni un autre hôte. Pour SSH, utilisez des entrées d'hôte ou des fiches d'identifiant distinctes selon les rôles. Ne comptez pas sur une clé unique toute-puissante et sur la promesse d'une personne de sélectionner la bonne cible.

Sallyport conserve les identifiants API et SSH dans un coffre chiffré et exécute lui-même l'action, de sorte qu'un agent reçoit les résultats plutôt que les secrets en clair. Cette conception est particulièrement importante pour les builds locaux, où les modifications du code sont attendues et où une variable d'environnement divulguée n'est autrement qu'à une impression de débogage de distance.

Cette séparation améliore aussi la réponse aux incidents. Si un client se comporte de manière étrange, vous pouvez arrêter ou révoquer sa session sans renouveler tous les secrets qu'il aurait pu charger en mémoire. La protection du coffre reste absolue lorsqu'il est verrouillé, et un coffre verrouillé refuse les actions au lieu d'essayer de deviner l'intention du développeur.

## La piste d'audit doit résoudre les désaccords, pas seulement accumuler des événements

Lorsqu'un processus d'approbation de build local fonctionne, quelqu'un demandera parfois pourquoi un agent a accédé à un service ou si un processus tournait encore après un transfert. La réponse doit venir d'une piste d'événements, pas de suppositions, de l'historique du terminal ou d'un message écrit après coup.

Consignez deux vues de la même activité sous-jacente. L'une doit afficher les exécutions d'agents et permettre une révocation immédiate. L'autre doit afficher les actions HTTP et SSH individuelles. Reliez les éléments pour qu'un examinateur puisse passer d'une demande suspecte à la session qui l'a autorisée, puis au contexte du processus à l'origine de la décision.

Un journal rendant toute altération détectable est particulièrement utile ici, car les builds locaux peuvent être modifiés par les mêmes personnes qui les examinent. Sallyport produit ses journaux Sessions et Activity à partir d'un journal d'audit chiffré unique, chaîné par hachage, et `sp audit verify` vérifie cette chaîne hors ligne sur le texte chiffré sans nécessiter de clé du coffre. L'équipe dispose ainsi d'un contrôle d'intégrité même lorsque l'examinateur ne doit pas recevoir l'accès aux secrets situés derrière les actions.

Effectuez la vérification dans le cadre d'une revue ou d'un exercice d'incident :

```sh
sp audit verify
```

Le résultat attendu est une confirmation claire ou un rapport identifiant un problème dans la chaîne. Traitez un échec de vérification comme un problème opérationnel jusqu'à ce que vous le compreniez. Ne supprimez pas le journal, ne réinstallez pas l'application et n'acceptez pas une nouvelle base de référence avant d'avoir préservé les enregistrements concernés.

Le journal ne remplace pas l'examen au moment de l'approbation. Il permet de vérifier que votre processus produit des éléments réellement exploitables. Si le journal indique seulement qu'« un client non signé » a effectué un appel, améliorez votre contrat de lancement et le contexte présenté lors de l'approbation. S'il affiche le processus, la session, la destination, l'heure et l'action, l'équipe peut enquêter sans transformer l'ordinateur de chaque développeur en chantier de criminalistique.

## Les conventions d'équipe empêchent les demandes d'approbation de devenir du bruit de fond

La difficulté principale d'un processus d'approbation de build local ne se trouve pas dans la ligne de commande. Elle consiste à préserver le sens humain d'une approbation après des mois de travail normal.

Rédigez une petite convention pour les agents locaux et gardez-la près du dépôt, plutôt que de l'enfouir dans un manuel de sécurité. Elle doit préciser où se trouvent les dépôts pris en charge, à quoi ressemblent les scripts de lancement, quels environnements comptent comme développement ordinaire, quels identifiants nécessitent une approbation par appel et ce que doivent faire les développeurs lorsque la demande affiche un processus inattendu.

Faites du refus une étape normale. Un développeur qui clique sur « Refuser » parce qu'un processus parent semble étrange ne doit pas avoir le sentiment d'avoir bloqué le travail. Il doit arrêter le processus, l'examiner, le relancer avec le script intermédiaire connu et approuver l'exécution propre. C'est plus rapide que de normaliser un processus mystérieux et d'essayer de l'expliquer plus tard.

Réexaminez la convention après une vraie surprise : un script de paquet a réécrit une sortie, un IDE a lancé un assistant inattendu, une branche pointait vers le mauvais environnement ou un agent a demandé un identifiant sans rapport avec sa tâche. Ces échecs sont utiles, car ils révèlent les éléments manquants. Ne réagissez pas en ajoutant une règle d'autorisation permanente. Renforcez le contrat de lancement, améliorez les informations affichées lors de l'approbation ou réduisez la portée de l'identifiant.

Un développeur qui compile son propre agent ne devrait pas avoir à choisir entre la mise en scène de la sécurité et les interruptions constantes. Donnez-lui une approbation de session propre à un processus visible, gardez les appels à conséquences importantes derrière une décision distincte et exigez un nouvel examen chaque fois que l'histoire du build local change. C'est assez strict pour repérer le mauvais processus et assez pratique pour que les équipes continuent à l'utiliser.
