Security → Audit events

Audit events

Current status: shipped. The Central Server reports Tunnel lifecycle events to an organization-scoped audit API in real time, they are stored in a durable database keyed by organization, and organization members can view them in a filterable, paginated activity log. This is live today, not a roadmap item.

How it works

  1. The Central Server posts each lifecycle event to POST /api/audit/events, authenticated with a server-to-server API key.
  2. Events are stored durably, one row per event, indexed by organization ID and creation time.
  3. Organization members can view their own organization's events at Activity in the admin console, filterable by event type and paginated.
  4. Events can also be retrieved programmatically via GET /api/organizations/<organization_id>/audit-events, scoped strictly to that organization.

Event types reported today

Event typeWhen
client_connected / agent_connected / relay_connectedA Client, Agent, or Relay completes registration and nonce verification.
client_disconnected / agent_disconnectedA Client or Agent's WebSocket connection closes.
tunnel_requestedA Client requests a Tunnel, before policy evaluation.
access_deniedOrganization, Device, protocol, or Agent-access policy rejects the request.
duplicate_tunnelA duplicate connection is rejected while an authenticated session is already active.
p2p_tunnel_established / p2p_tunnel_failed / p2p_tunnel_endedDirect Tunnel negotiation succeeds, fails, or ends.
tunnel_started / tunnel_endedA Relay-mediated Tunnel becomes active or is torn down.
tunnel_error / relay_error / device_errorA Tunnel, Relay spawn, or Client/Agent-local failure occurs.

What each event contains

Every stored event carries: organization ID, event type, Tunnel/session ID, Client and Agent identifiers and hostnames, requesting username, Relay identifier and kind (if a Relay was used), the requested Service and port, transport (p2p or relay), a short reason code, a free-text message, the reporting Device's IP address (when available), and a timestamp.

Events do not contain WebSocket bearer tokens, private keys, SSH public-key material, nonce values, raw WebSocket payloads, or Service application traffic — the audit pipeline only ever receives the structured fields above, not arbitrary connection data.

One nuance worth stating precisely: the device_error event's message field is free text supplied by the reporting Client or Agent (truncated to 2000 characters) describing a local failure — for example a failed dial or a private-host substitution. It comes from an already-authenticated Device, not an arbitrary source, but it is not a strictly allowlisted structured field the way the other columns are. Avoid characterizing every field in the audit log as fully server-validated metadata; this one is reporter-supplied text.

Access and retention

Audit events are scoped to the reporting organization at both the ingestion and read paths; there is no cross-organization query surface. Viewing the activity log in the admin console requires an authenticated session in that organization.

For the underlying event-type constants and API implementation, see the app/api.py module in TunnelCrib admin and server/main.py in the engineering repository.