Agent Control Standard (ACS)
Contrôle signé et fail-closed des cycles de vie des agents Scrydon.
Scrydon implémente le contrat de fil (wire contract) v0.1.0 de l'Agent Control
Standard (ACS). ACS distingue un Agent Observé (Observed Agent), qui
signale une étape avant d'agir, d'un Agent Gardien (Guardian Agent), qui
répond allow, deny, modify, ask ou defer.
Dans le déploiement par défaut, Agentic est l'Agent Observé et API Platform est le Gardien. Leur trafic est scopé par organisation, signé, protégé contre le rejeu, et persisté via Dapr, de sorte qu'un workflow peut reprendre sur une autre réplique sans démarrer une seconde session de contrôle.
Profils de conformité actifs
Les profils sont négociés pour chaque session. Scrydon ne déclare un profil que lorsque la composition en cours dispose des méthodes, algorithmes et sinks requis.
| Profil | Agentic (Agent Observé) | API Platform (Gardien) |
|---|---|---|
acs-core | Non revendiqué par la session lifecycle-only | Actif |
acs-audit | Actif | Actif |
acs-inspect | Non émis | Non activé par défaut ; nécessite une surface d'inventaire AgBOM composée et activement maintenue (la projection canonique, pas un export CycloneDX — aucun n'existe) |
acs-inspect-dynamic | Non émis | Non activé par défaut ; nécessite aussi la surface de changement en direct |
acs-provenance | Non déclaré | Actif par défaut ; accepté uniquement depuis des producteurs déterministes |
acs-trace | Non activé par défaut | Déclaré uniquement lorsqu'un sink OTel/OCSF est installé ; il n'existe pas de composant Dapr acs-trace |
acs-crypto | Non déclaré ; la référence HMAC-SHA256 est utilisée | Non déclaré ; la référence HMAC-SHA256 est utilisée |
Le package ACS contient des primitives Trace, AgBOM canonique et provenance
pour les compositions qui installent ces capacités. Il ne fournit qu'une seule
implémentation de signature, HMAC-SHA256, donc acs-crypto est refusé à la
construction de la politique plutôt qu'offert. Le support du package seul
n'est pas une revendication de conformité au niveau du déploiement.
Cycle de vie Agentic gouverné
Une exécution de bloc Agent correspond à une session ACS durable. Agentic émet aujourd'hui ces méthodes signées :
| Méthode | Point de contrôle |
|---|---|
handshake/hello | Négocie les méthodes, profils, transport et posture d'échec exacts |
steps/sessionStart | Ouvre la session scopée à l'exécution |
steps/agentTrigger | Enregistre comment l'agent a été activé |
steps/turnStart | Contrôle chaque tour fournisseur/boucle d'outils avant qu'il ne s'exécute |
steps/agentResponse | Contrôle la sortie du tour terminé avant que le workflow ne la reçoive |
steps/turnEnd | Enregistre le résultat terminal du tour |
steps/sessionEnd | Ferme la session et valide la tête de chaîne finale |
La session lifecycle ne revendique délibérément pas acs-core : Core exige des
hooks de requête et de résultat d'outil, alors que Scrydon gouverne ces
opérations dans la session d'exécution séparée d'API Platform. Sa liste
methods_implemented ne contient donc que les méthodes que cette session peut
réellement émettre. Le Gardien exige toujours acs-audit, donc le cycle de vie
reste signé, chaîné et fail-closed.
Les opérations modèle, récupération, mémoire et outils de la Platform sont également gouvernées au point de passage obligé (chokepoint) de l'extension-runtime d'API Platform. Ce chemin utilise le même instantané de guardrails d'organisation ordonné et le même vocabulaire d'étapes ACS, sans ajouter une seconde passe de politique : le hook ACS in-process enregistre le verdict déjà atteint par le pipeline de guardrails du kernel dans la chaîne de session durable — il ne réévalue pas une seconde fois. L'ingress Gardien pour les agents que Scrydon n'héberge pas (voir plus bas) résout ce même instantané de guardrails, mais l'évalue via sa propre couche de politique déterministe, et non via le runner du kernel.
Posture d'échec et de rejeu
Scrydon négocie on_decision_failure: deny, et cette posture est un
plancher que la contrepartie ne peut pas abaisser : le Gardien renvoie
toujours sa propre posture configurée dans le ServerHello négocié, quelle
que soit la demande du client — un pair ne peut donc pas négocier proceed.
Un délai dépassé, une signature invalide, une réponse malformée, un sidecar
Dapr manquant, ou une écriture d'état non confirmée arrêtent donc l'action.
Un résultat unguarded est enregistré distinctement et n'est jamais traité
comme une permission.
Chaque requête possède une identité unique, un horodatage et un nonce. Le Gardien rejette les identifiants de requête dupliqués, les nonces dupliqués, les requêtes hors de la fenêtre de tolérance (skew) négociée, et les têtes de chaîne client qui divergent de son état de session durable.
Les points de contrôle utilisent des lectures fortes et une concurrence
optimiste, mais Agentic et le Gardien ne partagent pas le même espace de clés.
L'état de cycle de vie propre à Agentic reste dans le store Dapr
exec-security-state. L'état de session du Gardien — le handshake négocié, la garde anti-rejeu, et la chaîne d'audit à partir de laquelle ses verdicts sont calculés — vit dans son propre composant, acs-guardian-state, réservé à API Platform seule et scellé au repos en AES-256-GCM sous une clé que seule API Platform détient : la charge de travail qu'un Gardien juge ne doit pas pouvoir forger les lignes qui décident de ses propres verdicts. Dans le chart livré, ce store réside encore dans la base de données d'Agentic ; un rôle de base de données dédié est suivi comme travail à venir, et d'ici là une session supprimée au niveau SQL échoue de manière fermée plutôt que d'être rejouée. Si la livraison réussit mais que le client ne peut pas confirmer son point de contrôle, la session échoue de manière fermée plutôt que de rejouer une requête dont l'issue est incertaine.
Signatures
Le déploiement par défaut dérive une clé de session HMAC-SHA256 distincte à
partir du secret partagé ACS_GUARDIAN_HMAC_SECRET, de l'organisation, du
workflow et des identifiants de session. Les requêtes et réponses sont signées
sur l'enveloppe canonique ; le secret racine ne transite jamais sur le fil.
Comme chaque appelant s'authentifie avec ce même secret partagé au niveau du déploiement, cette frontière de contrôle fonctionne uniquement pour les agents hébergés par Scrydon — un agent que votre organisation exploite en dehors du déploiement Scrydon ne peut pas détenir en toute sécurité la même clé racine partagée. Étendre le contrôle signé à un agent hébergé de manière indépendante par le client nécessite une signature asymétrique, que ce déploiement n'offre pas encore.
Configurez une seule clé (ACS_GUARDIAN_HMAC_SECRET, au moins 32 caractères)
pour API Platform et Agentic. Le chart Helm génère et préserve une clé à la
première installation. Sous global.requireExplicitSecrets: true — le mode
pour le rendu GitOps/sans lookup (helm template, ArgoCD, manifestes air-gap
hors ligne), où un repli de type « génère au rendu » ferait tourner le secret
en silence à chaque synchronisation — le chart échoue le rendu à la place et
nomme la valeur que l'opérateur doit définir explicitement :
auth.secrets.ACS_GUARDIAN_HMAC_SECRET. La même règle s'applique à la clé de
scellement propre au store du Gardien, auth.secrets.ACS_GUARDIAN_STATE_ENCRYPTION_KEY.
Variables d'environnement
| Variable | S'applique à | Signification |
|---|---|---|
ACS_REQUIRED | API Platform, Agentic | Indique si un processus incapable de joindre le Gardien peut démarrer. Le chart Helm rend true dans tous les environnements (auth.acs.required) ; un processus brut sans indicateur ne vaut true que lorsque NODE_ENV=production. Désactivé, une entrée manquante désactive ACS avec un avertissement qui la nomme, et les jauges scrydon_acs_guardian_enabled / scrydon_acs_observed_agent_enabled valent 0. |
ACS_GUARDIAN_HMAC_SECRET | API Platform, Agentic | Clé HMAC racine. Requise tant que ACS_REQUIRED est actif — le démarrage lève une exception et le processus ne démarre pas sans elle, sans un sidecar Dapr joignable, et (API Platform uniquement) sans la clé de scellement ci-dessous. |
ACS_GUARDIAN_MAX_CONCURRENT | API Platform | Plafond global d'évaluations d'ingress ACS signées que ce pod exécute simultanément, tous tenants confondus. Défaut 64. Le plafond par organisation (16) n'est pas un réglage par variable d'environnement — il existe spécifiquement pour qu'un tenant ne puisse pas consommer tout le budget. |
ACS_VERDICT_AUDIT | API Platform | Quels verdicts ACS deviennent des lignes du journal d'audit : all (défaut), denials, ou off. Voir Audit et observabilité. |
ACS_GUARDIAN_STATE_ENCRYPTION_KEY | API Platform | Clé AES-256-GCM qui scelle les valeurs du store acs-guardian-state du Gardien au repos. Détenue uniquement par API Platform — délibérément distincte de la clé EXEC_SECURITY_STATE_ENCRYPTION_KEY qu'Agentic détient aussi, puisqu'Agentic exécute les agents que ces lignes jugent. Requise tant que ACS_REQUIRED est actif ; il n'existe pas de repli « fonctionner sans scellement ». Valeur base64url non rembourrée de 32 octets (43 caractères) ; le chart en génère et en conserve une. |
Audit et observabilité
Les requêtes ACS porteuses de contenu font avancer une chaîne inviolable par
session. La chaîne s'engage sur les paramètres canoniques de la requête, y
compris le condensé précédent sous forme d'octets bruts, comme l'exige ACS.
turnEnd et sessionEnd sont des faits d'audit et ne peuvent pas opposer
rétroactivement leur veto à un travail déjà terminé.
L'extension runtime expose un unique plan d'effets (effects plane) avec des contrats de durabilité distincts :
- les projections d'audit et de facturation restent bloquantes ;
- ACS Trace est best-effort et ne peut ni changer ni retarder un verdict ;
- les observations de décision contiennent des métadonnées de corrélation et de politique, jamais le contenu modifié ni les charges utiles privées à la politique.
ACS_VERDICT_AUDIT (défaut all) sélectionne quels verdicts deviennent des
lignes du journal d'audit :
- une ligne de gouvernance (
acs.decision.denied), écrite immédiatement pour chaque verdict refusant l'action (deny, ask, defer), portant le Detection Finding OCSF ; - une ligne de télémétrie par exécution (
acs.decision.recorded), écrite à la fin de l'exécution, contenant la liste complète des décisions de cette exécution — y compris les étapes autorisées. Une réponse en streaming produit un verdict par fenêtre gouvernée et par chunk gouverné ; enregistrer chacun comme sa propre ligne aurait multiplié le volume d'audit d'un appel gouverné par la longueur de sa réponse, d'où cette agrégation. denialsn'écrit que la ligne de gouvernance ;offn'écrit aucune des deux — les spans continuent de tracer.
Il n'existe pas de redirecteur OCSF-vers-SIEM séparé : la ligne de gouvernance
emprunte la livraison SIEM existante du journal d'audit, signée par HMAC,
donc un Log Receiver déjà configuré pour le journal d'audit récupère les
refus ACS sans configuration supplémentaire. Il n'existe pas de composant
Dapr acs-trace.
Lorsqu'un sink Trace est installé, chaque étape gouvernée utilise le nom de
span OpenTelemetry ACS, son verdict est attaché comme un événement de span
acs.decision, et la même étape/décision est émise comme un événement OCSF
idempotent. Sans sink actif, le handshake ne revendique pas acs-trace.
Streaming
Un intercepteur en mode enforce qui déclare une fenêtre gouvernable
(streamWindow) évalue et libère une réponse en streaming par fenêtres,
de sorte qu'elle atteint l'appelant au fur et à mesure qu'elle est produite.
Cela ne tient que lorsque chaque intercepteur enforçant sur ce flux
déclare une fenêtre : si un seul ne le fait pas, tout le flux est retenu et
évalué comme un seul bloc avant que le premier octet ne soit libéré — c'est
la conception du streaming par fenêtres du pipeline de guardrails, pas une
panne. Les intercepteurs audit-only et les intercepteurs à fenêtre observent
chaque chunk et chaque fenêtre gouvernés au fil de l'eau.
Quel que soit le mode applicable, la chaîne de session ACS elle-même n'est pas multipliée par la longueur du flux : la chaîne enregistre une seule étape de preuve agrégée pour l'opération gouvernée, construite à partir des mêmes fenêtres et chunks observés par le pipeline de guardrails, plutôt qu'une entrée de chaîne par jeton.
Scrydon en tant que Gardien
Des identités de charge de travail authentifiées, scopées par organisation,
peuvent soumettre des enveloppes JSON-RPC ACS signées officielles via
l'opération Platform agent.control.evaluate. Son scope, agent-control:write,
est le seul scope Platform qu'une session utilisateur ou une clé API ne
satisfait jamais : seule une charge de travail de service que le registre
interne y autorise (Agentic aujourd'hui) peut atteindre le Gardien ou occuper
son budget d'admission. API Platform résout la politique DLP, de modération
et de classification courante de l'organisation pour chaque requête ; elle ne
met jamais en cache la politique d'un tenant pour un autre. L'ingress ACS
signé est plafonné par organisation (16 requêtes concurrentes, 64 au global,
une attente maximale de 2 secondes) ; une organisation saturée reçoit un
HTTP 429 avec reason: rate_limited plutôt que d'affamer le trafic agent de
tous les autres tenants.
L'authentification et l'attribution répondent à deux questions différentes.
Le Gardien authentifie la charge de travail appelante via l'identité de
transport propre à la gateway Platform — jamais une valeur lue dans le corps
de la requête — et attribue l'étape à un agent à partir du
metadata.agent_id de l'enveloppe signée. Une enveloppe bien formée nomme
l'agent workflow réellement observé ; une enveloppe malformée retombe sur
l'identité de la charge de travail authentifiée, de sorte que le Gardien
renvoie le INVALID_REQUEST propre au protocole plutôt que de faire confiance
à une revendication non vérifiée.
Le Gardien par défaut prend en charge la validation déterministe de la
provenance, les cinq dispositions, la protection contre le rejeu, et la
chaîne d'audit du contenu de la requête. Le kernel ACS peut valider des
instantanés et changements AgBOM canoniques, mais le Gardien ne revendique
Inspect qu'une fois ces éléments composés en une surface d'inventaire active
— le déploiement livré ne la compose pas aujourd'hui. acs-crypto n'est pas
non plus offert par la couche enveloppe ACS : ce package ne fournit qu'une
seule implémentation de signature fonctionnelle, HMAC-SHA256. Scrydon signe
séparément ses points de contrôle (checkpoints) du journal d'audit Better
Auth avec ML-DSA-65, mais il s'agit d'un système différent avec une chaîne
d'audit différente, et cela ne rend pas vraie la revendication acs-crypto
de cette couche enveloppe. Un état de politique malformé ou indisponible
échoue de manière fermée.
Visibilité pour les administrateurs
Les administrateurs d'organisation accèdent à ce contrôle via Paramètres →
Gouvernance → Agent Control. Le panneau expose la seule limite ACS
réellement configurable par un administrateur — maxAgentTurnsPerExecution,
le plafond de tours par exécution du Gardien — ainsi qu'un texte en lecture
seule décrivant ce que le plan de contrôle applique déjà automatiquement. Ce
n'est pas une sonde de handshake ou de posture en direct : le reste d'ACS
(audit, DLP, protection contre le rejeu, Trace) est soit appliqué sans bascule
de configuration, soit pas encore composé dans ce déploiement — et le panneau
le dit, plutôt que d'afficher un badge de statut qui ne reposerait sur rien.
Frontières actuelles
Le déploiement par défaut n'envoie pas les hooks Agentic de Scrydon à un
Gardien externe exploité par le client, n'émet pas les hooks de cycle de vie
de compaction, de sous-agent, de compétence (skill) ou d'AgBOM d'Agentic, et
ne revendique pas acs-inspect, acs-inspect-dynamic, ni acs-crypto. Ces
capacités doivent être composées et négociées avant de pouvoir être
revendiquées.