Einen Agenten von Anfang bis Ende steuern
Einen Agenten registrieren, eine menschliche Genehmigung bearbeiten, seinen Trace prüfen und versiegelte Evidence über die KLA Control Plane exportieren.
Dieser Leitfaden verwendet einen Claims-Triage-Agenten, der eine Rückerstattung empfehlen kann. Der Ablauf umfasst die Agenten-Registrierung, eine gesteuerte Ausführung, die menschliche Genehmigung, die Trace-Prüfung und den Evidence-Export.
Die Beispiele verwenden die KLA CLI für generierte Control-Plane-Prozeduren und curl für Evidence-REST-Routen. Die CLI liest KLA_ACCESS_TOKEN. API-Host und Tenant sind ausdrücklich gesetzt, damit dieselben Befehle gegen einen bereitgestellten Dev-Tenant ausgeführt werden können.
export KLA_API_URL='https://api.dev.kla.digital'
export KLA_ACCESS_TOKEN='<short-lived access token>'
export KLA_TENANT_ID='<tenant external ID>'
# Build the repository CLI while the package is private, then expose it in this shell.
corepack enable
pnpm install --frozen-lockfile
pnpm kla --help
KLA_REPO_ROOT="$(git rev-parse --show-toplevel)"
kla() { KLA_CLI_CWD="$PWD" pnpm --dir "$KLA_REPO_ROOT" --silent kla "$@"; }
kla auth test
kla api doctor
KLA_TENANT_ID konfiguriert den x-kla-tenant-id-Routing-Hinweis des CLI-Kontexts. Der verifizierte Tenant-Claim bleibt maßgeblich. Eine Anfrage an einen anderen Tenant verwendet x-kla-tenant-external-id; der Aufrufer muss dort eine aktive Mitgliedschaft haben.
Dieser Ablauf setzt einen Tenant mit einer genehmigten Policy und einem bereitgestellten Workflow voraus, der eine ausstehende Genehmigung erzeugen kann. Registrierung, Policy-Bindung, Release-Genehmigung, Bereitstellung und Ausführung schreiben in den Tenant. Verwenden Sie einen Tenant unter Ihrer Kontrolle und eine zweite Identität für die Maker-Checker-Genehmigung.
sequenceDiagram participant Dev as Developer participant Agent as Claims agent participant KLA as KLA Control Plane participant Desk as Decision Desk participant Room as Evidence Room Dev->>KLA: Register with agents.create Agent->>KLA: Start executions.execute KLA->>Desk: approvals.getPending returns a pending request Desk->>KLA: approvals.decide approves the request Dev->>KLA: Read traces.getTrace and traces.verify Dev->>Room: POST /v1/evidence/export
Schritt 1: Den Agenten registrieren
agents.create ist eine tRPC-Mutation. Der generierte CLI-Befehl sendet sie über den authentifizierten /v1-tRPC-Transport.
- Endpunkt:
POST /v1/agents.create
Erstellen Sie ein Registrierungsmanifest im von der Agent Registry akzeptierten Format:
cat > /tmp/claims-triage-registration.yaml <<'YAML'
name: Claims Triage Agent
flowId: claims_triage
description: Triages inbound claims and prepares refund recommendations for review.
model:
provider: openai
name: gpt-4o
pipeline:
- type: llm_generate
name: triage_claim
config:
template: Review the claim and recommend the next action with supporting facts.
owners:
- email: [email protected]
tools:
- lookup_claim
- process_refund
humanOversightRequired: true
YAML
Registrieren Sie es:
AGENT_JSON=$(kla agent create --file /tmp/claims-triage-registration.yaml)
echo "$AGENT_JSON"
AGENT_ID=$(jq -r '.id // .agentId' <<<"$AGENT_JSON")
Bewahren Sie die zurückgegebene Agenten-ID auf. Ein neu registrierter Agent benötigt eine genehmigte, bereitgestellte Policy-Bindung, bevor executions.execute einen gesteuerten Lauf starten kann. Der CLI-Lebenszyklus ist:
POLICY_ID='<approved policy ID>'
kla agent policy bind "$AGENT_ID" \
--policy "$POLICY_ID" \
--environment sandbox \
--description 'Attach the refund approval policy.'
Die Sandbox-Bindung erstellt oder aktualisiert den ausstehenden Sandbox-Entwurf des Agenten und seinen verwalteten Workflow. Sie macht ein Production-Release nicht bereitstellbar. agents.deployVersion verlangt, dass das unveränderliche Release-Manifest eine Production-Bindung governedExecution enthält.
Erstellen Sie dieses ausdrückliche Production-Release-Manifest. Eine neue UUID gibt dem verwalteten Production-Workflow eine eigene Identität; die erste Workflow-Version ist 1:
WORKFLOW_ID="$(node -e "console.log(require('node:crypto').randomUUID())")"
cp /tmp/claims-triage-registration.yaml /tmp/claims-triage.yaml
cat >> /tmp/claims-triage.yaml <<YAML
governedExecution:
policyId: ${POLICY_ID}
workflowId: ${WORKFLOW_ID}
workflowVersion: 1
environment: production
YAML
RELEASE_JSON=$(kla agent release create "$AGENT_ID" \
--manifest @/tmp/claims-triage.yaml \
--description 'Publish the claims triage release.' \
--justification 'The release adds the governed refund workflow.' \
--change-type minor)
echo "$RELEASE_JSON"
VERSION_ID=$(jq -r '.version.id // .versionId // .id' <<<"$RELEASE_JSON")
Der Release-Befehl akzeptiert @/tmp/claims-triage.yaml als strukturierte Dateieingabe. Die API validiert UUID, Workflow-Version, Policy-ID und Production-Umgebung und speichert anschließend die verwaltete Workflow-Bindung, bevor sie das unveränderliche Release erstellt.
Die Reviewer-Anmeldedaten müssen eine andere authentifizierte Identität bezeichnen. Der Server wendet die Rollen- und Vier-Augen-Regeln des Tenants auf jeden Lebenszyklus-Befehl an.
Verwenden Sie dieselbe API-URL und denselben Tenant mit einem kurzlebigen Reviewer-Token, das auf den Genehmigungsbefehl beschränkt ist:
export KLA_REVIEWER_ACCESS_TOKEN='<reviewer short-lived access token>'
KLA_ACCESS_TOKEN="$KLA_REVIEWER_ACCESS_TOKEN" \
kla agent release approve "$VERSION_ID" --yes
Nachdem der Reviewer das Release genehmigt hat, stellen Sie vor der Bereitstellung die Maker-Anmeldedaten wieder her:
export KLA_ACCESS_TOKEN='<maker short-lived access token>'
kla agent release deploy "$VERSION_ID" --yes
Schritt 2: Eine gesteuerte Ausführung starten
Führen Sie den Agenten in der Sandbox mit einem Claim aus, der die Genehmigungs-Policy erreichen sollte:
RUN_JSON=$(kla agent run "$AGENT_ID" \
--environment sandbox \
--input '{"claim_id":"clm_9921","requested_refund":1250.00}')
echo "$RUN_JSON"
EXECUTION_ID=$(jq -r '.executionId' <<<"$RUN_JSON")
kla api executions get --input "{\"id\":\"$EXECUTION_ID\"}"
executions.execute liefert eine executionId. Das Policy-Ergebnis require_approval lässt die Ausführung auf einen Reviewer warten. Ausführungsstatus und Ereignishistorie sind über executions.get und executions.getEvents verfügbar.
Schritt 3: Die ausstehende Anfrage finden und genehmigen
approvals.getPending listet die ausstehenden Decision Requests des Tenants auf. Der Ausführungsfilter liefert die Anfrage dieses Laufs:
- Endpunkt:
GET /v1/approvals.getPending
# A 202 PENDING result queues approval creation asynchronously. Retry the lookup until the request
# is visible. Rate limits, 5xx responses, and network failures stay retryable for 60 seconds.
APPROVAL_ID=''
PENDING_JSON='{}'
POLL_DEADLINE=$((SECONDS + 60))
while (( SECONDS < POLL_DEADLINE )); do
PENDING_ATTEMPT_STATUS=0
PENDING_ATTEMPT_STDERR_FILE="$(mktemp)"
PENDING_ATTEMPT_OUTPUT=$(kla api approvals getPending \
--input "{\"executionId\":\"$EXECUTION_ID\",\"limit\":20}" 2>"$PENDING_ATTEMPT_STDERR_FILE") || \
PENDING_ATTEMPT_STATUS=$?
PENDING_ATTEMPT_ERROR="$(cat "$PENDING_ATTEMPT_STDERR_FILE")"
rm -f "$PENDING_ATTEMPT_STDERR_FILE"
if [[ -n "$PENDING_ATTEMPT_ERROR" ]]; then
printf '%s\n' "$PENDING_ATTEMPT_ERROR" >&2
fi
if (( PENDING_ATTEMPT_STATUS != 0 )); then
if printf '%s\n' "$PENDING_ATTEMPT_ERROR" | grep -Eqi \
'(^|[^0-9])(429|5[0-9][0-9])([^0-9]|$)|network|timed out|timeout|ECONNRESET|ECONNREFUSED|ENOTFOUND|fetch failed|socket hang up|temporarily unavailable'; then
echo 'Transient approvals.getPending failure; retrying until the 60-second deadline.' >&2
PENDING_JSON="$PENDING_ATTEMPT_OUTPUT"
sleep 2
continue
fi
echo 'approvals.getPending returned a non-retryable authorization or schema error.' >&2
if [[ -n "$PENDING_ATTEMPT_OUTPUT" ]]; then
printf '%s\n' "$PENDING_ATTEMPT_OUTPUT" >&2
fi
exit 1
fi
PENDING_JSON="$PENDING_ATTEMPT_OUTPUT"
if ! APPROVAL_ID=$(jq -r '.approvals[0].approvalId // empty' <<<"$PENDING_JSON"); then
echo 'approvals.getPending returned invalid JSON.' >&2
exit 1
fi
if [[ -n "$APPROVAL_ID" ]]; then
break
fi
sleep 2
done
if [[ -z "$APPROVAL_ID" ]]; then
echo "No pending approval appeared for $EXECUTION_ID within 60 seconds." >&2
echo "$PENDING_JSON" >&2
exit 1
fi
echo "$PENDING_JSON"
Der Reviewer sendet approvals.decide mit der Genehmigungs-ID, einer Entscheidung und einer Begründung. Die Entscheidung akzeptiert approve, reject oder escalate. Dieses Beispiel verwendet die zweite Reviewer-Anmeldung:
- Endpunkt:
POST /v1/approvals.decide
cat > /tmp/approval-decision.json <<JSON
{
"approvalId": "${APPROVAL_ID}",
"decision": "approve",
"decisionAcknowledged": true,
"reason": "Claim documentation reviewed and refund authorized."
}
JSON
KLA_ACCESS_TOKEN="$KLA_REVIEWER_ACCESS_TOKEN" \
kla api approvals decide --input @/tmp/approval-decision.json
# The decision can return before the worker and pending ledger finish. Keep polling the execution
# until it reaches a terminal state. A 202/PENDING response remains expected during this window.
export KLA_ACCESS_TOKEN='<maker short-lived access token>'
EXECUTION_JSON='{}'
EXECUTION_STATUS=''
EXECUTION_STATUS_LOWER=''
EXECUTION_DEADLINE=$((SECONDS + 120))
while (( SECONDS < EXECUTION_DEADLINE )); do
EXECUTION_ATTEMPT_STATUS=0
EXECUTION_ATTEMPT_STDERR_FILE="$(mktemp)"
EXECUTION_ATTEMPT_OUTPUT=$(kla api executions get \
--input "{\"id\":\"$EXECUTION_ID\"}" 2>"$EXECUTION_ATTEMPT_STDERR_FILE") || \
EXECUTION_ATTEMPT_STATUS=$?
EXECUTION_ATTEMPT_ERROR="$(cat "$EXECUTION_ATTEMPT_STDERR_FILE")"
rm -f "$EXECUTION_ATTEMPT_STDERR_FILE"
if [[ -n "$EXECUTION_ATTEMPT_ERROR" ]]; then
printf '%s\n' "$EXECUTION_ATTEMPT_ERROR" >&2
fi
if (( EXECUTION_ATTEMPT_STATUS != 0 )); then
if printf '%s\n' "$EXECUTION_ATTEMPT_ERROR" | grep -Eqi \
'(^|[^0-9])(202|429|5[0-9][0-9])([^0-9]|$)|PENDING|network|timed out|timeout|ECONNRESET|ECONNREFUSED|ENOTFOUND|fetch failed|socket hang up|temporarily unavailable'; then
echo 'Transient execution status response; retrying until the 120-second deadline.' >&2
sleep 2
continue
fi
echo 'executions.get failed while waiting for the approved run.' >&2
if [[ -n "$EXECUTION_ATTEMPT_OUTPUT" ]]; then
printf '%s\n' "$EXECUTION_ATTEMPT_OUTPUT" >&2
fi
exit 1
fi
EXECUTION_JSON="$EXECUTION_ATTEMPT_OUTPUT"
if ! EXECUTION_STATUS=$(jq -r '.status // empty' <<<"$EXECUTION_JSON"); then
echo 'executions.get returned invalid JSON.' >&2
exit 1
fi
EXECUTION_STATUS_LOWER=$(printf '%s' "$EXECUTION_STATUS" | tr '[:upper:]' '[:lower:]')
case "$EXECUTION_STATUS_LOWER" in
completed|failed|blocked|cancelled|timeout|stale)
break
;;
pending|running|gated|waiting_for_input)
sleep 2
;;
'')
echo 'executions.get returned no status.' >&2
echo "$EXECUTION_JSON" >&2
exit 1
;;
*)
echo "executions.get returned an unknown status: $EXECUTION_STATUS" >&2
echo "$EXECUTION_JSON" >&2
exit 1
;;
esac
done
if [[ ! "$EXECUTION_STATUS_LOWER" =~ ^(completed|failed|blocked|cancelled|timeout|stale)$ ]]; then
echo "Execution $EXECUTION_ID did not reach a terminal state within 120 seconds." >&2
echo "$EXECUTION_JSON" >&2
exit 1
fi
if [[ "$EXECUTION_STATUS_LOWER" != 'completed' ]]; then
echo "Execution $EXECUTION_ID ended with status $EXECUTION_STATUS; trace and evidence checks require completed status." >&2
echo "$EXECUTION_JSON" >&2
exit 1
fi
echo "$EXECUTION_JSON"
Die Ausführung wird entsprechend dem bereitgestellten Workflow fortgesetzt. Eine Ablehnung lässt die nachgelagerte Aktion unausgeführt.
Schritt 4: Den Trace prüfen und verifizieren
Der Worker kann die Ausführung beenden, bevor die Audit-Ledger-Projektion sichtbar ist. Lesen Sie die Ereignishistorie, warten Sie auf den Audit-Eintrag der Genehmigung und verwenden Sie die zurückgegebenen IDs für Trace- und Proof-Aufrufe.
- Endpunkt:
GET /v1/traces.getTrace - Endpunkt:
GET /v1/traces.verify - Endpunkt:
GET /v1/audit.listByResource
EVENTS_OUTPUT=$(kla api executions getEvents \
--input "{\"id\":\"$EXECUTION_ID\",\"limit\":200}")
if ! jq -e 'type == "object" and (.events | type == "array")' <<<"$EVENTS_OUTPUT" >/dev/null; then
echo 'executions.getEvents returned invalid JSON or no event list.' >&2
echo "$EVENTS_OUTPUT" >&2
exit 1
fi
EVENTS_JSON="$EVENTS_OUTPUT"
TRACE_ID=$(jq -r --argjson events "$EVENTS_JSON" '
[
.traceId,
.trace_id,
($events.events[]? | .traceId, .trace_id, .metadata?.traceId, .metadata?.trace_id, .data?.traceId, .data?.trace_id)
]
| map(select(type == "string" and length > 0))
| first // empty
' <<<"$EXECUTION_JSON")
if [[ -z "$TRACE_ID" ]]; then
echo "No trace identifier was returned for completed execution $EXECUTION_ID." >&2
echo "$EVENTS_JSON" >&2
exit 1
fi
# Approval decisions and requests receive audit IDs asynchronously. Poll both the approval detail
# and audit projection so the proof call uses a materialized ID from this run.
AUDIT_ID=''
APPROVAL_DETAIL_JSON='{}'
AUDIT_JSON='{}'
LEDGER_DEADLINE=$((SECONDS + 60))
while (( SECONDS < LEDGER_DEADLINE )); do
DETAIL_ATTEMPT_STATUS=0
DETAIL_ATTEMPT_STDERR_FILE="$(mktemp)"
DETAIL_ATTEMPT_OUTPUT=$(kla api approvals getDetail \
--input "{\"approvalId\":\"$APPROVAL_ID\"}" 2>"$DETAIL_ATTEMPT_STDERR_FILE") || \
DETAIL_ATTEMPT_STATUS=$?
DETAIL_ATTEMPT_ERROR="$(cat "$DETAIL_ATTEMPT_STDERR_FILE")"
rm -f "$DETAIL_ATTEMPT_STDERR_FILE"
if [[ -n "$DETAIL_ATTEMPT_ERROR" ]]; then
printf '%s\n' "$DETAIL_ATTEMPT_ERROR" >&2
fi
if (( DETAIL_ATTEMPT_STATUS != 0 )); then
if printf '%s\n' "$DETAIL_ATTEMPT_ERROR" | grep -Eqi \
'(^|[^0-9])(429|5[0-9][0-9])([^0-9]|$)|network|timed out|timeout|ECONNRESET|ECONNREFUSED|ENOTFOUND|fetch failed|socket hang up|temporarily unavailable'; then
echo 'Transient approval detail response; retrying ledger lookup.' >&2
sleep 2
continue
fi
echo 'approvals.getDetail failed while waiting for its audit identifier.' >&2
if [[ -n "$DETAIL_ATTEMPT_OUTPUT" ]]; then
printf '%s\n' "$DETAIL_ATTEMPT_OUTPUT" >&2
fi
exit 1
fi
APPROVAL_DETAIL_JSON="$DETAIL_ATTEMPT_OUTPUT"
if ! jq -e 'type == "object"' <<<"$APPROVAL_DETAIL_JSON" >/dev/null; then
echo 'approvals.getDetail returned invalid JSON.' >&2
echo "$APPROVAL_DETAIL_JSON" >&2
exit 1
fi
AUDIT_ID=$(jq -r '
[
.decisionReceipt.auditEventId,
.metadata.decisionReceipt.auditEventId,
.metadata.requestAuditEventId
]
| map(select(type == "string" and length > 0))
| first // empty
' <<<"$APPROVAL_DETAIL_JSON")
if [[ -n "$AUDIT_ID" ]]; then
break
fi
AUDIT_ATTEMPT_STATUS=0
AUDIT_ATTEMPT_STDERR_FILE="$(mktemp)"
AUDIT_ATTEMPT_OUTPUT=$(kla api audit listByResource \
--input "{\"resource\":\"approval\",\"resourceId\":\"$APPROVAL_ID\",\"limit\":100,\"includeTotal\":true}" 2>"$AUDIT_ATTEMPT_STDERR_FILE") || \
AUDIT_ATTEMPT_STATUS=$?
AUDIT_ATTEMPT_ERROR="$(cat "$AUDIT_ATTEMPT_STDERR_FILE")"
rm -f "$AUDIT_ATTEMPT_STDERR_FILE"
if [[ -n "$AUDIT_ATTEMPT_ERROR" ]]; then
printf '%s\n' "$AUDIT_ATTEMPT_ERROR" >&2
fi
if (( AUDIT_ATTEMPT_STATUS != 0 )); then
if printf '%s\n' "$AUDIT_ATTEMPT_ERROR" | grep -Eqi \
'(^|[^0-9])(429|5[0-9][0-9])([^0-9]|$)|network|timed out|timeout|ECONNRESET|ECONNREFUSED|ENOTFOUND|fetch failed|socket hang up|temporarily unavailable'; then
echo 'Transient audit projection response; retrying ledger lookup.' >&2
sleep 2
continue
fi
echo 'audit.listByResource failed while waiting for its audit identifier.' >&2
if [[ -n "$AUDIT_ATTEMPT_OUTPUT" ]]; then
printf '%s\n' "$AUDIT_ATTEMPT_OUTPUT" >&2
fi
exit 1
fi
AUDIT_JSON="$AUDIT_ATTEMPT_OUTPUT"
if ! jq -e 'type == "object" and (.entries | type == "array")' <<<"$AUDIT_JSON" >/dev/null; then
echo 'audit.listByResource returned invalid JSON.' >&2
echo "$AUDIT_JSON" >&2
exit 1
fi
AUDIT_ID=$(jq -r --arg approval_id "$APPROVAL_ID" '
[
.entries[]?
| select(
(.resourceId == $approval_id) or
(.resource_id == $approval_id) or
(.details?.approvalId == $approval_id) or
(.details?.approval_id == $approval_id)
)
| (.id // .eventId // .event_id)
]
| map(select(type == "string" and length > 0))
| first // empty
' <<<"$AUDIT_JSON")
if [[ -n "$AUDIT_ID" ]]; then
break
fi
sleep 2
done
if [[ -z "$AUDIT_ID" ]]; then
echo "No materialized audit identifier appeared for approval $APPROVAL_ID within 60 seconds." >&2
echo "$APPROVAL_DETAIL_JSON" >&2
echo "$AUDIT_JSON" >&2
exit 1
fi
RESULT_JSON='{}'
MERKLE_ROOT=''
RESULT_DEADLINE=$((SECONDS + 60))
while (( SECONDS < RESULT_DEADLINE )); do
RESULT_ATTEMPT_STATUS=0
RESULT_ATTEMPT_STDERR_FILE="$(mktemp)"
RESULT_ATTEMPT_OUTPUT=$(kla api executions result \
--input "{\"id\":\"$EXECUTION_ID\"}" 2>"$RESULT_ATTEMPT_STDERR_FILE") || \
RESULT_ATTEMPT_STATUS=$?
RESULT_ATTEMPT_ERROR="$(cat "$RESULT_ATTEMPT_STDERR_FILE")"
rm -f "$RESULT_ATTEMPT_STDERR_FILE"
if [[ -n "$RESULT_ATTEMPT_ERROR" ]]; then
printf '%s\n' "$RESULT_ATTEMPT_ERROR" >&2
fi
if (( RESULT_ATTEMPT_STATUS != 0 )); then
if printf '%s\n' "$RESULT_ATTEMPT_ERROR" | grep -Eqi \
'(^|[^0-9])(202|429|5[0-9][0-9])([^0-9]|$)|PENDING|network|timed out|timeout|ECONNRESET|ECONNREFUSED|ENOTFOUND|fetch failed|socket hang up|temporarily unavailable'; then
echo 'Transient execution result response; retrying evidence materialization.' >&2
sleep 2
continue
fi
echo 'executions.result failed while waiting for the evidence anchor.' >&2
if [[ -n "$RESULT_ATTEMPT_OUTPUT" ]]; then
printf '%s\n' "$RESULT_ATTEMPT_OUTPUT" >&2
fi
exit 1
fi
RESULT_JSON="$RESULT_ATTEMPT_OUTPUT"
if ! MERKLE_ROOT=$(jq -r '.evidence.merkleRoot // empty' <<<"$RESULT_JSON"); then
echo 'executions.result returned invalid JSON.' >&2
echo "$RESULT_JSON" >&2
exit 1
fi
if [[ -n "$MERKLE_ROOT" ]]; then
break
fi
sleep 2
done
if [[ -z "$MERKLE_ROOT" ]]; then
echo "The evidence anchor for completed execution $EXECUTION_ID was not materialized within 60 seconds." >&2
echo "$RESULT_JSON" >&2
exit 1
fi
TRACE_INPUT=$(jq -cn \
--arg trace_id "$TRACE_ID" \
--arg tenant_id "$KLA_TENANT_ID" \
'{traceId: $trace_id, tenantId: $tenant_id, includeSensitiveData: false}')
kla api traces getTrace --input "$TRACE_INPUT"
PROOF_INPUT=$(jq -cn --arg tenant "$KLA_TENANT_ID" --arg audit "$AUDIT_ID" \
'{tenant: $tenant, auditId: $audit}')
PROOF_JSON=$(kla api traces verify --input "$PROOF_INPUT")
PROOF_VALID=$(jq -r '.valid // false' <<<"$PROOF_JSON")
PROOF_NONCE=$(jq -r '.nonce // empty' <<<"$PROOF_JSON")
PROOF_TX_ID=$(jq -r '.ledgerAttestation.txId // empty' <<<"$PROOF_JSON")
if [[ "$PROOF_VALID" != 'true' || -z "$PROOF_NONCE" ]]; then
echo "traces.verify did not validate audit $AUDIT_ID." >&2
echo "$PROOF_JSON" >&2
exit 1
fi
echo "$PROOF_JSON"
echo "Trace: $TRACE_ID"
echo "Audit: $AUDIT_ID"
echo "Evidence Merkle root: $MERKLE_ROOT"
echo "Proof nonce: $PROOF_NONCE"
echo "Proof ledger transaction: ${PROOF_TX_ID:-unavailable}"
traces.getTrace liest die Spans des zurückgegebenen Traces und maskiert sensible Daten standardmäßig. traces.verify erstellt und prüft einen Proof für die zurückgegebene Audit-ID. Die Proof-Antwort enthält eine Nonce und, wenn die Ledger-Attestierung verfügbar ist, ihre Transaktions-ID. Beide Aufrufe benötigen die entsprechenden Trace-Berechtigungen.
Schritt 5: Versiegelte Evidence exportieren
Der Evidence-Export der API ist eine REST-Route. Exportieren Sie anhand der Ausführungs-ID, lesen Sie danach das Manifest und laden Sie das verifizierte Archiv herunter:
- Endpunkt:
POST /v1/evidence/export
EXPORT_JSON=$(curl -sS -X POST "$KLA_API_URL/v1/evidence/export" \
-H "Authorization: Bearer $KLA_ACCESS_TOKEN" \
-H "x-kla-tenant-external-id: $KLA_TENANT_ID" \
-H 'Content-Type: application/json' \
-d "{\"runId\":\"$EXECUTION_ID\",\"format\":\"json\",\"includeProofs\":true}")
echo "$EXPORT_JSON"
EXPORT_ID=$(jq -r '.exportId' <<<"$EXPORT_JSON")
Die Antwort enthält eine exportId, ein Manifest und eine Download-URL. Die API stellt Manifest und Archiv über diese Routen bereit:
- Endpunkt:
GET /v1/evidence/exports/$EXPORT_ID - Endpunkt:
GET /v1/evidence/exports/$EXPORT_ID/download
curl -sS "$KLA_API_URL/v1/evidence/exports/$EXPORT_ID" \
-H "Authorization: Bearer $KLA_ACCESS_TOKEN" \
-H "x-kla-tenant-external-id: $KLA_TENANT_ID" | jq
curl -sS -f "$KLA_API_URL/v1/evidence/exports/$EXPORT_ID/download" \
-H "Authorization: Bearer $KLA_ACCESS_TOKEN" \
-H "x-kla-tenant-external-id: $KLA_TENANT_ID" \
-o "./evidence-$EXPORT_ID.zip"
Die CLI kapselt dieselben beiden Routen:
kla evidence export --days 7 --out ./evidence
kla evidence verify --bundle "./evidence-$EXPORT_ID.zip" --out ./evidence-report
kla evidence verify arbeitet mit dem heruntergeladenen Bundle und stellt keine Netzwerkverbindung her. Sein Bericht erfasst Bundlespezifikation, Manifest-Digest, Merkle-Root und Verifikationsergebnis.
Der abgeschlossene Ablauf lautet: mit agents.create registrieren, executions.execute starten, die ausstehende Anfrage über approvals.getPending und approvals.decide auflösen, mit traces.* prüfen und verifizieren und über /v1/evidence/* exportieren.
