@abloatai/ablo 0.42.0 → 0.44.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 +78 -7
- package/docs/deployment.md +53 -42
- 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/AGENTS.md
CHANGED
|
@@ -13,9 +13,10 @@ Don't hand-write the integration. Run the CLI; it generates the current-API sche
|
|
|
13
13
|
- **Auth:** set `ABLO_API_KEY` in the environment. Do **NOT** run `ablo login` — it opens a browser device flow and blocks an agent.
|
|
14
14
|
- **Connect your database — logical replication (the primary path):** `npx ablo connect` prints the setup SQL (`wal_level=logical`, a publication, a `REPLICATION` role); `npx ablo connect register` registers the source with Ablo in one step. Ablo **consumes your Postgres' logical-replication stream** — it never runs DDL on, writes to, owns, or migrates your database, and your application keeps the write path. Registration **is** the enable; there is no tier or flag to pick. (Ablo hosts only the transaction log + coordination, never your rows.)
|
|
15
15
|
- **Fallback — signed Data Source endpoint** (DB can't grant a `REPLICATION` role): the generated `ablo/data-source.ts` exposes one route; Ablo sends signed requests and your app touches its own DB. **Only in this mode** does `npx ablo migrate` provision the adapter's bookkeeping tables (`ablo_outbox`, `ablo_idempotency`) plus your Ablo models — it does **not** touch your other tables. Keep your own migrations (drizzle-kit / prisma migrate) for auth and anything outside the Ablo schema.
|
|
16
|
-
- **No database yet?** Run `npx ablo dev --no-watch --branch <name>` to create an isolated non-root branch and obtain its expiring `
|
|
16
|
+
- **No database yet?** Run `npx ablo dev --no-watch --branch <name>` to create an isolated non-root branch and obtain its expiring `sk_` credential. The branch uses a throwaway hosted data plane; Production remains the protected root. There is no shared Sandbox mode.
|
|
17
17
|
- **Adopt an existing DB schema:** `npx ablo pull prisma [path]` / `pull drizzle <module>` (lossless) or `pull` (live DB, lossy). Writes `ablo/schema.ts`.
|
|
18
18
|
- **Prepare a branch and push your schema — REQUIRED before any write works.** The server keeps its OWN copy of the schema. After you create or edit `ablo/schema.ts`, run `npx ablo dev --no-watch --branch <git-or-ci-ref>`: it ensures an isolated branch, mints a temporary branch credential, writes it to gitignored `.env.local`, pushes once, and exits. **Skip this and every write to a new or changed model fails with `server_execute_unknown_model`.** Plain `ablo dev` watches forever — never run it bare in an agent. `ablo push` is the lower-level one-shot command for an already selected key, including reviewed production deploys.
|
|
19
|
+
- **Do not invent one key variable per environment.** Branches replaced manual `ABLO_STAGING_KEY` / `ABLO_DEV_KEY` switching. Runtime code always reads `ABLO_API_KEY`; `ablo dev` wires the Git branch's expiring key. Use `npx ablo whoami` for the strict server-confirmed project/branch, or `npx ablo whoami --key-env <NAME>` only to inspect an explicitly named CI/legacy/recovery key without copying its value into argv.
|
|
19
20
|
- **Other long-running:** `npx ablo logs --no-follow` (default tails forever). `status`, `branch check`, `push`, `pull`, `check`, `generate`, `docs`, and `dev --no-watch` are one-shot.
|
|
20
21
|
|
|
21
22
|
When you use the signed-endpoint fallback, the generated `ablo/data-source.ts` is the whole endpoint and needs no hand-editing: `dataSourceNext({ schema, apiKey, adapter: prismaDataSource(prisma, schema) })` (or `drizzleDataSource(db, schema)`). The adapter owns commit / idempotency / outbox.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.44.0
|
|
4
|
+
|
|
5
|
+
### A scope denial names the wall it hit
|
|
6
|
+
|
|
7
|
+
`capability_scope_denied` now distinguishes the Ablo capability allowlist
|
|
8
|
+
from the customer database's row-level security. The error carries the
|
|
9
|
+
required capability, the resolved operations, the participant and user
|
|
10
|
+
principal, the branch, the organization and project, and any applied session
|
|
11
|
+
settings, so "permission denied" is a diagnosis instead of a dead end: you
|
|
12
|
+
can see whether your grant was missing a verb or whether your own database's
|
|
13
|
+
row policy rejected the session context Ablo applied.
|
|
14
|
+
|
|
15
|
+
### Write failures carry their request id
|
|
16
|
+
|
|
17
|
+
A WebSocket write failure now carries the `requestId` the server logged it
|
|
18
|
+
under, and a `wait: 'confirmed'` write rejects with the complete typed error
|
|
19
|
+
rather than a bare failure, so the error you catch is the error the server
|
|
20
|
+
recorded.
|
|
21
|
+
|
|
22
|
+
### `doctor` reports readiness, not destiny
|
|
23
|
+
|
|
24
|
+
`doctor` now says infrastructure is ready rather than promising a write will
|
|
25
|
+
succeed, because database constraints and row-level security still apply at
|
|
26
|
+
write time. The debugging guide explains how to read the new diagnostics,
|
|
27
|
+
and documents that `list()` may answer from the local pool while
|
|
28
|
+
`list({ type: 'complete' })` waits for the server round trip.
|
|
29
|
+
|
|
30
|
+
## 0.43.0
|
|
31
|
+
|
|
32
|
+
### Keys are branch-first
|
|
33
|
+
|
|
34
|
+
A new key is simply `sk_…`, `rk_…`, `pk_…`, `ek_…`, or `mk_…`: thirty
|
|
35
|
+
characters and a checksum, with no `live` or `test` in the name. A key's
|
|
36
|
+
project and branch are bindings on the server-side key record, never claims
|
|
37
|
+
encoded in the plaintext, so rotating, moving, or inspecting a key is a
|
|
38
|
+
server-side question with a server-confirmed answer. Existing `live`/`test`
|
|
39
|
+
keys keep working; those spellings are now compatibility forms rather than
|
|
40
|
+
the model.
|
|
41
|
+
|
|
42
|
+
What used to be called the effective key is now the runtime key, and
|
|
43
|
+
`status --json` exposes it as `runtimeKey`. The documentation explains the
|
|
44
|
+
project, branch, and capability model end to end in `docs/api-keys.md`,
|
|
45
|
+
`docs/branch-development.md`, `docs/cli.md`, and `docs/data-sources.md`.
|
|
46
|
+
|
|
47
|
+
### Credential lookup in the order mature CLIs use
|
|
48
|
+
|
|
49
|
+
Commands resolve their credential from the process `ABLO_API_KEY` first, then
|
|
50
|
+
an explicit `--env-file <path>`, then the stored credential. Read-only
|
|
51
|
+
diagnostics may inspect `.env.local` to help; anything that mutates requires
|
|
52
|
+
an explicit selection. Recovering a database connection is now one command:
|
|
53
|
+
`ablo connect rotate` re-keys the existing roles in place and reuses the
|
|
54
|
+
replication slot, so recovery never drops roles or touches your database by
|
|
55
|
+
hand.
|
|
56
|
+
|
|
57
|
+
### Removed
|
|
58
|
+
|
|
59
|
+
`logs --mode` is gone: `logs` follows the branch bound to the key it runs
|
|
60
|
+
with. The `effectiveKey` field of `status --json` is renamed `runtimeKey`.
|
|
61
|
+
|
|
3
62
|
## 0.42.0
|
|
4
63
|
|
|
5
64
|
### `ablo whoami`: what does this key act on?
|
package/docs/api-keys.md
CHANGED
|
@@ -10,19 +10,74 @@ import Ablo from '@abloatai/ablo';
|
|
|
10
10
|
const ablo = Ablo({ apiKey: process.env.ABLO_API_KEY });
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The
|
|
13
|
+
The server resolves the organization, project, immutable branch, and authority
|
|
14
|
+
from the key. Application code does not pass those targeting axes separately.
|
|
14
15
|
|
|
15
16
|
"Trusted" means the runtime can hold a secret: a backend or other server-side environment a browser can't read. Browser and app clients use the same `@abloatai/ablo` import but authenticate differently — they never carry a secret key.
|
|
16
17
|
|
|
17
|
-
##
|
|
18
|
+
## Start here: the normal workflow
|
|
19
|
+
|
|
20
|
+
Branches replace manual environment-key juggling. You should not normally keep
|
|
21
|
+
`ABLO_STAGING_KEY`, `ABLO_DEV_KEY`, and `ABLO_API_KEY_LIVE` beside one another
|
|
22
|
+
and remap them before each command.
|
|
23
|
+
|
|
24
|
+
| Job | Credential | How you get it |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Manage a project or its branches | `mk_` | `npx ablo login --project <slug>` stores it for the CLI. |
|
|
27
|
+
| Develop locally | expiring `sk_` bound to the current branch | `npx ablo dev` writes it as `ABLO_API_KEY` in gitignored `.env.local`. |
|
|
28
|
+
| Prepare a branch once, including CI | expiring `sk_` bound to that branch | `npx ablo dev --no-watch --branch <ref>`; CI supplies `ABLO_MANAGEMENT_KEY`. |
|
|
29
|
+
| Run the production backend | `sk_` bound to the production root | Store it as the deployment's `ABLO_API_KEY`. |
|
|
30
|
+
| Read in a browser | `pk_` | Publishable, read-only key. |
|
|
31
|
+
| Write in a browser as a user | short-lived `ek_` | Your backend exposes `authEndpoint` and mints it. |
|
|
32
|
+
|
|
33
|
+
The everyday loop is therefore:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx ablo login --project <project> # once per project
|
|
37
|
+
npx ablo dev # follows Git, mints and wires this branch
|
|
38
|
+
npx ablo status # broad readiness report
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Application code and agents still read one variable:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Switching Git branches does not re-scope the old key. Run `ablo dev` again; it
|
|
48
|
+
ensures the matching Ablo branch and replaces `.env.local` with a fresh key
|
|
49
|
+
bound to it.
|
|
50
|
+
|
|
51
|
+
## Three axes, not a key per environment name
|
|
52
|
+
|
|
53
|
+
A credential answers three separate questions:
|
|
54
|
+
|
|
55
|
+
1. **Project:** which application inside the organization.
|
|
56
|
+
2. **Branch (plane):** the production root or one immutable development/preview
|
|
57
|
+
child. Rows, schema, claims, logs, and database registration are isolated
|
|
58
|
+
here.
|
|
59
|
+
3. **Capability and audience:** management (`mk_`), trusted runtime (`sk_`),
|
|
60
|
+
restricted/delegated runtime (`rk_`), publishable browser read (`pk_`), or
|
|
61
|
+
ephemeral user session (`ek_`).
|
|
62
|
+
|
|
63
|
+
The prefix identifies the credential's capability class, not its target.
|
|
64
|
+
`sk_` means a trusted runtime secret. The server-side key row decides whether
|
|
65
|
+
that secret is bound to the production root or to a specific child branch. A
|
|
66
|
+
project selection or branch slug in a request cannot override that binding.
|
|
67
|
+
|
|
68
|
+
That is why Ablo does not need an application-level `environment` argument and
|
|
69
|
+
why `ablo projects use` cannot re-scope a credential. The bearer credential
|
|
70
|
+
already carries the target.
|
|
71
|
+
|
|
72
|
+
## Which credential to pass to the SDK
|
|
18
73
|
|
|
19
74
|
There's **one field — `apiKey`** — and what you pass depends on **where the code runs**.
|
|
20
75
|
Pick your row:
|
|
21
76
|
|
|
22
77
|
| Where your code runs | What to pass | Example |
|
|
23
78
|
|---|---|---|
|
|
24
|
-
| **Server / worker /
|
|
25
|
-
| **Browser: read-only** | a publishable `pk_` (safe to ship
|
|
79
|
+
| **Server / worker / agent** (can hold a secret) | your secret `sk_`: it defaults to `ABLO_API_KEY`, so usually pass **nothing** | `Ablo({ schema })` |
|
|
80
|
+
| **Browser: read-only** | a publishable `pk_` (safe to ship) | `Ablo({ schema, apiKey: process.env.NEXT_PUBLIC_ABLO_PUBLISHABLE_KEY })` |
|
|
26
81
|
| **Browser: writing as the signed-in user** | `authEndpoint`: the route on your own backend that mints a short-lived per-user token | `Ablo({ schema, authEndpoint: '/api/ablo-session' })` |
|
|
27
82
|
|
|
28
83
|
That's the whole story: one knob, filled by audience.
|
|
@@ -32,24 +87,18 @@ control-plane credential, not an application API key. It can manage projects
|
|
|
32
87
|
and branches and exchange for a branch-bound runtime key. Do not pass it to
|
|
33
88
|
`Ablo(...)` or put it in `ABLO_API_KEY`.
|
|
34
89
|
|
|
35
|
-
|
|
90
|
+
The credential class lives in the prefix:
|
|
36
91
|
|
|
37
|
-
|
|
|
92
|
+
| Prefix | Purpose | Stored where |
|
|
38
93
|
|---|---|---|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
44
|
-
Ablo also has one credential class that Stripe does not need:
|
|
94
|
+
| `mk_` | project and branch management | CLI credential store or `ABLO_MANAGEMENT_KEY` |
|
|
95
|
+
| `sk_` | trusted runtime, full branch authority | server-side `ABLO_API_KEY` |
|
|
96
|
+
| `rk_` | restricted runtime or agent | trusted runtime that needs the delegated scope |
|
|
97
|
+
| `pk_` | publishable, browser-safe read access | browser bundle |
|
|
98
|
+
| `ek_` | short-lived user session | browser memory |
|
|
45
99
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
| `mk_` | project and branch management | none | CLI credential store or `ABLO_MANAGEMENT_KEY` |
|
|
49
|
-
|
|
50
|
-
Trust class lives in the prefix too — `sk_test_` / `sk_live_` — exactly like Stripe.
|
|
51
|
-
It does not select a branch; the immutable server-side binding does that. The
|
|
52
|
-
`apiKey` resolver fetching an `ek_` is Ablo's ephemeral-key flow: server mints, client holds.
|
|
100
|
+
The prefix does not select a branch; the immutable server-side binding does.
|
|
101
|
+
For an `ek_`, the server mints and the client holds the short-lived result.
|
|
53
102
|
|
|
54
103
|
**Why a function for browser writes?** Anything you ship to a browser must be public, and a
|
|
55
104
|
public `pk_` is **read-only** — it can't carry one specific user's write authority. So when
|
|
@@ -57,8 +106,8 @@ the browser writes *as the logged-in user*, your backend (which holds the secret
|
|
|
57
106
|
knows who's signed in) mints a short-lived per-user token with `sessions.create({ user, can })`,
|
|
58
107
|
and the browser's `apiKey` function fetches it. You don't manage refresh — the SDK calls the
|
|
59
108
|
function once before connecting and then keeps the token fresh (re-mint before expiry, and on
|
|
60
|
-
tab-focus / network-online / device-wake).
|
|
61
|
-
|
|
109
|
+
tab-focus / network-online / device-wake). For a read-only app you don't need
|
|
110
|
+
any of this — just the `pk_` above.
|
|
62
111
|
|
|
63
112
|
Server-side, because `apiKey` defaults to `process.env.ABLO_API_KEY`, most backend and agent
|
|
64
113
|
code passes nothing. The secret `sk_` is **server-only** — never in a
|
|
@@ -90,12 +139,12 @@ Never ship a secret API key to a browser bundle.
|
|
|
90
139
|
## Publishable key (`pk_`): browser-safe, read-only
|
|
91
140
|
|
|
92
141
|
For a read-only browser experience, a publishable key is safe to ship in the
|
|
93
|
-
bundle.
|
|
94
|
-
|
|
142
|
+
bundle. It is long-lived, org-scoped, and used **directly as the bearer** —
|
|
143
|
+
never exchanged, never
|
|
95
144
|
expires, nothing to refresh:
|
|
96
145
|
|
|
97
146
|
```ts
|
|
98
|
-
const ablo = Ablo({ apiKey: process.env.NEXT_PUBLIC_ABLO_PUBLISHABLE_KEY }); //
|
|
147
|
+
const ablo = Ablo({ apiKey: process.env.NEXT_PUBLIC_ABLO_PUBLISHABLE_KEY }); // pk_…
|
|
99
148
|
```
|
|
100
149
|
|
|
101
150
|
A `pk_` grants **read-only** access to the org's data plane: it cannot write and
|
|
@@ -112,25 +161,53 @@ Production is the project's root branch. Development branches are isolated
|
|
|
112
161
|
children, and a key's immutable branch binding decides which rows, schema,
|
|
113
162
|
claims, and log it can reach:
|
|
114
163
|
|
|
115
|
-
- `
|
|
116
|
-
|
|
117
|
-
- `
|
|
164
|
+
- an `sk_…` bound to a development branch reads and writes only that child;
|
|
165
|
+
its rows are invisible to production and to other branches.
|
|
166
|
+
- an `sk_…` bound to the root reads and writes production.
|
|
118
167
|
|
|
119
168
|
`npx ablo dev` derives a branch from Git, ensures the matching child, and mints
|
|
120
|
-
an expiring `
|
|
169
|
+
an expiring `sk_` key for it. The credential carries the immutable branch
|
|
121
170
|
id; changing a slug in a request cannot change its authority. A child receives
|
|
122
171
|
the parent's active schema when it is created and owns its artifact after that.
|
|
123
|
-
A schema change reaches production only through the reviewed
|
|
172
|
+
A schema change reaches production only through the reviewed root-branch path in
|
|
124
173
|
[Deployment](./deployment.md).
|
|
125
174
|
|
|
126
175
|
The shared default sandbox is no longer part of the development workflow.
|
|
127
176
|
Branch identity is required for newly provisioned CLI and runtime credentials.
|
|
128
177
|
|
|
178
|
+
## Inspecting a credential
|
|
179
|
+
|
|
180
|
+
Use `status` for the whole setup and `whoami` for the narrow identity question:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx ablo status
|
|
184
|
+
npx ablo whoami
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`whoami` succeeds only when the server confirms the credential's organization,
|
|
188
|
+
project, and branch. It never prints the full secret. For CI or recovery, inspect
|
|
189
|
+
an explicitly named value without remapping `ABLO_API_KEY`:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npx ablo whoami --key-env PREVIEW_ABLO_KEY
|
|
193
|
+
npx ablo whoami --key-env ABLO_API_KEY_LIVE --json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`--key-env` reads that exact name from the process, `.env.local`, or `.env`; the
|
|
197
|
+
name makes the choice explicit, while the secret stays out of argv and shell
|
|
198
|
+
history. `--key <value>` exists for one-off use but is less safe because shells
|
|
199
|
+
and process listings may retain the value.
|
|
200
|
+
|
|
201
|
+
Multiple custom-named keys are reasonable at a CI secret boundary or during a
|
|
202
|
+
one-time migration from the old environment model. They are not the normal
|
|
203
|
+
local-development workflow. After a stranded-plane recovery, retire obsolete
|
|
204
|
+
variables rather than keeping them as permanent branch selectors.
|
|
205
|
+
|
|
129
206
|
## Scopes
|
|
130
207
|
|
|
131
208
|
Keys carry scopes following the principle of least privilege — each key gets
|
|
132
209
|
only what its job needs. A secret key with **no scopes** has full org authority
|
|
133
|
-
(the default for a `
|
|
210
|
+
(the default for a `sk_` backend key); a key with a non-empty scope set is
|
|
134
211
|
restricted to exactly those grants:
|
|
135
212
|
|
|
136
213
|
- `schema:push` — author the schema artifact on the key's bound plane
|
|
@@ -149,6 +226,23 @@ Branch binding remains an authority boundary even when a key has no granular
|
|
|
149
226
|
scope strings: a temporary child key can act only inside that child. It cannot
|
|
150
227
|
manage siblings or gain root authority.
|
|
151
228
|
|
|
229
|
+
## Current and legacy key spellings
|
|
230
|
+
|
|
231
|
+
New credentials use one spelling per capability class:
|
|
232
|
+
|
|
233
|
+
```text
|
|
234
|
+
sk_… trusted runtime
|
|
235
|
+
rk_… restricted runtime
|
|
236
|
+
pk_… publishable read-only browser
|
|
237
|
+
ek_… ephemeral user session
|
|
238
|
+
mk_… project and branch management
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Older `sk_live_…`, `sk_test_…`, `rk_live_…`, and related credentials continue
|
|
242
|
+
to authenticate during migration. Their `live`/`test` segment is a legacy hint,
|
|
243
|
+
not the source of truth. Rotation mints the current spelling, and `ablo whoami`
|
|
244
|
+
shows the persisted branch that actually controls the key.
|
|
245
|
+
|
|
152
246
|
### `ablo dev`
|
|
153
247
|
|
|
154
248
|
```sh
|
|
@@ -30,7 +30,7 @@ Ablo branch
|
|
|
30
30
|
├── active schema artifact
|
|
31
31
|
├── isolated transaction plane
|
|
32
32
|
├── immutable branch id
|
|
33
|
-
└── expiring
|
|
33
|
+
└── expiring sk_ credential
|
|
34
34
|
│
|
|
35
35
|
▼
|
|
36
36
|
gitignored .env.local
|
|
@@ -46,6 +46,13 @@ request parameter.
|
|
|
46
46
|
Production is the root branch. Development branches are children; they do not
|
|
47
47
|
inherit production write authority.
|
|
48
48
|
|
|
49
|
+
The branch workflow replaces a hand-maintained collection of environment keys.
|
|
50
|
+
Your application always reads `ABLO_API_KEY`; `ablo dev` changes which
|
|
51
|
+
branch-bound value sits there. Do not select branches in application code or
|
|
52
|
+
invent `ABLO_STAGING_KEY`/`ABLO_FEATURE_KEY` conventions. See
|
|
53
|
+
[API Keys](./api-keys.md#start-here-the-normal-workflow) for the complete
|
|
54
|
+
project → branch → credential model.
|
|
55
|
+
|
|
49
56
|
## Before the first run
|
|
50
57
|
|
|
51
58
|
Initialize the project and sign in:
|
|
@@ -113,7 +120,7 @@ branch.
|
|
|
113
120
|
### 3. Exchange for a temporary credential
|
|
114
121
|
|
|
115
122
|
The stored `mk_` credential authorizes the branch-management call. The server then
|
|
116
|
-
returns a new `
|
|
123
|
+
returns a new `sk_` credential bound only to the child branch.
|
|
117
124
|
|
|
118
125
|
The default lifetime is eight hours:
|
|
119
126
|
|
|
@@ -135,7 +142,7 @@ The temporary credential is written to:
|
|
|
135
142
|
|
|
136
143
|
```dotenv
|
|
137
144
|
# .env.local
|
|
138
|
-
ABLO_API_KEY=
|
|
145
|
+
ABLO_API_KEY=sk_...
|
|
139
146
|
```
|
|
140
147
|
|
|
141
148
|
The CLI creates `.env.local` with owner-only permissions when possible and adds
|
|
@@ -158,6 +165,12 @@ unset ABLO_API_KEY
|
|
|
158
165
|
npm run dev
|
|
159
166
|
```
|
|
160
167
|
|
|
168
|
+
Confirm the branch the resulting key actually targets:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx ablo whoami
|
|
172
|
+
```
|
|
173
|
+
|
|
161
174
|
### 5. Load and push the schema
|
|
162
175
|
|
|
163
176
|
By default, the CLI imports:
|
package/docs/cli.md
CHANGED
|
@@ -35,36 +35,59 @@ resume a branch and exchanges it for a temporary branch-bound runtime key.
|
|
|
35
35
|
| `ablo login` | Authorize in the browser; store one project management credential. |
|
|
36
36
|
| `ablo login --project <slug>` | Same, scoped to a project, which becomes active. |
|
|
37
37
|
| `ablo logout` | Remove the stored credentials. |
|
|
38
|
-
| `ablo
|
|
38
|
+
| `ablo whoami` | Strictly confirm which project and branch a credential acts on. |
|
|
39
|
+
| `ablo status` | Show the active org/project, resolved runtime credential, branch target, and server health. |
|
|
40
|
+
|
|
41
|
+
Credential precedence is deliberately small:
|
|
42
|
+
|
|
43
|
+
1. `ABLO_API_KEY` in the process environment.
|
|
44
|
+
2. An explicitly selected file, for commands that support
|
|
45
|
+
`--env-file <path>`.
|
|
46
|
+
3. A stored credential for legacy compatibility.
|
|
47
|
+
|
|
48
|
+
Read-only orientation commands (`status`, `whoami`, `logs`, and `connect
|
|
49
|
+
locate/check`) may inspect the application's `.env.local` so they describe what
|
|
50
|
+
the app would use. Mutations (`push`, `connect apply/rotate/register`, and
|
|
51
|
+
`connect deregister`) never let an ambient file silently choose a branch. Pass
|
|
52
|
+
`--env-file .env.local` when that file is the intended source; the command
|
|
53
|
+
reports the server-confirmed branch before it writes. For one-time recovery,
|
|
54
|
+
`connect deregister --key-env <NAME>` selects that exact named value without
|
|
55
|
+
putting the secret in argv.
|
|
39
56
|
|
|
40
57
|
Keys live in `~/.config/ablo/credentials.json` (mode `0600`), keyed by project.
|
|
41
58
|
The non-secret `config.json` holds the active project. In **CI**, don't log in —
|
|
42
59
|
set the project management credential as `ABLO_MANAGEMENT_KEY`; it overrides the
|
|
43
60
|
stored credential during branch bootstrap.
|
|
44
61
|
|
|
45
|
-
## Development branches
|
|
62
|
+
## Development branches and the production root
|
|
46
63
|
|
|
47
64
|
A branch is your project at full strength over its own rows: the same models,
|
|
48
65
|
the same schema artifacts, the same claims and the same rules production runs.
|
|
49
66
|
|
|
50
67
|
Production is the project root. `ablo dev` creates or reuses a child branch for
|
|
51
|
-
your Git branch, then mints a temporary `
|
|
68
|
+
your Git branch, then mints a temporary `sk_` key bound to that child.
|
|
52
69
|
Reads, writes, schema artifacts, claims, and credentials stay isolated from
|
|
53
70
|
production and from other development branches, which is what makes a
|
|
54
71
|
schema-changing pull request as routine as a code-only one.
|
|
55
72
|
|
|
56
73
|
There is no local mode switch. Development selection comes from Git or
|
|
57
|
-
`--branch`; production authority comes only from an explicit
|
|
74
|
+
`--branch`; production authority comes only from an explicit root-bound
|
|
75
|
+
credential.
|
|
58
76
|
Production schema changes use the reviewed one-shot path in
|
|
59
77
|
[Deployment](./deployment.md).
|
|
60
78
|
|
|
79
|
+
`ABLO_API_KEY` is the one runtime variable in every environment. Branches
|
|
80
|
+
replace manually switching between custom names such as `ABLO_STAGING_KEY` and
|
|
81
|
+
`ABLO_DEV_KEY`: rerun `ablo dev` when Git branches change. Use `ablo whoami`
|
|
82
|
+
for the narrow identity question, or `ablo whoami --key-env <NAME>` to inspect
|
|
83
|
+
an explicitly named CI/legacy key without exposing its value in argv.
|
|
84
|
+
|
|
61
85
|
## Projects
|
|
62
86
|
|
|
63
87
|
An org can have multiple **projects**, each with its own isolated keys, schema,
|
|
64
88
|
and data. Keys are scoped to a project **at mint** and never re-scoped, so the
|
|
65
|
-
CLI keeps a separate credential profile per project
|
|
66
|
-
`
|
|
67
|
-
selects which profile every command authenticates with.
|
|
89
|
+
CLI keeps a separate credential profile per project. The active project (set
|
|
90
|
+
with `projects use`) selects which profile every command authenticates with.
|
|
68
91
|
|
|
69
92
|
| Command | What it does |
|
|
70
93
|
| ----------------------------- | ---------------------------------------------------------------------------------- |
|
|
@@ -97,11 +120,11 @@ bypasses profiles for project/branch administration; the runtime key remains
|
|
|
97
120
|
| Command | What it does | Flags |
|
|
98
121
|
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
99
122
|
| `ablo init` | Scaffold `ablo/` (`schema.ts`, client, optional Data Source / agent / component), write `.env`, install the SDK. Offers to log in at the end. |: |
|
|
100
|
-
| `ablo login` / `logout` / `status` | Authentication
|
|
123
|
+
| `ablo login` / `logout` / `whoami` / `status` | Authentication, exact credential identity, and readiness (above). | `whoami --key-env <NAME>`, `whoami --json`, `status --json` |
|
|
101
124
|
| `ablo projects list\|create\|use\|rename` | Manage projects and the active one (see [Projects](#projects)). Each project's keys/schema/data are isolated. | `--name "<display>"` (create/rename) |
|
|
102
125
|
| `ablo dev` | **Hosted**: ensure an isolated Git branch, wire its temporary key, push, then watch `ablo/schema.ts`. | `--branch <slug>`, `--branch-ttl-hours <1-168>`, `--no-watch`, `--schema`, `--export`, `--url` |
|
|
103
126
|
| `ablo branch list\|status\|check\|create\|ensure\|credential\|delete` | Manage and diagnose immutable branch planes and expiring credentials. | Run `ablo branch --help`; use `--json` for automation. |
|
|
104
|
-
| `ablo logs` | Tail the
|
|
127
|
+
| `ablo logs` | Tail the resolved runtime credential's branch activity. Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow` |
|
|
105
128
|
| `ablo push` | **Hosted**: upload the schema to Ablo; the server diffs, migrates, and activates it. | `--force`, `--rename old:new`, `--backfill model.field=value`, `--schema`, `--export`, `--url` |
|
|
106
129
|
| `ablo migrate` | **Direct Postgres**: provision just the synced models (plus the adapter's `ablo_outbox` / `ablo_idempotency`) in your own `DATABASE_URL`. Leaves your other tables alone. | `--dry-run`, `--output <file>`, `--schema`, `--export` |
|
|
107
130
|
| `ablo pull` | **Direct Postgres**: generate `defineSchema(...)` from your existing tables (read-only, like `prisma db pull`). | `--out <path>`, `--app-schema <name>`, `--import <pkg>`, `--force` |
|
|
@@ -121,7 +144,7 @@ npx ablo docs --json # the page list, machine-readable
|
|
|
121
144
|
```
|
|
122
145
|
|
|
123
146
|
These pages ship inside the npm package, so they describe the code beside them
|
|
124
|
-
and stay reachable with no network —
|
|
147
|
+
and stay reachable with no network — isolated agent environments and CI runners
|
|
125
148
|
often have none. That matters most when a project is pinned: `get` / `getAll` /
|
|
126
149
|
`getCount` became `retrieve` / `list` in 0.35.0, and a website always describes
|
|
127
150
|
the newest release, so an agent on an earlier version reads the new name and
|
|
@@ -151,9 +174,10 @@ for the exact discovery order, CI flow, database boundary, and troubleshooting.
|
|
|
151
174
|
|
|
152
175
|
## `ablo logs`
|
|
153
176
|
|
|
154
|
-
Tail commit activity
|
|
155
|
-
key streams only
|
|
156
|
-
|
|
177
|
+
Tail commit activity. Scope comes from the persisted key binding: a child-bound
|
|
178
|
+
key streams only that child, while a root-bound key
|
|
179
|
+
streams production. You never pass a project or branch. Follows by default;
|
|
180
|
+
`--no-follow` prints recent and exits.
|
|
157
181
|
|
|
158
182
|
```bash
|
|
159
183
|
npx ablo logs # last 50, then stream
|
|
@@ -111,7 +111,7 @@ Both scopes carry:
|
|
|
111
111
|
|
|
112
112
|
| field | meaning |
|
|
113
113
|
|---|---|
|
|
114
|
-
| `object` |
|
|
114
|
+
| `object` | stable type tag: `'stale_notification'` |
|
|
115
115
|
| `scope` | `'row'` or `'group'` — which premise granularity fired |
|
|
116
116
|
| `target` | **the row that moved**, and which parts: `{ model, id, fields }`. The same locator a claim names its subject with |
|
|
117
117
|
| `readAt` | the watermark the committer reasoned against |
|
package/docs/data-sources.md
CHANGED
|
@@ -15,15 +15,48 @@ Ablo only writes rows into tables you already have, through a role scoped to
|
|
|
15
15
|
exactly that.
|
|
16
16
|
|
|
17
17
|
> **Just trying Ablo?** You don't need a database to start. Pass an `apiKey` only,
|
|
18
|
-
> and Ablo keeps your rows in its own log so you can build the whole app today
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
18
|
+
> and Ablo keeps your rows in its own log so you can build the whole app today.
|
|
19
|
+
> `ablo dev` gives each Git branch its own isolated plane.
|
|
20
|
+
> Keep it hosted with no database, or point that branch at a separate/local
|
|
21
|
+
> Postgres. Connect your production root (below) when you're ready for its
|
|
22
|
+
> database to be the system of record.
|
|
22
23
|
|
|
23
24
|
Connecting sets up two capabilities on your Postgres: **logical replication**, so
|
|
24
25
|
Ablo can read and confirm, and a **scoped DML role**, so Ablo can write. `ablo
|
|
25
26
|
connect` prints the exact SQL. `ablo connect apply` runs it for you.
|
|
26
27
|
|
|
28
|
+
Connect commands do not silently load a dotenv file for a mutation. Either
|
|
29
|
+
export `ABLO_API_KEY` and `DATABASE_URL`, pass `--url`, or explicitly select the
|
|
30
|
+
file:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx ablo connect apply --env-file .env.local --yes
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The explicit flag makes the credential choice visible and loads both the
|
|
37
|
+
branch-bound key and database URL. Shell environment variables take precedence.
|
|
38
|
+
|
|
39
|
+
If scoped roles already exist but their passwords are unavailable, do not drop
|
|
40
|
+
them or run `DROP OWNED`. Rotate them in place and re-register the fresh
|
|
41
|
+
credentials:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx ablo connect rotate --env-file .env.local --yes
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This is the supported recovery after moving a database between branches.
|
|
48
|
+
|
|
49
|
+
For a one-time release from an older branch, select the named recovery key
|
|
50
|
+
directly—no shell remapping:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx ablo connect deregister --key-env OLD_ABLO_KEY --yes
|
|
54
|
+
npx ablo connect rotate --env-file .env.local --yes
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`--key-env` reads that exact variable from the process, `.env.local`, or `.env`
|
|
58
|
+
without printing the secret. Retire the old variable after the move.
|
|
59
|
+
|
|
27
60
|
## Connect in one command
|
|
28
61
|
|
|
29
62
|
```bash
|
|
@@ -148,7 +181,7 @@ Your **app** holds only the API key — never a connection string:
|
|
|
148
181
|
|
|
149
182
|
```bash
|
|
150
183
|
# .env — server runtime only, never the browser
|
|
151
|
-
ABLO_API_KEY=
|
|
184
|
+
ABLO_API_KEY=sk_...
|
|
152
185
|
```
|
|
153
186
|
|
|
154
187
|
```ts
|
package/docs/debugging.md
CHANGED
|
@@ -15,14 +15,31 @@ const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, debug: true });
|
|
|
15
15
|
|
|
16
16
|
## CLI environment and target
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
Read-only diagnostics (`status`, `whoami`, `logs`, and `connect locate/check`)
|
|
19
|
+
may inspect the application-facing chain: exported `ABLO_API_KEY`,
|
|
20
|
+
`.env.local`, `.env`, then the stored credential. An exported value wins over
|
|
21
|
+
project files. Mutations (`push` and `connect apply/rotate/register/deregister`)
|
|
22
|
+
are intentionally stricter: they read the process environment, an explicit
|
|
23
|
+
`--env-file`, or a stored compatibility credential. An ambient file cannot
|
|
24
|
+
silently choose the branch a mutation acts on.
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
Use the two diagnostics according to the question:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx ablo whoami # strict: which project + branch does this credential target?
|
|
30
|
+
npx ablo status # broad: target, database, schema, drift, and write blockers
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For an old, CI, or recovery key stored under another variable name:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx ablo whoami --key-env ABLO_API_KEY_LIVE
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
That explicit lookup checks the process, `.env.local`, and `.env` for the named
|
|
40
|
+
variable, keeps the value out of argv, and either returns a server-confirmed
|
|
41
|
+
identity or fails non-zero. Do not infer that a key is invalid from an older
|
|
42
|
+
CLI's generic identity error; rerun with the current CLI.
|
|
26
43
|
|
|
27
44
|
`debug: true` is the simple switch. For finer control use `logLevel`, or set it without touching code via the `ABLO_LOG_LEVEL` environment variable.
|
|
28
45
|
|
|
@@ -216,3 +233,57 @@ AbloValidationError [model_required_field_missing]: A required field was absent.
|
|
|
216
233
|
```
|
|
217
234
|
|
|
218
235
|
Branch on `err.code` (stable) — never on the message (rewordable). See [Client Behavior](./client-behavior.md) for the full error model and which codes are safe to retry.
|
|
236
|
+
|
|
237
|
+
### Diagnosing `capability_scope_denied`
|
|
238
|
+
|
|
239
|
+
The same stable code covers two different enforcement layers, so inspect
|
|
240
|
+
`error.details.origin`:
|
|
241
|
+
|
|
242
|
+
- `capability_allowlist`: the branch/session credential did not grant the
|
|
243
|
+
operation. `requiredCapability.scope` names the missing `model.verb`, and
|
|
244
|
+
`details.resolvedOperations` shows the grants the server actually resolved.
|
|
245
|
+
- `database_row_level_security`: Ablo's capability gate allowed the operation,
|
|
246
|
+
but Postgres rejected it under the customer table's RLS policy.
|
|
247
|
+
`details.databaseSessionContext` shows the organization, project, branch,
|
|
248
|
+
participant kind, user principal, and custom session-setting values applied
|
|
249
|
+
to that transaction.
|
|
250
|
+
|
|
251
|
+
Every rejected live commit carries `requestId` on the thrown error and
|
|
252
|
+
`request_id` in its JSON form and warning line:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import { AbloError } from '@abloatai/ablo';
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await ablo.documents.create({
|
|
259
|
+
data,
|
|
260
|
+
wait: 'confirmed',
|
|
261
|
+
});
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof AbloError) {
|
|
264
|
+
console.error(error.code, error.requestId, error.requiredCapability, error.details);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
With `wait: 'confirmed'`, the awaited call rejects with that complete typed
|
|
270
|
+
error. `onMutationFailure` remains the notification channel for optimistic
|
|
271
|
+
writes that return before the server answers; it is not required to recover
|
|
272
|
+
details from a confirmed write.
|
|
273
|
+
|
|
274
|
+
### Local reads versus a confirmed server read
|
|
275
|
+
|
|
276
|
+
`list()` without a completeness option may return the current local pool
|
|
277
|
+
immediately. That is why it can be empty while Postgres contains rows: it is not
|
|
278
|
+
evidence that the replication source has no history.
|
|
279
|
+
|
|
280
|
+
Use:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
await ablo.documents.list({ type: 'complete' });
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`type: 'complete'` waits for a server round trip and returns the confirmed
|
|
287
|
+
result. `type: 'unknown'` returns the local result immediately and refreshes it
|
|
288
|
+
in the background. The distinction is freshness/completeness, not claimed
|
|
289
|
+
versus unclaimed data.
|