Docs / Audit streams
Stream your audit log to your own tools
Audit streaming sends your organization's audit events to a webhook, Splunk or Datadog as they are recorded, so your security team can search, alert and retain them where they already work.
What audit streaming is
Every sensitive action in your Farside organization is written to its audit log. A stream is a destination that receives a copy of those events. Owners and admins create streams in Farside under Settings, and a workspace can have up to 10 of them.
New streams start from now. You can also backfill up to 30 days of existing events when you create one. Each event carries the same fields as the audit log export, including the actor, the IP address, the request ID and the before and after state where there is one.
If you need a one-off copy of a date range instead of a continuous feed, use the audit log export in Settings.
Destinations
Destination URLs must use HTTPS and resolve to a public address. Farside does not send to private or link-local addresses. After you save a destination, send a test event to check that your receiver accepts it before real events start flowing.
Signed webhook
- In Settings, add a stream, choose Webhook and enter your HTTPS endpoint.
- Farside generates a signing secret that starts with whsec_ when the stream is created. Store it in your receiver's secret store. It is shown only once. If you lose it, rotate the secret in Settings.
- Verify the signature on every request, as described below, then return any 2xx status once you have stored the batch.
Splunk HEC
- In Splunk, go to Settings, then Data inputs, then HTTP Event Collector, and create a token. Choose the index and source type you want events to land in on the token itself, because Farside does not send an index name.
- Copy the token value and your HEC event endpoint URL, which usually looks like https://your-splunk-host:8088/services/collector/event.
- In Farside, add a stream, choose Splunk HEC, paste the URL and the token. Farside sends the token as Authorization: Splunk <token> and sets the source type to farside:audit.
Datadog logs
- In Datadog, create an API key under Organization Settings, then API Keys.
- Note the Datadog site your account lives on. Farside supports datadoghq.com, us3.datadoghq.com, us5.datadoghq.com, datadoghq.eu and ap1.datadoghq.com.
- In Farside, add a stream, choose Datadog, pick the site and paste the API key. Logs arrive with source farside, service farside-audit and the event as a JSON message.
What a webhook receives
Webhook requests are POSTs with a JSON body. One request carries a batch of up to 500 events in the data array. The id on each event is the audit log row ID, and it is stable, so it is the field to dedupe on. Splunk receives one event per line in the HEC format, and Datadog receives one log per event.
Test events use an id that starts with test_ and an action of audit_stream.test.
{
"type": "audit_events",
"destination_id": "6f0c1c52-4d1e-4a39-9c52-0b0f8f6f6a10",
"sent_at": "2026-11-12T15:04:05Z",
"data": [
{
"id": 48213,
"created_at": "2026-11-12T15:01:44Z",
"action": "api_key.create",
"resource_type": "api_key",
"resource_id": "…",
"actor_kind": "user",
"actor_user_id": 17,
"actor_api_key_id": null,
"actor_label": "Ada Lovelace",
"request_id": "…",
"ip_address": "203.0.113.9",
"user_agent": "…",
"before": null,
"after": { },
"detail": { }
}
]
}Verify the signature
Webhook requests carry two headers. X-Farside-Timestamp is the Unix time the request was signed. X-Farside-Signature has the form t=<unix timestamp>,v1=<hex>, where the hex value is HMAC-SHA256 of the string <timestamp>.<raw body> using your signing secret.
Compute the HMAC over the raw bytes you received, compare it with a constant-time function, and reject requests whose timestamp is more than 5 minutes from your clock. Do not parse and re-serialize the JSON before checking, because that changes the bytes.
import crypto from "node:crypto";
const TOLERANCE_SECONDS = 300;
// rawBody must be the exact bytes received, not re-serialized JSON.
export function verifyFarsideSignature(secret, signatureHeader, rawBody) {
const parts = Object.fromEntries(signatureHeader.split(",").map((part) => part.split("=")));
const timestamp = Number(parts.t);
if (!Number.isInteger(timestamp) || !parts.v1) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}import hashlib
import hmac
import time
TOLERANCE_SECONDS = 300
def verify_farside_signature(secret: str, signature_header: str, raw_body: bytes) -> bool:
# raw_body must be the exact bytes received, not re-serialized JSON.
try:
parts = dict(part.split("=", 1) for part in signature_header.split(","))
timestamp = int(parts["t"])
received = parts["v1"]
except (KeyError, ValueError):
return False
if abs(time.time() - timestamp) > TOLERANCE_SECONDS:
return False
expected = hmac.new(
secret.encode(), str(timestamp).encode() + b"." + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received)Delivery behavior
- Near real time. Events usually arrive within a few minutes of being recorded. Farside waits briefly before reading new events so that ones still being written are not missed.
- At least once. A batch can be delivered more than once, for example when your receiver processed it but its reply did not reach us.
- Possibly out of order. A late event can arrive after newer ones. Use created_at to order events on your side.
- Dedupe on the event id. Treat a repeated id as a repeat, not a new event.
- Retries with backoff. If your receiver does not return a 2xx, Farside retries with growing delays, from 1 minute up to 24 hours, and the stream shows as failing in Settings after repeated failures. The stream does not skip ahead: delivery resumes from the last batch you acknowledged.
When a stream is paused
A stream that fails continuously for 3 days is paused automatically, and owners and admins are notified. No events are skipped. Fix the receiver, then resume the stream in Settings and delivery picks up where it stopped.
You can also pause and resume a stream yourself at any time.
Last updated November 12, 2026. More on how we protect access is on the security page.
