@abloatai/ablo 0.37.0 → 0.38.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/docs/api-keys.md CHANGED
@@ -27,6 +27,11 @@ Pick your row:
27
27
 
28
28
  That's the whole story: one knob, filled by audience.
29
29
 
30
+ The `mk_` credential created by `ablo login` is different: it is a CLI
31
+ control-plane credential, not an application API key. It can manage projects
32
+ and branches and exchange for a branch-bound runtime key. Do not pass it to
33
+ `Ablo(...)` or put it in `ABLO_API_KEY`.
34
+
30
35
  **Coming from Stripe? It's the same key model, same prefixes:**
31
36
 
32
37
  | Stripe | Ablo | Where it goes |
@@ -36,7 +41,14 @@ That's the whole story: one knob, filled by audience.
36
41
  | restricted `rk_` (granular) | `rk_` | scoped agents (`agents.create({ can })`) |
37
42
  | ephemeral key (client, customer-scoped) | `ek_` | per-user browser sessions (`sessions.create({ user, can })`) |
38
43
 
39
- Mode lives in the prefix too `sk_test_` / `sk_live_` — exactly like Stripe. The
44
+ Ablo also has one credential class that Stripe does not need:
45
+
46
+ | Prefix | Purpose | Mode | Stored where |
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
40
52
  `apiKey` resolver fetching an `ek_` is Ablo's ephemeral-key flow: server mints, client holds.
41
53
 
42
54
  **Why a function for browser writes?** Anything you ship to a browser must be public, and a
@@ -91,21 +103,28 @@ cannot reach any control-plane operation. The moment the browser needs to write
91
103
  on a specific user's behalf, mint a short-lived `ek_` user session from your
92
104
  backend instead (see the Sessions guide).
93
105
 
94
- ## Sandboxes and production
106
+ ## Branches and production
95
107
 
96
- Test and live keys are the same shape; the prefix names the environment:
108
+ A branch is your project at full strength over its own rows: the same models,
109
+ the same schema, the same claims and the same rules production runs.
97
110
 
98
- - `sk_test_…` a key bound to a **sandbox**. Its reads and writes are isolated
99
- to that sandbox and are invisible to live keys (and to other sandboxes).
111
+ Production is the project's root branch. Development branches are isolated
112
+ children, and a key's immutable branch binding decides which rows, schema,
113
+ claims, and log it can reach:
114
+
115
+ - `sk_test_…` — a key bound to a development branch. Its reads and
116
+ writes are invisible to production and to other branches.
100
117
  - `sk_live_…` — a key against your live data.
101
118
 
102
- Every org has a default sandbox, plus any number of additional
103
- sandboxes you create. **Data is isolated per sandbox; the schema is one
104
- definition serving both.** A sandbox reads the production schema until it is
105
- pushed one of its own, so your test and live keys see the same models and only
106
- the rows differ how Stripe separates sandbox and production data while keeping
107
- the API shape identical. A schema change reaches production when you push it
108
- with a live key ([Deployment](./deployment.md)).
119
+ `npx ablo dev` derives a branch from Git, ensures the matching child, and mints
120
+ an expiring `sk_test_` key for it. The credential carries the immutable branch
121
+ id; changing a slug in a request cannot change its authority. A child receives
122
+ 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 live-key path in
124
+ [Deployment](./deployment.md).
125
+
126
+ The shared default sandbox is no longer part of the development workflow.
127
+ Branch identity is required for newly provisioned CLI and runtime credentials.
109
128
 
110
129
  ## Scopes
111
130
 
@@ -114,25 +133,30 @@ only what its job needs. A secret key with **no scopes** has full org authority
114
133
  (the default for a `sk_live_` backend key); a key with a non-empty scope set is
115
134
  restricted to exactly those grants:
116
135
 
