Log streaming
Ship the audit log to Splunk, Elastic, Datadog, Axiom, Loki or any HTTP collector as it is written, and how batches are retried or dropped.
Log streaming ships the audit log to a SIEM or log store as it is written, the way Tailscale’s log streaming ships the configuration log. Every event the server records is queued for each stream, batched, encoded in the shape the destination expects and posted. The audit log itself is never held back for a sink: a sink that stays down loses the batch after a few retries, and the loss is counted on the stream.
Setting one up
Create the stream from the Log streams tab of the console’s
Integrations page, with the CLI, or through /api/v1/log-stream:
$ slopscale log-streams create --name siem --destination splunk \
--url https://splunk.example.com:8088/services/collector/event \
--token 11111111-2222-3333-4444-555555555555
The URL is subject to the same outbound request rules
as a webhook: no loopback or link-local sink. The token is stored and never listed again; an update that leaves it empty
keeps the stored one. slopscale log-streams test ships one synthetic entry
(logstream.test) and reports the sink’s answer, and the list shows the
newest batch’s status with how many entries the sink has accepted and how
many were dropped over the stream’s life. A stream can be disabled to keep
its settings without shipping.
Managing streams needs the logs:configuration scope, which the owner, the
admins, the network admin and the IT admin hold; reading them needs
logs:configuration:read, which an auditor also holds.
Entries
A sink receives one object per audit event:
{
"time": "2026-09-12T09:00:00Z",
"type": "configuration",
"tailnet": "example.ts.net",
"id": 1204,
"action": "user.role.set",
"actor": { "kind": "api_key", "id": "3", "name": "alice" },
"target": { "kind": "user", "id": "5", "name": "bob" },
"outcome": 200,
"detail": { "role": "admin" },
"remoteAddr": "203.0.113.9"
}
id is the event’s ID in the audit log, so a finding in the SIEM can be
traced back with slopscale audit or the console. type is always
configuration: the traffic monitor is not streamed; read
it through its API.
Destinations
| Destination | URL | Credential | Body |
|---|---|---|---|
http |
any collector that takes JSON | optional, sent as Authorization: Bearer |
a JSON array of entries |
splunk |
the HTTP Event Collector endpoint | the HEC token, as Authorization: Splunk |
one HEC event per entry, sourcetype slopscale:configuration, host the tailnet |
elastic |
the index’s _bulk URL |
optional API key, as Authorization: ApiKey |
a bulk request; each document is the entry with @timestamp |
datadog |
the logs intake of your site (/api/v2/logs) |
the API key, as DD-API-KEY |
the entry’s fields with ddsource, service, ddtags (type, tailnet, stream) and a message line |
axiom |
the dataset’s /ingest URL |
the API token, as Authorization: Bearer |
a JSON array of entries with _time |
loki |
the push API (/loki/api/v1/push) |
optional, sent as Authorization: Bearer |
one stream labelled job=slopscale, type, stream and tailnet; each value is the entry as a JSON line |
The http destination fits Cribl, Panther, a Vector or Fluent Bit HTTP
source, or your own receiver. Splunk, Datadog and Axiom refuse a stream
without a token; the others take one when the sink is behind
authentication.
Delivery
A worker per stream takes entries off a queue of 4096, waits up to two seconds for more, and posts up to 100 at a time. A network error or a 5xx or 429 answer is retried after 2, 10 and 30 seconds; a 4xx is the sink’s verdict and is not retried. Redirects are not followed. A batch that is given up on is counted as dropped, as is an entry that found the queue full, and the last batch’s status stays on the stream. Nothing about a batch is kept beyond that: the audit log is the record, the stream a copy.
On shutdown the workers get five seconds to ship what they hold.