START HERE

Core concepts.

Channels, events, cursors, atomic state transitions, and the idempotency guarantees that make recovery safe.

Channels and events

A channel is the durable identity of one execution stream — an agent run, a LangGraph thread, a Claude session, a workflow instance, a business process. Each channel is an ordered, append-only event log.

An event is an immutable JSON blob with a type and optional payload. Events are never edited or deleted by normal operation; the log is the source of truth for what happened, in order.

Channels are cheap

Create a channel per logical execution. Forking, experiments, and lineage all operate on channels — naming them well is the foundation of every Actae workflow.

Cursors

Every event carries a cursor: its gapless, per-channel sequence number (1, 2, 3, …). Cursors are the currency of replay, subscribe-resume, and consumer-group acks.

  • Replay and subscribe use exclusive semantics: pass N - 1 to read everything up to and including cursor N.
  • Cursors are per-channel — a cursor in one channel means nothing in another.
  • State versions come from the server's global sequence; never assume per-channel gapless numbering for state.

Event records also carry a server-assigned global position (used for cross-channel ordering and lineage), but all cursor-relative APIs are per-channel.

State and transitions

Execution state — what a run knows so far — is persisted as versioned snapshots aligned with the event log. Three primitives cover most needs:

PrimitiveWhat it doesUse when
save_stateSaves a snapshot at a cursorManual, full control
transitionRecords an event and its resulting state in one server-side transactionThe state belongs to a specific event
AgentSessionSteps, auto-snapshots, fork/resumeInstrumenting a multi-step agent run

Atomicity. A transition commits the event and the resulting state in one transaction — the two can never be observed apart. Expected-version and expected-cursor guards reject stale writers instead of silently overwriting progress: a write with a stale expected_version fails loudly with a version conflict.

Rule of thumb

Use transition when the state belongs to an event, AgentSession when you are instrumenting a run, and save_state (or StateManager) when you want full control.

Idempotency

Retries are the norm in distributed systems, so Actae makes them safe. Pass a stable operation_id (a UUID you own) with a record, transition, or publish: if the server already applied that operation, the original result is returned instead of a duplicate.

  • Reusing an operation_id with different content fails loudly with 409 idempotency_conflict.
  • Replay comparison is canonical — key-order-insensitive — so re-serialized payloads replay idempotently.
  • For transitions, replay comparison also covers the snapshot state, and replays are evaluated before the expected-* guards (a stale retry still resolves idempotently).

SDK sessions generate operation IDs deterministically from step content (UUIDv5 over scope/type/channel/step number/canonical content), so a crash-recovery re-drive of the same step replays, while changed content records anew.

Replay to live

The WebSocket subscription model removes the handoff bug: a subscriber catches up from a cursor (past events delivered in order), synchronizes, and continues receiving live events on the same subscription. The SDK exposes this as subscribe() with a cursor, or stream() for async iteration.

  • Delivery is batched and ordered per channel.
  • Auto-reconnect resubscribes all topics from their last cursors — no gaps, no duplicates, no application-built catch-up.
  • The server does not echo a publish back to the connection that sent it; use echo_self=True for single-client demos.

Retention and security

Channels can be deleted; retention and GC policies are configurable per deployment. The API is API-key-gated (dashboard sessions via JWT), with token-bucket rate limiting, and optional TLS/mTLS in production. Execution payloads from self-hosted instances are never uploaded to the control plane — the SaaS processes account, entitlement, and billing metadata only.