Flue Zero to Expert, Part 5: Persistence and Durable Execution

Persist Flue state with SQLite or Postgres, understand recovery limits, and make external effects idempotent.

Series map

Part 1: Your First Flue Agent · Part 2: Typed Workflows · Part 3: Safe Tools · Part 4: Autonomous Agents · Part 5: Persistence and Durable Execution · Part 6: Production Routing and Deployment · Part 7: Operate Like an Expert

Tested with @flue/runtime and @flue/postgres 1.0.0-beta.9 on Node.js.

Persistence starts in memory

On Node.js, Flue uses in-memory SQLite by default. It stores agent conversations, accepted submissions, and workflow records only while the process runs.

Flue discovers persistence through a source-root db.ts, normally src/db.ts. For a single-host service, use file-backed SQLite:

// src/db.ts
import { sqlite } from '@flue/runtime/node';

export default sqlite('./data/flue.db');

This survives process restarts, but not loss of the host, and it is not shared storage for several replicas.

For Postgres, use the official blueprint:

npx flue add database postgres

The blueprint installs @flue/postgres, lets you choose a driver, and writes the driver-backed db.ts. In beta.9, postgres(...) accepts a PostgresRunner with query, transaction, and close; it does not accept a connection string. The package README includes complete pg and postgres driver examples. Flue runs the adapter's idempotent migrate() hook at startup.

Postgres is useful when state must survive host replacement or several replicas need shared workflow history. Shared storage still does not provide shared execution ownership: keep one live Node owner for each agent instance and avoid overlap during replacement.

The Cloudflare target is different. Its generated Durable Objects use SQLite automatically and reject db.ts.

What Flue stores

The adapter stores Flue runtime state:

  • The append-only conversation history for each agent instance.
  • Immutable attachment payloads.
  • Accepted direct prompts and dispatch(...) submissions.
  • Workflow-run records, event streams, and run indexes used by /runs and listRuns().

It does not persist sandbox files, installed dependencies, provider secrets, external API effects, or your application records. Keep business data in your business database and configure durable workspaces separately when needed.

Recovery is conservative

Flue reuses completed tool results and durable partial output. If a tool may have started but has no durable result, its outcome is unknown and Flue does not automatically repeat it. Provider execution may happen again only when no output was durably persisted.

Workflows are finite calls to run(...) with a runId. Flue does not checkpoint arbitrary TypeScript and resume from the last line. After an interruption, a Node workflow record can remain active, and a live reader can wait indefinitely. Inspect its recorded events, then start a new invocation only when your retry policy permits it.

Make external effects idempotent

Persistence cannot prove whether an interrupted external request succeeded. Send an application-owned idempotency key for payments, emails, tickets, jobs, and webhooks, store it with the effect, and enforce uniqueness in the system that owns that effect.

Sources

Next, continue to Part 6: Production Routing and Deployment.