@rehearsal-db/core 0.1.0-beta.9 → 0.1.0-rc.2

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.
Files changed (247) hide show
  1. package/CHANGELOG.md +319 -1
  2. package/COMPATIBILITY.md +15 -0
  3. package/README.md +73 -16
  4. package/dist/package.json +100 -0
  5. package/dist/scripts/install/create_root_config.d.mts +3 -0
  6. package/dist/scripts/install/create_root_config.mjs +13 -0
  7. package/dist/scripts/runtime/manage_database.d.mts +6 -0
  8. package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
  9. package/dist/src/application/interactive_task.d.mts +30 -0
  10. package/dist/src/application/interactive_task.mjs +83 -0
  11. package/dist/src/application/session.d.mts +97 -0
  12. package/dist/src/application/session.mjs +380 -0
  13. package/dist/src/baseline/artifact.d.mts +119 -0
  14. package/dist/src/baseline/artifact.mjs +522 -0
  15. package/dist/src/baseline/builder.d.mts +19 -0
  16. package/dist/src/baseline/builder.mjs +142 -0
  17. package/dist/src/baseline/input_discovery.d.mts +13 -0
  18. package/dist/src/baseline/input_discovery.mjs +149 -0
  19. package/dist/src/baseline/policy_review.d.mts +56 -0
  20. package/dist/src/baseline/policy_review.mjs +73 -0
  21. package/dist/src/baseline/preparation.d.mts +63 -0
  22. package/dist/src/baseline/preparation.mjs +295 -0
  23. package/dist/src/baseline/privacy_contract.d.mts +147 -0
  24. package/dist/src/baseline/privacy_contract.mjs +2 -0
  25. package/dist/src/baseline/privacy_dependency_graph.d.mts +7 -0
  26. package/dist/src/baseline/privacy_dependency_graph.mjs +110 -0
  27. package/dist/src/baseline/privacy_engine.d.mts +5 -0
  28. package/dist/src/baseline/privacy_engine.mjs +4 -0
  29. package/dist/src/baseline/privacy_key.d.mts +7 -0
  30. package/dist/src/baseline/privacy_key.mjs +42 -0
  31. package/dist/src/baseline/privacy_pipeline.d.mts +7 -0
  32. package/dist/src/baseline/privacy_pipeline.mjs +127 -0
  33. package/dist/src/baseline/privacy_policy.d.mts +6 -0
  34. package/dist/src/baseline/privacy_policy.mjs +237 -0
  35. package/dist/src/baseline/privacy_recipe_validation.d.mts +5 -0
  36. package/dist/src/baseline/privacy_recipe_validation.mjs +289 -0
  37. package/dist/src/baseline/privacy_rule_registry.d.mts +18 -0
  38. package/dist/src/baseline/privacy_rule_registry.mjs +143 -0
  39. package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
  40. package/dist/src/baseline/privacy_structured_json.mjs +464 -0
  41. package/dist/src/baseline/privacy_transforms.d.mts +18 -0
  42. package/dist/src/baseline/privacy_transforms.mjs +142 -0
  43. package/dist/src/baseline/privacy_validation_shared.d.mts +31 -0
  44. package/dist/src/baseline/privacy_validation_shared.mjs +127 -0
  45. package/dist/src/baseline/sanitization_policy.d.mts +69 -0
  46. package/dist/src/baseline/sanitization_policy.mjs +227 -0
  47. package/dist/src/baseline/schema_snapshot.d.mts +19 -0
  48. package/dist/src/baseline/schema_snapshot.mjs +79 -0
  49. package/dist/src/cli/application_command_handlers.d.mts +8 -0
  50. package/dist/src/cli/application_command_handlers.mjs +52 -0
  51. package/dist/src/cli/arguments.d.mts +38 -0
  52. package/dist/src/cli/arguments.mjs +130 -0
  53. package/dist/src/cli/basic_command_handlers.d.mts +4 -0
  54. package/dist/src/cli/basic_command_handlers.mjs +57 -0
  55. package/dist/src/cli/cli_commands.d.mts +14 -0
  56. package/dist/src/cli/cli_commands.mjs +34 -0
  57. package/dist/src/cli/command_contract.d.mts +57 -0
  58. package/dist/src/cli/command_contract.mjs +39 -0
  59. package/dist/src/cli/command_output.d.mts +12 -0
  60. package/dist/src/cli/command_output.mjs +20 -0
  61. package/dist/src/cli/command_registry.d.mts +7 -0
  62. package/dist/src/cli/command_registry.mjs +14 -0
  63. package/dist/src/cli/guided.d.mts +53 -0
  64. package/dist/src/cli/guided.mjs +727 -0
  65. package/dist/src/cli/identity_command_handlers.d.mts +12 -0
  66. package/dist/src/cli/identity_command_handlers.mjs +239 -0
  67. package/dist/src/cli/identity_renderer.d.mts +8 -0
  68. package/dist/src/cli/identity_renderer.mjs +33 -0
  69. package/dist/src/cli/init_command.d.mts +21 -0
  70. package/dist/src/cli/init_command.mjs +90 -0
  71. package/dist/src/cli/inspection_command_handlers.d.mts +3 -0
  72. package/dist/src/cli/inspection_command_handlers.mjs +79 -0
  73. package/dist/src/cli/project_command_handlers.d.mts +11 -0
  74. package/dist/src/cli/project_command_handlers.mjs +92 -0
  75. package/dist/src/cli/project_commands.d.mts +283 -0
  76. package/dist/src/cli/project_commands.mjs +192 -0
  77. package/dist/src/cli/rehearsal.d.mts +7 -0
  78. package/dist/src/cli/rehearsal.mjs +190 -0
  79. package/dist/src/cli/renderers.d.mts +299 -0
  80. package/dist/src/cli/renderers.mjs +554 -0
  81. package/dist/src/cli/runtime_action_handlers.d.mts +12 -0
  82. package/dist/src/cli/runtime_action_handlers.mjs +201 -0
  83. package/dist/src/cli/runtime_commands.d.mts +294 -0
  84. package/dist/src/cli/runtime_commands.mjs +619 -0
  85. package/dist/src/cli/source_command_handlers.d.mts +8 -0
  86. package/dist/src/cli/source_command_handlers.mjs +62 -0
  87. package/dist/src/cli/source_commands.d.mts +436 -0
  88. package/dist/src/cli/source_commands.mjs +301 -0
  89. package/dist/src/cli/terminal.d.mts +59 -0
  90. package/dist/src/cli/terminal.mjs +230 -0
  91. package/dist/src/identity/claim.d.mts +5 -0
  92. package/dist/src/identity/claim.mjs +4 -0
  93. package/dist/src/identity/claim_contract.d.mts +135 -0
  94. package/dist/src/identity/claim_contract.mjs +6 -0
  95. package/dist/src/identity/claim_executor.d.mts +10 -0
  96. package/dist/src/identity/claim_executor.mjs +358 -0
  97. package/dist/src/identity/claim_identity_lookup.d.mts +23 -0
  98. package/dist/src/identity/claim_identity_lookup.mjs +134 -0
  99. package/dist/src/identity/claim_plan.d.mts +8 -0
  100. package/dist/src/identity/claim_plan.mjs +61 -0
  101. package/dist/src/identity/claim_policy.d.mts +3 -0
  102. package/dist/src/identity/claim_policy.mjs +377 -0
  103. package/dist/src/identity/claim_sql.d.mts +10 -0
  104. package/dist/src/identity/claim_sql.mjs +46 -0
  105. package/dist/src/identity/claim_verification.d.mts +13 -0
  106. package/dist/src/identity/claim_verification.mjs +35 -0
  107. package/dist/src/identity/connect.d.mts +30 -0
  108. package/dist/src/identity/connect.mjs +111 -0
  109. package/dist/src/identity/provider_configuration.d.mts +37 -0
  110. package/dist/src/identity/provider_configuration.mjs +132 -0
  111. package/dist/src/identity/storage.d.mts +21 -0
  112. package/dist/src/identity/storage.mjs +122 -0
  113. package/dist/src/index.d.mts +4 -0
  114. package/dist/src/index.mjs +3 -0
  115. package/dist/src/project/configuration.d.mts +13 -0
  116. package/dist/src/project/configuration.mjs +9 -0
  117. package/dist/src/project/configuration_contract.d.mts +125 -0
  118. package/dist/src/project/configuration_contract.mjs +27 -0
  119. package/dist/src/project/configuration_discovery.d.mts +7 -0
  120. package/dist/src/project/configuration_discovery.mjs +37 -0
  121. package/dist/src/project/configuration_normalization.d.mts +107 -0
  122. package/dist/src/project/configuration_normalization.mjs +302 -0
  123. package/dist/src/project/configuration_rendering.d.mts +14 -0
  124. package/dist/src/project/configuration_rendering.mjs +228 -0
  125. package/dist/src/project/configuration_resolution.d.mts +125 -0
  126. package/dist/src/project/configuration_resolution.mjs +81 -0
  127. package/dist/src/project/configuration_schema.d.mts +22 -0
  128. package/dist/src/project/configuration_schema.mjs +160 -0
  129. package/dist/src/project/configuration_validation.d.mts +22 -0
  130. package/dist/src/project/configuration_validation.mjs +136 -0
  131. package/dist/src/project/install_scaffold.d.mts +42 -0
  132. package/dist/src/project/install_scaffold.mjs +263 -0
  133. package/dist/src/project/project_detection.d.mts +14 -0
  134. package/dist/src/project/project_detection.mjs +47 -0
  135. package/dist/src/project/setup.d.mts +87 -0
  136. package/dist/src/project/setup.mjs +414 -0
  137. package/dist/src/project/support_report.d.mts +88 -0
  138. package/dist/src/project/support_report.mjs +83 -0
  139. package/dist/src/runtime/cleanup.d.mts +85 -0
  140. package/dist/src/runtime/cleanup.mjs +311 -0
  141. package/dist/src/runtime/lifecycle_engine.d.mts +63 -0
  142. package/dist/src/runtime/lifecycle_engine.mjs +114 -0
  143. package/dist/src/runtime/migration_history.d.mts +51 -0
  144. package/dist/src/runtime/migration_history.mjs +170 -0
  145. package/dist/src/runtime/plan.d.mts +134 -0
  146. package/dist/src/runtime/plan.mjs +640 -0
  147. package/dist/src/runtime/policy.d.mts +66 -0
  148. package/dist/src/runtime/policy.mjs +369 -0
  149. package/dist/src/runtime/restore.d.mts +22 -0
  150. package/dist/src/runtime/restore.mjs +276 -0
  151. package/dist/src/runtime/service_environment.d.mts +11 -0
  152. package/dist/src/runtime/service_environment.mjs +58 -0
  153. package/dist/src/runtime/topology.d.mts +51 -0
  154. package/dist/src/runtime/topology.mjs +128 -0
  155. package/dist/src/shared/diagnostics.d.mts +97 -0
  156. package/dist/src/shared/diagnostics.mjs +170 -0
  157. package/dist/src/shared/human_output.d.mts +2 -0
  158. package/{src → dist/src}/shared/human_output.mjs +1 -3
  159. package/dist/src/shared/node_runtime.d.mts +14 -0
  160. package/dist/src/shared/node_runtime.mjs +25 -0
  161. package/dist/src/shared/operation_guard.d.mts +22 -0
  162. package/dist/src/shared/operation_guard.mjs +150 -0
  163. package/dist/src/shared/process_environment.d.mts +15 -0
  164. package/dist/src/shared/process_environment.mjs +50 -0
  165. package/dist/src/source/access.d.mts +165 -0
  166. package/dist/src/source/access.mjs +602 -0
  167. package/dist/src/source/asset_transfer.d.mts +27 -0
  168. package/dist/src/source/asset_transfer.mjs +202 -0
  169. package/dist/src/source/baseline.d.mts +66 -0
  170. package/dist/src/source/baseline.mjs +372 -0
  171. package/dist/src/source/external_reader.d.mts +42 -0
  172. package/dist/src/source/external_reader.mjs +278 -0
  173. package/dist/src/source/postgresql_access.d.mts +86 -0
  174. package/dist/src/source/postgresql_access.mjs +454 -0
  175. package/dist/src/source/privacy_paths.d.mts +10 -0
  176. package/dist/src/source/privacy_paths.mjs +27 -0
  177. package/dist/src/targets/driver.d.mts +2 -0
  178. package/dist/src/targets/driver.mjs +13 -0
  179. package/dist/src/targets/postgresql.d.mts +5 -0
  180. package/dist/src/targets/postgresql.mjs +620 -0
  181. package/dist/src/targets/supabase.d.mts +8 -0
  182. package/dist/src/targets/supabase.mjs +629 -0
  183. package/dist/src/targets/supabase_environment.d.mts +54 -0
  184. package/dist/src/targets/supabase_environment.mjs +184 -0
  185. package/dist/src/targets/target.d.mts +33 -0
  186. package/dist/src/targets/target.mjs +39 -0
  187. package/docs/README.md +35 -16
  188. package/docs/architecture.md +135 -10
  189. package/docs/commands.md +53 -15
  190. package/docs/configuration.md +186 -31
  191. package/docs/getting-started.md +66 -9
  192. package/docs/index.md +67 -0
  193. package/docs/production-source.md +175 -17
  194. package/docs/releasing.md +24 -12
  195. package/docs/roadmap.md +3 -2
  196. package/docs/runtime-lifecycle.md +99 -0
  197. package/docs/runtime-policies.md +80 -7
  198. package/docs/sanitization.md +572 -4
  199. package/docs/security-model.md +8 -0
  200. package/docs/stability.md +70 -0
  201. package/docs/standalone-workflow.md +36 -13
  202. package/docs/troubleshooting.md +82 -10
  203. package/docs/tutorial.md +7 -1
  204. package/docs/typescript.md +134 -0
  205. package/package.json +44 -29
  206. package/types/configuration.d.mts +5 -0
  207. package/src/README.md +0 -17
  208. package/src/application/session.mjs +0 -313
  209. package/src/baseline/artifact.mjs +0 -661
  210. package/src/baseline/builder.mjs +0 -164
  211. package/src/baseline/input_discovery.mjs +0 -155
  212. package/src/baseline/policy_review.mjs +0 -92
  213. package/src/baseline/preparation.mjs +0 -323
  214. package/src/baseline/privacy_engine.mjs +0 -413
  215. package/src/baseline/sanitization_policy.mjs +0 -299
  216. package/src/baseline/schema_snapshot.mjs +0 -113
  217. package/src/cli/arguments.mjs +0 -120
  218. package/src/cli/guided.mjs +0 -807
  219. package/src/cli/rehearsal.mjs +0 -933
  220. package/src/cli/renderers.mjs +0 -584
  221. package/src/cli/runtime_commands.mjs +0 -553
  222. package/src/cli/source_commands.mjs +0 -326
  223. package/src/cli/terminal.mjs +0 -275
  224. package/src/identity/claim.mjs +0 -975
  225. package/src/identity/storage.mjs +0 -165
  226. package/src/project/configuration.d.mts +0 -153
  227. package/src/project/configuration.mjs +0 -1197
  228. package/src/project/setup.mjs +0 -379
  229. package/src/project/support_report.mjs +0 -105
  230. package/src/runtime/cleanup.mjs +0 -381
  231. package/src/runtime/migration_history.mjs +0 -220
  232. package/src/runtime/plan.mjs +0 -685
  233. package/src/runtime/policy.mjs +0 -438
  234. package/src/runtime/restore.mjs +0 -329
  235. package/src/runtime/service_environment.mjs +0 -82
  236. package/src/runtime/topology.mjs +0 -177
  237. package/src/shared/diagnostics.mjs +0 -199
  238. package/src/shared/operation_guard.mjs +0 -162
  239. package/src/shared/process_environment.mjs +0 -68
  240. package/src/source/access.mjs +0 -578
  241. package/src/source/asset_transfer.mjs +0 -177
  242. package/src/source/baseline.mjs +0 -446
  243. package/src/source/postgresql_access.mjs +0 -480
  244. package/src/targets/postgresql.mjs +0 -808
  245. package/src/targets/supabase.mjs +0 -830
  246. package/src/targets/supabase_environment.mjs +0 -229
  247. package/src/targets/target.mjs +0 -66
