Skip to main content

Observability & Audit

The Observability & Audit APIs help you capture every policy decision as an operational event.

This is critical for production systems where Actra decisions must be:

  • auditable
  • searchable
  • measurable
  • rollout-safe
  • compliance-ready

The JavaScript SDK provides two real observability surfaces:

  • runtime.setDecisionObserver()
  • runtime.audit()

Together, these enable both decision event streaming and non-blocking shadow evaluation.


Core Mental Model

Runtime decision -> event -> logs / metrics / SIEM

Every evaluation can become a structured event for:

  • security logging
  • compliance exports
  • rollout analysis
  • anomaly detection
  • incident forensics

Decision Observer

Use the runtime observer to receive every decision event.

runtime.setDecisionObserver((event) => {
console.log(event.decision.effect)
})

This is the primary hook for observability pipelines.


Event Payload

The observer event includes runtime metadata such as:

  • decision
  • action
  • resolved actor
  • resolved snapshot
  • timestamp
  • durationMs

This makes it ideal for both logs and metrics.


Structured Logging

A common production pattern is structured JSON logs.

runtime.setDecisionObserver((event) => {
logger.info({
effect: event.decision.effect,
matched_rule: event.decision.matched_rule,
durationMs: event.durationMs,
timestamp: event.timestamp,
})
})

Great for:

  • ELK
  • Datadog
  • CloudWatch
  • Loki
  • OpenSearch

Metrics & Dashboards

Observers are perfect for metrics.

runtime.setDecisionObserver((event) => {
metrics.increment(`actra.${event.decision.effect}`)
})

Useful dashboards:

  • allow vs block rate
  • approval frequency
  • top matched rules
  • latency distribution
  • action hotspots

Security & SIEM

A very strong enterprise use case.

runtime.setDecisionObserver((event) => {
siem.send({
effect: event.decision.effect,
actor: event.actor,
action: event.action,
matched_rule: event.decision.matched_rule,
})
})

Best for:

  • SOC workflows
  • insider risk
  • privilege escalation detection
  • destructive action tracing
  • regulated environments

Non-Blocking Audit Mode

Use audit() for shadow evaluation.

const auditedRefund = runtime.audit("refund", refund)

This always executes the function while still:

  1. evaluating policy
  2. emitting observer events
  3. capturing decision metadata

This is ideal for:

  • safe rollouts
  • policy migrations
  • dry runs
  • measuring blast radius
  • legacy system onboarding

Shadow Rollout Pattern

A highly recommended production workflow.

const guarded = runtime.audit("deploy", deployService)

Observe for:

  • unexpected blocks
  • approval spikes
  • missing resolver state
  • noisy rules
  • performance regressions

Then switch to:

runtime.admit("deploy", deployService)

once confidence is high.


Compliance Audit Trail

Actra decision events are excellent compliance evidence.

Track:

  • who attempted the action
  • what was attempted
  • current state
  • which rule matched
  • whether approval was required
  • how long evaluation took

This is highly valuable for:

  • SOX
  • ISO 27001
  • PCI
  • internal audit
  • change management

Failure Behavior

Observer Failures

Observer exceptions are intentionally swallowed.

This means:

  • business execution continues
  • policy enforcement remains deterministic
  • telemetry failures do not break requests

This is a major production safety guarantee.


Best Practices

Always instrument matched rules

Track:

matched_rule

This makes incident forensics dramatically easier.


Start with audit() in rollouts

Use shadow mode before enabling blocking enforcement.


Track latency budgets

Use durationMs to watch policy cost under load.


Production Mental Model

Function call
-> Evaluate policy
-> Emit decision event
-> Log / metric / SIEM
-> Execute or shadow-run

This makes Actra observable as a production control plane.


Next Steps

  • Build Testing Policies
  • Add CI Validation
  • Combine with Explain & Debugging
  • Roll out with audit() shadow mode