audit
audit
¶
Append-only JSONL audit log for credential-bearing broker requests.
Records one line per phantom-token-validated proxy call so the operator can
answer "what did this subject's credentials get used for?" after the fact.
The schema is deliberately small — caller-supplied labels (scope,
subject, credential_set, provider) plus request shape
(method, path, status, outcome, duration_ms). No
request bodies, no response bodies; the broker is a transparent proxy and
audit's job is forensic context, not deep packet capture.
One JSONL file under the vault state dir, shared across every subject the broker has ever served. The sandbox makes no per-subject layout decisions because it doesn't model "subject" as anything other than an opaque label — read-side filtering is the orchestrator's job.
Soft-fail semantics throughout: a missing parent dir, a full disk, an
EACCES on the file all degrade gracefully to a single WARNING log
line. The proxy's primary job is forwarding, not auditing; an audit
write must never block or kill a credential-bearing request.
AuditWriter(path)
¶
Append-only JSONL writer with one process-wide async lock per file.
Holds a single line-buffered append handle for the lifetime of the
broker — opening per request would dominate the sub-millisecond proxy
hot path. Concurrent aiohttp handlers serialise through an
asyncio.Lock so their JSONL bytes can't interleave.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Where to write. Parent directories and the file itself are
created lazily on first write so the broker can be started
against a vault dir that doesn't exist yet (fresh installs,
tests using |
required |
Bind the writer to path without touching the filesystem.
Source code in src/terok_sandbox/vault/daemon/audit.py
write(entry)
async
¶
Append one JSON-encoded line. Soft-fail on every error path.
Marshals entry with compact separators (no whitespace) and
writes json + "\n" under the per-writer lock. Line-buffered
text mode flushes after every newline, so a crash mid-broker
loses at most the most recent partial line.
The actual file write goes through asyncio.to_thread so a
slow disk / stuck mount can't stall the broker's event loop —
the write happens on the default executor, the lock keeps
ordering across concurrent callers, and the soft-fail
OSError path stays the same shape as the sync version.
Serialization failures (a non-JSON-serializable field slipping into entry) are also soft-failed — a malformed audit entry must not kill a credential-bearing request.
Source code in src/terok_sandbox/vault/daemon/audit.py
close()
async
¶
Close the underlying handle if open. Idempotent.