Skip to content

feat(cli): structured write-action audit log #131

Description

@chmmou

Cross-cutting prerequisite for the v0.2.0 write phase. Sibling of #109. Subset of #13.

Every destructive call (add_*, update_*, delete_*, reset_*, move_*) should leave a structured trace on stderr — independent of --verbose — so that a user can reconstruct after the fact what was changed, against which resource, and what the KAS-API response was. Today there is no record beyond shell history.

Scope

  • A shared helper in internal/cli/ (or internal/audit/) that emits one line per dispatched write action with at minimum:
    • timestamp (RFC 3339 UTC),
    • resolved login (KAS account),
    • KAS action name (add_mailaccount, delete_dns_settings, …),
    • target identifier (mail login, DNS record id, FTP login, …),
    • outcome (success | failure:<fault_code>),
    • request-correlating field where the KAS API returns one (e.g. record_id from add_dns_settings).
  • Default output format: structured key-value on stderr (logfmt-ish) — easy to grep, no JSON-parser dependency for users.
  • A --audit-log <path> flag (and KAS_AUDIT_LOG env var) appends the same record as JSON Lines to a file when set; the file is created with mode 0600.
  • Records are emitted after the SOAP call returns, regardless of success — failed writes are arguably more important to log than successful ones.
  • Secrets (auth_data, KasFloodDelay tokens, passwords passed as parameters like mail_password) are never logged. Pre-flight redaction list lives next to the helper.

Relationship to existing logging

--verbose / -v today dumps SOAP actions on stderr for debugging. The audit log is orthogonal: it is always on for write actions, opinionated about the schema, and survives --quiet (if/when that flag exists). Read actions continue to produce no audit record.

Out of scope

  • A separate read-side audit log.
  • Remote sinks (syslog, journald, HTTP). The file path is enough; users can pipe stderr or tail -F the file.
  • The actual write endpoints — those live in the per-resource issues spawned from Write operations #13.

Status (codebase verified against main @ ceca090, 2026-05-17)

Helper + flag + env var + redaction + tests + docs landed in PR #165 (feat(cli): structured write-action audit log, merged ceca090) and are verified present in internal/cli/audit.go:

  • cli.AuditRecord (Time RFC 3339 nano UTC, Login, Action, Target, Outcome, Fields) + cli.OutcomeFor (success / failure:<kas_code> via api.AsError / failure).
  • cli.WriteAudit: always-on logfmt line on stderr (independent of --verbose); cli.OpenAuditFile appends JSON Lines, file created 0600 with an explicit re-Chmod (Windows-aware, mirrors the session store).
  • Global --audit-log flag + KAS_AUDIT_LOG env (cli.AuditLogPath, flag wins).
  • cli.RedactParams: explicit secret-key set (auth_data, kas_auth_data, *password, session, token) plus a password/passwd/secret/token/auth_data substring rule; tests assert no secret value reaches stderr or the JSON-Lines file.
  • Tests: redaction / outcome / logfmt / JSONL+0600 / flag-env precedence. Docs: docs/usage/destructive-writes.md "Audit log" section; CHANGELOG ### Added.

Three boxes are not closable in isolation: no #13 write command exists yet (so nothing emits a record end-to-end, and the redaction list cannot be exhaustively matched against per-endpoint write parameters that are not documented/wired). They transfer to the first write-endpoint PR, which calls WriteAudit after dispatch and will carry Closes #131.

Acceptance

Parent: #13. Depends on #109 (shares the destructive-command dispatch path) — #109 infrastructure landed in PR #164.


Closed by PR #167 (first #13 write endpoint, merged 1b56d1a): the mail-forward write slice (mail forwards add/update/delete) exercises this end-to-end through the shared cli.runWriteE runner. The deferred end-to-end / Closes boxes are now satisfied.

Metadata

Metadata

Assignees

Labels

area/cliCLI scaffold, subcommands, output formattingenhancementNew feature or requestphase/writeWrite/destructive API endpoints

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions