Skip to main content

Durable audit delivery

An audit-oriented system must not quietly discard the record of an accepted enforcement decision. Console-managed ActionRail commits reports to a local SQLite outbox before returning control to the producer.

Delivery pipeline

The queue contains action-surface reports, observed call shapes, decisions, and Source health metadata. It contains report-safe representations, not raw tool arguments, Source records, or the agent key.

Default behavior

SettingDefaultMeaning
Capacity1000 eventsMaximum durable pending rows for one reporter.
Batch size50 eventsMaximum rows read per worker batch.
Batch interval0.1 secondsShort collection window before delivery.
Request retries2Two retries after the initial request.
Retry backoff0.2 secondsExponential delay between request attempts.
Recovery-cycle backoff1 secondDelay before the worker retries durable pending rows.
Shutdown timeout7 secondsDefault bounded flush and worker-stop deadline.

These are Reporter constructor defaults. The high-level enforce() integration constructs the reporter automatically.

Idempotency

Decision and observed-call reports include stable event identifiers. The control plane deduplicates retries for the same agent.

If the Console stores a request but the acknowledgement is lost, the SDK can safely send that event again. At-least-once transport does not become a duplicate Activity record.

Action-surface reports are snapshots; when several are queued, only the most recent snapshot in a batch matters.

Saturation and backpressure

When pending rows reach capacity, the producer waits for the worker to free space. ActionRail logs that the durable outbox is full and applies backpressure instead of dropping the oldest event or accepting an event it cannot persist.

This can increase tool-call or startup latency during a prolonged Console outage. That is intentional and visible. Alert before capacity is reached by monitoring reporting.pending_events and reporting.capacity.

Capacity is per wrapped agent reporter. Plan persistent disk and alert thresholds for the number of agent processes, expected decision rate, and maximum control-plane outage.

Persistent recovery

The outbox is scoped by control-plane endpoint and agent ID and stored under:

~/.actionrail/sdk/outbox/<runtime-key>.sqlite3

The runtime key is a one-way filesystem-safe hash of endpoint and agent ID. A new process using the same state directory, endpoint, and agent resumes pending rows automatically.

Configure a persistent location for containers and ephemeral hosts:

export ACTIONRAIL_SDK_STATE_DIR=/var/lib/my-agent/actionrail

The SQLite database uses write-ahead logging and full synchronous commits. Include the outbox database and its sidecar files on the same persistent volume.

Shutdown

from actionrail import close_reporting

clean = close_reporting(agent, timeout=7)

A False result means rows remain pending or a worker did not stop before the deadline. Committed rows are not discarded. The next matching process can resume them.

Set the workload termination grace period longer than the application’s shutdown timeout. A hard process kill cannot perform a graceful flush, although already committed outbox rows remain recoverable on persistent storage.

Operational signals

Alert on:

  • sustained pending-event growth;
  • repeated failed delivery cycles;
  • an outbox approaching capacity;
  • a non-empty outbox after control-plane recovery;
  • shutdown returning False repeatedly;
  • an unwritable or ephemeral SDK state directory.

Audit delivery degradation does not automatically change the allow/hold/block outcome. Capacity backpressure is the mechanism that prevents indefinite silent divergence between enforcement and its audit trail.