Flow Logs

Flow Logs record the network flows between Clients and Resources. Each flow captures the actor and Policy it was authorized under, when it started and ended, the network path it took, and the volume of data transferred in each direction. This makes Flow Logs the primary stream for incident response and forensic analysis, where the goal is to reconstruct network activity precisely.

Both sides of the tunnel report flows: the initiator is always the Client that opened the connection, while the responder is the device that answered for the Resource, either a Gateway or another Client for device pool Resources. Each device batch-uploads its flow data to the control plane through a dedicated ingest endpoint, but the authorization attribution, meaning the actor, Policy, and Resource, comes from the control plane rather than the device, so a device can't misattribute its own traffic. How flow uploads are authenticated explains how this works.

Flow logs are enabled by default. You can disable them on a per-Policy basis.

Flow log reporting to the admin portal is not supported for the Internet Resource. Emitting flow logs to STDOUT is the only supported way to capture flows for it.

Log IDs

Flow Log IDs start with f, and because the bits after the stream character are random, they carry no ordering meaning. Order entries by flow_start instead.

Flow lifecycle

A flow produces one entry that evolves over its lifetime: when the flow opens, the entry records the network tuples and attribution but no traffic counters yet, and when it closes, the same entry is updated in place with flow_end, last_packet, and the final packet and byte counts.

When delivered through a Log Sink, each flow is rendered as a start event and an end event sharing the flow's log_id: the start event is suffixed with -s and carries no counters, while the end event is suffixed with -e and carries the final totals. A flow that closes before its open state was ever delivered still yields both events.

Two-sided reporting

Each flow is reported twice on purpose: the initiator and the responder track it independently, report it with the same tuples, and label directions from the initiator's perspective, so the two records describe the same traffic and can be joined on their network tuples and time window.

This redundancy is the integrity mechanism for flow statistics. While the ingest token signs a flow's attribution, the packet and byte counters come from the reporting device itself, so trust in them rests on the two-sided cross-check rather than on any single report. If the initiator and responder report meaningfully different statistics for the same tuples within the same flow_start to flow_end window, treat that as a suspicious event worth a deeper look: one side may be tampering with its reports, or something between the two devices is interfering with traffic.

Fields

Authorization

FieldDescription
log_idUnique ID of the entry.
roleinitiator for the connecting Client, or responder for the Gateway or the answering Client for device pool Resources.
actor_id, actor_email, actor_nameThe actor the flow is attributed to.
policy_idThe Policy that authorized the flow.
policy_authorization_idThe specific authorization grant for this flow.
resource_id, resource_name, resource_addressThe Resource the flow was authorized for.
auth_provider_idThe authentication provider used to sign in.
authorized_at, authorization_expires_atWhen the authorization was granted and when it expires.

Device

FieldDescription
device_idThe device that reported the flow.
client_versionVersion of the Firezone Client.
device_os_name, device_os_versionOperating system of the device.
device_serial, device_uuidHardware identifiers, where available.

Network

FieldDescription
protocoltcp or udp.
domainThe resolved domain, for DNS Resources.
inner_src_ip, inner_src_port, inner_dst_ip, inner_dst_portThe tunneled connection's source and destination.
outer_src_ip, outer_src_port, outer_dst_ip, outer_dst_portThe WireGuard connection's source and destination.

The two 4-tuples describe the same flow at two different layers:

  • The inner tuple describes the connection inside the tunnel, the traffic as the application sees it: the source is the initiator's Firezone tunnel address, and the destination is the address and port the Resource was reached at. For DNS Resources, domain also records which FQDN the destination address was resolved from.
  • The outer tuple is the transport the flow is tunneled over, meaning the endpoints of the encrypted WireGuard packets on the underlying network. These are typically each side's public, NAT-translated address and port, or a Relay's address when a direct connection couldn't be established.

The outer tuple identifies the network path that carried the traffic, while the inner tuple identifies the endpoints that communicated over it.

Both sides record each tuple from the initiator's perspective, with the initiator side always the source. This means the initiator's and responder's records for the same flow carry matching tuples.

Timing and counters

FieldDescription
flow_start, flow_endWhen the flow started and ended.
last_packetWhen the last packet was seen.
rx_packets, tx_packetsPackets received and transmitted.
rx_bytes, tx_bytesBytes received and transmitted.

Flow Logs carry no geolocation data because, unlike the other streams, there is no subject IP to geolocate: the network tuples identify the endpoints directly. If you need location information for a device, correlate with Session Logs, which record a session every time a Client or Gateway connects to the control plane, including the connection's source IP and its resolved location.

How flow uploads are authenticated