@@ -23,6 +23,15 @@ The privacy key stays ignored under `.rehearsal`. The policy is safe to review i
23
23
  control because it contains hashes and environment-variable names, not credentials,
24
24
  URLs, or raw owner identifiers.
25
25
 
26
+ `credentialFile` must be inside the selected target's
27
+ `baseline.artifactDirectory`, under its `secrets` folder. With the default artifact
28
+ directory, use `.rehearsal/secrets/source-reader.env`. For a target whose artifact
29
+ directory is `targets/primary/.rehearsal`, use
30
+ `targets/primary/.rehearsal/secrets/source-reader.env` and place `privacyKey` there as
31
+ well. Rehearsal stores that target's access receipt beside its baseline at
32
+ `targets/primary/.rehearsal/source-access-receipt.json`; separate targets never share
33
+ the receipt or credential.
34
+
26
35
  ## 2. Declare the source boundary
27
36
 
28
37
  `source-access-policy.json` uses this shape:
@@ -33,6 +42,7 @@ URLs, or raw owner identifiers.
33
42
  "targetFingerprint": "<sha256 of the approved PostgreSQL host, port, and database>",
34
43
  "administratorEnvironmentVariable": "REHEARSAL_SOURCE_ADMIN_URL",
35
44
  "reader": {
45
+ "mode": "managed",
36
46
  "role": "rehearsal_example_reader",
37
47
  "ownerRole": "rehearsal_example_owner",
38
48
  "credentialFile": ".rehearsal/secrets/source-reader.env",
@@ -61,7 +71,7 @@ URLs, or raw owner identifiers.
61
71
  "view": "approved_owner_profile",
62
72
  "targetSchema": "app_api",
63
73
  "targetTable": "profiles",
64
- "columns": ["id", "email"],
74
+ "columns": ["id", "email", "avatar_path"],
65
75
  "orderBy": ["id"],
66
76
  "rowScope": {
67
77
  "kind": "approved-owner",
@@ -70,10 +80,28 @@ URLs, or raw owner identifiers.
70
80
  }
71
81
  }
