@rehearsal-db/core 0.1.0-beta.12 → 0.1.0-beta.13
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 +40 -0
- package/COMPATIBILITY.md +6 -0
- package/README.md +2 -2
- package/docs/commands.md +15 -11
- package/docs/getting-started.md +4 -3
- package/docs/production-source.md +138 -16
- package/docs/sanitization.md +73 -7
- package/docs/security-model.md +8 -0
- package/docs/standalone-workflow.md +24 -9
- package/docs/troubleshooting.md +12 -5
- package/docs/tutorial.md +2 -1
- package/package.json +2 -2
- package/src/baseline/artifact.mjs +27 -4
- package/src/baseline/privacy_engine.mjs +220 -9
- package/src/cli/arguments.mjs +3 -0
- package/src/cli/guided.mjs +8 -6
- package/src/cli/rehearsal.mjs +20 -2
- package/src/cli/renderers.mjs +17 -4
- package/src/cli/runtime_commands.mjs +92 -9
- package/src/cli/source_commands.mjs +26 -5
- package/src/project/setup.mjs +5 -23
- package/src/runtime/plan.mjs +7 -3
- package/src/runtime/restore.mjs +13 -5
- package/src/shared/diagnostics.mjs +7 -0
- package/src/shared/node_runtime.mjs +31 -0
- package/src/source/access.mjs +224 -49
- package/src/source/asset_transfer.mjs +35 -5
- package/src/source/baseline.mjs +42 -6
- package/src/source/external_reader.mjs +255 -0
- package/src/source/postgresql_access.mjs +143 -42
- package/src/source/privacy_paths.mjs +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ a Changelog, and versions will follow Semantic Versioning after the package exis
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Runtime and CI compatibility now cover maintained Node.js 22, 24, and 26 releases;
|
|
11
|
+
Node.js 24 LTS remains the recommended default and release-build runtime.
|
|
12
|
+
- Source access policies can use an externally provisioned, database-expiring
|
|
13
|
+
PostgreSQL reader without administrator credentials. Rehearsal binds canonical export
|
|
14
|
+
view definitions, verifies the complete approved read surface and deny controls, and
|
|
15
|
+
never changes or retires provider-owned source objects.
|
|
16
|
+
- `rehearsal --version`, its conventional `-V` alias, and `rehearsal version` now
|
|
17
|
+
report the installed package version without requiring project configuration.
|
|
18
|
+
- Privacy policy version 2 can use one reviewed identity-aware path mapping for both
|
|
19
|
+
database references and physical Storage destinations. Owner prefixes stay in the
|
|
20
|
+
environment, review plans retain only their hashes, and unsafe or mismatched paths
|
|
21
|
+
fail closed.
|
|
22
|
+
- Related timestamps can share a named deterministic date-shift group, preserving their
|
|
23
|
+
ordering and duration without consumer callbacks.
|
|
24
|
+
- The baseline verification corpus now exercises two unrelated target schemas, a
|
|
25
|
+
10,003-row packed-package stream, concurrent row/schema/ledger changes, malformed and
|
|
26
|
+
truncated results, connection loss, low disk, interruption, and deterministic
|
|
27
|
+
fixed-key output.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Baseline preparation now rejects missing, negative, overflowing, malformed, or
|
|
32
|
+
oversized PostgreSQL estimate and cursor results before activation. Every preparation
|
|
33
|
+
failure preserves the active baseline, removes private staging, and leaves an edited
|
|
34
|
+
runtime untouched.
|
|
35
|
+
- Approved-owner UUID columns are no longer confused with PostgreSQL sequence-backed
|
|
36
|
+
identities. Restore checks the actual local schema before resetting a sequence, while
|
|
37
|
+
genuine numeric identity columns retain correct next-value behavior.
|
|
38
|
+
- A failed Storage transfer now recursively restores write permission only within its
|
|
39
|
+
owned staging tree, removes that incomplete generation, and preserves the original
|
|
40
|
+
safe preparation error instead of replacing it with a cleanup error.
|
|
41
|
+
- Storage preparation now records a canonical media type from reviewed object metadata,
|
|
42
|
+
removing response-only parameters such as a text charset that local bucket MIME
|
|
43
|
+
allowlists reject during restore.
|
|
44
|
+
- Container startup and health failures are now classified as runtime dependency
|
|
45
|
+
failures and include safe Docker/Colima capacity context instead of being mislabeled
|
|
46
|
+
as migration verification failures.
|
|
47
|
+
|
|
8
48
|
## [0.1.0-beta.12] - 2026-10-04
|
|
9
49
|
|
|
10
50
|
### Added
|
package/COMPATIBILITY.md
CHANGED
|
@@ -21,6 +21,12 @@ The first stable `1.0.0` requires external beta evidence, a support policy, and
|
|
|
21
21
|
public API. Supporting additional database families, package managers, or operating
|
|
22
22
|
systems is not implied by the 0.x contract.
|
|
23
23
|
|
|
24
|
+
Rehearsal supports maintained even-numbered Node.js release lines that pass its CI
|
|
25
|
+
matrix. Beta.13 supports Node.js 22, 24, and 26; Node.js 24 LTS is the recommended
|
|
26
|
+
default and the release-build runtime. End-of-life and odd-numbered Node.js releases are
|
|
27
|
+
not supported. A future release may remove a Node.js line after its upstream end-of-life
|
|
28
|
+
date, with that change called out in the changelog.
|
|
29
|
+
|
|
24
30
|
The npm install hook may create project scaffolding only when it proves one application
|
|
25
31
|
root. Ordinary npm application installs are covered. A workspace is automatic only when
|
|
26
32
|
one declaring consumer is already visible in its manifest or lockfile; npm can save a
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ production deployment tool.
|
|
|
22
22
|
|
|
23
23
|
## What you need
|
|
24
24
|
|
|
25
|
-
- Node.js 24 and npm
|
|
25
|
+
- Node.js 22, 24, or 26 and npm (Node.js 24 LTS recommended)
|
|
26
26
|
- Docker Desktop, Colima, or another Docker-compatible engine
|
|
27
27
|
- timestamped `.sql` migration files
|
|
28
28
|
- a project test command that can prove the migrated application works
|
|
@@ -169,7 +169,7 @@ problems belong in the private process described in [SECURITY.md](SECURITY.md).
|
|
|
169
169
|
|
|
170
170
|
## For contributors
|
|
171
171
|
|
|
172
|
-
Use Node.js 24, run `npm ci`, then run:
|
|
172
|
+
Use a supported Node.js release (22, 24, or 26), run `npm ci`, then run:
|
|
173
173
|
|
|
174
174
|
```bash
|
|
175
175
|
npm run check
|
package/docs/commands.md
CHANGED
|
@@ -43,17 +43,17 @@ existing config.
|
|
|
43
43
|
These commands are separate from ordinary rehearsals. Configure `preparation` only after
|
|
44
44
|
reviewing [the standalone security contract](standalone-workflow.md).
|
|
45
45
|
|
|
46
|
-
| Command | What it does
|
|
47
|
-
| ------------------------------------------------------------- |
|
|
48
|
-
| `npx rehearsal privacy key` | Preview the owner-only pseudonym-key path.
|
|
49
|
-
| `npx rehearsal privacy key --write` | Create the key once; refuses to replace an existing key.
|
|
50
|
-
| `npx rehearsal source plan` | Preview the exact temporary reader, views, columns, and asset scope.
|
|
51
|
-
| `npx rehearsal source apply --confirm-source-access=<sha256>` | Create
|
|
52
|
-
| `npx rehearsal baseline refresh` | Replace only the baseline from a reviewed source.
|
|
53
|
-
| `npx rehearsal refresh` | Preview a complete safe refresh of the baseline and local runtime.
|
|
54
|
-
| Add `--confirm-refresh=<sha256>` | Create the replacement, reset locally, and prune listed old copies.
|
|
55
|
-
| `npx rehearsal source retire` | Preview
|
|
56
|
-
| Add `--confirm-source-retirement=<sha256>` | Apply that retirement plan and verify the exact object inventory.
|
|
46
|
+
| Command | What it does |
|
|
47
|
+
| ------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
48
|
+
| `npx rehearsal privacy key` | Preview the owner-only pseudonym-key path. |
|
|
49
|
+
| `npx rehearsal privacy key --write` | Create the key once; refuses to replace an existing key. |
|
|
50
|
+
| `npx rehearsal source plan` | Preview the exact temporary reader, views, columns, and asset scope. |
|
|
51
|
+
| `npx rehearsal source apply --confirm-source-access=<sha256>` | Create managed access, or verify external access without source writes. |
|
|
52
|
+
| `npx rehearsal baseline refresh` | Replace only the baseline from a reviewed source. |
|
|
53
|
+
| `npx rehearsal refresh` | Preview a complete safe refresh of the baseline and local runtime. |
|
|
54
|
+
| Add `--confirm-refresh=<sha256>` | Create the replacement, reset locally, and prune listed old copies. |
|
|
55
|
+
| `npx rehearsal source retire` | Preview managed retirement or external local-secret cleanup. |
|
|
56
|
+
| Add `--confirm-source-retirement=<sha256>` | Apply that retirement plan and verify the exact object inventory. |
|
|
57
57
|
|
|
58
58
|
Source credentials are read from environment variables named in the reviewed source
|
|
59
59
|
policy. Never paste a connection string into an argument. `baseline refresh` leaves the
|
|
@@ -176,6 +176,8 @@ belonging to other projects are never included. This follows
|
|
|
176
176
|
|
|
177
177
|
| Option | Meaning |
|
|
178
178
|
| -------------------------------------- | ---------------------------------------------------------------- |
|
|
179
|
+
| `--help`, `-h` | Show the installed command reference. |
|
|
180
|
+
| `--version`, `-V` | Print the installed package version and exit. |
|
|
179
181
|
| `--json` | Return the versioned machine-readable result. |
|
|
180
182
|
| `--verbose` | Show more safe detail. |
|
|
181
183
|
| `--debug` | Show the most diagnostic detail; still review it before sharing. |
|
|
@@ -193,6 +195,8 @@ belonging to other projects are never included. This follows
|
|
|
193
195
|
| `--write` | Apply a setup, policy, or cleanup preview. |
|
|
194
196
|
|
|
195
197
|
Run `npx rehearsal --help` to print the command list available in your installed version.
|
|
198
|
+
Run `npx rehearsal --version` to print only its version number. Add `--json` when a
|
|
199
|
+
script also needs the package name.
|
|
196
200
|
|
|
197
201
|
When a project has more than one config, pass `--config=<path>` on every command for the
|
|
198
202
|
non-default one. Rehearsal keeps that exact config selected through status, run, reset,
|
package/docs/getting-started.md
CHANGED
|
@@ -7,7 +7,8 @@ Want to experiment first? Use the [safe hands-on tutorial](tutorial.md).
|
|
|
7
7
|
|
|
8
8
|
## 1. Check the required tools
|
|
9
9
|
|
|
10
|
-
You need Node.js 24, npm, and a running Docker-compatible engine
|
|
10
|
+
You need Node.js 22, 24, or 26, npm, and a running Docker-compatible engine. Node.js 24
|
|
11
|
+
LTS is the recommended default:
|
|
11
12
|
|
|
12
13
|
```bash
|
|
13
14
|
node --version
|
|
@@ -15,8 +16,8 @@ npm --version
|
|
|
15
16
|
docker info
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
`node --version` must begin with `v24`. If `docker info` fails, start
|
|
19
|
-
Colima, or your usual Docker engine.
|
|
19
|
+
`node --version` must begin with `v22`, `v24`, or `v26`. If `docker info` fails, start
|
|
20
|
+
Docker Desktop, Colima, or your usual Docker engine.
|
|
20
21
|
|
|
21
22
|
Choose the extra requirement for your project:
|
|
22
23
|
|
|
@@ -33,6 +33,7 @@ URLs, or raw owner identifiers.
|
|
|
33
33
|
"targetFingerprint": "<sha256 of the approved PostgreSQL host, port, and database>",
|
|
34
34
|
"administratorEnvironmentVariable": "REHEARSAL_SOURCE_ADMIN_URL",
|
|
35
35
|
"reader": {
|
|
36
|
+
"mode": "managed",
|
|
36
37
|
"role": "rehearsal_example_reader",
|
|
37
38
|
"ownerRole": "rehearsal_example_owner",
|
|
38
39
|
"credentialFile": ".rehearsal/secrets/source-reader.env",
|
|
@@ -61,7 +62,7 @@ URLs, or raw owner identifiers.
|
|
|
61
62
|
"view": "approved_owner_profile",
|
|
62
63
|
"targetSchema": "app_api",
|
|
63
64
|
"targetTable": "profiles",
|
|
64
|
-
"columns": ["id", "email"],
|
|
65
|
+
"columns": ["id", "email", "avatar_path"],
|
|
65
66
|
"orderBy": ["id"],
|
|
66
67
|
"rowScope": {
|
|
67
68
|
"kind": "approved-owner",
|
|
@@ -70,10 +71,28 @@ URLs, or raw owner identifiers.
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
],
|
|
73
|
-
"
|
|
74
|
+
"assetReader": {
|
|
75
|
+
"endpointFingerprint": "<sha256 of the approved Storage endpoint>",
|
|
76
|
+
"baseUrlEnvironmentVariable": "REHEARSAL_SOURCE_STORAGE_URL",
|
|
77
|
+
"tokenEnvironmentVariable": "REHEARSAL_SOURCE_STORAGE_TOKEN",
|
|
78
|
+
"maximumObjects": 1000,
|
|
79
|
+
"maximumObjectBytes": 52428800,
|
|
80
|
+
"maximumTotalBytes": 2147483648
|
|
81
|
+
},
|
|
82
|
+
"assets": [
|
|
83
|
+
{
|
|
84
|
+
"bucket": "avatars",
|
|
85
|
+
"prefixEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_ID",
|
|
86
|
+
"rights": "approved-owner",
|
|
87
|
+
"pathMapping": "owner-storage"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
74
90
|
}
|
|
75
91
|
```
|
|
76
92
|
|
|
93
|
+
`managed` is the default for existing policies, but writing it explicitly makes the
|
|
94
|
+
credential boundary easier to review.
|
|
95
|
+
|
|
77
96
|
Calculate fingerprints with the exported helpers from `@rehearsal-db/core/source-access`
|
|
78
97
|
or a reviewed local script. A fingerprint binds the destination without recording its
|
|
79
98
|
address. Changing the host, port, or database invalidates the plan.
|
|
@@ -82,13 +101,72 @@ address. Changing the host, port, or database invalidates the plan.
|
|
|
82
101
|
use. `approved-owner` requires one explicit owner value from the named environment
|
|
83
102
|
variable. Rehearsal never chooses an owner by finding an administrator.
|
|
84
103
|
|
|
104
|
+
### Use a provider-managed reader
|
|
105
|
+
|
|
106
|
+
If a database owner already supplies the restricted export views and reader, use
|
|
107
|
+
`mode: "external"`. Omit `administratorEnvironmentVariable`; Rehearsal will not request
|
|
108
|
+
an administrator credential or change the source:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"reader": {
|
|
113
|
+
"mode": "external",
|
|
114
|
+
"role": "provided_rehearsal_reader",
|
|
115
|
+
"connectionEnvironmentVariable": "REHEARSAL_SOURCE_READER_URL",
|
|
116
|
+
"credentialFile": ".rehearsal/secrets/source-reader.env",
|
|
117
|
+
"maximumValidForMinutes": 30
|
|
118
|
+
},
|
|
119
|
+
"exportSchema": "provided_rehearsal_export",
|
|
120
|
+
"relations": [
|
|
121
|
+
{
|
|
122
|
+
"source": { "schema": "private", "table": "profiles" },
|
|
123
|
+
"view": "approved_owner_profile",
|
|
124
|
+
"targetSchema": "app_api",
|
|
125
|
+
"targetTable": "profiles",
|
|
126
|
+
"columns": ["id", "email", "avatar_path"],
|
|
127
|
+
"orderBy": ["id"],
|
|
128
|
+
"rowScope": {
|
|
129
|
+
"kind": "approved-owner",
|
|
130
|
+
"column": "id",
|
|
131
|
+
"valueEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_ID"
|
|
132
|
+
},
|
|
133
|
+
"viewDefinitionSha256": "<sha256 from externalViewDefinitionFingerprint>"
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The database owner must create a `security_barrier` view owned by a role other than the
|
|
140
|
+
reader. Record PostgreSQL's exact canonical definition, not handwritten SQL:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
import { externalViewDefinitionFingerprint } from "@rehearsal-db/core/source-access";
|
|
144
|
+
|
|
145
|
+
console.log(externalViewDefinitionFingerprint(canonicalViewDefinition));
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Obtain `canonicalViewDefinition` with
|
|
149
|
+
`select pg_get_viewdef('provided_rehearsal_export.approved_owner_profile'::regclass, false)`.
|
|
150
|
+
Rehearsal verifies that fingerprint before saving any local access receipt.
|
|
151
|
+
The resulting receipt also binds the normalized source-policy fingerprint. Editing the
|
|
152
|
+
view hash, columns, scopes, or reader declaration requires a new verification.
|
|
153
|
+
|
|
154
|
+
External mode also requires all non-system readable columns to match only the declared
|
|
155
|
+
views and migration-ledger columns. It refuses database, schema, relation, or column
|
|
156
|
+
writes; inherited roles; privileged role attributes; executable non-system
|
|
157
|
+
security-definer functions; a reader-owned view; or a database-enforced expiration
|
|
158
|
+
beyond `maximumValidForMinutes`.
|
|
159
|
+
|
|
85
160
|
`targetSchema` is optional and defaults to `public`. Set it when the restored relation
|
|
86
161
|
belongs elsewhere, and use the same schema in privacy policy version 2. This is relation
|
|
87
162
|
identity, not a request to search every schema.
|
|
88
163
|
|
|
89
|
-
For Storage,
|
|
90
|
-
|
|
91
|
-
|
|
164
|
+
For Storage, the reader URL and token come from named environment variables. A public,
|
|
165
|
+
non-identity prefix may use `prefix`. A private owner prefix uses
|
|
166
|
+
`prefixEnvironmentVariable`, `rights: "approved-owner"`, and a `pathMapping` declared in
|
|
167
|
+
the privacy policy below. Its raw value stays out of tracked files and the review plan
|
|
168
|
+
records only its SHA-256. Objects outside the exact bucket/prefix are not listed or
|
|
169
|
+
downloaded. Size, object-count, path, ETag, and exact-byte checks apply.
|
|
92
170
|
|
|
93
171
|
## 3. Use executable privacy policy version 2
|
|
94
172
|
|
|
@@ -98,6 +176,21 @@ Every selected column has one action and, where required, a bounded recipe:
|
|
|
98
176
|
{
|
|
99
177
|
"policyVersion": 2,
|
|
100
178
|
"migrationCutoff": "20260101000000",
|
|
179
|
+
"bindings": {
|
|
180
|
+
"approved-owner": {
|
|
181
|
+
"environmentVariable": "REHEARSAL_APPROVED_OWNER_ID",
|
|
182
|
+
"approvedValueSha256": "<sha256 of the exact approved owner ID>"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"pathMappings": {
|
|
186
|
+
"owner-storage": {
|
|
187
|
+
"binding": "approved-owner",
|
|
188
|
+
"format": "uuid",
|
|
189
|
+
"namespace": "account-id",
|
|
190
|
+
"maximumBytes": 512,
|
|
191
|
+
"maximumSegments": 8
|
|
192
|
+
}
|
|
193
|
+
},
|
|
101
194
|
"tables": [
|
|
102
195
|
{
|
|
103
196
|
"schema": "app_api",
|
|
@@ -119,6 +212,14 @@ Every selected column has one action and, where required, a bounded recipe:
|
|
|
119
212
|
"generated": "NEVER",
|
|
120
213
|
"identity": "NO",
|
|
121
214
|
"foreignKey": null
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "avatar_path",
|
|
218
|
+
"action": "DERIVE",
|
|
219
|
+
"recipe": { "kind": "path-map", "mapping": "owner-storage" },
|
|
220
|
+
"generated": "NEVER",
|
|
221
|
+
"identity": "NO",
|
|
222
|
+
"foreignKey": null
|
|
122
223
|
}
|
|
123
224
|
]
|
|
124
225
|
}
|
|
@@ -127,9 +228,10 @@ Every selected column has one action and, where required, a bounded recipe:
|
|
|
127
228
|
```
|
|
128
229
|
|
|
129
230
|
Supported recipes are intentionally small: keyed UUID/email/text/integer pseudonyms,
|
|
130
|
-
explicit constants, bounded date shifts,
|
|
131
|
-
tables, columns, JSON keys, formats, and recipes
|
|
132
|
-
or SQL escape hatch.
|
|
231
|
+
explicit constants, bounded or grouped date shifts, identity-aware path mappings, and
|
|
232
|
+
fully classified JSON objects. Unknown tables, columns, JSON keys, formats, and recipes
|
|
233
|
+
stop the refresh. There is no JavaScript or SQL escape hatch. See
|
|
234
|
+
[Sanitization policy](sanitization.md) for complete path and date examples.
|
|
133
235
|
|
|
134
236
|
## 4. Preview, apply, refresh, and retire
|
|
135
237
|
|
|
@@ -146,10 +248,12 @@ npx rehearsal source retire
|
|
|
146
248
|
npx rehearsal source retire --confirm-source-retirement=<full-sha256>
|
|
147
249
|
```
|
|
148
250
|
|
|
149
|
-
`source apply` creates new time-limited roles and export views only when
|
|
150
|
-
is unused. It verifies the reader cannot read raw tables, write,
|
|
151
|
-
an exposed security-definer network function.
|
|
152
|
-
|
|
251
|
+
In managed mode, `source apply` creates new time-limited roles and export views only when
|
|
252
|
+
every exact name is unused. It verifies the reader cannot read raw tables, write,
|
|
253
|
+
escalate roles, or call an exposed security-definer network function. In external mode,
|
|
254
|
+
the same command performs only the read-only checks above and stores the already supplied
|
|
255
|
+
credential in the ignored owner-only local file. It never creates, grants, alters, or
|
|
256
|
+
drops a source object.
|
|
153
257
|
|
|
154
258
|
`refresh` previews the complete replacement first. After exact confirmation, its baseline
|
|
155
259
|
step uses one read-only repeatable-read transaction for the schema shape,
|
|
@@ -160,13 +264,31 @@ runtime is rolled back to the previous baseline and runtime. The lower-level `ba
|
|
|
160
264
|
refresh` command remains available when automation should activate a baseline without
|
|
161
265
|
resetting the runtime or pruning old generations.
|
|
162
266
|
|
|
267
|
+
Rows committed after the repeatable-read transaction begins belong to the next refresh;
|
|
268
|
+
they cannot leak into the current snapshot midway through streaming. A schema or ledger
|
|
269
|
+
change during preparation stops activation. Malformed batches, network loss, interruption,
|
|
270
|
+
limit overruns, and low disk also leave the previous baseline active. Because `baseline
|
|
271
|
+
refresh` never resets the runtime, local runtime edits remain in place whether that
|
|
272
|
+
baseline-only preparation succeeds or fails.
|
|
273
|
+
|
|
274
|
+
Privacy `identity` metadata describes PostgreSQL `GENERATED ... AS IDENTITY`, not a
|
|
275
|
+
person or account identifier. UUID account keys normally use `identity: "NO"` while
|
|
276
|
+
remaining explicitly pseudonymized. During restore, Rehearsal consults the actual local
|
|
277
|
+
table for a backing sequence before resetting it; UUID columns never receive numeric
|
|
278
|
+
`max` or `setval` operations.
|
|
279
|
+
|
|
163
280
|
Storage cannot share PostgreSQL's transaction snapshot. Rehearsal instead verifies each
|
|
164
281
|
object's inventory version and byte count during transfer, then records its SHA-256 in
|
|
165
|
-
the immutable baseline.
|
|
282
|
+
the immutable baseline. For an identity-aware path, the database reference and physical
|
|
283
|
+
destination use the same package-owned pseudonym; the source suffix is preserved.
|
|
166
284
|
|
|
167
|
-
`source retire` inventories the exact recorded views before removal,
|
|
168
|
-
those views, the two recorded roles, and local credential/receipt
|
|
169
|
-
|
|
285
|
+
In managed mode, `source retire` inventories the exact recorded views before removal,
|
|
286
|
+
then removes only those views, the two recorded roles, and local credential/receipt
|
|
287
|
+
files. In external mode, it removes only Rehearsal's local credential and receipt; the
|
|
288
|
+
provider-owned role and views remain untouched. The database-enforced expiration still
|
|
289
|
+
limits access, and the provider may revoke it sooner. Rehearsal does not claim that local
|
|
290
|
+
cleanup revoked a provider-owned credential. Unrelated roles, grants, objects, baselines,
|
|
291
|
+
runtimes, and Docker resources are preserved.
|
|
170
292
|
|
|
171
293
|
## Limits
|
|
172
294
|
|
package/docs/sanitization.md
CHANGED
|
@@ -40,10 +40,10 @@ Two policy versions exist:
|
|
|
40
40
|
Rehearsal can sanitize without project callbacks.
|
|
41
41
|
|
|
42
42
|
Version 2 supports keyed UUID, email, text, and integer pseudonyms; explicit constant
|
|
43
|
-
replacement; bounded date shifting;
|
|
44
|
-
JSON objects; and bounded JSON
|
|
45
|
-
|
|
46
|
-
from the policy.
|
|
43
|
+
replacement; bounded and grouped date shifting; identity-aware path mapping;
|
|
44
|
+
approved-owner conditionals; exhaustively classified JSON objects; and bounded JSON
|
|
45
|
+
arrays. Unknown tables, columns, nested keys, formats, recipes, or missing required
|
|
46
|
+
fields fail closed. It never evaluates JavaScript or SQL from the policy.
|
|
47
47
|
|
|
48
48
|
## Approved-owner values
|
|
49
49
|
|
|
@@ -69,7 +69,7 @@ environment-variable name and SHA-256 receipt:
|
|
|
69
69
|
"action": "PSEUDONYMIZE",
|
|
70
70
|
"recipe": { "format": "uuid", "namespace": "account-id" },
|
|
71
71
|
"generated": "NEVER",
|
|
72
|
-
"identity": "
|
|
72
|
+
"identity": "NO",
|
|
73
73
|
"foreignKey": null
|
|
74
74
|
},
|
|
75
75
|
{
|
|
@@ -101,12 +101,78 @@ node -e 'const {createHash}=require("node:crypto"); process.stdout.write(createH
|
|
|
101
101
|
|
|
102
102
|
Every conditional table names its owner column. Missing bindings, hash mismatches,
|
|
103
103
|
missing owner columns, null/unsupported owner values, and conditionals without an owner
|
|
104
|
-
binding are refused. The owner column must
|
|
105
|
-
|
|
104
|
+
binding are refused. The owner column must use `PSEUDONYMIZE`, preventing a conditional
|
|
105
|
+
policy from retaining raw account IDs. `identity` describes PostgreSQL's generated SQL
|
|
106
|
+
identity metadata, not whether a value identifies a person: ordinary UUID primary keys
|
|
107
|
+
use `"NO"`, while a genuine `GENERATED ... AS IDENTITY` column uses `"YES"`. Rehearsal
|
|
108
|
+
checks the restored PostgreSQL schema for a real backing sequence before attempting a
|
|
109
|
+
sequence reset, so UUID identity values never receive numeric sequence operations. Other
|
|
106
110
|
owners take the declared `otherwise` path; every other identity-bearing field still
|
|
107
111
|
needs its own pseudonymization declaration. Supply the binding only to the local
|
|
108
112
|
server-side Rehearsal process; do not put it in the policy, baseline, or tracked files.
|
|
109
113
|
|
|
114
|
+
## Identity-aware Storage paths
|
|
115
|
+
|
|
116
|
+
Use one named mapping when a database column and a physical Storage object both begin
|
|
117
|
+
with the approved owner's ID. Rehearsal pseudonymizes only that first segment and keeps
|
|
118
|
+
the remaining path unchanged:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"pathMappings": {
|
|
123
|
+
"owner-storage": {
|
|
124
|
+
"binding": "approved-owner",
|
|
125
|
+
"format": "uuid",
|
|
126
|
+
"namespace": "account-id",
|
|
127
|
+
"maximumBytes": 512,
|
|
128
|
+
"maximumSegments": 8
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"tables": [
|
|
132
|
+
{
|
|
133
|
+
"name": "profiles",
|
|
134
|
+
"sourceRows": "STREAM AND SANITIZE",
|
|
135
|
+
"columns": [
|
|
136
|
+
{
|
|
137
|
+
"name": "avatar_path",
|
|
138
|
+
"action": "DERIVE",
|
|
139
|
+
"recipe": { "kind": "path-map", "mapping": "owner-storage" },
|
|
140
|
+
"generated": "NEVER",
|
|
141
|
+
"identity": "NO",
|
|
142
|
+
"foreignKey": null
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The source policy uses the same mapping name and names the binding's environment
|
|
151
|
+
variable as `prefixEnvironmentVariable`; it never contains the raw owner ID. During
|
|
152
|
+
`rehearsal refresh`, the package uses the mapping for both database values and physical
|
|
153
|
+
object destinations. A missing mapping, a different environment variable, a path whose
|
|
154
|
+
first segment is not the exact reviewed owner, traversal, or a size/segment limit breach
|
|
155
|
+
stops the refresh.
|
|
156
|
+
|
|
157
|
+
## Related dates
|
|
158
|
+
|
|
159
|
+
Give related timestamps the same `group` and `days` value to shift them by one stable
|
|
160
|
+
offset. This preserves their order and duration:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"name": "starts_at",
|
|
165
|
+
"action": "DERIVE",
|
|
166
|
+
"recipe": { "kind": "date-shift", "days": 30, "group": "event-window" },
|
|
167
|
+
"generated": "NEVER",
|
|
168
|
+
"identity": "NO",
|
|
169
|
+
"foreignKey": null
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Apply the same recipe to `ends_at`. Rehearsal refuses a group that declares conflicting
|
|
174
|
+
`days` values. Leave out `group` when a date should shift independently.
|
|
175
|
+
|
|
110
176
|
## Structured JSON
|
|
111
177
|
|
|
112
178
|
`json-object` fields are required by default. Set `"required": false` on an explicitly
|
package/docs/security-model.md
CHANGED
|
@@ -26,6 +26,9 @@ runtime.
|
|
|
26
26
|
- successful receipts written only after verification.
|
|
27
27
|
- separate exact-digest approval for source setup, source retirement, and identity claim;
|
|
28
28
|
- short-lived source readers limited to reviewed views/columns and deny checks;
|
|
29
|
+
- externally provisioned readers require canonical view-definition receipts,
|
|
30
|
+
`security_barrier`, independent ownership, an exact non-system readable-column surface,
|
|
31
|
+
no write or role-escalation paths, and database-enforced expiration;
|
|
29
32
|
- bounded read-only extraction with no raw dump or row-value logging;
|
|
30
33
|
- exhaustive privacy policy version 2 with an owner-only local pseudonym key;
|
|
31
34
|
- application children receive only explicit local mappings and stay in an owned process
|
|
@@ -39,6 +42,11 @@ No single environment variable or config edit can redirect runtime commands to
|
|
|
39
42
|
production. Optional source preparation is a separate boundary and never provides a
|
|
40
43
|
hosted execution mode.
|
|
41
44
|
|
|
45
|
+
External source mode verifies access but does not own it. Rehearsal makes no source-side
|
|
46
|
+
changes and removes only its local credential and receipt. The provider owns early
|
|
47
|
+
revocation and the database enforces the reviewed short expiration; Rehearsal never
|
|
48
|
+
reports local cleanup as proof that a provider-owned role was deleted.
|
|
49
|
+
|
|
42
50
|
## Identity providers
|
|
43
51
|
|
|
44
52
|
An optional external identity handshake is distinct from database access. Only declared
|
|
@@ -8,16 +8,18 @@ The optional preparation workflow is a separate security boundary. It has
|
|
|
8
8
|
three deliberately separate phases:
|
|
9
9
|
|
|
10
10
|
1. `source plan` reads declarations and produces a redacted, exact plan.
|
|
11
|
-
2. `source apply` requires the plan digest
|
|
12
|
-
named environment variable
|
|
13
|
-
|
|
11
|
+
2. `source apply` requires the plan digest. Managed mode uses source-owner credentials
|
|
12
|
+
from a named environment variable and creates only the declared, time-limited reader
|
|
13
|
+
and export surface. External mode verifies an already restricted reader without an
|
|
14
|
+
administrator credential or source-side writes.
|
|
14
15
|
3. `refresh` uses that reader in a read-only repeatable-read transaction, builds
|
|
15
16
|
privately, activates only after verification, resets the local runtime, and applies
|
|
16
17
|
reviewed retention. `baseline refresh` is the lower-level baseline-only form.
|
|
17
18
|
|
|
18
|
-
`source retire` has its own preview and digest.
|
|
19
|
-
reader and export objects Rehearsal recorded.
|
|
20
|
-
|
|
19
|
+
`source retire` has its own preview and digest. Managed mode removes only the exact
|
|
20
|
+
reader and export objects Rehearsal recorded. External mode removes only local Rehearsal
|
|
21
|
+
credentials and receipts, preserving provider-owned objects. It never searches for
|
|
22
|
+
similarly named roles or removes unrelated grants.
|
|
21
23
|
|
|
22
24
|
## What belongs in declarations
|
|
23
25
|
|
|
@@ -46,9 +48,12 @@ separate. A declaration contains only the name of an environment variable or
|
|
|
46
48
|
owner-only secret file. Rehearsal never puts credential values in command-line
|
|
47
49
|
arguments, reports, baselines, receipts, or a launched application.
|
|
48
50
|
|
|
49
|
-
The
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
The default managed workflow creates and verifies its own temporary PostgreSQL reader.
|
|
52
|
+
External mode accepts a provider-managed reader only when its complete non-system
|
|
53
|
+
readable-column surface exactly matches the reviewed export views and migration ledger.
|
|
54
|
+
It also verifies canonical view-definition fingerprints, `security_barrier`, ownership,
|
|
55
|
+
write denial, role isolation, security-definer denial, and a short database-enforced
|
|
56
|
+
expiration. Rehearsal never claims ownership or revocation of provider-managed objects.
|
|
52
57
|
|
|
53
58
|
## Snapshot and privacy guarantees
|
|
54
59
|
|
|
@@ -57,6 +62,16 @@ read-only repeatable-read PostgreSQL transaction. Rehearsal rechecks the source
|
|
|
57
62
|
identity and migration evidence before commit. Rows stream in bounded batches;
|
|
58
63
|
record bodies never appear in progress or error output.
|
|
59
64
|
|
|
65
|
+
The snapshot boundary is deliberate: row commits made after extraction starts are left
|
|
66
|
+
for the next baseline, while concurrent schema or migration-ledger changes block the
|
|
67
|
+
current activation. Invalid estimates, malformed batches, connection loss, interruption,
|
|
68
|
+
and disk or configured-limit failures remove private staging and preserve the previously
|
|
69
|
+
active baseline.
|
|
70
|
+
|
|
71
|
+
Cleanup recursively makes only Rehearsal's private staging tree removable. A failed
|
|
72
|
+
Storage transfer therefore reports the original safe transfer error, removes incomplete
|
|
73
|
+
files, and does not alter the active generation.
|
|
74
|
+
|
|
60
75
|
Storage objects do not share the database transaction guarantee. Their inventory records
|
|
61
76
|
size and version metadata; transfer requires that version, verifies the exact byte count,
|
|
62
77
|
and records a SHA-256 in the immutable baseline. Transfer refuses an object that changes,
|
package/docs/troubleshooting.md
CHANGED
|
@@ -34,14 +34,15 @@ Choose **Set the stage** and approve the preview. For pnpm and Yarn, use this gu
|
|
|
34
34
|
automatic root generation is currently tested with npm. An existing supported config is
|
|
35
35
|
kept in place, including `infrastructure/rehearsal/rehearsal.config.mjs`.
|
|
36
36
|
|
|
37
|
-
## Setup says Node.js
|
|
37
|
+
## Setup says this Node.js version is unsupported
|
|
38
38
|
|
|
39
|
-
Rehearsal
|
|
40
|
-
the current shell before installing or
|
|
39
|
+
Rehearsal supports the maintained Node.js 22, 24, and 26 release lines. Node.js 20 and
|
|
40
|
+
odd-numbered releases are end-of-life. Switch the current shell before installing or
|
|
41
|
+
running Rehearsal. The latest LTS is the recommended default:
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
|
-
nvm install
|
|
44
|
-
nvm use
|
|
44
|
+
nvm install --lts
|
|
45
|
+
nvm use --lts
|
|
45
46
|
node --version
|
|
46
47
|
```
|
|
47
48
|
|
|
@@ -53,6 +54,12 @@ writes setup files.
|
|
|
53
54
|
Start Docker Desktop or Colima, confirm `docker info`, then rerun `rehearsal doctor`.
|
|
54
55
|
Restarting the computer is rarely necessary.
|
|
55
56
|
|
|
57
|
+
When a container stays in `starting` or becomes `unhealthy`, Rehearsal reports a runtime
|
|
58
|
+
dependency failure rather than a migration failure. The safe diagnostic includes Docker
|
|
59
|
+
CPU, memory, and container counts; Colima also includes available VM memory when it can
|
|
60
|
+
be read. Stop unrelated local stacks or increase the engine's resources before retrying.
|
|
61
|
+
Rehearsal never changes engine settings or bypasses service health checks.
|
|
62
|
+
|
|
56
63
|
## Docker or Colima is out of disk space
|
|
57
64
|
|
|
58
65
|
Preview what Rehearsal can safely remove:
|
package/docs/tutorial.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
This tutorial runs a complete PostgreSQL rehearsal in a temporary fictional project. It
|
|
4
4
|
does not use your application, production data, or a hosted database.
|
|
5
5
|
|
|
6
|
-
Allow about five minutes. You need Node.js 24 and a running
|
|
6
|
+
Allow about five minutes. You need Node.js 22, 24, or 26 and a running
|
|
7
|
+
Docker-compatible engine. Node.js 24 LTS is recommended.
|
|
7
8
|
|
|
8
9
|
## 1. Prepare Rehearsal
|
|
9
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rehearsal-db/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Safely rehearse PostgreSQL and Supabase migrations against sanitized, production-shaped data.",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": "
|
|
21
|
+
"node": "^22.0.0 || ^24.0.0 || ^26.0.0"
|
|
22
22
|
},
|
|
23
23
|
"bin": {
|
|
24
24
|
"rehearsal": "src/cli/rehearsal.mjs"
|