@everystack/cli 0.4.82 → 0.4.84
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 +44 -0
- package/package.json +10 -2
- package/src/cli/authz-compile.ts +0 -0
- package/src/cli/authz-contract.ts +5 -3
- package/src/cli/authz-reconcile.ts +18 -15
- package/src/cli/aws.ts +17 -1
- package/src/cli/commands/db-apply.ts +445 -60
- package/src/cli/commands/db-check.ts +39 -20
- package/src/cli/commands/db-plan.ts +61 -27
- package/src/cli/commands/db-pull.ts +67 -11
- package/src/cli/commands/db-reconcile.ts +22 -1
- package/src/cli/commands/db.ts +137 -19
- package/src/cli/db-source.ts +8 -1
- package/src/cli/derived-catchup.ts +154 -0
- package/src/cli/derived-render.ts +531 -245
- package/src/cli/derived-source.ts +25 -90
- package/src/cli/edge-plan.ts +76 -14
- package/src/cli/index.ts +7 -7
- package/src/cli/model-wire-lane.ts +143 -0
- package/src/cli/output.ts +12 -0
- package/src/cli/plain-output.ts +521 -0
- package/src/cli/schema-diff.ts +6 -0
- package/src/cli/schema-fingerprint-v11.ts +234 -0
- package/src/cli/schema-fingerprint.ts +20 -6
- package/src/cli/sql-statement.ts +98 -0
- package/src/cli/state-apply.ts +46 -24
- package/src/wire.ts +30 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,50 @@ All notable changes to `@everystack/cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
Format based on [Keep a Changelog](https://keepachangelog.com/).
|
|
6
6
|
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- automate the version-drift check — a manual note is not a gate
|
|
12
|
+
|
|
13
|
+
## [0.4.84] - 2026-09-16
|
|
14
|
+
|
|
15
|
+
_Internal changes only._
|
|
16
|
+
|
|
17
|
+
## [0.4.83] - 2026-09-16
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- db:pull --derived-out takes a directory, split by kind and schema
|
|
22
|
+
- a real SELECT-only role for db:query's read connection
|
|
23
|
+
- version-guard the Model-derived wire payloads (provision, converge-ownership, reconcile)
|
|
24
|
+
- two symbols and plain words — the default output for db:plan/db:apply/db:check
|
|
25
|
+
- db:apply does the whole job — the plan, then the functions and views
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- derive USAGE only on serial sequences (least privilege) (#11)
|
|
30
|
+
- close the closing-gate findings on --derived-out <dir>
|
|
31
|
+
- owner-adoption fast-follows — identity guard, self-grant revoke, ENACT transaction
|
|
32
|
+
- mirror the reader fields in ServerProvision, and pin that no credential prints
|
|
33
|
+
- an E-string prefix only counts when it stands alone
|
|
34
|
+
- throw a private sentinel instead of calling tx.rollback() — closes a console-sandbox swallow/neutralize hole
|
|
35
|
+
- the shape-pin also covers enum value domains, not just field names
|
|
36
|
+
- symmetric v-prefix in the mismatch message, restore db:provision's --direct detail
|
|
37
|
+
- ADD CONSTRAINT/PRIMARY KEY are not additive — they can fail against existing data
|
|
38
|
+
- the lease covers the catch-up, the receipt marks a strip, the preflight never exits mid-chain
|
|
39
|
+
- the catch-up's refusal mode belongs to the caller, and the failure line says why
|
|
40
|
+
- the catch-up failure names two subjects, quotes its cause once, and nobody inherits 'exit'
|
|
41
|
+
- db:apply refuses a malformed plan `statements` instead of throwing
|
|
42
|
+
- what a statement IS gets read once, and a comment can neither hide a revoke nor invent a drop
|
|
43
|
+
- the declared side rides on the plan, so a strip reads the same before and after
|
|
44
|
+
- one statement, one bucket — the summary's two halves read the same declared side
|
|
45
|
+
- the plan's optional grantee set is refused at load, not dereferenced after the write
|
|
46
|
+
|
|
47
|
+
### Documentation
|
|
48
|
+
|
|
49
|
+
- the workflow is three commands — db:plan, db:apply, db:check
|
|
50
|
+
|
|
7
51
|
## [0.4.82] - 2026-09-08
|
|
8
52
|
|
|
9
53
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@everystack/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.84",
|
|
4
4
|
"description": "CLI and OTA updates for Expo apps on everystack",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "Scalable Technology, Inc. <licensing@scalable.technology>",
|
|
@@ -48,6 +48,11 @@
|
|
|
48
48
|
"types": "./src/cli/db-source.ts",
|
|
49
49
|
"default": "./src/cli/db-source.ts"
|
|
50
50
|
},
|
|
51
|
+
"./wire": {
|
|
52
|
+
"source": "./src/wire.ts",
|
|
53
|
+
"types": "./src/wire.ts",
|
|
54
|
+
"default": "./src/wire.ts"
|
|
55
|
+
},
|
|
51
56
|
"./reconcile": {
|
|
52
57
|
"source": "./src/reconcile.ts",
|
|
53
58
|
"types": "./src/reconcile.ts",
|
|
@@ -120,15 +125,17 @@
|
|
|
120
125
|
"@aws-sdk/client-rds": "3.1053.0",
|
|
121
126
|
"@aws-sdk/client-s3": "3.1053.0",
|
|
122
127
|
"@aws-sdk/client-ssm": "3.1053.0",
|
|
128
|
+
"@aws-sdk/credential-provider-node": "3.972.44",
|
|
123
129
|
"@aws-sdk/lib-storage": "3.1053.0",
|
|
124
130
|
"@aws-sdk/client-sts": "3.1053.0",
|
|
125
131
|
"@aws-sdk/signature-v4a": "3.1048.0",
|
|
126
132
|
"glob": "13.0.6",
|
|
133
|
+
"minimatch": "10.2.5",
|
|
127
134
|
"node-forge": "1.4.0",
|
|
128
135
|
"structured-headers": "1.0.1",
|
|
129
136
|
"tsx": "4.21.0",
|
|
130
137
|
"typescript": "5.9.3",
|
|
131
|
-
"@everystack/model": "0.4.
|
|
138
|
+
"@everystack/model": "0.4.18"
|
|
132
139
|
},
|
|
133
140
|
"peerDependencies": {
|
|
134
141
|
"@everystack/server": ">=0.4.0",
|
|
@@ -174,6 +181,7 @@
|
|
|
174
181
|
"jest": "29.7.0",
|
|
175
182
|
"react": "19.2.0",
|
|
176
183
|
"ts-jest": "29.4.9",
|
|
184
|
+
"zod": "3.25.67",
|
|
177
185
|
"@everystack/server": "0.4.30"
|
|
178
186
|
},
|
|
179
187
|
"scripts": {
|
package/src/cli/authz-compile.ts
CHANGED
|
Binary file
|
|
@@ -385,9 +385,11 @@ export interface SequenceGrantContract {
|
|
|
385
385
|
/**
|
|
386
386
|
* The owning column is `GENERATED … AS IDENTITY`, not `serial` — and that changes who needs
|
|
387
387
|
* what. A serial column's value comes from a DEFAULT `nextval(...)` evaluated with the
|
|
388
|
-
* CALLER's privileges, so an INSERTing role must hold USAGE
|
|
389
|
-
* every insert fails `permission denied for sequence`.
|
|
390
|
-
*
|
|
388
|
+
* CALLER's privileges, so an INSERTing role must hold USAGE on the sequence (all `nextval`
|
|
389
|
+
* needs) or every insert fails `permission denied for sequence`. `SELECT`/`UPDATE` are NOT
|
|
390
|
+
* required for an INSERT — they are administrative (see SEQUENCE_PRIVILEGES_FOR_INSERT). An
|
|
391
|
+
* identity column's value is supplied by the system as part of the column definition; no
|
|
392
|
+
* caller privilege is consulted at all.
|
|
391
393
|
*
|
|
392
394
|
* Measured, not assumed: a role with INSERT on the table and NOTHING on the sequence inserts
|
|
393
395
|
* successfully into an identity table and fails on a serial one. So the A6 inheritance rule
|
|
@@ -277,20 +277,23 @@ function tableMap(c: AuthzContract): Map<string, TableContract> {
|
|
|
277
277
|
* Measured on a consumer's database: every INSERT into a serial-PK table failed
|
|
278
278
|
* `permission denied for sequence`, and two of their 563 tests were exactly that.
|
|
279
279
|
*
|
|
280
|
-
* The rule is INSERT on the table → USAGE
|
|
281
|
-
*
|
|
282
|
-
*
|
|
280
|
+
* The rule is INSERT on the table → USAGE on its sequence, and ONLY USAGE. A serial column's
|
|
281
|
+
* value comes from a DEFAULT `nextval(...)`; USAGE is exactly the privilege `nextval` (and
|
|
282
|
+
* `currval`) requires. `setval` (reseed the counter) needs UPDATE and reading `last_value`
|
|
283
|
+
* directly needs SELECT — but neither is part of an INSERT. They are administrative operations,
|
|
284
|
+
* so an INSERT-holder must not inherit them.
|
|
283
285
|
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
286
|
+
* Least privilege, corrected after a regression: an earlier form derived SELECT + UPDATE + USAGE
|
|
287
|
+
* and handed every INSERT-holder `setval` control of a surrogate key. It widened a consumer's
|
|
288
|
+
* governed prod migrator by 28 UPDATE grants nobody asked for — governance ADDED privileges the
|
|
289
|
+
* role had worked fine without. The fix returns to the minimum INSERT needs: USAGE. A role that
|
|
290
|
+
* genuinely reseeds a counter is administrative and is either ungoverned (the reconciler leaves
|
|
291
|
+
* it alone) or must declare the grant deliberately; it is never inferred from INSERT.
|
|
290
292
|
*
|
|
291
|
-
* Derived, never declared: there is no `sequence:` key in the model
|
|
293
|
+
* Derived, never declared: there is no `sequence:` key in the model. Declaring sequence UPDATE
|
|
294
|
+
* for a governed role is a deliberately deferred feature — added when a real need names it.
|
|
292
295
|
*/
|
|
293
|
-
export const SEQUENCE_PRIVILEGES_FOR_INSERT = ['
|
|
296
|
+
export const SEQUENCE_PRIVILEGES_FOR_INSERT = ['USAGE'] as const;
|
|
294
297
|
|
|
295
298
|
export function inheritedSequenceGrants(table: TableContract): Record<string, string[]> {
|
|
296
299
|
const out: Record<string, string[]> = {};
|
|
@@ -332,10 +335,10 @@ function reconcileSequenceGrants(
|
|
|
332
335
|
// NOTHING on the sequence inserts fine into an identity table and fails on a serial one.
|
|
333
336
|
//
|
|
334
337
|
// Applying the rule here was a real WIDENING, caught by the round-trip oracle the day
|
|
335
|
-
// identity columns became declarable: the rebuild handed an INSERTing role
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
//
|
|
338
|
+
// identity columns became declarable: the rebuild handed an INSERTing role USAGE on the
|
|
339
|
+
// identity sequence that the original database never granted and never needed. Nothing is
|
|
340
|
+
// revoked either: a grant somebody made deliberately is not ours to remove, and no
|
|
341
|
+
// declaration says it should not be there.
|
|
339
342
|
if (seq.identity) continue;
|
|
340
343
|
const target = quoteQualified(seq.sequence);
|
|
341
344
|
const grantees = new Set([...Object.keys(declared), ...Object.keys(seq.grants)]);
|
package/src/cli/aws.ts
CHANGED
|
@@ -290,7 +290,23 @@ export async function resolveFunctionName(
|
|
|
290
290
|
* credential-free introspection path. Every deployed-stage read verb
|
|
291
291
|
* (db:plan, db:apply --stage's descent probe, …) shares it: catalog SQL runs
|
|
292
292
|
* inside AWS on the operator connection, and the row set comes back over IAM.
|
|
293
|
-
*
|
|
293
|
+
*
|
|
294
|
+
* WHAT MAKES THIS READ-ONLY: not this file, and no longer a keyword blocklist in the
|
|
295
|
+
* action. `db:query` runs its single statement inside an explicit PostgreSQL `READ ONLY`
|
|
296
|
+
* transaction that always rolls back, so the ENGINE refuses a write — after parsing,
|
|
297
|
+
* where no leading comment, quoted identifier, writable CTE, `SELECT ... INTO`,
|
|
298
|
+
* `EXPLAIN ANALYZE <write>`, `COPY`, `MERGE`, `CALL` or `DO` block reaches. (A red team
|
|
299
|
+
* against a real PostgreSQL 16 defeated the blocklist that stood here before; see the
|
|
300
|
+
* comment on the `db:query` action in `@everystack/server`'s plugin.ts.)
|
|
301
|
+
*
|
|
302
|
+
* WHAT IT DOES NOT COVER: a read-only transaction stops DATA writes, not every effect.
|
|
303
|
+
* Still reachable by anyone who may invoke this action — `CLUSTER`, `REINDEX`,
|
|
304
|
+
* `LOCK TABLE ... ACCESS EXCLUSIVE`, `pg_sleep`, `NOTIFY`, and session advisory locks:
|
|
305
|
+
* availability and side-channel effects, not data change. An out-of-transaction escape
|
|
306
|
+
* exists via `dblink_exec` / `pg_background` IF those extensions are installed AND
|
|
307
|
+
* executable by the operator role. Closing those needs a real SELECT-only role wired in
|
|
308
|
+
* as the query connection (`readDatabaseUrl`) — a separate, already-filed follow-up, and
|
|
309
|
+
* an infrastructure decision rather than a code change here.
|
|
294
310
|
*/
|
|
295
311
|
export function lambdaQueryRunner(
|
|
296
312
|
region: string,
|