Docs

Log

Redaction and trust

What keeps the Log safe and honest: secrets are redacted on the way in and on the way out, every line says how far its attribution can be trusted, a pull request cannot redirect events, and credentials never sit in the config.

Redaction
Provenance
Default branch
Env credentials

About redaction and trust

Five safeguards protect the Log:

  1. Redaction. Secrets are removed and home paths rewritten before a line is written, and again before it is sent or read.
  2. Provenance. Every schema 2 and 3 header says how far its attribution can be trusted, in meta.provenance.
  3. A trusted destination. An unattended run takes its destination from the default branch only, so a pull request cannot redirect telemetry.
  4. Credentials from the environment. Server credentials come from environment variables, never from the committed config.
  5. Proven producers. Tests prove every declared event has a real producer, and every family is written only from the code that owns it.

How it works

Redaction runs at two boundaries: when a line is written, and when a stored line is sent to a server or read back. If one boundary misses a pattern, the other catches it. It walks every string in the whole event, header included. It is pure: the home directory is passed in.

What is redacted

Patterns are applied in this order:

#MatchesBecomes
1A GitHub token (gho_, ghp_, ghs_, ghu_, ghr_, github_pat_)<redacted>
2A 40-hex GitHub token, only after token, GITHUB_TOKEN, PAT, api_key or api_token and a : or =Keyword kept, value <redacted>
3Bearer <token><redacted>
4An AWS access key AKIA…<redacted>
5A Slack token xox[baprs]-…<redacted>
6A Stripe key: sk_, pk_ or rk_ followed by live_ or test_<redacted>
7An Anthropic key sk-ant-…, or an OpenAI-shaped sk-…<redacted>
8An npm token npm_…<redacted>
9A JWT eyJ….….…<redacted>
10A PEM private-key block<redacted>
11Basic auth in a URL, https://user:pass@https://<redacted>@
12A secret, password, passwd, api_key, access_key, client_secret or private_key assignment of 16 or more charactersKeyword kept, value <redacted>
13A string starting with your home directory~ plus the rest

Other fields are safe by design:

  • meta.machine is a sha256 of the hostname, never the name.
  • gate · checked stores a digest of the check's inputs (input_fingerprint), never the inputs, and a structured reason code, never the check's message.
  • driver_exited.error_class is an error code or class name, never a message.
  • dispatch_failed.usage_limit never stores the vendor's message.
  • operation.field_names and custom-event refusals never carry values.
  • handoff carries no evidence or transcript. requested_decision is cleaned the same way as the public pause comment.
  • A finding's description is redacted and capped at 1000 characters.

Provenance

meta.provenance says how much to trust a line's attribution. From most to least trusted:

ValueMeaningSet by
parent_attributedThe parent that started this exact child vouches for itRole dispatch, for its own dispatch-side events
env_correlatedAn environment variable the worker controls named a role or taskThe header, when VINAYA_ROLE or VINAYA_TASK is set
self_reportedText the writer reported about itself; proves no authorizationPlanned
unavailableNo basisThe header, by default

Rules:

  • No silent upgrade. The header never raises provenance on its own. Only a caller that passes a value can assert a stronger one, and the normal write path passes none.
  • Branch-derived Issues stay untrusted. An Issue number taken from the checked-out branch (or GITHUB_HEAD_REF in CI) forces unavailable, even when VINAYA_ROLE is set.
  • The forge confirms the branch. An Issue number taken from the branch is checked with the forge, and a number the forge denies is never recorded. If the forge does not answer, the branch's number stays, still unavailable.
  • Task tools never guess. vinaya task-tools serve turns the branch lookup off, so its events keep issue: null.
  • Roles come from the environment only. subject.role comes only from VINAYA_ROLE; a caller cannot declare its own. meta.actor_id is an unchecked claim.
  • Old CLIs are low trust. Readers treat any line from a CLI older than 0.33.0 as low trust; see Telemetry.

A destination a pull request cannot change

An unattended process is every dispatched role's child process, and any run with VINAYA_UNATTENDED set. It honours a working-tree logs.folder or logs.url only when the default branch declares the identical value. Otherwise it falls back to the default folder, never to "no destination".

  • For a server, the default branch's own headers are used, so a pull request that edits only headers is caught too.
  • With no local logs, the default branch's value applies.
  • logs.folder must be absolute and outside the repository, for every caller.
  • vinaya doctor treats a CI host as unattended whatever VINAYA_UNATTENDED says.
  • Not held to this rule: logs.events (not a destination) and logs.readHeaders (this machine's read credential for a url that is already approved).

The logs destination is one of the keys read from the default branch for an unattended run; see Vinaya keys and Trust.

Credentials from environment variables

  • ${VAR_NAME} in logs.headers and logs.readHeaders is filled in from the sending process's own environment when the event is delivered, never from a worker's environment.
  • An unset variable becomes an empty string. The server then refuses authentication; nothing crashes.
  • If any referenced variable is missing or empty, there is no credential. CI then records nothing, and vinaya log selftest names the variable.
  • vinaya doctor's [log-credential] reports, for each variable, whether it is set, never its value. Printed findings strip the header value, the credential without its scheme, each referenced variable's value, and any credentials in a URL or its query.

Proven producers

Tests refuse a change that would let an event or a family drift:

Test refusesExample
A declared event with no producerA family and event pair in the schema that no producer claims and that is not listed as an exemption; or a producer that claims an event its code does not write
A family written from the wrong placeA file writing a family it does not own (read from the code itself); or an owner entry naming a file that no longer writes it
A second writer of the queueAny file other than the Log sink opening the retry-queue path
An unlisted entry pointA hook, workflow step, agent-runtime hook or check with no inventory entry; an entry whose path is gone; a route whose command lacks its token.
A leaking testA test producer that could reach the Vinaya repository's own destination

Events exempt, kept in the schema only so older lines still read: effect · attempted, effect · observed, and forge_write · validated, refused and written.

The producers are role dispatch, round assessment, the dev-review loop driver, the check runner, the effect executor, the broker, the custom-event emitter, the task-tools cancel and resume handlers, and pause and resume.

Configuration

Key or variableEffect
logs.folder, logs.urlThe destination, held to the default-branch rule
logs.headers, logs.readHeadersCredentials, as ${VAR}
VINAYA_UNATTENDED, VINAYA_ROLEMake a caller unattended
VINAYA_ROLE, VINAYA_TASKProduce env_correlated provenance

What's next