@runeditrun/rer 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (527) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -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 +182 -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 +158 -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 +117 -0
  71. package/global/launch/facts.mjs +94 -0
  72. package/global/launch/failure.mjs +5 -0
  73. package/global/launch/local-defaults.mjs +4 -0
  74. package/global/launch/prepare.mjs +63 -0
  75. package/global/launch/render.mjs +44 -0
  76. package/global/launch/state.mjs +106 -0
  77. package/global/launch/steps.mjs +90 -0
  78. package/global/launch/stop.mjs +89 -0
  79. package/global/launch/worker.mjs +62 -0
  80. package/global/list.mjs +59 -0
  81. package/global/main.mjs +53 -0
  82. package/global/package-info.mjs +38 -0
  83. package/global/registry.mjs +24 -0
  84. package/global/rer.mjs +29 -0
  85. package/global/update/archive.mjs +89 -0
  86. package/global/update/command.mjs +90 -0
  87. package/global/update/latest.mjs +6 -0
  88. package/global/update/notice.mjs +58 -0
  89. package/global/update/registry.mjs +47 -0
  90. package/global/version.mjs +29 -0
  91. package/install.sh +144 -0
  92. package/modules/ai/LICENSE +21 -0
  93. package/modules/ai/README.md +90 -0
  94. package/modules/ai/client.mjs +71 -0
  95. package/modules/ai/config.mjs +39 -0
  96. package/modules/ai/dev/index.d.mts +6 -0
  97. package/modules/ai/dev/index.mjs +64 -0
  98. package/modules/ai/dev/outbox.mjs +33 -0
  99. package/modules/ai/errors.mjs +28 -0
  100. package/modules/ai/fake/index.d.mts +3 -0
  101. package/modules/ai/fake/index.mjs +82 -0
  102. package/modules/ai/index.d.mts +10 -0
  103. package/modules/ai/index.mjs +3 -0
  104. package/modules/ai/package.json +6 -0
  105. package/modules/container/README.md +156 -0
  106. package/modules/container/adapters/d1.mjs +34 -0
  107. package/modules/container/adapters/egress.mjs +40 -0
  108. package/modules/container/adapters/jobs.mjs +47 -0
  109. package/modules/container/adapters/objects.mjs +84 -0
  110. package/modules/container/adapters/retained.mjs +14 -0
  111. package/modules/container/adapters/sqlite-error.mjs +7 -0
  112. package/modules/container/adapters/sqlite-worker.mjs +26 -0
  113. package/modules/container/assets.mjs +14 -0
  114. package/modules/container/bindings.mjs +1 -0
  115. package/modules/container/boundary.mjs +38 -0
  116. package/modules/container/build.mjs +65 -0
  117. package/modules/container/deploy-input.mjs +17 -0
  118. package/modules/container/entrypoint.sh +5 -0
  119. package/modules/container/host.mjs +43 -0
  120. package/modules/container/ingress.mjs +23 -0
  121. package/modules/container/lock.mjs +11 -0
  122. package/modules/container/migrations.mjs +16 -0
  123. package/modules/container/node-engine.mjs +39 -0
  124. package/modules/container/portability-schema.mjs +14 -0
  125. package/modules/container/remote/deploy.py +76 -0
  126. package/modules/container/remote/files.py +275 -0
  127. package/modules/container/remote/resources.py +157 -0
  128. package/modules/container/runner.mjs +10 -0
  129. package/modules/container/ssh-plan.mjs +44 -0
  130. package/modules/container/ssh.mjs +63 -0
  131. package/modules/container/transfer.mjs +27 -0
  132. package/modules/container/worker-engine.mjs +32 -0
  133. package/modules/dependencies/README.md +211 -0
  134. package/modules/dependencies/approval.mjs +51 -0
  135. package/modules/dependencies/auth.mjs +40 -0
  136. package/modules/dependencies/authority.mjs +14 -0
  137. package/modules/dependencies/crypto.mjs +13 -0
  138. package/modules/dependencies/index.d.mts +23 -0
  139. package/modules/dependencies/index.mjs +113 -0
  140. package/modules/dependencies/logout.mjs +42 -0
  141. package/modules/dependencies/native.mjs +9 -0
  142. package/modules/dependencies/protocol.mjs +23 -0
  143. package/modules/dependencies/schema/approval-attempt.sql +3 -0
  144. package/modules/dependencies/schema/dependencies.sql +15 -0
  145. package/modules/dependencies/store.mjs +19 -0
  146. package/modules/deploy-kit/README.md +308 -0
  147. package/modules/deploy-kit/artifact.mjs +47 -0
  148. package/modules/deploy-kit/build-modules.mjs +21 -0
  149. package/modules/deploy-kit/check.mjs +13 -0
  150. package/modules/deploy-kit/cli.mjs +50 -0
  151. package/modules/deploy-kit/container-config.mjs +8 -0
  152. package/modules/deploy-kit/default-stage.mjs +14 -0
  153. package/modules/deploy-kit/deploy.mjs +80 -0
  154. package/modules/deploy-kit/health-request.mjs +86 -0
  155. package/modules/deploy-kit/health.mjs +99 -0
  156. package/modules/deploy-kit/index.mjs +12 -0
  157. package/modules/deploy-kit/local-only.mjs +10 -0
  158. package/modules/deploy-kit/migrations.mjs +48 -0
  159. package/modules/deploy-kit/owner-cli.mjs +63 -0
  160. package/modules/deploy-kit/owner.mjs +297 -0
  161. package/modules/deploy-kit/prepare.mjs +62 -0
  162. package/modules/deploy-kit/provider.mjs +99 -0
  163. package/modules/deploy-kit/queue-consumer.mjs +10 -0
  164. package/modules/deploy-kit/receipt.mjs +16 -0
  165. package/modules/deploy-kit/render.mjs +47 -0
  166. package/modules/deploy-kit/resources.mjs +106 -0
  167. package/modules/deploy-kit/service-grants.mjs +25 -0
  168. package/modules/deploy-kit/sql-safety.mjs +94 -0
  169. package/modules/deploy-kit/sql-tokens.mjs +49 -0
  170. package/modules/deploy-kit/teardown.mjs +100 -0
  171. package/modules/deploy-kit/templates.mjs +31 -0
  172. package/modules/deploy-kit/validation.mjs +58 -0
  173. package/modules/deploy-kit/worker.mjs +105 -0
  174. package/modules/erasure/CONTRACT.md +90 -0
  175. package/modules/erasure/README.md +221 -0
  176. package/modules/erasure/audit.mjs +23 -0
  177. package/modules/erasure/blobs.mjs +45 -0
  178. package/modules/erasure/cascade.mjs +91 -0
  179. package/modules/erasure/completion.mjs +62 -0
  180. package/modules/erasure/declaration.mjs +51 -0
  181. package/modules/erasure/fence.mjs +19 -0
  182. package/modules/erasure/index.d.mts +62 -0
  183. package/modules/erasure/index.mjs +85 -0
  184. package/modules/erasure/order.mjs +56 -0
  185. package/modules/erasure/plan.mjs +121 -0
  186. package/modules/erasure/rate.mjs +57 -0
  187. package/modules/erasure/receipt.mjs +32 -0
  188. package/modules/erasure/root.mjs +21 -0
  189. package/modules/erasure/schema/erasure-rates.sql +5 -0
  190. package/modules/erasure/schema/erasure.sql +27 -0
  191. package/modules/erasure/scope.mjs +29 -0
  192. package/modules/erasure/sql.mjs +23 -0
  193. package/modules/erasure/verification.mjs +69 -0
  194. package/modules/integrations/core/CONTRACT.md +50 -0
  195. package/modules/integrations/core/README.md +27 -0
  196. package/modules/integrations/core/crypto.ts +19 -0
  197. package/modules/integrations/core/database.ts +21 -0
  198. package/modules/integrations/core/delivery.ts +97 -0
  199. package/modules/integrations/core/destination.ts +13 -0
  200. package/modules/integrations/core/index.ts +7 -0
  201. package/modules/integrations/core/node-transport.ts +72 -0
  202. package/modules/integrations/core/public-address.ts +57 -0
  203. package/modules/integrations/core/repository.ts +74 -0
  204. package/modules/integrations/core/schema.sql +36 -0
  205. package/modules/integrations/core/sqlite.ts +20 -0
  206. package/modules/integrations/core/timestamped-body-signature.ts +38 -0
  207. package/modules/integrations/core/tokens.ts +71 -0
  208. package/modules/integrations/core/types.ts +52 -0
  209. package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
  210. package/modules/integrations/core/webhook.ts +73 -0
  211. package/modules/job-queue/README.md +173 -0
  212. package/modules/job-queue/cli.mjs +64 -0
  213. package/modules/job-queue/command.mjs +64 -0
  214. package/modules/job-queue/config.mjs +48 -0
  215. package/modules/job-queue/identity.mjs +48 -0
  216. package/modules/job-queue/index.mjs +135 -0
  217. package/modules/job-queue/membership.mjs +30 -0
  218. package/modules/job-queue/module.json +10 -0
  219. package/modules/job-queue/policy.mjs +29 -0
  220. package/modules/job-queue/processes.mjs +80 -0
  221. package/modules/job-queue/resources.mjs +37 -0
  222. package/modules/job-queue/storage.mjs +64 -0
  223. package/modules/job-queue/supervisor.mjs +30 -0
  224. package/modules/job-queue/waiting.mjs +16 -0
  225. package/modules/mail/LICENSE +21 -0
  226. package/modules/mail/README.md +336 -0
  227. package/modules/mail/doctor.mjs +81 -0
  228. package/modules/mail/index.mjs +8 -0
  229. package/modules/mail/live-proof/README.md +113 -0
  230. package/modules/mail/live-proof/run.mjs +55 -0
  231. package/modules/mail/package.json +19 -0
  232. package/modules/mail/pnpm-lock.yaml +537 -0
  233. package/modules/mail/src/adapters/cloudflare/README.md +84 -0
  234. package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
  235. package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
  236. package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
  237. package/modules/mail/src/adapters/messaging/README.md +92 -0
  238. package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
  239. package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
  240. package/modules/mail/src/adapters/messaging/index.mjs +61 -0
  241. package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
  242. package/modules/mail/src/adapters/messaging/request.mjs +52 -0
  243. package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
  244. package/modules/mail/src/adapters/resend/README.md +120 -0
  245. package/modules/mail/src/adapters/resend/index.mjs +22 -0
  246. package/modules/mail/src/adapters/resend/response.mjs +54 -0
  247. package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
  248. package/modules/mail/src/adapters/resend/size.mjs +99 -0
  249. package/modules/mail/src/adapters/sendgrid/README.md +75 -0
  250. package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
  251. package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
  252. package/modules/mail/src/core/errors.mjs +9 -0
  253. package/modules/mail/src/core/transport.mjs +33 -0
  254. package/modules/mail/src/core/validation.mjs +172 -0
  255. package/modules/mail/src/development/index.d.mts +37 -0
  256. package/modules/mail/src/development/index.mjs +35 -0
  257. package/modules/mail/src/development/outbox.mjs +78 -0
  258. package/modules/mail/src/development/schema.sql +9 -0
  259. package/modules/mail/src/development/viewer-html.mjs +42 -0
  260. package/modules/mail/src/development/viewer.mjs +65 -0
  261. package/modules/mail/src/doctor/README.md +107 -0
  262. package/modules/mail/src/doctor/common.mjs +64 -0
  263. package/modules/mail/src/doctor/dns.mjs +102 -0
  264. package/modules/mail/src/doctor/index.mjs +28 -0
  265. package/modules/mail/src/doctor/probe.mjs +66 -0
  266. package/modules/mail/src/doctor/receive.mjs +81 -0
  267. package/modules/mail/src/doctor/records.mjs +45 -0
  268. package/modules/mail/src/events/README.md +268 -0
  269. package/modules/mail/src/events/d1-schema.sql +25 -0
  270. package/modules/mail/src/events/d1-storage.mjs +76 -0
  271. package/modules/mail/src/events/index.mjs +3 -0
  272. package/modules/mail/src/events/intake.mjs +201 -0
  273. package/modules/mail/src/events/storage.mjs +52 -0
  274. package/modules/mail/src/events/verify.mjs +92 -0
  275. package/modules/mail/src/inbound/README.md +137 -0
  276. package/modules/mail/src/inbound/bytes.mjs +91 -0
  277. package/modules/mail/src/inbound/headers.mjs +143 -0
  278. package/modules/mail/src/inbound/index.mjs +29 -0
  279. package/modules/mail/src/inbound/mime.mjs +36 -0
  280. package/modules/mail/src/inbound/multipart.mjs +27 -0
  281. package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
  282. package/modules/mail/src/inbound/resend.mjs +54 -0
  283. package/modules/mail/src/inbound/sendgrid.mjs +60 -0
  284. package/modules/mail/src/sender.mjs +33 -0
  285. package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
  286. package/modules/mail/src/sendgrid-email/index.mjs +86 -0
  287. package/modules/mail/src/unsubscribe/index.mjs +142 -0
  288. package/modules/operator-auth/README.md +599 -0
  289. package/modules/operator-auth/accounts/adapter.mjs +41 -0
  290. package/modules/operator-auth/accounts/admin.mjs +159 -0
  291. package/modules/operator-auth/accounts/credentials.mjs +27 -0
  292. package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
  293. package/modules/operator-auth/accounts/index.d.mts +9 -0
  294. package/modules/operator-auth/accounts/index.mjs +9 -0
  295. package/modules/operator-auth/accounts/native.d.mts +38 -0
  296. package/modules/operator-auth/accounts/native.mjs +171 -0
  297. package/modules/operator-auth/accounts/oa.mjs +90 -0
  298. package/modules/operator-auth/accounts/provider-control.mjs +50 -0
  299. package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
  300. package/modules/operator-auth/accounts/roles.mjs +26 -0
  301. package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
  302. package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
  303. package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
  304. package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
  305. package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
  306. package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
  307. package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
  308. package/modules/operator-auth/accounts/settings-page.mjs +4 -0
  309. package/modules/operator-auth/accounts/store.mjs +246 -0
  310. package/modules/operator-auth/branding.mjs +66 -0
  311. package/modules/operator-auth/config.mjs +35 -0
  312. package/modules/operator-auth/conformance/README.md +81 -0
  313. package/modules/operator-auth/conformance/index.d.mts +43 -0
  314. package/modules/operator-auth/conformance/index.mjs +300 -0
  315. package/modules/operator-auth/conformance/presentation.mjs +57 -0
  316. package/modules/operator-auth/crypto.mjs +17 -0
  317. package/modules/operator-auth/dev/badge.mjs +62 -0
  318. package/modules/operator-auth/dev/index.d.mts +14 -0
  319. package/modules/operator-auth/dev/index.mjs +137 -0
  320. package/modules/operator-auth/dev/personas.mjs +17 -0
  321. package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
  322. package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
  323. package/modules/operator-auth/dev-admission.d.mts +4 -0
  324. package/modules/operator-auth/dev-admission.mjs +10 -0
  325. package/modules/operator-auth/dev-gate.mjs +86 -0
  326. package/modules/operator-auth/docs/README.md +78 -0
  327. package/modules/operator-auth/docs/native-accounts.md +313 -0
  328. package/modules/operator-auth/docs/tanstack-start.md +218 -0
  329. package/modules/operator-auth/docs/vite-spa.md +144 -0
  330. package/modules/operator-auth/errors.mjs +9 -0
  331. package/modules/operator-auth/http.mjs +42 -0
  332. package/modules/operator-auth/identity/admission.mjs +105 -0
  333. package/modules/operator-auth/identity/after-confirm.mjs +18 -0
  334. package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
  335. package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
  336. package/modules/operator-auth/identity/email-link/index.mjs +128 -0
  337. package/modules/operator-auth/identity/email-link/message.mjs +16 -0
  338. package/modules/operator-auth/identity/index.d.mts +7 -0
  339. package/modules/operator-auth/identity/index.mjs +5 -0
  340. package/modules/operator-auth/identity/oidc/README.md +171 -0
  341. package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
  342. package/modules/operator-auth/identity/oidc/config.mjs +102 -0
  343. package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
  344. package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
  345. package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
  346. package/modules/operator-auth/identity/oidc/index.mjs +287 -0
  347. package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
  348. package/modules/operator-auth/identity/oidc/network.mjs +93 -0
  349. package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
  350. package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
  351. package/modules/operator-auth/identity/oidc/store.mjs +68 -0
  352. package/modules/operator-auth/identity/principal.mjs +28 -0
  353. package/modules/operator-auth/index.d.mts +223 -0
  354. package/modules/operator-auth/index.mjs +11 -0
  355. package/modules/operator-auth/input.mjs +55 -0
  356. package/modules/operator-auth/local-cli.mjs +16 -0
  357. package/modules/operator-auth/page.mjs +34 -0
  358. package/modules/operator-auth/policy.mjs +20 -0
  359. package/modules/operator-auth/routes.mjs +91 -0
  360. package/modules/operator-auth/service.mjs +152 -0
  361. package/modules/operator-auth/signin/index.mjs +67 -0
  362. package/modules/owner-claim/README.md +137 -0
  363. package/modules/owner-claim/idp.mjs +40 -0
  364. package/modules/owner-claim/index.d.mts +27 -0
  365. package/modules/owner-claim/index.mjs +39 -0
  366. package/modules/owner-claim/page.mjs +11 -0
  367. package/modules/owner-claim/schema/idp.sql +4 -0
  368. package/modules/owner-claim/schema/owner-claim.sql +18 -0
  369. package/modules/owner-claim/transaction.mjs +87 -0
  370. package/modules/owner-claim/verify.mjs +80 -0
  371. package/modules/portability/README.md +444 -0
  372. package/modules/portability/bridge.mjs +102 -0
  373. package/modules/portability/catalogue.mjs +118 -0
  374. package/modules/portability/cli-options.mjs +38 -0
  375. package/modules/portability/cli.mjs +56 -0
  376. package/modules/portability/copies.mjs +68 -0
  377. package/modules/portability/dev-data.mjs +32 -0
  378. package/modules/portability/diff.mjs +33 -0
  379. package/modules/portability/export.mjs +129 -0
  380. package/modules/portability/files.mjs +65 -0
  381. package/modules/portability/format.mjs +122 -0
  382. package/modules/portability/generated.mjs +60 -0
  383. package/modules/portability/hash.mjs +79 -0
  384. package/modules/portability/http.mjs +42 -0
  385. package/modules/portability/import.mjs +185 -0
  386. package/modules/portability/index.d.mts +58 -0
  387. package/modules/portability/index.mjs +22 -0
  388. package/modules/portability/journal.mjs +49 -0
  389. package/modules/portability/legacy-files.mjs +43 -0
  390. package/modules/portability/legacy-safety.mjs +27 -0
  391. package/modules/portability/lines.mjs +36 -0
  392. package/modules/portability/migration-history.mjs +18 -0
  393. package/modules/portability/migration-statements.mjs +23 -0
  394. package/modules/portability/payloads.mjs +52 -0
  395. package/modules/portability/provider-data.mjs +25 -0
  396. package/modules/portability/restore-safety.mjs +30 -0
  397. package/modules/portability/schema/portability.sql +25 -0
  398. package/modules/portability/storage.mjs +49 -0
  399. package/modules/release/README.md +212 -0
  400. package/modules/release/catalogue.mjs +79 -0
  401. package/modules/release/export.mjs +51 -0
  402. package/modules/release/produce.mjs +136 -0
  403. package/modules/release/public-child-transport-state.cjs +29 -0
  404. package/modules/release/public-child.mjs +71 -0
  405. package/modules/release/public-git.mjs +21 -0
  406. package/modules/release/publication-scan.mjs +22 -0
  407. package/modules/release/publish-handoff.mjs +37 -0
  408. package/modules/release/source.mjs +71 -0
  409. package/modules/release/test-summary.mjs +50 -0
  410. package/onboarding/README.md +163 -0
  411. package/onboarding/activity.mjs +31 -0
  412. package/onboarding/adapter-auth.mjs +88 -0
  413. package/onboarding/adapter-hosting.mjs +35 -0
  414. package/onboarding/adapter-labels.mjs +13 -0
  415. package/onboarding/adapter-settings.mjs +129 -0
  416. package/onboarding/adapter.mjs +238 -0
  417. package/onboarding/agent-handoff.mjs +9 -0
  418. package/onboarding/app-processes.mjs +21 -0
  419. package/onboarding/archive-output.d.mts +3 -0
  420. package/onboarding/archive-output.mjs +54 -0
  421. package/onboarding/command-custody.mjs +24 -0
  422. package/onboarding/command-recovery.mjs +8 -0
  423. package/onboarding/commands.mjs +166 -0
  424. package/onboarding/dependency-approval.mjs +36 -0
  425. package/onboarding/dev-identity.mjs +23 -0
  426. package/onboarding/email.mjs +24 -0
  427. package/onboarding/failures.mjs +42 -0
  428. package/onboarding/input-limits.mjs +7 -0
  429. package/onboarding/installer-inputs.mjs +24 -0
  430. package/onboarding/launcher.mjs +34 -0
  431. package/onboarding/local/build-files.mjs +64 -0
  432. package/onboarding/local/build.mjs +49 -0
  433. package/onboarding/local/environment.mjs +30 -0
  434. package/onboarding/local/files.mjs +35 -0
  435. package/onboarding/local/operation.mjs +166 -0
  436. package/onboarding/local/owner.mjs +34 -0
  437. package/onboarding/local/process-table.mjs +114 -0
  438. package/onboarding/local/recovery.mjs +79 -0
  439. package/onboarding/local/state.mjs +31 -0
  440. package/onboarding/metadata.mjs +7 -0
  441. package/onboarding/operator-install.mjs +45 -0
  442. package/onboarding/operator-installer.mjs +39 -0
  443. package/onboarding/operator-prerequisites.mjs +27 -0
  444. package/onboarding/package.json +6 -0
  445. package/onboarding/prefill.mjs +16 -0
  446. package/onboarding/private-files.mjs +97 -0
  447. package/onboarding/private.mjs +35 -0
  448. package/onboarding/process-cleanup.mjs +134 -0
  449. package/onboarding/process-diagnostics.mjs +91 -0
  450. package/onboarding/process-scope.mjs +118 -0
  451. package/onboarding/process-table.mjs +118 -0
  452. package/onboarding/process-tree.mjs +36 -0
  453. package/onboarding/production-modules.mjs +22 -0
  454. package/onboarding/production-options.mjs +40 -0
  455. package/onboarding/production-plan.mjs +8 -0
  456. package/onboarding/production-runtime.mjs +30 -0
  457. package/onboarding/project-vite.mjs +14 -0
  458. package/onboarding/providers.mjs +289 -0
  459. package/onboarding/queue.mjs +53 -0
  460. package/onboarding/queued-entry.mjs +34 -0
  461. package/onboarding/readiness.mjs +59 -0
  462. package/onboarding/redaction-jwt.mjs +32 -0
  463. package/onboarding/redaction.mjs +298 -0
  464. package/onboarding/schema/SETUP.schema.json +514 -0
  465. package/onboarding/schema/dependencies.mjs +35 -0
  466. package/onboarding/schema/json-schema.mjs +202 -0
  467. package/onboarding/schema/setup-validation.mjs +325 -0
  468. package/onboarding/secrets.mjs +21 -0
  469. package/onboarding/server.mjs +188 -0
  470. package/onboarding/setup-manifest.mjs +116 -0
  471. package/onboarding/state.mjs +46 -0
  472. package/onboarding/theme-files.mjs +37 -0
  473. package/onboarding/theme.mjs +118 -0
  474. package/onboarding/ui/activity.js +98 -0
  475. package/onboarding/ui/address-client.mjs +33 -0
  476. package/onboarding/ui/app.js +377 -0
  477. package/onboarding/ui/index.html +1 -0
  478. package/onboarding/ui/model-core.mjs +127 -0
  479. package/onboarding/ui/model.mjs +5 -0
  480. package/onboarding/ui/style.css +63 -0
  481. package/onboarding/ui/theme.css +37 -0
  482. package/onboarding/ui/workspace-art.js +30 -0
  483. package/onboarding/wrangler-auth.mjs +43 -0
  484. package/operations/README.md +246 -0
  485. package/operations/operations.mjs +168 -0
  486. package/operations/project-operations.mjs +157 -0
  487. package/package.json +11 -4
  488. package/scripts/publish-check/README.md +72 -0
  489. package/scripts/publish-check/checksum_maps.py +52 -0
  490. package/scripts/publish-check/cli.py +34 -0
  491. package/scripts/publish-check/command_arguments.py +51 -0
  492. package/scripts/publish-check/command_shell_quotes.py +41 -0
  493. package/scripts/publish-check/command_source_quotes.py +120 -0
  494. package/scripts/publish-check/content_identities.py +250 -0
  495. package/scripts/publish-check/copy_identities.py +41 -0
  496. package/scripts/publish-check/der.py +61 -0
  497. package/scripts/publish-check/destination.py +117 -0
  498. package/scripts/publish-check/env_values.py +74 -0
  499. package/scripts/publish-check/font_licence.py +23 -0
  500. package/scripts/publish-check/git_history.py +115 -0
  501. package/scripts/publish-check/github_refs.py +217 -0
  502. package/scripts/publish-check/gitleaks.py +72 -0
  503. package/scripts/publish-check/gitleaks.toml +40 -0
  504. package/scripts/publish-check/history_scan.py +176 -0
  505. package/scripts/publish-check/hit_location.py +18 -0
  506. package/scripts/publish-check/hygiene_config.py +42 -0
  507. package/scripts/publish-check/hygiene_text.py +11 -0
  508. package/scripts/publish-check/identity-recipe.mjs +54 -0
  509. package/scripts/publish-check/json_identity.py +79 -0
  510. package/scripts/publish-check/license-exceptions.json +11 -0
  511. package/scripts/publish-check/licenses.py +49 -0
  512. package/scripts/publish-check/policy.py +18 -0
  513. package/scripts/publish-check/project_identity.py +89 -0
  514. package/scripts/publish-check/provenance.py +91 -0
  515. package/scripts/publish-check/provenance_pins.py +33 -0
  516. package/scripts/publish-check/public_base.py +63 -0
  517. package/scripts/publish-check/public_hygiene.py +80 -0
  518. package/scripts/publish-check/publication-config.json +12 -0
  519. package/scripts/publish-check/publication_config.py +34 -0
  520. package/scripts/publish-check/report.py +52 -0
  521. package/scripts/publish-check/repository_commands.py +75 -0
  522. package/scripts/publish-check/repository_intent.py +52 -0
  523. package/scripts/publish-check/reviewed.py +41 -0
  524. package/scripts/publish-check/source_hashes.mjs +6 -0
  525. package/scripts/publish-check/tree_scan.py +88 -0
  526. package/scripts/publish-check/trust_bundle.py +87 -0
  527. package/scripts/publish-check.mjs +58 -0
