@runeditrun/rer 0.0.0-stage → 0.1.0

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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -2
  3. package/bin/rer.mjs +3 -0
  4. package/cli/README.md +243 -0
  5. package/cli/adapter.mjs +10 -0
  6. package/cli/ai-local.mjs +65 -0
  7. package/cli/archive-path.mjs +52 -0
  8. package/cli/archive-vite.mjs +55 -0
  9. package/cli/archive.mjs +8 -0
  10. package/cli/auth-copy.mjs +80 -0
  11. package/cli/bounded.mjs +70 -0
  12. package/cli/cache-doctor.mjs +63 -0
  13. package/cli/container-transfer.mjs +35 -0
  14. package/cli/container.mjs +62 -0
  15. package/cli/contract.mjs +25 -0
  16. package/cli/dependencies.mjs +45 -0
  17. package/cli/dependency-approval.mjs +33 -0
  18. package/cli/dependency-doctor.mjs +34 -0
  19. package/cli/dependency-input.mjs +43 -0
  20. package/cli/deploy-config.mjs +118 -0
  21. package/cli/deploy.mjs +37 -0
  22. package/cli/dev-identity.mjs +80 -0
  23. package/cli/errors.mjs +14 -0
  24. package/cli/export.mjs +42 -0
  25. package/cli/font-cache.mjs +29 -0
  26. package/cli/hosted-output.mjs +110 -0
  27. package/cli/independent.mjs +29 -0
  28. package/cli/inspect.mjs +174 -0
  29. package/cli/jsonc.mjs +111 -0
  30. package/cli/lifecycle.mjs +148 -0
  31. package/cli/local.mjs +100 -0
  32. package/cli/main.mjs +57 -0
  33. package/cli/managed-server.mjs +143 -0
  34. package/cli/node-runtime-cache.mjs +68 -0
  35. package/cli/node-runtime-discovery.mjs +56 -0
  36. package/cli/node-runtime-download.mjs +59 -0
  37. package/cli/node-runtime.mjs +50 -0
  38. package/cli/output.mjs +22 -0
  39. package/cli/parse.mjs +77 -0
  40. package/cli/port.mjs +52 -0
  41. package/cli/portability-worker.mjs +56 -0
  42. package/cli/portability.mjs +66 -0
  43. package/cli/preview-cache.mjs +99 -0
  44. package/cli/project.mjs +48 -0
  45. package/cli/release.mjs +24 -0
  46. package/cli/runtime-front-door.mjs +46 -0
  47. package/cli/scheduled.mjs +81 -0
  48. package/cli/server-activity.mjs +12 -0
  49. package/cli/server-proxy.mjs +93 -0
  50. package/cli/server-registry.mjs +177 -0
  51. package/cli/server-resources.mjs +22 -0
  52. package/cli/settings.mjs +15 -0
  53. package/cli/setup-answers.mjs +29 -0
  54. package/cli/setup-input.mjs +113 -0
  55. package/cli/shared.mjs +7 -0
  56. package/cli/transfers.mjs +33 -0
  57. package/cli/version.mjs +36 -0
  58. package/composition/git-source.mjs +111 -0
  59. package/edition-authoring/files.mjs +81 -0
  60. package/edition-authoring/manifest.mjs +65 -0
  61. package/global/README.md +155 -0
  62. package/global/checkout.mjs +72 -0
  63. package/global/cli/command.mjs +13 -0
  64. package/global/cli/install.mjs +75 -0
  65. package/global/cli/layout.mjs +45 -0
  66. package/global/cli/status.mjs +54 -0
  67. package/global/cli/uninstall.mjs +36 -0
  68. package/global/delegate.mjs +13 -0
  69. package/global/launch/capacity.mjs +72 -0
  70. package/global/launch/dev.mjs +129 -0
  71. package/global/launch/facts.mjs +93 -0
  72. package/global/launch/failure.mjs +5 -0
  73. package/global/launch/local-defaults.mjs +4 -0
  74. package/global/launch/prepare.mjs +61 -0
  75. package/global/launch/render.mjs +44 -0
  76. package/global/launch/state.mjs +97 -0
  77. package/global/launch/steps.mjs +81 -0
  78. package/global/launch/worker.mjs +43 -0
  79. package/global/list.mjs +59 -0
  80. package/global/main.mjs +53 -0
  81. package/global/package-info.mjs +38 -0
  82. package/global/registry.mjs +24 -0
  83. package/global/rer.mjs +29 -0
  84. package/global/update/archive.mjs +89 -0
  85. package/global/update/command.mjs +90 -0
  86. package/global/update/latest.mjs +6 -0
  87. package/global/update/notice.mjs +58 -0
  88. package/global/update/registry.mjs +47 -0
  89. package/global/version.mjs +29 -0
  90. package/install.sh +144 -0
  91. package/modules/ai/LICENSE +21 -0
  92. package/modules/ai/README.md +90 -0
  93. package/modules/ai/client.mjs +71 -0
  94. package/modules/ai/config.mjs +39 -0
  95. package/modules/ai/dev/index.d.mts +6 -0
  96. package/modules/ai/dev/index.mjs +64 -0
  97. package/modules/ai/dev/outbox.mjs +33 -0
  98. package/modules/ai/errors.mjs +28 -0
  99. package/modules/ai/fake/index.d.mts +3 -0
  100. package/modules/ai/fake/index.mjs +82 -0
  101. package/modules/ai/index.d.mts +10 -0
  102. package/modules/ai/index.mjs +3 -0
  103. package/modules/ai/package.json +6 -0
  104. package/modules/container/README.md +156 -0
  105. package/modules/container/adapters/d1.mjs +34 -0
  106. package/modules/container/adapters/egress.mjs +40 -0
  107. package/modules/container/adapters/jobs.mjs +47 -0
  108. package/modules/container/adapters/objects.mjs +84 -0
  109. package/modules/container/adapters/retained.mjs +14 -0
  110. package/modules/container/adapters/sqlite-error.mjs +7 -0
  111. package/modules/container/adapters/sqlite-worker.mjs +26 -0
  112. package/modules/container/assets.mjs +14 -0
  113. package/modules/container/bindings.mjs +1 -0
  114. package/modules/container/boundary.mjs +38 -0
  115. package/modules/container/build.mjs +65 -0
  116. package/modules/container/deploy-input.mjs +17 -0
  117. package/modules/container/entrypoint.sh +5 -0
  118. package/modules/container/host.mjs +43 -0
  119. package/modules/container/ingress.mjs +23 -0
  120. package/modules/container/lock.mjs +11 -0
  121. package/modules/container/migrations.mjs +16 -0
  122. package/modules/container/node-engine.mjs +39 -0
  123. package/modules/container/portability-schema.mjs +14 -0
  124. package/modules/container/remote/deploy.py +76 -0
  125. package/modules/container/remote/files.py +275 -0
  126. package/modules/container/remote/resources.py +157 -0
  127. package/modules/container/runner.mjs +10 -0
  128. package/modules/container/ssh-plan.mjs +44 -0
  129. package/modules/container/ssh.mjs +63 -0
  130. package/modules/container/transfer.mjs +27 -0
  131. package/modules/container/worker-engine.mjs +32 -0
  132. package/modules/dependencies/README.md +211 -0
  133. package/modules/dependencies/approval.mjs +51 -0
  134. package/modules/dependencies/auth.mjs +40 -0
  135. package/modules/dependencies/authority.mjs +14 -0
  136. package/modules/dependencies/crypto.mjs +13 -0
  137. package/modules/dependencies/index.d.mts +23 -0
  138. package/modules/dependencies/index.mjs +113 -0
  139. package/modules/dependencies/logout.mjs +42 -0
  140. package/modules/dependencies/native.mjs +9 -0
  141. package/modules/dependencies/protocol.mjs +23 -0
  142. package/modules/dependencies/schema/approval-attempt.sql +3 -0
  143. package/modules/dependencies/schema/dependencies.sql +15 -0
  144. package/modules/dependencies/store.mjs +19 -0
  145. package/modules/deploy-kit/README.md +308 -0
  146. package/modules/deploy-kit/artifact.mjs +47 -0
  147. package/modules/deploy-kit/build-modules.mjs +21 -0
  148. package/modules/deploy-kit/check.mjs +13 -0
  149. package/modules/deploy-kit/cli.mjs +50 -0
  150. package/modules/deploy-kit/container-config.mjs +8 -0
  151. package/modules/deploy-kit/default-stage.mjs +14 -0
  152. package/modules/deploy-kit/deploy.mjs +80 -0
  153. package/modules/deploy-kit/health-request.mjs +86 -0
  154. package/modules/deploy-kit/health.mjs +99 -0
  155. package/modules/deploy-kit/index.mjs +12 -0
  156. package/modules/deploy-kit/local-only.mjs +10 -0
  157. package/modules/deploy-kit/migrations.mjs +48 -0
  158. package/modules/deploy-kit/owner-cli.mjs +63 -0
  159. package/modules/deploy-kit/owner.mjs +297 -0
  160. package/modules/deploy-kit/prepare.mjs +62 -0
  161. package/modules/deploy-kit/provider.mjs +99 -0
  162. package/modules/deploy-kit/queue-consumer.mjs +10 -0
  163. package/modules/deploy-kit/receipt.mjs +16 -0
  164. package/modules/deploy-kit/render.mjs +47 -0
  165. package/modules/deploy-kit/resources.mjs +106 -0
  166. package/modules/deploy-kit/service-grants.mjs +25 -0
  167. package/modules/deploy-kit/sql-safety.mjs +94 -0
  168. package/modules/deploy-kit/sql-tokens.mjs +49 -0
  169. package/modules/deploy-kit/teardown.mjs +100 -0
  170. package/modules/deploy-kit/templates.mjs +31 -0
  171. package/modules/deploy-kit/validation.mjs +58 -0
  172. package/modules/deploy-kit/worker.mjs +105 -0
  173. package/modules/erasure/CONTRACT.md +90 -0
  174. package/modules/erasure/README.md +221 -0
  175. package/modules/erasure/audit.mjs +23 -0
  176. package/modules/erasure/blobs.mjs +45 -0
  177. package/modules/erasure/cascade.mjs +91 -0
  178. package/modules/erasure/completion.mjs +62 -0
  179. package/modules/erasure/declaration.mjs +51 -0
  180. package/modules/erasure/fence.mjs +19 -0
  181. package/modules/erasure/index.d.mts +62 -0
  182. package/modules/erasure/index.mjs +85 -0
  183. package/modules/erasure/order.mjs +56 -0
  184. package/modules/erasure/plan.mjs +121 -0
  185. package/modules/erasure/rate.mjs +57 -0
  186. package/modules/erasure/receipt.mjs +32 -0
  187. package/modules/erasure/root.mjs +21 -0
  188. package/modules/erasure/schema/erasure-rates.sql +5 -0
  189. package/modules/erasure/schema/erasure.sql +27 -0
  190. package/modules/erasure/scope.mjs +29 -0
  191. package/modules/erasure/sql.mjs +23 -0
  192. package/modules/erasure/verification.mjs +69 -0
  193. package/modules/integrations/core/CONTRACT.md +50 -0
  194. package/modules/integrations/core/README.md +27 -0
  195. package/modules/integrations/core/crypto.ts +19 -0
  196. package/modules/integrations/core/database.ts +21 -0
  197. package/modules/integrations/core/delivery.ts +97 -0
  198. package/modules/integrations/core/destination.ts +13 -0
  199. package/modules/integrations/core/index.ts +7 -0
  200. package/modules/integrations/core/node-transport.ts +72 -0
  201. package/modules/integrations/core/public-address.ts +57 -0
  202. package/modules/integrations/core/repository.ts +74 -0
  203. package/modules/integrations/core/schema.sql +36 -0
  204. package/modules/integrations/core/sqlite.ts +20 -0
  205. package/modules/integrations/core/timestamped-body-signature.ts +38 -0
  206. package/modules/integrations/core/tokens.ts +71 -0
  207. package/modules/integrations/core/types.ts +52 -0
  208. package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
  209. package/modules/integrations/core/webhook.ts +73 -0
  210. package/modules/job-queue/README.md +173 -0
  211. package/modules/job-queue/cli.mjs +64 -0
  212. package/modules/job-queue/command.mjs +64 -0
  213. package/modules/job-queue/config.mjs +48 -0
  214. package/modules/job-queue/identity.mjs +48 -0
  215. package/modules/job-queue/index.mjs +135 -0
  216. package/modules/job-queue/membership.mjs +30 -0
  217. package/modules/job-queue/module.json +10 -0
  218. package/modules/job-queue/policy.mjs +29 -0
  219. package/modules/job-queue/processes.mjs +80 -0
  220. package/modules/job-queue/resources.mjs +37 -0
  221. package/modules/job-queue/storage.mjs +64 -0
  222. package/modules/job-queue/supervisor.mjs +30 -0
  223. package/modules/job-queue/waiting.mjs +16 -0
  224. package/modules/mail/LICENSE +21 -0
  225. package/modules/mail/README.md +336 -0
  226. package/modules/mail/doctor.mjs +81 -0
  227. package/modules/mail/index.mjs +8 -0
  228. package/modules/mail/live-proof/README.md +113 -0
  229. package/modules/mail/live-proof/run.mjs +55 -0
  230. package/modules/mail/package.json +19 -0
  231. package/modules/mail/pnpm-lock.yaml +537 -0
  232. package/modules/mail/src/adapters/cloudflare/README.md +84 -0
  233. package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
  234. package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
  235. package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
  236. package/modules/mail/src/adapters/messaging/README.md +92 -0
  237. package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
  238. package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
  239. package/modules/mail/src/adapters/messaging/index.mjs +61 -0
  240. package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
  241. package/modules/mail/src/adapters/messaging/request.mjs +52 -0
  242. package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
  243. package/modules/mail/src/adapters/resend/README.md +120 -0
  244. package/modules/mail/src/adapters/resend/index.mjs +22 -0
  245. package/modules/mail/src/adapters/resend/response.mjs +54 -0
  246. package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
  247. package/modules/mail/src/adapters/resend/size.mjs +99 -0
  248. package/modules/mail/src/adapters/sendgrid/README.md +75 -0
  249. package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
  250. package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
  251. package/modules/mail/src/core/errors.mjs +9 -0
  252. package/modules/mail/src/core/transport.mjs +33 -0
  253. package/modules/mail/src/core/validation.mjs +172 -0
  254. package/modules/mail/src/development/index.d.mts +37 -0
  255. package/modules/mail/src/development/index.mjs +35 -0
  256. package/modules/mail/src/development/outbox.mjs +78 -0
  257. package/modules/mail/src/development/schema.sql +9 -0
  258. package/modules/mail/src/development/viewer-html.mjs +42 -0
  259. package/modules/mail/src/development/viewer.mjs +65 -0
  260. package/modules/mail/src/doctor/README.md +107 -0
  261. package/modules/mail/src/doctor/common.mjs +64 -0
  262. package/modules/mail/src/doctor/dns.mjs +102 -0
  263. package/modules/mail/src/doctor/index.mjs +28 -0
  264. package/modules/mail/src/doctor/probe.mjs +66 -0
  265. package/modules/mail/src/doctor/receive.mjs +81 -0
  266. package/modules/mail/src/doctor/records.mjs +45 -0
  267. package/modules/mail/src/events/README.md +268 -0
  268. package/modules/mail/src/events/d1-schema.sql +25 -0
  269. package/modules/mail/src/events/d1-storage.mjs +76 -0
  270. package/modules/mail/src/events/index.mjs +3 -0
  271. package/modules/mail/src/events/intake.mjs +201 -0
  272. package/modules/mail/src/events/storage.mjs +52 -0
  273. package/modules/mail/src/events/verify.mjs +92 -0
  274. package/modules/mail/src/inbound/README.md +137 -0
  275. package/modules/mail/src/inbound/bytes.mjs +91 -0
  276. package/modules/mail/src/inbound/headers.mjs +143 -0
  277. package/modules/mail/src/inbound/index.mjs +29 -0
  278. package/modules/mail/src/inbound/mime.mjs +36 -0
  279. package/modules/mail/src/inbound/multipart.mjs +27 -0
  280. package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
  281. package/modules/mail/src/inbound/resend.mjs +54 -0
  282. package/modules/mail/src/inbound/sendgrid.mjs +60 -0
  283. package/modules/mail/src/sender.mjs +33 -0
  284. package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
  285. package/modules/mail/src/sendgrid-email/index.mjs +86 -0
  286. package/modules/mail/src/unsubscribe/index.mjs +142 -0
  287. package/modules/operator-auth/README.md +599 -0
  288. package/modules/operator-auth/accounts/adapter.mjs +41 -0
  289. package/modules/operator-auth/accounts/admin.mjs +159 -0
  290. package/modules/operator-auth/accounts/credentials.mjs +27 -0
  291. package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
  292. package/modules/operator-auth/accounts/index.d.mts +9 -0
  293. package/modules/operator-auth/accounts/index.mjs +9 -0
  294. package/modules/operator-auth/accounts/native.d.mts +38 -0
  295. package/modules/operator-auth/accounts/native.mjs +171 -0
  296. package/modules/operator-auth/accounts/oa.mjs +90 -0
  297. package/modules/operator-auth/accounts/provider-control.mjs +50 -0
  298. package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
  299. package/modules/operator-auth/accounts/roles.mjs +26 -0
  300. package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
  301. package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
  302. package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
  303. package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
  304. package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
  305. package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
  306. package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
  307. package/modules/operator-auth/accounts/settings-page.mjs +4 -0
  308. package/modules/operator-auth/accounts/store.mjs +246 -0
  309. package/modules/operator-auth/branding.mjs +66 -0
  310. package/modules/operator-auth/config.mjs +35 -0
  311. package/modules/operator-auth/conformance/README.md +81 -0
  312. package/modules/operator-auth/conformance/index.d.mts +43 -0
  313. package/modules/operator-auth/conformance/index.mjs +300 -0
  314. package/modules/operator-auth/conformance/presentation.mjs +57 -0
  315. package/modules/operator-auth/crypto.mjs +17 -0
  316. package/modules/operator-auth/dev/badge.mjs +62 -0
  317. package/modules/operator-auth/dev/index.d.mts +14 -0
  318. package/modules/operator-auth/dev/index.mjs +137 -0
  319. package/modules/operator-auth/dev/personas.mjs +17 -0
  320. package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
  321. package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
  322. package/modules/operator-auth/dev-admission.d.mts +4 -0
  323. package/modules/operator-auth/dev-admission.mjs +10 -0
  324. package/modules/operator-auth/dev-gate.mjs +86 -0
  325. package/modules/operator-auth/docs/README.md +78 -0
  326. package/modules/operator-auth/docs/native-accounts.md +313 -0
  327. package/modules/operator-auth/docs/tanstack-start.md +218 -0
  328. package/modules/operator-auth/docs/vite-spa.md +144 -0
  329. package/modules/operator-auth/errors.mjs +9 -0
  330. package/modules/operator-auth/http.mjs +42 -0
  331. package/modules/operator-auth/identity/admission.mjs +105 -0
  332. package/modules/operator-auth/identity/after-confirm.mjs +18 -0
  333. package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
  334. package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
  335. package/modules/operator-auth/identity/email-link/index.mjs +128 -0
  336. package/modules/operator-auth/identity/email-link/message.mjs +16 -0
  337. package/modules/operator-auth/identity/index.d.mts +7 -0
  338. package/modules/operator-auth/identity/index.mjs +5 -0
  339. package/modules/operator-auth/identity/oidc/README.md +171 -0
  340. package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
  341. package/modules/operator-auth/identity/oidc/config.mjs +102 -0
  342. package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
  343. package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
  344. package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
  345. package/modules/operator-auth/identity/oidc/index.mjs +287 -0
  346. package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
  347. package/modules/operator-auth/identity/oidc/network.mjs +93 -0
  348. package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
  349. package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
  350. package/modules/operator-auth/identity/oidc/store.mjs +68 -0
  351. package/modules/operator-auth/identity/principal.mjs +28 -0
  352. package/modules/operator-auth/index.d.mts +223 -0
  353. package/modules/operator-auth/index.mjs +11 -0
  354. package/modules/operator-auth/input.mjs +55 -0
  355. package/modules/operator-auth/local-cli.mjs +16 -0
  356. package/modules/operator-auth/page.mjs +34 -0
  357. package/modules/operator-auth/policy.mjs +20 -0
  358. package/modules/operator-auth/routes.mjs +91 -0
  359. package/modules/operator-auth/service.mjs +152 -0
  360. package/modules/operator-auth/signin/index.mjs +67 -0
  361. package/modules/owner-claim/README.md +137 -0
  362. package/modules/owner-claim/idp.mjs +40 -0
  363. package/modules/owner-claim/index.d.mts +27 -0
  364. package/modules/owner-claim/index.mjs +39 -0
  365. package/modules/owner-claim/page.mjs +11 -0
  366. package/modules/owner-claim/schema/idp.sql +4 -0
  367. package/modules/owner-claim/schema/owner-claim.sql +18 -0
  368. package/modules/owner-claim/transaction.mjs +87 -0
  369. package/modules/owner-claim/verify.mjs +80 -0
  370. package/modules/portability/README.md +444 -0
  371. package/modules/portability/bridge.mjs +102 -0
  372. package/modules/portability/catalogue.mjs +118 -0
  373. package/modules/portability/cli-options.mjs +38 -0
  374. package/modules/portability/cli.mjs +56 -0
  375. package/modules/portability/copies.mjs +68 -0
  376. package/modules/portability/dev-data.mjs +32 -0
  377. package/modules/portability/diff.mjs +33 -0
  378. package/modules/portability/export.mjs +129 -0
  379. package/modules/portability/files.mjs +65 -0
  380. package/modules/portability/format.mjs +122 -0
  381. package/modules/portability/generated.mjs +60 -0
  382. package/modules/portability/hash.mjs +79 -0
  383. package/modules/portability/http.mjs +42 -0
  384. package/modules/portability/import.mjs +185 -0
  385. package/modules/portability/index.d.mts +58 -0
  386. package/modules/portability/index.mjs +22 -0
  387. package/modules/portability/journal.mjs +49 -0
  388. package/modules/portability/legacy-files.mjs +43 -0
  389. package/modules/portability/legacy-safety.mjs +27 -0
  390. package/modules/portability/lines.mjs +36 -0
  391. package/modules/portability/migration-history.mjs +18 -0
  392. package/modules/portability/migration-statements.mjs +23 -0
  393. package/modules/portability/payloads.mjs +52 -0
  394. package/modules/portability/provider-data.mjs +25 -0
  395. package/modules/portability/restore-safety.mjs +30 -0
  396. package/modules/portability/schema/portability.sql +25 -0
  397. package/modules/portability/storage.mjs +49 -0
  398. package/modules/release/README.md +212 -0
  399. package/modules/release/catalogue.mjs +79 -0
  400. package/modules/release/export.mjs +51 -0
  401. package/modules/release/produce.mjs +136 -0
  402. package/modules/release/public-child-transport-state.cjs +29 -0
  403. package/modules/release/public-child.mjs +71 -0
  404. package/modules/release/public-git.mjs +21 -0
  405. package/modules/release/publication-scan.mjs +22 -0
  406. package/modules/release/publish-handoff.mjs +37 -0
  407. package/modules/release/source.mjs +71 -0
  408. package/modules/release/test-summary.mjs +50 -0
  409. package/onboarding/README.md +163 -0
  410. package/onboarding/activity.mjs +31 -0
  411. package/onboarding/adapter-auth.mjs +88 -0
  412. package/onboarding/adapter-hosting.mjs +35 -0
  413. package/onboarding/adapter-labels.mjs +13 -0
  414. package/onboarding/adapter-settings.mjs +129 -0
  415. package/onboarding/adapter.mjs +238 -0
  416. package/onboarding/agent-handoff.mjs +9 -0
  417. package/onboarding/app-processes.mjs +21 -0
  418. package/onboarding/archive-output.d.mts +3 -0
  419. package/onboarding/archive-output.mjs +54 -0
  420. package/onboarding/command-custody.mjs +24 -0
  421. package/onboarding/command-recovery.mjs +8 -0
  422. package/onboarding/commands.mjs +166 -0
  423. package/onboarding/dependency-approval.mjs +36 -0
  424. package/onboarding/dev-identity.mjs +23 -0
  425. package/onboarding/email.mjs +24 -0
  426. package/onboarding/failures.mjs +42 -0
  427. package/onboarding/input-limits.mjs +7 -0
  428. package/onboarding/installer-inputs.mjs +24 -0
  429. package/onboarding/launcher.mjs +34 -0
  430. package/onboarding/local/build-files.mjs +64 -0
  431. package/onboarding/local/build.mjs +49 -0
  432. package/onboarding/local/environment.mjs +30 -0
  433. package/onboarding/local/files.mjs +35 -0
  434. package/onboarding/local/operation.mjs +166 -0
  435. package/onboarding/local/owner.mjs +34 -0
  436. package/onboarding/local/process-table.mjs +114 -0
  437. package/onboarding/local/recovery.mjs +79 -0
  438. package/onboarding/local/state.mjs +31 -0
  439. package/onboarding/metadata.mjs +7 -0
  440. package/onboarding/operator-install.mjs +45 -0
  441. package/onboarding/operator-installer.mjs +39 -0
  442. package/onboarding/operator-prerequisites.mjs +27 -0
  443. package/onboarding/package.json +6 -0
  444. package/onboarding/prefill.mjs +16 -0
  445. package/onboarding/private-files.mjs +97 -0
  446. package/onboarding/private.mjs +35 -0
  447. package/onboarding/process-cleanup.mjs +134 -0
  448. package/onboarding/process-diagnostics.mjs +91 -0
  449. package/onboarding/process-scope.mjs +118 -0
  450. package/onboarding/process-table.mjs +118 -0
  451. package/onboarding/process-tree.mjs +36 -0
  452. package/onboarding/production-modules.mjs +22 -0
  453. package/onboarding/production-options.mjs +40 -0
  454. package/onboarding/production-plan.mjs +8 -0
  455. package/onboarding/production-runtime.mjs +30 -0
  456. package/onboarding/project-vite.mjs +14 -0
  457. package/onboarding/providers.mjs +289 -0
  458. package/onboarding/queue.mjs +53 -0
  459. package/onboarding/queued-entry.mjs +34 -0
  460. package/onboarding/readiness.mjs +59 -0
  461. package/onboarding/redaction-jwt.mjs +32 -0
  462. package/onboarding/redaction.mjs +298 -0
  463. package/onboarding/schema/SETUP.schema.json +514 -0
  464. package/onboarding/schema/dependencies.mjs +35 -0
  465. package/onboarding/schema/json-schema.mjs +202 -0
  466. package/onboarding/schema/setup-validation.mjs +325 -0
  467. package/onboarding/secrets.mjs +21 -0
  468. package/onboarding/server.mjs +188 -0
  469. package/onboarding/setup-manifest.mjs +116 -0
  470. package/onboarding/state.mjs +46 -0
  471. package/onboarding/theme-files.mjs +37 -0
  472. package/onboarding/theme.mjs +118 -0
  473. package/onboarding/ui/activity.js +98 -0
  474. package/onboarding/ui/address-client.mjs +33 -0
  475. package/onboarding/ui/app.js +377 -0
  476. package/onboarding/ui/index.html +1 -0
  477. package/onboarding/ui/model-core.mjs +127 -0
  478. package/onboarding/ui/model.mjs +5 -0
  479. package/onboarding/ui/style.css +63 -0
  480. package/onboarding/ui/theme.css +37 -0
  481. package/onboarding/ui/workspace-art.js +30 -0
  482. package/onboarding/wrangler-auth.mjs +43 -0
  483. package/operations/README.md +246 -0
  484. package/operations/operations.mjs +168 -0
  485. package/operations/project-operations.mjs +157 -0
  486. package/package.json +11 -4
  487. package/scripts/publish-check/README.md +72 -0
  488. package/scripts/publish-check/checksum_maps.py +52 -0
  489. package/scripts/publish-check/cli.py +34 -0
  490. package/scripts/publish-check/command_arguments.py +51 -0
  491. package/scripts/publish-check/command_shell_quotes.py +41 -0
  492. package/scripts/publish-check/command_source_quotes.py +120 -0
  493. package/scripts/publish-check/content_identities.py +250 -0
  494. package/scripts/publish-check/copy_identities.py +41 -0
  495. package/scripts/publish-check/der.py +61 -0
  496. package/scripts/publish-check/destination.py +117 -0
  497. package/scripts/publish-check/env_values.py +74 -0
  498. package/scripts/publish-check/font_licence.py +23 -0
  499. package/scripts/publish-check/git_history.py +115 -0
  500. package/scripts/publish-check/github_refs.py +217 -0
  501. package/scripts/publish-check/gitleaks.py +72 -0
  502. package/scripts/publish-check/gitleaks.toml +40 -0
  503. package/scripts/publish-check/history_scan.py +176 -0
  504. package/scripts/publish-check/hit_location.py +18 -0
  505. package/scripts/publish-check/hygiene_config.py +42 -0
  506. package/scripts/publish-check/hygiene_text.py +11 -0
  507. package/scripts/publish-check/identity-recipe.mjs +54 -0
  508. package/scripts/publish-check/json_identity.py +79 -0
  509. package/scripts/publish-check/license-exceptions.json +11 -0
  510. package/scripts/publish-check/licenses.py +49 -0
  511. package/scripts/publish-check/policy.py +18 -0
  512. package/scripts/publish-check/project_identity.py +89 -0
  513. package/scripts/publish-check/provenance.py +91 -0
  514. package/scripts/publish-check/provenance_pins.py +33 -0
  515. package/scripts/publish-check/public_base.py +63 -0
  516. package/scripts/publish-check/public_hygiene.py +80 -0
  517. package/scripts/publish-check/publication-config.json +12 -0
  518. package/scripts/publish-check/publication_config.py +34 -0
  519. package/scripts/publish-check/report.py +52 -0
  520. package/scripts/publish-check/repository_commands.py +75 -0
  521. package/scripts/publish-check/repository_intent.py +52 -0
  522. package/scripts/publish-check/reviewed.py +41 -0
  523. package/scripts/publish-check/source_hashes.mjs +6 -0
  524. package/scripts/publish-check/tree_scan.py +88 -0
  525. package/scripts/publish-check/trust_bundle.py +87 -0
  526. package/scripts/publish-check.mjs +58 -0
