@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 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 `sk_test_` credential. The branch uses a throwaway hosted data plane; Production remains the protected root. There is no shared Sandbox mode.
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.43.0
4
+
5
+ ### Keys are branch-first
6
+
7
+ A new key is simply `sk_…`, `rk_…`, `pk_…`, `ek_…`, or `mk_…`: thirty
8
+ characters and a checksum, with no `live` or `test` in the name. A key's
9
+ project and branch are bindings on the server-side key record, never claims
10
+ encoded in the plaintext, so rotating, moving, or inspecting a key is a
11
+ server-side question with a server-confirmed answer. Existing `live`/`test`
12
+ keys keep working; those spellings are now compatibility forms rather than
13
+ the model.
14
+
15
+ What used to be called the effective key is now the runtime key, and
16
+ `status --json` exposes it as `runtimeKey`. The documentation explains the
17
+ project, branch, and capability model end to end in `docs/api-keys.md`,
18
+ `docs/branch-development.md`, `docs/cli.md`, and `docs/data-sources.md`.
19
+
20
+ ### Credential lookup in the order mature CLIs use
21
+
22
+ Commands resolve their credential from the process `ABLO_API_KEY` first, then
23
+ an explicit `--env-file <path>`, then the stored credential. Read-only
24
+ diagnostics may inspect `.env.local` to help; anything that mutates requires
25
+ an explicit selection. Recovering a database connection is now one command:
26
+ `ablo connect rotate` re-keys the existing roles in place and reuses the
27
+ replication slot, so recovery never drops roles or touches your database by
28
+ hand.
29
+
30
+ ### Removed
31
+
32
+ `logs --mode` is gone: `logs` follows the branch bound to the key it runs
33
+ with. The `effectiveKey` field of `status --json` is renamed `runtimeKey`.
34
+
35
+ ## 0.42.0
36
+
37
+ ### `ablo whoami`: what does this key act on?
38
+
39
+ `whoami` returns the server-confirmed organization, project, and branch a key
40
+ acts on, or fails. Where `status` is the broad health report and deliberately
41
+ degrades when an older or unreachable server cannot confirm identity, `whoami`
42
+ is the strict form of the question, which makes it safe to run before a
43
+ `connect` or `deregister` instead of inferring a key's scope from a failed
44
+ mutation.
45
+
46
+ ### Bound sessions resolve their schema again
47
+
48
+ Every read seam that honored a first-party schema binding substituted the
49
+ owner's organization but kept the caller's own branch, a location where the
50
+ owner's schema never lives. Bound sessions therefore resolved an empty schema:
51
+ bootstraps returned no models and every query answered `unknown_model`. Reads
52
+ and commits now resolve the model map on the owner's branch axis, and an
53
+ absent branch resolves to the owner's root.
54
+
55
+ ### Re-pushing an unchanged schema no longer fails
56
+
57
+ The unchanged-schema fast path skipped the provisioning deferral the real
58
+ migration path has, so an identical `ablo push` was refused on any plane that
59
+ had no DDL to run. The fast path now defers exactly like the real path, and a
60
+ second push of the same schema succeeds with nothing left undone.
61
+
3
62
  ## 0.41.0
4
63
 
5
64
  ### Log in before you have a schema
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 key identifies the Ablo account. Application code does not pass an organization id; Ablo derives scope from the credential.
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
- ## Which credential to use
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 / CLI** (can hold a secret) | your secret `sk_`: it defaults to `ABLO_API_KEY`, so usually pass **nothing** | `Ablo({ schema })` |
25
- | **Browser: read-only** | a publishable `pk_` (safe to ship, like a Stripe `pk_`) | `Ablo({ schema, apiKey: process.env.NEXT_PUBLIC_ABLO_PUBLISHABLE_KEY })` |
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
- **Coming from Stripe? It's the same key model, same prefixes:**
90
+ The credential class lives in the prefix:
36
91
 
37
- | Stripe | Ablo | Where it goes |
92
+ | Prefix | Purpose | Stored where |
38
93
  |---|---|---|
