Querying
GET /api/bng/audit/timeline read_only
| Parameter | Default | Notes |
|---|---|---|
since |
now − 3600 | Epoch seconds. Unparseable falls back to the default. |
until |
now | Epoch seconds. |
limit |
500 | Clamped to 1–5000. |
sources |
the three defaults | Comma-separated; intersected with the four known names. |
actor |
— | Exact match. |
q |
— | Case-insensitive substring over summary and detail. |
related_event_id |
— | Exact match. Used for deep-linking from an event. |
detail |
lifecycle |
Journal ladder, above. |
since_cursor |
— | Incremental polling; see below. |
format |
— | csv or json for a file download. |
Sources are fetched in parallel, each with its own timeout guard, so wall
clock is the slowest source rather than the sum. A failing source never fails
the request — it is reported as degraded and the rest of the timeline is
returned.
{
"entries": [ … ],
"count": 42,
"window": { "since": 1752996400.0, "until": 1753000000.0 },
"cursor": "a1b2c3d4e5f6a7b8",
"sources": {
"qt-events": { "count": 10, "ok": true, "error": null, "latency_ms": 3 },
"controller-api": { "count": 0, "ok": false, "error": "connection refused", "latency_ms": 12 },
"systemd-journal": { "count": 32, "ok": true, "error": null, "latency_ms": 140 },
"portal-access": { "count": 0, "ok": true, "error": null, "latency_ms": 0 }
},
"truncated": false
}
The sources object always contains all four keys, including sources not
requested, which appear zero-filled. A client therefore never has to handle a
missing key. Note that the per-source count is that source's contribution
before filtering and limiting, not the number of its entries that survived into
entries.
Ordering is newest first, with ties broken by a fixed source priority
(qt-events, then controller-api, then systemd-journal). Since the sort is
newest-first, truncation drops the oldest entries; truncated reports when
that happened.
There is deliberately no deduplication and no clock-skew correction. Every
source is a clock on the same box. A single real-world moment appearing in two
sources produces two rows, which is correct — they are two independent
observations. The source-priority tiebreak exists to make those colocated rows
read in a meaningful order, not to collapse them.
since_cursor supports incremental polling: pass back the cursor from the
previous response and receive only entries newer than it. Be aware of what this
is and is not — it reduces payload and client re-render, but the backend still
fetches and merges the full window. It is not backend pagination. A cursor that
has aged out of the window silently returns the full window rather than erroring.
Two supporting endpoints complete the surface. GET /api/bng/audit/sources
probes the three default sources over a short window and reports liveness and
latency without returning entries — useful as a collection health check.
GET /api/bng/audit/timeline/{entry_id} resolves a single entry by its hash;
note that it re-derives a fixed one-hour window, so an entry older than an
hour returns 404 even though its identifier is valid.