When a Client is authorized to access a Resource, the control plane mints a per-authorization ingest token and hands one to each side of the tunnel. The token is stamped at authorization time with the complete attribution snapshot: the reporting device and its role, the policy authorization, Policy, Resource, actor, authentication provider, and the authorization's validity window.

The token is the sole authenticator for flow uploads. When the portal ingests a batch, every attribution field is copied from the verified token's claims, and the device itself supplies only the network tuples, timing, and counters. This is what prevents spoofing: a device can only report flows for authorizations it actually holds, so it cannot attribute traffic to another device, another actor, or a Resource it was never authorized to access. The strongest claim a malicious device can make is a false statistic about its own authorized traffic, and that is exactly what two-sided reporting exists to catch.

Reporting and spooling

While connected to Firezone, devices spool flow records to disk locally and upload them in batches, and on disconnect or shutdown they perform a best-effort flush of whatever is pending. Flows that couldn't be uploaded, for example from a laptop that went offline, remain spooled and are uploaded on the next connection.

A spooled flow stays uploadable until its ingest token expires, 30 days past its authorization's expiry, after which it is cleaned up on the device automatically to prevent unbounded log growth.

Flow logs are spooled at the following locations:

PlatformSpool location
Gateway/var/lib/firezone/flow_logs
Linux Client, GUI and headless/var/lib/dev.firezone.client/flow_logs
Windows Client, GUI and headlessC:\ProgramData\dev.firezone.client\flow_logs
macOS, iOS, and Android ClientsManaged inside the app's sandbox container.

While the Gateway or Client is stopped, it's safe to delete the contents of the spool directory to reclaim space, though any flows not yet uploaded are lost and won't appear in your Flow Logs.

Emitting flow logs to STDOUT

Independent of the hosted feature, Gateways and headless Clients can print flow records directly to STDOUT. This is useful for feeding flows into your own log pipeline, and it's the only supported way to capture flows for the Internet Resource:

  • Gateways: set FIREZONE_FLOW_LOGS=true or pass --flow-logs. The Gateway then tracks flows even when flow logs aren't enabled for your Policies, and emits each record to its log output. Flows tracked only because of this flag stay local, since uploading to Firezone is always controlled by the portal.
  • Headless Clients: flow records are emitted as structured log events under the flow_logs target. Include them in the Client's output with RUST_LOG=info,flow_logs=trace.

Example

A closed flow reported by a Client:

{
  "log_id": "f4e8a2c61b09d735e2a48b17",
  "role": "initiator",
  "actor_id": "7a1d9e3c-5b2f-4c8d-9e0f-1a2b3c4d5e6f",
  "actor_name": "Riley Engineer",
  "actor_email": "riley@company.com",
  "policy_id": "0a83588e-0d92-4a4e-a856-4b8f2f4a2f8e",
  "policy_authorization_id": "6c8e0a2b-4d6f-4a8c-9e0b-2d4f6a8c0e2d",
  "resource_id": "5c8f6f6e-7c1a-4d8b-9c9e-1c2c3d4e5f60",
  "resource_name": "GitLab",
  "resource_address": "gitlab.company.com",
  "auth_provider_id": "9d0c2f4e-8b6a-4c2d-8e1f-3a5b7c9d0e2f",
  "authorized_at": "2026-07-14T09:20:00.000000Z",
  "authorization_expires_at": "2026-07-14T19:20:00.000000Z",
  "device_id": "2b4d6f8a-0c1e-4a3b-8d5f-7e9a1c3b5d7f",
  "client_version": "1.4.0",
  "device_os_name": "iOS",
  "device_os_version": "17.4",
  "protocol": "tcp",
  "domain": "gitlab.company.com",
  "inner_src_ip": "100.64.0.1",
  "inner_src_port": 54321,
  "inner_dst_ip": "10.0.0.5",
  "inner_dst_port": 443,
  "outer_src_ip": "203.0.113.10",
  "outer_src_port": 51820,
  "outer_dst_ip": "198.51.100.5",
  "outer_dst_port": 51820,
  "flow_start": "2026-07-14T09:25:31.000000Z",
  "flow_end": "2026-07-14T09:26:01.000000Z",
  "last_packet": "2026-07-14T09:26:01.000000Z",
  "rx_packets": 100,
  "tx_packets": 80,
  "rx_bytes": 102400,
  "tx_bytes": 20480
}

Viewing and retention

Flow Logs live under LogsFlow Logs in the admin portal. You can also query them via the REST API's /logs endpoint with type=flow. Entries are kept for 90 days.


Need help? See all support options.

Found a problem with this page? Open an issue
Last updated: July 20, 2026