39
- | publishable `pk_` (client-safe) | `pk_` | browser: read-only |
40
- | secret `sk_` (server, full) | `sk_` | server: full authority |
41
- | restricted `rk_` (granular) | `rk_` | scoped agents (`agents.create({ can })`) |
42
- | ephemeral key (client, customer-scoped) | `ek_` | per-user browser sessions (`sessions.create({ user, can })`) |
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
- | 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
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). This is the Stripe ephemeral-key / Supabase
61
- session model. For a read-only app you don't need any of this — just the `pk_` above.
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. Like a Stripe `pk_` or a Supabase anon key, it is long-lived,
94
- org-scoped, and used **directly as the bearer** — never exchanged, never
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 }); // pk_live_
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
- - `sk_test_…` — a key bound to a development branch. Its reads and
116
- writes are invisible to production and to other branches.
117
- - `sk_live_…` a key against your live data.
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 `sk_test_` key for it. The credential carries the immutable branch
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 live-key path in
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 `sk_live_` backend key); a key with a non-empty scope set is
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 sk_test_ credential
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 `sk_test_` credential bound only to the child branch.
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=sk_test_...
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 status` | Show the active org/project, effective credential, branch target, and server health. |
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 vs live
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 `sk_test_` key bound to that child.
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 live credential.
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 Stripe's
66
- `login --project-name` model. The active project (set with `projects use`)
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 & status (above). |: |
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 effective credential's branch activity. Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow` |
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 — the sandboxes and CI runners agents work in
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, like `stripe logs tail`. Scope comes from the key a test
155
- key streams only its sandbox's writes, a live key the org's — so you never pass
156
- an org. Follows by default; `--no-follow` prints recent and exits.
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` | Stripe-style type tag: `'stale_notification'` |
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 |
@@ -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
- > like Stripe test mode. For a sandbox you can throw away, point Ablo at a separate
20
- > or local Postgres. Connect your production database (below) when you're ready for
21
- > it to be the system of record.
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=sk_live_...
184
+ ABLO_API_KEY=sk_...
152
185
  ```
153
186
 
154
187
  ```ts
package/docs/debugging.md CHANGED
@@ -15,14 +15,30 @@ const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, debug: true });
15
15
 
16
16
  ## CLI environment and target
17
17
 
18
- The CLI checks an explicit credential in this order: exported `ABLO_API_KEY`,
19
- `.env.local`, `.env`, then the key saved by `ablo login`. An exported value wins
20
- over project files. If a stale shell export (for example `OPENAI_API_KEY`) is
21
- shadowing a value in `.env.local`, restart the shell or unset the stale variable.
22
-
23
- When a command appears to use the wrong plane or project, run `ablo status`.
24
- It reports the credential source and, when reachable, the server-confirmed
25
- project and environment; that confirmed target is authoritative.
18
+ `push`, `dev`, and `status` follow the application-facing chain: exported
19
+ `ABLO_API_KEY`, `.env.local`, `.env`, then the stored credential. An exported
20
+ value wins over project files. Connect-family administration is intentionally
21
+ stricter: it reads only the process environment and stored login, so an ambient
22
+ file cannot silently choose the plane a database mutation acts on. When a file
23
+ contains the missing key, the refusal names that rule and the remedy.
24
+
25
+ Use the two diagnostics according to the question:
26
+
27
+ ```bash
28
+ npx ablo whoami # strict: which project + branch does this credential target?
29
+ npx ablo status # broad: target, database, schema, drift, and write blockers
30
+ ```
31
+
32
+ For an old, CI, or recovery key stored under another variable name:
33
+
34
+ ```bash
35
+ npx ablo whoami --key-env ABLO_API_KEY_LIVE
36
+ ```
37
+
38
+ That explicit lookup checks the process, `.env.local`, and `.env` for the named
39
+ variable, keeps the value out of argv, and either returns a server-confirmed
40
+ identity or fails non-zero. Do not infer that a key is invalid from an older
41
+ CLI's generic identity error; rerun with the current CLI.
26
42
 
27
43
  `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
44