Skip to content

feat(cli): azureclaw audit tail — stream durable JSONL audit log (Slice 4b) - #289

Merged
Pal Lakatos-Toth (pallakatos) merged 1 commit into
devfrom
slice-4b-audit-tail-cli
May 13, 2026
Merged

Pal Lakatos-Toth (pallakatos) merged 1 commit into
devfrom
slice-4b-audit-tail-cli

Conversation

@pallakatos

Copy link
Copy Markdown
Collaborator

Closes Slice 4 DoD #7. Builds directly on Slice 4a (PR #287).

What

azureclaw audit tail <sandbox> shells into the sandbox's
inference-router container and tails the day-keyed JSONL file the
Slice 4a sink writes at /var/log/azureclaw/audit/{YYYY-MM-DD}.jsonl.

Flags:

flag default purpose
-l, --lines N 50 trailing lines to print (capped at 10000)
-f, --follow off tail -F semantics: stream as the router writes
--decision <state> — filter by AuditEntry.decision exact-match
--agent <id> — filter by agent_id exact-match
--action <substr> — filter by action substring
--date YYYY-MM-DD today (UTC) read a historical day file
--dir <path> /var/log/azureclaw/audit non-default AZURECLAW_AUDIT_DIR
--json off emit one JSON object per line instead of the pretty tree
-n, --namespace <ns> azureclaw-<sandbox> override pod namespace

Pretty render colours by decision (allowed/success green,
denied/flagged/rejected red, sanitized yellow), truncates long
agent_id and action with …, and prints a one-line header.

Wire contract

Matches audit_jsonl::JsonlRecord byte-for-byte: sandbox, seq, ts, agent_id, action, decision, prev_hash, hash. parseRow is
forward-compatible — extra fields are preserved on the row but ignored
by the renderer.

Safety

--date is regex-validated (^\\d{4}-\\d{2}-\\d{2}$) before
interpolation into the in-pod shell command. --dir is single-quoted
with embedded-quote escape. There is no --exec/raw-shell escape
hatch.

Tests

26 new vitest cases (cli/src/commands/audit.test.ts) cover:

  • parseLines boundary (default, valid, zero, negative, non-numeric, over-cap, cap-exact)
  • todayUtcKey formatting and zero-padding
  • validateDateKey accepting valid + rejecting metacharacter attempts
  • parseRow round-trip + forward-compat + malformed-JSON + wrong-type + missing-field paths
  • matchesFilters decision / agent / action / AND-combined / no-filter
  • renderHeader column set, renderPrettyLine content + truncation

Suite total: 679 passing (was 652 on dev). npm run typecheck +
npm run lint + npm run build all clean.

DoD coverage

Slice 4 DoD #7 ✅. Remaining 4b deferrals (cross-day search, jq-friendly
alias) tracked under Slice 4c.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

…ce 4b)

Closes Slice 4 DoD #7. Slice 4a (PR #287) gave the router a sandbox-local
JSONL audit sink at /var/log/azureclaw/audit/{YYYY-MM-DD}.jsonl; this
slice gives operators a first-class way to read it.

`azureclaw audit tail <sandbox>` shells into the sandbox's
inference-router container and tails the day-keyed JSONL file. Flags
mirror the practical operator workflow: --follow, --lines (cap 10000),
--decision / --agent / --action filters, --date YYYY-MM-DD for
historical files, --json for machine-readable output, --dir override
for non-default AZURECLAW_AUDIT_DIR.

The --date parameter is regex-validated (^\d{4}-\d{2}-\d{2}$) before
shell interpolation; shell-metacharacter injection attempts are rejected
at the CLI boundary.

26 vitest cases cover parseRow/parseLines/validateDateKey/matchesFilters/
renderPrettyLine, including malformed-line resilience (the tail keeps
going) and forward-compat for future AuditRow fields.

CLI test suite: 679 passing (was 652 — +26 audit, +1 elsewhere on dev).
typecheck + lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant