Skip to content
Run it

Exporting the audit trail

Verify the decision chain on the endpoint, page the tenant audit log, and stream signed decisions to your own SIEM through Spectra.

Every decision Beacon makes is signed and streamed to Spectra, forming one audit chain that maps to OWASP ASI and to NIST AI RMF and ISO/IEC 42001 controls. Getting that record into your own systems takes one of three paths: verify it on the endpoint, page it from the tenant, or stream it to a collector you own.

Verifying the chain

Chained events are policy decisions and detections. Each one commits to the previous link in its session, so a gap or an edit breaks verification without anyone having to trust the writer. The chain verifier reads an NDJSON export and answers one question:

alyria verify-chain --events ./decisions.ndjson

Output is a single line, VALID, or INVALID (first broken link at index N) with a non-zero exit, which is what a pipeline should branch on. --events reads standard input when it is omitted, so the same check works in a pipe.

Two things to keep straight:

  • Verify the file the agent wrote, in order. Filtering it down to decisions alone and then verifying produces a false failure once an interleaved detection has advanced the chain, because both classes share one sequence.
  • The verifier reads. A chain that fails verification is evidence of a problem, not a corrupt file to repair.

Pulling the tenant audit log

Control-plane actions live behind a paginated query:

GET /v1/tenants/{tenantId}/audit
ParameterMeaning
eventTypeOptional filter
from, toOptional ISO 8601 datetimes
limit1 to 100, default 50
offset0-based, default 0

The response is { entries, total, limit, offset }. Because limit is capped at 100, an export is a loop:

set -euo pipefail
: "${TOKEN:?set TOKEN}" "${TENANT:?set TENANT}"

offset=0
while :; do
  page="audit-$offset.json"

  if ! curl -sS --fail --max-time 30 \
      -H "Authorization: Bearer $TOKEN" \
      "https://observatory.example/v1/tenants/$TENANT/audit?from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z&limit=100&offset=$offset" \
      -o "$page"; then
    echo "export failed at offset $offset: request refused or failed" >&2
    exit 1
  fi

  total=$(jq -er '.total | select(type == "number" and . >= 0 and . == floor)' "$page") || {
    echo "export failed at offset $offset: response has no valid total" >&2
    exit 1
  }

  offset=$((offset + 100))
  if [ "$offset" -ge "$total" ]; then
    break
  fi
done

echo "exported $total entries"

--fail is the part that matters: without it curl treats an HTTP 401 or 500 as a successful transfer, writes the error body to disk, and the loop keeps going with a total of null. With it, a refused request stops the export and says where.

The jq check rejects anything that is not a non-negative whole number, so a malformed or unauthenticated response exits before the offset advances. Fix from and to for the whole run so a live tenant cannot shift the window underneath you, and keep the pages with the query bounds and the total you saw. An export is evidence, not a spreadsheet.

This log covers tenant-level actions: signup, token minting, key changes, policy publication. Decisions made on a laptop are in the endpoint chain and in whatever sink you pointed it at.

Streaming to your sink

Decisions are signed at the endpoint and forwarded to Spectra. The service terminates TLS itself, takes a destination policy and its key set, and exposes a health check, so your collector sits in front of a component that already speaks the format:

spectra-service serve --listen 127.0.0.1:8787 --destination-policy <path> --destination-policy-keys <path>
spectra-service healthcheck --host 127.0.0.1 --port 8787 --path /healthz

If your SIEM is the system of record, this is the integration to build. Collecting chain exports and verifying them on your side is the other supported path, and it is more manual.

Retention and what to keep

Audit history is included for 30 days within plan capacity; longer retention is part of an agreed enterprise deployment. Whatever your plan, treat the endpoint chain and the tenant log as two different records with two different retention rules, and export on a schedule rather than when an auditor asks. The evidence you cannot reproduce is the decision you did not collect.