Audit: reading the trail
The audit trail says who did what, on which file, and whether it worked;
refusals and errors included. The vocabulary of the lines (actions, fields,
reason) is in the reference.
{"timestamp":"2026-10-07T14:41:25.402Z","level":"INFO","target":"audit","fields":{"message":"audit: operation succeeded","source":"sftp","action":"upload","session_id":"550e8400-e29b-41d4-a716-446655440000","remote_addr":"192.168.1.42","username":"alice","path":"/in/rapport.csv","backend":"disque","new_path":"","count":524288,"replaced":"no","removed":"","result":"success"}}
Where it goes
| Setting | Where the trail goes |
|---|---|
| default | stdout, mixed with the application log, one JSON line per event, target = audit |
[log] dir | in addition, craft-file-gate-audit.log (the trail alone) next to craft-file-gate.log |
[log] stdout = false | the files only |
[telemetry] | each operation line is also an event of the operation’s span |
Rotation, retention and level: Logs.
What it keeps
log.audit (all, changes, failures) only removes successes; repeated
anonymous refusals are summarized as connection_rejected_summary. See
The volume of the trail
and Repeated refusals.
Severity
| Level | Lines |
|---|---|
WARN | what only a credential holder produces: a denied, error or unknown operation; a refusal after an accepted credential; a refused admin action; ip_banned |
INFO | what any peer produces: the other connection_rejected; connection_accepted, session_end, the summaries, the successes |
An alert on the trail’s WARN lines therefore does not fire for an
anonymous scanner.
Filtering
In the console, Logs tab, source audit (permission audit): filters
by level, period, text and field:value; each read writes an
audit_read line. See Admin API and console.
# the admin console API
curl -H "Authorization: Bearer $TOKEN" \
'http://serveur:8081/admin/logs/audit?field=username:alice&level=WARN&since=2026-10-07T00:00:00Z'
# the files; without [log] dir: docker logs ... | jq -c 'select(.target == "audit")'
jq -c 'select(.fields.username == "alice")' /var/log/craft-file-gate/craft-file-gate-audit.log
| Question | Filter |
|---|---|
| everything an SFTP session did | .fields.session_id == "<id>" |
| overwritten files | .fields.action == "upload" and .fields.replaced == "yes" |
| ACL refusals | .fields.reason == "acl" |
| storage failures | .fields.result == "error" and .fields.backend == "<name>" |
| refusals and errors | .fields.result != "success" |
The text of a storage error is not in the trail: look for it in the
application log, at the same time, with the same path.
An incomplete trail
A line the server cannot write (closed pipe, full disk) is lost:
craftfilegate_log_write_errors_total counts it, stderr says so at most
once per minute, and the server keeps serving. Alert on
increase(craftfilegate_log_write_errors_total[5m]) > 0.