Skip to content

Logs ​

The server writes its log to standard error. Output from moin-server admin goes to standard output, so the two never mix. There's no log file: systemd, Docker and other supervisors already collect standard error.

Level and format ​

toml
[log]
level = "info"  # error, warn, info, debug or trace
format = "text" # or "json", one object per line
LevelIncludes
errorThe server lost something and needs you: a failed write to its data directory, a certificate that won't load, a discovery storage failure.
warnSomething went wrong or looks hostile, and the server carried on: too many join attempts, a slow device disconnected, a slow shutdown.
infoStartup, devices connecting and leaving, and the audit log. The default.
debugConnections that never became a session, and other detail.
traceEverything.

level applies to Moin's own messages. The libraries it's built on only log warnings and errors, and the audit log is always kept, whatever the level.

For finer control, set the RUST_LOG environment variable. It replaces the level entirely, using EnvFilter syntax:

sh
RUST_LOG=warn,moin_coordinator=debug,audit=info moin-server --config moin-server.toml

Keep audit=info in it if you want the audit log. format still comes from the configuration file.

Text output is coloured when it goes to a terminal and NO_COLOR isn't set.

Startup ​

moin server starting shows the version, the server ID (endpoint_id), the path of its state file (state), the room list (catalog: configured or managed), max_connected_devices, the access mode, and whether a password, invitations and a bootstrap secret are set. It never shows their values.

It's followed by moin server listening with the bound addresses, or with [server], by stable coordinator started with the origin, the web listener, the UDP sockets, and whether relay, qad and pkarr are on.

Devices connecting ​

Messages about one device's connection carry its connection_id and device_id, and once it has joined, a session_id. In text they appear as a connection{…} prefix; in JSON they're in a span object.

MessageLevelDetails
session openedinfouser_agent: the app and platform, such as Moin/2026.1 (macos)
session rejectedinfo, or warn for RateLimitedreason, below
session endedinforeason: Disconnected, SessionReplaced, ProtocolViolation, ServerShutdown or AccessRevoked
disconnecting a closed or slow control connectionwarn
incoming connection failed, control stream not opened, session opening not received, control connection endeddebug

Reasons a device is turned away:

ReasonMeaning
InvalidNicknameThe app sent a nickname the server doesn't accept.
ServerFullmax_connected_devices is reached.
ServerUnavailableThe server couldn't use its data directory, or is shutting down. Look for an error just before it.
AuthenticationRequiredThe server is protected and the device isn't a member.
PasswordRequired, InvalidPasswordNo password, or the wrong one.
InvalidInvite, InviteExpired, InviteExhaustedThe invitation is unknown or revoked, expired, or used up.
RateLimitedToo many join attempts; see Guessing protection.

Audit ​

Every administrative change is logged as privileged action, whether it succeeded or was refused, and whether it came from the app or from moin-server admin. Listings and status aren't logged.

FieldMeaning
actorThe device ID that made the change, or local for moin-server admin.
actionclaim_admin, claim_invite, set_role, clear_role, remove_device, create_invite, revoke_invite, revoke_all_invites, move_member, create_room, edit_room, delete_room, configure_access, update_access_policy, enroll_device, revoke_device or revoke_all_devices.
subjectThe device, room or invitation acted on, or the one created.
roleThe role given, for set_role, and the role an invitation carries, for create_invite and claim_invite.
roomThe destination, for move_member; absent when the member was moved out of their room.
revoked_inviteThe invitation revoked along with the device, for remove_device.
outcomeok, or why it was refused, such as PermissionDenied.

claim_invite is a device joining with an invitation that has a role: it became a member and got the role. Only successful ones are logged here; a refused join logs session rejected instead.

Devices disconnected by a change also log session ended with AccessRevoked.

To keep only the audit log, select target == "audit" in JSON output, or lines containing audit: in text.

What the log leaves out ​

Moin's own messages identify devices by device ID, and rooms and invitations by ID. They never contain nicknames, room or community names, IP addresses, passwords, invitation links or the bootstrap secret. Configuration errors give the line and column, never the line's contents.

Messages from the libraries Moin uses can include network addresses. Keep that in mind when choosing how long to keep logs.