@rehearsal-db/core 0.1.0-beta.10 → 0.1.0-beta.12
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 +64 -1
- package/COMPATIBILITY.md +9 -0
- package/README.md +27 -7
- package/docs/README.md +25 -16
- package/docs/commands.md +11 -4
- package/docs/configuration.md +116 -20
- package/docs/getting-started.md +22 -4
- package/docs/index.md +47 -0
- package/docs/runtime-policies.md +67 -5
- package/docs/sanitization.md +104 -3
- package/docs/standalone-workflow.md +2 -0
- package/docs/troubleshooting.md +39 -3
- package/package.json +12 -3
- package/scripts/install/create_root_config.mjs +36 -0
- package/src/baseline/privacy_engine.mjs +358 -30
- package/src/cli/init_command.mjs +94 -0
- package/src/cli/rehearsal.mjs +6 -51
- package/src/cli/renderers.mjs +31 -2
- package/src/identity/claim.mjs +232 -44
- package/src/identity/provider_configuration.mjs +177 -0
- package/src/project/configuration.d.mts +11 -0
- package/src/project/configuration.mjs +159 -21
- package/src/project/install_scaffold.mjs +291 -0
- package/src/project/setup.mjs +157 -52
- package/src/runtime/plan.mjs +33 -0
- package/src/targets/supabase.mjs +11 -1
package/docs/sanitization.md
CHANGED
|
@@ -40,9 +40,110 @@ 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
|
-
tables, columns, nested keys, formats,
|
|
45
|
-
evaluates JavaScript or SQL
|
|
43
|
+
replacement; bounded date shifting; approved-owner conditionals; exhaustively classified
|
|
44
|
+
JSON objects; and bounded JSON arrays. Unknown tables, columns, nested keys, formats,
|
|
45
|
+
recipes, or missing required fields fail closed. It never evaluates JavaScript or SQL
|
|
46
|
+
from the policy.
|
|
47
|
+
|
|
48
|
+
## Approved-owner values
|
|
49
|
+
|
|
50
|
+
Keep the raw approved owner outside the policy and baseline. The policy stores only its
|
|
51
|
+
environment-variable name and SHA-256 receipt:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"bindings": {
|
|
56
|
+
"approved-owner": {
|
|
57
|
+
"environmentVariable": "REHEARSAL_APPROVED_OWNER_ID",
|
|
58
|
+
"approvedValueSha256": "<lowercase sha256 of the exact owner value>"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"tables": [
|
|
62
|
+
{
|
|
63
|
+
"name": "profiles",
|
|
64
|
+
"sourceRows": "STREAM AND SANITIZE",
|
|
65
|
+
"ownerBinding": { "binding": "approved-owner", "column": "user_id" },
|
|
66
|
+
"columns": [
|
|
67
|
+
{
|
|
68
|
+
"name": "user_id",
|
|
69
|
+
"action": "PSEUDONYMIZE",
|
|
70
|
+
"recipe": { "format": "uuid", "namespace": "account-id" },
|
|
71
|
+
"generated": "NEVER",
|
|
72
|
+
"identity": "YES",
|
|
73
|
+
"foreignKey": null
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "display_name",
|
|
77
|
+
"action": "DERIVE",
|
|
78
|
+
"recipe": {
|
|
79
|
+
"kind": "approved-owner",
|
|
80
|
+
"approved": { "action": "KEEP" },
|
|
81
|
+
"otherwise": {
|
|
82
|
+
"action": "REPLACE",
|
|
83
|
+
"recipe": { "kind": "constant", "value": "Synthetic account" }
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"generated": "NEVER",
|
|
87
|
+
"identity": "NO",
|
|
88
|
+
"foreignKey": null
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Generate the receipt without putting the owner value in shell history:
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
node -e 'const {createHash}=require("node:crypto"); process.stdout.write(createHash("sha256").update(process.env.REHEARSAL_APPROVED_OWNER_ID).digest("hex")+"\n")'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Every conditional table names its owner column. Missing bindings, hash mismatches,
|
|
103
|
+
missing owner columns, null/unsupported owner values, and conditionals without an owner
|
|
104
|
+
binding are refused. The owner column must be marked `identity: "YES"` and use
|
|
105
|
+
`PSEUDONYMIZE`, preventing a conditional policy from retaining raw account IDs. Other
|
|
106
|
+
owners take the declared `otherwise` path; every other identity-bearing field still
|
|
107
|
+
needs its own pseudonymization declaration. Supply the binding only to the local
|
|
108
|
+
server-side Rehearsal process; do not put it in the policy, baseline, or tracked files.
|
|
109
|
+
|
|
110
|
+
## Structured JSON
|
|
111
|
+
|
|
112
|
+
`json-object` fields are required by default. Set `"required": false` on an explicitly
|
|
113
|
+
optional field. Unknown keys remain errors. `json-array` declares one item recipe and a
|
|
114
|
+
required `maximumItems`. Both recipes accept `maximumDepth`, `maximumBytes`, and
|
|
115
|
+
`allowNull`; defaults are 8 levels, 65,536 bytes, and no null container. Bounds are
|
|
116
|
+
checked before producing the transformed structure. Nested objects and arrays repeat
|
|
117
|
+
the same declarations, and every nested identity uses an explicit pseudonym recipe.
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"action": "DERIVE",
|
|
122
|
+
"recipe": {
|
|
123
|
+
"kind": "json-array",
|
|
124
|
+
"maximumItems": 25,
|
|
125
|
+
"maximumDepth": 4,
|
|
126
|
+
"maximumBytes": 8192,
|
|
127
|
+
"items": {
|
|
128
|
+
"action": "DERIVE",
|
|
129
|
+
"recipe": {
|
|
130
|
+
"kind": "json-object",
|
|
131
|
+
"fields": {
|
|
132
|
+
"actor_id": {
|
|
133
|
+
"action": "PSEUDONYMIZE",
|
|
134
|
+
"recipe": { "format": "uuid", "namespace": "account-id" }
|
|
135
|
+
},
|
|
136
|
+
"note": {
|
|
137
|
+
"action": "REPLACE",
|
|
138
|
+
"required": false,
|
|
139
|
+
"recipe": { "kind": "constant", "value": "Synthetic note" }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
46
147
|
|
|
47
148
|
The package still exports `validateSanitizationCoverage` and
|
|
48
149
|
`applySanitizationAction` for existing version 1 preparation tools:
|
|
@@ -28,6 +28,8 @@ name:
|
|
|
28
28
|
- whether rows are approved public data or belong to one explicitly approved
|
|
29
29
|
owner;
|
|
30
30
|
- privacy actions and bounded transformation recipes;
|
|
31
|
+
- hashed server-side owner bindings and explicit approved/other-owner branches;
|
|
32
|
+
- bounded nested JSON objects and arrays with required/optional fields;
|
|
31
33
|
- licensed or consented Storage buckets and prefixes;
|
|
32
34
|
- schema and extension prerequisites;
|
|
33
35
|
- local identities and reference locations;
|
package/docs/troubleshooting.md
CHANGED
|
@@ -20,6 +20,20 @@ npx rehearsal --help
|
|
|
20
20
|
|
|
21
21
|
Do not use `sudo` or require a global install.
|
|
22
22
|
|
|
23
|
+
## Installation did not create `rehearsal.config.mjs`
|
|
24
|
+
|
|
25
|
+
Rehearsal intentionally skips automatic creation when install scripts are disabled, the
|
|
26
|
+
install is global or temporary, or more than one workspace application could own the
|
|
27
|
+
config. From the intended application directory, run:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx rehearsal
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Choose **Set the stage** and approve the preview. For pnpm and Yarn, use this guided path;
|
|
34
|
+
automatic root generation is currently tested with npm. An existing supported config is
|
|
35
|
+
kept in place, including `infrastructure/rehearsal/rehearsal.config.mjs`.
|
|
36
|
+
|
|
23
37
|
## Setup says Node.js 24 is required
|
|
24
38
|
|
|
25
39
|
Rehearsal intentionally supports one maintained Node.js major in its first beta. Switch
|
|
@@ -132,9 +146,31 @@ changes.
|
|
|
132
146
|
|
|
133
147
|
## Local authentication fails
|
|
134
148
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
149
|
+
For Google or GitHub, confirm `supabase.authentication` is declared in
|
|
150
|
+
`rehearsal.config.mjs`, its ignored environment file exists with `chmod 600`, and the
|
|
151
|
+
provider has the printed local Auth callback registered. Rehearsal generates the
|
|
152
|
+
disposable Supabase provider block.
|
|
153
|
+
|
|
154
|
+
When migrating from the legacy service environment fields, replace those fields together
|
|
155
|
+
with `supabase.authentication` and rerun `rehearsal doctor`. A compatible Google or
|
|
156
|
+
GitHub provider section may remain in the tracked template: Rehearsal supersedes it only
|
|
157
|
+
inside the generated runtime copy. Doctor refuses unknown provider settings, duplicate
|
|
158
|
+
or nested provider sections, and nonce or optional-email settings that do not match the
|
|
159
|
+
new declaration. Keep both options false unless there is a reviewed provider-specific
|
|
160
|
+
need; skipping nonce validation reduces replay protection. Otherwise keep using the
|
|
161
|
+
documented legacy configuration.
|
|
162
|
+
Never paste provider secrets into tracked config or terminal output.
|
|
163
|
+
|
|
164
|
+
If `identity plan` refuses the matcher, read the message literally:
|
|
165
|
+
|
|
166
|
+
- **Provider or matcher type is unsupported:** use only the combinations documented in
|
|
167
|
+
[Runtime and identity policies](runtime-policies.md).
|
|
168
|
+
- **No verified identity matches:** complete an ordinary local sign-in, confirm the
|
|
169
|
+
correct environment value is present, then run the plan again.
|
|
170
|
+
- **More than one identity matches:** narrow the provider allowlist or use a hashed
|
|
171
|
+
provider-subject matcher. Rehearsal will not guess between accounts.
|
|
172
|
+
- **Provider subject conflicts:** the local Auth identity disagrees with itself; remove
|
|
173
|
+
and recreate that disposable local sign-in instead of forcing a claim.
|
|
138
174
|
|
|
139
175
|
## Runtime changes disappeared
|
|
140
176
|
|
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.12",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Safely rehearse PostgreSQL and Supabase migrations against sanitized, production-shaped data.",
|
|
6
6
|
"repository": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"bugs": {
|
|
11
11
|
"url": "https://github.com/Ddupasquier/rehearsal-db/issues"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://github.
|
|
13
|
+
"homepage": "https://ddupasquier.github.io/rehearsal-db/",
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public",
|
|
16
16
|
"tag": "beta",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"src",
|
|
43
|
+
"scripts/install/create_root_config.mjs",
|
|
43
44
|
"scripts/runtime/manage_database.mjs",
|
|
44
45
|
"docs/*.md",
|
|
45
46
|
"README.md",
|
|
@@ -51,6 +52,9 @@
|
|
|
51
52
|
"LICENSE"
|
|
52
53
|
],
|
|
53
54
|
"scripts": {
|
|
55
|
+
"postinstall": "node scripts/install/create_root_config.mjs",
|
|
56
|
+
"docs:build": "node scripts/docs/build.mjs",
|
|
57
|
+
"docs:preview": "node scripts/docs/serve.mjs",
|
|
54
58
|
"test": "vitest run",
|
|
55
59
|
"test:unit": "vitest run tests/unit",
|
|
56
60
|
"test:integration": "vitest run tests/integration tests/contracts",
|
|
@@ -58,8 +62,10 @@
|
|
|
58
62
|
"test:fixture": "node scripts/verification/fixtures/supabase.mjs",
|
|
59
63
|
"test:fixture:postgresql": "node scripts/verification/fixtures/postgresql.mjs",
|
|
60
64
|
"test:fixture:dependent": "node scripts/verification/fixtures/dependent.mjs",
|
|
65
|
+
"test:fixture:onboarding": "node scripts/verification/fixtures/onboarding.mjs",
|
|
61
66
|
"test:fixture:standalone": "node scripts/verification/fixtures/standalone.mjs",
|
|
62
|
-
"
|
|
67
|
+
"test:fixture:privacy": "node scripts/verification/fixtures/privacy.mjs",
|
|
68
|
+
"check": "npm run check:syntax && npm run test && npm run docs:build && npm run format:check && npm run package:audit",
|
|
63
69
|
"check:syntax": "find src scripts tests -type f -name '*.mjs' -exec node --check {} +",
|
|
64
70
|
"format": "prettier --write .",
|
|
65
71
|
"format:check": "prettier --check .",
|
|
@@ -78,7 +84,10 @@
|
|
|
78
84
|
"pg": "^8.23.1"
|
|
79
85
|
},
|
|
80
86
|
"devDependencies": {
|
|
87
|
+
"@fontsource-variable/dm-sans": "^5.3.0",
|
|
88
|
+
"@fontsource-variable/fraunces": "^5.3.0",
|
|
81
89
|
"@lydell/node-pty": "^1.2.0-beta.15",
|
|
90
|
+
"marked": "^18.0.14",
|
|
82
91
|
"prettier": "^3.9.6",
|
|
83
92
|
"vitest": "^5.0.2"
|
|
84
93
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Create safe project-root Rehearsal scaffolding during a local install. */
|
|
3
|
+
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createInstalledRehearsalScaffold } from "../../src/project/install_scaffold.mjs";
|
|
6
|
+
|
|
7
|
+
const packageRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const result = await createInstalledRehearsalScaffold({ packageRoot });
|
|
11
|
+
if (result.status === "ready") {
|
|
12
|
+
const changed = result.files.filter((file) => file.action !== "unchanged");
|
|
13
|
+
console.log(
|
|
14
|
+
[
|
|
15
|
+
"Rehearsal is ready in this project.",
|
|
16
|
+
...(changed.length
|
|
17
|
+
? [
|
|
18
|
+
...changed.map(
|
|
19
|
+
(file) =>
|
|
20
|
+
` ${file.action === "create" ? "+" : "~"} ${file.path}`,
|
|
21
|
+
),
|
|
22
|
+
]
|
|
23
|
+
: [" Existing Rehearsal configuration preserved."]),
|
|
24
|
+
"Next: review rehearsal.config.mjs, then run npx rehearsal.",
|
|
25
|
+
].join("\n"),
|
|
26
|
+
);
|
|
27
|
+
} else {
|
|
28
|
+
console.log(
|
|
29
|
+
`Rehearsal did not create project files: ${result.reason}.\nNext: run npx rehearsal from the application directory.`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.warn(
|
|
34
|
+
`Rehearsal could not safely create project files during installation: ${String(error?.message ?? error)}\nNext: run npx rehearsal from the application directory.`,
|
|
35
|
+
);
|
|
36
|
+
}
|