Manage
Logs and webhooks
The tamper-evident audit log, network logs, webhooks for events, and streaming logs to your own tools.
Audit log
Logs → Audit log records every change, sign-in and refused request in your workspace: who did it, when, from which address, and what changed. Owners and admins can see it.
- Filters: Machines, Access, Apps & services, Keys, People & sign-ins; by person; by time (24 hours, 7 days, 30 days, 90 days); and search.
- Export CSV: up to 10,000 events.
- Kept: 90 days.
- Tamper-evident: each entry includes a fingerprint (SHA-256) of the one before it, so editing, removing or reordering entries breaks the chain. Click Verify integrity to check it.
Event names look like machine.approved, key.created, policy.updated, member.removed and account.signed_in. When the API refuses a change, it's recorded as <area>.failed.
Network logs
Logs → Network logs shows which machines talked to which, and how much. It's off by default; an owner or admin turns it on.
- Recorded: for each connection, the protocol, source, destination, packets and bytes each way, packets blocked by access rules, and whether it was between machines, to a subnet, or to the internet. Contents are never recorded.
- Reported: by the machines themselves, every half minute. Machines on the system WireGuard engine don't report.
- Kept: 30 days.
- Filters: 15 minutes to 7 days; machines, subnets or internet; search; auto-refresh; Export CSV.
Network logs need app version 0.5 or newer.
Webhooks
Logs → Webhooks sends events to a URL as they happen.
| Setting | Options |
|---|---|
| Format | JSON, n8n, Slack, Discord, Google Chat |
| Events | Everything, or any of Machines, Keys, Members, Invites, Access controls, Apps, Services, Workspace settings, Webhooks, Log streams, Sign-ins |
| URL | https only; private and internal addresses are refused |
A JSON delivery looks like:
{
"id": "…",
"type": "machine.approved",
"time": "2026-10-28T10:42:00Z",
"tailnet": "…",
"actor": "amara@acme.com",
"ip": "203.0.113.24",
"target": { "type": "machine", "id": "…", "name": "db-prod" },
"details": {},
"summary": "…"
}Verify deliveries
JSON and n8n webhooks are signed with a secret (whsec_…), shown once and rotatable. Each request has:
X-Webhook-Event: <type>X-Webhook-Signature: t=<unix time>,v1=<hex>, wherev1is the HMAC-SHA256 of"<t>.<raw body>"with your secret
Check the signature, and reject requests older than 5 minutes:
import crypto from "node:crypto";
function verify(secret, header, rawBody) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
const a = Buffer.from(v1 ?? ""), b = Buffer.from(expected);
return fresh && a.length === b.length && crypto.timingSafeEqual(a, b);
}Send test sends a test.ping event.
Delivery and retries
A failed delivery is retried after 2 seconds, 10 seconds, 1 minute and 5 minutes. After 20 failures in a row the webhook is paused (Paused: failing). The last 50 deliveries are kept, and you can redeliver any of them.
Log streaming
Logs → Log streaming sends your audit log or network logs to a logging service.
| Destination | Authentication |
|---|---|
| Datadog | DD-API-KEY |
| Splunk HEC | Authorization: Splunk <token> |
| Elastic (bulk API) | Authorization: ApiKey <key> |
| Axiom | Authorization: Bearer <token> |
| HTTP (JSON) | Authorization: Bearer <token> |
Events are sent in batches of 100 (500 for network logs), when a batch fills or 5 seconds after its first event. Network log items have type: "network.flow". Failed batches are retried after 2, 10 and 30 seconds.
Limits
Up to 10 webhooks and 10 log streams per workspace.