@abloatai/ablo 0.64.2 → 0.64.3
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/CHANGELOG.md +46 -0
- package/docs/api-keys.md +18 -6
- package/docs/cli.md +9 -2
- package/docs/customer-organizations.md +110 -147
- package/docs/examples/account-multiplayer.md +2 -1
- package/docs/react.md +6 -0
- package/docs/sessions.md +18 -28
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.64.3
|
|
4
|
+
|
|
5
|
+
### Existing connections make room for new tables
|
|
6
|
+
|
|
7
|
+
`ablo connect apply --tables` now checks the requested tables even when an
|
|
8
|
+
existing connection reports ready. It reconciles publication membership before
|
|
9
|
+
returning, keeping existing credentials intact. If a snapshot needs recovery,
|
|
10
|
+
the requested tables are reconciled before that recovery begins; a snapshot
|
|
11
|
+
already in progress is left undisturbed.
|
|
12
|
+
|
|
13
|
+
Generated PostgreSQL setup also includes the replica identity needed to retain
|
|
14
|
+
old row values for updates and deletes. The companion sync-server changes track
|
|
15
|
+
snapshot coverage against the active schema, so adding a model cannot inherit
|
|
16
|
+
an older snapshot's readiness. Those server changes require a runtime rollout;
|
|
17
|
+
upgrading the CLI alone does not deploy them.
|
|
18
|
+
|
|
19
|
+
### One session mint at browser startup
|
|
20
|
+
|
|
21
|
+
A cold reactive client now uses its first session credential to resolve identity,
|
|
22
|
+
avoiding a second call to the application's session endpoint and its membership
|
|
23
|
+
checks. Later refreshes still obtain a fresh credential. The React guide also
|
|
24
|
+
clarifies application-owned disposal, account switching and startup retry.
|
|
25
|
+
|
|
26
|
+
The browser packages now include their event emitter dependency. Applications
|
|
27
|
+
can bundle the React entry with an ordinary browser bundler without supplying a
|
|
28
|
+
Node `events` polyfill themselves.
|
|
29
|
+
|
|
30
|
+
### A clearer boundary for customer accounts
|
|
31
|
+
|
|
32
|
+
The customer-isolation guide now follows one application connection, explicit
|
|
33
|
+
model subject rules and sessions minted from verified membership. It separates
|
|
34
|
+
your application's account IDs from Ablo organization, project and branch scope,
|
|
35
|
+
and explains what must change in writers, existing rows and database policies
|
|
36
|
+
when adopting that model. Session and API-key guidance use the same distinction.
|
|
37
|
+
|
|
38
|
+
### Prepare schema SQL without a connection
|
|
39
|
+
|
|
40
|
+
`ablo migrate --offline` generates schema SQL without database credentials or an
|
|
41
|
+
Ablo API key. It prints the SQL or writes it with `--output`, using the same
|
|
42
|
+
planner as a connected migration. The output is explicitly unvalidated: applying
|
|
43
|
+
a migration still requires the connected deployment checks, as do previews that
|
|
44
|
+
omit `--offline`.
|
|
45
|
+
|
|
46
|
+
This release introduces no breaking API changes. Ablo, Transaction, Humans and
|
|
47
|
+
the CLI are published together at 0.64.3.
|
|
48
|
+
|
|
3
49
|
## 0.64.2
|
|
4
50
|
|
|
5
51
|
### More complete upgrade guidance
|
package/docs/api-keys.md
CHANGED
|
@@ -234,7 +234,7 @@ Both management scopes are explicit grants on `mk_` credentials. Runtime
|
|
|
234
234
|
`sk_`, `rk_`, `pk_`, and `ek_` credentials cannot become management
|
|
235
235
|
credentials through an empty scope set or a CLI fallback.
|
|
236
236
|
|
|
237
|
-
|
|
237
|
+
For ordinary application keys, branch binding remains an authority boundary even when a key has no granular
|
|
238
238
|
scope strings: a temporary child key can act only inside that child. It cannot
|
|
239
239
|
manage siblings or gain root authority.
|
|
240
240
|
|
|
@@ -253,11 +253,23 @@ Treat that key as a dedicated minting credential:
|
|
|
253
253
|
- rotate it on a schedule and revoke it immediately after suspected exposure;
|
|
254
254
|
- log the target `organizationId`, minted session id, and request id for audit.
|
|
255
255
|
|
|
256
|
-
The
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
The issuer's `organization:act-as` grant can target **any organization**. Ablo
|
|
257
|
+
does not enforce an application-customer allowlist for that authority. A resulting
|
|
258
|
+
session is restricted by its target organization, model operations and expiry,
|
|
259
|
+
but restricting a session does not narrow the issuer's power to mint another.
|
|
260
|
+
Cross-organization mints select the target organization's root branch, including
|
|
261
|
+
when the issuer is bound to a child branch.
|
|
262
|
+
|
|
263
|
+
Normal dashboard key provisioning does not grant this scope. Ablo operators
|
|
264
|
+
provision it using `apps/sync-server/scripts/provision-platform-key.ts` with
|
|
265
|
+
`--org=<issuer-org> --branch=<issuer-branch> --env=sandbox` (or `production`). The
|
|
266
|
+
script requires control-plane database access and emits a new secret once.
|
|
267
|
+
The environment flag controls the credential prefix; it does not force target
|
|
268
|
+
sessions onto a staging branch.
|
|
269
|
+
|
|
270
|
+
For customers sharing one application, use model subject rules and verified
|
|
271
|
+
session groups with an ordinary application key. See
|
|
272
|
+
[Serving Many Customers](./customer-organizations.md).
|
|
261
273
|
|
|
262
274
|
## Current and legacy key spellings
|
|
263
275
|
|
package/docs/cli.md
CHANGED
|
@@ -133,7 +133,7 @@ branch-bound runtime credential before starting application code.
|
|
|
133
133
|
| `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. |
|
|
134
134
|
| `ablo logs` | Tail the resolved runtime credential's branch activity. Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow` |
|
|
135
135
|
| `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` |
|
|
136
|
-
| `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` |
|
|
136
|
+
| `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. | `--offline`, `--dry-run`, `--output <file>`, `--schema`, `--export` |
|
|
137
137
|
| `ablo pull` | **Direct Postgres**: generate `defineSchema(...)` from your existing tables (read-only, like `prisma db pull`). | `--out <path>`, `--app-schema <name>`, `--import <pkg>`, `--force` |
|
|
138
138
|
| `ablo check` | **Direct Postgres**: verify your _existing_ tables fit the schema (read-only, no schema changes). | `--schema <path>`, `--export <name>`, `--app-schema <name>` |
|
|
139
139
|
| `ablo connect plan\|apply\|check\|rotate\|deregister` | **Direct Postgres**: register and maintain a database plane. Here `--schema` means the existing PostgreSQL namespace, never a TypeScript file. | `--url <postgres-url>`, `--schema <postgres-schema>`, `--env-file <path>`, `--yes` |
|
|
@@ -266,9 +266,16 @@ server applies the change and version-gates connecting clients.
|
|
|
266
266
|
```bash
|
|
267
267
|
ablo migrate --dry-run # preview the exact SQL
|
|
268
268
|
ablo migrate # apply to DATABASE_URL
|
|
269
|
-
ablo migrate --output schema.sql # write SQL to a file
|
|
269
|
+
ablo migrate --output schema.sql # write SQL to a file after connected validation
|
|
270
|
+
ablo migrate --offline --output schema.sql # generate SQL without credentials
|
|
270
271
|
```
|
|
271
272
|
|
|
273
|
+
`--offline` uses the same schema SQL generator without reading database or Ablo
|
|
274
|
+
credentials. Omit `--output` to print SQL. This is schema generation, not a
|
|
275
|
+
validated deployment plan: it cannot detect existing-data conflicts or drift.
|
|
276
|
+
Run `ablo migrate` with credentials to validate and apply; `--dry-run` and
|
|
277
|
+
`--output` without `--offline` retain connected validation.
|
|
278
|
+
|
|
272
279
|
### One database, two schemas
|
|
273
280
|
|
|
274
281
|
`ablo migrate` does **not** own your whole database. It creates exactly the
|
|
@@ -1,184 +1,147 @@
|
|
|
1
1
|
# Serving Many Customers
|
|
2
2
|
|
|
3
|
-
> One
|
|
3
|
+
> One application connection, customer subject rules, and sessions minted from verified membership.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
For a multi-account application, keep one Ablo organization, one project for the
|
|
6
|
+
application, and one branch per environment. Your customers remain rows in your
|
|
7
|
+
own database. Their existing account IDs can identify the subjects authorized by
|
|
8
|
+
Ablo; they do not need corresponding Ablo account registrations.
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
`accountId` to the `account` group, mint sessions only after verifying membership,
|
|
10
|
-
and use those scoped sessions for server operations too. The complete
|
|
11
|
-
[account multiplayer walkthrough](./examples/account-multiplayer.md) shows this
|
|
12
|
-
composition and its verification path.
|
|
10
|
+
## What each layer owns
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
| Layer | Meaning | Boundary |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| Ablo organization | Your team's Ablo account | Outer organization authority |
|
|
15
|
+
| Project | Your application and its schema | Application data plane |
|
|
16
|
+
| Branch | An environment of that application | Connection, retained log and branch-bound credentials |
|
|
17
|
+
| Customer subject | An account in your application | A model rule checks the row against verified session groups |
|
|
18
|
+
| Session | One human or agent's granted access | Subject membership, model operations and expiry |
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
`organizationId` on a session names the outer Ablo organization. It does not
|
|
21
|
+
select an account inside your application. A model's `subject` declares that
|
|
22
|
+
inner boundary; session `groups` supplies the authenticated membership needed
|
|
23
|
+
to pass it. Routing groups alone are not row authorization.
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export const schema = defineSchema(
|
|
27
|
-
{
|
|
28
|
-
// Its rows form the group `customer:<id>`; the kind comes from `groups.root`.
|
|
29
|
-
customers: model(
|
|
30
|
-
{ name: z.string() },
|
|
31
|
-
{ groups: { root: 'customer' } },
|
|
32
|
-
),
|
|
33
|
-
// A child inherits its customer's group through the `parent` edge.
|
|
34
|
-
decks: model(
|
|
35
|
-
{ customerId: z.string(), title: z.string() },
|
|
36
|
-
{ relations: { customer: relation.belongsTo('customers', 'customerId', { parent: true }) } },
|
|
37
|
-
),
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
identityRoles: [
|
|
41
|
-
identityRole({ kind: 'org', source: 'organizationId' }),
|
|
42
|
-
identityRole({ kind: 'user', source: 'userId' }),
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
);
|
|
46
|
-
```
|
|
25
|
+
## Declare the customer boundary
|
|
26
|
+
|
|
27
|
+
Use an explicit required account field on every customer-protected model:
|
|
47
28
|
|
|
48
29
|
```ts
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
user: { id: member.userId },
|
|
57
|
-
can: { customers: ['read'], decks: ['read', 'create', 'update'] },
|
|
58
|
-
groups: [syncGroup('customer', member.customerId)],
|
|
59
|
-
}),
|
|
30
|
+
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
31
|
+
|
|
32
|
+
export const schema = defineSchema({
|
|
33
|
+
conversationSummaries: model({
|
|
34
|
+
accountId: z.string().min(1),
|
|
35
|
+
title: z.string(),
|
|
36
|
+
}, { subject: { field: 'accountId', group: 'account' }, load: 'instant' }),
|
|
60
37
|
});
|
|
61
38
|
```
|
|
62
39
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Four things carry a name in this arrangement, and mixing two of them up is the
|
|
69
|
-
one mistake worth spending a page to prevent.
|
|
70
|
-
|
|
71
|
-
| Layer | What it is | Where it lives |
|
|
72
|
-
|---|---|---|
|
|
73
|
-
| Your account | The organization you signed up with. Colleagues join it with their own logins and share one bill. | Ablo |
|
|
74
|
-
| Your application | A project. One per app you run, bound to one schema in your database. | Ablo |
|
|
75
|
-
| Your customer | A row in your own table, with your own id on it. | Your database |
|
|
76
|
-
| One person's session | An `ek_` your backend mints, cut to one customer's group. | Minted per sign-in |
|
|
77
|
-
|
|
78
|
-
Your customers sit in the third row. They are not accounts, because an account
|
|
79
|
-
is something you invite colleagues into. They are not projects, because a
|
|
80
|
-
project binds to a Postgres schema and you run one application, not one per
|
|
81
|
-
customer.
|
|
82
|
-
|
|
83
|
-
Your `sk_` already carries your account, so a session never names it. What the
|
|
84
|
-
session adds is which customer the person in front of it may read.
|
|
85
|
-
|
|
86
|
-
## Where the boundary is enforced
|
|
40
|
+
The subject rule requires the session to hold `account:<row.accountId>`. Keep
|
|
41
|
+
account IDs from Better Auth, or another membership system, in this field.
|
|
42
|
+
The outer tenancy column `organization_id` retains the owning Ablo organization;
|
|
43
|
+
it is not a second name for the customer account field.
|
|
87
44
|
|
|
88
|
-
|
|
45
|
+
Push the schema to the application branch and apply subject policies through
|
|
46
|
+
[supported connection setup](./data-sources.md). Updating TypeScript alone does
|
|
47
|
+
not update the server schema, existing rows or database policies. For an existing
|
|
48
|
+
projection, migrate its writer and backfill as well as its model declaration.
|
|
89
49
|
|
|
90
|
-
|
|
91
|
-
organization, project, and branch, and all three are compared on every read and
|
|
92
|
-
every write, from the credential rather than the request. A client cannot reach
|
|
93
|
-
past them by asking. This is the boundary that holds unconditionally.
|
|
50
|
+
## Mint after verifying membership
|
|
94
51
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
| Path | Group cut applied |
|
|
100
|
-
|---|---|
|
|
101
|
-
| Live delivery and fan-out | Yes |
|
|
102
|
-
| HTTP read on a log-served plane (a connected database) | Yes |
|
|
103
|
-
| HTTP read on a hosted or direct-query plane | **No.** Scoped by organization |
|
|
104
|
-
| Writes | **No.** The groups are recorded on the change, never checked against the row |
|
|
105
|
-
| Claim listings and presence | Yes |
|
|
106
|
-
|
|
107
|
-
So a session cut to one customer, on a hosted plane, can read another
|
|
108
|
-
customer's rows over HTTP; and on any plane it can write to them. What stops it
|
|
109
|
-
today is the organization, which both customers share under this shape.
|
|
110
|
-
|
|
111
|
-
If isolating your customers is a security requirement, give each one its own
|
|
112
|
-
Ablo organization. The stronger row-and-subject authorization that would make
|
|
113
|
-
this shape safe on every path is not in the engine yet.
|
|
114
|
-
|
|
115
|
-
## Naming a group
|
|
116
|
-
|
|
117
|
-
Build a group with the `syncGroup(kind, id)` helper rather than a string. The
|
|
118
|
-
kind is the one you declared in `groups.root`, and the id is your own
|
|
119
|
-
identifier for the customer.
|
|
52
|
+
Your backend authenticates the person and verifies membership in the requested
|
|
53
|
+
account on every mint. Use the verified account ID, never an unchecked request
|
|
54
|
+
parameter:
|
|
120
55
|
|
|
121
56
|
```ts
|
|
122
|
-
|
|
57
|
+
import Sessions from '@abloatai/ablo/sessions';
|
|
58
|
+
import { syncGroup } from '@abloatai/ablo/schema';
|
|
59
|
+
import { schema } from './schema';
|
|
60
|
+
|
|
61
|
+
const sessions = Sessions({ schema, apiKey: process.env.ABLO_API_KEY! });
|
|
62
|
+
const session = await sessions.create({
|
|
63
|
+
user: { id: verifiedMember.userId },
|
|
64
|
+
groups: [syncGroup('account', verifiedMember.accountId)],
|
|
65
|
+
can: { conversationSummaries: ['read'] },
|
|
66
|
+
ttlSeconds: 300,
|
|
67
|
+
});
|
|
123
68
|
```
|
|
124
69
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
70
|
+
This session inherits the issuer's organization, project and branch. No
|
|
71
|
+
`organizationId` override or `organization:act-as` grant is needed. The model's
|
|
72
|
+
subject rule authorizes rows; `can` authorizes operations. A list filter merely
|
|
73
|
+
selects a view.
|
|
128
74
|
|
|
129
|
-
|
|
75
|
+
The [account multiplayer walkthrough](./examples/account-multiplayer.md) contains
|
|
76
|
+
the complete server membership handler, browser lifecycle and scoped server
|
|
77
|
+
writes. A secret-key singleton must not substitute for a subject-scoped session
|
|
78
|
+
when performing customer operations.
|
|
130
79
|
|
|
131
|
-
|
|
132
|
-
organization when one of them reading or writing another's rows would be an
|
|
133
|
-
incident rather than a bug, when you cannot audit group coverage across every
|
|
134
|
-
model, or when a customer is a separate paying business that signs in to Ablo
|
|
135
|
-
itself and invites its own developers.
|
|
80
|
+
## Share one database connection
|
|
136
81
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
82
|
+
Customers with subject-protected rows in the same application branch use that
|
|
83
|
+
branch's connected database and schema. There is no per-customer connection
|
|
84
|
+
registration. Ablo serves connected-plane reads from its retained log, populated
|
|
85
|
+
from the database; readiness and snapshot coverage still need verification.
|
|
140
86
|
|
|
141
|
-
|
|
87
|
+
A Data Source is selected by organization, project and branch. Neither a subject
|
|
88
|
+
ID nor a routing group selects a different connection.
|
|
142
89
|
|
|
143
|
-
|
|
144
|
-
derived from the row's id, and the first session minted against it is delivered
|
|
145
|
-
its data.
|
|
90
|
+
## Where isolation is enforced
|
|
146
91
|
|
|
147
|
-
|
|
148
|
-
|
|
92
|
+
For models with subject rules, the server checks subject membership beneath the
|
|
93
|
+
UI across bootstrap, known-ID and list HTTP reads, live delivery and catch-up.
|
|
94
|
+
Write authorization checks the row's subject too. Routing membership in another
|
|
95
|
+
group does not bypass the subject rule. Models without subject rules do not gain
|
|
96
|
+
customer isolation merely because a session names an account group.
|
|
149
97
|
|
|
150
|
-
|
|
98
|
+
Before enabling browser reads, test two real accounts against the deployed SDK,
|
|
99
|
+
server and connection configuration:
|
|
151
100
|
|
|
152
|
-
|
|
101
|
+
- Bootstrap exposes only the authorized account's rows.
|
|
102
|
+
- Reading another account's known row ID over HTTP is denied.
|
|
103
|
+
- Live updates and reconnect catch-up do not expose foreign rows.
|
|
104
|
+
- Switching accounts disposes the previous client and does not display its
|
|
105
|
+
IndexedDB data in the newly authorized account.
|
|
153
106
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
107
|
+
The maintained example and authorization journeys provide runnable checks;
|
|
108
|
+
local tests are not evidence that a deployed fleet has the same behavior.
|
|
109
|
+
[Groups and shared context](./groups.md) describes cache clearing and membership
|
|
110
|
+
changes. An external membership change does not automatically revoke every
|
|
111
|
+
already-issued session, and offline cached copies cannot be remotely retracted.
|
|
157
112
|
|
|
158
|
-
|
|
113
|
+
## When to use separate Ablo organizations
|
|
159
114
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
115
|
+
Choose separate organizations when customers own separate Ablo accounts or need
|
|
116
|
+
separate outer data planes. The engine can use an unregistered organization
|
|
117
|
+
scope ID; this does not register an Ablo billing account or connect a database.
|
|
163
118
|
|
|
164
|
-
|
|
119
|
+
Cross-organization minting requires a secret key explicitly granted
|
|
120
|
+
`organization:act-as`. That authority can target any organization; there is no
|
|
121
|
+
application-customer allowlist on the grant. Normal dashboard key creation does
|
|
122
|
+
not grant it. It is operator-provisioned platform authority, not a prerequisite
|
|
123
|
+
for serving customers through the shared-application pattern above.
|
|
165
124
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
125
|
+
A cross-organization session uses the target organization's default project and
|
|
126
|
+
root branch. It does **not** inherit the issuer's staging branch. Its schema may
|
|
127
|
+
come from the issuer's project, but its data connection does not: every target
|
|
128
|
+
plane needs its own supported data-source setup. Sharing a schema artifact alone
|
|
129
|
+
does not establish shared Aurora reads.
|
|
169
130
|
|
|
170
|
-
|
|
131
|
+
See [Sessions](./sessions.md) for schema binding and [API keys](./api-keys.md) for
|
|
132
|
+
the authority and lifecycle of a platform mint key.
|
|
171
133
|
|
|
172
|
-
|
|
173
|
-
npx ablo whoami --json
|
|
174
|
-
```
|
|
134
|
+
## Troubleshooting
|
|
175
135
|
|
|
176
|
-
The
|
|
177
|
-
|
|
136
|
+
**The mint is refused.** For customers inside one application, omit
|
|
137
|
+
`organizationId`, declare subject rules and grant verified account groups. Do
|
|
138
|
+
not remove the override without migrating a model that currently uses the
|
|
139
|
+
customer ID as its outer tenancy column.
|
|
178
140
|
|
|
179
|
-
|
|
141
|
+
**A session reads nothing.** Check the branch, pushed subject rule, stored account
|
|
142
|
+
field, granted groups and connection readiness. A schema shared across
|
|
143
|
+
organizations does not share the source connection.
|
|
180
144
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
- [Projects](/projects) — one project per application.
|
|
145
|
+
**A session reads another customer's row.** Check the actual pushed model's
|
|
146
|
+
subject declaration and the session's verified groups. A parent edge, routing
|
|
147
|
+
group or client filter alone is not an authorization rule.
|
|
@@ -50,7 +50,8 @@ Your application authenticates the person and verifies membership on every
|
|
|
50
50
|
mint. The subject rule authorizes rows; `groups` proves membership; `can`
|
|
51
51
|
authorizes operations. Synchronization delivers the authorized data. A list
|
|
52
52
|
filter helps select a view but does not establish any of these permissions.
|
|
53
|
-
|
|
53
|
+
React reads application identity from your auth context; the provider accepts
|
|
54
|
+
no `userId` prop.
|
|
54
55
|
|
|
55
56
|
## Browser reads and server writes share the scope
|
|
56
57
|
|
package/docs/react.md
CHANGED
|
@@ -181,6 +181,12 @@ for the requested account on every mint; the URL itself grants no access.
|
|
|
181
181
|
See the [account multiplayer walkthrough](./examples/account-multiplayer.md)
|
|
182
182
|
for the complete ownership boundary and runnable component.
|
|
183
183
|
|
|
184
|
+
Use `onError` to show a startup failure outside the bootstrap gate. After a
|
|
185
|
+
transient failure, remount the provider with the same client to retry readiness;
|
|
186
|
+
failed `ready()` attempts are retryable. After logout or an account change,
|
|
187
|
+
create a fresh client instead. Strict Mode may create two client instances in
|
|
188
|
+
development; each instance owns its own credential lifecycle.
|
|
189
|
+
|
|
184
190
|
## Render immediately with connection status
|
|
185
191
|
|
|
186
192
|
`useAblo(ablo => ablo.status)` works during provider startup, in passthrough children and in
|
package/docs/sessions.md
CHANGED
|
@@ -182,7 +182,7 @@ for the actor.
|
|
|
182
182
|
|---|---|---|
|
|
183
183
|
| `user` / `agent` | both | The actor. `id` becomes the token's `participantId`. Pass exactly one. |
|
|
184
184
|
| `can` | both | Required non-empty per-model operation allowlist, typed off the schema. |
|
|
185
|
-
| `organizationId` | user |
|
|
185
|
+
| `organizationId` | user | Select another Ablo organization, not an application account. Requires `organization:act-as`; uses the target root branch. |
|
|
186
186
|
| `schemaProject` | user | Override the schema project for a cross-org mint. Usually omitted because the owning key's project is the default. |
|
|
187
187
|
| `groups` | both | Narrow the session below its default scope. Omit to inherit. |
|
|
188
188
|
| `ttlSeconds` | both | Lifetime in seconds. Defaults to `900` (15m). |
|
|
@@ -273,34 +273,24 @@ Some apps need each customer to be its **own** tenant — a hard data boundary
|
|
|
273
273
|
scoping. The law-firm shape (Legora): every firm is its own org, many users
|
|
274
274
|
inside it.
|
|
275
275
|
|
|
276
|
-
|
|
276
|
+
For customers sharing one application connection, declare a model `subject`
|
|
277
|
+
rule and mint verified account `groups` with the ordinary application key. Keep
|
|
278
|
+
its organization, project and branch. The [customer guide](./customer-organizations.md)
|
|
279
|
+
owns the complete tenancy model, migration requirements and isolation checks.
|
|
277
280
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
Sync-group routing controls which changes are delivered; it does not grant or
|
|
284
|
-
deny reads. Do not use scope roots as a tenant security boundary unless every
|
|
285
|
-
model declares the matching policy. If that invariant is difficult to audit,
|
|
286
|
-
use one organization per customer.
|
|
287
|
-
|
|
288
|
-
For the complete key, backend-route, browser, lifecycle, and troubleshooting
|
|
289
|
-
flow, see [Customer Organizations](./customer-organizations.md).
|
|
290
|
-
|
|
291
|
-
The problem that creates: if each customer is a separate org, a naïve setup would
|
|
292
|
-
make you re-push your schema into every new customer's org. You don't have to.
|
|
293
|
-
Keep **one** project as the home of your schema. When its key mints into another
|
|
294
|
-
organization, Ablo automatically resolves the session's *schema* from that key's
|
|
295
|
-
project while its *data* stays in the customer's own org:
|
|
281
|
+
Separate Ablo organizations are separate data planes. A cross-organization user
|
|
282
|
+
session can reuse the issuer's schema artifact, but it uses the target
|
|
283
|
+
organization's default project and root branch. It does not inherit the issuer's
|
|
284
|
+
staging branch or database connection. Each target plane needs its own supported
|
|
285
|
+
data-source setup.
|
|
296
286
|
|
|
297
287
|
```ts
|
|
298
|
-
const
|
|
299
|
-
const
|
|
288
|
+
const sessions = Sessions({ schema, apiKey: process.env.ABLO_PLATFORM_KEY! });
|
|
289
|
+
const session = await sessions.create({
|
|
300
290
|
user: { id: userId },
|
|
301
|
-
organizationId, //
|
|
291
|
+
organizationId, // A separate Ablo organization and data plane.
|
|
302
292
|
can: { records: ['read', 'update'] },
|
|
303
|
-
ttlSeconds:
|
|
293
|
+
ttlSeconds: 300,
|
|
304
294
|
});
|
|
305
295
|
```
|
|
306
296
|
|
|
@@ -322,10 +312,10 @@ your users in the key's own organization.
|
|
|
322
312
|
|
|
323
313
|
## Security
|
|
324
314
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
315
|
+
Session authority combines the organization/project/branch boundary, model
|
|
316
|
+
subject rules, verified groups, the `can` allowlist and expiry. A short TTL limits
|
|
317
|
+
exposure; it does not create row isolation. Keep issuer secret keys on the
|
|
318
|
+
server and verify membership whenever issuing or renewing customer sessions.
|
|
329
319
|
|
|
330
320
|
## User vs. agent sessions
|
|
331
321
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.3",
|
|
4
4
|
"description": "The public Ablo SDK for coordinated reads, commits, claims, observation, and reactive applications.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -120,6 +120,7 @@
|
|
|
120
120
|
"verify:context-package": "node scripts/verify-context-package.mjs",
|
|
121
121
|
"typecheck": "tsc --noEmit && tsc -p typetests/tsconfig.json && tsc -p examples/tsconfig.json",
|
|
122
122
|
"test": "vitest run",
|
|
123
|
+
"test:browser-bundle": "node scripts/check-browser-bundle.mjs",
|
|
123
124
|
"generate:errors": "tsx --conditions=@ablo/source scripts/generate-error-docs.mts",
|
|
124
125
|
"lint:errors": "tsx --conditions=@ablo/source scripts/check-error-docs.mts",
|
|
125
126
|
"generate:pricing": "tsx scripts/generate-pricing-docs.mts",
|
|
@@ -146,8 +147,8 @@
|
|
|
146
147
|
"directory": "packages/ablo"
|
|
147
148
|
},
|
|
148
149
|
"dependencies": {
|
|
149
|
-
"@abloatai/humans": "0.64.
|
|
150
|
-
"@abloatai/transaction": "0.64.
|
|
150
|
+
"@abloatai/humans": "0.64.3",
|
|
151
|
+
"@abloatai/transaction": "0.64.3",
|
|
151
152
|
"zod": "^4.4.3"
|
|
152
153
|
},
|
|
153
154
|
"peerDependencies": {
|