Skip to content

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.

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.

{
"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.

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:

Terminal window
# 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")'

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.

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.

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:

~/.config/secretspec/config.toml
[audit]
enabled = false