Skip to content

Audit Log

NORA records registry operations in a structured audit log. Each entry is a JSON object written to a single line (JSONL format).

VariableDefaultDescription
NORA_AUDIT_LOGfileOutput mode: file, stdout, both, or off
config.toml
[audit]
mode = "file" # "file", "stdout", "both", "off"

Modes:

ModeBehavior
fileWrite to {storage_path}/audit.jsonl
stdoutWrite to stderr (12-factor compatible, works with log aggregators)
bothWrite to both file and stderr
offDisable audit logging

Each line is a JSON object with these fields:

FieldTypeDescription
tsstringISO 8601 timestamp (UTC)
actionstringpush, pull, delete, cache_hit, proxy_fetch, overwrite, retention-apply
actorstringOrigin of the operation: api (HTTP request), scheduler (retention), or cli
artifactstringPackage name or image reference
registrystringRegistry type: docker, npm, pypi, maven, etc.
detailstringAdditional context (version, digest, size)
{"ts":"2026-05-18T10:30:45.123Z","action":"push","actor":"api","artifact":"myapp","registry":"docker","detail":"sha256:abc123"}
{"ts":"2026-05-18T10:31:02.456Z","action":"pull","actor":"api","artifact":"lodash","registry":"npm","detail":"4.17.21"}
{"ts":"2026-05-18T10:32:15.789Z","action":"delete","actor":"api","artifact":"old-image:v1","registry":"docker","detail":"manifest"}
{"ts":"2026-05-18T10:33:00.001Z","action":"cache_hit","actor":"api","artifact":"","registry":"maven","detail":""}
{"ts":"2026-05-18T10:33:05.200Z","action":"proxy_fetch","actor":"api","artifact":"","registry":"npm","detail":""}
{"ts":"2026-05-18T10:34:10.500Z","action":"overwrite","actor":"api","artifact":"libs/config.yaml","registry":"raw","detail":""}

Action types:

ActionDescription
pushClient uploaded a new artifact
pullClient downloaded an artifact from local storage
deleteClient deleted an artifact
cache_hitRequest served from local cache without contacting upstream
proxy_fetchArtifact fetched from an upstream registry
overwriteExisting artifact replaced (Raw registry)
retention-applyArtifact removed by retention policy

When using file or both mode, the audit log is written to:

{NORA_STORAGE_PATH}/audit.jsonl

Default: data/audit.jsonl (relative to the working directory).

The file is created automatically. Parent directories are created if they do not exist.

services:
nora:
image: ghcr.io/getnora-io/nora:0.9
environment:
NORA_AUDIT_LOG: both
NORA_STORAGE_PATH: /data
volumes:
- nora-data:/data

Then tail the log:

Terminal window
docker exec nora tail -f /data/audit.jsonl | jq .

With stdout mode, audit entries go to stderr alongside regular logs. Use your container orchestrator’s log driver to forward them:

Terminal window
# Filter audit entries from container logs
docker logs nora 2>&1 | grep '"action":' | jq .

For dedicated file-based forwarding (Filebeat, Promtail, Vector):

filebeat.yml
filebeat.inputs:
- type: log
paths:
- /data/audit.jsonl
json.keys_under_root: true
json.add_error_key: true
Terminal window
# All pushes today
grep '"action":"push"' /data/audit.jsonl | jq .
# All Docker operations by user "admin"
jq -c 'select(.registry == "docker" and .actor == "admin")' /data/audit.jsonl
# Count operations per registry
jq -r '.registry' /data/audit.jsonl | sort | uniq -c | sort -rn