Opt-in logging

Inference logging is off by default. With it off, the gateway writes no per-request row at all: it enforces your policy, forwards the request, and keeps nothing.

Switching it on gives you one row per proxied request. Metadata only — see zero data retention for why content cannot appear here even in principle.

vulnetix ai-firewall settings --logs      # on
vulnetix ai-firewall settings --no-logs   # off

Or the Usage tab of the dashboard. Requires a paid plan.

Check the current state:

vulnetix ai-firewall status
# Inference logging: on

Exactly what is recorded

The complete field list. There is nothing else.

FieldWhat it is
providerSlugWhich provider the request went to
modelThe model requested
wireWhich request surface it arrived on — chat, responses, or messages
decisionallow, block, redact, or flag
blockedByThe name of the rule that blocked it, if any
violationsWhich rules fired — names and types, never the matched content
inspectedHow many text segments the guardrails read
statusCodeThe HTTP status returned
promptTokens / completionTokensToken counts, when the provider reports them
streamWhether the response was streamed
latencyMsHow long it took
createdAtWhen

What is not recorded

  • The prompt. Any part of it.
  • The completion. Any part of it.
  • The content that tripped a guardrail. A flag records that a rule matched and which one — never what it matched.
  • Headers, tool definitions, tool arguments, or tool results.

The two fields worth understanding

inspected — proof the firewall actually looked

This is the number of text segments the guardrail engine read out of the request.

It exists to make one specific, nasty failure visible: a request that parses cleanly but exposes nothing to inspect is a firewall screening nothing — and without this number it looks exactly like a clean pass. Everything is green, every dashboard says allow, and no rule is running against any text.

An inspected of 0 against a non-empty policy set is a bug, and it is the one you most want to catch. It is the reason the field is there.

wire — which client this actually was

Which surface the request arrived on. Without it, a Codex inference is indistinguishable from a Claude Code one after the fact, and any per-client incident becomes impossible to reason about.

What you can do with it

  • Tune your guardrails. Which rules fire, how often, and on which surface. This is how you run the flag → redact → block ladder with evidence rather than a guess.
  • See which models are actually being used. Frequently, this is not the set anyone expected, and it is the input to a sensible model allowlist.
  • Attribute cost. Token counts per model, per surface. Note: the firewall does no cost accounting itself and applies no markup — this is raw token counts, and your bill still comes from the provider.
  • Investigate a block. blockedBy and violations tell you exactly which rule refused a request and why, without exposing what was in it.

The trade-off, stated plainly

Because violations never record content, you cannot read the prompts your rules caught. You learn a rule fired forty times; you cannot see the forty prompts.

That makes tuning harder. It is a deliberate choice: the alternative is that enabling a guardrail creates a new datastore containing the most sensitive strings your organisation produces, which is precisely the thing you adopted a firewall to prevent.

Tune by reasoning about the pattern, not by reading the traffic. See tuning false positives.

Turning it off

vulnetix ai-firewall settings --no-logs

Takes effect on the next request. New rows stop being written immediately.

In policy as code

spec:
  settings:
    logsEnabled: true

See policy as code.