@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/deployment.md
CHANGED
|
@@ -6,20 +6,17 @@ One command answers the question this page exists for — would a write succeed
|
|
|
6
6
|
right now, and if not, why:
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
ABLO_API_KEY=
|
|
9
|
+
ABLO_API_KEY=sk_… npx ablo status
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
13
|
ablo status
|
|
14
14
|
|
|
15
|
-
key
|
|
16
|
-
mode production
|
|
15
|
+
key sk_51H8… (ABLO_API_KEY env — overrides stored)
|
|
17
16
|
org org_3nKq…
|
|
18
17
|
project checkout (prj_7Yb2…)
|
|
19
|
-
acts on production
|
|
20
|
-
|
|
21
|
-
● production — no key
|
|
22
|
-
push production with sk_live_51H8… (env)
|
|
18
|
+
acts on production root
|
|
19
|
+
push production with sk_51H8… (env)
|
|
23
20
|
api https://api.abloatai.com reachable
|
|
24
21
|
data ✓ database connected to this plane (direct)
|
|
25
22
|
schema 4 models pushed (rev 12) hash 3f9a2c81 @ 2026-07-18
|
|
@@ -45,7 +42,7 @@ three pieces pointed at the same plane.
|
|
|
45
42
|
|
|
46
43
|
| Ingredient | Who runs it | What "deploying" means for it |
|
|
47
44
|
|---|---|---|
|
|
48
|
-
| **Your Postgres** | You (or your provider) | Registering it against
|
|
45
|
+
| **Your Postgres** | You (or your provider) | Registering it against the production root, once, with a root-bound key. |
|
|
49
46
|
| **Ablo** | Hosted at `api.abloatai.com` | Nothing to run. You choose a project, a plane, and the keys that reach them. |
|
|
50
47
|
| **Your app and agents** | You | Holding the right credential for the runtime, and pushing the schema in the deploy. |
|
|
51
48
|
|
|
@@ -62,10 +59,9 @@ per-plane, and knowing which three is most of what production readiness means:
|
|
|
62
59
|
your dev database are separate registrations.
|
|
63
60
|
- **The active schema artifact:** the model shapes the engine actually routes on.
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
passes an environment.
|
|
62
|
+
Every `sk_` is fixed to one branch at mint. The persisted branch id—not a
|
|
63
|
+
suffix or user-supplied slug—says whether that is the production root or one
|
|
64
|
+
development child. This is why application code never passes an environment.
|
|
69
65
|
|
|
70
66
|
A child copies its parent's active schema when it is created, then owns its
|
|
71
67
|
schema history. A child push never changes production. Production gets new
|
|
@@ -78,12 +74,12 @@ There is no shared development plane. New credentials are branch-bound; follow
|
|
|
78
74
|
|
|
79
75
|
Your production database joins Ablo the same way your dev database did — logical
|
|
80
76
|
replication so Ablo can read and confirm, a scoped writer role so Ablo can land
|
|
81
|
-
rows — run once, with a
|
|
82
|
-
production
|
|
77
|
+
rows — run once, with a root-bound key so the registration attaches to the
|
|
78
|
+
production root:
|
|
83
79
|
|
|
84
80
|
```bash
|
|
85
|
-
ABLO_API_KEY=
|
|
86
|
-
ABLO_API_KEY=
|
|
81
|
+
ABLO_API_KEY=sk_… npx ablo connect apply --url postgres://admin:…@host:5432/db
|
|
82
|
+
ABLO_API_KEY=sk_… npx ablo connect check
|
|
87
83
|
```
|
|
88
84
|
|
|
89
85
|
[Connect Your Database](./data-sources.md) is the full walkthrough — the SQL, the
|
|
@@ -129,28 +125,41 @@ runs. In production that resolves to four rows:
|
|
|
129
125
|
|
|
130
126
|
| Runtime | Credential | Notes |
|
|
131
127
|
|---|---|---|
|
|
132
|
-
| Server, worker, agent, cron | `
|
|
133
|
-
| Serverless function | `
|
|
134
|
-
| Browser, read-only | `
|
|
128
|
+
| Server, worker, agent, cron | `sk_` in `ABLO_API_KEY` | Defaults from the environment, so most code passes nothing. |
|
|
129
|
+
| Serverless function | `sk_` in `ABLO_API_KEY`, with `transport: 'http'` | Stateless request/response; nothing held open across invocations. |
|
|
130
|
+
| Browser, read-only | root-bound `pk_` | Publishable, safe to ship, and read-only. |
|
|
135
131
|
| Browser, writing as the signed-in user | `authEndpoint` | A route on your backend mints a short-lived `ek_` per user. |
|
|
136
132
|
|
|
137
133
|
[API Keys](./api-keys.md) covers the model; [Sessions](./sessions.md) covers
|
|
138
134
|
minting. Two things bite specifically at deploy time.
|
|
139
135
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
`ablo status --json` reports
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
**An explicit key always wins.**
|
|
150
|
-
`.env.local`, then `.env`, then
|
|
151
|
-
|
|
136
|
+
**`ablo login` does not give the application production authority.** It stores
|
|
137
|
+
a project-scoped `mk_` management credential that manages branches and exchanges
|
|
138
|
+
for temporary child credentials; it cannot read or write application data or
|
|
139
|
+
push the production schema. A production deploy needs a **secret** `sk_`
|
|
140
|
+
from the dashboard, supplied as `ABLO_API_KEY`. `ablo whoami` names what an
|
|
141
|
+
explicit key acts on, and `ablo status --json` reports its capability as
|
|
142
|
+
`runtimeKey.kind` for a pipeline to check before pushing. There is no local
|
|
143
|
+
mode switch.
|
|
144
|
+
|
|
145
|
+
**An explicit key always wins.** Application-facing commands resolve
|
|
146
|
+
`ABLO_API_KEY`, then `.env.local`, then `.env`, then a legacy stored runtime
|
|
147
|
+
credential — never the `mk_` management login for data operations. `ablo status`
|
|
148
|
+
prints what it found under `key`, with its source. When a deploy lands somewhere
|
|
152
149
|
surprising, that line is usually the answer.
|
|
153
150
|
|
|
151
|
+
Use `ablo whoami` when you need only the authoritative identity:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
ABLO_API_KEY=sk_… npx ablo whoami
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For a deployment secret already stored under another name, prefer
|
|
158
|
+
`ablo whoami --key-env <NAME>` over copying it into argv or permanently
|
|
159
|
+
maintaining parallel local variables. The command reads the explicitly named
|
|
160
|
+
value from the process or project env files and reports its project and branch
|
|
161
|
+
without printing the full secret.
|
|
162
|
+
|
|
154
163
|
## 3. Pushing the schema is a deploy step
|
|
155
164
|
|
|
156
165
|
The server keeps its own copy of your schema and routes on that copy. Until it
|
|
@@ -159,7 +168,9 @@ has yours, a write to a new model fails with `server_execute_unknown_model` —
|
|
|
159
168
|
depends on the new models goes live.
|
|
160
169
|
|
|
161
170
|
```bash
|
|
162
|
-
ABLO_API_KEY=
|
|
171
|
+
ABLO_API_KEY=sk_… npx ablo push --yes
|
|
172
|
+
# or, explicitly:
|
|
173
|
+
npx ablo push --env-file .env.production --yes
|
|
163
174
|
```
|
|
164
175
|
|
|
165
176
|
Production requires confirmation: interactively you type the destination
|
|
@@ -210,8 +221,8 @@ deployed HTTPS endpoint is registered once, and Ablo returns the signing secret
|
|
|
210
221
|
a single time:
|
|
211
222
|
|
|
212
223
|
```bash
|
|
213
|
-
ABLO_API_KEY=
|
|
214
|
-
ABLO_API_KEY=
|
|
224
|
+
ABLO_API_KEY=sk_… npx ablo webhooks create https://yourapp.com/api/ablo/[...all]
|
|
225
|
+
ABLO_API_KEY=sk_… npx ablo webhooks list # endpoints + delivery health
|
|
215
226
|
```
|
|
216
227
|
|
|
217
228
|
`webhooks list` reports each endpoint's status, cursor, and last error — the
|
|
@@ -220,9 +231,9 @@ handler, the Standard Webhooks signature, and rolling a secret.
|
|
|
220
231
|
|
|
221
232
|
## What to watch once it is live
|
|
222
233
|
|
|
223
|
-
- **`ablo logs`:** commit activity as it happens, scoped by the key
|
|
224
|
-
key streams
|
|
225
|
-
NDJSON for piping.
|
|
234
|
+
- **`ablo logs`:** commit activity as it happens, scoped by the key. A
|
|
235
|
+
root-bound key streams production; a child-bound key streams only that child.
|
|
236
|
+
`--json` emits NDJSON for piping.
|
|
226
237
|
- **`ablo status`:** the readiness verdict. Cheap enough to run from a health
|
|
227
238
|
check on your own side.
|
|
228
239
|
- **The [audit log](./audit.md):** every confirmed write traced back to the key
|
|
@@ -244,17 +255,17 @@ and what each promises.
|
|
|
244
255
|
| `server_execute_unknown_model` | The plane's active schema does not carry that model. | `ablo push` with a key for that plane. |
|
|
245
256
|
| Clients rejected at connect | The deployed schema and the client's schema disagree. | Push this tree, or deploy the revision the server is running. |
|
|
246
257
|
| `project_scope_denied` (403) | The model belongs to another project in your org. | Use a key minted for that project: a push cannot cross projects. |
|
|
247
|
-
| 403 on `ablo push` | The key authenticated but cannot author schema. | A secret `
|
|
258
|
+
| 403 on `ablo push` | The key authenticated but cannot author schema. | A secret `sk_`; the `mk_` stored by `ablo login` is management-only. |
|
|
248
259
|
|
|
249
260
|
## The checklist
|
|
250
261
|
|
|
251
262
|
1. Production database registered against the production plane, direct host, and
|
|
252
263
|
`ablo connect check` all green.
|
|
253
|
-
2. A secret `
|
|
264
|
+
2. A secret `sk_` in the deploy environment as `ABLO_API_KEY` — never in a
|
|
254
265
|
browser bundle.
|
|
255
266
|
3. `ablo push --yes` in the pipeline, ahead of the code that needs the new models.
|
|
256
267
|
4. `ablo status --json` gating the deploy on an empty `blockers` array.
|
|
257
|
-
5. Browser clients on a `
|
|
268
|
+
5. Browser clients on a root-bound `pk_` or an `authEndpoint`, not a secret key.
|
|
258
269
|
6. Webhook endpoints registered at their deployed URLs, with the signing secret
|
|
259
270
|
in your environment.
|
|
260
271
|
|
package/docs/how-it-works.md
CHANGED
|
@@ -93,7 +93,7 @@ npx ablo connect apply --url postgres://admin:...@host:5432/db
|
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
95
|
# Your app's environment holds only the API key — never a connection string.
|
|
96
|
-
ABLO_API_KEY=
|
|
96
|
+
ABLO_API_KEY=sk_...
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
```ts
|
|
@@ -127,8 +127,9 @@ here is the current, correct reading:
|
|
|
127
127
|
It is not the write path. You write **through** Ablo.
|
|
128
128
|
- **"Ablo is out of the write path."** Not anymore. Ablo is the write chokepoint;
|
|
129
129
|
every write flows through its coordination and then into your database.
|
|
130
|
-
- **"Ablo holds my rows."** Only
|
|
131
|
-
database, your rows live in *your*
|
|
130
|
+
- **"Ablo holds my rows."** Only on a development branch where you have not
|
|
131
|
+
connected a database yet. Once you connect one, your rows live in *your*
|
|
132
|
+
Postgres — Ablo keeps just the log.
|
|
132
133
|
- **"Ablo migrates my schema."** No. Ablo writes rows through a scoped role and runs
|
|
133
134
|
no DDL; your migrations own the shape of your database.
|
|
134
135
|
|
package/docs/idempotency.md
CHANGED
|
@@ -62,7 +62,7 @@ use a new key.
|
|
|
62
62
|
|
|
63
63
|
## Failures are not replayed: they re-run
|
|
64
64
|
|
|
65
|
-
This is where Ablo deliberately differs from
|
|
65
|
+
This is where Ablo deliberately differs from most request/response APIs, and it is the
|
|
66
66
|
behaviour most likely to surprise you.
|
|
67
67
|
|
|
68
68
|
**Only successful writes are recorded.** A write that failed leaves no idempotency record, so
|
package/docs/identity.md
CHANGED
|
@@ -318,7 +318,7 @@ session" glossed over. Concretely:
|
|
|
318
318
|
|
|
319
319
|
1. **Your `ABLO_API_KEY` lives only on your trusted server**, scoped to your
|
|
320
320
|
account. It signs your app's relationship with Ablo. It must never reach a
|
|
321
|
-
browser bundle
|
|
321
|
+
browser bundle.
|
|
322
322
|
2. **Your server authenticates the user with your own system.** That's the
|
|
323
323
|
request that knows "this is user `U`, org `O`, teams `[...]`".
|
|
324
324
|
3. **Your server hands that authenticated identity to Ablo**, and the browser
|
|
@@ -342,9 +342,8 @@ server, never by the browser.**
|
|
|
342
342
|
> the client could name its own org or sync groups, any user could read another
|
|
343
343
|
> tenant's data by editing a request. By keeping the API key server-side and
|
|
344
344
|
> deriving scope from the identity your server already authenticated, the trust
|
|
345
|
-
> boundary lands in the one place you control.
|
|
346
|
-
>
|
|
347
|
-
> server-side.
|
|
345
|
+
> boundary lands in the one place you control. Scope resolution and session
|
|
346
|
+
> minting therefore stay server-side.
|
|
348
347
|
|
|
349
348
|
## Wiring the provider
|
|
350
349
|
|
|
@@ -59,7 +59,7 @@ confirms it over the WAL. Application and agent code hold only `ABLO_API_KEY`
|
|
|
59
59
|
client never sees a connection string. [Connect Your Database](./data-sources.md)
|
|
60
60
|
is the single source of truth for both paths.
|
|
61
61
|
|
|
62
|
-
##
|
|
62
|
+
## Try the public sandbox demo
|
|
63
63
|
|
|
64
64
|
Use the public `/sandbox` page to understand the state flow. It is a visual,
|
|
65
65
|
deterministic demo; it does not call your API key or mutate hosted Ablo data.
|
|
@@ -69,7 +69,7 @@ Codex and ask it to wire one real model through the schema model API.
|
|
|
69
69
|
Use `npx ablo dev` for real integration work. It derives an immutable branch
|
|
70
70
|
from Git, inherits the parent schema, and writes a temporary branch credential
|
|
71
71
|
to `.env.local`. Each developer or pull request gets independent schema, rows,
|
|
72
|
-
claims, and logs. Use an explicit `
|
|
72
|
+
claims, and logs. Use an explicit `sk_*` root credential only in the
|
|
73
73
|
reviewed production deployment.
|
|
74
74
|
|
|
75
75
|
When handing this to a coding agent, give it a concrete target:
|
|
@@ -231,9 +231,9 @@ export async function POST() {
|
|
|
231
231
|
|
|
232
232
|
### Why two credential shapes
|
|
233
233
|
|
|
234
|
-
`ABLO_API_KEY` is your long-lived account credential.
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
`ABLO_API_KEY` is your long-lived account credential. It stays on trusted
|
|
235
|
+
servers, never reaches a browser bundle, and signs server-to-server requests.
|
|
236
|
+
It is the right credential
|
|
237
237
|
for trusted runtimes (Next.js server actions, background workers,
|
|
238
238
|
migration scripts) where the code reading it is yours.
|
|
239
239
|
|
|
@@ -250,10 +250,8 @@ ABLO_API_KEY ─exchange─► scoped token ────────► narrow
|
|
|
250
250
|
server only) revocable)
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
AssumeRole (returns time-bounded creds with the minimal needed scope).
|
|
256
|
-
You never type that token into your app; the SDK mints one when it needs one and
|
|
253
|
+
You never type that token into your app; the SDK mints a time-bounded,
|
|
254
|
+
minimally scoped token when it needs one and
|
|
257
255
|
refreshes before expiry.
|
|
258
256
|
|
|
259
257
|
## 3. Read State
|
|
@@ -440,7 +438,7 @@ Ablo needs your Data Source endpoint and API key. Your app
|
|
|
440
438
|
stores one Ablo credential:
|
|
441
439
|
|
|
442
440
|
```bash
|
|
443
|
-
ABLO_API_KEY=
|
|
441
|
+
ABLO_API_KEY=sk_...
|
|
444
442
|
```
|
|
445
443
|
|
|
446
444
|
The API key verifies Ablo's request. It is not a database credential.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Maintainer scoping doc. Closes the one real day-one DX gap in Data Source
|
|
4
4
|
mode: the `commit`/`load`/`list` legs are inbound webhooks (Ablo → your
|
|
5
5
|
endpoint), so on `localhost` they need a tunnel (ngrok/cloudflared). This
|
|
6
|
-
scopes a built-in reverse channel so Data Source works on localhost
|
|
7
|
-
|
|
6
|
+
scopes a built-in reverse channel so Data Source works on localhost without a
|
|
7
|
+
public tunnel.
|
|
8
8
|
|
|
9
9
|
## The gap, precisely
|
|
10
10
|
|
|
@@ -26,10 +26,9 @@ managed mode "just works" locally and Data Source doesn't.
|
|
|
26
26
|
|
|
27
27
|
## Prior art
|
|
28
28
|
|
|
29
|
-
- **
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for the `commit`/`load`/`list` leg.
|
|
29
|
+
- **Outbound relay:** the CLI opens a WebSocket to the hosted service, receives
|
|
30
|
+
events over that connection, and forwards them to `localhost`. No public URL
|
|
31
|
+
or tunnel is required. We want the same for the `commit`/`load`/`list` leg.
|
|
33
32
|
- **Our own `createPushQueue`:** already proves the outbound-from-customer
|
|
34
33
|
pattern for the `events` leg. The reverse channel is the symmetric primitive
|
|
35
34
|
for the other direction.
|
|
@@ -78,7 +77,7 @@ import { dataSource, createSourceConnector } from '@abloatai/ablo';
|
|
|
78
77
|
import { sourceOptions } from './ablo.source'; // shared with route.ts
|
|
79
78
|
|
|
80
79
|
const connector = createSourceConnector({
|
|
81
|
-
apiKey: process.env.ABLO_API_KEY!, //
|
|
80
|
+
apiKey: process.env.ABLO_API_KEY!, // sk_* bound to a child branch
|
|
82
81
|
handler: dataSource(sourceOptions), // the unchanged (Request)=>Response
|
|
83
82
|
});
|
|
84
83
|
await connector.run(abortSignal);
|
|
@@ -90,8 +89,8 @@ await connector.run(abortSignal);
|
|
|
90
89
|
### Server side (sync-server)
|
|
91
90
|
|
|
92
91
|
- New WS endpoint `/v1/source/listen`. Auth: project API key → resolves the
|
|
93
|
-
source. Reject if the key
|
|
94
|
-
opts into reverse
|
|
92
|
+
source. Reject if the persisted key binding is the root branch unless the
|
|
93
|
+
source explicitly opts into a production reverse channel (see below).
|
|
95
94
|
- Per-source request queue. When a `commit`/`load`/`list` needs the customer
|
|
96
95
|
and a connector is attached, enqueue + drain down the socket instead of
|
|
97
96
|
POSTing the webhook URL. Reuse the same signed-envelope shape so the
|
|
@@ -106,18 +105,16 @@ await connector.run(abortSignal);
|
|
|
106
105
|
project key, so the connector verifies them through the existing
|
|
107
106
|
`verifyAbloSourceRequest` with no special-casing. The transport changes; the
|
|
108
107
|
trust model does not.
|
|
109
|
-
- Gate to
|
|
110
|
-
customer's process; nothing here gives Ablo the `DATABASE_URL`.
|
|
111
|
-
|
|
112
|
-
##
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
piece that makes `sk_test_*` a complete local loop rather than just a data
|
|
120
|
-
namespace.
|
|
108
|
+
- Gate to non-root branch bindings by default. The DB still stays canonical in
|
|
109
|
+
the customer's process; nothing here gives Ablo the `DATABASE_URL`.
|
|
110
|
+
|
|
111
|
+
## Development-branch interplay
|
|
112
|
+
|
|
113
|
+
The reverse channel is the natural home for child-branch traffic: a local
|
|
114
|
+
connector attached with a child-bound `sk_*` receives that branch's commits,
|
|
115
|
+
runs them against the customer's development DB, and the SDK sees confirmed
|
|
116
|
+
rows and fan-out exactly as on the root. The server resolves child versus root
|
|
117
|
+
from the persisted branch binding, never from the key spelling.
|
|
121
118
|
|
|
122
119
|
## Production stance
|
|
123
120
|
|
package/docs/migration.md
CHANGED
|
@@ -13,13 +13,14 @@ change when you upgrade.
|
|
|
13
13
|
|
|
14
14
|
| Version | What changed | What to do |
|
|
15
15
|
|---|---|---|
|
|
16
|
+
| **Next** | New keys no longer encode `live`/`test`; branch binding is authoritative | Accept the new `sk_…`/`rk_…`/`pk_…`/`ek_…` spellings. Do not route or authorize from a key prefix. Old `_live_`/`_test_` keys continue to authenticate; rotate them to mint the current form |
|
|
16
17
|
| **0.36.0** | `ttlSeconds` deprecated on the join surfaces in favour of `ttl` | `useJoin({ scope, ttlSeconds: '5m' })` → `useJoin({ scope, ttl: '5m' })`; same for `ParticipantJoinOptions`. Both spellings work until 0.37.0 |
|
|
17
18
|
| **0.35.0** | Synchronous reads moved under `local`, mirroring the async verbs | `get(id)` → `local.get(id)`; `getAll(options)` → `local.list(options)`; `getCount(options)` → `local.count(options)` |
|
|
18
19
|
| **0.35.0** | `causedByTaskId` write option + seven `turn_*` error codes removed | Delete the `causedByTaskId` argument from writes; a branch on `turn_validation_failed` was unreachable and can go with it |
|
|
19
20
|
| **0.34.0** | Presence verb renamed `watch` → `join` | `ablo.<model>.watch(ids)` → `ablo.<model>.join(ids)`; `useWatch` → `useJoin`; the `WatchOptions` / `UseWatchOptions` / `UseWatchReturn` types → `JoinOptions` / `UseJoinOptions` / `UseJoinReturn`; error code `model_watch_not_configured` → `model_join_not_configured` |
|
|
20
21
|
| **0.28.0** | Removed React placeholders that had no working runtime | `usePresence` → `usePeers` or `useJoin`; `useClaim` → `ablo.<model>.claim`; `SyncGroupProvider` / `useSyncGroup` → `useJoin({ scope })` |
|
|
21
22
|
| **0.11.0** | Historical `intent` → `claim` rename | The hook renamed in that release was later removed in 0.28.0. Current code uses `ablo.<model>.claim` or `useJoin` |
|
|
22
|
-
| **0.10.0** | Environment enum renamed `test`/`live` → `sandbox`/`production` |
|
|
23
|
+
| **0.10.0** | Environment enum renamed `test`/`live` → `sandbox`/`production` | Historical: update code that branches on the environment (`'test'`→`'sandbox'`, `'live'`→`'production'`). That release retained `sk_test_`/`sk_live_`; current releases mint mode-free, branch-bound keys |
|
|
23
24
|
| **0.9.2** | `turn` primitive + agent-work `tasks` resource removed | Coordinate with `claim`; mint a scoped session instead of `agent().run()` |
|
|
24
25
|
| **0.9.2** | `intents` deprecated in favor of `claim` | Use `ablo.<model>.claim`; `ablo.intents` is now `@internal` |
|
|
25
26
|
| **0.9.0** | One options object per verb | `update(id, data, opts)` → `update({ id, data, ...opts })` |
|
|
@@ -32,6 +33,27 @@ change when you upgrade.
|
|
|
32
33
|
|
|
33
34
|
---
|
|
34
35
|
|
|
36
|
+
## Next: key spelling follows capability, branch binding follows authority
|
|
37
|
+
|
|
38
|
+
New runtime credentials have one prefix per capability class:
|
|
39
|
+
|
|
40
|
+
```diff
|
|
41
|
+
- sk_live_… / sk_test_…
|
|
42
|
+
+ sk_…
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The same applies to `rk_`, `pk_`, and `ek_`. This does **not** merge branch
|
|
46
|
+
data or weaken production safety. Every key row is still immutably bound to one
|
|
47
|
+
project and one branch, and the server-confirmed `branchRoot` flag distinguishes
|
|
48
|
+
the protected production root from a development child.
|
|
49
|
+
|
|
50
|
+
Remove code that chooses an endpoint, environment, or confirmation policy by
|
|
51
|
+
matching `_live_` or `_test_`. Use the resolved identity (`ablo whoami` in the
|
|
52
|
+
CLI) instead. Existing suffixed credentials remain valid compatibility inputs,
|
|
53
|
+
so rollout does not require an immediate rotation.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
35
57
|
## 0.36.0: one lease is spelled `ttl`
|
|
36
58
|
|
|
37
59
|
```diff
|
|
@@ -187,14 +209,18 @@ on presence and claim state.
|
|
|
187
209
|
|
|
188
210
|
## 0.10.0: environment enum `sandbox` / `production`; stateless HTTP transport
|
|
189
211
|
|
|
212
|
+
> Historical migration note: current development uses immutable branches, not a
|
|
213
|
+
> shared sandbox selector. The enum and compatibility fields below remain
|
|
214
|
+
> relevant to integrations written against 0.10, but new applications let the
|
|
215
|
+
> branch-bound credential select the plane.
|
|
216
|
+
|
|
190
217
|
### Environment enum rename (the only breaking change)
|
|
191
218
|
|
|
192
|
-
|
|
193
|
-
`live` and `test`).
|
|
194
|
-
on-the-wire
|
|
195
|
-
`
|
|
196
|
-
|
|
197
|
-
`production` / `sandbox`.
|
|
219
|
+
In 0.10 the canonical environment values became **`production`** and
|
|
220
|
+
**`sandbox`** (from `live` and `test`). At that release this was only a
|
|
221
|
+
vocabulary change at the type/API layer: on-the-wire keys still used
|
|
222
|
+
`sk_test_…` / `sk_live_…`. Current releases accept those legacy spellings but
|
|
223
|
+
mint mode-free `sk_…` keys whose persisted branch binding is authoritative.
|
|
198
224
|
|
|
199
225
|
You only need to act if your code branches on the environment value — most
|
|
200
226
|
commonly a Data Source handler keyed on `mode`. The mapping is exactly
|
package/docs/projects.md
CHANGED
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
A **project** is the isolation unit inside your organization — the shape you
|
|
6
6
|
know from Neon or Supabase. Each app you build gets its own project, and each
|
|
7
|
-
project gets its own
|
|
8
|
-
|
|
7
|
+
project gets its own root and child branches, schemas, data planes, and API
|
|
8
|
+
keys. Two teams in one org can ship two apps that never see each
|
|
9
9
|
other's models, keys, or rows.
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
12
|
organization
|
|
13
13
|
├── project: default ← every org has one; pre-project apps live here
|
|
14
|
-
│ ├──
|
|
15
|
-
│ ├──
|
|
16
|
-
│ └──
|
|
14
|
+
│ ├── production root
|
|
15
|
+
│ ├── development/preview branches
|
|
16
|
+
│ └── branch-bound schema, data plane, and credentials
|
|
17
17
|
└── project: my-app ← npx ablo init creates this for a new app
|
|
18
|
-
├──
|
|
19
|
-
├──
|
|
20
|
-
└──
|
|
18
|
+
├── production root
|
|
19
|
+
├── development/preview branches
|
|
20
|
+
└── branch-bound schema, data plane, and credentials
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## The default project
|
|
@@ -40,19 +40,23 @@ Schema pushes, database registrations, reads, and writes all act on the
|
|
|
40
40
|
|
|
41
41
|
- `npx ablo push` activates the schema for the pushing key's project — it
|
|
42
42
|
can never demote another project's schema.
|
|
43
|
-
- Registering a database (`DATABASE_URL`) attaches it to the key's project
|
|
44
|
-
|
|
43
|
+
- Registering a database (`DATABASE_URL`) attaches it to the key's project and
|
|
44
|
+
immutable branch.
|
|
45
45
|
- A write or read against a model that belongs to **another** project in
|
|
46
46
|
your org fails with a typed `project_scope_denied` — never a silent empty
|
|
47
47
|
result, and never the misleading "unknown model, run ablo push".
|
|
48
48
|
|
|
49
|
-
##
|
|
49
|
+
## Branches belong to a project
|
|
50
50
|
|
|
51
|
-
Production is
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
schema
|
|
55
|
-
|
|
51
|
+
Production is the protected root branch; development and preview branches are
|
|
52
|
+
children. Each branch has its own rows, active schema artifact, claims, log,
|
|
53
|
+
credentials, and optional registered database. A child copies the parent's
|
|
54
|
+
active schema when created and owns its history afterward, so a feature-branch
|
|
55
|
+
push cannot change a sibling or production.
|
|
56
|
+
|
|
57
|
+
`ablo dev` derives a child from Git and mints an expiring `sk_` credential
|
|
58
|
+
bound to its immutable branch id. Production runtimes use `sk_` bound to
|
|
59
|
+
the root. There is no shared sandbox schema and no runtime environment switch.
|
|
56
60
|
|
|
57
61
|
## CLI
|
|
58
62
|
|
|
@@ -68,15 +72,16 @@ npx ablo projects use default # back to the org default
|
|
|
68
72
|
npx ablo status # shows the active project
|
|
69
73
|
```
|
|
70
74
|
|
|
71
|
-
The active project is a local targeting preference
|
|
72
|
-
|
|
73
|
-
existing key can reach —
|
|
74
|
-
mint.
|
|
75
|
+
The active project is a local targeting preference in your CLI config: new
|
|
76
|
+
management and branch credentials you mint pick it up. It never changes what
|
|
77
|
+
an existing key can reach — project and branch scope are decided server-side at
|
|
78
|
+
mint. Use `npx ablo whoami` to confirm the exact target of the active
|
|
79
|
+
credential.
|
|
75
80
|
|
|
76
81
|
## API
|
|
77
82
|
|
|
78
|
-
Projects are a
|
|
79
|
-
|
|
83
|
+
Projects are a control-plane resource authenticated with a management (`mk_`)
|
|
84
|
+
key:
|
|
80
85
|
|
|
81
86
|
```bash
|
|
82
87
|
curl https://api.abloatai.com/api/v1/projects \
|
package/docs/quickstart.md
CHANGED
|
@@ -11,7 +11,7 @@ which stays the system of record. Ablo writes rows but **runs no DDL and owns no
|
|
|
11
11
|
schema** — your migration tool stays in charge of the shape of your database.
|
|
12
12
|
|
|
13
13
|
> No database yet? Pass an `apiKey` only and Ablo keeps your rows in its own log,
|
|
14
|
-
> so you can build the whole app today
|
|
14
|
+
> so you can build the whole app today. Point it at a
|
|
15
15
|
> separate or local Postgres for a development branch, or at your production
|
|
16
16
|
> database when you're ready.
|
|
17
17
|
|
|
@@ -35,12 +35,18 @@ export ABLO_MANAGEMENT_KEY=mk_...
|
|
|
35
35
|
|
|
36
36
|
Every runtime call needs a branch-bound API key. `ablo dev` exchanges the
|
|
37
37
|
stored management credential for
|
|
38
|
-
an expiring `
|
|
39
|
-
Production runtimes use `
|
|
38
|
+
an expiring `sk_*` key bound to the current development branch.
|
|
39
|
+
Production runtimes use `sk_*`. In production a key points at the database
|
|
40
40
|
*you* own; on a development branch you can skip the database entirely and let
|
|
41
41
|
Ablo host the rows (apiKey only). There is no keyless mode — a key is always
|
|
42
42
|
required. (The public `/sandbox` page is a separate hosted demo, not your app.)
|
|
43
43
|
|
|
44
|
+
You do not create a separate named variable per development environment.
|
|
45
|
+
Application code always reads `ABLO_API_KEY`; `ablo dev` wires the value for the
|
|
46
|
+
current branch. Run `npx ablo whoami` whenever you want the server-confirmed
|
|
47
|
+
project and branch for that value. See [API Keys](./api-keys.md) for the full
|
|
48
|
+
project → branch → credential model.
|
|
49
|
+
|
|
44
50
|
## 2. Your Ablo schema (init scaffolded it)
|
|
45
51
|
|
|
46
52
|
The schema is the contract — it generates `ablo.<model>` methods for app code,
|
|
@@ -149,7 +155,7 @@ Your **app** holds only the API key — never a connection string:
|
|
|
149
155
|
|
|
150
156
|
```bash
|
|
151
157
|
# .env — server runtime only, never the browser
|
|
152
|
-
ABLO_API_KEY=
|
|
158
|
+
ABLO_API_KEY=sk_...
|
|
153
159
|
```
|
|
154
160
|
|
|
155
161
|
```ts
|
package/docs/react.md
CHANGED
|
@@ -21,8 +21,7 @@ import { useAblo } from '@abloatai/ablo/react';
|
|
|
21
21
|
You build the Ablo client once — that's where the schema, the session endpoint,
|
|
22
22
|
and connection config live — then hand it to the provider. The provider takes
|
|
23
23
|
the already-built `client`; it no longer takes `schema`, `url`, `apiKey`, etc.
|
|
24
|
-
as props.
|
|
25
|
-
the thing, then pass it.
|
|
24
|
+
as props. Construct the client once, then pass that instance to the provider.
|
|
26
25
|
|
|
27
26
|
```ts
|
|
28
27
|
// lib/ablo.ts
|
package/docs/schema-contract.md
CHANGED
|
@@ -106,11 +106,11 @@ the fresh row. Reads stay open; only acting on the row serializes.
|
|
|
106
106
|
|
|
107
107
|
Every schema model is backed by your own database, and you write to it through
|
|
108
108
|
`ablo.<model>`. There are three start states, all covered in [Connect Your
|
|
109
|
-
Database](./data-sources.md) (the single source of truth):
|
|
110
|
-
|
|
111
|
-
scoped writer role plus logical replication, so Ablo
|
|
112
|
-
them over the WAL), or a signed Data Source
|
|
113
|
-
replication.
|
|
109
|
+
Database](./data-sources.md) (the single source of truth): a development branch
|
|
110
|
+
with no database yet (`apiKey` only — Ablo keeps that branch's rows in its own
|
|
111
|
+
log), `npx ablo connect` (a scoped writer role plus logical replication, so Ablo
|
|
112
|
+
writes your rows and confirms them over the WAL), or a signed Data Source
|
|
113
|
+
endpoint when your database can't grant replication.
|
|
114
114
|
|
|
115
115
|
Your database connects out of band, so the client holds only `ABLO_API_KEY` —
|
|
116
116
|
never a connection string. Browser code goes through `<AbloProvider>` or a scoped
|
package/docs/session-settings.md
CHANGED
|
@@ -45,8 +45,8 @@ context, whether or not you map anything:
|
|
|
45
45
|
| --- | --- |
|
|
46
46
|
| `app.current_org_id` | The organization the credential acts for |
|
|
47
47
|
| `app.current_project_id` | The project |
|
|
48
|
-
| `app.current_environment` | The
|
|
49
|
-
| `app.current_sandbox_id` |
|
|
48
|
+
| `app.current_environment` | The key's test/live trust class (`sandbox`/`production` on this compatibility surface) |
|
|
49
|
+
| `app.current_sandbox_id` | Legacy compatibility coordinate for older test planes; not a branch selector |
|
|
50
50
|
| `app.current_participant_id` | The participant making the write |
|
|
51
51
|
| `app.current_participant_kind` | Whether that participant is a person, an agent, or the system |
|
|
52
52
|
| `app.current_user_id` | The person on whose behalf the write is made |
|
|
@@ -70,6 +70,11 @@ authenticated key and the plane:
|
|
|
70
70
|
`orgId` · `projectId` · `environment` · `sandboxId` · `participantId` ·
|
|
71
71
|
`participantKind`
|
|
72
72
|
|
|
73
|
+
`environment` and `sandboxId` retain their published names for compatibility
|
|
74
|
+
with existing RLS policies. Current development isolation comes from the
|
|
75
|
+
credential's immutable branch binding; callers do not choose a sandbox or
|
|
76
|
+
branch through session settings.
|
|
77
|
+
|
|
73
78
|
Because none of them come from the caller, a mapping can forward the tenant
|
|
74
79
|
identity Ablo already trusts, but cannot widen what a writer sees. A setting name
|
|
75
80
|
takes exactly one source — the name is the key — so naming the same setting twice
|