@abloatai/ablo 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +59 -0
- package/docs/api-keys.md +124 -30
- package/docs/branch-development.md +16 -3
- package/docs/cli.md +37 -13
- package/docs/concurrency-convention.md +1 -1
- package/docs/data-sources.md +38 -5
- package/docs/debugging.md +24 -8
- package/docs/deployment.md +51 -40
- package/docs/examples/existing-python-backend.md +1 -1
- package/docs/how-it-works.md +4 -3
- package/docs/idempotency.md +1 -1
- package/docs/identity.md +3 -4
- package/docs/integration-guide.md +8 -10
- package/docs/internal/data-source-reverse-channel.md +18 -21
- package/docs/migration.md +33 -7
- package/docs/projects.md +27 -22
- package/docs/quickstart.md +10 -4
- package/docs/react.md +1 -2
- package/docs/schema-contract.md +5 -5
- package/docs/session-settings.md +7 -2
- package/docs/sessions.md +2 -3
- package/docs/webhooks.md +5 -15
- package/examples/README.md +1 -1
- package/examples/data-source/run.ts +2 -2
- package/examples/quickstart.ts +1 -1
- package/llms.txt +15 -13
- package/package.json +3 -3
package/docs/sessions.md
CHANGED
|
@@ -31,8 +31,7 @@ the raw agent token.
|
|
|
31
31
|
It exists because of one rule: **the browser can never hold a secret.** Your
|
|
32
32
|
`sk_` lives on the server; the browser only ever holds a minted session token
|
|
33
33
|
(which already names your org). So the per-actor credential is minted
|
|
34
|
-
server-side, scoped, and expires in minutes
|
|
35
|
-
client-side SDKs.
|
|
34
|
+
server-side, scoped, and expires in minutes.
|
|
36
35
|
|
|
37
36
|
## Why
|
|
38
37
|
|
|
@@ -90,7 +89,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
90
89
|
```
|
|
91
90
|
|
|
92
91
|
The client owns auth, the credential lifecycle, and the connection; the provider
|
|
93
|
-
is the thin reactive binding over it
|
|
92
|
+
is only the thin reactive binding over it.
|
|
94
93
|
Build the client **once** at module scope — a new instance per render tears down
|
|
95
94
|
the socket. Need custom headers or a
|
|
96
95
|
body on the exchange? `authEndpoint` also accepts an async resolver that
|
package/docs/webhooks.md
CHANGED
|
@@ -8,8 +8,8 @@ database; Ablo holds only the log. **Webhooks stream that log to your systems as
|
|
|
8
8
|
signed events:** every committed change is POSTed to an endpoint in your app, and
|
|
9
9
|
your handler decides what to do with it.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
You call Ablo to make changes through the client, and Ablo calls your webhook
|
|
12
|
+
with each committed change. Webhooks are the
|
|
13
13
|
*push* way to keep a store in step with the log — your own database, a warehouse,
|
|
14
14
|
a search index, a background job. The *direct* alternative is `ablo connect`,
|
|
15
15
|
where Ablo reads your write-ahead log and writes back through a scoped role. Either
|
|
@@ -30,18 +30,8 @@ There are two ways data flows out of Ablo, and they're for different jobs:
|
|
|
30
30
|
Most apps use both: the realtime stream for the UI, the webhook stream to keep
|
|
31
31
|
their database in sync. This page is the webhook stream.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
| Stripe | Ablo |
|
|
36
|
-
|---|---|
|
|
37
|
-
| `stripe.x.create(...)`: make the change | the Ablo client: make the change (+ live sync) |
|
|
38
|
-
| `/stripe-webhook`: confirm and persist | `/api/ablo/[...all]`: persist into your database |
|
|
39
|
-
| Stripe owns the charges | Ablo owns the transaction log |
|
|
40
|
-
| you mirror charges into your database | you mirror the log into your database |
|
|
41
|
-
|
|
42
|
-
The difference in Ablo's favor: every event carries `syncId`, a monotonic log
|
|
43
|
-
position, so you can both dedupe **and** apply in order — Ablo guarantees the
|
|
44
|
-
order because it owns the log.
|
|
33
|
+
Every event carries `syncId`, a monotonic log position, so you can both dedupe
|
|
34
|
+
**and** apply in order. Ablo guarantees the order because it owns the log.
|
|
45
35
|
|
|
46
36
|
## The event object
|
|
47
37
|
|
|
@@ -52,7 +42,7 @@ Every delivery is a batch of events. Each event:
|
|
|
52
42
|
| `type` | `"<model>.<verb>"` with the model name lowercased, e.g. `task.updated` |
|
|
53
43
|
| `model` | the model name exactly as declared in your schema: the table to write |
|
|
54
44
|
| `objectId` | the changed row's id |
|
|
55
|
-
| `data` | the post-change row, or `null` on delete
|
|
45
|
+
| `data` | the post-change row, or `null` on delete |
|
|
56
46
|
| `syncId` | monotonic log position: **dedupe and order by this** |
|
|
57
47
|
| `id` | `String(syncId)`: the event id |
|
|
58
48
|
| `createdAt` | ISO commit timestamp |
|
package/examples/README.md
CHANGED
|
@@ -23,7 +23,7 @@ import { handleAbloSource, _inspectStore } from './customer-server';
|
|
|
23
23
|
import { AbloDriver } from './ablo-driver';
|
|
24
24
|
|
|
25
25
|
const API_KEY =
|
|
26
|
-
process.env.ABLO_API_KEY ?? '
|
|
26
|
+
process.env.ABLO_API_KEY ?? 'sk_example_key_do_not_use';
|
|
27
27
|
|
|
28
28
|
// `dataSource()` reads `options.apiKey` at request time; we re-export
|
|
29
29
|
// the same value to the driver so signer and verifier agree.
|
|
@@ -70,7 +70,7 @@ async function main() {
|
|
|
70
70
|
log('\n--- 5. signature failure (wrong API key) ---');
|
|
71
71
|
const badDriver = new AbloDriver({
|
|
72
72
|
handler: handleAbloSource,
|
|
73
|
-
apiKey: '
|
|
73
|
+
apiKey: 'sk_wrong_example_key',
|
|
74
74
|
});
|
|
75
75
|
try {
|
|
76
76
|
await badDriver.load('tasks', 'task_seed');
|
package/examples/quickstart.ts
CHANGED
package/llms.txt
CHANGED
|
@@ -25,11 +25,13 @@ First action when integrating into an app: run `npx ablo init --yes --framework
|
|
|
25
25
|
|
|
26
26
|
Second: make sure a key exists — WITHOUT printing it. The key is a secret; it must never appear in your output, your reasoning, or a file you echo (it would live in the conversation history forever). Check PRESENCE only: `[ -n "$ABLO_API_KEY" ] && echo set` and `grep -cq '^ABLO_API_KEY=' .env.local && echo wired` — never `cat .env.local`, never `echo $ABLO_API_KEY`. If neither check passes, ask the HUMAN to run `npx ablo login` once — it opens a browser and saves a project-scoped branch-management key locally; an agent must NOT run it. You never copy a runtime key by hand: the next step mints one for the development branch, writes it into `.env.local`, and gitignores it.
|
|
27
27
|
|
|
28
|
-
Then PREPARE THE BRANCH AND PUSH — this is the step everything depends on. The server keeps its OWN copy of the schema. Run `npx ablo dev --no-watch --branch <git-or-ci-ref>`: it ensures an isolated child branch, exchanges the CLI key for an expiring branch-only `
|
|
28
|
+
Then PREPARE THE BRANCH AND PUSH — this is the step everything depends on. The server keeps its OWN copy of the schema. Run `npx ablo dev --no-watch --branch <git-or-ci-ref>`: it ensures an isolated child branch, exchanges the CLI key for an expiring branch-only `sk_` key, writes that key to gitignored `.env.local`, pushes `ablo/schema.ts`, and exits. Until the schema is pushed, EVERY write to a new or changed model fails with `server_execute_unknown_model`. Re-run it after schema changes. Plain `dev` watches forever; never run it bare in an agent. `push` is the lower-level one-shot command for an already selected credential, including a reviewed production deployment.
|
|
29
|
+
|
|
30
|
+
Branches replace manual environment-key juggling. Runtime code always reads ONE `ABLO_API_KEY`; `ablo dev` wires the Git branch's expiring value. Do not create permanent `ABLO_STAGING_KEY` / `ABLO_DEV_KEY` selectors. `npx ablo whoami` strictly confirms the current key's project + immutable branch; `npx ablo whoami --key-env <NAME>` explicitly inspects a named process/`.env.local`/`.env` value for CI, migration, or stranded-plane recovery without putting the secret in argv. `status` is the broad readiness report; `whoami` is the narrow identity answer.
|
|
29
31
|
|
|
30
32
|
## Projects (one org, many apps)
|
|
31
33
|
|
|
32
|
-
Each app gets its own PROJECT inside the org — its own schema,
|
|
34
|
+
Each app gets its own PROJECT inside the org — its own production root + development/preview child branches, each with branch-bound schema, rows, database registration, claims, log, and credentials (the Neon/Supabase shape). `npx ablo init` creates one automatically (slug from package.json name; `--project <slug>` to choose, `--no-project` for the org default). A key belongs to exactly ONE project and branch, fixed at mint; everything it mints inherits that scope. Touching another project's models fails typed: `project_scope_denied` (403) — the fix is a key minted for THAT project, never `ablo push`. Manage: `npx ablo projects list|create <slug>|use <slug|default>`; `npx ablo status` shows readiness and `npx ablo whoami` strictly confirms the credential target. Wire: `GET/POST /api/v1/projects` (management authority; duplicate slug → `project_slug_taken` 409).
|
|
33
35
|
|
|
34
36
|
## Use this API
|
|
35
37
|
|
|
@@ -175,20 +177,20 @@ export const { POST } = dataSourceNext({
|
|
|
175
177
|
|
|
176
178
|
Connect a database with `npx ablo connect` (logical replication — the one path above). The signed Data Source endpoint (code above) is the fallback when app database credentials must stay private; scaffold it with `npx ablo init --storage endpoint` — Ablo only calls the endpoint.
|
|
177
179
|
|
|
178
|
-
##
|
|
180
|
+
## Public demo and development branches
|
|
179
181
|
|
|
180
182
|
Public `/sandbox` is a deterministic visual demo. It should teach shared state,
|
|
181
183
|
claims, stale-write rejection, receipts, and deltas, but it does not use a real
|
|
182
184
|
API key. It also exposes a Claude Code / Codex handoff prompt. Prefer that shape
|
|
183
185
|
when an agent is asked to "make Ablo work" in an existing app.
|
|
184
186
|
|
|
185
|
-
Authenticated development branches are real
|
|
186
|
-
derives one from Git and mints an expiring `
|
|
187
|
-
branch id. A
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
Authenticated development branches are real isolated environments. `ablo dev`
|
|
188
|
+
derives one from Git and mints an expiring `sk_*` key bound to its immutable
|
|
189
|
+
branch id. A child CAN host rows without your own database, so you can try Ablo
|
|
190
|
+
with `apiKey` only. (On the production root, your own Postgres is the system of
|
|
191
|
+
record.) Branch schemas and rows are isolated from siblings and production.
|
|
192
|
+
There is no shared-sandbox escape hatch and no different production key class:
|
|
193
|
+
the persisted branch binding distinguishes child from root.
|
|
192
194
|
|
|
193
195
|
For coding agents, the development success path is: pick one shared model,
|
|
194
196
|
declare schema, prepare an explicit branch with
|
|
@@ -198,9 +200,9 @@ one direct mutation with a typed `ablo.<model>.update(...)`, use selector
|
|
|
198
200
|
|
|
199
201
|
## Production
|
|
200
202
|
|
|
201
|
-
A PLANE is what a credential acts on: `production` is the root branch and development/preview branches are children. Rows, the registered database, and the active schema artifact are all PER PLANE.
|
|
203
|
+
A PLANE is what a credential acts on: `production` is the root branch and development/preview branches are children. Rows, the registered database, and the active schema artifact are all PER PLANE. Every `sk_` has an immutable persisted branch binding; its spelling does not distinguish root from child, so app code never passes an environment. A child copies its parent's active schema at creation and owns its artifact after that. A CHILD push does NOT reach production. Production gets models only when the reviewed deployment pushes to the root.
|
|
202
204
|
|
|
203
|
-
Going live is three things, each done with a `
|
|
205
|
+
Going live is three things, each done with a root-bound `sk_` key: register the production database (`ablo connect apply` — the DIRECT host, never a pooler; a pooler refuses in the words of a wrong password), push the schema AHEAD of the code that needs it, and hold the right credential per runtime (server/serverless `sk_`; browser `pk_` read-only or an `authEndpoint` minting `ek_`). `ablo login` stores an `mk_` management credential and CANNOT read/write application data or push the production schema — a production push needs a root-bound `sk_` in `ABLO_API_KEY`. Gate a deploy on `npx ablo status --json` having an EMPTY `blockers` array; each blocker carries a `problem` and the one `fix`. Your agents do NOT each hold a database connection — they talk to Ablo, and Ablo holds at most 4 connections per plane (`application_name = 'ablo-direct-writer'`) however many callers write behind them, so size the database for that number and not for your agent count. Read `deployment` for the full path.
|
|
204
206
|
|
|
205
207
|
## Public Surface
|
|
206
208
|
|
|
@@ -227,7 +229,7 @@ Do not teach `/api`, `/agent`, `/core`, `/realtime`, or internal subpaths. (`/so
|
|
|
227
229
|
- `npx ablo connect` connects your database via logical replication — the read path (prints the `wal_level=logical` + publication + `REPLICATION`-role SQL); `npx ablo connect register` registers it, `npx ablo connect check` validates the registered database from Ablo's own side and needs only `ABLO_API_KEY` (no database credential in your environment). `npx ablo connect apply` does the whole setup from a one-time admin connection and leaves your app holding only `ABLO_API_KEY`.
|
|
228
230
|
- Key: see "Start here" — env → `.env.local` → ask the human to `npx ablo login`; never run `login` yourself, never copy keys by hand (`ablo dev` mints and wires the temporary branch key).
|
|
229
231
|
- Adopt an existing DB: `npx ablo pull prisma [path]` / `npx ablo pull drizzle <module>`.
|
|
230
|
-
- `npx ablo dev --no-watch --branch <git-or-ci-ref>` ensures an isolated branch, mints and wires its temporary key, pushes once, and exits. Plain `dev` watches forever. `npx ablo branch check <id-or-slug>` prints lifecycle, schema/parent compatibility, datasource identity, and readiness. `npx ablo push` pushes with the already selected key and does NOT choose a branch for you. A PRODUCTION push (`
|
|
232
|
+
- `npx ablo dev --no-watch --branch <git-or-ci-ref>` ensures an isolated branch, mints and wires its temporary key, pushes once, and exits. Plain `dev` watches forever. `npx ablo branch check <id-or-slug>` prints lifecycle, schema/parent compatibility, datasource identity, and readiness. `npx ablo push` pushes with the already selected key and does NOT choose a branch for you. A PRODUCTION push (`sk_` key) needs `--yes`: with no TTY it REFUSES rather than deploying unattended, and interactively it demands the destination project typed by name. Destructive steps (dropped model/field, narrowed enum, lossy cast) need `--force`; a new required field on a populated table needs `--backfill model.field=value`. `npx ablo logs --no-follow` exits instead of tailing forever. `npx ablo push`/`status`/`pull`/`check`/`generate` are one-shot.
|
|
231
233
|
|
|
232
234
|
- `npx ablo docs` lists every documentation page; `npx ablo docs <page>` prints one as markdown. These pages ship INSIDE the installed package, so they describe the version in `node_modules` and need no network. Prefer them over a docs URL whenever the project pins a version: a website always describes the newest release, so on an older pin it will hand you a call your package does not have (`retrieve`/`list` replaced `get`/`getAll`/`getCount` in 0.35.0). One-shot, safe to run unattended.
|
|
233
235
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "The public Ablo SDK for coordinated reads, commits, claims, observation, and reactive applications.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -124,8 +124,8 @@
|
|
|
124
124
|
"directory": "packages/ablo"
|
|
125
125
|
},
|
|
126
126
|
"dependencies": {
|
|
127
|
-
"@abloatai/humans": "^0.
|
|
128
|
-
"@abloatai/transaction": "^0.
|
|
127
|
+
"@abloatai/humans": "^0.43.0",
|
|
128
|
+
"@abloatai/transaction": "^0.43.0"
|
|
129
129
|
},
|
|
130
130
|
"peerDependencies": {
|
|
131
131
|
"ai": "^6.0.0 || ^7.0.0",
|