Input Controls
Audit Logger
audit-logger adds a marker, audit_logger.logged, to the decision of every request that reaches it, and always lets the request continue. Use it to show in each decision that a request passed a specific point of the chain.
It does not store anything. Request decisions are stored as runtime events, and administrative changes in Trail. This control does not configure storage or retention for either.
Configuration
Save this as policy-config.yaml in a test directory:
pack:
name: audit-logger-example
version: 1.0.0
enabled: true
policies:
chain:
- audit-logger
- prompt-injection
- pii-detector
policy:
audit-logger: {}The chain entry turns the control on. A policy.audit-logger block alone does nothing. Put the entry before any control that can block, so that blocked requests also get the marker.
Fields
The block has no fields: audit-logger: {}.
Lint rejects the removed keys retention_days, immutable, hipaa_storage, and log_all_access. Retention, integrity, and export belong to Trail and the event export workflows.
How it works
- The gateway runs the input stage of the chain in order.
- At
audit-logger, it adds a result withpolicy_kind: audit-logger,phase: preflight, verdictallow, andaudit_logger.logged, withoutdetails. - The chain continues. The
allowdoes not change the verdict or reason code of the decision. - A clean chain ends with
allowandok. The marker is inpolicy_results, not in the decision reason code. - A connected gateway sends the decision event, with the marker, to Verdictan. A gateway without that connection evaluates the marker but sends no event.
Verify the marker
Create a JSON test next to the configuration:
{
"name": "records-audit-marker",
"input": {
"messages": [
{
"role": "user",
"content": "Summarize the release notes."
}
]
},
"expected": {
"verdict": "allow",
"reason_code": "ok"
}
}verdictan policy lint --file policy-config.yaml
verdictan policy test --jsonThe case passes with verdict: allow and reason_code: ok, and details.policy_results contains an audit-logger entry with audit_logger.logged. Do not expect audit_logger.logged as the case reason_code: the test compares the decision reason, and the marker is a nested result.
Through a gateway, send one allowed request and one that a later control blocks. Both events show the marker:
verdictan events tail --since 10m --jsonPhase and verdicts
| Item | Value |
|---|---|
| Phase | preflight |
| Request families | Chat Completions, Completions, Responses, Messages, Embeddings, Audio transcriptions, Moderation |
| Verdicts | allow |
| Reason codes | audit_logger.logged |
Limits and failure behavior
- The input stage stops at the first
blockorescalate. Anaudit-loggerentry after that point adds no marker. - A
whencondition or targeting on the entry can skip it. If a marker is missing, examine the effective chain of the route. - The control stores no evidence. Use runtime events for request results and Trail for the history of control-plane changes.
- A gateway without a control-plane connection sends no events, so a missing event does not prove that the control did not run.
- The events API returns events for one organization and one time window. Query the right ones, and match events by request ID.
Next steps
- verdictan events: decision events and their nested results.
- Trail and Audit Evidence: storage, integrity, and export of evidence.
- Export Evidence for a Review: prepare an evidence package.