117
- - `schema:push` — author the org schema (`ablo schema push`, `ablo dev`). A
118
- high-risk, org-wide grant: because schema is shared, a push affects the live
119
- table shape. A full-authority key has it implicitly; a *restricted* key (such
120
- as a sandbox key) needs it granted explicitly.
121
- - `sandbox:<id>`identifies which sandbox the key belongs to. (The key's data
122
- isolation comes from that sandbox binding, not from this scope string.)
136
+ - `schema:push` — author the schema artifact on the key's bound plane
137
+ (`ablo push`, `ablo dev`). A production push is high-risk because it changes
138
+ the live contract; a child push remains inside that branch. A
139
+ full-authority key has it implicitly; a restricted key needs it explicitly.
140
+ - `project:manage`list, create, and rename projects.
141
+ - `branch:manage` list, create, and delete child branches and mint their
142
+ temporary credentials.
143
+
144
+ Both management scopes are explicit grants on `mk_` credentials. Runtime
145
+ `sk_`, `rk_`, `pk_`, and `ek_` credentials cannot become management
146
+ credentials through an empty scope set or a CLI fallback.
123
147
 
124
- A key minted from the default sandbox carries `schema:push`, so
125
- `ablo dev` works out of the box. Keys from other sandboxes are **data-only** by
126
- default enable "schema authoring" when minting if you want that key to push
127
- schema too. Hand data-only keys to embedded apps and CI agents; reserve
128
- schema-authoring keys for the developer running `ablo dev`.
148
+ Branch binding remains an authority boundary even when a key has no granular
149
+ scope strings: a temporary child key can act only inside that child. It cannot
150
+ manage siblings or gain root authority.
129
151
 
130
152
  ### `ablo dev`
131
153
 
132
154
  ```sh
133
- ABLO_API_KEY=sk_test_… npx ablo dev
155
+ npx ablo login
156
+ npx ablo dev
134
157
  ```
135
158
 