@@ -0,0 +1,599 @@
1
+ # Operator auth (IF-055-CORE-v1)
2
+
3
+ Portable server-only ESM: import `createOperatorAuth`, `createAuthHandler` and
4
+ `createSignInHandler` from `modules/operator-auth/index.mjs`. TypeScript
5
+ declarations are `index.d.mts`. No framework or mail transport is bundled. Apply
6
+ `accounts/schema/operator-auth.sql` once for a fresh auth database (and
7
+ `identity/oidc/schema/oidc.sql` when the app offers identity providers). Preserve applied migration history in an existing
8
+ store and follow “Adopting this version” below for its additive upgrade. Tables
9
+ use the `oa_` namespace. This module does not seed any product data.
10
+
11
+ ```js
12
+ import { createOperatorAuth, createAuthHandler } from './modules/operator-auth/index.mjs';
13
+ import { createMailSender } from './modules/mail/index.mjs';
14
+ // Secrets are Worker secret bindings, never source text.
15
+ const { SESSION_SECRET, SETUP_TOKEN, MAIL_API_KEY: apiKey } = env;
16
+ const auth = createOperatorAuth({
17
+ db: env.DB,
18
+ config: {
19
+ origin: 'https://app.example.com', OWNER_EMAIL: env.OWNER_EMAIL,
20
+ SESSION_SECRET, SETUP_TOKEN,
21
+ MAIL_FROM: env.MAIL_FROM,
22
+ // Optional; omit for neutral workspace wording and /signin recovery.
23
+ branding: { productName: 'Example', accentColor: '#334155', signInPath: '/signin' },
24
+ // Optional explicit address list further restricts active operators.
25
+ // allowList: ['owner@example.com', 'staff@example.com'],
26
+ },
27
+ mailSender: createMailSender({ provider: env.MAIL_PROVIDER, mode: 'production', apiKey,
28
+ ...(env.MAIL_PROVIDER === 'cloudflare' ? { accountId: env.MAIL_ACCOUNT_ID } : {}) }),
29
+ // Use only infrastructure-authenticated client addresses. In Cloudflare's
30
+ // public Worker boundary CF-Connecting-IP is edge-owned. Do not reuse this
31
+ // resolver behind a proxy which lets callers supply that header.
32
+ clientIp: request => request.headers.get('CF-Connecting-IP') ?? '',
33
+ });
34
+ const response = await createAuthHandler(auth)(request);
35
+ // response === null means another dispatcher owns this path.
36
+ ```
37
+
38
+ For first setup omit `mailSender` and `MAIL_FROM` entirely. Provision independent
39
+ random `SESSION_SECRET` and `SETUP_TOKEN` secrets (at least 32 characters; generate
40
+ 32 random bytes or more). POST `/auth/bootstrap` with `{token,displayName,returnTo}`
41
+ creates the configured owner and session atomically only on an empty auth store.
42
+ Remove SETUP_TOKEN after bootstrap. Changing it never resets the bootstrap ledger.
43
+ No owner is automatically provisioned when requesting a link or invoking OIDC.
44
+
45
+ All public mutation APIs require POST, the exact configured request origin and
46
+ `Origin` header, and reject cross-site Fetch Metadata. Never derive `origin` from
47
+ request headers. `Host`, when supplied, must match the configured authority; local
48
+ HTTP additionally requires an explicit matching loopback Host. Browser forms and
49
+ JSON are accepted by the handler (8 KiB maximum). Parsed query/form parameters
50
+ with duplicate decoded names or JSON object keys are rejected with `invalid_input` before
51
+ auth processing, including escaped names. POST tokens are accepted only in the
52
+ body; a query token on a POST is rejected as an ambiguous credential source.
53
+ Protect every private API, server function and document with
54
+ `auth.getSession(request)`. Missing/invalid,
55
+ expired, revoked, deactivated and disallowed sessions yield null; host violations
56
+ throw `AuthError` with 403. Never return a private page then check in the browser.
57
+
58
+ ## Layers
59
+
60
+ Base's operator auth is the only staff sign-in, in three layers:
61
+
62
+ | Directory | Holds | Interface |
63
+ | --- | --- | --- |
64
+ | `identity/` | Proving who a person is: the email link (`identity/email-link/`), identity providers over OpenID Connect (`identity/oidc/`, see [its README](identity/oidc/README.md)), and the admission that turns a verified principal into a session (`identity/admission.mjs`). | Outputs a **verified principal** `{issuer, subject, provider?, email?, emailVerified?, emailVerifiedAt?, amr?}`; `identity/index.mjs`. |
65
+ | `accounts/` | Who a person is in this app and what they may do: operators, roles, invitations, sessions, domain rules and step-up proofs in the `oa_` store (`accounts/store.mjs`, `accounts/schema/`). | The **accounts adapter interface** (`accounts/adapter.mjs`); Base's implementation over `oa_` is `accounts/oa.mjs`. |
66
+ | `dev/` | Local-only dev sessions, personas and the badge, loaded only from a local-only Worker entry. | `dev/index.mjs`; see "Local dev identity" below. |
67
+
68
+ The files at the root are shared by all three: HTTP and input checks, crypto,
69
+ configuration and branding, the shared page frame (`page.mjs`), the key gate
70
+ (`dev-gate.mjs`, `dev-admission.mjs`, which production loads to *refuse* dev
71
+ credentials), the composition (`service.mjs`), the HTTP adapter (`routes.mjs`)
72
+ and the sign-in page (`signin/`).
73
+
74
+ ### Native composition
75
+
76
+ `createOperatorAuth({ accounts, proofs, ... })` routes the complete service
77
+ through the seed's native accounts, cookies, sessions and durable email/step-up
78
+ proof persistence. Both are required; partial injection fails closed. Native
79
+ roles are generic types, and unsupported administration/provisioning explicitly
80
+ refuses. Apply identity/dependency storage only, without OA account tables.
81
+ See the [native adoption contract](docs/native-accounts.md) for exact APIs,
82
+ transaction fences, optional capabilities, callback/trust settings and the
83
+ Slate/HappyToHelp continuation steps.
84
+
85
+ ### The accounts adapter interface
86
+
87
+ Identity never decides who may enter; it asks the app's accounts. A seed that
88
+ keeps its own tables (HappyToHelp, Slate) implements these methods over them and
89
+ passes the adapter to `createAdmission` from `identity/index.mjs`:
90
+
91
+ | Method | Contract |
92
+ | --- | --- |
93
+ | `findByEmail(email)` | The person whose account holds this **proven** email, or null. A person is `{id, email, status: 'active'\|'invited'\|'inactive', owner: boolean}`. |
94
+ | `findBinding({issuer, subject})` | The person bound to this provider principal, or null. |
95
+ | `bind({issuer, subject, person, associated})` | Binds `(iss, sub)` to the person and returns whether it now names them. Never rebinds a principal bound to someone else; binds only proven emails (or an explicit configured association, `associated: true`); revokes the person's earlier password credentials. |
96
+ | `issueSession({person, principal, method, returnTo, request})` | A completed session `{session, operator, cookie, returnTo}`, or null when the person changed meanwhile. |
97
+ | `revokeSession(sessionId)` | Ends one session (used when a seed's `afterConfirm` refuses a sign-in). |
98
+ | `identityEnabled()` *(optional)* | The app-owned provider sign-in setting; omission means off. |
99
+ | `currentBoundSubject({personId,issuer})` *(optional)* | The current bound subject for this person at this issuer; null for ambiguity. Required for OIDC editor authority. |
100
+ | `bindAndIssueSession(...)` *(optional)* | Atomically bind and issue the session with current person and provider-setting fences; Base uses this path. |
101
+ | `owner()` | The app's owner, or null. |
102
+ | `acceptInvitation(...)` *(optional)* | Accepts a pending invitation through a principal that proves the invited email. |
103
+ | `requiredProvider(email)` *(optional)* | The provider id the email's domain must use, or null. |
104
+
105
+ `accounts/oa.mjs` is Base's own implementation: bindings live in
106
+ `oa_external_identities`, sessions in `oa_sessions` (fenced to the operator's
107
+ version), domain rules in `oa_domain_rules`. `auth.accounts` exposes it and
108
+ `auth.admission` the admission built on it.
109
+
110
+ ## HTTP contract
111
+
112
+ | Path | Method | Input / result |
113
+ | --- | --- | --- |
114
+ | `/auth/bootstrap` | POST | token, displayName, returnTo?; 303 with cookie and safe Location |
115
+ | `/auth/magic-link` | POST | email, returnTo?; 202 `{status:'accepted', requiredProvider?}` for admitted/unknown/disallowed address. A native HTML form post (URL-encoded) gets a 303 back to the sign-in page with `notice=sent` instead. |
116
+ | `/auth/confirm?token=…` | GET/HEAD | 200 confirmation form, or 400 expired/invalid; **no writes, no session cookie** |
117
+ | `/auth/confirm` | POST | token; consume once, create session and 303 to stored returnTo. A step-up link completes only in the session that asked for it and sets no new cookie. |
118
+ | `/auth/session` | GET | session and operator, or 401 |
119
+ | `/auth/signout` | POST | `{}`; revoke current session, clear cookie, 204 |
120
+ | `/auth/invite` | POST | email, displayName, role? (member), returnTo?; mail invitation, 202 |
121
+ | `/auth/invite/cancel` | POST | operatorId; cancel pending invitation, return inactive operator, 200 |
122
+ | `/auth/operators` | GET | authorized operator list |
123
+ | `/auth/operator/status` | POST | operatorId, status (`active`/`inactive`) |
124
+ | `/auth/operator/role` | POST | operatorId, role |
125
+ | `/auth/sessions?operatorId=…` | GET | authorized session metadata, no credentials |
126
+ | `/auth/session/revoke` | POST | sessionId; 204 |
127
+ | `/auth/review-session` | POST | returnTo?; explicitly configured loopback review session |
128
+ | `/auth/step-up` | POST | action, returnTo?; email a step-up link to the signed-in operator, 202 (a form post gets 303 with `notice=step_up_sent`) |
129
+ | `/auth/domain-rules` | GET / POST | list rules; POST domain, provider (null lifts); owner only, step-up |
130
+ | `/auth/providers` | GET | the pinned provider links; owner only |
131
+ | `/auth/providers/unlink`, `/auth/providers/relink` | POST | provider; owner only, step-up |
132
+ | `/auth/oidc/<id>/start`, `/auth/oidc/<id>/callback` | POST / GET | each provider's own routes (see [identity/oidc](identity/oidc/README.md)) |
133
+
134
+ Unknown `/auth/*` paths return 404. Unsupported methods return 405. Except for invalid/expired confirmation GET/HEAD documents, known public
135
+ errors are JSON `{error:code}`: invalid input 400, unauthenticated 401, policy/host/
136
+ Origin rejection 403, missing resource 404, conflict/last owner 409, oversized input
137
+ 413, unsupported content type 415, rate limit 429, missing mail/database 503.
138
+ Internal errors are redacted as `auth_unavailable`. Methods throw typed AuthError;
139
+ the Fetch adapter translates it. Every response has no-store headers. Successful
140
+ confirmation GET/HEAD responses use `Referrer-Policy: strict-origin` so native
141
+ form POSTs retain their Origin without exposing the token path or query in
142
+ Referer; HTTPS-to-HTTP downgrade requests omit Referer. Other responses retain
143
+ `Referrer-Policy: no-referrer`. Exact Origin/CSRF validation is unchanged.
144
+ Do not log confirmation URLs, input tokens, cookies or provider message bodies.
145
+
146
+ ## Confirmation and branding
147
+
148
+ GET/HEAD confirmation uses a self-contained, responsive document. A magic link
149
+ says “Confirm sign-in to Example”; an invitation says “Accept your invitation to
150
+ Example”. Both explain that the link works once and show its remaining lifetime,
151
+ rounded up to minutes (singular for one minute). The sole primary form button is
152
+ “Confirm”. Only its hidden POST field contains the token. Invalid, expired,
153
+ consumed or unauthorized links return a styled 400 document with “Request a new
154
+ link” pointing to the configured sign-in page. HEAD has the same status and
155
+ headers without a body. Preview never writes auth state or sets a cookie.
156
+
157
+ The heading uses 30px type (28px at viewport widths up to 480px), 1.3 line
158
+ height, -.02em tracking and a 24px bottom margin; the brand-to-heading gap is
159
+ 32px. Confirmation documents contain one inline stylesheet, and the CSP adds
160
+ only that stylesheet's exact UTF-8 SHA-256 hash.
161
+
162
+ Optional `config.branding` fields are independently defaulted and validated:
163
+
164
+ | Field | Default | Validation |
165
+ | --- | --- | --- |
166
+ | `productName` | `your workspace` | At most 80 UTF-16 code units before trimming, with at least one visible letter, number, punctuation or symbol; rejects C0/C1 controls, U+2028/U+2029 and default-ignorable code points, except contextual script joiners and emoji presentation described below |
167
+ | `accentColor` | `#334155` | Exactly six hexadecimal digits after `#` |
168
+ | `signInPath` | `/signin` | Same-origin absolute path, at most 2048 characters; no scheme, network-path reference, backslash or control characters, including encoded variants |
169
+
170
+ Product names preserve letters and combining marks in every script. ZWNJ
171
+ (U+200C) is allowed after a letter (including its combining marks) when a letter
172
+ or decimal digit follows, directly or after spaces, as in Persian "کتاب‌ ها" and
173
+ "سال‌۱۴۰۰". ZWJ (U+200D) is allowed between letters and after a virama, including
174
+ at the end of a word, so legacy Malayalam chillu forms are kept. ZWJ also joins
175
+ emoji pictographs, including an emoji presentation selector or skin-tone modifier
176
+ on the preceding emoji. U+FE0F is allowed immediately after an emoji pictograph,
177
+ and after a keycap base (0–9, # or *) only when U+20E3 follows; all other
178
+ variation selectors are rejected. Rejected default-ignorable characters include
179
+ U+200B/U+200E/U+200F, bidi overrides and isolates, U+2060–U+206F, U+FEFF, U+061C,
180
+ U+180E, U+034F, U+115F/U+1160, U+3164 and U+FFA0. The object replacement
181
+ character U+FFFC and lone surrogates (text that is not well formed) are rejected.
182
+ Space-only and invisible-only names are invalid; the braille blank U+2800 does not
183
+ count as a visible symbol.
184
+
185
+ The consumer must serve that sign-in path. Query strings and fragments are
186
+ allowed; malformed encodings in the configured value are rejected. Encoded
187
+ percent data is preserved, and nested encoded separators and controls are still
188
+ rejected. Invalid branding throws
189
+ `AuthError('invalid_configuration', 503)`. Branding is copied and frozen at
190
+ construction. `auth.presentation` exposes only the three normalized branding
191
+ fields; it never exposes configuration secrets. `previewLink` returns only
192
+ `{kind, expiresInMinutes}` for an authorized link, otherwise null. Names are
193
+ HTML-escaped in documents and mail. The accent chooses black or white button
194
+ text for contrast, with a visible keyboard focus ring in light and dark schemes.
195
+
196
+ The module's confirmation HTML responses contain exactly one inline stylesheet. Their
197
+ CSP adds only its exact UTF-8 SHA-256 `style-src` hash to the existing restrictive
198
+ policy. No scripts, external resources, style attributes, CSS URLs or imports
199
+ are included in the document. JSON errors, redirects and other responses retain
200
+ the original CSP. Do not add a broad inline-style allowance to an adopting seed.
201
+
202
+ An application can compose its same-origin brand font head onto this document,
203
+ as Base's foundation/reference do. Preserve the inline style hash, private
204
+ headers and native form behavior; permit only same-origin styles and fonts,
205
+ with no scripts or third-party resources. Set the conformance adapter's
206
+ `confirmationFonts: true` for this composition. The same public auth scenarios
207
+ then require one fixed same-origin stylesheet and primary font preload, the
208
+ unchanged hash and restrictive CSP, and all existing token/session protections.
209
+ The standalone module's default conformance still requires no font resources.
210
+
211
+ ## Sessions, tokens and policies
212
+
213
+ Opaque 256-bit credentials are HMAC-SHA256 hashed with domain separation at rest.
214
+ Cookies are host-only, Path=/, HttpOnly, SameSite=Lax; production uses Secure and
215
+ `__Host-operator_session`. Explicit `local:true` allows only literal localhost,
216
+ 127.0.0.1 or [::1] and uses `operator_session_local_<port>` (for example
217
+ `operator_session_local_8787`): browsers keep cookies per host, not per port, so
218
+ checkouts side by side on one loopback host each keep their own session. Session expiry defaults to
219
+ 7 days (maximum 30), links to 15 minutes (maximum 60); no silent sliding renewal.
220
+ `returnTo` must be a local absolute path; unsafe values become `/app`. It is stored
221
+ with the token, and cannot be replaced on confirmation. Auth methods never return
222
+ raw link tokens. `CompletedSession.cookie` is server-only and must only be emitted
223
+ as Set-Cookie, never serialized into browser data.
224
+
225
+ D1 batches transactionally claim setup, consume links, accept invitations, bind
226
+ external identities and issue sessions. A unique token/session relation prevents
227
+ double consumption. Operator version fences invalidate **all** old links and
228
+ sessions after status/role changes, including after reactivation. Mutation SQL
229
+ rechecks the actor's active session and authority snapshot. Last active owner
230
+ removal/demotion is rejected by SQL triggers, including competing mutations.
231
+ Reactivate never bypasses invitation acceptance. Retrying an invitation with the
232
+ same email/role resends it and revokes older links; an existing active or
233
+ ordinarily inactive operator conflicts. An inactive operator with a durable
234
+ cancellation marker can be reinvited as described below. Unknown addresses never
235
+ become operators through sign-in.
236
+
237
+ `cancelInvitation(request, operatorId)` uses the existing `operators:deactivate`
238
+ policy with the pending operator as target. It changes only an `invited` operator
239
+ to `inactive`, retaining the operator ID, bootstrap ledger and history. The SQL
240
+ write fences the actor's session and authority version and the target's status
241
+ and version. Its existing version trigger invalidates every outstanding link;
242
+ GET/HEAD preview and POST confirmation then fail. A durable cancellation ledger
243
+ and SQL trigger reject ordinary activation with `invitation_required`, even after
244
+ credentials are pruned. A cancelled identity can receive a fresh invitation only
245
+ when the requested role matches its recorded role. Reissue changes that same row
246
+ back to `invited`, updates its display name, and leaves its ID and creation/history
247
+ intact. Its status trigger advances the version; the fenced D1 batch inserts a new
248
+ link and revokes older unconsumed links. The cancellation ledger remains present
249
+ while the replacement is pending. Cancelling that pending invitation again is
250
+ allowed and refreshes the durable marker, so repeated cancel/reissue cycles retain
251
+ the same guard. Existing active and ordinarily deactivated operators still conflict,
252
+ as do role mismatches; a same-role pending invitation can still be resent with
253
+ status 202. A completed invitation cannot be cancelled.
254
+
255
+ The cancellation ledger is removed only inside the successful token-consume batch,
256
+ witnessed by the session inserted from that invitation immediately before the
257
+ operator is activated. Session creation, ledger removal, activation and token
258
+ consumption commit atomically. An ordinary status update cannot supply that
259
+ acceptance evidence. A confirmation racing with cancellation has one winner: a
260
+ completed join makes cancellation conflict; a completed cancellation prevents the
261
+ join and session creation. Use `setOperatorStatus` for explicit active-operator
262
+ removal; its last-owner protection remains in force.
263
+
264
+ The full `accounts/schema/operator-auth.sql` includes the cancellation ledger and triggers
265
+ for a fresh database. In an existing store that lacks these objects, apply
266
+ `accounts/schema/invitation-cancellation.sql` as a **new numbered migration before switching
267
+ to this runtime**: the runtime reads the ledger for reinvitation and consumes it
268
+ on confirmation. Inspect the seed's migration history and assign its next unused
269
+ number; never rewrite an applied migration. Do not apply both files to a fresh
270
+ store. Missing cancellation schema fails closed. The additive migration leaves
271
+ existing rows and credentials unchanged and does not infer historical acceptance
272
+ for existing active or inactive operators; only a witnessed pending cancellation
273
+ writes a ledger row. Keep the ledger and triggers on rollback, and never prune
274
+ `oa_invitation_cancellations` as maintenance.
275
+
276
+ Invitations also bind their issuer's identity and authority version. Preview and
277
+ confirmation require that issuer to remain active and allowed, and re-evaluate
278
+ the configured `operators:invite` policy for the issued role. Confirmation fences
279
+ that issuer/version in the same transaction as invitation acceptance and session
280
+ creation. Changing the issuer's status or role permanently invalidates unused
281
+ invitations, even after restoration; an authorized operator must issue a fresh
282
+ link. Signing out or revoking just the issuing browser session does not invalidate
283
+ an invitation whose issuer still has authority.
284
+
285
+ Durable fixed-window limits pair the client-IP resolver with a normalized email;
286
+ defaults are 30 per IP and 5 per email per 15 minutes. `limitWindowSeconds`,
287
+ `ipLimit` and `emailLimit` configure those defaults. Bootstrap, magic link,
288
+ invitation, confirmation and review use separate scopes. Confirmation limits bind
289
+ the presented opaque token instead of the address. `clientIp` is mandatory;
290
+ unavailable addresses fail closed (503). Header trust belongs to the deployment
291
+ boundary. Do not trust X-Forwarded-For.
292
+ Periodically prune old oa_limits rows and expired tokens/sessions in an
293
+ edition-owned maintenance job; preserve referenced rows or delete referencing
294
+ sessions first. Never delete oa_bootstrap as maintenance.
295
+
296
+ Default applied: **owner/admin/member**. Owner manages all operators; admin invites
297
+ members and manages members; members view/revoke their own sessions. Only owners
298
+ can promote roles. `rolePolicy({actor,action,target,role})` replaces this policy
299
+ for product variations. It receives current persisted operators, never caller
300
+ role claims; keep it deterministic and side-effect-free. Active operator, active
301
+ session, allow-list, version fences and last-owner safeguards cannot be overridden.
302
+ The exported `isActiveOperator` is the shared admission predicate.
303
+
304
+ ## Mail, OIDC, password and review seams
305
+
306
+ Magic and invitation mail include both plain text and HTML. Each part has exactly
307
+ one confirmation URL; the HTML part presents it as a button with inline mail-safe
308
+ styling. The body sets Arial; its centered main content has a 480px maximum width
309
+ and 32px by 24px padding. The “Confirm” link opens a page; the copy says “Open this link to review and confirm”.
310
+ Both parts direct readers to press Confirm on that page and explain single use and configured expiry.
311
+ The invitation subject is “You're invited to Example”; no subject includes a
312
+ credential. The product name is escaped in HTML and is plain text in the subject
313
+ and text part. Delivery results still control revocation as described below.
314
+
315
+ Mail consumes actual IF-056-MAIL-v1 `createMailSender(config).send(message)`;
316
+ `deliveryId` is the token record UUID. No parallel transport or implicit outbox
317
+ exists. Missing/failing/uncertain transport revokes the link and reports
318
+ `mail_unavailable`; retries require a new link. Explicit development results are
319
+ accepted only in local mode. Auth never claims mailbox receipt.
320
+
321
+ Identity providers verify the whole protocol and hand the admission a verified
322
+ principal; [identity/oidc/README.md](identity/oidc/README.md) describes them. The
323
+ original server seams remain for existing callers:
324
+ `auth.admitVerifiedIdentity(request, identity, {returnTo})` requires
325
+ `{issuer,subject,email,emailVerified:true}` from an issuer in
326
+ `config.externalIssuers`, and `auth.createIdentityAdmission({issuer,
327
+ subjectEmails})` snapshots an exact issuer's subject associations:
328
+
329
+ ```js
330
+ const admit = auth.createIdentityAdmission({
331
+ issuer: 'https://idp.example.com',
332
+ subjectEmails: { 'independently-verified-subject': 'operator@example.com' },
333
+ });
334
+ // Only after full protocol validation; the mapping supplies the account.
335
+ const result = await admit(request, { issuer, subject }, { returnTo });
336
+ ```
337
+
338
+ Both now admit through the accounts adapter, under the same rules as provider
339
+ sign-in. A binding on `(iss, sub)` is never silently rebound: a bound principal
340
+ always names the same account, and one presenting another existing account's
341
+ email is refused. A proven email binds a person's **first** principal at each
342
+ issuer; another subject at the same issuer needs an explicit association, while
343
+ a different issuer may bind its own principal by a proven email. Binding revokes
344
+ the person's password sessions, and `admitVerifiedPassword` then refuses with
345
+ `password_revoked`. An email counts only when proven in the last 30 days
346
+ (`emailVerifiedAt`, where sent). Identity providers also refuse, in a production
347
+ app (not `local`), `amr` containing `dev` and addresses under `.localhost`,
348
+ `.test` or `.invalid`.
349
+ A pending invitation is accepted when a principal proves the invited email.
350
+ Neither path provisions operators or changes roles. The subject map must come
351
+ from trusted deployment configuration, never HTTP input or token claims.
352
+
353
+ `admitVerifiedPassword(request, operatorId, {returnTo})` supports a seed's existing
354
+ real password verifier. It requires POST/Origin and shares active-operator,
355
+ allow-list, version, domain-rule and session policy; an operator bound to a
356
+ provider has no password admission. The caller must verify and rate-limit the
357
+ password **before** invoking it. It neither creates an operator nor claims setup;
358
+ there is no password checker or password endpoint in this module.
359
+
360
+ For a loopback review session, configure `review:{enabled:true,operatorEmail}` and invoke
361
+ `issueReviewSession`. It requires an already-active operator, POST/Origin, and
362
+ literal loopback URL **and Host**. Non-loopback Host returns 403 regardless of
363
+ flags or forwarded headers; admission of the resulting review cookie checks the
364
+ same boundary every time. Review defaults/maxes at one hour. Disable the flag to
365
+ invalidate review admission immediately. Bind the local review listener to a
366
+ loopback interface; a forged Host is not network isolation. Production configuration
367
+ must never enable review. No fixture/sample operator is created for review.
368
+
369
+ ## The sign-in page
370
+
371
+ `createSignInHandler(auth, {setup, links, defaultReturnTo})` serves the one
372
+ sign-in page every Base seed mounts, at `branding.signInPath` (GET and HEAD; null
373
+ for other requests). It is server-rendered plain HTML with no script, so it works
374
+ before hydration and without JavaScript, in the same hashed-style frame as the
375
+ confirmation page (light and dark, the seed's `rer-theme.css` when declared).
376
+ It shows "Continue with…" for each linked, reachable provider, then the email
377
+ link, then optional seed links (the local outbox) and, while the first owner is
378
+ unclaimed, the setup form. A provider that has failed discovery or JWKS for 5
379
+ minutes, or answered 404 or a DNS failure, is hidden, and a browser that still
380
+ posts to it comes back to this page with a notice and the email link instead of
381
+ being redirected away. Its `form-action` allows exactly this origin plus each
382
+ visible provider's authorization origin, since the start route redirects there.
383
+
384
+ Two hooks on `createOperatorAuth({hooks})` keep a seed's own sign-in work in place:
385
+
386
+ - `beforeMagicLink({request, email, operator})` runs before an email link is sent
387
+ to an active operator; returning `false` sends nothing (the public answer is
388
+ unchanged).
389
+ - `afterConfirm({request, operator, session, method, returnTo})` runs after an
390
+ email-link or provider sign-in (`method: 'email' | 'oidc'`). Return
391
+ `{returnTo}` to redirect (Vector's restored-operator review), do other work
392
+ (Pencil's audit mail), or throw an `AuthError` to refuse: the session it was
393
+ about to get is revoked first (Cradle's forgery check).
394
+
395
+ ## Domain rules and step-up
396
+
397
+ An owner can require a provider for an email domain (`POST /auth/domain-rules`
398
+ `{domain, provider}`; `provider: null` lifts it). Sign-ins for an address in that
399
+ domain by any other route are refused: the email link is not sent (the public
400
+ answer is the same, and names the domain's provider), another provider is
401
+ refused with `oidc_provider_required`, and the password and review seams refuse
402
+ with `provider_required`. App owners are exempt. Domains match exactly.
403
+ Unlinking a provider lifts the rules naming it.
404
+
405
+ An identity provider can assert any email, so these actions need a fresh
406
+ **app-local email-link proof**: unlinking or changing a provider, changing domain
407
+ rules, transferring ownership (making or unmaking an owner), export, delete and
408
+ minting long-lived credentials (`STEP_UP_ACTIONS`). `POST /auth/step-up
409
+ {action}` emails a link to the signed-in operator's own address; confirming it in
410
+ the same session records the proof, which lasts 10 minutes. An email-link
411
+ sign-in within those 10 minutes is itself a proof. A provider-issued session is
412
+ never one. Base's own routes enforce it; a seed guards its export, delete and
413
+ credential-minting routes with `await auth.requireStepUp(request, action)`,
414
+ which refuses with `step_up_required` (403).
415
+
416
+ The shared administrative actor guard applies that proof to every owner role
417
+ grant or removal, owner invitation (new, reissued or reinstated after cancellation),
418
+ owner invitation cancellation, and owner activation or deactivation. Unchanged
419
+ roles/statuses and non-owner status changes retain their usual authorization.
420
+ The existing version fences and last-active-owner protection still apply after
421
+ the proof succeeds.
422
+ Creating a server-declared owner dev persona also requires the signed-in owner's
423
+ fresh local proof in addition to the checkout key. Its creation repeats the actor
424
+ session/version fence in the database write.
425
+
426
+ ## Local dev identity and the key gate
427
+
428
+ `dev-gate.mjs` (exported as `devGate`, production-safe) is the per-checkout key
429
+ gate. `rer setup` writes a random `RER_DEV_KEY` to `.rer/local/dev-key.env`;
430
+ only local runtimes receive it as a binding, and `rer deploy` refuses it, so a
431
+ deployed Worker has no gate and every gated surface answers 404. A tunnel or
432
+ local reverse proxy makes remote requests look local (forwarded requests carry
433
+ a loopback URL and Host, and `CF-Connecting-IP` is whatever the caller sent), so
434
+ the gate is the key, not the network. `devGate({key, origin, local})` returns
435
+ null unless the key is present and the origin is an explicit loopback origin in
436
+ local mode. `admits(request)` accepts the `rer_dev_gate_<port>` cookie (an HMAC of the
437
+ key, so a reset key refuses old cookies) or the key in `x-rer-dev-key` (the rer
438
+ CLI and test harnesses). `exchange(request)` serves `GET /__dev/key` and trades
439
+ the key from the URL fragment, through a same-origin POST, for a host-only
440
+ `HttpOnly; SameSite=Strict; Path=/` cookie. Seeds gate their development outbox
441
+ with it.
442
+
443
+ `dev/` is the dev layer: **server-only and local-only**. Import it only from a
444
+ local-only Worker entry (a Vite-serve or local `wrangler dev` entry), never
445
+ from the deployed main; its `DEV_LAYER_MARKER` lets `rer build` refuse any output
446
+ that bundles it. `withDevIdentity(worker, {bindings, config, personas})` wraps
447
+ the seed's Worker and serves, behind the gate only:
448
+
449
+ | Path | Result |
450
+ | --- | --- |
451
+ | `/auth/dev/enter?as=owner\|signed-out\|PERSONA&returnTo=` | Creates or reuses a real `oa_sessions` row of kind `dev` for the configured owner or persona and 303s to `returnTo` (default `/app`); `signed-out` revokes the current session. |
452
+ | `/__dev/personas` | `[{as,email,role?}]`, used by `rer dev` for `RER_SIGNED_IN`. |
453
+
454
+ `personas()` returns the seed's `devPersonas()`: `[{as, role, displayName?}]`,
455
+ real operators at `<as>@dev.localhost` created only by dev sign-in. Dev
456
+ sessions pass the same version fences, revocation, deactivation and allow-list
457
+ as every other session. The core admits a `dev` session, and any credential
458
+ minted while using one (`oa_dev_credentials`, today an invitation link, whose
459
+ confirmation then yields a `dev` session), only when the dev layer's
460
+ registered check passes for that request; a production Worker never registers
461
+ one. HTML documents gain a dev-only badge for switching identity; the root
462
+ document also gets the fragment exchange script. Both are added under the
463
+ document's own CSP by hash. With `RER_DEV_IDENTITY=off` (`rer dev --login`)
464
+ dev sign-in and personas answer 404 while the gate still opens the outbox. The
465
+ layer answers 503 to every request when `RER_INSTALLATION_ID` is set beside a
466
+ non-loopback `RER_MANAGEMENT_ORIGIN`.
467
+
468
+ `dev/sign-in-as.mjs` is the test harness door: `signInAs({origin, key, as})`
469
+ returns the cookies a test browser or fetch client installs, and
470
+ `readDevKey(root)` reads the checkout's key.
471
+
472
+ Portable archives never carry dev identity: export drops `dev` sessions and
473
+ dev-minted credentials and marks personas suspended, and import refuses an
474
+ archive holding either (`modules/portability/dev-data.mjs`). Seeds that mint
475
+ their own credentials while signed in record them in `oa_dev_credentials` and
476
+ admit them only behind the gate.
477
+
478
+ ## Integration and checks
479
+
480
+ Copy this directory as source; import only its server entry from `.server` modules.
481
+ A Start server function must call getSession on each invocation. A Vite SPA must
482
+ use protected HTTP APIs and never bundle this implementation, secrets or D1. The
483
+ consumer owns its native unauthorized document redirect and API 401 behavior.
484
+ Mount `/auth/*` before private namespace guards and before Start; keep private
485
+ unknown API paths behind authentication before returning 404.
486
+
487
+ At the Base repository root, `pnpm run verify:operator-auth` runs the operator-auth,
488
+ identity-provider and sign-in page `.test.mjs` suites; `pnpm run verify:modules` runs the module `.test.ts`
489
+ suites. The default suite includes a static pre-ledger schema and populated-row
490
+ upgrade fixture, exercising current handlers and persistent D1 with a fixed test
491
+ clock; its provenance is documented in `tests/fixtures/pre-ledger/README.md`.
492
+ `tests/prior-data.test.mjs` runs a store written by the auth-1 main's own API
493
+ (`tests/fixtures/auth-1-store/README.md`) through these layers after the two
494
+ additive migrations below.
495
+ The "cancelled before the ledger" fixture row represents a state reachable only
496
+ by a direct database update, not through the cancellation API before its ledger
497
+ exists. The prior-data test executes every statement in the shipped migration
498
+ and checks that the applied count matches the file's statement count.
499
+ The pre-update source upgrade check is separate from those globs. Prepare
500
+ an independently captured pre-update source snapshot containing the complete
501
+ `operator-auth/` directory (including `tests/worker.mjs` and
502
+ `accounts/schema/invitation-cancellation.sql`) and `pre-cancellation.sql` at the snapshot
503
+ root. With the frozen workspace dependencies installed, run:
504
+
505
+ ```sh
506
+ OA_BASELINE_SOURCE=/path/to/snapshot node --test modules/operator-auth/tests/reinvitation-upgrade.mjs
507
+ ```
508
+
509
+ Runtime tests use the locked Wrangler Miniflare/esbuild dependencies and run the
510
+ bundled implementation in real workerd with D1. They create disposable test data and
511
+ dispose runtimes. Adversarial request tests use a test-only envelope to preserve
512
+ literal Host and Origin through Miniflare's local proxy; the production handler,
513
+ crypto and D1 execute in workerd. These are module-level checks, not proof of an
514
+ adopting seed's emitted browser transport or deployment. Each seed must wire its
515
+ corresponding auth and browser checks into CI. Hosted provider/IdP proof and
516
+ deployment remain separate.
517
+
518
+ ## Adopting this version
519
+
520
+ Copy the **complete** `modules/operator-auth/` directory from one reviewed Base
521
+ revision into the seed's operator-auth source location. Keep its layers
522
+ (`identity/`, `accounts/`, `dev/`), runtime files, declarations, schema,
523
+ conformance files and tests from that same revision; `rer check` names any copied
524
+ file that differs from the pinned revision (`SC-05.auth-copy.*`); the runtime imports neighboring helpers within the module. Keep imports
525
+ server-only, reconcile the seed's route integration, and run that seed's real auth
526
+ and browser checks.
527
+
528
+ Wire `config.branding` with the seed's `productName`, `accentColor` and
529
+ `signInPath`; the defaults are `your workspace`, `#334155` and `/signin`. Keep
530
+ `MAIL_FROM` in auth config and provide a real `mailSender` that implements
531
+ `send(message)`. The Base example builds it with `createMailSender` from
532
+ [the mail module](../mail/README.md), using mail config fields `provider`, `mode`, `apiKey` and
533
+ Cloudflare-only `accountId`; its sample maps `provider` to `MAIL_PROVIDER`,
534
+ `apiKey` to `MAIL_API_KEY`, and Cloudflare `accountId` to `MAIL_ACCOUNT_ID`, with
535
+ `mode: 'production'`. `MAIL_FROM` is a separate auth config field. If sending is
536
+ intentionally unavailable for first setup, omit both `mailSender` and `MAIL_FROM`
537
+ until mail is configured.
538
+
539
+ For a fresh auth database, install `accounts/schema/operator-auth.sql` through the seed's
540
+ initial schema or migration process, once. In an existing store that has the
541
+ earlier operator-auth schema but lacks cancellation objects,
542
+ apply `accounts/schema/invitation-cancellation.sql` under the seed's next unused migration
543
+ number **before switching the runtime**. The consume path now reads and removes
544
+ the ledger during invitation acceptance, so the migration is a runtime prerequisite
545
+ for such stores. Never rewrite an applied migration, infer cancellation for old
546
+ inactive rows, or apply both schema files to a fresh store. The complete fresh
547
+ schema already includes the ledger. This update adds no schema objects beyond
548
+ that existing cancellation-capable schema.
549
+
550
+ The updated invite behavior permits same-role reissue of an inactive identity only
551
+ when it carries the durable cancellation marker. Same-role invitations that remain
552
+ pending, including expired pending invitations, retain the existing resend path.
553
+ Cancelled reissue preserves the identity row and history, creates a fresh invitation
554
+ link, invalidates older links and retains the marker until a successful confirmation
555
+ creates a session. Repeated cancellation/reissue is supported. Existing active or
556
+ ordinarily inactive identities and role mismatches still conflict; an ordinary
557
+ status change cannot accept a pending invitation.
558
+ Pre-ledger cancelled identities have no cancellation record and remain refused
559
+ for reinvitation until the owner deliberately removes the identity and reinvites
560
+ the same email; the current status API cannot distinguish them from ordinary
561
+ inactive identities.
562
+
563
+ **Dev sessions.** The fresh schema admits session kind `dev` and includes
564
+ `oa_dev_credentials`. An existing store needs `accounts/schema/dev-sessions.sql` as a
565
+ **new numbered migration under the seed's next unused number, before switching
566
+ to this runtime**: SQLite cannot change a CHECK in place, so it rebuilds
567
+ `oa_sessions` with `dev` added, keeping every row, column, index and
568
+ constraint (no table refers to `oa_sessions`), and creates
569
+ `oa_dev_credentials`. The runtime reads that table when previewing and
570
+ confirming links, so a store without it fails closed. Unlike the cancellation
571
+ migration, this file is also safe after the fresh schema. The reference seed
572
+ applies it as `migrations/core/0007_dev_sessions.sql`;
573
+ `tests/dev-sessions-upgrade.test.mjs` upgrades a populated store built from
574
+ the reference's earlier migrations and proves every stored session keeps its id
575
+ and expiry. Native, review and password sessions retain usable authority after
576
+ the later additive migrations. Legacy provider sessions require a new sign-in:
577
+ upgrades start with provider sign-in off, and enabling it does not manufacture
578
+ missing provider session provenance.
579
+
580
+ **Layers, domain rules and step-up.** An existing store needs
581
+ `accounts/schema/domain-rules-step-up.sql` (`oa_domain_rules`,
582
+ `oa_step_up_challenges`) and, when it offers providers or had the earlier OIDC
583
+ adapter, `identity/oidc/schema/oidc.sql` again (it adds `oa_identity_providers`
584
+ and `oa_identity_provider_health`; every statement is `IF NOT EXISTS`), each as
585
+ a new numbered migration before switching to this runtime. Both are additive and
586
+ safe after the fresh schema; the runtime reads them when previewing links and
587
+ requesting email links. The reference applies them as
588
+ `migrations/core/0008_identity_providers.sql` and
589
+ `0009_domain_rules_step_up.sql`. The earlier single-issuer `oidc/` adapter
590
+ (`createOidcClient`) is replaced by `config.providers`; its existing bindings keep
591
+ signing in once the same issuer is configured as a provider.
592
+
593
+ Seeds copy this module at different revisions, and an older copy lacks the
594
+ cancellation migration and the optional OIDC and conformance files. Check each
595
+ seed's present source and applied migrations before adopting. A seed owns its
596
+ route configuration, CI and release; Base checks do not establish integrated
597
+ adoption or deployment.
598
+
599
+ The application-side dependency, owner-setting and logout protocol is documented in [dependencies](../dependencies/README.md). Existing stores default to provider sign-in off, and provider admission requires the app-owned setting.
@@ -0,0 +1,41 @@
1
+ import { AuthError } from '../errors.mjs';
2
+
3
+ // The accounts adapter interface: everything identity needs from an app's own
4
+ // accounts. Base's oa_ store (./oa.mjs) is one implementation; a seed with its
5
+ // own tables (HappyToHelp, Slate) implements the same methods over them.
6
+ //
7
+ // findByEmail(email) the person whose account holds this proven email, or null
8
+ // findBinding({issuer,subject}) the person bound to this provider principal, or null
9
+ // bind({issuer,subject,person,associated})
10
+ // bind the principal; true when it now names this person.
11
+ // Never rebinds a principal bound to someone else, binds only
12
+ // proven emails or explicit associations, and revokes the
13
+ // person's earlier password credentials.
14
+ // issueSession({person,principal,method,returnTo,request})
15
+ // a CompletedSession ({session,operator,cookie,returnTo}) or null
16
+ // revokeSession(sessionId) end one session
17
+ // owner() the app's owner, or null
18
+ // Optional:
19
+ // bindAndIssueSession(input) atomically bind and issue with current setting/version fences
20
+ // identityEnabled() provider admission setting; missing means off
21
+ // currentBoundSubject(input) current subject for person/issuer; absent denies editor authority
22
+ // acceptInvitation({person,principal,returnTo,request}) accept a pending invitation
23
+ // requiredProvider(email) the provider id an email's domain must use, or null
24
+ //
25
+ // A person is { id, email, status: 'active'|'invited'|'inactive', owner: boolean }.
26
+ const REQUIRED = ['findByEmail', 'findBinding', 'bind', 'issueSession', 'revokeSession', 'owner'];
27
+ const OPTIONAL = ['bindAndIssueSession','acceptInvitation', 'requiredProvider', 'currentBoundSubject', 'identityEnabled'];
28
+
29
+ export function accountsAdapter(adapter) {
30
+ if (!adapter || typeof adapter !== 'object' || REQUIRED.some(name => typeof adapter[name] !== 'function')
31
+ || OPTIONAL.some(name => adapter[name] !== undefined && typeof adapter[name] !== 'function')) throw new AuthError('invalid_accounts_adapter', 503);
32
+ return adapter;
33
+ }
34
+
35
+ export function person(value) {
36
+ if (!value) return null;
37
+ if (typeof value.id !== 'string' || typeof value.email !== 'string' || !['active', 'invited', 'inactive'].includes(value.status) || typeof value.owner !== 'boolean') {
38
+ throw new AuthError('invalid_accounts_adapter', 503);
39
+ }
40
+ return value;
41
+ }