@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,14 @@
1
+ // Lamp's retained-public-bundle contract only. This is not a Durable Object implementation.
2
+ export function retainedCache(sqlite) {
3
+ sqlite.exec('CREATE TABLE IF NOT EXISTS platform_retained(id TEXT PRIMARY KEY,generation INTEGER NOT NULL,body TEXT NOT NULL)');
4
+ return { idFromName: name => name, get: id => ({ async fetch(input, init) {
5
+ const request = new Request(input, init), current = () => sqlite.prepare('SELECT * FROM platform_retained WHERE id=?').get(id);
6
+ if (request.method === 'HEAD') { const row = current(); return new Response(null, { status: row ? 200 : 404, headers: row ? { 'x-lamp-generation': String(row.generation) } : {} }); }
7
+ if (request.method === 'GET') { const row = current(); return new Response(row?.body, { status: row ? 200 : 404, headers: { 'content-type': 'application/json' } }); }
8
+ if (request.method !== 'PUT') return new Response(null, { status: 405 });
9
+ const body = await request.text(); let bundle;
10
+ try { bundle = JSON.parse(body); const keys = new Set(); if (typeof bundle.version !== 'string' || !Number.isFinite(Date.parse(bundle.generatedAt)) || !Number.isInteger(bundle.generation) || bundle.generation < 0 || !Array.isArray(bundle.artifacts)) throw new Error(); for (const artifact of bundle.artifacts) { if (typeof artifact.key !== 'string' || typeof artifact.body !== 'string' || typeof artifact.contentType !== 'string' || keys.has(artifact.key)) throw new Error(); keys.add(artifact.key); } if (!['page.html','feed.atom','widget.js','status.json'].every(key => keys.has(key))) throw new Error(); } catch { return new Response('Invalid complete snapshot', { status: 400 }); }
11
+ sqlite.prepare('INSERT INTO platform_retained VALUES(?,?,?) ON CONFLICT(id) DO UPDATE SET generation=excluded.generation,body=excluded.body WHERE excluded.generation>platform_retained.generation').run(id, bundle.generation, body);
12
+ return new Response(null, { status: 204 });
13
+ } }) };
14
+ }
@@ -0,0 +1,7 @@
1
+ const primary = ['SQLITE_OK','SQLITE_ERROR','SQLITE_INTERNAL','SQLITE_PERM','SQLITE_ABORT','SQLITE_BUSY','SQLITE_LOCKED','SQLITE_NOMEM','SQLITE_READONLY','SQLITE_INTERRUPT','SQLITE_IOERR','SQLITE_CORRUPT','SQLITE_NOTFOUND','SQLITE_FULL','SQLITE_CANTOPEN','SQLITE_PROTOCOL','SQLITE_EMPTY','SQLITE_SCHEMA','SQLITE_TOOBIG','SQLITE_CONSTRAINT','SQLITE_MISMATCH','SQLITE_MISUSE','SQLITE_NOLFS','SQLITE_AUTH','SQLITE_FORMAT','SQLITE_RANGE','SQLITE_NOTADB','SQLITE_NOTICE','SQLITE_WARNING'];
2
+ const constraints = ['CHECK','COMMITHOOK','FOREIGNKEY','FUNCTION','NOTNULL','PRIMARYKEY','TRIGGER','UNIQUE','VTAB','ROWID','PINNED','DATATYPE'];
3
+ export function sqliteError(cause) {
4
+ const code = cause.errcode ?? 1, base = primary[code & 255] ?? 'SQLITE_ERROR';
5
+ const extended = (code & 255) === 19 && code > 255 ? constraints[(code >> 8) - 1] : undefined;
6
+ return `D1_ERROR: ${cause.message}: ${base}${extended ? ` (extended: SQLITE_CONSTRAINT_${extended})` : ''}`;
7
+ }
@@ -0,0 +1,26 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { parentPort, workerData } from 'node:worker_threads';
3
+ import { performance } from 'node:perf_hooks';
4
+ import { sqliteError } from './sqlite-error.mjs';
5
+ const db = new DatabaseSync(workerData.path, { limits:{length:2000000,sqlLength:100000,column:100,variableNumber:100},enableDoubleQuotedStringLiterals:true });
6
+ db.exec('PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA busy_timeout=30000;');
7
+ function execute({sql,args=[]}) {
8
+ const started=performance.now(), statement=db.prepare(sql), columns=statement.columns();statement.setReturnArrays(true);
9
+ const before=db.prepare('SELECT total_changes() AS n').get().n;
10
+ const rawResults=columns.length ? statement.all(...args).map(row=>row.map(value=>ArrayBuffer.isView(value)?[...value]:value)) : (statement.run(...args),[]);
11
+ const results=rawResults.map(row=>Object.fromEntries(columns.map((column,index)=>[column.name,row[index]])));
12
+ const after=db.prepare('SELECT total_changes() AS n,last_insert_rowid() AS last_row_id').get();
13
+ const changes=after.n-before;
14
+ return { success:true,results,rawResults,meta:{duration:performance.now()-started,changes,last_row_id:after.last_row_id,changed_db:changes>0,rows_read:results.length,rows_written:changes},columns:columns.map(item=>item.name) };
15
+ }
16
+ parentPort.on('message',({id,operation,statements,sql})=>{
17
+ parentPort.postMessage({id,started:true});
18
+ try {
19
+ let result;
20
+ if(operation==='batch') {
21
+ db.exec('BEGIN');try{result=statements.map(execute);db.exec('COMMIT');}catch(cause){db.exec('ROLLBACK');throw cause;}
22
+ } else if(operation==='exec') {const start=performance.now();db.exec(sql);result={count:sql.split('\n').filter(line=>line.trim()).length,duration:performance.now()-start};}
23
+ else result=execute(statements[0]);
24
+ parentPort.postMessage({id,result});
25
+ }catch(cause){parentPort.postMessage({id,error:sqliteError(cause)});}
26
+ });
@@ -0,0 +1,14 @@
1
+ import { readFile, realpath, stat } from 'node:fs/promises';
2
+ import { join, relative, extname } from 'node:path';
3
+ const types = { '.js':'text/javascript','.mjs':'text/javascript','.css':'text/css','.html':'text/html; charset=utf-8','.json':'application/json','.svg':'image/svg+xml','.png':'image/png','.jpg':'image/jpeg','.woff2':'font/woff2','.ico':'image/x-icon' };
4
+ export function assets(directory) {
5
+ return { async fetch(input, init) {
6
+ const request = new Request(input, init); if (!['GET','HEAD'].includes(request.method)) return new Response(null, { status: 405 });
7
+ let path; try { path = decodeURIComponent(new URL(request.url).pathname); } catch { return new Response(null, { status: 400 }); }
8
+ if (path.includes('\0') || path.includes('\\') || path.split('/').some(item => item.startsWith('.'))) return new Response(null, { status: 404 });
9
+ const root = await realpath(directory), candidate = join(root, path); let file;
10
+ try { file = await realpath(candidate); if ((await stat(file)).isDirectory()) file = await realpath(join(file, 'index.html')); } catch { return new Response(null, { status: 404 }); }
11
+ const local = relative(root, file); if (local === '..' || local.startsWith('../')) return new Response(null, { status: 404 });
12
+ const bytes = await readFile(file); return new Response(request.method === 'HEAD' ? null : bytes, { headers: { 'content-type': types[extname(file)] ?? 'application/octet-stream' } });
13
+ } };
14
+ }
@@ -0,0 +1 @@
1
+ export const env = new Proxy({}, { get: (_target, key) => globalThis[Symbol.for('rer.container.bindings')]?.[key], ownKeys: () => Reflect.ownKeys(globalThis[Symbol.for('rer.container.bindings')] ?? {}), getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true }) });
@@ -0,0 +1,38 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join, relative } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { pathToFileURL } from 'node:url';
5
+ export async function checkBoundary(root, { engine='node', cacheContract }={}) {
6
+ const ts=await import(pathToFileURL(createRequire(join(root,'package.json')).resolve('typescript'))), failures=[];
7
+ async function walk(directory){
8
+ for(const item of await readdir(directory,{withFileTypes:true})){
9
+ const path=join(directory,item.name);if(item.isDirectory()){await walk(path);continue;}if(!/\.[cm]?[jt]sx?$/.test(item.name))continue;
10
+ const name=relative(root,path),tree=ts.createSourceFile(path,await readFile(path,'utf8'),ts.ScriptTarget.Latest,true);
11
+ const cache=cacheContract==='lamp-retained-v1'&&/(?:^|\/)platform\/blobs\/snapshot-cache\.ts$/.test(name);
12
+ const bindings=/(?:^|\/)platform\/bindings\.server\.ts$/.test(name);
13
+ const fail=api=>failures.push({path:name,api}),localNames=new Set();
14
+ function names(node){if((ts.isVariableDeclaration(node)||ts.isFunctionDeclaration(node)||ts.isImportSpecifier(node))&&node.name&&ts.isIdentifier(node.name))localNames.add(node.name.text);ts.forEachChild(node,names);}names(tree);
15
+ function visit(node){
16
+ if((ts.isImportDeclaration(node)||ts.isExportDeclaration(node))&&node.moduleSpecifier&&ts.isStringLiteral(node.moduleSpecifier)){
17
+ const module=node.moduleSpecifier.text;
18
+ if(module.startsWith('cloudflare:')){
19
+ const imports=node.importClause?.namedBindings;
20
+ const allowed=module==='cloudflare:workers'&&imports&&ts.isNamedImports(imports)&&imports.elements.every(element=>(bindings&&(element.propertyName??element.name).text==='env')||(cache&&(element.propertyName??element.name).text==='DurableObject'));
21
+ if(!allowed)fail('Cloudflare import outside binding/cache contract');
22
+ }
23
+ }
24
+ if(ts.isCallExpression(node)&&node.arguments.some(arg=>ts.isStringLiteral(arg)&&arg.text.startsWith('cloudflare:')))fail('Dynamic Cloudflare import');
25
+ if(ts.isIdentifier(node)&&['WebSocketPair','HTMLRewriter','passThroughOnException','timingSafeEqual'].includes(node.text))fail(node.text);
26
+ if(ts.isCallExpression(node)&&ts.isIdentifier(node.expression)&&node.expression.text==='email'&&!localNames.has('email'))fail('email()');
27
+ if(ts.isPropertyAccessExpression(node)&&((node.expression.getText(tree)==='caches'&&node.name.text==='default')||(node.expression.getText(tree)==='request'&&node.name.text==='cf')))fail(node.getText(tree));
28
+ if(ts.isElementAccessExpression(node)&&ts.isStringLiteral(node.argumentExpression)&&((node.expression.getText(tree)==='caches'&&node.argumentExpression.text==='default')||(node.expression.getText(tree)==='request'&&node.argumentExpression.text==='cf')))fail(node.getText(tree));
29
+ if(ts.isHeritageClause(node)&&node.token===ts.SyntaxKind.ExtendsKeyword&&node.types.some(type=>/DurableObject/.test(type.expression.getText(tree)))&&!cache)fail('New Durable Object');
30
+ ts.forEachChild(node,visit);
31
+ }
32
+ visit(tree);
33
+ }
34
+ }
35
+ await walk(join(root,'src'));
36
+ if(failures.length){const cause=new Error('Container import boundary failed');cause.failures=failures;throw cause;}
37
+ return {engine,ok:true};
38
+ }
@@ -0,0 +1,65 @@
1
+ import { readFile, writeFile, readdir, cp, mkdir, rm } from 'node:fs/promises';
2
+ import { join, resolve } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { createHash } from 'node:crypto';
6
+ import { parseJsonc } from '../../cli/jsonc.mjs';
7
+ import { portabilityPlan } from './portability-schema.mjs';
8
+ import { checkBoundary } from './boundary.mjs';
9
+ const hash = source => createHash('sha256').update(source).digest('hex');
10
+ export async function buildContainer(root, options={}) {
11
+ const previous=process.cwd();process.chdir(root);
12
+ try{return await buildImage(root,options);}finally{process.chdir(previous);}
13
+ }
14
+ async function buildImage(root, { output = join(root,'.rer/container/image'), engine, cacheContract, runner, declaration } = {}) {
15
+ const require = createRequire(join(root,'package.json'));
16
+ declaration ??= JSON.parse(await readFile(join(root,'deploy.json'),'utf8'));
17
+ engine ??= declaration.container?.engine ?? 'workerd'; cacheContract ??= declaration.container?.cacheContract; runner ??= declaration.container?.runner;
18
+ if (!['workerd','node'].includes(engine)) throw new Error('Unsupported container engine');
19
+ await checkBoundary(root,{engine,cacheContract});
20
+ const vite = await import(pathToFileURL(require.resolve('vite'))), loaded = await vite.loadConfigFromFile({ command:'build',mode:'production' },undefined,root);
21
+ if (!loaded) throw new Error('Vite configuration missing');
22
+ const config = loaded.config;
23
+ if (engine === 'node') {
24
+ const plugins = (await Promise.all(config.plugins.flat(Infinity))).filter(plugin => plugin && !plugin.name.startsWith('vite-plugin-cloudflare') && plugin.name !== 'tanstack-start-core:post-build');
25
+ const bindingShim = resolve(import.meta.dirname,'bindings.mjs');
26
+ config.plugins = [{ name:'rer-container-bindings', enforce:'pre',
27
+ resolveId(id) { if (id === 'cloudflare:workers') return bindingShim; },
28
+ transform(source,id) { if (cacheContract === 'lamp-retained-v1' && id === join(root,'src/server.ts')) return source.replace(/export\s*\{\s*SnapshotCache\s*\}\s*from\s*['"][^'"]+['"];?/,''); },
29
+ },...plugins];
30
+ config.ssr = { ...config.ssr,noExternal:true }; config.environments = { ...config.environments,ssr:{ ...config.environments?.ssr,resolve:{ noExternal:true },build:{ ...config.environments?.ssr?.build,rolldownOptions:{ output:{ entryFileNames:'index.mjs' } } } } };
31
+ }
32
+ const builder = await vite.createBuilder({ ...config,root,configFile:false }); await builder.buildApp();
33
+ // Apply the same local-only bundle guard before packaging any container image.
34
+ await (await import('../../cli/dev-identity.mjs')).refuseDevLayerOutput(root);
35
+ let workerConfig;
36
+ if (engine === 'workerd') workerConfig = JSON.parse(await readFile(join(root,'dist/server/wrangler.json'),'utf8'));
37
+ else workerConfig = parseJsonc(await readFile(join(root,'wrangler.jsonc'),'utf8'));
38
+ const { unstable_splitSqlQuery:split } = require('wrangler');
39
+ const migrations = {};
40
+ for (const db of workerConfig.d1_databases ?? []) {
41
+ let directory = resolve(root,db.migrations_dir ?? 'migrations');
42
+ try { await readdir(directory); } catch { directory = resolve(root,'dist/server',db.migrations_dir); }
43
+ migrations[db.binding] = [];
44
+ for (const name of (await readdir(directory)).filter(name => name.endsWith('.sql')).sort()) { const sql = await readFile(join(directory,name),'utf8'); migrations[db.binding].push({ name,sql,sha256:hash(sql),statements:split(sql) }); }
45
+ }
46
+ const entry = engine === 'workerd' ? 'dist/server/'+workerConfig.main : 'dist/server/index.mjs';
47
+ await readFile(join(root,entry));
48
+ if (engine === 'node' && workerConfig.kv_namespaces?.length) throw new Error('Node container does not support KV');
49
+ for (const secret of declaration.secrets ?? []) if (Object.hasOwn(workerConfig.vars ?? {},secret.binding)) throw new Error('Secret declared as an image variable: '+secret.binding);
50
+ const manifest = { version:1,engine,entry,cacheContract,runner,config:workerConfig,migrations,portabilityMigrations:await portabilityPlan(split),maxRequestBytes:25*1024*1024 };
51
+ await rm(output,{recursive:true,force:true}); await mkdir(join(output,'app'),{recursive:true});
52
+ await cp(join(root,'dist'),join(output,'app/dist'),{recursive:true}); await writeFile(join(output,'app/container.json'),JSON.stringify(manifest,null,2)+'\n');
53
+ if (runner) {
54
+ const within = path => { if (typeof path !== 'string' || path.startsWith('/') || path.split('/').some(part => ['..','.',''].includes(part))) throw new Error('Invalid packaged runner path'); return join(root,path); };
55
+ await readFile(within(runner.entry));
56
+ for (const path of runner.files) await cp(within(path),join(output,'app/runner',path),{recursive:true,filter:path => !path.includes('/tests/') && !path.endsWith('/synthetic.mjs')});
57
+ }
58
+ const base = resolve(import.meta.dirname,'../..');
59
+ for (const path of ['modules/container','modules/portability','modules/deploy-kit','cli/scheduled.mjs','cli/jsonc.mjs','modules/integrations/core/public-address.ts']) { await mkdir(join(output,'runtime',path,'..'),{recursive:true}); await cp(join(base,path),join(output,'runtime',path),{recursive:true,filter:path => !path.includes('/tests/')}); }
60
+ const dependencies = engine === 'workerd' ? { miniflare:'5.20260907.0-alpha' } : {};
61
+ await writeFile(join(output,'runtime/package.json'),JSON.stringify({private:true,type:'module',dependencies}));
62
+ await writeFile(join(output,'Dockerfile'),`FROM node:26.8.1-bookworm-slim\nRUN apt-get update && apt-get install -y --no-install-recommends util-linux ca-certificates && apt-get clean\nRUN mkdir -p /private /archive\nWORKDIR /runtime\nCOPY runtime/package.json ./package.json\nRUN npm install --omit=dev --no-audit --no-fund\nCOPY runtime/ /runtime/\nCOPY app/ /app/\nENV NODE_ENV=production PORT=8080 WRANGLER_SEND_METRICS=false\nEXPOSE 8080\nVOLUME /data\nENTRYPOINT ["sh", "/runtime/modules/container/entrypoint.sh"]\n`);
63
+ return { output,manifest };
64
+ }
65
+ if (process.argv[1] === import.meta.filename) { try { const result=await buildContainer(resolve(process.argv[2])); console.log(JSON.stringify({output:result.output,engine:result.manifest.engine})); } catch(cause) { console.error(cause.message); if(cause.failures)console.error(JSON.stringify(cause.failures));process.exitCode=1; } }
@@ -0,0 +1,17 @@
1
+ import { open, readFile } from 'node:fs/promises';
2
+ import { constants } from 'node:fs';
3
+ import { join, resolve } from 'node:path';
4
+ import { refuseLocalModels } from '../deploy-kit/local-only.mjs';
5
+ import { LOCAL_ONLY_KEYS, localOnlyBinding } from '../deploy-kit/owner.mjs';
6
+ export async function deployInputs({root,envFile,localOnlyKeys=[]}) {
7
+ await refuseLocalModels(root);
8
+ const declaration=JSON.parse(await readFile(join(root,'deploy.json'),'utf8'));
9
+ if(localOnlyBinding(declaration,localOnlyKeys))throw new Error('Deploy declaration contains a local-only binding');
10
+ if(!envFile)throw new Error('Container deploy requires --env-file with private production settings');
11
+ const handle=await open(resolve(root,envFile),constants.O_RDONLY|constants.O_NOFOLLOW);
12
+ let encoded;
13
+ try{const status=await handle.stat();if(!status.isFile()||status.nlink!==1||(status.mode&0o077)!==0||(process.getuid&&status.uid!==process.getuid()))throw new Error('Deploy env file must be an owned private regular file (0600)');encoded=await handle.readFile('utf8');}finally{await handle.close();}
14
+ const settings={};for(const line of encoded.split(/\r?\n/)){if(!line.trim()||line.trim().startsWith('#'))continue;const match=/^([A-Z][A-Z0-9_]*)=(.*)$/.exec(line);if(!match)throw new Error('Invalid deploy env file');if(Object.hasOwn(settings,match[1]))throw new Error('Repeated deploy env key');settings[match[1]]=match[2].replace(/^"(.*)"$/,'$1');}
15
+ if([...LOCAL_ONLY_KEYS,...localOnlyKeys].some(key=>Object.hasOwn(settings,key)))throw new Error('Local-only keys are refused for deploy');
16
+ return{declaration,settings,encoded:Object.entries(settings).map(([key,value])=>key+'='+value).join('\n')+'\n'};
17
+ }
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ umask 077
4
+ mkdir -p /data
5
+ exec flock --nonblock --no-fork /data/host.lock node /runtime/modules/container/host.mjs "$@"
@@ -0,0 +1,43 @@
1
+ import { createServer } from 'node:http';
2
+ import { readFile, mkdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { recordVolumeOwner } from './lock.mjs';
5
+ import { incoming, outgoing } from './ingress.mjs';
6
+ import { parseCron, cronMatches } from '../../cli/scheduled.mjs';
7
+ import { superviseRunner } from './runner.mjs';
8
+ export async function startHost({ root = '/app', data = '/data', settings = process.env, port = Number(process.env.PORT ?? 8080), edge, engineVersion = process.version, automaticTicks=true, runnerEnabled=true } = {}) {
9
+ await mkdir(data, { recursive:true });
10
+ const manifest = JSON.parse(await readFile(join(root, 'container.json'), 'utf8'));
11
+ const release = await recordVolumeOwner(data);
12
+ let engine, server, timer, idleTimer, runner, closing, lastMinute = -1, lastRequest=Date.now();
13
+ try {
14
+ const create = manifest.engine === 'node' ? (await import('./node-engine.mjs')).nodeEngine : (await import('./worker-engine.mjs')).workerEngine;
15
+ engine = await create({ root,data,manifest,settings,edge });
16
+ const schedules = (manifest.config.triggers?.crons ?? []).map(cron => [cron,parseCron(cron)]);
17
+ const tick = async time => { for (const [cron, expression] of schedules) if (cronMatches(expression, new Date(time))) await engine.scheduled({ cron,scheduledTime:time }); await engine.drain(); };
18
+ server = createServer(async (req, res) => {
19
+ try {
20
+ lastRequest=Date.now();
21
+ const request = await incoming(req, { trustedProxy:settings.TRUSTED_PROXY, maxRequestBytes:manifest.maxRequestBytes });
22
+ let response = await engine.fetch(request);
23
+ if (new URL(request.url).pathname === '/health' && response.ok) {
24
+ const body = await response.json(); const deadJobs = engine.dead();
25
+ response = Response.json({ ...body, platform:'container', deployment:{ ...body.deployment, platform:'container', versionId:settings.RER_IMAGE_DIGEST ?? null }, container:{ engine:manifest.engine,engineVersion,deadJobs,runner:manifest.runner ? (runner?.running() ? 'running' : 'stopped') : null } }, { status:deadJobs || (manifest.runner && runnerEnabled && !runner?.running()) ? 503 : response.status,headers:response.headers });
26
+ }
27
+ outgoing(response, res);
28
+ } catch (cause) { res.statusCode = cause.status ?? 500; res.end('Container request failed'); console.error('Container request failed:', cause.name); }
29
+ });
30
+ await new Promise((resolve, reject) => { server.once('error', reject); server.listen(port,'0.0.0.0',resolve); });
31
+ let ticking=false;
32
+ if(automaticTicks){timer = setInterval(async () => { if(ticking)return;ticking=true;const minute = Math.floor(Date.now()/60000); try { if (minute !== lastMinute) { lastMinute = minute; await tick(minute * 60000); } else await engine.drain(); } catch { console.error('Container scheduled event failed'); }finally{ticking=false;} },1000); timer.unref();}
33
+ const close = () => closing ??= (async () => { clearInterval(timer); clearInterval(idleTimer); await runner?.close(); await new Promise(resolve => server.close(resolve)); await engine.close(); await release(); })();
34
+ const idle=Number(settings.RER_CONTAINER_IDLE_SECONDS);if(Number.isFinite(idle)&&idle>0){idleTimer=setInterval(()=>{if(Date.now()-lastRequest>=idle*1000)void close();},Math.min(1000,idle*1000));idleTimer.unref();}
35
+ if(runnerEnabled)runner=superviseRunner({root,manifest,settings,failed(){console.error('Required container runner stopped');void close().finally(()=>{process.exitCode=1;});}});
36
+ return { engine,manifest,server,tick,close };
37
+ } catch (cause) { clearInterval(timer); clearInterval(idleTimer); await runner?.close(); server?.close(); await engine?.close(); await release(); throw cause; }
38
+ }
39
+ if (process.argv[1] === import.meta.filename) {
40
+ let host;
41
+ try { host = await startHost(); console.log('RER_CONTAINER_READY'); } catch (cause) { console.error('Container startup failed:', cause.message?.match(/^[a-z-]+$/)?.[0] ?? cause.name); process.exitCode=1; }
42
+ if (host) for (const signal of ['SIGINT','SIGTERM']) process.once(signal,async () => { await host.close(); process.exit(0); });
43
+ }
@@ -0,0 +1,23 @@
1
+ import { Readable } from 'node:stream';
2
+ import { lookup } from 'node:dns/promises';
3
+ import { isIP } from 'node:net';
4
+ export async function incoming(req, { trustedProxy, maxRequestBytes = 25 * 1024 * 1024 } = {}) {
5
+ const headers = new Headers(); for (const [name, value] of Object.entries(req.headers)) if (value !== undefined) headers.set(name, Array.isArray(value) ? value.join(',') : value);
6
+ const peer = req.socket.remoteAddress?.replace('::ffff:', '') ?? '';
7
+ const trusted = trustedProxy && (isIP(trustedProxy) ? peer===trustedProxy : (await lookup(trustedProxy,{all:true})).some(item=>item.address.replace('::ffff:','')===peer));
8
+ let host = req.headers.host, proto = 'http', address = peer;
9
+ if (trusted) { host = req.headers['x-forwarded-host'] ?? host; proto = req.headers['x-forwarded-proto'] ?? proto; address = req.headers['x-forwarded-for']?.split(',').at(-1)?.trim() ?? peer; }
10
+ for (const name of ['cf-connecting-ip','x-forwarded-host','x-forwarded-proto','x-forwarded-for','forwarded','x-rer-original-url']) headers.delete(name);
11
+ if (!['http', 'https'].includes(proto) || typeof host !== 'string' || !/^(?:[a-zA-Z0-9.-]+|\[[0-9a-fA-F:]+\])(?::[0-9]+)?$/.test(host)) throw new Error('Invalid ingress origin');
12
+ headers.set('cf-connecting-ip', address); headers.set('host', host);
13
+ const url = `${proto}://${host}${req.url}`;
14
+ const chunks = []; let size = 0;
15
+ for await (const chunk of req) { size += chunk.length; if (size > maxRequestBytes) { const error = new Error('Request too large'); error.status = 413; throw error; } chunks.push(chunk); }
16
+ return new Request(url, { method: req.method, headers, ...(!['GET','HEAD'].includes(req.method) ? { body: Buffer.concat(chunks) } : {}) });
17
+ }
18
+ export function outgoing(response, res) {
19
+ res.statusCode = response.status;
20
+ for (const [name, value] of response.headers) if (!['set-cookie','content-length','transfer-encoding'].includes(name)) res.setHeader(name, value);
21
+ const cookies = response.headers.getSetCookie(); if (cookies.length) res.setHeader('set-cookie', cookies);
22
+ if (response.body) { const stream = Readable.fromWeb(response.body); res.once('close', () => stream.destroy()); stream.once('error', () => res.destroy()); stream.pipe(res); } else res.end();
23
+ }
@@ -0,0 +1,11 @@
1
+ import { open } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ // entrypoint.sh holds a kernel flock on this inode for the entire process lifetime.
4
+ // It works across container PID namespaces and releases automatically on SIGKILL.
5
+ // Never unlink the lock inode: a new inode would permit a second live holder.
6
+ export async function recordVolumeOwner(directory) {
7
+ const file = await open(join(directory, 'host.lock'), 'r+', 0o600);
8
+ const write = async () => { const bytes = Buffer.from(JSON.stringify({ pid: process.pid, heartbeat: new Date().toISOString() })); await file.truncate(0); await file.write(bytes, 0, bytes.length, 0); await file.sync(); };
9
+ await write(); const timer = setInterval(() => void write().catch(() => { process.exitCode = 1; }), 10000); timer.unref();
10
+ return async () => { clearInterval(timer); await file.close(); };
11
+ }
@@ -0,0 +1,16 @@
1
+ import { validateMigrations } from '../deploy-kit/migrations.mjs';
2
+ export async function migrate(db, migrations, { ledger = '_rer_deploy_migrations' } = {}) {
3
+ if (!['_rer_deploy_migrations', '_rer_portability_migrations'].includes(ledger)) throw new Error('Invalid migration ledger');
4
+ validateMigrations(migrations);
5
+ await db.prepare(`CREATE TABLE IF NOT EXISTS ${ledger}(name TEXT PRIMARY KEY,sha256 TEXT NOT NULL,state TEXT NOT NULL CHECK(state IN ('applying','applied')))`).run();
6
+ const history = (await db.prepare(`SELECT name,sha256,state FROM ${ledger} ORDER BY name`).all()).results;
7
+ if (history.some((row, i) => row.name !== migrations[i]?.name || row.sha256 !== migrations[i]?.sha256 || row.state !== 'applied')) throw new Error('changed-or-uncertain-migration-history');
8
+ const legacy = await db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='d1_migrations'").first();
9
+ if (ledger === '_rer_deploy_migrations' && legacy && (await db.prepare('SELECT name FROM d1_migrations ORDER BY id').all()).results.some(row => !history.some(prior => prior.name === row.name))) throw new Error('legacy-migration-history-needs-reviewed-hash-adoption');
10
+ for (const migration of migrations.slice(history.length)) {
11
+ await db.prepare(`INSERT INTO ${ledger} VALUES(?,?,'applying')`).bind(migration.name, migration.sha256).run();
12
+ // A claim commits separately. Application and completion share one transaction.
13
+ await db.batch([...migration.statements.map(sql => db.prepare(sql)), db.prepare(`UPDATE ${ledger} SET state='applied' WHERE name=? AND sha256=? AND state='applying'`).bind(migration.name, migration.sha256)]);
14
+ }
15
+ return { applied: migrations.length, pending: 0 };
16
+ }
@@ -0,0 +1,39 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { join } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { D1 } from './adapters/d1.mjs';
5
+ import { Jobs } from './adapters/jobs.mjs';
6
+ import { Objects } from './adapters/objects.mjs';
7
+ import { retainedCache } from './adapters/retained.mjs';
8
+ import { publicFetch } from './adapters/egress.mjs';
9
+ import { assets } from './assets.mjs';
10
+ import { ensurePortabilitySchema } from './portability-schema.mjs';
11
+ import { migrate } from './migrations.mjs';
12
+ export async function nodeEngine({ root, data, manifest, settings, edge }) {
13
+ const platform = new DatabaseSync(join(data, 'platform.sqlite')); platform.exec('PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON');
14
+ const env = { ...manifest.config.vars, ...settings, ASSETS: assets(join(root, 'dist/client')) }, databases = {}, queues = [];
15
+ for (const [binding, plan] of Object.entries(manifest.migrations)) { const db = new D1(join(data, binding + '.sqlite')); databases[binding] = db; env[binding] = db; await migrate(db, plan); await ensurePortabilitySchema(db, manifest.portabilityMigrations); }
16
+ const objects = {};
17
+ for (const bucket of manifest.config.r2_buckets ?? []) { const store = new Objects(platform, join(data, 'objects'), bucket.binding); objects[bucket.binding] = store; env[bucket.binding] = store; }
18
+ await Object.values(objects)[0]?.recover();
19
+ for (const producer of manifest.config.queues?.producers ?? []) { const consumer = manifest.config.queues.consumers.find(item => item.queue === producer.queue); const jobs = new Jobs(platform, producer.queue, { maxRetries: consumer?.max_retries, batchSize: consumer?.max_batch_size }); env[producer.binding] = jobs; queues.push(jobs); }
20
+ if (manifest.config.durable_objects?.bindings?.length) {
21
+ for (const binding of manifest.config.durable_objects.bindings) { if (manifest.cacheContract !== 'lamp-retained-v1' || binding.class_name !== 'SnapshotCache') throw new Error('Unsupported Node binding'); env[binding.name] = retainedCache(platform); }
22
+ }
23
+ globalThis[Symbol.for('rer.container.bindings')] = env;
24
+ const previousFetch = globalThis.fetch; globalThis.fetch = edge ? (input, init) => edge(new Request(input, init), publicFetch) : publicFetch;
25
+ // Lamp uses its persistent retained contract; the optional Cache API is deliberately unsupported.
26
+ globalThis.caches = { open: async () => { throw new Error('Cache API unavailable on container Node'); } };
27
+ const app = (await import(pathToFileURL(join(root, manifest.entry)))).default;
28
+ const pending = new Set(), execution = { waitUntil(promise) { pending.add(promise); promise.catch(() => console.error('Background effect failed')).finally(() => pending.delete(promise)); } };
29
+ return { env, databases, objects, app, execution,
30
+ async fetch(request) {
31
+ if(['GET','HEAD'].includes(request.method)&&!manifest.config.assets?.run_worker_first){const response=await env.ASSETS.fetch(request);if(response.status!==404)return response;}
32
+ return app.fetch(request,env,execution);
33
+ },
34
+ scheduled: event => app.scheduled?.(event, env, execution),
35
+ async drain() { if (app.queue) for (const jobs of queues) await jobs.drain(app.queue.bind(app), env, execution); },
36
+ dead: () => queues.reduce((sum, jobs) => sum + jobs.dead(), 0),
37
+ async close() { await Promise.allSettled([...pending]); await Promise.all(Object.values(databases).map(db => db.close())); platform.close(); globalThis.fetch = previousFetch; },
38
+ };
39
+ }
@@ -0,0 +1,14 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { createHash } from 'node:crypto';
3
+ import { migrate } from './migrations.mjs';
4
+ export async function portabilityPlan(split) {
5
+ const sql = await readFile(new URL('../portability/schema/portability.sql', import.meta.url), 'utf8');
6
+ return [{ name:'0001_portability.sql', sql, sha256:createHash('sha256').update(sql).digest('hex'), statements:split(sql) }];
7
+ }
8
+ // Existing product-owned bookkeeping remains under its original migrations.
9
+ // Independently installed bookkeeping cannot interleave later product migrations.
10
+ export async function ensurePortabilitySchema(db, plan) {
11
+ const owned = await db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='_rer_portability_migrations'").first();
12
+ if (!owned && await db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='rer_portability_state'").first()) return;
13
+ await migrate(db, plan, { ledger:'_rer_portability_migrations' });
14
+ }
@@ -0,0 +1,76 @@
1
+ import sys
2
+
3
+
4
+ def deploy():
5
+ payload = json.load(sys.stdin)
6
+ plan = payload['plan']
7
+ install, lock, path, chain = open_install(plan)
8
+ stage = generation_fd = None
9
+ previous, pending = {}, {}
10
+ try:
11
+ # Retain cleanup identities before Docker/other asynchronous work can
12
+ # give another process an opportunity to substitute private paths.
13
+ for entry in os.listdir(install):
14
+ if re.fullmatch(r'(generation|stage)-[0-9a-f]{32}', entry):
15
+ previous[entry] = retain_generation(install, entry)
16
+ elif re.fullmatch(r'current-[0-9a-f]{32}', entry):
17
+ pending[entry] = file_info(install, entry)
18
+ if pending[entry] is None:
19
+ refuse('Host deployment path changed')
20
+ manifest = retain_file(install, 'current.json')
21
+ def validate_previous():
22
+ for entry, (fd, entries) in previous.items():
23
+ validate_generation_entries(install, entry, fd, entries)
24
+ for entry, created in pending.items():
25
+ same_inode(created, file_info(install, entry))
26
+ same_file(install, 'current.json', manifest)
27
+ owned, containers = inventory(plan)
28
+ admit_ports(plan, owned, containers)
29
+ if payload.get('phase') == 'check':
30
+ print('{}')
31
+ return
32
+ stage, generation, generation_fd, records = stage_files(install, payload['files'])
33
+ caddy = prepare_resources(plan, owned)
34
+ # Recheck destinations and all resource ownership immediately before
35
+ # committing a complete generation and before the first shutdown.
36
+ inventory(plan)
37
+ validate_install(chain, lock, plan)
38
+ validate_previous()
39
+ validate_generation(install, stage, generation_fd, records)
40
+ publish_generation(install, stage, generation)
41
+ stage = generation
42
+ manifest = commit_manifest(install, generation, manifest)
43
+ stage = None
44
+ def validate():
45
+ validate_install(chain, lock, plan)
46
+ validate_previous()
47
+ validate_generation(install, generation, generation_fd, records)
48
+ replace_containers(plan, validate, payload['files'], caddy)
49
+ validate()
50
+ for entry, (fd, entries) in previous.items():
51
+ remove_generation(install, entry, fd, entries)
52
+ for entry, created in pending.items():
53
+ same_inode(created, file_info(install, entry))
54
+ os.unlink(entry, dir_fd=install)
55
+ print(json.dumps({'installation': path, 'generation': generation}))
56
+ finally:
57
+ try:
58
+ if stage:
59
+ remove_generation(install, stage, generation_fd, {filename: created for filename, (_, created) in records.items()})
60
+ finally:
61
+ if generation_fd is not None:
62
+ os.close(generation_fd)
63
+ for fd, _ in previous.values():
64
+ os.close(fd)
65
+ for _, fd, _ in reversed(chain):
66
+ os.close(fd)
67
+ os.close(lock)
68
+
69
+
70
+ try:
71
+ deploy()
72
+ except BaseException as error:
73
+ # No exceptions from parsers or subprocesses may echo private inputs.
74
+ message = str(error) if isinstance(error, RuntimeError) else 'Host deployment refused: unsafe path or failed operation'
75
+ print(message, file=sys.stderr)
76
+ sys.exit(1)