# Execution backends

Choose SQLite, PostgreSQL, or Temporal.

Part of **Advanced topics**. Start with [Durable agent runs](../durable-agent-runs/) for the runtime API, session identity, run handles, and lifecycle.

AgentRuntime uses the same execution semantics with different storage and scheduling backends. Choose based on deployment needs, then qualify the actual services and adapters.

## Compare the implementations

| Backend | Stores / drives | Operational requirements |
| --- | --- | --- |
| SQLite | Local execution state, events, journal, artifacts | Writable database and single-host session locking |
| PostgreSQL | Shared SQL state, events, journal, artifacts | Existing database, schema privileges, worker resume logic |
| Temporal | Workflow recovery; activities execute model/tool calls | Temporal, Redis events, shared artifact store, compatible workers |

SQLite is the default. PostgreSQL and Temporal implementations still require service-backed release qualification.

## SQLite

~~~python
from harnessx import SQLiteBackend

backend = await SQLiteBackend.connect("runtime.sqlite3")  # construct and initialise

async with SQLiteBackend("runtime.sqlite3") as scoped:   # initialise on entry, close on exit
    ...
~~~

`await SQLiteBackend.connect(path)` constructs and initialises in one call. Every backend is also an async context manager: `async with backend:` initialises on entry and closes on exit. A backend constructed directly is initialised by the runtime's `start()` or `resume()`, and initialisation is idempotent. SQL backends save transitions and their events transactionally. SQLite uses an OS session lock. The runtime does not schedule work after your process exits; an application must reopen and resume it.

## PostgreSQL

Start with the [PostgreSQL crash-and-resume walkthrough](../postgres-durability/) for setup, separate process commands, expected output, database inspection, and recovery limits. It uses scripted model responses and needs no model API key.

~~~python
import os
from harnessx import Agent, AgentConfig, AgentRuntime, PostgresBackend

backend = await PostgresBackend.connect(
    os.environ["DATABASE_URL"],
    schema="harness_x",
    auto_migrate=True,
)
agent = Agent(config=AgentConfig(system_prompt="You are helpful."))
runtime = AgentRuntime(agent, backend=backend)
~~~

`await PostgresBackend.connect(dsn, **kwargs)` opens the pool and migrates in one call; `async with PostgresBackend(dsn) as backend:` does the same for the duration of a block. Pass the configured Agent directly; no agent registry is required. Call `await runtime.start()` for a new session. In a replacement process, construct a fresh compatible Agent and call `await runtime.resume(session_id)`. The [PostgreSQL guide](../postgres-durability/#use-an-agent-directly) includes complete functions with resource cleanup.

The CLI accepts --config examples/postgres.config.json with a password placeholder; its check command connects and prepares the runtime schema without invoking a model. Follow the [connection setup](../postgres-durability/#1-connect-to-postgresql) for the config format and commands.

Install harnessx[postgres]. The backend provisions its schema, tables, constraints, and migration history in an existing database. It does not create the database server or users.

Migrations are transactionally locked and checksummed. With auto_migrate=False, initialization validates the schema without provisioning it. PostgreSQL leases are renewed and stale writers are fenced; a replacement worker must still call resume.

## Temporal

Install harnessx[temporal]. Bind versioned agent factories in an AgentRegistry on workers; submitting clients use AgentRef identifiers. Configure `await TemporalBackend.connect(target, events=RedisEvents(...), artifact_store=S3ArtifactStore(...), task_queue=...)`. `TemporalBackend.aclose()` closes the Temporal client when `connect()` created it and leaves an injected client open.

Model and tool calls run in activities. Redis carries provisional events; it is not the source of execution truth. Expired event cursors or service outages can produce a gap. Query final results through the runtime.

Tools and files tied to a local host do not become portable because Temporal is used. Recording=True is explicitly unsupported on the Temporal path in this milestone.

## Runtime configuration

`RuntimeConfig` carries `storage_dir` and `max_session_duration_seconds`. The 0.3 fields `checkpoint_interval` and `max_checkpoints` were removed in 0.4: the runtime checkpoints at every phase boundary, and had ignored them since 0.3. Each checkpoint emits `HookEvent.CHECKPOINT` with `session_id`, `run_id`, `status`, and `phase`.

The runtime, backends, recorder, and their errors are also importable together from `harnessx.durable`.

## Artifact and schema operations

PostgreSQL can store content-addressed artifact bytes or use a configured S3ArtifactStore. Artifact storage and external tool effects are not part of one global transaction.

Keep migration checksums stable. Test schema upgrades, recovery, worker compatibility, event reconnection, and actual adapters against your release configuration.

## Further reading

[Durable agent runs](../durable-agent-runs/) covers the runtime API, approvals, and what survives a failure. The [PostgreSQL walkthrough](../postgres-durability/) covers schema provisioning, leases, and a crash-and-resume demonstration. [Sessions](../sessions/) contains a complete local example.

