Governare un agente esistente con una policy
Simula, rivedi, pubblica e collega una policy di pagamento fail-closed a un Agent che esiste già nel KLA Control Plane.
Questa guida aggiunge una policy revisionata a un Agent che esiste già nel KLA Control Plane. La policy consente chiamate payments.create fino a 10.000 EUR, instrada i pagamenti superiori al Decision Desk e blocca ogni richiesta di pagamento che non corrisponde a nessuna delle due regole. La versione pubblicata è limitata a questo solo Agent, quindi gli altri Agent di produzione del tenant non sono interessati.
Una bozza limitata a un Agent può essere inviata in revisione solo dopo che l'Agent ha un ambiente governato, e un Agent riceve un ambiente governato solo tramite un collegamento di policy distribuito. La sequenza prevede quindi due versioni della policy: la versione 1.0.0 viene revisionata e collegata per dare all'Agent il suo ambiente e non viene mai pubblicata; la versione 1.1.0 nomina l'Agent nel suo ambito, viene pubblicata e viene collegata come policy applicata.
Ogni comando è pronto da copiare. I comandi creano record nel tenant selezionato, quindi usa un tenant che controlli.
Prima di iniziare
Ti servono:
- la CLI
klacompilata dal repository come descritto nella guida all'installazione; jq;- un Agent esistente il cui strumento di pagamento si chiama
payments.createe riceve unamountnumerico e unacurrencydi tipo stringa; - un token di accesso maker di un membro che può creare, simulare e pubblicare policy e configurare e distribuire Agent;
- un token di accesso checker di un membro diverso che ha il ruolo
kla:approver, che revisiona le policy, decide le Decision Request e approva le Release di Agent.
La CLI legge KLA_ACCESS_TOKEN. Il token maker resta esportato per tutta la guida. Ogni comando checker riceve il token checker solo per quel comando. Tieni entrambi i token fuori da file e argomenti di comando.
export KLA_API_URL='https://api.dev.kla.digital'
export KLA_TENANT_ID='<tenant external ID>'
export KLA_ACCESS_TOKEN='<maker short-lived access token>'
export KLA_CHECKER_ACCESS_TOKEN='<checker short-lived access token>'
KLA_REPO_ROOT="$(git rev-parse --show-toplevel)"
kla() { node "$KLA_REPO_ROOT/cli/dist/index.js" "$@"; }
kla auth status > maker-auth.json
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" kla auth status > checker-auth.json
jq -e '.token.subject | type == "string"' maker-auth.json
jq -e '.token.subject | type == "string"' checker-auth.json
jq -en --slurpfile maker maker-auth.json --slurpfile checker checker-auth.json \
'$maker[0].token.subject != $checker[0].token.subject'
jq -e '.token.roles | index("kla:approver") != null' checker-auth.json
kla api tenants getSettings | jq -e '.governance.publishApprovalMode == "two_person"'
kla auth status stampa i claim non segreti di ogni token. I due soggetti devono essere diversi: l'API rifiuta una revisione o un'approvazione da parte dell'identità che ha effettuato la richiesta. La modalità di pubblicazione del tenant deve essere two_person; in modalità single_actor il passo di pubblicazione registra la propria approvazione e il passo 7 non ha alcuna Decision Request da decidere.
Passo 1: Identificare l'Agent
Cerca nell'Agent Registry, copia l'UUID dal risultato e salva il record dell'Agent. L'Agent non deve avere bozze in sospeso: un collegamento di policy viene unito a una bozza esistente e il checker approverebbe modifiche non correlate insieme al collegamento.
kla agent list --search 'payment'
export AGENT_UUID='<agent UUID>'
kla agent get "$AGENT_UUID" > agent-before.json
jq -e --arg agent "$AGENT_UUID" '.id == $agent' agent-before.json
kla agent release history "$AGENT_UUID" > agent-history-before.json
jq -e '[.versions[] | select(.is_draft == true)] | length == 0' agent-history-before.json
Se esiste una bozza in sospeso, il suo manifest è in quell'output della cronologia. Falla approvare dal checker o scartare dal maker prima di continuare.
La policy seguente nomina lo strumento payments.create e gli argomenti amount e currency. Se il tuo Agent usa un altro nome di strumento o di argomento, modifica quei valori nella policy e in entrambi gli input di Simulazione.
Passo 2: Scrivere la policy fail-closed
Salva questo file come policy.template.json. La versione 1.0.0 è limitata all'ambiente governato production ed è la versione di bootstrap. Il passo 6 restringe l'ambito all'Agent per la versione 1.1.0.
{
"schemaVersion": "1.0.0",
"policyId": "existing-agent-payment-controls",
"workspaceId": "TENANT_EXTERNAL_ID",
"name": "Existing agent payment controls",
"description": "Allows EUR payments of 10,000 or less and routes larger EUR payments to review.",
"status": "draft",
"version": "1.0.0",
"scope": {
"environments": ["production"]
},
"defaultDecision": "block",
"rules": [
{
"ruleId": "allow-managed-execution-gate",
"name": "Allow the managed Process execution gate",
"interceptionPoint": "input",
"when": {
"expression": {
"==": [{ "var": "context.action.actionType" }, "policy_gate"]
}
},
"then": {
"decision": "allow",
"reason": "The execution may start; each payment tool call is evaluated separately.",
"reasonCodes": ["execution_gate_allowed"]
},
"execution": { "mode": "deterministic" },
"evidence": {
"evaluatedFields": ["context.action.actionType"],
"artifactRefs": []
}
},
{
"ruleId": "allow-payment-up-to-10000",
"name": "Allow positive payment up to EUR 10,000",
"interceptionPoint": "tool_call",
"when": {
"expression": {
"and": [
{ "in": [{ "var": "context.action.actionType" }, ["tool_input", "tool_call"]] },
{ "==": [{ "var": "context.action.toolName" }, "payments.create"] },
{ "==": [{ "var": "context.action.toolArgs.currency" }, "EUR"] },
{ ">": [{ "var": "context.action.toolArgs.amount" }, 0] },
{ "<=": [{ "var": "context.action.toolArgs.amount" }, 10000] }
]
}
},
"then": {
"decision": "allow",
"reason": "Payment is within the approved automatic limit.",
"reasonCodes": ["payment_within_automatic_limit"]
},
"execution": { "mode": "deterministic" },
"evidence": {
"evaluatedFields": [
"context.action.actionType",
"context.action.toolName",
"context.action.toolArgs.currency",
"context.action.toolArgs.amount"
],
"artifactRefs": []
}
},
{
"ruleId": "review-payment-over-10000",
"name": "Review payment over EUR 10,000",
"interceptionPoint": "tool_call",
"when": {
"expression": {
"and": [
{ "in": [{ "var": "context.action.actionType" }, ["tool_input", "tool_call"]] },
{ "==": [{ "var": "context.action.toolName" }, "payments.create"] },
{ "==": [{ "var": "context.action.toolArgs.currency" }, "EUR"] },
{ ">": [{ "var": "context.action.toolArgs.amount" }, 10000] }
]
}
},
"then": {
"decision": "require_approval",
"reason": "Payment exceeds the approved automatic limit.",
"reasonCodes": ["payment_over_automatic_limit"],
"approverGroup": "kla:approver",
"remediation": {
"summary": "Obtain a payment approval before execution.",
"steps": []
}
},
"execution": { "mode": "deterministic" },
"evidence": {
"evaluatedFields": [
"context.action.actionType",
"context.action.toolName",
"context.action.toolArgs.currency",
"context.action.toolArgs.amount"
],
"artifactRefs": []
}
}
]
}
La prima regola consente il gate di esecuzione. Una policy collegata diventa il primo policy_gate del Process gestito dell'Agent, e quel gate viene valutato con actionType policy_gate e l'ID della policy come toolName; senza questa regola il default fail-closed fermerebbe ogni esecuzione prima di qualsiasi chiamata a strumento. Le due regole di pagamento si applicano alle richieste di chiamata a strumento, il cui actionType è tool_input o tool_call, e si escludono a vicenda. Un importo mancante, zero o negativo, un'altra valuta o un altro strumento non corrisponde a nessuna regola e si risolve nella defaultDecision block. La condizione > 0 è importante: un importo mancante viene valutato come 0 nelle espressioni delle regole e altrimenti soddisferebbe <= 10000. approverGroup diventa il ruolo richiesto della Decision Request, quindi nomina il ruolo configurato kla:approver. Il lint delle policy rifiuta una defaultDecision allow con POLICY_DEFAULT_DECISION_FAIL_OPEN.
Inserisci l'ID esterno del tenant e crea la bozza di bootstrap:
jq --arg workspace "$KLA_TENANT_ID" '.workspaceId = $workspace' \
policy.template.json > policy.json
export POLICY_ID='existing-agent-payment-controls'
export BOOTSTRAP_VERSION='1.0.0'
export POLICY_VERSION='1.1.0'
kla policy registry create --file policy.json
kla policy registry get "$POLICY_ID" --version "$BOOTSTRAP_VERSION" \
| jq -e '.versions[0].status == "draft"'
Passo 3: Eseguire due Simulazioni durevoli
Una Simulazione invia alla bozza un GateContext, la busta di richiesta valutata dal KLA Policy Engine. Il Policy Registry memorizza ogni Simulazione completata con un riferimento alle prove, e la prontezza alla revisione richiede una Simulazione memorizzata del contenuto esatto della bozza.
Salva il GateContext di basso valore come simulate-500.template.json:
{
"context": {
"schemaVersion": "1.0.0",
"occurredAt": "2026-09-06T12:00:00Z",
"identifiers": {
"workspaceId": "TENANT_EXTERNAL_ID",
"environment": "production"
},
"agentId": "AGENT_UUID",
"action": {
"actionType": "tool_input",
"toolName": "payments.create",
"toolArgs": { "amount": 500, "currency": "EUR" },
"environment": "production"
}
}
}
Salva il GateContext di alto valore come simulate-15000.template.json:
{
"context": {
"schemaVersion": "1.0.0",
"occurredAt": "2026-09-06T12:00:00Z",
"identifiers": {
"workspaceId": "TENANT_EXTERNAL_ID",
"environment": "production"
},
"agentId": "AGENT_UUID",
"action": {
"actionType": "tool_input",
"toolName": "payments.create",
"toolArgs": { "amount": 15000, "currency": "EUR" },
"environment": "production"
}
}
}
Inserisci gli identificativi del tenant e dell'Agent, poi esegui entrambe le Simulazioni. kla policy registry simulate invia l'header Idempotency-Key richiesto dalla route.
for amount in 500 15000; do
jq --arg workspace "$KLA_TENANT_ID" --arg agent "$AGENT_UUID" \
'.context.identifiers.workspaceId = $workspace | .context.agentId = $agent' \
"simulate-${amount}.template.json" > "simulate-${amount}.json"
done
kla policy registry simulate "$POLICY_ID" "$BOOTSTRAP_VERSION" \
--input @simulate-500.json > simulation-500.json
jq -e '
.decision.decision == "allow" and
.decision.primaryBasis.ruleId == "allow-payment-up-to-10000" and
(.simulation.simulationId | type == "string") and
(.simulation.evidenceId | type == "string")
' simulation-500.json
kla policy registry simulate "$POLICY_ID" "$BOOTSTRAP_VERSION" \
--input @simulate-15000.json > simulation-15000.json
jq -e '
.decision.decision == "require_approval" and
.decision.primaryBasis.ruleId == "review-payment-over-10000" and
.decision.approval.assigneeGroup == "kla:approver" and
(.simulation.simulationId | type == "string") and
(.simulation.evidenceId | type == "string")
' simulation-15000.json
Entrambi i controlli devono passare prima di inviare la bozza. Ogni modifica successiva alla bozza cambia il suo hash del contenuto e richiede una nuova Simulazione.
Passo 4: Inviare e approvare la versione di bootstrap
Il maker invia la versione 1.0.0 e il checker la approva. La versione resta approved e non viene mai pubblicata, quindi non ha mai effetto su alcun gate.
kla policy registry submit-review "$POLICY_ID" "$BOOTSTRAP_VERSION" \
--comment 'Threshold outcomes and fail-closed default verified by Simulation.'
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
kla policy registry review "$POLICY_ID" "$BOOTSTRAP_VERSION" \
--decision approve \
--comment 'Rule scope, outcomes, and Simulation evidence reviewed.' \
--yes
kla policy registry get "$POLICY_ID" --version "$BOOTSTRAP_VERSION" \
| jq -e '.versions[0].status == "approved"'
Una risposta 409 con readiness elenca il controllo bloccante. required_simulation_missing significa che la Simulazione memorizzata non corrisponde al contenuto attuale della bozza; ripeti il passo 3.
Passo 5: Dare all'Agent il suo ambiente governato
kla agent policy bind accetta una policy approvata, pubblicata o attiva. Scrive il collegamento in una nuova bozza dell'Agent, crea il Process gestito per l'ambiente governato e restituisce l'UUID della bozza. Il checker approva la bozza, che viene pubblicata come Release immutabile, e il maker distribuisce la Release. agent release deploy richiede un collegamento production e sposta il manifest attivo dell'Agent, da cui viene letto l'ambiente governato durante la revisione della policy.
kla agent policy bind "$AGENT_UUID" \
--policy "$POLICY_ID" \
--environment production \
--description 'Bind the reviewed payment control to establish the production environment.' \
> agent-binding-bootstrap.json
jq -e --arg agent "$AGENT_UUID" '.success == true and .agentId == $agent' \
agent-binding-bootstrap.json
export BOOTSTRAP_DRAFT_UUID="$(jq -er '.draftId' agent-binding-bootstrap.json)"
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
kla agent release approve "$BOOTSTRAP_DRAFT_UUID" \
--notes 'Bootstrap binding for the production governed environment reviewed.' \
--yes | jq -e '.success == true'
kla agent release deploy "$BOOTSTRAP_DRAFT_UUID" --yes > agent-rollout-bootstrap.json
kla agent get "$AGENT_UUID" \
| jq -e '.manifest.governedExecution.environment == "production"'
Da questo punto fino al completamento del passo 8, il Process gestito dell'Agent fa riferimento a una versione di policy non pubblicata e le sue esecuzioni falliscono in modo fail-closed.
Passo 6: Creare, simulare e revisionare la versione limitata all'Agent
La versione 1.1.0 ha le stesse regole e nomina l'Agent in scope.agentIds. Gli input di Simulazione del passo 3 vengono riutilizzati.
jq --arg workspace "$KLA_TENANT_ID" --arg agent "$AGENT_UUID" --arg version "$POLICY_VERSION" \
'.workspaceId = $workspace | .version = $version | .scope.agentIds = [$agent]' \
policy.template.json > policy-agent-scoped.json
kla policy registry create --file policy-agent-scoped.json
kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
--input @simulate-500.json | jq -e '.decision.decision == "allow"'
kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
--input @simulate-15000.json | jq -e '.decision.decision == "require_approval"'
kla policy registry submit-review "$POLICY_ID" "$POLICY_VERSION" \
--comment 'Agent-scoped version; outcomes verified by Simulation.'
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
kla policy registry review "$POLICY_ID" "$POLICY_VERSION" \
--decision approve \
--comment 'Agent scope, outcomes, and Simulation evidence reviewed.' \
--yes
kla policy registry get "$POLICY_ID" --version "$POLICY_VERSION" \
| jq -e '.versions[0].status == "approved"'
Il controllo di prontezza alla revisione risolve ogni Agent di scope.agentIds nell'Agent Registry e richiede che ciascuno abbia un ambiente governato corrispondente a scope.environments. Il passo 5 lo ha garantito per questo Agent.
Passo 7: Pubblicare
Il maker richiede la pubblicazione. In modalità two_person la richiesta restituisce pendingApproval: true e un identificativo di Decision Request.
export KLA_EFFECTIVE_FROM="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
kla policy registry publish "$POLICY_ID" "$POLICY_VERSION" \
--effective-from "$KLA_EFFECTIVE_FROM" \
--reason 'Activate the reviewed payment control.' \
--yes > publication-request.json
jq -e '.success == true and .pendingApproval == true and .status == "PENDING"' \
publication-request.json
export APPROVAL_UUID="$(jq -er '.approvalId' publication-request.json)"
Il checker decide la Decision Request tramite approvals.decide:
jq -n --arg approvalId "$APPROVAL_UUID" '{
approvalId: $approvalId,
decision: "approve",
decisionAcknowledged: true,
reason: "Reviewed policy version approved for publication."
}' > approval-decision.json
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
kla api approvals decide --input @approval-decision.json > publication-decision.json
jq -e '.decision == "approve" and .status == "APPROVED"' publication-decision.json
La pubblicazione avviene dopo la registrazione della decisione. Attendi che la versione raggiunga published o active, poi leggi il pacchetto di policy compilato.
for attempt in $(seq 1 30); do
kla policy registry get "$POLICY_ID" --version "$POLICY_VERSION" > published-policy.json
jq -e '.versions[0].status == "published" or .versions[0].status == "active"' \
published-policy.json > /dev/null && break
sleep 2
done
jq -e '.versions[0].status == "published" or .versions[0].status == "active"' \
published-policy.json
kla policy registry compiled "$POLICY_ID" "$POLICY_VERSION" > compiled-policy.json
jq -e --arg policy "$POLICY_ID" --arg version "$POLICY_VERSION" '
.integrity.policyId == $policy and
.integrity.policyVersion == $version and
.integrity.manifestHashVerified == true
' compiled-policy.json
integrity.verification.status è PASS quando il tenant firma i pacchetti di policy e SKIPPED quando la firma è disattivata in quell'ambiente. Fermati se la versione resta non pubblicata o se manifestHashVerified è false.
Passo 8: Collegare la versione pubblicata
Il collegamento seleziona la versione attiva o pubblicata della policy, quindi lo stesso comando di collegamento fissa ora la versione 1.1.0 e aggiorna il Process gestito. Il checker approva la nuova bozza e il maker la distribuisce.
kla agent policy bind "$AGENT_UUID" \
--policy "$POLICY_ID" \
--environment production \
--description 'Bind the published Agent-scoped payment control.' \
> agent-binding.json
jq -e --arg agent "$AGENT_UUID" '.success == true and .agentId == $agent' agent-binding.json
export DRAFT_UUID="$(jq -er '.draftId' agent-binding.json)"
kla agent release history "$AGENT_UUID" > agent-history-draft.json
jq -e --arg release "$DRAFT_UUID" --arg policy "$POLICY_ID" --arg version "$POLICY_VERSION" '
any(.versions[];
.id == $release and
.is_draft == true and
.manifest.governedExecution.policyId == $policy and
.manifest.governedExecution.policyVersion == $version)
' agent-history-draft.json
KLA_ACCESS_TOKEN="$KLA_CHECKER_ACCESS_TOKEN" \
kla agent release approve "$DRAFT_UUID" \
--notes 'Published policy binding and managed Process reviewed.' \
--yes | jq -e '.success == true'
kla agent release deploy "$DRAFT_UUID" --yes > agent-rollout.json
Il controllo della cronologia mostra al checker che la bozza contiene il collegamento alla versione 1.1.0 prima dell'approvazione.
Passo 9: Verificare
Leggi l'Agent e la sua cronologia delle Release.
kla agent get "$AGENT_UUID" > agent-after.json
jq -e --arg policy "$POLICY_ID" --arg version "$POLICY_VERSION" '
.manifest.governedExecution.policyId == $policy and
.manifest.governedExecution.policyVersion == $version and
.manifest.governedExecution.environment == "production" and
(.manifest.governedExecution.workflowId | type == "string") and
any(.passport.activePolicies[]; .policyId == $policy and .version == $version)
' agent-after.json
kla agent release history "$AGENT_UUID" > agent-history.json
jq -e --arg release "$DRAFT_UUID" '
any(.versions[];
.id == $release and
.is_draft == false and
.is_current_version == true and
.approved_at != null and
.deployed_at != null)
' agent-history.json
Esegui entrambe le Simulazioni sulla versione pubblicata e conserva i risultati insieme al record della modifica.
kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
--input @simulate-500.json | jq -e '.decision.decision == "allow"'
kla policy registry simulate "$POLICY_ID" "$POLICY_VERSION" \
--input @simulate-15000.json | jq -e '.decision.decision == "require_approval"'
Ora hai una policy pubblicata e limitata all'Agent le cui Simulazioni risolvono 500 EUR in allow e 15.000 EUR in require_approval, e una Release di Agent distribuita che la collega. Una chiamata reale payments.create superiore a 10.000 EUR da questo Agent apre nel Decision Desk una Decision Request per il ruolo kla:approver. La guida Governare un agente end-to-end copre l'esecuzione, l'approvazione e l'esportazione delle prove successive.
