@abloatai/ablo 0.30.2 → 0.31.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/CHANGELOG.md +26 -12
- package/README.md +12 -14
- package/dist/cli.cjs +277 -214
- package/docs/data-sources.md +133 -96
- package/docs/integration-guide.md +6 -5
- package/docs/quickstart.md +37 -30
- package/docs/schema-contract.md +7 -5
- package/package.json +1 -1
package/docs/data-sources.md
CHANGED
|
@@ -1,31 +1,49 @@
|
|
|
1
1
|
# Connect Your Database
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
You write through Ablo, and Ablo writes to your Postgres. A call to
|
|
4
|
+
`ablo.<model>.create / update / delete` enters Ablo's commit chokepoint — where
|
|
5
|
+
claims, ordering, and idempotency are enforced — and Ablo applies the change to
|
|
6
|
+
your database through a scoped role. Your rows live in your database, which stays
|
|
7
|
+
the system of record. Ablo reads your write-ahead log (WAL) to confirm each write
|
|
8
|
+
landed and to keep every connected human and agent current.
|
|
9
|
+
|
|
10
|
+
Ablo writes your **rows**; it never touches your **schema**. It runs no DDL and no
|
|
11
|
+
migrations — your migration tool stays in charge of the shape of your database.
|
|
12
|
+
Ablo only writes rows into tables you already have, through a role scoped to
|
|
13
|
+
exactly that.
|
|
14
|
+
|
|
15
|
+
> **Just trying Ablo?** You don't need a database to start. Pass an `apiKey` only,
|
|
16
|
+
> and Ablo keeps your rows in its own log so you can build the whole app today —
|
|
17
|
+
> like Stripe test mode. For a sandbox you can throw away, point Ablo at a separate
|
|
18
|
+
> or local Postgres. Connect your production database (below) when you're ready for
|
|
19
|
+
> it to be the system of record.
|
|
20
|
+
|
|
21
|
+
Connecting sets up two capabilities on your Postgres: **logical replication**, so
|
|
22
|
+
Ablo can read and confirm, and a **scoped DML role**, so Ablo can write. `ablo
|
|
23
|
+
connect` prints the exact SQL. `ablo connect --apply` runs it for you.
|
|
24
|
+
|
|
25
|
+
## Connect in one command
|
|
11
26
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
> replication (below) when you're ready for it to be the system of record.
|
|
27
|
+
```bash
|
|
28
|
+
npx ablo connect --apply --url postgres://admin:...@host:5432/db
|
|
29
|
+
```
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
31
|
+
Pass an admin connection string with `--url` and it creates the publication, the
|
|
32
|
+
two scoped roles, and the grants, turns on logical decoding where it can, registers
|
|
33
|
+
both scoped roles with Ablo, and proves the setup by reconnecting and reading back.
|
|
34
|
+
The admin credential is used on this machine only and never persisted — nothing is
|
|
35
|
+
written to your `.env`, which keeps holding only `ABLO_API_KEY`. Pass `--show-sql`
|
|
36
|
+
to see every statement first, or drop `--apply` to print the SQL and run it
|
|
37
|
+
yourself. Rotate the scoped passwords any time with `ablo connect --rotate`.
|
|
19
38
|
|
|
20
|
-
|
|
39
|
+
The rest of this page is what that command sets up, step by step, for when you want
|
|
40
|
+
to run it by hand or review exactly what changes.
|
|
21
41
|
|
|
22
|
-
|
|
23
|
-
prints the exact SQL and validates it for you — you never hand-craft replication
|
|
24
|
-
internals.
|
|
42
|
+
## The setup, step by step
|
|
25
43
|
|
|
26
44
|
### 1. Enable logical decoding
|
|
27
45
|
|
|
28
|
-
Turn on logical WAL so Ablo can decode row changes:
|
|
46
|
+
Turn on logical WAL so Ablo can decode row changes and confirm writes:
|
|
29
47
|
|
|
30
48
|
```sql
|
|
31
49
|
ALTER SYSTEM SET wal_level = 'logical';
|
|
@@ -34,21 +52,20 @@ ALTER SYSTEM SET wal_level = 'logical';
|
|
|
34
52
|
`wal_level` is **not reloadable** — you must **restart Postgres** for it to take
|
|
35
53
|
effect. On Amazon RDS / Aurora you can't `ALTER SYSTEM`; set
|
|
36
54
|
`rds.logical_replication = 1` in the instance's parameter group instead, then
|
|
37
|
-
reboot.
|
|
55
|
+
reboot. (`ablo connect --apply` attempts this for you and, where a managed
|
|
56
|
+
provider refuses, hands you the one remaining step.)
|
|
38
57
|
|
|
39
|
-
### 2. Run `ablo connect`
|
|
58
|
+
### 2. Run `ablo connect` for the publication and roles
|
|
40
59
|
|
|
41
60
|
```bash
|
|
42
61
|
npx ablo connect
|
|
43
62
|
```
|
|
44
63
|
|
|
45
|
-
`ablo connect` prints the exact, copy-pasteable setup SQL for **your** Postgres
|
|
46
|
-
|
|
47
|
-
a driver, because logical replication is how you connect. Run the printed SQL
|
|
48
|
-
against your database (as a superuser / the DB owner). It does three things:
|
|
64
|
+
`ablo connect` prints the exact, copy-pasteable setup SQL for **your** Postgres.
|
|
65
|
+
Run it against your database as a superuser or the DB owner. It creates:
|
|
49
66
|
|
|
50
|
-
- **A publication** naming the tables Ablo
|
|
51
|
-
single canonical name the runtime subscribes to):
|
|
67
|
+
- **A publication** naming the tables Ablo reads and confirms against
|
|
68
|
+
(`ablo_publication`, the single canonical name the runtime subscribes to):
|
|
52
69
|
|
|
53
70
|
```sql
|
|
54
71
|
CREATE PUBLICATION "ablo_publication" FOR ALL TABLES;
|
|
@@ -56,22 +73,33 @@ against your database (as a superuser / the DB owner). It does three things:
|
|
|
56
73
|
|
|
57
74
|
Scope it to a subset with `npx ablo connect --tables a,b,c`.
|
|
58
75
|
|
|
59
|
-
- **A
|
|
60
|
-
|
|
61
|
-
servers:
|
|
76
|
+
- **A replication role** — it streams the WAL and `SELECT`s, nothing more. This is
|
|
77
|
+
the role Ablo reads and confirms through. You choose the password; it never
|
|
78
|
+
passes through Ablo's CLI or servers:
|
|
62
79
|
|
|
63
80
|
```sql
|
|
64
81
|
CREATE ROLE "ablo_replicator" WITH REPLICATION LOGIN PASSWORD '<password>';
|
|
65
82
|
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "ablo_replicator";
|
|
66
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "ablo_replicator";
|
|
67
83
|
```
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
On Amazon RDS the `REPLICATION` attribute is granted, not set directly:
|
|
86
|
+
`GRANT rds_replication TO "ablo_replicator";`.
|
|
87
|
+
|
|
88
|
+
- **A scoped writer role** — the role Ablo writes your rows through. It gets row
|
|
89
|
+
DML (`SELECT, INSERT, UPDATE, DELETE`) and the sync ledger, and nothing else: no
|
|
90
|
+
`REPLICATION`, no schema `CREATE`, `NOSUPERUSER NOBYPASSRLS`, row security on. It
|
|
91
|
+
can change rows in your tables; it cannot change your database:
|
|
92
|
+
|
|
93
|
+
```sql
|
|
94
|
+
CREATE ROLE "ablo_writer" WITH LOGIN PASSWORD '<write-password>'
|
|
95
|
+
NOSUPERUSER NOBYPASSRLS NOCREATEDB NOCREATEROLE NOREPLICATION NOINHERIT;
|
|
96
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "ablo_writer";
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Rename either role with `--role <name>` / `--write-role <name>`.
|
|
71
100
|
|
|
72
101
|
The **replication slot** is created and owned by Ablo's runtime when it first
|
|
73
|
-
subscribes
|
|
74
|
-
are the only objects the recipe asks you to create.
|
|
102
|
+
subscribes — you don't pre-create it.
|
|
75
103
|
|
|
76
104
|
### 3. Validate with `ablo connect --check`
|
|
77
105
|
|
|
@@ -82,27 +110,40 @@ database is replication-ready:
|
|
|
82
110
|
npx ablo connect --check
|
|
83
111
|
```
|
|
84
112
|
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
It checks the four invariants and prints a green checklist or the precise per-item
|
|
114
|
+
fix:
|
|
87
115
|
|
|
88
116
|
- `wal_level` is `logical`
|
|
89
117
|
- the `ablo_publication` publication exists
|
|
90
|
-
- the
|
|
118
|
+
- the replication role has the `REPLICATION` attribute
|
|
91
119
|
- every published table has a usable `REPLICA IDENTITY` (a primary key, or
|
|
92
120
|
`REPLICA IDENTITY FULL`) so `UPDATE`/`DELETE` can replicate
|
|
93
121
|
|
|
122
|
+
`--check` reads the two scoped roles from `ABLO_REPLICATION_DATABASE_URL` and
|
|
123
|
+
`ABLO_WRITE_DATABASE_URL` (the `ABLO_`-namespaced names keep a replication-only
|
|
124
|
+
credential from ever landing in the generic `DATABASE_URL` your ORM reads).
|
|
94
125
|
Re-run it until every item is green.
|
|
95
126
|
|
|
96
|
-
### 4.
|
|
127
|
+
### 4. Register the two roles with Ablo
|
|
128
|
+
|
|
129
|
+
`ablo connect --apply` already did this. If you ran the SQL by hand instead, hand
|
|
130
|
+
Ablo both connection strings — the replication role it reads and confirms through,
|
|
131
|
+
and the writer role it lands your rows through:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx ablo connect --register
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This is out of band and happens once. `--register` reads the two scoped connection
|
|
138
|
+
strings from `ABLO_REPLICATION_DATABASE_URL` and `ABLO_WRITE_DATABASE_URL` and
|
|
139
|
+
posts them to Ablo, which holds them encrypted and uses them to read and write your
|
|
140
|
+
database. The role passwords are generated for you and never printed — rotate them
|
|
141
|
+
any time with `ablo connect --rotate`. After this, Ablo does all the connecting.
|
|
97
142
|
|
|
98
|
-
|
|
99
|
-
`REPLICATION`-attributed role that streams the WAL and `SELECT`s, nothing more
|
|
100
|
-
(not a read-only account; see the privilege note below). The same value `--check`
|
|
101
|
-
validated:
|
|
143
|
+
Your **app** holds only the API key — never a connection string:
|
|
102
144
|
|
|
103
145
|
```bash
|
|
104
146
|
# .env — server runtime only, never the browser
|
|
105
|
-
DATABASE_URL=postgres://ablo_replicator:<password>@host:5432/db?sslmode=require
|
|
106
147
|
ABLO_API_KEY=sk_live_...
|
|
107
148
|
```
|
|
108
149
|
|
|
@@ -116,28 +157,33 @@ export const ablo = Ablo({
|
|
|
116
157
|
});
|
|
117
158
|
```
|
|
118
159
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
and
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
owned by you," which is exactly right.
|
|
160
|
+
The Ablo schema describes **only your synced, collaborative models** — the rows
|
|
161
|
+
Ablo coordinates and fans out in realtime. It is *not* your whole-database schema
|
|
162
|
+
and does *not* replace your `schema.prisma` (or Drizzle schema). Your auth,
|
|
163
|
+
billing, and other tables stay in your own ORM schema, owned by your own
|
|
164
|
+
migrations. `ablo check` reflects this — it reports tables you didn't declare as
|
|
165
|
+
"ignored / owned by you," which is exactly right.
|
|
126
166
|
|
|
127
|
-
### 5.
|
|
167
|
+
### 5. Write through `ablo.<model>`
|
|
128
168
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
The read, claim, and coordination surface (`ablo.<model>`) layers on top:
|
|
169
|
+
Every change goes through Ablo. The write enters the commit chokepoint, Ablo
|
|
170
|
+
applies it to your Postgres through the writer role, and the WAL echo confirms it
|
|
171
|
+
landed:
|
|
133
172
|
|
|
134
173
|
```ts
|
|
174
|
+
// Enters the chokepoint (claims, ordering, idempotency), lands in your Postgres.
|
|
175
|
+
await ablo.weatherReports.update('report_stockholm', { high: 21 });
|
|
176
|
+
|
|
177
|
+
// Block until your database has it and the WAL echo confirms.
|
|
178
|
+
await ablo.weatherReports.update('report_stockholm', { high: 21 }, { wait: 'confirmed' });
|
|
179
|
+
|
|
180
|
+
// Reads are live off the same stream.
|
|
135
181
|
const report = ablo.weatherReports.get('report_stockholm');
|
|
136
|
-
const active = ablo.weatherReports.claim.state({ id: 'report_stockholm' });
|
|
137
182
|
```
|
|
138
183
|
|
|
139
|
-
|
|
140
|
-
[
|
|
184
|
+
A commit is accepted the moment Ablo takes it (`queued`); it becomes `confirmed`
|
|
185
|
+
once the row appears on your WAL. See [Guarantees](./guarantees.md) for what each
|
|
186
|
+
state means and when to wait.
|
|
141
187
|
|
|
142
188
|
## What Ablo touches in your database — the honest footprint
|
|
143
189
|
|
|
@@ -145,9 +191,10 @@ This is the complete list. Nothing else.
|
|
|
145
191
|
|
|
146
192
|
| Object | What it is | Owned by |
|
|
147
193
|
|---|---|---|
|
|
148
|
-
| `ablo_publication` | A
|
|
194
|
+
| `ablo_publication` | A publication naming the tables Ablo reads and confirms against. | You create it (step 2). |
|
|
195
|
+
| `ablo_replicator` role | A `REPLICATION` + `SELECT` role Ablo reads and confirms through. | You create it (step 2). |
|
|
196
|
+
| `ablo_writer` role | A scoped DML role Ablo writes your rows through — row DML + ledger, nothing more. | You create it (step 2). |
|
|
149
197
|
| Replication slot | A logical slot Ablo subscribes through to track its WAL position. | Ablo's runtime creates it on first connect. |
|
|
150
|
-
| `ablo_replicator` role | A least-privilege `REPLICATION` + `SELECT` role. | You create it (step 2). |
|
|
151
198
|
| `wal_level = logical` | A server setting that **requires a restart**. | You set it (step 1). |
|
|
152
199
|
|
|
153
200
|
Operational reality you should know up front:
|
|
@@ -155,30 +202,26 @@ Operational reality you should know up front:
|
|
|
155
202
|
- **`wal_level = logical` needs a restart.** It is a one-time, server-wide change
|
|
156
203
|
and is not reloadable.
|
|
157
204
|
- **A replication slot retains WAL.** While Ablo is connected, the slot holds the
|
|
158
|
-
WAL it hasn't yet acknowledged. If Ablo is disconnected for a long time, that
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
- **The role
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- It **never runs DDL** against your database.
|
|
172
|
-
- It **never owns or migrates your schema** — your migration tool stays in charge.
|
|
173
|
-
- It **never writes your rows** — writes are yours, through your backend.
|
|
205
|
+
WAL it hasn't yet acknowledged. If Ablo is disconnected for a long time, that WAL
|
|
206
|
+
accumulates and consumes disk. **Ablo monitors slot lag and WAL retention** and
|
|
207
|
+
surfaces it, so disk pressure never surprises you; an abandoned slot is dropped
|
|
208
|
+
rather than left to grow unbounded.
|
|
209
|
+
- **The writer role changes rows, not your database.** It carries row DML plus the
|
|
210
|
+
sync ledger and nothing more — no `REPLICATION`, no DDL, no object ownership, and
|
|
211
|
+
it runs with row security on and `NOBYPASSRLS`. It is a real, tightly-scoped
|
|
212
|
+
privilege — describe it that way in a security review.
|
|
213
|
+
|
|
214
|
+
Ablo runs **no DDL** and **owns no schema**: your migration tool stays in charge of
|
|
215
|
+
the shape of your database, and Ablo writes only rows, only into tables you already
|
|
216
|
+
have.
|
|
174
217
|
|
|
175
218
|
## What Ablo stores on its side
|
|
176
219
|
|
|
177
220
|
Your schema *definition* (model names, fields, types — pushed with `ablo push`),
|
|
178
221
|
your hashed API keys, a safe projection of the connection registration (host,
|
|
179
222
|
database, schema — the connection string itself is sealed and never echoed back),
|
|
180
|
-
the replication slot position, and the
|
|
181
|
-
rows.
|
|
223
|
+
the replication slot position, and the ordered transaction log that drives sync and
|
|
224
|
+
coordination. Your rows live in your database.
|
|
182
225
|
|
|
183
226
|
> **Postgres replication status: Preview.** Registration, readiness checks, and
|
|
184
227
|
> the server replication fleet are implemented and boot-wired. Preview describes
|
|
@@ -186,6 +229,17 @@ rows.
|
|
|
186
229
|
> [internal/postgres-replication.md](./internal/postgres-replication.md) for the
|
|
187
230
|
> architecture and operational invariants.
|
|
188
231
|
|
|
232
|
+
## When your database can't grant replication
|
|
233
|
+
|
|
234
|
+
Some managed databases won't grant a `REPLICATION` role. For those, Ablo connects
|
|
235
|
+
through a **signed Data Source endpoint** instead: you expose one signed HTTP route
|
|
236
|
+
built from your ORM (`prismaDataSource` / `drizzleDataSource`, with `ablo_outbox` /
|
|
237
|
+
`ablo_idempotency` bookkeeping), and Ablo writes and confirms through it — same
|
|
238
|
+
`ablo.<model>` surface, same commit chokepoint, same `queued` → `confirmed`
|
|
239
|
+
lifecycle. It needs no replication setup, which is exactly why it's the fallback:
|
|
240
|
+
reach for it only when logical replication isn't available, and prefer `ablo
|
|
241
|
+
connect` everywhere else.
|
|
242
|
+
|
|
189
243
|
## Next steps
|
|
190
244
|
|
|
191
245
|
- [Quickstart](./quickstart.md) — connect and write through `ablo.<model>`.
|
|
@@ -194,20 +248,3 @@ rows.
|
|
|
194
248
|
- [Guarantees](./guarantees.md) — what confirmed writes and stale checks mean.
|
|
195
249
|
- [Integration Guide](./integration-guide.md) — the full app, React, multiplayer,
|
|
196
250
|
and agent path.
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## Legacy / not recommended
|
|
201
|
-
|
|
202
|
-
> **Use logical replication instead** (top of this page). The shapes below
|
|
203
|
-
> predate the single connect path. They are documented only so existing
|
|
204
|
-
> integrations can be read and understood — do **not** reach for them when
|
|
205
|
-
> connecting a new database. They are the seams that caused painful onboarding,
|
|
206
|
-
> and `ablo connect` exists precisely to replace them.
|
|
207
|
-
|
|
208
|
-
This older shape connected Ablo to a database by exposing a **signed Data Source
|
|
209
|
-
endpoint** built from an ORM adapter (`prismaDataSource` / `drizzleDataSource`,
|
|
210
|
-
with `ablo_outbox` / `ablo_idempotency` bookkeeping and a reverse-channel
|
|
211
|
-
connector for VPCs). It required Ablo to proxy every write, and has been superseded
|
|
212
|
-
by logical replication, where Ablo only reads your WAL. If you are maintaining one
|
|
213
|
-
of these integrations, migrate it to `ablo connect` at the next opportunity.
|
|
@@ -47,11 +47,12 @@ objects by hand.
|
|
|
47
47
|
Every schema model is backed by **your own database**. The SDK call shape is the
|
|
48
48
|
same everywhere.
|
|
49
49
|
|
|
50
|
-
In this guide — an app that already owns its backend and database — keep
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
In this guide — an app that already owns its backend and database — keep the
|
|
51
|
+
database credentials inside your server runtime and connect out of band: run `npx
|
|
52
|
+
ablo connect` to set up logical replication and a scoped writer role, or expose a
|
|
53
|
+
signed Data Source endpoint when your database can't grant replication. Either way,
|
|
54
|
+
you write through `ablo.<model>`; Ablo lands each change in your Postgres and
|
|
55
|
+
confirms it over the WAL. Application and agent code hold only `ABLO_API_KEY` — the
|
|
55
56
|
client never sees a connection string. [Connect Your Database](./data-sources.md)
|
|
56
57
|
is the single source of truth for both paths.
|
|
57
58
|
|
package/docs/quickstart.md
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Build with Ablo on **the Postgres you already have**. You declare a small Ablo
|
|
4
4
|
schema for the models humans and agents edit together, connect Ablo to your
|
|
5
|
-
database
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
database (`ablo connect`), and read and write every one of those models through
|
|
6
|
+
`ablo.<model>`. You write through Ablo; it lands the change in your Postgres and
|
|
7
|
+
confirms it by tailing your write-ahead log (WAL). Your rows live in your database,
|
|
8
|
+
which stays the system of record. Ablo writes rows but **runs no DDL and owns no
|
|
9
|
+
schema** — your migration tool stays in charge of the shape of your database.
|
|
10
10
|
|
|
11
|
-
> No database yet?
|
|
12
|
-
>
|
|
13
|
-
>
|
|
11
|
+
> No database yet? Pass an `apiKey` only and Ablo keeps your rows in its own log,
|
|
12
|
+
> so you can build the whole app today — like Stripe test mode. Point it at a
|
|
13
|
+
> separate or local Postgres for a throwaway sandbox, or at your production
|
|
14
|
+
> database when you're ready.
|
|
14
15
|
|
|
15
16
|
## 1. Install and initialize
|
|
16
17
|
|
|
@@ -97,32 +98,38 @@ collapses to the untyped client.
|
|
|
97
98
|
|
|
98
99
|
## 3. Connect your database with `ablo connect`
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
`ablo connect` sets your database up so Ablo can write your rows (a scoped DML
|
|
102
|
+
role) and read them back to confirm (logical replication). It writes rows through
|
|
103
|
+
that role but runs no DDL and owns no schema — your migration tool stays in charge.
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# On RDS/Aurora: set rds.logical_replication = 1 in the parameter group, reboot.
|
|
108
|
-
|
|
109
|
-
# 2. Print the exact publication + replication-role SQL for YOUR Postgres, run it:
|
|
110
|
-
npx ablo connect
|
|
105
|
+
You run `ablo connect` once, out of band — it provisions the roles and hands them
|
|
106
|
+
to Ablo. From then on Ablo does the connecting; your app never opens a database
|
|
107
|
+
connection.
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
```bash
|
|
110
|
+
# Point it at an admin connection once — it does the whole ceremony: creates the
|
|
111
|
+
# roles + publication, turns on logical decoding where it can, registers both
|
|
112
|
+
# scoped roles with Ablo, and proves it by reading back. Nothing lands in your .env.
|
|
113
|
+
npx ablo connect --apply --url postgres://admin:...@host:5432/db
|
|
114
|
+
|
|
115
|
+
# ...or print the SQL and run it yourself, then register:
|
|
116
|
+
# npx ablo connect # prints the publication + two scoped roles
|
|
117
|
+
# npx ablo connect --check # validates the database is ready
|
|
118
|
+
# npx ablo connect --register # hands the two scoped roles to Ablo
|
|
114
119
|
```
|
|
115
120
|
|
|
116
|
-
`ablo connect`
|
|
117
|
-
`
|
|
118
|
-
`
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
`ablo connect --apply` generates two roles and their passwords — an
|
|
122
|
+
`ablo_replicator` role (`REPLICATION` + `SELECT`, for reads and confirmation) and
|
|
123
|
+
an `ablo_writer` role (scoped row DML, for writes) — and registers both connection
|
|
124
|
+
strings with Ablo's control plane, encrypted. Ablo's runtime uses them to read and
|
|
125
|
+
write your database. The admin credential you pass to `--url` is used on this
|
|
126
|
+
machine only and never persisted. The role passwords are generated for you and
|
|
127
|
+
never printed — rotate them any time with `ablo connect --rotate`.
|
|
128
|
+
|
|
129
|
+
Your **app** holds only the API key — never a connection string:
|
|
122
130
|
|
|
123
131
|
```bash
|
|
124
132
|
# .env — server runtime only, never the browser
|
|
125
|
-
DATABASE_URL=postgres://ablo_replicator:<password>@host:5432/db?sslmode=require
|
|
126
133
|
ABLO_API_KEY=sk_test_...
|
|
127
134
|
```
|
|
128
135
|
|
|
@@ -137,9 +144,9 @@ export const ablo = Ablo({
|
|
|
137
144
|
});
|
|
138
145
|
```
|
|
139
146
|
|
|
140
|
-
The full setup, the honest footprint (publication + slot + `REPLICATION`
|
|
141
|
-
the `wal_level` restart + slot/WAL retention Ablo monitors), and the
|
|
142
|
-
status
|
|
147
|
+
The full setup, the honest footprint (publication + slot + the `REPLICATION` and
|
|
148
|
+
writer roles + the `wal_level` restart + slot/WAL retention Ablo monitors), and the
|
|
149
|
+
Preview status are in [Connect Your Database](./data-sources.md).
|
|
143
150
|
|
|
144
151
|
## 4. Push the schema, then map it to tables
|
|
145
152
|
|
package/docs/schema-contract.md
CHANGED
|
@@ -102,11 +102,13 @@ the fresh row. Reads stay open; only acting on the row serializes.
|
|
|
102
102
|
|
|
103
103
|
## Storage boundary
|
|
104
104
|
|
|
105
|
-
Every schema model is backed by your own database
|
|
106
|
-
all covered in [Connect Your
|
|
107
|
-
truth): the sandbox (`apiKey`
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
Every schema model is backed by your own database, and you write to it through
|
|
106
|
+
`ablo.<model>`. There are three start states, all covered in [Connect Your
|
|
107
|
+
Database](./data-sources.md) (the single source of truth): the sandbox (`apiKey`
|
|
108
|
+
only, no database — Ablo keeps your rows in its own log), `npx ablo connect` (a
|
|
109
|
+
scoped writer role plus logical replication, so Ablo writes your rows and confirms
|
|
110
|
+
them over the WAL), or a signed Data Source endpoint when your database can't grant
|
|
111
|
+
replication.
|
|
110
112
|
|
|
111
113
|
Your database connects out of band, so the client holds only `ABLO_API_KEY` —
|
|
112
114
|
never a connection string. Browser code goes through `<AbloProvider>` or a scoped
|