Skip to main content

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:

policy-config.yaml
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​

  1. The gateway runs the input stage of the chain in order.
  2. At audit-logger, it adds a result with policy_kind: audit-logger, phase: preflight, verdict allow, and audit_logger.logged, without details.
  3. The chain continues. The allow does not change the verdict or reason code of the decision.
  4. A clean chain ends with allow and ok. The marker is in policy_results, not in the decision reason code.
  5. 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:

tests/audit-marker.json
{
  "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 --json

The 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 --json

Phase and verdicts​

ItemValue
Phasepreflight
Request familiesChat Completions, Completions, Responses, Messages, Embeddings, Audio transcriptions, Moderation
Verdictsallow
Reason codesaudit_logger.logged

Limits and failure behavior​

  • The input stage stops at the first block or escalate. An audit-logger entry after that point adds no marker.
  • A when condition 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​