72
82
  ],
73
- "assets": []
83
+ "assetReader": {
84
+ "endpointFingerprint": "<sha256 of the approved Storage endpoint>",
85
+ "baseUrlEnvironmentVariable": "REHEARSAL_SOURCE_STORAGE_URL",
86
+ "tokenEnvironmentVariable": "REHEARSAL_SOURCE_STORAGE_TOKEN",
87
+ "maximumObjects": 1000,
88
+ "maximumObjectBytes": 52428800,
89
+ "maximumTotalBytes": 2147483648
90
+ },
91
+ "assets": [
92
+ {
93
+ "bucket": "avatars",
94
+ "prefixEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_ID",
95
+ "rights": "approved-owner",
96
+ "pathMapping": "owner-storage"
97
+ }
98
+ ]
74
99
  }
75
100
  ```
76
101
 
102
+ `managed` is the default for existing policies, but writing it explicitly makes the
103
+ credential boundary easier to review.
104
+
77
105
  Calculate fingerprints with the exported helpers from `@rehearsal-db/core/source-access`
78
106
  or a reviewed local script. A fingerprint binds the destination without recording its
79
107
  address. Changing the host, port, or database invalidates the plan.
@@ -82,13 +110,92 @@ address. Changing the host, port, or database invalidates the plan.
82
110
  use. `approved-owner` requires one explicit owner value from the named environment
83
111
  variable. Rehearsal never chooses an owner by finding an administrator.
84
112
 
113
+ ### Use a provider-managed reader
114
+
115
+ If a database owner already supplies the restricted export views and reader, use
116
+ `mode: "external"`. Omit `administratorEnvironmentVariable`; Rehearsal will not request
117
+ an administrator credential or change the source:
118
+
119
+ ```json
120
+ {
121
+ "reader": {
122
+ "mode": "external",
123
+ "role": "provided_rehearsal_reader",
124
+ "allowedMemberships": ["provided_rehearsal_readers"],
125
+ "connectionEnvironmentVariable": "REHEARSAL_SOURCE_READER_URL",
126
+ "credentialFile": ".rehearsal/secrets/source-reader.env",
127
+ "maximumValidForMinutes": 30
128
+ },
129
+ "exportSchema": "provided_rehearsal_export",
130
+ "relations": [
131
+ {
132
+ "source": { "schema": "private", "table": "profiles" },
133
+ "view": "approved_owner_profile",
134
+ "targetSchema": "app_api",
135
+ "targetTable": "profiles",
136
+ "columns": ["id", "email", "avatar_path"],
137
+ "orderBy": ["id"],
138
+ "rowScope": {
139
+ "kind": "approved-owner",
140
+ "column": "id",
141
+ "valueEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_ID"
142
+ },
143
+ "viewDefinitionSha256": "<sha256 from externalViewDefinitionFingerprint>"
144
+ }
145
+ ]
146
+ }
147
+ ```
148
+
149
+ The database owner must create a `security_barrier` view owned by a role other than the
150
+ reader. Record PostgreSQL's exact canonical definition, not handwritten SQL:
151
+
152
+ ```js
153
+ import { externalViewDefinitionFingerprint } from "@rehearsal-db/core/source-access";
154
+
155
+ console.log(externalViewDefinitionFingerprint(canonicalViewDefinition));
156
+ ```
157
+
158
+ Obtain `canonicalViewDefinition` with
159
+ `select pg_get_viewdef('provided_rehearsal_export.approved_owner_profile'::regclass, false)`.
160
+ Rehearsal verifies that fingerprint before saving any local access receipt.
161
+ The resulting receipt also binds the normalized source-policy fingerprint. Editing the
162
+ view hash, columns, scopes, or reader declaration requires a new verification.
163
+
164
+ External mode also requires all non-system readable columns to match only the declared
165
+ views and migration-ledger columns. It refuses database, schema, relation, or column
166
+ writes; privileged role attributes; executable non-system security-definer functions;
167
+ a reader-owned view; or a database-enforced expiration beyond
168
+ `maximumValidForMinutes`.
169
+
170
+ This audit is intentionally conservative: an object-level table, column, sequence, or
171
+ function grant remains disallowed even when its schema currently lacks `USAGE`. Do not
172
+ use schema visibility to hide stale grants. Revoke the object grant or move the approved
173
+ surface behind the reviewed export views; this also avoids relying on behavior of an
174
+ already-open or prepared database session.
175
+
176
+ An external login may inherit its narrow permissions from provider-owned group roles.
177
+ List every direct group in `allowedMemberships`; omit the field or use `[]` when the
178
+ login has direct grants only. Rehearsal requires the observed membership set to match
179
+ that list exactly. Every allowed group must be non-login and non-privileged, grant
180
+ inheritance to the login, disable both `SET ROLE` and membership delegation, inherit no
181
+ other role, and own none of the export views. PostgreSQL versions that cannot prove the
182
+ per-membership role-switch setting are refused. Rehearsal still evaluates the login's
183
+ complete effective read, write, sequence, and executable-function surface after those
184
+ checks. Adding, removing, or changing a group changes the reviewed plan digest.
185
+
85
186
  `targetSchema` is optional and defaults to `public`. Set it when the restored relation
86
187
  belongs elsewhere, and use the same schema in privacy policy version 2. This is relation
87
188
  identity, not a request to search every schema.
88
189
 
89
- For Storage, add an `assetReader` block and approved `assets` entries. The reader URL and
90
- token come from named environment variables. Objects outside the exact bucket/prefix are
91
- not listed or downloaded. Size, object-count, path, ETag, and exact-byte checks apply.
190
+ For Storage, the reader URL and token come from named environment variables. A public,
191
+ non-identity prefix may use `prefix`. A private owner prefix uses
192
+ `prefixEnvironmentVariable`, `rights: "approved-owner"`, and a `pathMapping` declared in
193
+ the privacy policy below. Its raw value stays out of tracked files and the review plan
194
+ records only its SHA-256. Objects outside the exact bucket/prefix are not listed or
195
+ downloaded. Size, object-count, path, ETag, and exact-byte checks apply.
196
+ Supabase folder entries are traversed recursively beneath that exact prefix. Folder
197
+ entries count toward `maximumObjects`, so the same reviewed limit bounds both the
198
+ directory walk and transferred files. Empty files remain files and are copied normally.
92
199
 
93
200
  ## 3. Use executable privacy policy version 2
94
201
 
@@ -98,6 +205,21 @@ Every selected column has one action and, where required, a bounded recipe:
98
205
  {
99
206
  "policyVersion": 2,
100
207
  "migrationCutoff": "20260101000000",
208
+ "bindings": {
209
+ "approved-owner": {
210
+ "environmentVariable": "REHEARSAL_APPROVED_OWNER_ID",
211
+ "approvedValueSha256": "<sha256 of the exact approved owner ID>"
212
+ }
213
+ },
214
+ "pathMappings": {
215
+ "owner-storage": {
216
+ "binding": "approved-owner",
217
+ "format": "uuid",
218
+ "namespace": "account-id",
219
+ "maximumBytes": 512,
220
+ "maximumSegments": 8
221
+ }
222
+ },
101
223
  "tables": [
102
224
  {
103
225
  "schema": "app_api",
@@ -119,6 +241,14 @@ Every selected column has one action and, where required, a bounded recipe:
119
241
  "generated": "NEVER",
120
242
  "identity": "NO",
121
243
  "foreignKey": null
244
+ },
245
+ {
246
+ "name": "avatar_path",
247
+ "action": "DERIVE",
248
+ "recipe": { "kind": "path-map", "mapping": "owner-storage" },
249
+ "generated": "NEVER",
250
+ "identity": "NO",
251
+ "foreignKey": null
122
252
  }
123
253
  ]
124
254
  }
@@ -126,10 +256,13 @@ Every selected column has one action and, where required, a bounded recipe:
126
256
  }
127
257
  ```
