@rehearsal-db/core 0.1.0-beta.1
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/BENCHMARKS.md +61 -0
- package/CHANGELOG.md +59 -0
- package/COMPATIBILITY.md +22 -0
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/SECURITY.md +19 -0
- package/SUPPORT.md +15 -0
- package/docs/adapters.md +23 -0
- package/docs/baselines.md +33 -0
- package/docs/commands.md +35 -0
- package/docs/configuration.md +98 -0
- package/docs/getting-started.md +247 -0
- package/docs/glossary.md +33 -0
- package/docs/production-source.md +33 -0
- package/docs/releasing.md +79 -0
- package/docs/sanitization.md +69 -0
- package/docs/security-model.md +40 -0
- package/docs/troubleshooting.md +50 -0
- package/docs/tutorial.md +96 -0
- package/package.json +77 -0
- package/scripts/lib/environment/local_supabase.mjs +197 -0
- package/scripts/lib/rehearsal/baseline_artifact.mjs +536 -0
- package/scripts/lib/rehearsal/baseline_builder.mjs +155 -0
- package/scripts/lib/rehearsal/configuration.d.mts +85 -0
- package/scripts/lib/rehearsal/configuration.mjs +559 -0
- package/scripts/lib/rehearsal/diagnostics.mjs +193 -0
- package/scripts/lib/rehearsal/migration_history.mjs +220 -0
- package/scripts/lib/rehearsal/plan.mjs +587 -0
- package/scripts/lib/rehearsal/process_environment.mjs +64 -0
- package/scripts/lib/rehearsal/runtime_restore.mjs +310 -0
- package/scripts/lib/rehearsal/sanitization_policy.mjs +169 -0
- package/scripts/lib/rehearsal/schema_snapshot.mjs +113 -0
- package/scripts/lib/rehearsal/service_environment.mjs +82 -0
- package/scripts/operations/database/manage_rehearsal_database.mjs +784 -0
- package/scripts/operations/rehearsal/rehearsal_cli.mjs +565 -0
package/docs/commands.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# CLI commands
|
|
2
|
+
|
|
3
|
+
All commands run from the consuming project root. State-reporting commands accept
|
|
4
|
+
`--json`; `--verbose` and `--debug` increase safe diagnostics without revealing row
|
|
5
|
+
values or credentials.
|
|
6
|
+
|
|
7
|
+
| Command | Mutates local state | Purpose |
|
|
8
|
+
| ------------------------------------------------------------ | ------------------- | --------------------------------------------------------- |
|
|
9
|
+
| `rehearsal init` | No | Preview safe starter configuration. |
|
|
10
|
+
| `rehearsal init --write` | Config only | Create config without overwriting. |
|
|
11
|
+
| `rehearsal baseline create --records=<path> --ledger=<path>` | Artifact only | Activate a baseline from explicit safe local inputs. |
|
|
12
|
+
| `rehearsal doctor` | No | Check dependencies, inputs, and safety barriers. |
|
|
13
|
+
| `rehearsal explain` | No | Print the immutable execution plan. |
|
|
14
|
+
| `rehearsal run --dry-run` | No | Alias the same plan used by `explain`. |
|
|
15
|
+
| `rehearsal candidates` | No | Print pending migrations and their exact digest. |
|
|
16
|
+
| `rehearsal inspect baseline` | No | Print data-free artifact provenance. |
|
|
17
|
+
| `rehearsal inspect migrations` | No | Classify each migration. |
|
|
18
|
+
| `rehearsal run --confirm-candidates=<sha256>` | Yes, local only | Reset, apply exact candidates, verify, and run app proof. |
|
|
19
|
+
| `rehearsal start` | Runtime only | Start a verified runtime without resetting its data. |
|
|
20
|
+
| `rehearsal migrate --confirm-candidates=<sha256>` | Yes, local only | Apply the exact suffix without resetting current data. |
|
|
21
|
+
| `rehearsal verify` | No data mutation | Verify the current local runtime and receipt. |
|
|
22
|
+
| `rehearsal status` | No | Report runtime, baseline, and candidate state. |
|
|
23
|
+
| `rehearsal reset` | Yes, local only | Replace runtime data with the immutable baseline. |
|
|
24
|
+
| `rehearsal stop` | Runtime only | Stop this project's local services. |
|
|
25
|
+
| `rehearsal discard` | Yes, local only | Remove only this project's disposable runtime and volume. |
|
|
26
|
+
|
|
27
|
+
`run` requires the digest from the current candidate set. Any added, removed, reordered,
|
|
28
|
+
or edited migration changes the digest and invalidates the confirmation.
|
|
29
|
+
|
|
30
|
+
`baseline create` never extracts data. The NDJSON and migration-ledger files must already
|
|
31
|
+
exist inside the project and be safe to retain. Add `--assets=<manifest.json>` to include
|
|
32
|
+
bounded local Storage bytes; every manifest `file` must also remain inside the project.
|
|
33
|
+
|
|
34
|
+
Automation should use `--json` and inspect both exit status and the versioned envelope.
|
|
35
|
+
Exit-code meanings are documented in the root README. Scripts must not parse human text.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Configuration reference
|
|
2
|
+
|
|
3
|
+
`rehearsal.config.ts` is executable configuration with a strict versioned schema.
|
|
4
|
+
Unknown fields and unknown schema versions are errors, not warnings.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { defineRehearsalConfig } from "@rehearsal-db/core";
|
|
8
|
+
|
|
9
|
+
export default defineRehearsalConfig({
|
|
10
|
+
schemaVersion: 1,
|
|
11
|
+
project: { name: "example-app" },
|
|
12
|
+
supabase: {
|
|
13
|
+
workdir: ".",
|
|
14
|
+
migrationDirectory: "supabase/migrations",
|
|
15
|
+
rehearsalConfig: "infrastructure/rehearsal/supabase/config.toml",
|
|
16
|
+
runtimeWorkdir: ".rehearsal/runtime",
|
|
17
|
+
},
|
|
18
|
+
baseline: {
|
|
19
|
+
artifactDirectory: ".rehearsal",
|
|
20
|
+
sanitizationPolicy: "infrastructure/rehearsal/sanitization-policy.json",
|
|
21
|
+
},
|
|
22
|
+
application: {
|
|
23
|
+
startCommand: "npm run dev:rehearsal",
|
|
24
|
+
proofCommand: "npm run test:rehearsal",
|
|
25
|
+
},
|
|
26
|
+
runtime: {
|
|
27
|
+
applicationUrl: "http://localhost:5175",
|
|
28
|
+
projectId: "example-app-rehearsal",
|
|
29
|
+
apiPort: 58321,
|
|
30
|
+
databasePort: 58322,
|
|
31
|
+
studioPort: 58323,
|
|
32
|
+
},
|
|
33
|
+
safety: { hostedAccess: "disabled", outboundNetwork: "deny" },
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Paths
|
|
38
|
+
|
|
39
|
+
All paths resolve inside the consuming project. The artifact directory must be named
|
|
40
|
+
`.rehearsal`; this is an intentional deletion guard. `runtimeWorkdir` must be its
|
|
41
|
+
`runtime` child, and the generated application environment file must remain inside it.
|
|
42
|
+
|
|
43
|
+
## Supabase service environment
|
|
44
|
+
|
|
45
|
+
Some local identity providers need a client ID and secret. Configure both fields or
|
|
46
|
+
neither:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
serviceEnvironmentFile: ".env.rehearsal-service.local",
|
|
50
|
+
serviceEnvironmentVariables: ["LOCAL_IDP_CLIENT_ID", "LOCAL_IDP_SECRET"],
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The file must be ignored, owner-readable only, and contain only the exact allowlisted
|
|
54
|
+
names. These credentials may authorize an identity handshake, but the callback must
|
|
55
|
+
terminate at local Auth. They do not grant hosted database access.
|
|
56
|
+
|
|
57
|
+
## Safety fields
|
|
58
|
+
|
|
59
|
+
Version 1 accepts loopback hosts only. `hostedAccess` can only be `disabled`, and
|
|
60
|
+
`outboundNetwork` can only be `deny`. Ambient hosted Supabase variables are quarantined
|
|
61
|
+
from child processes. There is no force flag to weaken these rules.
|
|
62
|
+
|
|
63
|
+
## Ports and project identity
|
|
64
|
+
|
|
65
|
+
Use unique, non-privileged ports that do not overlap. `projectId` accepts lowercase
|
|
66
|
+
letters, numbers, and hyphens. It labels the local Docker resources so cleanup targets
|
|
67
|
+
only this project.
|
|
68
|
+
|
|
69
|
+
## Application proof
|
|
70
|
+
|
|
71
|
+
`proofCommand` is mandatory and project-owned. It should test restored relationships,
|
|
72
|
+
authentication shape, critical reads, and candidate-migration behavior. A command that
|
|
73
|
+
only checks whether the home page returns 200 is usually too weak.
|
|
74
|
+
|
|
75
|
+
## Runtime adapters
|
|
76
|
+
|
|
77
|
+
Most projects do not need an adapter. Use one only for schema-specific restore setup,
|
|
78
|
+
synthetic local identities, explicit generated environment variables, or post-restore
|
|
79
|
+
invariants. See [adapters](adapters.md).
|
|
80
|
+
|
|
81
|
+
## Advanced library entry points
|
|
82
|
+
|
|
83
|
+
The CLI is the primary interface. Baseline builders and project-owned refresh tooling
|
|
84
|
+
may use these explicit subpaths:
|
|
85
|
+
|
|
86
|
+
- `@rehearsal-db/core/baseline`
|
|
87
|
+
- `@rehearsal-db/core/migrations`
|
|
88
|
+
- `@rehearsal-db/core/schema`
|
|
89
|
+
- `@rehearsal-db/core/diagnostics`
|
|
90
|
+
- `@rehearsal-db/core/process-environment`
|
|
91
|
+
- `@rehearsal-db/core/service-environment`
|
|
92
|
+
|
|
93
|
+
These advanced entry points are ESM JavaScript APIs in the first beta. The root
|
|
94
|
+
configuration and sanitization API has TypeScript declarations; the advanced subpaths do
|
|
95
|
+
not yet promise a typed surface.
|
|
96
|
+
|
|
97
|
+
Undocumented files under `scripts/` are package internals and are not compatibility
|
|
98
|
+
contracts.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
This guide creates a safe local Rehearsal project from synthetic data. It does not
|
|
4
|
+
connect to production, request hosted credentials, or require an existing baseline.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- Node.js 24
|
|
9
|
+
- npm
|
|
10
|
+
- Supabase CLI 2.117.0 (the version proved by package CI)
|
|
11
|
+
- Docker Desktop, Colima, or another Docker-compatible engine
|
|
12
|
+
- a Supabase project with timestamped SQL migrations
|
|
13
|
+
|
|
14
|
+
Confirm the tools first:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node --version
|
|
18
|
+
npm --version
|
|
19
|
+
supabase --version
|
|
20
|
+
docker info
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If you want to see the complete lifecycle before touching your own project, clone the
|
|
24
|
+
Rehearsal repository and run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm ci --ignore-scripts
|
|
28
|
+
npm run test:fixture
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That proof installs the packed package into a disposable fictional project, restores a
|
|
32
|
+
row and Storage object, applies a valid migration, rejects invalid SQL, and removes only
|
|
33
|
+
its labeled local runtime. It does not need a hosted Supabase project or credentials.
|
|
34
|
+
|
|
35
|
+
## 1. Install and initialize
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install --save-dev @rehearsal-db/core
|
|
39
|
+
npx rehearsal init
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`init` is a preview. Read the generated configuration, then explicitly write it:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx rehearsal init --write
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Rehearsal never overwrites an existing configuration.
|
|
49
|
+
|
|
50
|
+
The generated configuration is intentionally incomplete until you review its ports,
|
|
51
|
+
project ID, application commands, and project-owned input paths. Do not run `doctor`
|
|
52
|
+
until the next section's files exist.
|
|
53
|
+
|
|
54
|
+
## 2. Add the project-owned inputs
|
|
55
|
+
|
|
56
|
+
Create the paths named by `rehearsal.config.ts`:
|
|
57
|
+
|
|
58
|
+
- a dedicated local Supabase `config.toml`;
|
|
59
|
+
- a sanitization policy describing every exported field;
|
|
60
|
+
- an active baseline below `.rehearsal/`;
|
|
61
|
+
- an application proof command that exits nonzero when the restored app is wrong.
|
|
62
|
+
|
|
63
|
+
For the generated default paths:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mkdir -p infrastructure/rehearsal/supabase infrastructure/rehearsal rehearsal
|
|
67
|
+
cp supabase/config.toml infrastructure/rehearsal/supabase/config.toml
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Edit the copied Supabase config. Give it the same unique `project_id`, API port, database
|
|
71
|
+
port, and Studio port used by `rehearsal.config.ts`. Disable services your proof does
|
|
72
|
+
not need. This must remain an unlinked local config; never run `supabase link` from it.
|
|
73
|
+
|
|
74
|
+
Also replace the generated `application.startCommand`, `application.proofCommand`, and
|
|
75
|
+
`verification.commands` with real commands from your project. The proof should check a
|
|
76
|
+
restored relationship and the candidate schema—not merely that `/` returns 200.
|
|
77
|
+
|
|
78
|
+
Start with synthetic rows shaped like your schema. Do not start onboarding with
|
|
79
|
+
production data. The following SQL, policy, row, and ledger form one matched example;
|
|
80
|
+
do not mix them with differently shaped snippets.
|
|
81
|
+
|
|
82
|
+
Historical migration `supabase/migrations/20260101000000_create_widgets.sql`:
|
|
83
|
+
|
|
84
|
+
```sql
|
|
85
|
+
create table public.widgets (
|
|
86
|
+
id bigint generated by default as identity primary key,
|
|
87
|
+
name text not null,
|
|
88
|
+
created_at timestamptz not null default now()
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
insert into storage.buckets (id, name, public)
|
|
92
|
+
values ('fixture-assets', 'fixture-assets', false);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Sanitization policy `infrastructure/rehearsal/sanitization-policy.json`:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"policyVersion": 1,
|
|
100
|
+
"migrationCutoff": "20260101000000",
|
|
101
|
+
"tables": [
|
|
102
|
+
{
|
|
103
|
+
"name": "widgets",
|
|
104
|
+
"group": "synthetic",
|
|
105
|
+
"sourceRows": "STREAM AND SANITIZE",
|
|
106
|
+
"columns": [
|
|
107
|
+
{
|
|
108
|
+
"name": "id",
|
|
109
|
+
"action": "KEEP EXACTLY",
|
|
110
|
+
"generated": "NEVER",
|
|
111
|
+
"identity": "YES",
|
|
112
|
+
"foreignKey": null
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "name",
|
|
116
|
+
"action": "REPLACE WITH SYNTHETIC",
|
|
117
|
+
"generated": "NEVER",
|
|
118
|
+
"identity": "NO",
|
|
119
|
+
"foreignKey": null
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "created_at",
|
|
123
|
+
"action": "DERIVE",
|
|
124
|
+
"generated": "NEVER",
|
|
125
|
+
"identity": "NO",
|
|
126
|
+
"foreignKey": null
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Safe row `rehearsal/synthetic-data.ndjson` (one JSON object per physical line):
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"table": "widgets",
|
|
139
|
+
"row": {
|
|
140
|
+
"id": 1,
|
|
141
|
+
"name": "Synthetic Widget",
|
|
142
|
+
"created_at": "2026-01-01T00:00:00.000Z"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Migration ledger `rehearsal/migration-ledger.json`:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
[
|
|
151
|
+
{
|
|
152
|
+
"version": "20260101000000",
|
|
153
|
+
"name": "create_widgets",
|
|
154
|
+
"statements": [
|
|
155
|
+
"create table public.widgets (\n\tid bigint generated by default as identity primary key,\n\tname text not null,\n\tcreated_at timestamptz not null default now()\n)",
|
|
156
|
+
"insert into storage.buckets (id, name, public)\nvalues ('fixture-assets', 'fixture-assets', false)"
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The ledger is evidence, not a second migration language. Its version, name, order, and
|
|
163
|
+
statements must exactly represent the historical migration bytes in the baseline. SQL
|
|
164
|
+
that is merely equivalent is rejected. For a production-shaped baseline, generate this
|
|
165
|
+
ledger through the project's reviewed source/export tooling; do not reconstruct years of
|
|
166
|
+
history by hand.
|
|
167
|
+
|
|
168
|
+
Then activate the safe input:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx rehearsal baseline create \
|
|
172
|
+
--records=rehearsal/synthetic-data.ndjson \
|
|
173
|
+
--ledger=rehearsal/migration-ledger.json
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The independent fixture in this repository is the executable reference example.
|
|
177
|
+
|
|
178
|
+
Expected result:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
Activated synthetic baseline ...: 1 rows across 1 tables; 1 migrations through 20260101000000.
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 3. Check readiness
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx rehearsal doctor
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Do not continue until it ends with `READY`. Doctor checks the local-only boundary,
|
|
191
|
+
dependencies, artifact integrity, migration lineage, ports, and project commands.
|
|
192
|
+
|
|
193
|
+
If it reports `NOT READY`, fix each named prerequisite and rerun it. Do not bypass a
|
|
194
|
+
check or copy a hosted connection string into the generated runtime environment.
|
|
195
|
+
|
|
196
|
+
## 4. Review pending work
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npx rehearsal explain
|
|
200
|
+
npx rehearsal candidates
|
|
201
|
+
npx rehearsal inspect baseline
|
|
202
|
+
npx rehearsal inspect migrations
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The plan prints an exact candidate digest. It does not start services or change data.
|
|
206
|
+
|
|
207
|
+
## 5. Run the rehearsal
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npx rehearsal run --confirm-candidates=<sha256>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Rehearsal restores the immutable baseline, applies only the confirmed migration suffix,
|
|
214
|
+
verifies the runtime, and runs the project-owned application proof. The resulting local
|
|
215
|
+
database is writable, so you can test real application changes without mutating the
|
|
216
|
+
baseline.
|
|
217
|
+
|
|
218
|
+
## 6. Work, verify, and reset
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
npx rehearsal status
|
|
222
|
+
npx rehearsal start
|
|
223
|
+
npx rehearsal verify
|
|
224
|
+
npx rehearsal reset
|
|
225
|
+
npx rehearsal stop
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Changes persist in the disposable runtime until `reset` or runtime removal. `start`
|
|
229
|
+
resumes that runtime without resetting it. `reset` restores the exact baseline. `stop`
|
|
230
|
+
stops only this project's runtime.
|
|
231
|
+
|
|
232
|
+
## Next steps
|
|
233
|
+
|
|
234
|
+
- Follow [the full tutorial](tutorial.md).
|
|
235
|
+
- Read [the security model](security-model.md) before designing a production export.
|
|
236
|
+
- Define an exhaustive [sanitization policy](sanitization.md).
|
|
237
|
+
- Learn the [baseline lifecycle](baselines.md).
|
|
238
|
+
|
|
239
|
+
## Generated files
|
|
240
|
+
|
|
241
|
+
- `rehearsal.config.ts` is written only by `init --write`.
|
|
242
|
+
- `.rehearsal/generations/<id>/` contains one immutable baseline generation.
|
|
243
|
+
- `.rehearsal/current` selects the active generation atomically.
|
|
244
|
+
- `.rehearsal/runtime/` contains the disposable local Supabase project and receipts.
|
|
245
|
+
- `.rehearsal/runtime.env` contains generated loopback-only application credentials.
|
|
246
|
+
|
|
247
|
+
Ignore all of `.rehearsal/`. Do not commit it even when its inputs were synthetic.
|
package/docs/glossary.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Glossary and architecture
|
|
2
|
+
|
|
3
|
+
**Baseline** — immutable sanitized starting artifact.
|
|
4
|
+
|
|
5
|
+
**Candidate migration** — ordered migration suffix not represented by the baseline.
|
|
6
|
+
|
|
7
|
+
**Candidate digest** — checksum binding the exact candidate filenames and bytes.
|
|
8
|
+
|
|
9
|
+
**Project-owned** — application-specific policy or code that remains outside the package.
|
|
10
|
+
|
|
11
|
+
**Represented migration** — historical migration whose exact bytes are bound into the
|
|
12
|
+
baseline.
|
|
13
|
+
|
|
14
|
+
**Runtime** — disposable, writable local Supabase/PostgreSQL environment.
|
|
15
|
+
|
|
16
|
+
**Sanitization policy** — exhaustive project decision for every exported field.
|
|
17
|
+
|
|
18
|
+
**Verified receipt** — local evidence that the exact runtime and candidate suffix passed.
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
flowchart LR
|
|
22
|
+
A[Project-owned approved source] --> B[Project-owned sanitization]
|
|
23
|
+
B --> C[Immutable baseline]
|
|
24
|
+
D[Project migrations] --> E[Digest planner]
|
|
25
|
+
C --> F[Disposable local Supabase]
|
|
26
|
+
E --> F
|
|
27
|
+
F --> G[Project application proof]
|
|
28
|
+
G --> H[Verified local receipt]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The reusable package owns the path from a completed baseline plus migration directory to
|
|
32
|
+
a verified local runtime. The consuming project owns everything that decides which source
|
|
33
|
+
data is allowed, how it is transformed, and what application behavior counts as correct.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Designing a production source boundary
|
|
2
|
+
|
|
3
|
+
The npm package does not connect to production and does not ship an extraction command.
|
|
4
|
+
That separation is a security boundary: each project must authorize, sanitize, and
|
|
5
|
+
audit its own source.
|
|
6
|
+
|
|
7
|
+
## Recommended flow
|
|
8
|
+
|
|
9
|
+
1. Define versioned read-only export views containing only approved columns.
|
|
10
|
+
2. Grant a temporary role `SELECT` on those views and nothing else.
|
|
11
|
+
3. Use a short-lived credential outside shell history and source control.
|
|
12
|
+
4. Stream records through the project-owned sanitization policy.
|
|
13
|
+
5. Write into a private building generation below `.rehearsal`.
|
|
14
|
+
6. Verify counts, checksums, policy coverage, migration history, and secret canaries.
|
|
15
|
+
7. Atomically activate the completed generation.
|
|
16
|
+
8. Revoke and verify removal of the temporary credential.
|
|
17
|
+
9. Delete raw intermediate files and retain only the sanitized artifact.
|
|
18
|
+
|
|
19
|
+
Use server-side cursors or another bounded streaming mechanism. A full in-memory dump
|
|
20
|
+
is not acceptable for large sources. Extraction logs should contain counts and hashes,
|
|
21
|
+
never row bodies.
|
|
22
|
+
|
|
23
|
+
## What not to do
|
|
24
|
+
|
|
25
|
+
- Do not point `rehearsal.config.ts` at a hosted URL.
|
|
26
|
+
- Do not put a production connection string in `.rehearsal/runtime.env`.
|
|
27
|
+
- Do not grant table-wide access merely because a view is inconvenient.
|
|
28
|
+
- Do not commit sanitized baselines; sanitized data is still data.
|
|
29
|
+
- Do not let the reusable package infer which fields are safe.
|
|
30
|
+
- Do not leave the export role provisioned after refresh.
|
|
31
|
+
|
|
32
|
+
The local execution engine remains useful with synthetic or manually approved baselines.
|
|
33
|
+
Production-shaped onboarding is a separate project security exercise.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Release process
|
|
2
|
+
|
|
3
|
+
Rehearsal publishes from a reviewed GitHub release through the protected `npm`
|
|
4
|
+
environment. The workflow builds and hashes the candidate before the environment
|
|
5
|
+
approval gate, then publishes those exact bytes with npm provenance. A local working
|
|
6
|
+
tree is never the release source.
|
|
7
|
+
|
|
8
|
+
The initial publishable `0.1.0-beta.1` release branch is the first change allowed to set
|
|
9
|
+
`"private": false`. Publication still requires the exact protected-main tag, prerelease
|
|
10
|
+
flag, artifact checks, and protected `npm` environment gate described below.
|
|
11
|
+
|
|
12
|
+
`0.1.0-beta.0` was prepared under the unavailable `@rehearsal` npm scope and was never
|
|
13
|
+
published. `0.1.0-beta.1` supersedes that candidate under `@rehearsal-db/core` without
|
|
14
|
+
rewriting the earlier Git tag or GitHub prerelease.
|
|
15
|
+
|
|
16
|
+
## One-time first-package bootstrap
|
|
17
|
+
|
|
18
|
+
npm trusted publishing and staged publishing are package-level settings, so they cannot
|
|
19
|
+
be configured until `@rehearsal-db/core` exists in the registry. The first public version has
|
|
20
|
+
a deliberately narrower bootstrap path:
|
|
21
|
+
|
|
22
|
+
1. The npm owner enables 2FA and creates or confirms the public `@rehearsal-db` organization
|
|
23
|
+
scope. Do not send a password, OTP, recovery code, or access token to another person.
|
|
24
|
+
2. After the reviewed release change reaches protected `main`, the owner creates the
|
|
25
|
+
exact `v0.1.0-beta.1` GitHub release and marks it as a prerelease. The prepare job runs without npm credentials,
|
|
26
|
+
packs the tag, and uploads its versioned tarball plus SHA-1 and SHA-256 metadata. The
|
|
27
|
+
publish job waits at the protected environment and cannot run yet.
|
|
28
|
+
3. The owner downloads or inspects that prepared artifact, confirms its version and
|
|
29
|
+
checksum, and explicitly authorizes those exact bytes.
|
|
30
|
+
4. Only then, the owner creates a short-lived granular npm token with read/write access
|
|
31
|
+
limited to the `@rehearsal-db` scope and **Bypass 2FA** enabled. npm requires that bypass
|
|
32
|
+
for a non-interactive first publish; the environment approval remains the human
|
|
33
|
+
release gate. Store the token only as the `NPM_TOKEN` secret in the protected GitHub
|
|
34
|
+
`npm` environment.
|
|
35
|
+
5. The owner approves the waiting `npm` deployment. GitHub Actions publishes the exact
|
|
36
|
+
uploaded tarball with provenance. The workflow
|
|
37
|
+
refuses to use the bootstrap secret for any version other than `0.1.0-beta.1`.
|
|
38
|
+
6. Immediately after the registry verification passes, the owner deletes the GitHub
|
|
39
|
+
environment secret and revokes the temporary npm token.
|
|
40
|
+
7. From the new package's npm settings, configure the trusted GitHub Actions publisher
|
|
41
|
+
for repository `Ddupasquier/rehearsal-db`, workflow `publish.yml`, and environment
|
|
42
|
+
`npm`. Allow direct `npm publish` for this workflow because the protected GitHub
|
|
43
|
+
environment supplies the human gate. A later switch to npm staged publication must
|
|
44
|
+
change and prove the workflow before narrowing the trusted publisher permission.
|
|
45
|
+
8. Set package publishing access to require 2FA and disallow traditional tokens.
|
|
46
|
+
|
|
47
|
+
This bootstrap token is a one-release compromise imposed by npm's package-creation
|
|
48
|
+
boundary. It is never committed, printed, copied into a ticket, or retained for later
|
|
49
|
+
versions.
|
|
50
|
+
|
|
51
|
+
## Every release candidate
|
|
52
|
+
|
|
53
|
+
1. Start from protected `main` on a dedicated ticketed release branch.
|
|
54
|
+
2. Verify the exact version, changelog, compatibility notes, and packed file list.
|
|
55
|
+
3. Run unit and contract tests, formatting, package-content and secret audits,
|
|
56
|
+
dependency audit, clean tarball installation, and the installed Docker fixture.
|
|
57
|
+
4. Have a developer unfamiliar with the implementing project follow the clean-project
|
|
58
|
+
onboarding. Correct and retest the first confusing, missing, or wrong instruction.
|
|
59
|
+
5. Change `private` to `false` only in the reviewed release change.
|
|
60
|
+
6. Record the exact tarball filename, SHA-1, SHA-256, allowlisted files, unpacked size,
|
|
61
|
+
executable, and zero-runtime-dependency result.
|
|
62
|
+
7. Obtain explicit publication authorization for that exact version and artifact.
|
|
63
|
+
8. Merge the approved release commit through protected `main` and create the exact
|
|
64
|
+
`v<package-version>` tag and GitHub release.
|
|
65
|
+
9. Review and approve the protected `npm` deployment only after its prepare job matches
|
|
66
|
+
the approved version and checksum.
|
|
67
|
+
10. Verify public visibility, ownership, provenance, registry SHA-1, README rendering,
|
|
68
|
+
exact-version clean installation, CLI execution, signatures, and the unrelated
|
|
69
|
+
installed Docker fixture.
|
|
70
|
+
11. Replace consuming projects' temporary Git/archive references only on their own
|
|
71
|
+
protected integration branches and rerun their complete verification.
|
|
72
|
+
|
|
73
|
+
The workflow publishes prereleases under the `beta` dist-tag and refuses a stable
|
|
74
|
+
version. A stable tag requires a later contract, compatibility, and release decision.
|
|
75
|
+
|
|
76
|
+
Creating the repository, passing CI, extracting the engine, merging a release branch,
|
|
77
|
+
or creating a tag does not authorize npm publication. Publication requires explicit
|
|
78
|
+
authorization for the exact packed artifact, and the protected GitHub environment
|
|
79
|
+
supplies the final human gate.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Sanitization policy
|
|
2
|
+
|
|
3
|
+
Rehearsal deliberately does not decide what your application may copy. The consuming
|
|
4
|
+
project owns an exhaustive, reviewable policy for its export surface.
|
|
5
|
+
|
|
6
|
+
Every exported field should receive one action:
|
|
7
|
+
|
|
8
|
+
- `KEEP`: retain an explicitly non-sensitive value needed for realistic behavior;
|
|
9
|
+
- `PSEUDONYMIZE`: replace identity while preserving stable joins;
|
|
10
|
+
- `REPLACE`: substitute a safe value of compatible shape;
|
|
11
|
+
- `EXCLUDE`: omit data that the rehearsal does not need;
|
|
12
|
+
- `DERIVE`: create a bounded safe value from approved inputs.
|
|
13
|
+
|
|
14
|
+
Fail if a new column is unclassified. Do not default unknown fields to `KEEP`.
|
|
15
|
+
|
|
16
|
+
The package exports `validateSanitizationCoverage` and
|
|
17
|
+
`applySanitizationAction` as generic primitives:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {
|
|
21
|
+
applySanitizationAction,
|
|
22
|
+
validateSanitizationCoverage,
|
|
23
|
+
} from "@rehearsal-db/core";
|
|
24
|
+
|
|
25
|
+
validateSanitizationCoverage({ policy, schemaTables });
|
|
26
|
+
|
|
27
|
+
const safeValue = applySanitizationAction({
|
|
28
|
+
action: column.action,
|
|
29
|
+
value: sourceValue,
|
|
30
|
+
pseudonymize: projectPseudonymizer,
|
|
31
|
+
replace: projectReplacement,
|
|
32
|
+
derive: projectDerivation,
|
|
33
|
+
context: { table: table.name, column: column.name },
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Coverage validation requires a one-to-one table and column match: missing and unknown
|
|
38
|
+
entries both fail. The package supplies the operation contract; the project supplies
|
|
39
|
+
the schema inventory, keys, replacements, and derivation logic.
|
|
40
|
+
|
|
41
|
+
## Stable identity
|
|
42
|
+
|
|
43
|
+
Use keyed, deterministic pseudonyms when relationships must survive across tables.
|
|
44
|
+
Keep the key outside source control and outside the final baseline. The same source ID
|
|
45
|
+
should map consistently within a generation, while the original value cannot be
|
|
46
|
+
recovered from the artifact.
|
|
47
|
+
|
|
48
|
+
## High-risk fields
|
|
49
|
+
|
|
50
|
+
Exclude or replace secrets, password material, refresh tokens, session tokens, payment
|
|
51
|
+
data, private messages, precise location, raw uploads, provider credentials, and
|
|
52
|
+
unbounded free text unless a reviewed test requirement proves they are necessary.
|
|
53
|
+
|
|
54
|
+
Images and Storage objects need the same classification as database columns. A public
|
|
55
|
+
product image may be retained under its license; a private upload generally may not.
|
|
56
|
+
|
|
57
|
+
## Validation
|
|
58
|
+
|
|
59
|
+
Before activation, verify:
|
|
60
|
+
|
|
61
|
+
1. every exported column is classified;
|
|
62
|
+
2. prohibited values and secret canaries are absent;
|
|
63
|
+
3. referential relationships required by the app remain valid;
|
|
64
|
+
4. row counts match the approved export manifest;
|
|
65
|
+
5. the policy digest is recorded in the baseline manifest;
|
|
66
|
+
6. raw staging files and temporary credentials are removed.
|
|
67
|
+
|
|
68
|
+
Completeness is not correctness. Human review of the policy and export boundary remains
|
|
69
|
+
mandatory before any real source is introduced.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Security model
|
|
2
|
+
|
|
3
|
+
Rehearsal assumes a mistake is more likely than an attacker. Its design makes ordinary
|
|
4
|
+
misconfiguration fail closed before a state-changing operation.
|
|
5
|
+
|
|
6
|
+
## Trust boundaries
|
|
7
|
+
|
|
8
|
+
The engine trusts the reviewed package code, strict project configuration, a verified
|
|
9
|
+
active baseline, exact migration bytes, and explicitly project-owned proof code. It does
|
|
10
|
+
not trust ambient environment variables, hosted project state, unknown config fields,
|
|
11
|
+
changed migration history, incomplete artifacts, or an unverified runtime.
|
|
12
|
+
|
|
13
|
+
## Independent barriers
|
|
14
|
+
|
|
15
|
+
- loopback-only application and service URLs;
|
|
16
|
+
- a dedicated unlinked Supabase workdir and project ID;
|
|
17
|
+
- exact non-overlapping local ports;
|
|
18
|
+
- a clean child-process environment that omits hosted credentials;
|
|
19
|
+
- immutable baseline files and checksums;
|
|
20
|
+
- exact migration-prefix and candidate digests;
|
|
21
|
+
- local runtime labels used for bounded stop/removal;
|
|
22
|
+
- successful receipts written only after verification.
|
|
23
|
+
|
|
24
|
+
No single environment variable or config edit should redirect the tool to production.
|
|
25
|
+
Version 1 provides no hosted execution mode.
|
|
26
|
+
|
|
27
|
+
## Identity providers
|
|
28
|
+
|
|
29
|
+
An optional external identity handshake is distinct from database access. Only declared
|
|
30
|
+
credential names are read from an ignored owner-only file, and the callback must target
|
|
31
|
+
local Auth. The local account may represent a sanitized production identity, but its
|
|
32
|
+
session and writes remain local.
|
|
33
|
+
|
|
34
|
+
## Remaining responsibilities
|
|
35
|
+
|
|
36
|
+
Rehearsal cannot prove that retained data is lawful, a sanitization rule is ethically
|
|
37
|
+
appropriate, a migration has the intended business meaning, or a project adapter is
|
|
38
|
+
safe. Repository owners must review those decisions and protect artifacts.
|
|
39
|
+
|
|
40
|
+
Report vulnerabilities using the private process in the root security policy.
|