Audit Log
NORA records registry operations in a structured audit log. Each entry is a JSON object written to a single line (JSONL format).
Configuration
Section titled “Configuration”| Variable | Default | Description |
|---|---|---|
NORA_AUDIT_LOG | file | Output mode: file, stdout, both, or off |
[audit]mode = "file" # "file", "stdout", "both", "off"Modes:
| Mode | Behavior |
|---|---|
file | Write to {storage_path}/audit.jsonl |
stdout | Write to stderr (12-factor compatible, works with log aggregators) |
both | Write to both file and stderr |
off | Disable audit logging |
Log format
Section titled “Log format”Each line is a JSON object with these fields:
| Field | Type | Description |
|---|---|---|
ts | string | ISO 8601 timestamp (UTC) |
action | string | What happened — see Action types |
actor | string | Who triggered it — see Actor values |
artifact | string | Package, path or image reference; empty for pull, cache_hit and proxy_fetch |
registry | string | Registry type: docker, npm, pypi, maven, raw, etc.; admin for token mints, the reindex scope for reindex, * for retention |
detail | string | Additional context: blob/manifest for Docker, the curation rule and reason for curation_block, role and TTL for admin_token_mint, counts for retention-apply |
Example entries
Section titled “Example entries”{"ts":"2026-10-13T07:46:45.283Z","action":"push","actor":"ci","artifact":"team/a.txt","registry":"raw","detail":""}{"ts":"2026-10-13T07:46:47.990Z","action":"pull","actor":"proxy","artifact":"","registry":"raw","detail":""}{"ts":"2026-10-13T07:46:52.149Z","action":"push","actor":"api","artifact":"team/tiny:1.0","registry":"docker","detail":"manifest"}{"ts":"2026-10-13T07:46:53.645Z","action":"delete","actor":"ci","artifact":"team/b.txt","registry":"raw","detail":""}{"ts":"2026-10-13T07:47:03.543Z","action":"push","actor":"anonymous","artifact":"team/c.txt","registry":"raw","detail":""}{"ts":"2026-10-13T08:00:00.000Z","action":"retention-apply","actor":"scheduler","artifact":"12 versions","registry":"*","detail":"keys=36 bytes_freed=104857600 duration=1.2s"}Action types
Section titled “Action types”| Action | Description |
|---|---|
push | Client uploaded an artifact |
pull | Cargo, pub or Raw download served from local storage |
delete | Client deleted an artifact |
overwrite | Existing Raw artifact replaced |
mount | Docker cross-repository blob mount |
cache_hit | Request served from the proxy cache without contacting upstream |
proxy_fetch | Artifact fetched from an upstream registry |
curation_block | Download refused by curation |
reindex | Index rebuild requested through POST /api/v1/admin/reindex, or an RPM/Debian repository reindex |
admin_token_mint | Admin created an API token for a user through POST /api/v1/admin/tokens (artifact is the user; the token itself is never logged) |
retention-apply | Retention run deleted versions (one summary entry per run) |
Actor values
Section titled “Actor values”| Actor | When |
|---|---|
| username | Authenticated user for push, delete, overwrite and RPM/Debian reindex in Cargo, Maven, npm, PyPI, Raw, RPM and Debian; also for curation_block, admin reindex and admin_token_mint. An API token records its owner’s username |
anonymous | The same operations when the request carries no identity (authentication disabled or anonymous access) |
api | Docker push, delete and mount — Docker writes do not record the username |
proxy | pull, cache_hit and proxy_fetch |
scheduler | retention-apply from the built-in retention scheduler |
cli | retention-apply from nora retention-apply |
File location
Section titled “File location”When using file or both mode, the audit log is written to:
{NORA_STORAGE_PATH}/audit.jsonlDefault: data/audit.jsonl (relative to the working directory).
The file is created automatically. Parent directories are created if they do not exist.
Docker Compose example
Section titled “Docker Compose example”services: nora: image: ghcr.io/getnora-io/nora:latest environment: NORA_AUDIT_LOG: both NORA_STORAGE_PATH: /data volumes: - nora-data:/dataThen tail the log:
docker exec nora tail -f /data/audit.jsonl | jq .Forwarding to a log aggregator
Section titled “Forwarding to a log aggregator”With stdout mode, audit entries go to stderr alongside regular logs. Use your container orchestrator’s log driver to forward them:
# Filter audit entries from container logsdocker logs nora 2>&1 | grep '"action":' | jq .For dedicated file-based forwarding (Filebeat, Promtail, Vector):
filebeat.inputs: - type: log paths: - /data/audit.jsonl json.keys_under_root: true json.add_error_key: trueQuerying the audit log
Section titled “Querying the audit log”# All pushes todaygrep '"action":"push"' /data/audit.jsonl | jq .
# Everything user "ci" pushed, overwrote or deleted# (Docker writes are recorded with actor "api", not the username)jq -c 'select(.actor == "ci")' /data/audit.jsonl
# Downloads refused by curationjq -c 'select(.action == "curation_block")' /data/audit.jsonl
# Count operations per registryjq -r '.registry' /data/audit.jsonl | sort | uniq -c | sort -rnSee Also
Section titled “See Also”- Settings —
NORA_AUDIT_LOGenv var - Prometheus Metrics — quantitative monitoring