▸ Agent Skills
4 min read

Audit Logging

secretspec records every secret access to a local audit log so you can review, after the fact, what secret was accessed, when, by whom, with what reason, if supplied, which software integration called SecretSpec (0.20+), and what the outcome was. Auditing is on by default.

Secret values are never written to the log. Only metadata is recorded, and any credentials embedded in a provider URI are redacted.

Where the log lives

By default the log is written to the per-user state directory, one entry per line in JSON Lines format:

PlatformDefault path
Linux~/.local/state/secretspec/audit.log
macOS~/.local/state/secretspec/audit.log

(secretspec follows the XDG state-directory convention on macOS too, matching where it keeps its config, so the path is the same as on Linux. Set [audit] path to override it.)

The file is created with owner-only permissions (0600 on Unix), inside an owner-only directory (0700). The first time secretspec writes to it, it prints a one-time note telling you where the log is and how to turn it off.

What a record looks like

{  "v": 1,  "id": "386987e6-291f-4e8f-a08b-73db9d80897b",  "ts": "2026-06-04T17:04:00.893Z",  "session_id": "d59e0f0f-ed2f-456f-a2b6-be25a24b7ec7",  "seq": 0,  "action": "get",  "project": "my-app",  "profile": "production",  "key": "DATABASE_URL",  "provider": "keyring://",  "outcome": "found",  "reason": "deploy web frontend",  "caller": {    "name": "git",    "version": "2.51.0",    "operation": "credential_get",    "resource": "github.com"  },  "actor": { "user": "alice", "agent": "claude-code", "is_agent": true },  "version": "0.20.0"}
FieldMeaning
vSchema version of the record
idUnique id for this event
tsRFC 3339 UTC timestamp
session_idShared by every event from one secretspec invocation
seqMonotonic sequence within that invocation
actionThe operation: get, set, check, run, import, export, cache_clear / cache_refresh (0.17+), or delete (0.18+)
project / profileThe project and profile in effect
scopeThe named scope for a scoped check, run, or export; omitted otherwise (SecretSpec 0.17+)
keyThe secret name for single-secret actions (get/set, and delete in 0.18+); never its value
keysThe set of secret names for bulk actions (check/run/import/export)
commandFor run, the executed program (argv[0] only — never its arguments, which may contain secrets)
providerThe provider URI that served the access, with credentials redacted
outcomefound, missing, default, written, deleted (0.17+ cache clear), started (a run launched its command), or error
A cached route writing its local entry is recorded as cache_refresh/written, never as set: no authoritative store was written. Dropping an entry — cache clear, or an entry a write superseded — is cache_clear/deleted.
error_kindA non-sensitive tag when outcome is error
reasonThe reason supplied via --reason / SECRETSPEC_REASON / the SDK, if any
callerCaller-asserted software integration context: name, and optional version, operation, and non-secret resource (SecretSpec 0.20+)
actorThe OS user, the detected coding agent (if any), and whether this is an agent session

This pairs naturally with the require_reason policy: when that policy applies, SecretSpec requires the caller to state why before proceeding and records the supplied reason alongside the access.

Caller context answers what software requested access; reason answers why the user requested it. Caller context is informational, is not an authenticated identity, and never satisfies require_reason. Integrations must not place a credential or secret value in any caller field.

Reading the log

The log is plain JSON Lines, so any tool works (cat, tail -f, jq). The secretspec audit command reads it for you with filters and a readable summary:

# Last 20 entries, formatted$ secretspec audit -n 20
# Only `run` events for one project$ secretspec audit --project my-app --action run
# Raw JSON Lines, piped to jq$ secretspec audit --json | jq 'select(.outcome == "missing")'

Terminal window

Size cap

The log is a single file capped at 1 MiB by default. When it reaches the cap it is truncated and started fresh, so disk usage stays bounded without any log rotation to manage. This makes the log a size-bounded recent record rather than a complete, permanent history — it is not intended to satisfy long-term compliance retention on its own. Forward it to a central system if you need that.

Reliability

Auditing never blocks secret access. If the log cannot be written (for example, a read-only filesystem), secretspec prints a warning: to stderr and continues — your get, set, and run still work.

Configuration

Auditing is a per-machine concern, so it is configured in your user-global config (~/.config/secretspec/config.toml) under the top-level [audit] table — not in the project’s secretspec.toml. This means a repository you clone cannot turn off or redirect your audit log. See the configuration reference for all options. To turn it off:

[audit]enabled = false

~/.config/secretspec/config.toml


Last updated Oct 08, 2026