Schema: AuditLogEntry
File: audit-log-entry.json
Purpose
One line of the opt-in audit log, and one item of data.entries returned by tool audit-log. It records a single command invocation so that an incident review can reconstruct what an agent ran, with which arguments, and how it ended, long after the response itself is gone (§33).
Three design decisions shape the type:
- Same values as the response.
timestamp,command,exit_code,duration_ms,request_id, andtrace_idrepeat the invocation'smetafields, so an entry joins to a captured response or trace byrequest_idortrace_id - Warnings as codes.
warningskeeps only the codes of the response'swarnings[]. Facts that must be queryable later, such as an over-privileged credential or disabled injection protection, survive without copying message text - Bounded by construction. An entry never exceeds 16 KiB; oversized
argsvalues are replaced with[TRUNCATED]andtruncatedis set, so one entry is always one write
AuditLogEntry
| Field | Type | Required | Description |
|---|---|---|---|
timestamp |
string ISO 8601 date-time | yes | Invocation start time, matching meta.timestamp |
command |
string | yes | Command path equal to meta.command, space- or dot-separated as the framework spells it (config set, config.set) |
args |
object | yes | Parsed argument map after REQ-F-034 redaction; never the raw argv. A passthrough command (REQ-C-031) records its forwarded argv as "argv": "[OMITTED]" |
exit_code |
integer 0–255 |
yes | Process exit code, matching meta.exit_code |
duration_ms |
integer | yes | Wall-clock milliseconds, matching meta.duration_ms |
request_id |
string | yes | Unique invocation identifier, matching meta.request_id |
trace_id |
string | when TOOL_TRACE_ID is set |
Trace ID propagated from TOOL_TRACE_ID (REQ-F-025) |
session_id |
string | when the session variable is set | Agent session identifier, recorded verbatim from the framework's one documented session variable (<PREFIX>SESSION_ID unless it already reads a prefixed one) |
effects |
object | mutating streams | Events per effect value of a mutating stream (REQ-O-004): its summary line's effects, or the events emitted before a failure |
warnings |
string[] | yes | Codes from the response's warnings[], in emission order; may be empty |
truncated |
boolean | no | true when args values were replaced to fit 16 KiB |
Examples
Successful deploy with a redacted secret
{"timestamp": "2026-03-17T14:00:01Z", "command": "deploy", "args": {"env": "prod", "token": "[REDACTED]"}, "exit_code": 0, "duration_ms": 1247, "request_id": "req-001", "trace_id": "abc123", "warnings": []}
Dry run under an over-privileged credential, inside an agent session
{"timestamp": "2026-03-17T14:05:22Z", "command": "delete", "args": {"resource_id": "r-42", "dry_run": true}, "exit_code": 0, "duration_ms": 8, "request_id": "req-002", "session_id": "s-1", "warnings": ["CREDENTIAL_OVER_PRIVILEGED"]}
Mutating stream: one entry for the whole run
{"timestamp": "2026-03-17T14:06:10Z", "command": "sync-users", "args": {"source": "users.csv"}, "exit_code": 0, "duration_ms": 412, "request_id": "req-005", "effects": {"created": 2, "noop": 1}, "warnings": []}
sync-users streamed three events (REQ-O-004); the entry repeats its summary line's effects instead of logging each event.
Large payload truncated to fit the entry cap
{"timestamp": "2026-03-17T14:07:00Z", "command": "import", "args": {"source": "s3://bucket/data.json", "raw_payload": "[TRUNCATED]"}, "exit_code": 0, "duration_ms": 5310, "request_id": "req-003", "warnings": [], "truncated": true}
Valid: passthrough command, forwarded argv omitted
{"timestamp": "2026-03-17T14:09:30Z", "command": "ingest", "args": {"argv": "[OMITTED]"}, "exit_code": 2, "duration_ms": 412, "request_id": "req-004", "warnings": []}
ingest hands its arguments to another tool's parser (REQ-C-031). Without a schema for them the framework cannot redact them, so it records none; exit_code 2 is the delegated tool's own.
Invalid: raw argv instead of the parsed map
{"timestamp": "2026-03-17T14:00:01Z", "command": "deploy", "args": ["--env", "prod", "--token", "abc123"], "exit_code": 0, "duration_ms": 1247, "request_id": "req-001", "warnings": []}
Violation: args must be an object. Raw argv cannot be redacted reliably and leaks the token.
Common mistakes
- Logging raw argv. Redaction (REQ-F-034) works on named arguments; a positional token or
--token=abcinside a string list slips through - Writing
trace_id: nullwhenTOOL_TRACE_IDis unset. Omit the field, asmeta.trace_iddoes - Copying full warning objects.
warningsholds codes only; message text is for humans and bloats every line - Truncating the serialized line. Cutting the JSON text produces an invalid line; replace values in
argsand settruncated commanddiffers frommeta.command. The entry repeatsmeta.commandexactly; a framework that answersconfig.setinmeta.commandmust not logconfig set, or the reverse, and must not mix both spellings in one log
Agent interpretation
- Join an entry to a response or trace by
request_idortrace_id, never bytimestamp exit_codenon-zero withargs.dry_runorargs.validate_onlyset means nothing was changed; the entry still shows what was attemptedtruncated: truemeans someargsvalues are[TRUNCATED]; do not replay the invocation from the entryeffectsmarks a mutating stream and counts its events; with a non-zeroexit_code, the counts cover the events completed before the failure. An absenteffectson an entry written before AuditLogEntry 1.1 means unknown, not zeroargs.argvequal to[OMITTED]marks a passthrough command (REQ-C-031): the forwarded arguments were never written, and itsexit_codeis the delegated tool's, so a2does not rule out side effects- A
[REDACTED]value is a declared secret; never try to recover it from other sources warningscontainingINJECTION_PROTECTION_DISABLEDmarks an invocation whose external data was returned untagged; treat its outputs as untrusted during review- An empty result from
tool audit-logmeans no matching invocation was recorded while the log was enabled; a disabled log fails withAUDIT_LOG_DISABLEDinstead
Coding agent notes
Type representation
- Generate args as a string-keyed map of JSON values, not as a typed struct; its keys are the command's argument names
- Generate warnings as a list of strings and trace_id, session_id, effects, and truncated as optional fields that are omitted, not null
- Generate effects as a string-keyed map of non-negative integers; its keys are effect values such as created or would_create
Construction
- Build the entry from the same values the envelope factory writes to meta, after the exit code is known and before the response is emitted
- Serialize once, check the byte length, and replace the largest args values with [TRUNCATED] until the line fits 16 KiB
Tests to generate
- Every written line validates against audit-log-entry.json
- A secret argument appears as [REDACTED]
- A 50 MB argument yields one entry of at most 16 KiB with truncated: true
Implementation notes
additionalProperties: falsekeeps the entry shape predictable fortool audit-logfilters and external log shippers; command-specific facts belong inargs- The 16 KiB cap keeps each entry a single
O_APPENDwrite well under common pipe and filesystem atomicity limits - The log file is created with mode
0600; entries still carry unredacted hostnames, ids, and paths - The entry is not wrapped in a
ResponseEnvelope; the bufferedtool audit-loganswer (the default, or--no-streamwhen the command is streaming-default) returns entries insidedata.entriesof a normal envelope withmeta.pagination
Related
| Document | Relationship |
|---|---|
| REQ-O-030 | Consumes: defines the audit log and the audit-log command that emit this type |
| REQ-C-031 | Consumes: a passthrough command records its forwarded argv as [OMITTED] |
| schemas/response-envelope.md | Provides: the meta values each entry repeats and the envelope around data.entries |
| §33 Observability & Audit Trail | Sources: the failure mode this schema addresses |