Skip to content
AbloAblo Docs
Esc
navigateopen⌘Jpreview
On this page

How Ablo Works

You write through Ablo, Ablo writes to your Postgres, and the write-ahead log confirms it.

You write through Ablo, and Ablo writes to your Postgres. That one sentence is the whole model — everything below explains what it means and how to use it.

// You call Ablo. Ablo lands the change in your database and confirms it.
await ablo.records.update({ id: 'record_42', data: { status: 'done' } });

// Reads come back live, kept current from your database.
const record = ablo.records.local.get('record_42');

The mental model: read this once

Ablo is a coordination layer in front of your Postgres. Agents, background jobs, and the people alongside them all change the same application data through one API, and Ablo makes sure their writes don’t clobber each other.

  • Writes go through Ablo: ablo.<model>.create / update / delete are authorized, made idempotent, and applied to your Postgres through a scoped writer role. A plain write is last-write-wins. Use a functional update, a explicit reads: [returnedRow], or a claim when the new value depends on an earlier read.
  • Your database confirms the write. Ablo tails your write-ahead log (WAL). When the row it wrote shows up there, the receipt is promoted to confirmed. So your database, not Ablo, is the source of truth for row state — the WAL echo is how Ablo confirms, not how it writes.
  • Reads are live. Ablo serves current state and keeps every connected client up to date off that same stream.
  • Ablo stores only the change history and coordination state. Your rows live in your database. Ablo does not run application migrations; your migration tool stays in charge of tables and columns.

That’s the shape: you write through Ablo → it lands in your Postgres → the WAL echo confirms it → everyone connected sees it live.

The primitives

Primitive Plane Purpose
Schema State Declares typed models the app and agents can read and write.
Model State The generated ablo.<model> model. Use get/list (async reads), local.get/local.list/local.count (the same verbs, synchronous and local-only), create, update, and delete.
Claim Coordination Who is working on a target. Taken via ablo.<model>.claim({ id }) and read via ablo.<model>.claim.state({ id }). Ephemeral, never persisted.
Commit Protocol The durable write underneath model updates. Most users do not call it directly.
Receipt Protocol The result of a lower-level commit. Awaiting a schema write waits for confirmation.

Where your data lives

You point Ablo at a Postgres database, and that’s where its rows live. Only which database differs by environment — the code is identical.

  • Production: your Postgres. ablo connect sets up a scoped writer role and logical replication; your rows live in your database, and Ablo writes to them through that role.
  • Development branches and local dev: a separate or local Postgres, or a branch of the one you already run. Same models, same code, a different database behind them.
  • Before you connect one. Ablo keeps state in its own log, so you can build the whole app today and point it at a real database when you’re ready.

Registering the database is the whole switch. There is no tier or flag to choose.

Use it end to end

# 1. Install and scaffold.
npm install @abloatai/ablo
npx ablo init

# 2. Push your schema (the models your agents edit together).
npx ablo push

# 3. Connect your database — one command, admin credential used once and discarded.
npx ablo connect apply --url postgres://admin:...@host:5432/db
# Your app's environment holds only the API key — never a connection string.
ABLO_API_KEY=sk_...
// 4. Build the client once.
import Ablo from '@abloatai/ablo';
import { schema } from './ablo/schema';
export const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });

// 5. Write through Ablo. Local state changes immediately; await blocks until
//    the authoritative feed proves the row is there.
await ablo.records.update({ id: 'record_42', data: { status: 'done' } });

// 6. Read — live, no fetch loop.
const record = ablo.records.local.get('record_42');

// 7. Coordinate when more than one actor can touch a row. Hold a claim and Ablo
//    serializes writes on that key against everyone else; read after claiming,
//    then write. The lease releases automatically at the end of the scope.
await using _hold = await ablo.records.claim('record_42');
const latest = ablo.records.local.get('record_42'); // read after claiming, not from memory
await ablo.records.update({ id: 'record_42', data: { status: 'done' } });

For the setup details, see Connect Your Database. For the coordination loop, see Coordination. For what queued and confirmed guarantee, see Guarantees.

Was this page helpful?