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)Durable state for applications and agents
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/coreimport { 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 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
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.
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)Change several records as one unit. All of them commit, or none do, including across a crash.
db.transaction(async (tx) => { … })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)Give a transaction an id. A retry after an uncertain outcome comes back as a duplicate and applies nothing.
db.transaction(fn, { transactionId })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 })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
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.
// 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: 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
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.
Stored in IndexedDB. Survives reloads and works offline.
{ browser: true }A directory your process owns. Durable across restarts.
{ path: './data' }feltdb-server, run where you choose: authentication, keys scoped to a namespace, backups.
{ server: { url, token } }We run it: applications, contracts promoted per environment, runtime keys.
{ server: { url, token, applicationId } }Built for agents
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.
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');Orders, accounts, documents: records several people and processes change, that must survive a crash.
Collections · versions · transactionsClaim 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 · retriesCheckpoint progress, hand off to another agent, and resume from committed state instead of a transcript.
Checkpoints · handoffs · resumable workKeep what was decided, by whom, and what it changed, written in the same transaction as the change.
Audit · history · evidenceExplicit guarantees
Each guarantee is stated next to the test that proves it, and the limits are written down too.
Building a whole application?
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
Pick the path that fits. Every package is public on npm.
npm install @feltdb/coreNode 24 or a modern browser. The getting-started guide takes five minutes.
Read the guide →npx --yes create-feltdb@latest my-appScaffolds an app, starts it, and opens Studio to look inside its state.
Read the guide →npm create feltkit@latest my-appFeltKit 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.