@@ -0,0 +1,137 @@
1
+ // Local dev identity. SERVER-ONLY and LOCAL-ONLY: load this layer only from a
2
+ // local-only Worker entry (a `wrangler dev` or Vite-serve entry), never from the
3
+ // deployed main. `rer build` refuses any build output containing the marker.
4
+ import { configure } from '../config.mjs';
5
+ import { AuthStore } from '../accounts/store.mjs';
6
+ import { createOperatorAuth } from '../service.mjs';
7
+ import { AuthError } from '../errors.mjs';
8
+ import { hashToken, opaqueToken } from '../crypto.mjs';
9
+ import { readCookie, sessionCookie, isLoopbackHost } from '../http.mjs';
10
+ import { safeReturnTo } from '../policy.mjs';
11
+ import { isActiveOperator, defaultRolePolicy } from '../accounts/roles.mjs';
12
+ import { devGate, devNotFound } from '../dev-gate.mjs';
13
+ import { registerDevAdmission } from '../dev-admission.mjs';
14
+ import { definePersonas } from './personas.mjs';
15
+ import { decorate } from './badge.mjs';
16
+
17
+ export const DEV_LAYER_MARKER = 'rer-dev-layer:local-only-never-deploy';
18
+ const headers = { 'cache-control': 'no-store', 'referrer-policy': 'no-referrer', 'x-content-type-options': 'nosniff', 'x-robots-tag': 'noindex, nofollow', 'x-rer-dev-layer': DEV_LAYER_MARKER };
19
+ const text = (status, body) => new Response(body, { status, headers: { ...headers, 'content-type': 'text/plain; charset=utf-8' } });
20
+
21
+ // RER Managed sets RER_INSTALLATION_ID with its management origin. Dev identity
22
+ // never runs inside such an installation, even one reached from this computer.
23
+ export function managedInstallation(env) {
24
+ if (!env.RER_INSTALLATION_ID || !env.RER_MANAGEMENT_ORIGIN) return false;
25
+ try { return !isLoopbackHost(new URL(env.RER_MANAGEMENT_ORIGIN).hostname); } catch { return true; }
26
+ }
27
+
28
+ /**
29
+ * Wraps a seed's Worker with dev sign-in (`/auth/dev/enter?as=…`), the persona
30
+ * list (`/__dev/personas`) and the dev-only badge. Every surface needs the key
31
+ * gate and answers 404 without it. `config(env)` is the seed's own AuthConfig
32
+ * and `personas()` its devPersonas() declaration.
33
+ */
34
+ export function withDevIdentity(worker, { bindings = env => env, config: authConfig, personas = () => [] }) {
35
+ const declared = definePersonas(personas());
36
+ return {
37
+ ...worker,
38
+ async fetch(request, original, execution) {
39
+ const env = bindings(original);
40
+ if (managedInstallation(env)) {
41
+ // Never admit a dev credential here; with dev identity on, serve nothing.
42
+ registerDevAdmission(null);
43
+ if (env.RER_DEV_IDENTITY !== 'off') return text(503, 'Dev identity refuses to run beside RER_INSTALLATION_ID with a non-loopback RER_MANAGEMENT_ORIGIN. Remove those bindings from local settings or use rer dev --login.');
44
+ return new URL(request.url).pathname === '/auth/dev/enter' || new URL(request.url).pathname === '/__dev/personas' ? devNotFound() : worker.fetch(request, original, execution);
45
+ }
46
+ const gate = devGate({ key: env.RER_DEV_KEY, origin: env.AUTH_ORIGIN, local: env.AUTH_LOCAL === 'true' });
47
+ registerDevAdmission(gate ? r => gate.admits(r) : null);
48
+ const path = new URL(request.url).pathname;
49
+ if (path === '/auth/dev/enter' || path === '/__dev/personas') {
50
+ if (!gate || env.RER_DEV_IDENTITY === 'off' || !await gate.admits(request)) return devNotFound();
51
+ const context = identity(env, authConfig, declared);
52
+ try {
53
+ return path === '/__dev/personas' ? Response.json(context.list(), { headers }) : await enter(request, gate, context);
54
+ } catch (error) {
55
+ if (error instanceof AuthError) return Response.json({ error: error.code }, { status: error.status, headers });
56
+ throw error;
57
+ }
58
+ }
59
+ const response = await worker.fetch(request, original, execution);
60
+ if (!gate || request.method !== 'GET' || path.startsWith('/__dev/')) return response;
61
+ const admitted = await gate.admits(request);
62
+ const badge = admitted && env.RER_DEV_IDENTITY !== 'off' ? await identity(env, authConfig, declared).badge(request) : null;
63
+ return decorate(response, { exchange: path === '/', badge });
64
+ },
65
+ };
66
+ }
67
+
68
+ function identity(env, authConfig, declared) {
69
+ const config = configure(authConfig(env)), store = new AuthStore(env.DB);
70
+ const hash = token => hashToken(config.SESSION_SECRET, 'session', token);
71
+ const everyone = () => [{ as: 'owner', email: config.OWNER_EMAIL }, ...declared];
72
+ async function current(request) {
73
+ const token = readCookie(request, config);
74
+ if (!token) return null;
75
+ const session = await store.session(await hash(token), Date.now());
76
+ return session && isActiveOperator(session.operator, config.allowList) ? session : null;
77
+ }
78
+ return {
79
+ config, store, hash, current, everyone,
80
+ async ownerProof(request) {
81
+ // Reuse the same persisted app-local proof guard as the administration
82
+ // routes. This read-only guard never needs a mail sender or an IP budget.
83
+ const auth = createOperatorAuth({ db: env.DB, config: authConfig(env), clientIp: () => null });
84
+ const actor = await auth.requireStepUp(request, 'owner:transfer');
85
+ if (!defaultRolePolicy({ actor: actor.operator, action: 'operators:invite', role: 'owner' })) throw new AuthError('forbidden', 403);
86
+ return { ...actor, hash: await hash(readCookie(request, config)) };
87
+ },
88
+ list: () => [...everyone().map(({ as, email, role }) => ({ as, email, ...(role ? { role } : {}) })), { as: 'signed-out' }],
89
+ async badge(request) { return { session: await current(request), choices: everyone() }; },
90
+ };
91
+ }
92
+
93
+ async function enter(request, gate, { config, store, hash, current, everyone, ownerProof }) {
94
+ if (request.method !== 'GET') return text(405, 'Method not allowed');
95
+ const url = new URL(request.url), as = url.searchParams.get('as') ?? 'owner';
96
+ const reply = new Headers(headers);
97
+ // A harness presenting the key header also receives the browser cookie.
98
+ if (!(request.headers.get('cookie') ?? '').includes(`${gate.cookieName}=`)) reply.append('set-cookie', await gate.setCookie());
99
+ const existing = await current(request);
100
+ if (as === 'signed-out') {
101
+ const token = readCookie(request, config);
102
+ if (token) await store.run('UPDATE oa_sessions SET revoked_at=COALESCE(revoked_at,?) WHERE token_hash=?', [Date.now(), await hash(token)]);
103
+ reply.append('set-cookie', sessionCookie('', config, 0));
104
+ reply.set('location', '/');
105
+ return new Response(null, { status: 303, headers: reply });
106
+ }
107
+ const persona = everyone().find(p => p.as === as);
108
+ if (!persona) return devNotFound();
109
+ let operator = await store.byEmail(persona.email);
110
+ // Personas are real operators, created only here, in local dev.
111
+ if (!operator && persona.role) {
112
+ if (persona.role === 'owner') {
113
+ const actor = await ownerProof(request), now = Date.now();
114
+ const fence = store.actorFence(actor, now);
115
+ const result = await store.run(`INSERT INTO oa_operators(id,email,display_name,role,status,created_at)
116
+ SELECT ?,?,?,'owner','active',? WHERE ${fence.sql} ON CONFLICT(email) DO NOTHING`,
117
+ [crypto.randomUUID(), persona.email, persona.displayName, now, ...fence.args]);
118
+ if (result.meta.changes !== 1) throw new AuthError('authority_changed', 409);
119
+ } else {
120
+ await store.run(`INSERT INTO oa_operators(id,email,display_name,role,status,created_at) VALUES(?,?,?,?,'active',?) ON CONFLICT(email) DO NOTHING`,
121
+ [crypto.randomUUID(), persona.email, persona.displayName, persona.role, Date.now()]);
122
+ }
123
+ operator = await store.byEmail(persona.email);
124
+ }
125
+ if (!operator) return text(409, 'The owner that rer setup claimed is not in this database. Run rer setup.');
126
+ if (!isActiveOperator(operator, config.allowList)) return text(403, `${as} is not an active operator in this app.`);
127
+ const domain = operator.email.slice(operator.email.lastIndexOf('@') + 1);
128
+ if (operator.role !== 'owner' && await store.domainRule(domain)) return text(403, 'Sign-in refused');
129
+ if (existing?.kind !== 'dev' || existing.operator.id !== operator.id) {
130
+ const token = opaqueToken(), session = { id: crypto.randomUUID(), hash: await hash(token), expiresAt: Date.now() + config.sessionSeconds * 1000 };
131
+ const issued = await store.issue({ operatorId: operator.id, expectedVersion: operator.version, session, now: Date.now(), kind: 'dev' });
132
+ if (!issued) return text(409, 'The operator changed while signing in. Try again.');
133
+ reply.append('set-cookie', sessionCookie(token, config, config.sessionSeconds));
134
+ }
135
+ reply.set('location', safeReturnTo(url.searchParams.get('returnTo')));
136
+ return new Response(null, { status: 303, headers: reply });
137
+ }
@@ -0,0 +1,17 @@
1
+ import { DEFAULT_ROLES } from '../accounts/roles.mjs';
2
+
3
+ // A seed's devPersonas() returns [{ as, role, displayName? }]. Each persona is
4
+ // a real operator at `<as>@dev.localhost`, created only by local dev sign-in.
5
+ // Export marks them suspended; `.localhost` mail never leaves this computer.
6
+ export const PERSONA_DOMAIN = 'dev.localhost';
7
+ export function definePersonas(list) {
8
+ if (!Array.isArray(list)) throw new TypeError('devPersonas() must return an array.');
9
+ const seen = new Set(['owner', 'signed-out']);
10
+ return Object.freeze(list.map(entry => {
11
+ const { as, role, displayName } = entry ?? {};
12
+ if (typeof as !== 'string' || !/^[a-z][a-z0-9-]{0,39}$/.test(as) || seen.has(as)) throw new TypeError(`Invalid or repeated dev persona name: ${String(as)}`);
13
+ if (!DEFAULT_ROLES.includes(role)) throw new TypeError(`Dev persona ${as} needs a role: ${DEFAULT_ROLES.join(', ')}.`);
14
+ seen.add(as);
15
+ return Object.freeze({ as, role, email: `${as}@${PERSONA_DOMAIN}`, displayName: typeof displayName === 'string' && displayName.trim() ? displayName.trim().slice(0, 80) : `Dev ${as}` });
16
+ }));
17
+ }
@@ -0,0 +1,3 @@
1
+ export const DEV_KEY_FILE: string;
2
+ export function readDevKey(root: string): Promise<string>;
3
+ export function signInAs(input: { origin: string; key: string; as?: string; returnTo?: string }): Promise<{ cookies: { name: string; value: string }[]; location: string | null; header: string }>;
@@ -0,0 +1,25 @@
1
+ // Node test harness door: sign a test browser or fetch client in as a persona
2
+ // on a running local app. The harness presents the checkout's RER_DEV_KEY; the
3
+ // app answers 404 without it. Never import this from Worker code.
4
+ import { readFile } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+
7
+ export const DEV_KEY_FILE = '.rer/local/dev-key.env';
8
+ export async function readDevKey(root) {
9
+ const text = await readFile(join(root, DEV_KEY_FILE), 'utf8');
10
+ const key = /^RER_DEV_KEY=([A-Za-z0-9_-]{43,256})$/m.exec(text)?.[1];
11
+ if (!key) throw new Error(`${DEV_KEY_FILE} holds no RER_DEV_KEY. Run rer setup.`);
12
+ return key;
13
+ }
14
+
15
+ /** Returns the cookies to install ({ name, value }[]) and the redirect target. */
16
+ export async function signInAs({ origin, key, as = 'owner', returnTo }) {
17
+ const url = new URL('/auth/dev/enter', origin);
18
+ url.searchParams.set('as', as);
19
+ if (returnTo) url.searchParams.set('returnTo', returnTo);
20
+ const response = await fetch(url, { redirect: 'manual', headers: { 'x-rer-dev-key': key } });
21
+ await response.body?.cancel();
22
+ if (response.status !== 303) throw new Error(`Dev sign-in as ${as} answered HTTP ${response.status}.`);
23
+ const cookies = response.headers.getSetCookie().map(line => line.split(';')[0]).map(pair => ({ name: pair.slice(0, pair.indexOf('=')), value: pair.slice(pair.indexOf('=') + 1) }));
24
+ return { cookies, location: response.headers.get('location'), header: cookies.filter(c => c.value).map(c => `${c.name}=${c.value}`).join('; ') };
25
+ }
@@ -0,0 +1,4 @@
1
+ /** Registers the local key gate's check. Only the local-only dev layer calls it; a non-function clears it. */
2
+ export function registerDevAdmission(check: ((request: Request) => boolean | Promise<boolean>) | null | undefined): void;
3
+ /** True only when a registered check returns exactly true for this request; false with none registered or when it throws. */
4
+ export function devAdmitted(request: Request): Promise<boolean>;
@@ -0,0 +1,10 @@
1
+ // A dev session, and any credential minted while using one, counts only on a
2
+ // request that passes the local key gate. Only the local-only dev layer
3
+ // (./dev/) registers that check. A production Worker never loads the layer, so
4
+ // every dev credential is refused there.
5
+ let admission = null;
6
+ export function registerDevAdmission(check) { admission = typeof check === 'function' ? check : null; }
7
+ export async function devAdmitted(request) {
8
+ if (!admission) return false;
9
+ try { return (await admission(request)) === true; } catch { return false; }
10
+ }
@@ -0,0 +1,86 @@
1
+ import { constantEqual } from './crypto.mjs';
2
+ import { isLoopbackHost, localCookieName } from './http.mjs';
3
+
4
+ // The local key gate. `rer setup` writes a random RER_DEV_KEY per checkout and
5
+ // the local runtimes receive it as a binding; a deployed Worker never has one
6
+ // (`rer deploy` refuses it), so this gate is inert there. A tunnel or local
7
+ // reverse proxy makes remote requests look local, so the network is not the
8
+ // gate: holding the key is. Browsers trade it once for a host-only cookie; test
9
+ // harnesses and the rer command line present it in a header. The cookie is
10
+ // named for the origin's port (`rer_dev_gate_8787`), so checkouts side by side
11
+ // on one loopback host keep their own.
12
+ export const DEV_KEY_HEADER = 'x-rer-dev-key';
13
+ export const DEV_KEY_PATH = '/__dev/key';
14
+ const encoder = new TextEncoder();
15
+ const validKey = value => typeof value === 'string' && /^[A-Za-z0-9_-]{43,256}$/.test(value);
16
+
17
+ async function digest(text) {
18
+ const bytes = await crypto.subtle.digest('SHA-256', encoder.encode(text));
19
+ return btoa(String.fromCharCode(...new Uint8Array(bytes)));
20
+ }
21
+ async function cookieValue(key) {
22
+ const hmac = await crypto.subtle.importKey('raw', encoder.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
23
+ const bytes = await crypto.subtle.sign('HMAC', hmac, encoder.encode('rer-dev-gate\0cookie'));
24
+ return Array.from(new Uint8Array(bytes), b => b.toString(16).padStart(2, '0')).join('');
25
+ }
26
+
27
+ // Reads `#rer-dev=KEY` (with optional `as` or `next`), removes it from the
28
+ // address bar first, then trades it through a same-origin POST. The fragment
29
+ // never reaches a server; the key never appears in a query string.
30
+ export const EXCHANGE_SCRIPT = "(()=>{const h=new URLSearchParams(location.hash.slice(1)),k=h.get('rer-dev');if(!k)return;history.replaceState(null,'',location.pathname+location.search);const a=h.get('as'),n=h.get('next')||(a?'/auth/dev/enter?as='+encodeURIComponent(a):location.pathname+location.search),to=n.startsWith('/')&&!n.startsWith('//')&&!n.includes('\\\\')?n:'/';fetch('" + DEV_KEY_PATH + "',{method:'POST',credentials:'same-origin',headers:{'content-type':'application/json'},body:JSON.stringify({key:k})}).then(r=>{if(r.ok)location.replace(to);else document.body.insertAdjacentHTML('beforeend','<p role=\"alert\">This link no longer opens the app. Run rer dev again for a fresh one.</p>')})})()";
31
+ const PAGE_STYLE = 'body{margin:0;font:16px/1.5 system-ui,sans-serif;color:#17212e;background:#f5f7fa}main{max-width:36rem;margin:0 auto;padding:32px 16px}';
32
+ let hashes;
33
+ export async function exchangeHashes() {
34
+ return hashes ??= { script: `'sha256-${await digest(EXCHANGE_SCRIPT)}'`, style: `'sha256-${await digest(PAGE_STYLE)}'` };
35
+ }
36
+
37
+ const privateHeaders = { 'cache-control': 'no-store', 'referrer-policy': 'no-referrer', 'x-content-type-options': 'nosniff', 'x-robots-tag': 'noindex, nofollow' };
38
+ export function devNotFound() {
39
+ return new Response('Not found', { status: 404, headers: { ...privateHeaders, 'content-type': 'text/plain; charset=utf-8',
40
+ 'content-security-policy': "default-src 'none'; script-src 'none'; style-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; object-src 'none'" } });
41
+ }
42
+
43
+ function readGateCookie(request, name) {
44
+ const entries = (request.headers.get('cookie') || '').split(';').map(part => part.trim()).filter(part => part.startsWith(`${name}=`));
45
+ return entries.length === 1 ? entries[0].slice(name.length + 1) : null;
46
+ }
47
+
48
+ /**
49
+ * Returns the gate for a local runtime holding RER_DEV_KEY, or null. Null means
50
+ * every gated surface answers 404: the binding is missing, or the configured
51
+ * origin is not an explicit local loopback origin.
52
+ */
53
+ export function devGate({ key, origin, local }) {
54
+ if (local !== true || !validKey(key) || typeof origin !== 'string') return null;
55
+ let url; try { url = new URL(origin); } catch { return null; }
56
+ if (url.origin !== origin || url.username || url.password || !isLoopbackHost(url.hostname)) return null;
57
+ let expected;
58
+ const cookieName = localCookieName('rer_dev_gate', origin);
59
+ const cookie = () => expected ??= cookieValue(key);
60
+ const setCookie = async () => `${cookieName}=${await cookie()}; Path=/; HttpOnly; SameSite=Strict; Max-Age=2592000${url.protocol === 'https:' ? '; Secure' : ''}`;
61
+ return Object.freeze({
62
+ cookieName,
63
+ async admits(request) {
64
+ const presented = request.headers.get(DEV_KEY_HEADER);
65
+ if (presented !== null) return constantEqual(presented, key);
66
+ const value = readGateCookie(request, cookieName);
67
+ return value !== null && constantEqual(value, await cookie());
68
+ },
69
+ setCookie,
70
+ // GET serves the exchange page; POST trades the key for the cookie.
71
+ async exchange(request) {
72
+ if (new URL(request.url).pathname !== DEV_KEY_PATH) return null;
73
+ if (request.method === 'GET' || request.method === 'HEAD') {
74
+ const { script, style } = await exchangeHashes();
75
+ const body = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex, nofollow"><title>Opening the local app</title><style>${PAGE_STYLE}</style></head><body><main><h1>Opening the local app</h1><p>Open the RER_OPEN link that rer prints on this computer.</p></main><script>${EXCHANGE_SCRIPT}</script></body></html>`;
76
+ return new Response(request.method === 'HEAD' ? null : body, { headers: { ...privateHeaders, 'content-type': 'text/html; charset=utf-8',
77
+ 'content-security-policy': `default-src 'none'; script-src ${script}; style-src ${style}; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'` } });
78
+ }
79
+ if (request.method !== 'POST' || request.headers.get('sec-fetch-site') === 'cross-site' || !request.headers.get('content-type')?.startsWith('application/json')) return devNotFound();
80
+ const text = await request.text();
81
+ let presented; try { presented = text.length <= 1024 ? JSON.parse(text)?.key : undefined; } catch {}
82
+ if (typeof presented !== 'string' || !constantEqual(presented, key)) return devNotFound();
83
+ return new Response(null, { status: 204, headers: { ...privateHeaders, 'set-cookie': await setCookie() } });
84
+ },
85
+ });
86
+ }
@@ -0,0 +1,78 @@
1
+ # Adopting operator auth
2
+
3
+ This module is server-only. Copy `modules/operator-auth/` into the product source,
4
+ import its server entry from server-owned files, and apply
5
+ `accounts/schema/operator-auth.sql` as a new numbered migration on the product's primary
6
+ D1 database. The integrator assigns the next migration number. Keep applied
7
+ migrations unchanged, plan an explicit existing-operator import for populated
8
+ systems, and never treat applying the schema as creating an owner or product data.
9
+ When upgrading an existing auth store, apply the separate additive
10
+ `accounts/schema/invitation-cancellation.sql` before enabling pending-invitation cancellation;
11
+ do not replay the complete schema. Its ledger is durable admission state, not
12
+ credential cleanup data. See the [migration and cancellation contract](../README.md).
13
+
14
+ Use the standard Worker secrets `OWNER_EMAIL`, `SESSION_SECRET`, and
15
+ `SETUP_TOKEN`. Generate independent random values of at least 32 bytes for the
16
+ two secrets; retain only `OWNER_EMAIL` after first-owner setup and remove
17
+ `SETUP_TOKEN` after bootstrap. `SESSION_SECRET` must remain stable for sessions.
18
+ They belong in server bindings, never `VITE_*`, rendered HTML, loaders, browser
19
+ storage, logs or responses. Bootstrap creates only the configured owner and its
20
+ session. It runs without mail and does not populate product tables. Invoke the
21
+ one-time `POST /auth/bootstrap` from an owner-controlled setup operation that
22
+ keeps `SETUP_TOKEN` server-side; never add the token to a frontend form or
23
+ client-side request payload.
24
+
25
+ For sign-in and invitations, configure the actual IF-056 sender through
26
+ `MAIL_PROVIDER`, `MAIL_API_KEY`, optional `MAIL_ACCOUNT_ID` for Cloudflare, and
27
+ `MAIL_FROM`. In server code, construct the sender with the real
28
+ `createMailSender({ provider, mode: 'production', apiKey, accountId })` from the
29
+ selected `modules/mail/index.mjs` and pass that object to `createOperatorAuth`.
30
+ Do not add another sender or select a fallback provider. Auth accepts only an
31
+ IF-056 result with `status: 'accepted'` (or the explicit local-only development
32
+ result); `rejected`, `rate_limited`, and `uncertain` revoke that link and return
33
+ `503 mail_unavailable`. An accepted provider submission is not proof that a
34
+ mailbox received the message. Issue a new link after a failed or uncertain
35
+ attempt; the auth core does not retry it.
36
+
37
+ `OWNER_EMAIL` is the first owner. The default roles are `owner`, `admin`, and
38
+ `member`. Configure `allowList` only as an additional admission restriction; it
39
+ does not grant access. Use the exported `rolePolicy` for deterministic product
40
+ policy variations while preserving the module's active-session, allow-list,
41
+ version-fence and last-owner checks.
42
+
43
+ Follow one of these integration recipes:
44
+
45
+ - [TanStack Start](./tanstack-start.md): keep the module and bindings in dynamic
46
+ server imports, check every private server function, and mount the auth HTTP
47
+ handler before protected Hono routes and Start.
48
+ - [Vite SPA](./vite-spa.md): keep auth, D1 and secrets in the Worker API; the SPA
49
+ uses same-origin auth and private APIs and never imports this module.
50
+
51
+ For first-owner, confirmation, role, revocation, rate-limit and mail-result
52
+ coverage, register the shared suite once from the seed's existing test file. See
53
+ [`../conformance/README.md`](../conformance/README.md) and its exact adapter
54
+ types. The adapter must exercise the adopting Worker and real D1, and the private
55
+ request must traverse the product's actual session gate.
56
+
57
+ ## Identity providers, the sign-in page and local review
58
+
59
+ Mount `createSignInHandler(auth)` at the seed's sign-in path ahead of the app's
60
+ own routes; it replaces a seed's own sign-in page once the seed has checked its
61
+ `ACCEPTANCE.md`. Offer providers through `config.providers` (each with a preset:
62
+ `auth`, `google`, `microsoft` or `okta`) plus an independent
63
+ `OIDC_TRANSACTION_SECRET`, pass the runtime's `fetch`, apply
64
+ `identity/oidc/schema/oidc.sql`, and keep client secrets and the app's private
65
+ key in Worker bindings. `createAuthHandler(auth)` then also serves each
66
+ provider's `/auth/oidc/<id>/start` and `/auth/oidc/<id>/callback`; preserve all
67
+ `Set-Cookie` headers individually. See [identity/oidc](../identity/oidc/README.md)
68
+ for presets, pinning, email trust and reachability, and the module README for
69
+ domain rules, step-up and the `beforeMagicLink`/`afterConfirm` hooks. Microsoft
70
+ and Okta identify people only through `subjectEmails`, configured from trusted
71
+ server configuration, never callback input. The provider-key-free conformance
72
+ case checks only a first binding into an unbound active owner; the fake-provider
73
+ tests cover the protocol without live provider credentials.
74
+
75
+ `review` is a local-only mechanism. Keep `review.enabled` false in production;
76
+ bind a local review server to a loopback interface; use literal `localhost`,
77
+ `127.0.0.1`, or `[::1]` with a matching Host and exact Origin on POST. The core
78
+ does not trust forwarded headers. A matching Host is not network isolation.