128
258
 
129
- Supported recipes are intentionally small: keyed UUID/email/text/integer pseudonyms,
130
- explicit constants, bounded date shifts, and fully classified JSON objects. Unknown
131
- tables, columns, JSON keys, formats, and recipes stop the refresh. There is no JavaScript
132
- or SQL escape hatch.
259
+ Supported recipes are intentionally small: keyed UUID/email/text/integer/hex/GTIN/URL
260
+ pseudonyms, hexadecimal digests of explicitly named sanitized columns, explicit
261
+ constants, bounded or grouped date shifts, identity-aware path mappings, bounded
262
+ reviewed-identity substitution inside retained text, and fully classified JSON
263
+ structures. Unknown tables, columns, digest inputs, JSON keys, formats, and recipes stop
264
+ the refresh. There is no JavaScript or SQL escape hatch. See
265
+ [Sanitization policy](sanitization.md) for complete path and date examples.
133
266
 
134
267
  ## 4. Preview, apply, refresh, and retire
135
268
 
@@ -146,10 +279,12 @@ npx rehearsal source retire
146
279
  npx rehearsal source retire --confirm-source-retirement=<full-sha256>
147
280
  ```
148
281
 
149
- `source apply` creates new time-limited roles and export views only when every exact name
150
- is unused. It verifies the reader cannot read raw tables, write, escalate roles, or call
151
- an exposed security-definer network function. Externally provisioned readers are not yet
152
- supported; use the managed reader so Rehearsal can prove its scope and retirement.
282
+ In managed mode, `source apply` creates new time-limited roles and export views only when
283
+ every exact name is unused. It verifies the reader cannot read raw tables, write,
284
+ escalate roles, or call an exposed security-definer network function. In external mode,
285
+ the same command performs only the read-only checks above and stores the already supplied
286
+ credential in the ignored owner-only local file. It never creates, grants, alters, or
287
+ drops a source object.
153
288
 
154
289
  `refresh` previews the complete replacement first. After exact confirmation, its baseline
155
290
  step uses one read-only repeatable-read transaction for the schema shape,
@@ -160,13 +295,36 @@ runtime is rolled back to the previous baseline and runtime. The lower-level `ba
160
295
  refresh` command remains available when automation should activate a baseline without
161
296
  resetting the runtime or pruning old generations.
162
297
 
298
+ Rows committed after the repeatable-read transaction begins belong to the next refresh;
299
+ they cannot leak into the current snapshot midway through streaming. A schema or ledger
300
+ change during preparation stops activation. Malformed batches, network loss, interruption,
301
+ limit overruns, and low disk also leave the previous baseline active. Because `baseline
302
+ refresh` never resets the runtime, local runtime edits remain in place whether that
303
+ baseline-only preparation succeeds or fails.
304
+
305
+ Privacy `identity` metadata describes PostgreSQL `GENERATED ... AS IDENTITY`, not a
306
+ person or account identifier. UUID account keys normally use `identity: "NO"` while
307
+ remaining explicitly pseudonymized. During restore, Rehearsal consults the actual local
308
+ table for a backing sequence before resetting it; UUID columns never receive numeric
309
+ `max` or `setval` operations.
310
+
163
311
  Storage cannot share PostgreSQL's transaction snapshot. Rehearsal instead verifies each
164
312
  object's inventory version and byte count during transfer, then records its SHA-256 in
165
- the immutable baseline.
313
+ the immutable baseline. For an identity-aware path, the database reference and physical
314
+ destination use the same package-owned pseudonym; the source suffix is preserved.
315
+
316
+ In managed mode, `source retire` inventories the exact recorded views before removal,
317
+ then removes only those views, the two recorded roles, and local credential/receipt
318
+ files. In external mode, it removes only Rehearsal's local credential and receipt; the
319
+ provider-owned role and views remain untouched. The database-enforced expiration still
320
+ limits access, and the provider may revoke it sooner. Rehearsal does not claim that local
321
+ cleanup revoked a provider-owned credential. Unrelated roles, grants, objects, baselines,
322
+ runtimes, and Docker resources are preserved.
166
323
 
167
- `source retire` inventories the exact recorded views before removal, then removes only
168
- those views, the two recorded roles, and local credential/receipt files. Unrelated roles,
169
- grants, objects, baselines, runtimes, and Docker resources are preserved.
324
+ Apply, refresh, and retire always resolve those local files from the active config's
325
+ `baseline.artifactDirectory`. If you switch configs or target roots, Rehearsal refuses
326
+ to retire a receipt from the other target. It also refuses an existing receipt or
327
+ credential rather than overwriting or adopting it.
170
328
 
171
329
  ## Limits
172
330
 
package/docs/releasing.md CHANGED
@@ -31,10 +31,13 @@ publication must change and prove the workflow before narrowing that permission.
31
31
 
32
32
  1. Start from protected `main` on a dedicated ticketed release branch.
33
33
  2. Verify the exact version, changelog, compatibility notes, and packed file list.
34
- 3. Run unit and contract tests, formatting, package-content and secret audits,
35
- dependency audit, clean tarball installation, and the installed Docker fixture.
36
- 4. Have a developer unfamiliar with the implementing project follow the clean-project
37
- onboarding. Correct and retest the first confusing, missing, or wrong instruction.
34
+ 3. Run `npm run release:verify`. It performs the package checks and every
35
+ installed-consumer scenario against one tarball, then records the exact package
36
+ version, SHA-256, environment, commands, timings, and results in
37
+ `test-results/release-gate/`. Run the dependency audit separately.
38
+ 4. When an unfamiliar developer is available, have them follow clean-project onboarding.
39
+ Correct and retest the first confusing, missing, or wrong instruction. Record when
40
+ this human observation was unavailable; automated checks must not impersonate it.
38
41
  5. Change `private` to `false` only in the reviewed release change.
39
42
  6. Record the exact tarball filename, SHA-1, SHA-256, allowlisted files, unpacked size,
40
43
  executable, declared runtime dependency inventory, and dependency audit result.
@@ -51,16 +54,25 @@ publication must change and prove the workflow before narrowing that permission.
51
54
  11. Replace consuming projects' temporary Git/archive references only on their own
52
55
  protected integration branches and rerun their complete verification.
53
56
 
54
- The workflow publishes prereleases under the `beta` dist-tag and refuses a stable
55
- version. While Rehearsal has no stable release, the workflow also moves `latest` to the
56
- same reviewed beta. This keeps the npm package page and the ordinary
57
- `npm install @rehearsal-db/core` command current. When Rehearsal gains a stable release,
58
- `latest` must switch to the stable line while `beta` continues to identify prereleases.
57
+ The independent Rehearsal Test Lab is the primary pre-release consumer environment.
58
+ Real product integrations provide additional compatibility evidence; they do not
59
+ replace the package-owned release gate or Test Lab.
60
+
61
+ The workflow publishes beta and release-candidate versions under the `beta` dist-tag.
62
+ While Rehearsal has no stable release, it also moves `latest` to the same reviewed
63
+ prerelease. This keeps the npm package page and the ordinary
64
+ `npm install @rehearsal-db/core` command current. A stable version publishes under
65
+ `latest` without changing `beta`; after that point, later prereleases update only `beta`
66
+ and cannot replace the stable `latest` line. The GitHub prerelease setting must agree
67
+ with the package version or preparation fails.
68
+
69
+ The objective promotion checklist and compatibility boundary live in
70
+ [Stability and the first stable release](stability.md).
59
71
 
60
72
  The trusted publisher allows `npm dist-tag` only so this workflow can maintain those two
61
- tags without a stored npm token. A manual workflow run from `main` can repair the tags
62
- for the exact prerelease version currently recorded in `package.json`; it cannot publish
63
- a package or select a different version. The protected `npm` environment still supplies
73
+ tags without a stored npm token. A manual workflow run from `main` can repair the correct
74
+ channel for the exact version currently recorded in `package.json`; it cannot publish a
75
+ package or select a different version. The protected `npm` environment still supplies
64
76
  the human approval gate.
65
77
 
66
78
  Creating the repository, passing CI, extracting the engine, merging a release branch,
package/docs/roadmap.md CHANGED
@@ -29,8 +29,9 @@ Track the full breakdown in
29
29
  invalid migrations, identities, images, persistence, reset, and cleanup.
30
30
  - Run the released package in the real reporting consumer and record positives and
31
31
  negatives.
32
- - Directly observe any claimed Google account-picker/callback and unfamiliar-developer
33
- onboarding behavior; automation cannot invent that evidence.
32
+ - Directly observe any claimed Google account-picker/callback. Record
33
+ unfamiliar-developer onboarding behavior when a tester is available; automation cannot
34
+ invent either kind of human evidence.
34
35
  - Remove consumer integration code only after its replacement passes and that deletion
35
36
  is separately approved.
36
37
 
@@ -0,0 +1,99 @@
1
+ # Container runtime lifecycle
2
+
3
+ Rehearsal uses a local Docker-compatible engine for disposable PostgreSQL and Supabase
4
+ targets. It owns the targets it creates; it does not own the shared engine or another
5
+ project's containers.
6
+
7
+ ## What starts Docker?
8
+
9
+ Read-only commands such as `doctor`, `status`, `candidates`, `explain`, and `inspect` do
10
+ not start Docker. Commands that create or resume a database—such as `start`, `reset`,
11
+ `run`, and `open`—need an already-running engine.
12
+
13
+ Generated configurations keep startup explicit:
14
+
15
+ ```js
16
+ containerRuntime: {
17
+ autoStartColima: false,
18
+ },
19
+ ```
20
+
21
+ With the default, a stopped engine produces an actionable error. Start Docker Desktop,
22
+ Colima, Rancher Desktop, Podman with Docker compatibility, or another supported engine,
23
+ then repeat the command.
24
+
25
+ Setting `autoStartColima: true` is an explicit convenience opt-in. If Docker is
26
+ unavailable and the `colima` command exists, Rehearsal runs plain `colima start` and
27
+ reports it. Rehearsal does not resize Colima and does not stop the shared engine
28
+ automatically. Starting Docker can also wake unrelated containers that use Docker's
29
+ `always` or `unless-stopped` restart policy.
30
+
31
+ ## What remains after a command?
32
+
33
+ - `run` stops the application proof process but keeps the database target available for
34
+ hands-on testing.
35
+ - `open` keeps the application and databases available until the application session is
36
+ interrupted. The database target remains available afterward.
37
+ - `stop` stops every configured Rehearsal target in reverse dependency order and retains
38
+ its local data.
39
+ - `start` resumes a stopped target, reusing compatible state.
40
+ - `reset` recreates the target from the active immutable baseline.
41
+ - `discard` removes the selected disposable target. It is the data-destructive runtime
42
+ operation and retains the immutable baseline.
43
+
44
+ After Supabase starts, Rehearsal changes only containers with the exact configured
45
+ Supabase project label to Docker's `no` restart policy. This does not remove containers
46
+ or volumes. It prevents a later Docker restart from silently waking stopped Rehearsal
47
+ stacks; `rehearsal start` still resumes them normally.
48
+
49
+ ## A low-memory daily workflow
50
+
51
+ 1. Start your Docker-compatible engine.
52
+ 2. Run `rehearsal run` or `rehearsal open`.
53
+ 3. When finished, run `rehearsal stop` from the project root.
54
+ 4. Check other projects before stopping the shared engine:
55
+
56
+ ```bash
57
+ docker ps
58
+ ```
59
+
60
+ 5. If nothing else needs Docker, stop the engine using its own command, such as
61
+ `colima stop`.
62
+
63
+ Rehearsal intentionally does not infer that every running container belongs to it. It
64
+ also never runs a global Docker prune. Database and Storage volumes are not deleted by
65
+ `stop`.
66
+
67
+ ## Choosing PostgreSQL or Supabase
68
+
69
+ Use the PostgreSQL target for migration, schema, trigger, privacy, and ordinary relational
70
+ data checks that do not require Supabase services. It uses one database container.
71
+
72
+ Use the Supabase target when the proof needs Auth, Storage, PostgREST, or the local API
73
+ gateway. Rehearsal already excludes Realtime, Edge Runtime, Analytics/Logflare, and Vector
74
+ from its Supabase stack. A future service-profile feature will make additional optional
75
+ services explicit; until then, do not trade away application fidelity merely to reduce a
76
+ container count.
77
+
78
+ ## Diagnosing unexpected activity
79
+
80
+ Start with read-only checks:
81
+
82
+ ```bash
83
+ docker context show
84
+ docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Label "com.supabase.cli.project"}}'
85
+ docker stats --no-stream
86
+ colima status
87
+ ```
88
+
89
+ For each container, inspect its project ownership and restart policy before taking any
90
+ action:
91
+
92
+ ```bash
93
+ docker inspect --format '{{json .Config.Labels}} {{.HostConfig.RestartPolicy.Name}}' CONTAINER
94
+ ```
95
+
96
+ Do not remove anonymous volumes or old images based on names alone. Use `rehearsal
97
+ cleanup` for package-owned baseline retention and its reviewed, opt-in image cleanup.
98
+ Shared resource inventory and stale-environment cleanup are tracked separately; until
99
+ that lands, stopping a configured target from its own project is the safe path.
@@ -77,9 +77,12 @@ graph without an application callback patch:
77
77
  "identities": [
78
78
  {
79
79
  "name": "approved-owner",
80
- "provider": "google",
81
- "emailEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_EMAIL",
82
- "approvedEmailSha256": "<sha256 of the lowercase approved email>",
80
+ "matcher": {
81
+ "type": "verified-email",
82
+ "providers": ["google"],
83
+ "emailEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_EMAIL",
84
+ "approvedEmailSha256": "<sha256 of the lowercase approved email>"
85
+ },
83
86
  "placeholderUserId": "11111111-1111-4111-8111-111111111111",
84
87
  "references": [
85
88
  {
@@ -152,8 +155,29 @@ graph without an application callback patch:
152
155
  }
153
156
  ```
