01 · The log you already have
Vault's audit device, tailed into Loki.
A stdout audit device makes Vault print one JSON record per request
and one per response. Your log collector already ships pod logs to
Loki, so the audit trail lands there with no extra agent.
The assistant runs a read-only LogQL query against that
stream. It holds no Vault token and cannot change Vault.
02 · Find the refusals
Parse every record. Keep the denials.
Each audit record is parsed as JSON. A denial is a response that
carries permission denied, together with who asked
(the entity and its bound policies), what they asked for (path and
operation) and when.
Records that do not parse are skipped and counted. A failed read
never turns into "no denials".
03 · Group them
One row per (entity, path), not one alert per line.
246 denials in two days is usually one misconfigured client, not 246
problems. Denials are clustered by entity and path,
with a count, first and last seen, and how many distinct path
prefixes the entity was refused on.
A single occurrence is noise. A cluster needs to repeat before it is
considered at all.
04 · Widen, probe or ambiguous
A fixed rule decides, not a model.
The classifier is deterministic code, with no LLM involved:
- Probe: the path is sensitive (
sys/, auth/, the audit device, key-management paths), or the entity is unknown, or it was refused on three or more different prefixes.
- Widen: a known entity, refused again and again on one ordinary path prefix, with plain operations.
- Ambiguous: everything in between.
05 · Probes and grey areas stay issues
One digest issue, refreshed every run.
Probe and ambiguous clusters are never turned into a policy change.
They roll into a single audit-denial digest issue per Vault
repo, with a cause table that is recomputed on every run, so you get
one place to look instead of a new ticket per entity.
Each row says how the denials arrived: a steady rhythm reads
as a misconfigured client, a burst reads differently. When there are
too few timestamps to support that label, it is withheld and the
shortfall is stated.
06 · A clear gap becomes a draft MR In development
Append one stanza to one policy. Nothing else.
For a widen cluster with exactly one candidate policy and one
operation, the assistant renders the change against the policy file
in git: it adds the missing capability to an existing
path block, or appends a new block for the denied path.
The rest of the file is left byte for byte as it was.
This path is built and switched on, but has not yet fired on a real
production denial, which is why it is marked in development.
07 · What it will never widen
A safety layer that fails closed.
Every finding passes one guard before it is emitted. A widen can
never target an escalation surface (sys/,
auth/, identity/, CA and key management),
never grant sudo, never touch an audit device, and
never edit the protected root policy.
If the renderer is unsure about anything, the finding stays an
issue.
08 · Did the grant land?
The next run checks for successes, not just silence.
For each entity the assistant also looks for its successful
responses in the same window. "The denials stopped and the service
now reads the path" is visible from the issue itself.
That lookup only runs when the entity is a real, unique Vault entity
id. A shared login name or IP address would mix up clients, so the
lookup is skipped and the issue says so.