Rail SDK
Give a Python or TypeScript agent memory around every turn. Rail recalls rules and facts before your agent runs and stores what the turn taught.
Rail is Caura's SDK for agents you write yourself. Before your agent runs, Rail fetches the governance rules and the facts relevant to the current message and hands you prompt-ready context. After the agent replies, Rail extracts facts worth keeping and writes them back. Your code stays in charge of the model call. Python and TypeScript share the same semantics, and both work against managed and self-hosted Caura.
Use the thin REST clients when you only need to call the API. Use Rail when an agent should remember and follow rules.
The complete Rail documentation lives in the public repository, not on this site: the guide, the API reference, and the reliability semantics. This page and the tutorial cover the first hour; those three pages cover everything else.
Install
pip install caura-rail # Python 3.10+
npm install @caura/rail # Node.js 22+, TypeScript or JavaScriptRail reads CAURA_URL, CAURA_API_KEY, and optionally CAURA_TENANT from the
environment. On managed Caura, CAURA_URL is https://caura.ai and the key is
a tenant-scoped mc_ key from Settings → API Keys; Rail resolves the
tenant from the key. On a self-hosted standalone server, use the server URL and
the placeholder key standalone.
Your first turn
from caura_rail import MemoryScope, Rail, RestMemoryStore, Visibility
scope = MemoryScope(agent_id="support-1", fleet_id="support", visibility=Visibility.TEAM)
with RestMemoryStore.from_env() as store:
rail = Rail(store, scope)
with rail.turn("Remember: We deploy in eu-west-1.") as turn:
# Call your model here. turn.context.text holds rules first, then facts.
turn.reply = "Understood. Context used:\n" + turn.context.text
print(turn.reply)
print("degraded:", turn.degraded, "writes:", [w.status for w in turn.writes])Asynchronous applications use AsyncRail with AsyncRestMemoryStore.
import { MemoryScope, Rail, RestMemoryStore } from "@caura/rail";
const scope = new MemoryScope({ agentId: "support-1", fleetId: "support", visibility: "scope_team" });
const rail = new Rail({ store: RestMemoryStore.fromEnv(process.env), scope });
const turn = await rail.turn("Remember: We deploy in eu-west-1.", async (message, context) => {
// Call your model here. context.text holds rules first, then facts.
return "Understood. Context used:\n" + context.text;
});
console.log(turn.reply);
console.log("degraded:", turn.degraded, "writes:", turn.writes.map(w => w.status));Every turn does four things in order: recall, run your code, extract, write. If
your code raises, nothing is written. If Caura is unreachable, the turn is
marked degraded, your reply still returns, and retryable writes wait in an
in-memory outbox that you replay with flush_outbox() / flushOutbox().
What you get on every turn
- Context: keystone rules sorted by weight, then
recalled facts, plus
textformatted for a prompt. Rules are never dropped to make room for facts. - Writes: one result per extracted fact with status
written,deduplicated,deferred, orrejected. - Diagnostics:
errors,degraded, and process-local counters inrail.telemetry.
Under the hood each turn calls GET /api/v1/keystones,
POST /api/v1/search with the agent as caller_agent_id, and
POST /api/v1/memories with write_mode: "strong", so a stored
fact is searchable by the next turn. Rail never calls the LLM-summary endpoint /recall.
The default extractor stores user lines that begin with Remember:, We use,
We deploy, Our plan, or Our contract. Pass your own extractor to store
anything else, or return an empty list to make Rail recall-only.
Scope, fleets, and rules
MemoryScope names the agent and says who may recall what it writes:
agent-private by default, TEAM within a fleet, or ORG across the tenant.
An agent belongs to the fleet of its first write; use one agent id per fleet.
Rules are read on every turn; they are authored through the
keystones API or the dashboard. On managed
Caura the gateway refuses a tenant-scoped key for rule writes (HTTP 403,
AGENT_NOT_REGISTERED); mint an agent-scoped credential at trust level 2 with
one call to the per-agent keys endpoint
and author with that. Team facts that other clients stored without a fleet are
visible to every agent in the tenant and will appear in recall alongside your
fleet's own.
Set require_keystones / requireKeystones when an agent must not run without
its rules.
Compatibility
Python 3.10 to 3.14 and Node.js 22 and 24, against managed Caura and
open-source release backend-v2.47.0 or later. Earlier servers ignore the caller
identity Rail asserts on search, so an agent cannot recall its own private
facts; pin CAURA_VERSION in the server's .env rather than relying on the
latest image. The open-source quick start ships a placeholder embedder;
configure a real embedding provider before judging recall quality.
Step by step
Give a coded agent memory with Rail walks through a first turn, a teammate recalling it, a governance rule landing in the context, and a custom extractor, in both languages.
Complete example
Harborline
in the Rail repository is a whole application: a web UI, a Python service that
runs its turns through AsyncRail with governance rules required, and a
TypeScript intake service on @caura/rail, all sharing one fleet. One
environment variable switches it between the open-source server and caura.ai,
and its ./demo.sh runs an asserted end-to-end scenario against either. Each
turn in its UI shows the context Rail assembled, the reply, and the status of
every stored fact.
Learn more
The Rail repository has the full guide, API reference, and reliability semantics. Every code block there is executed and type-checked in CI against a live Caura server.