154
157
 
155
- The email stays in an environment variable; only its reviewed hash is committed. After
156
- ordinary local sign-in creates the account, preview and confirm the association:
158
+ Authentication has three separate parts:
159
+
160
+ 1. `config.supabase.authentication` can enable a Google or GitHub connection in local
161
+ Supabase and reads its credentials from the ignored owner-only environment file.
162
+ Existing projects may still own that local provider TOML manually. Email sign-in does
163
+ not use an OAuth credential declaration.
164
+ 2. The application completes the ordinary sign-in and receives a normal Supabase
165
+ session. Rehearsal does not intercept the callback.
166
+ 3. The identity policy matches that verified local identity and transfers only the
167
+ reviewed copied-account graph.
168
+
169
+ The email stays in an environment variable; only its reviewed hash is committed. The
170
+ guided command keeps the app available for ordinary sign-in, detects the reviewed local
171
+ Auth match, presents a redacted preview, and supplies the exact plan fingerprint
172
+ internally after one confirmation:
173
+
174
+ ```bash
175
+ npx rehearsal identity connect
176
+ ```
177
+
178
+ It never intercepts provider credentials or reads browser state. After connection it
179
+ reopens the app; sign out and sign in once to receive fresh claims. Scripts may retain
180
+ the explicit plan and claim sequence:
157
181
 
