Durable state for applications and agents

Application state you can trust.

FeltDB is a database you use straight from your code. Every write is durable before you are told it succeeded, several changes commit together or not at all, and two writers never silently overwrite each other. It runs in the browser, in Node, on a server you operate, or managed.

npm install @feltdb/core
Get started →
shop.ts
import { createFeltDB } from '@feltdb/core';

const db = createFeltDB({
  namespace: 'shop', path: './shop-data',
});
const orders = db.collection('orders');

await orders.insert({ status: 'pending' }, 'o-1');

// Change it only if nobody else did.
const seen = await orders.get('o-1');
await orders.updateIfVersion('o-1',
  seen.__version, { status: 'packed' });
● Durable on disk before each call returns

Durable writesAcknowledged only once they are stored

Atomic transactionsAll of it commits, or none of it

Safe concurrencyOf two racing writers, exactly one wins

One API everywhereBrowser, Node, your server, managed

The code you stop writing

The fragile parts are FeltDB's job.

Around a table or a JSON file, every team ends up writing the same code: version columns, claim flags, retry guards, cleanup for half-finished writes. FeltDB does it for you, and every example in the getting-started guide runs in FeltDB's own tests.

01

A version on every record

Each record carries a __version. Make a write conditional on it, and a lost race comes back as a value: nothing changed, so read again and decide again.

orders.updateIfVersion(id, version, changes)
02

Changes that commit together

Change several records as one unit. All of them commit, or none do, including across a crash.

db.transaction(async (tx) => { … })
03

Durable before it says so

A write returns only once it is durable. Reopen the store, or restart after a crash, and every acknowledged write is there, at the same version.

await orders.insert(record, id)
04

Retries that apply once

Give a transaction an id. A retry after an uncertain outcome comes back as a duplicate and applies nothing.

db.transaction(fn, { transactionId })
05

Queries that stay bounded

Every query has a limit and a deterministic order, and its plan tells you when it scanned instead of using an index.

db.query({ where, orderBy, limit })
06

Updates you can render

Subscribe to a collection and re-render when it changes, in the browser or on the server.

orders.subscribe(render)

A database in your code

The version check is the lock.

Two workers race to claim the same job. With FeltDB exactly one wins, its event is written in the same transaction, and the loser gets a clear answer, PRECONDITION_FAILED, with nothing half-written. Nothing is held between the read and the write, so a worker that dies holds nothing anyone has to clean up.

Without it
// Claiming a job, the usual way
const job = await db.jobs.findOne({ id: 'job-1' });
if (job.state !== 'queued') return;          // someone else?
await db.jobs.update({ id: 'job-1' },
  { state: 'running', owner: worker });      // ...or both of us
await db.events.insert({ job: 'job-1', by: worker });
// crash here, and the claim has no event
With FeltDB
// With FeltDB: one winner, nothing half-written
await db.transaction(async (tx) => {
  tx.collection('jobs').set('job-1',
    { state: 'running', owner: worker },
    { expectedVersion: 1 });
  tx.collection('job_events').insert(
    { job: 'job-1', by: worker }, `claim-${worker}`,
    { requireAbsent: true });
});

Where it runs

One API, wherever your state lives.

Choose where state lives with one option to createFeltDB. The contract is the same on each, and where a runtime cannot honor an operation it says so, with CONTRACT_UNSUPPORTED, instead of approximating it.

In the browser

Stored in IndexedDB. Survives reloads and works offline.

{ browser: true }

In Node

A directory your process owns. Durable across restarts.

{ path: './data' }

On your own server

feltdb-server, run where you choose: authentication, keys scoped to a namespace, backups.

{ server: { url, token } }

Managed FeltDB

We run it: applications, contracts promoted per environment, runtime keys.

{ server: { url, token, applicationId } }

Built for agents

Agents that pick up where they left off.

An agent crashes, restarts, or hands its work to another agent. With FeltDB's agent state, built from the same versions and transactions, it continues from committed checkpoints, not from a transcript. Agent state is experimental.

agent.ts
const state = await openAgentState(db);

await state.createTask({
  taskId: 'triage-42', actor: 'agent-a',
  objective: 'Triage the inbox',
  pendingWork: ['scan', 'reply'], constraints: [],
});

await state.checkpoint('triage-42', {
  actor: 'agent-a', expectedVersion: 1,
  status: 'running', summary: 'Scanned 120 messages.',
  completed: ['scan'], pendingWork: ['reply'],
  decisions: [], evidence: [],
});

// After a crash, or on another agent:
const { task, checkpoint } =
  await state.getTask('triage-42');
01

Application state

Orders, accounts, documents: records several people and processes change, that must survive a crash.

Collections · versions · transactions
02

Jobs and workflows

Claim work with a version fence, so two workers never run the same step. A worker that dies holds nothing that needs cleaning up.

Claims · leases · retries
03

Agents

Checkpoint progress, hand off to another agent, and resume from committed state instead of a transcript.

Checkpoints · handoffs · resumable work
04

A record of decisions

Keep what was decided, by whom, and what it changed, written in the same transaction as the change.

Audit · history · evidence

Explicit guarantees

Precise about what it promises.

Each guarantee is stated next to the test that proves it, and the limits are written down too.

FeltDB promises

  • An acknowledged write survives a process kill, at the same version.
  • A transaction commits all of its writes, or none of them.
  • Of two writers that read the same version, exactly one wins.
  • A retried transaction with the same id is applied once.

FeltDB does not promise

  • Exactly-once execution: it makes a commit apply once and duplicate work detectable, but it cannot unsend an email.
  • Durability through power loss or an OS crash: documented as unproven.
Read the guarantees and limits →

Building a whole application?

FeltKit is built on FeltDB.

FeltKit gives you typed functions, live queries, roles enforced on the server, built-in services, agent access over MCP, a dashboard, and deployment, with one FeltDB instance per environment underneath.

npm create feltkit@latest my-app
@feltdb/feltkit on npm ↗

Start in a minute.

Pick the path that fits. Every package is public on npm.

Add it to an app

npm install @feltdb/core

Node 24 or a modern browser. The getting-started guide takes five minutes.

Read the guide →

Start a FeltDB app, with Studio

npx --yes create-feltdb@latest my-app

Scaffolds an app, starts it, and opens Studio to look inside its state.

Read the guide →

Build a whole application

npm create feltkit@latest my-app

FeltKit adds typed functions, roles, services, agent access, and deployment on FeltDB.

Learn more ↗

Or work in Studio, FeltDB's web workspace. Free to start, no credit card.