Logs
Two streams
| Stream | Target | Content |
|---|---|---|
| application log | everything but audit | startup, connections, warnings, errors |
| audit trail | audit | one line per operation, connection, refusal, ban, admin action |
Both go to stdout. With log.dir, they also go to two separate files.
[log]
[log]
level = "info"
format = "json"
audit = "all"
dir = "/var/log/craft-file-gate"
All the keys: Reference [log].
The volume of the trail: log.audit
log.audit removes only successes. A refusal, an error or an unknown
outcome is always written.
log.audit | Operation successes written | Ordinary connection_accepted, session_end |
|---|---|---|
all | all | yes |
changes | upload, delete, delete_recursive, rename, mkdir, rmdir, rmdir_recursive | no |
failures | none | no |
Always written, whatever log.audit: connection_rejected,
connection_rejected_summary, ip_banned, everything the admin API
writes, and the session_end of a cut session (administrator, shutdown,
ban, internal error).
The log level does not touch the trail: the audit target stays at info
whatever log.level.
Log files: log.dir
| File | Content | Retention |
|---|---|---|
craft-file-gate.log | the application log | retention_days (7 days) |
craft-file-gate-audit.log | the audit trail, and only it | audit_retention_days (90 days) |
- Rotation: on the first write of a new UTC day, and before a write
that would exceed
max_file_size_mb. The archive carries the moment it was opened,craft-file-gate.2026-09-27T00-00-00Z.log.gz, and holds only lines from that UTC day. - Retention: an archive goes once its day plus the retention has passed, judged on its name. The other files of the directory are not touched.
- Files in
0640,.gzarchives always complete; one directory per process.
In a container, stdout and the platform’s rotation (kubelet, journald,
Docker) remain the normal way; dir needs a volume there. The Helm chart
offers it: logFiles.enabled: true.
Repeated refusals: one summary line
An anonymous client that insists (scanner, misconfigured client) would
produce one line per attempt. Per door, address, reason and result,
the first refusal_summary_threshold refusals of a window of
refusal_summary_window_secs keep their line; the next ones are counted,
then summarized in one connection_rejected_summary line at the end of the
window, with suppressed, threshold and window_secs.
Only refusals that judged no credential are summarized:
missing credential, empty credential, malformed credential,
method disabled, verifier unavailable, banned, rate limit,
password checks saturated, password checks saturated for address,
shutting down. A wrong password always keeps its line.
Beyond refusal_summary_max_addresses tracked keys, the extra refusals
are summarized together (overflow=true).
SFTP session ends
The application line for the end of a session states its cause:
INFO line | Cause |
|---|---|
connection ended without a channel close — unregistering session | the client left |
SFTP channel closed — unregistering session | the client closed the channel |
session cut by an administrator — unregistering session | DELETE /admin/sessions/{id} |
session cut by the ban of its address — unregistering session | the address was just banned |
idle session cut by the server shutting down — unregistering session | shutdown, session with no transfer |
session cut by the server shutting down — unregistering session | shutdown, end of the grace period |
The trail carries one session_end line per session, with the same cause
in reason.
A connection that ends before the session (a client that does not speak
SSH, a negotiation with no common algorithm) writes INFO
SSH session ended: <reason>, with peer and client_version. These are
ordinary ends: alert on their number, not on each one.
What you will see
| When | Line |
|---|---|
| startup | INFO log filter in force, fields filter and source |
| startup | INFO audit trail filter in force (refusals and errors are always recorded), field audit |
startup with dir | INFO log files in force, both paths, the size and the retentions |
refusal_summary_* changed | INFO refusal summary settings reloaded |
| line that cannot be written (disk full) | metric craftfilegate_log_write_errors_total; one line on stderr at most per minute |