158
182
  ```bash
159
183
  npx rehearsal identity plan --identity=approved-owner
@@ -165,6 +189,50 @@ Rehearsal requires exactly one verified matching local provider identity. Every
165
189
  `required` reference must exist after transfer. Declare application role tables as
166
190
  references; `claims` alone is not a replacement for a database-backed token hook.
167
191
 
192
+ ### Supported identity matchers
193
+
194
+ Rehearsal currently supports the Supabase provider values `email`, `google`, and
195
+ `github`. Other provider names fail closed until their Auth representation has package
196
+ coverage. Two matcher types are supported:
197
+
198
+ - `verified-email` accepts one or more supported providers. It lowercases the email from
199
+ the named environment variable and compares its SHA-256 receipt before querying Auth.
200
+ - `provider-subject` accepts one OAuth provider (`google` or `github`). It compares the
201
+ reviewed SHA-256 to Supabase's stable provider ID/subject without placing the raw
202
+ subject in the policy, baseline, command output, SQL parameters, or receipt.
203
+
204
+ Google by verified email:
205
+
206
+ ```json
207
+ "matcher": {
208
+ "type": "verified-email",
209
+ "providers": ["google"],
210
+ "emailEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_EMAIL",
211
+ "approvedEmailSha256": "<sha256 of the lowercase email>"
212
+ }
213
+ ```
214
+
215
+ Supabase email/password or magic-link sign-in uses the same matcher with
216
+ `"providers": ["email"]`. An account intentionally allowed to use either ordinary
217
+ email or Google can declare `"providers": ["email", "google"]`; the claim still refuses
218
+ zero or multiple matching identities.
219
+
220
+ GitHub by stable provider subject:
221
+
222
+ ```json
223
+ "matcher": {
224
+ "type": "provider-subject",
225
+ "provider": "github",
226
+ "subjectEnvironmentVariable": "REHEARSAL_APPROVED_OWNER_SUBJECT",
227
+ "approvedSubjectSha256": "<sha256 of the exact GitHub subject>"
228
+ }
229
+ ```
230
+
231
+ Set the raw value only in the local shell or an ignored environment file. Existing
232
+ policies using the older top-level `provider`, `emailEnvironmentVariable`, and
233
+ `approvedEmailSha256` fields remain accepted as a single-provider verified-email
234
+ matcher. New policies should use the explicit `matcher` object.
235
+
168
236
  References use `"strategy": "transfer"` by default. Use `"preserve-audit"` only for
169
237
  immutable historical authorship, such as a completed review. Rehearsal leaves that row
170
238
  and its synthetic Auth actor unchanged, but still transfers the active profile, roles,
@@ -179,9 +247,14 @@ or unrelated local application data rolls back the entire claim.
179
247
  verifies the destination bytes, updates the declared database paths, and then removes
180
248
  the old objects. A database failure removes the new copies and leaves the originals
181
249
  usable. Restored objects may begin with either the placeholder owner or no owner;
182
- another owner is refused. `pathReferences` updates matching text or JSONB pointers. A configured
183
- `tokenHook` must return the declared claim subset before commit. The browser must then
250
+ another owner is refused. `pathReferences` updates matching text or JSONB pointers. A
251
+ configured `tokenHook` must return the declared claim subset before commit. The browser must then
184
252
  refresh its session or sign in again because Rehearsal cannot rewrite an issued JWT.
253
+ Run `rehearsal open` again for that fresh-session check; runtime data is preserved.
254
+
255
+ After a claim, sign out and sign in again (or otherwise force a full token refresh) before
256
+ judging application roles. This preserves normal RLS, account blocks, role checks, and
257
+ MFA; identity association is not an authentication bypass.
185
258
 
186
259
  The transaction does not import production passwords, sessions, refresh tokens,
187
260
  cookies, MFA secrets, or provider credentials. Local MFA and authorization rules remain