136
- Pushes your `ablo/schema.ts` to the test sandbox, prints the one line you need
137
- in `.env.local`, and re-pushes on every save. It refuses `sk_live_` keys so a
138
- tight save loop can never churn production data.
159
+ The stored `mk_` project credential is used only to ensure the Git-derived child and
160
+ mint an expiring branch credential. `dev` writes that temporary key to
161
+ gitignored `.env.local`, pushes `ablo/schema.ts` to the child, and re-pushes on
162
+ every save. See [Branch-first development](./branch-development.md).
@@ -0,0 +1,392 @@
1
+ # Branch-first development
2
+
3
+ > Understand exactly what `ablo dev` prepares, what it watches, and how each Git branch gets an isolated Ablo schema and credential.
4
+
5
+ `ablo dev` is Ablo's schema-development loop. It prepares an isolated Ablo
6
+ branch for the Git branch you are working on, gives your local application a
7
+ short-lived credential for that branch, pushes the schema, and keeps the schema
8
+ registered as you edit it.
9
+
10
+ It does **not** start your application or run database migrations.
11
+
12
+ The everyday setup is two terminals:
13
+
14
+ ```bash
15
+ # Terminal 1: your application
16
+ npm run dev
17
+
18
+ # Terminal 2: Ablo's schema loop
19
+ npx ablo dev
20
+ ```
21
+
22
+ ## The mental model
23
+
24
+ ```text
25
+ Git branch
26
+
27
+ │ npx ablo dev
28
+
29
+ Ablo branch
30
+ ├── active schema artifact
31
+ ├── isolated transaction plane
32
+ ├── immutable branch id
33
+ └── expiring sk_test_ credential
34
+
35
+
36
+ gitignored .env.local
37
+
38
+
39
+ your local application
40
+ ```
41
+
42
+ A branch name is a human handle. The credential carries the immutable Ablo
43
+ branch id, so application requests cannot switch branches by changing a slug or
44
+ request parameter.
45
+
46
+ Production is the root branch. Development branches are children; they do not
47
+ inherit production write authority.
48
+
49
+ ## Before the first run
50
+
51
+ Initialize the project and sign in:
52
+
53
+ ```bash
54
+ npx ablo init
55
+ npx ablo login
56
+ ```
57
+
58
+ Login stores one project-scoped `mk_` management credential. It has no
59
+ production/test mode and no application-data authority. It can manage projects
60
+ and branches and exchange for an expiring credential bound to one branch.
61
+
62
+ If you switch projects, log in for the selected project before running `dev`:
63
+
64
+ ```bash
65
+ npx ablo projects use orders
66
+ npx ablo login --project orders
67
+ ```
68
+
69
+ ## What happens when you run it
70
+
71
+ Given:
72
+
73
+ ```bash
74
+ git switch -c feature/order-approval
75
+ npx ablo dev
76
+ ```
77
+
78
+ Ablo performs these steps.
79
+
80
+ ### 1. Discover the branch
81
+
82
+ The first available source wins:
83
+
84
+ 1. `--branch <name>`
85
+ 2. `ABLO_BRANCH`
86
+ 3. GitHub's `GITHUB_HEAD_REF` or `GITHUB_REF_NAME`
87
+ 4. Vercel's `VERCEL_GIT_COMMIT_REF`
88
+ 5. GitLab's `CI_COMMIT_REF_NAME`
89
+ 6. the current local Git branch
90
+
91
+ The reference is normalized into a lowercase plane-safe slug:
92
+
93
+ ```text
94
+ feature/order-approval → feature-order-approval
95
+ Feature: Billing V2 → feature-billing-v2
96
+ ```
97
+
98
+ Long names are shortened with a stable hash, so the same Git branch resolves to
99
+ the same Ablo branch on every machine.
100
+
101
+ ### 2. Ensure the Ablo branch
102
+
103
+ The CLI lists branches for the active project and reuses one with the same slug.
104
+ If none exists, it creates a child of the project's production root.
105
+
106
+ Creation is idempotent: two CI jobs racing to ensure the same branch converge on
107
+ the same server record.
108
+
109
+ At creation, the child receives its own copy of the parent's active schema.
110
+ Later production schema pushes do not silently change an in-flight feature
111
+ branch.
112
+
113
+ ### 3. Exchange for a temporary credential
114
+
115
+ The stored `mk_` credential authorizes the branch-management call. The server then
116
+ returns a new `sk_test_` credential bound only to the child branch.
117
+
118
+ The default lifetime is eight hours:
119
+
120
+ ```bash
121
+ npx ablo dev --branch-ttl-hours 12
122
+ ```
123
+
124
+ Allowed values are 1–168 hours. Rerunning `ablo dev` mints a fresh credential.
125
+ This lifetime belongs to the credential, not the branch. The branch persists
126
+ until `ablo branch delete` or preview automation removes it.
127
+
128
+ Temporary branch credentials can read, write, and push schema on their own
129
+ branch. They cannot create or delete siblings, mint sibling credentials, or
130
+ obtain production authority.
131
+
132
+ ### 4. Wire the local application
133
+
134
+ The temporary credential is written to:
135
+
136
+ ```dotenv
137
+ # .env.local
138
+ ABLO_API_KEY=sk_test_...
139
+ ```
140
+
141
+ The CLI creates `.env.local` with owner-only permissions when possible and adds
142
+ it to `.gitignore` if it is not already ignored. The key is not added to Ablo's
143
+ long-lived credential store.
144
+
145
+ Most application frameworks load `.env.local` automatically. Plain Node can
146
+ load it explicitly:
147
+
148
+ ```bash
149
+ node --env-file=.env.local app.ts
150
+ ```
151
+
152
+ An `ABLO_API_KEY` exported in your shell overrides `.env.local` for child
153
+ processes. `ablo dev` warns when it detects that mismatch. Unset the exported
154
+ value before starting the application:
155
+
156
+ ```bash
157
+ unset ABLO_API_KEY
158
+ npm run dev
159
+ ```
160
+
161
+ ### 5. Load and push the schema
162
+
163
+ By default, the CLI imports:
164
+
165
+ ```text
166
+ ablo/schema.ts
167
+ ```
168
+
169
+ and reads its `schema` export. Override either:
170
+
171
+ ```bash
172
+ npx ablo dev \
173
+ --schema src/ablo-schema.ts \
174
+ --export appSchema
175
+ ```
176
+
177
+ The schema is serialized and uploaded to the selected branch. The server
178
+ compares it with that branch's active artifact and returns either:
179
+
180
+ - unchanged, with the current version;
181
+ - activated, with a new version and hash; or
182
+ - rejected, with the incompatible changes and the required next action.
183
+
184
+ `push` registers a contract. It does not execute DDL.
185
+
186
+ ### 6. Watch for edits
187
+
188
+ After the first successful push, `ablo dev` watches the schema module. Editor
189
+ write/rename bursts are debounced into one reload and one push.
190
+
191
+ Stop it with `Ctrl-C`.
192
+
193
+ For a single branch preparation and push:
194
+
195
+ ```bash
196
+ npx ablo dev --no-watch
197
+ ```
198
+
199
+ ## Command reference
200
+
201
+ ```bash
202
+ # Discover from Git and watch
203
+ npx ablo dev
204
+
205
+ # Choose the branch explicitly
206
+ npx ablo dev --branch preview-pr-482
207
+
208
+ # Push once
209
+ npx ablo dev --no-watch
210
+
211
+ # Change the temporary-key lifetime
212
+ npx ablo dev --branch-ttl-hours 24
213
+
214
+ # Use another schema module/export
215
+ npx ablo dev --schema db/ablo.ts --export schema
216
+
217
+ ```
218
+
219
+ Lower-level branch operations are also available:
220
+
221
+ ```bash
222
+ npx ablo branch list
223
+ npx ablo branch status feature-orders
224
+ npx ablo branch check feature-orders
225
+ npx ablo branch create feature-orders
226
+ npx ablo branch ensure preview-pr-482 --credential --ttl-hours 168 --json
227
+ npx ablo branch credential br_... --ttl-hours 8
228
+ npx ablo branch delete br_...
229
+ ```
230
+
231
+ Automation should retain immutable ids returned by the API. Slugs are for
232
+ people and discovery.
233
+
234
+ `branch status` and `branch check` are aliases. They show lifecycle state, the
235
+ active schema and hash, compatibility with the parent schema, the bound
236
+ database's safe coordinates, and an exact readiness fix.
237
+
238
+ ## CI and preview deployments
239
+
240
+ For a one-shot CI schema check:
241
+
242
+ ```bash
243
+ # Store the project management credential as the masked secret.
244
+ ABLO_MANAGEMENT_KEY="mk_..." \
245
+ ABLO_BRANCH="preview-pr-${PR_NUMBER}" \
246
+ npx ablo dev --no-watch
247
+ ```
248
+
249
+ For infrastructure that needs the credential response directly:
250
+
251
+ ```bash
252
+ npx ablo branch ensure "preview-pr-${PR_NUMBER}" \
253
+ --kind preview \
254
+ --credential \
255
+ --ttl-hours 168 \
256
+ --json
257
+ ```
258
+
259
+ `--credential` explicitly requests plaintext secret material. Treat the JSON
260
+ result as a secret, mask it in logs, and pass it through the deployment
261
+ provider's secret-variable mechanism.
262
+
263
+ Closing a preview should call `branch delete`. Deletion immediately makes
264
+ branch-bound credentials fail authentication even if their expiry is later.
265
+
266
+ ## Managing branches in Sync Web
267
+
268
+ The persistent dashboard header selects a project and then one branch inside it. The
269
+ selection scopes API keys, Schema, Audit log, and Server log. Production is the
270
+ protected root; every other item is a named development branch.
271
+
272
+ Use the **Branches** page to create, select, inspect, or delete a branch.
273
+ Deleting a child revokes all of its credentials first, removes its datasource
274
+ connection material, and then removes it from discovery. It never deletes a
275
+ sibling, the production root, customer tables, or retained schema history.
276
+
277
+ See [Ablo branch lifecycle](../../../docs/explainers/branch-lifecycle.md) for
278
+ the complete project/branch mental model and dashboard journey.
279
+
280
+ ## Your database and migrations
281
+
282
+ There are two independent contracts:
283
+
284
+ ```text
285
+ Your ORM/migration tool owns tables, columns, relations, and DDL
286
+ Ablo schema owns the coordination contract for synced models
287
+ ```
288
+
289
+ `ablo dev` currently isolates the Ablo branch and schema. It does not:
290
+
291
+ - run Prisma, Drizzle, or SQL migrations;
292
+ - create a Neon/Supabase/RDS database branch;
293
+ - copy production data;
294
+ - automatically register whichever `DATABASE_URL` happens to be present.
295
+
296
+ The last point is a safety boundary. A generic `DATABASE_URL` does not prove
297
+ that the database is an isolated feature branch; automatically registering it
298
+ could bind a development credential to production.
299
+
300
+ Until provider-verified database-branch binding lands, use Ablo's hosted branch
301
+ plane for the inner loop or explicitly prepare and review an isolated database
302
+ through [Connect Your Database](./data-sources.md). Never point a child branch at
303
+ production merely because its migrations are compatible.
304
+
305
+ ## Production rollout
306
+
307
+ There is intentionally no `branch merge` that promotes child rows or logs.
308
+
309
+ Production rollout remains:
310
+
311
+ 1. Merge code and migration files through Git.
312
+ 2. Run the reviewed migration against the production database.
313
+ 3. Push the production/root Ablo schema through the deployment workflow.
314
+ 4. Deploy the application.
315
+
316
+ The feature branch proves the change. It does not become production.
317
+
318
+ ## Troubleshooting
319
+
320
+ ### “Creating a development branch needs the active project CLI key”
321
+
322
+ Run:
323
+
324
+ ```bash
325
+ npx ablo login
326
+ ```
327
+
328
+ For a non-default project:
329
+
330
+ ```bash
331
+ npx ablo login --project <project>
332
+ ```
333
+
334
+ If `ABLO_API_KEY` is exported, it overrides the stored login key. Unset it when
335
+ you want the CLI to use the active project's stored profile.
336
+
337
+ ### “Branch credentials cannot manage branches”
338
+
339
+ You supplied a temporary child credential to a management command. Branch
340
+ runtime credentials are deliberately leaf authority. Unset the override and let
341
+ the CLI use the project login key.
342
+
343
+ ### Check the whole branch
344
+
345
+ ```bash
346
+ npx ablo branch check feature-order-approval
347
+ ```
348
+
349
+ The result distinguishes a missing schema, a branch lifecycle failure, and an
350
+ unready connected database. A hosted branch is a valid development state and
351
+ is reported explicitly rather than as a missing database.
352
+
353
+ ### The app still reaches another plane
354
+
355
+ Check for an exported variable:
356
+
357
+ ```bash
358
+ env | grep '^ABLO_API_KEY='
359
+ ```
360
+
361
+ An exported value wins over `.env.local`. Unset it and restart the application
362
+ process so the framework reloads `.env.local`.
363
+
364
+ ### `server_execute_unknown_model`
365
+
366
+ The active branch does not have the schema containing that model. Keep
367
+ `ablo dev` running, or push once:
368
+
369
+ ```bash
370
+ npx ablo dev --no-watch
371
+ ```
372
+
373
+ Confirm that `--schema` and `--export` point at the module your application
374
+ uses.
375
+
376
+ ### Git branch discovery fails
377
+
378
+ Detached checkouts may not expose a local branch. Pass the intended name:
379
+
380
+ ```bash
381
+ npx ablo dev --branch preview-pr-482
382
+ ```
383
+
384
+ or set `ABLO_BRANCH` in CI.
385
+
386
+ ## Related guides
387
+
388
+ - [Quickstart](./quickstart.md) — install, connect, define a schema, and write.
389
+ - [CLI](./cli.md) — the complete command surface.
390
+ - [Schema Contract](./schema-contract.md) — what the registered schema controls.
391
+ - [Connect Your Database](./data-sources.md) — Postgres roles, WAL, and database ownership.
392
+ - [Deployment](./deployment.md) — production schema rollout.