@omega.js/manager 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (416) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/LICENSE +98 -0
  3. package/README.md +165 -0
  4. package/bin/omega +2 -0
  5. package/bin/omg +2 -0
  6. package/claude-plugin/.claude-plugin/plugin.json +11 -0
  7. package/claude-plugin/.mcp.json +9 -0
  8. package/claude-plugin/README.md +139 -0
  9. package/claude-plugin/hooks/gate/mark.sh +58 -0
  10. package/claude-plugin/hooks/gate/run.sh +97 -0
  11. package/claude-plugin/hooks/guard/run.sh +249 -0
  12. package/claude-plugin/hooks/hooks.json +63 -0
  13. package/claude-plugin/hooks/inject/run.sh +127 -0
  14. package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
  15. package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
  16. package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
  17. package/claude-plugin/hooks/quality/run.sh +132 -0
  18. package/claude-plugin/hooks/shape/run.sh +45 -0
  19. package/claude-plugin/mcp-router-launch.js +29 -0
  20. package/claude-plugin/skills/README.md +48 -0
  21. package/claude-plugin/skills/accessibility/SKILL.md +31 -0
  22. package/claude-plugin/skills/analytics/SKILL.md +30 -0
  23. package/claude-plugin/skills/backend/SKILL.md +25 -0
  24. package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
  25. package/claude-plugin/skills/browser/SKILL.md +64 -0
  26. package/claude-plugin/skills/client/SKILL.md +24 -0
  27. package/claude-plugin/skills/desktop/SKILL.md +25 -0
  28. package/claude-plugin/skills/extension/SKILL.md +25 -0
  29. package/claude-plugin/skills/main/SKILL.md +54 -0
  30. package/claude-plugin/skills/manager/SKILL.md +25 -0
  31. package/claude-plugin/skills/seo/SKILL.md +36 -0
  32. package/claude-plugin/skills/theme/SKILL.md +33 -0
  33. package/claude-plugin/skills/web/SKILL.md +29 -0
  34. package/dist/cli-run.js +34 -0
  35. package/dist/cli.js +29 -0
  36. package/dist/commands/build.js +17 -0
  37. package/dist/commands/clean.js +16 -0
  38. package/dist/commands/company.js +46 -0
  39. package/dist/commands/deploy.js +143 -0
  40. package/dist/commands/dev.js +437 -0
  41. package/dist/commands/devlog.js +25 -0
  42. package/dist/commands/help.js +48 -0
  43. package/dist/commands/manage.js +53 -0
  44. package/dist/commands/migrate.js +76 -0
  45. package/dist/commands/onboard.js +31 -0
  46. package/dist/commands/pipeline.js +319 -0
  47. package/dist/commands/test.js +380 -0
  48. package/dist/commands/update.js +128 -0
  49. package/dist/commands/version.js +8 -0
  50. package/dist/company-init.js +167 -0
  51. package/dist/company.js +283 -0
  52. package/dist/config.js +1359 -0
  53. package/dist/devlog/index.js +226 -0
  54. package/dist/devlog/lib/collect-commits.js +136 -0
  55. package/dist/devlog/lib/generate-post.js +201 -0
  56. package/dist/devlog/lib/ghostii.js +122 -0
  57. package/dist/devlog/lib/project-map.js +98 -0
  58. package/dist/devlog/lib/publish-website.js +88 -0
  59. package/dist/index.js +45 -0
  60. package/dist/lib/agents-md.js +201 -0
  61. package/dist/lib/analytics-secret.js +22 -0
  62. package/dist/lib/argv.js +21 -0
  63. package/dist/lib/auth-admin.js +120 -0
  64. package/dist/lib/automation-client.js +210 -0
  65. package/dist/lib/backend-marketing.js +49 -0
  66. package/dist/lib/brand.js +240 -0
  67. package/dist/lib/bundle-id.js +53 -0
  68. package/dist/lib/claude-settings.js +125 -0
  69. package/dist/lib/company-scaffold.js +213 -0
  70. package/dist/lib/company.js +227 -0
  71. package/dist/lib/config-flow.js +350 -0
  72. package/dist/lib/config-write.js +41 -0
  73. package/dist/lib/custom-target.js +75 -0
  74. package/dist/lib/domain-utils.js +19 -0
  75. package/dist/lib/duration.js +39 -0
  76. package/dist/lib/env-order.js +236 -0
  77. package/dist/lib/env-secret.js +78 -0
  78. package/dist/lib/firestore-rest.js +326 -0
  79. package/dist/lib/framework-bin.js +156 -0
  80. package/dist/lib/gitignore.js +63 -0
  81. package/dist/lib/google-auth.js +495 -0
  82. package/dist/lib/google-token.js +66 -0
  83. package/dist/lib/jwt.js +31 -0
  84. package/dist/lib/legacy-oauth.js +59 -0
  85. package/dist/lib/node-version.js +127 -0
  86. package/dist/lib/owner-plan.js +78 -0
  87. package/dist/lib/package-scripts.js +152 -0
  88. package/dist/lib/preflight.js +470 -0
  89. package/dist/lib/product-create.js +144 -0
  90. package/dist/lib/run-command.js +74 -0
  91. package/dist/lib/run-gates.js +70 -0
  92. package/dist/lib/run-output.js +42 -0
  93. package/dist/lib/run-summary.js +384 -0
  94. package/dist/lib/scaffold.js +427 -0
  95. package/dist/lib/service-input.js +207 -0
  96. package/dist/lib/service-runner.js +466 -0
  97. package/dist/lib/stale.js +24 -0
  98. package/dist/lib/target-selection.js +155 -0
  99. package/dist/lib/verb-fanout.js +128 -0
  100. package/dist/lib/verify-live.js +188 -0
  101. package/dist/manage.js +311 -0
  102. package/dist/omega-bin.js +7 -0
  103. package/dist/onboard.js +532 -0
  104. package/dist/services/account/ensure/users.js +259 -0
  105. package/dist/services/account/index.js +97 -0
  106. package/dist/services/account/lib/backend-client.js +107 -0
  107. package/dist/services/account/lib/password.js +36 -0
  108. package/dist/services/account/lib/resolve-password.js +100 -0
  109. package/dist/services/advertising/ensure/sites.js +101 -0
  110. package/dist/services/advertising/index.js +101 -0
  111. package/dist/services/advertising/lib/adsense-api.js +41 -0
  112. package/dist/services/ai/ensure/keys.js +21 -0
  113. package/dist/services/ai/index.js +36 -0
  114. package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
  115. package/dist/services/analytics/ensure/google-streams.js +256 -0
  116. package/dist/services/analytics/ensure/meta-pixel.js +22 -0
  117. package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
  118. package/dist/services/analytics/index.js +134 -0
  119. package/dist/services/analytics/lib/analytics-api.js +156 -0
  120. package/dist/services/analytics/lib/meta-api.js +83 -0
  121. package/dist/services/analytics/lib/pixel-account.js +78 -0
  122. package/dist/services/analytics/lib/pixel-provision.js +152 -0
  123. package/dist/services/analytics/lib/pixel-specs.js +57 -0
  124. package/dist/services/analytics/lib/pixel-token.js +88 -0
  125. package/dist/services/analytics/lib/property-flow.js +77 -0
  126. package/dist/services/analytics/lib/tiktok-api.js +157 -0
  127. package/dist/services/analytics/lib/tiktok-auth.js +153 -0
  128. package/dist/services/assets/ensure/logo-gen.js +68 -0
  129. package/dist/services/assets/index.js +109 -0
  130. package/dist/services/assets/lib/assets-config.js +168 -0
  131. package/dist/services/assets/lib/brandmark-api.js +160 -0
  132. package/dist/services/assets/lib/derived.js +113 -0
  133. package/dist/services/assets/lib/font-loader.js +67 -0
  134. package/dist/services/assets/lib/reconcile.js +122 -0
  135. package/dist/services/assets/lib/reset.js +118 -0
  136. package/dist/services/assets/lib/svg-logo-generator.js +154 -0
  137. package/dist/services/assets/lib/svg-to-black.js +32 -0
  138. package/dist/services/assets/write/favicons.js +82 -0
  139. package/dist/services/assets/write/icons.js +64 -0
  140. package/dist/services/assets/write/process.js +96 -0
  141. package/dist/services/assets/write/reconcile.js +30 -0
  142. package/dist/services/assets/write/social-icons.js +108 -0
  143. package/dist/services/assets/write/templates.js +345 -0
  144. package/dist/services/bookmark/ensure/sync.js +255 -0
  145. package/dist/services/bookmark/index.js +17 -0
  146. package/dist/services/campaigns/ensure/contact-person.js +40 -0
  147. package/dist/services/campaigns/ensure/custom-fields.js +84 -0
  148. package/dist/services/campaigns/ensure/domain-auth.js +92 -0
  149. package/dist/services/campaigns/ensure/event-webhook.js +89 -0
  150. package/dist/services/campaigns/ensure/link-branding.js +187 -0
  151. package/dist/services/campaigns/ensure/list.js +50 -0
  152. package/dist/services/campaigns/ensure/segments.js +109 -0
  153. package/dist/services/campaigns/ensure/sender-identity.js +153 -0
  154. package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
  155. package/dist/services/campaigns/index.js +79 -0
  156. package/dist/services/campaigns/lib/dns-sync.js +81 -0
  157. package/dist/services/campaigns/lib/segment-query.js +138 -0
  158. package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
  159. package/dist/services/captcha/ensure/site-key.js +83 -0
  160. package/dist/services/captcha/index.js +78 -0
  161. package/dist/services/captcha/lib/console-url.js +45 -0
  162. package/dist/services/captcha/lib/recaptcha-api.js +36 -0
  163. package/dist/services/certificates/ensure/api-key.js +22 -0
  164. package/dist/services/certificates/ensure/bundle-ids.js +115 -0
  165. package/dist/services/certificates/ensure/certificates.js +187 -0
  166. package/dist/services/certificates/ensure/profiles.js +143 -0
  167. package/dist/services/certificates/index.js +225 -0
  168. package/dist/services/certificates/lib/apple-api.js +183 -0
  169. package/dist/services/certificates/lib/certificate-manager.js +252 -0
  170. package/dist/services/certificates/lib/identifier-manager.js +107 -0
  171. package/dist/services/certificates/lib/keychain.js +135 -0
  172. package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
  173. package/dist/services/certificates/lib/profile-manager.js +115 -0
  174. package/dist/services/chat/data/baseline-knowledge.md +96 -0
  175. package/dist/services/chat/ensure/chat.js +106 -0
  176. package/dist/services/chat/ensure/user.js +45 -0
  177. package/dist/services/chat/index.js +139 -0
  178. package/dist/services/chat/lib/baseline-knowledge.js +137 -0
  179. package/dist/services/cloud/ensure/authentication.js +267 -0
  180. package/dist/services/cloud/ensure/billing.js +120 -0
  181. package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
  182. package/dist/services/cloud/ensure/database.js +60 -0
  183. package/dist/services/cloud/ensure/firestore.js +79 -0
  184. package/dist/services/cloud/ensure/functions.js +49 -0
  185. package/dist/services/cloud/ensure/hosting.js +398 -0
  186. package/dist/services/cloud/ensure/oauth-consent.js +248 -0
  187. package/dist/services/cloud/ensure/project-settings.js +72 -0
  188. package/dist/services/cloud/ensure/sdk-config.js +95 -0
  189. package/dist/services/cloud/ensure/service-account.js +152 -0
  190. package/dist/services/cloud/ensure/services.js +103 -0
  191. package/dist/services/cloud/ensure/storage.js +47 -0
  192. package/dist/services/cloud/index.js +130 -0
  193. package/dist/services/cloud/lib/access-heal.js +130 -0
  194. package/dist/services/cloud/lib/firebase-api.js +662 -0
  195. package/dist/services/cloud/lib/project-flow.js +102 -0
  196. package/dist/services/directory/ensure/entry.js +48 -0
  197. package/dist/services/directory/index.js +64 -0
  198. package/dist/services/directory/lib/blocks.js +104 -0
  199. package/dist/services/disperse/index.js +45 -0
  200. package/dist/services/disperse/write/certs.js +117 -0
  201. package/dist/services/domain/ensure/nameservers.js +179 -0
  202. package/dist/services/domain/index.js +69 -0
  203. package/dist/services/domain/lib/namecheap-api.js +167 -0
  204. package/dist/services/domain/lib/registrars.js +46 -0
  205. package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
  206. package/dist/services/edge/ensure/cache-rules.js +105 -0
  207. package/dist/services/edge/ensure/dns-records.js +304 -0
  208. package/dist/services/edge/ensure/email-routing.js +292 -0
  209. package/dist/services/edge/ensure/rules-configuration.js +98 -0
  210. package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
  211. package/dist/services/edge/ensure/rules-redirect.js +107 -0
  212. package/dist/services/edge/ensure/rules-response-headers.js +90 -0
  213. package/dist/services/edge/ensure/rules-security.js +142 -0
  214. package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
  215. package/dist/services/edge/ensure/workers.js +219 -0
  216. package/dist/services/edge/ensure/zone-settings.js +108 -0
  217. package/dist/services/edge/ensure/zone.js +183 -0
  218. package/dist/services/edge/index.js +73 -0
  219. package/dist/services/edge/lib/cloudflare-api.js +84 -0
  220. package/dist/services/edge/lib/dns-records-helpers.js +518 -0
  221. package/dist/services/edge/lib/read-cache.js +16 -0
  222. package/dist/services/edge/lib/ruleset-helper.js +108 -0
  223. package/dist/services/edge/workers/omega-api-proxy.js +43 -0
  224. package/dist/services/email/data/baseline-filter.md +4 -0
  225. package/dist/services/email/data/baseline-knowledge.md +86 -0
  226. package/dist/services/email/ensure/agent.js +107 -0
  227. package/dist/services/email/ensure/user.js +45 -0
  228. package/dist/services/email/index.js +138 -0
  229. package/dist/services/email/lib/baseline.js +82 -0
  230. package/dist/services/email/lib/knowledge-file.js +53 -0
  231. package/dist/services/forms/ensure/form.js +46 -0
  232. package/dist/services/forms/ensure/user.js +45 -0
  233. package/dist/services/forms/index.js +138 -0
  234. package/dist/services/migrations/ensure/notifications.js +189 -0
  235. package/dist/services/migrations/ensure/orders.js +34 -0
  236. package/dist/services/migrations/ensure/payment-provider.js +132 -0
  237. package/dist/services/migrations/ensure/payments-intents.js +34 -0
  238. package/dist/services/migrations/ensure/state-retirement.js +266 -0
  239. package/dist/services/migrations/ensure/targets-rename.js +124 -0
  240. package/dist/services/migrations/ensure/users.js +1176 -0
  241. package/dist/services/migrations/index.js +101 -0
  242. package/dist/services/migrations/lib/attribution-touch.js +64 -0
  243. package/dist/services/migrations/lib/ensure-metadata.js +144 -0
  244. package/dist/services/migrations/lib/migration-runner.js +473 -0
  245. package/dist/services/migrations/lib/sanitize-strings.js +112 -0
  246. package/dist/services/migrations/lib/schema-validator.js +154 -0
  247. package/dist/services/monitoring/ensure/dsn.js +54 -0
  248. package/dist/services/monitoring/ensure/projects.js +135 -0
  249. package/dist/services/monitoring/index.js +54 -0
  250. package/dist/services/monitoring/lib/sentry-api.js +94 -0
  251. package/dist/services/newsletter/ensure/custom-fields.js +90 -0
  252. package/dist/services/newsletter/ensure/publication.js +94 -0
  253. package/dist/services/newsletter/ensure/segments.js +140 -0
  254. package/dist/services/newsletter/ensure/webhook.js +108 -0
  255. package/dist/services/newsletter/index.js +64 -0
  256. package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
  257. package/dist/services/newsletter/lib/segment-automation.js +577 -0
  258. package/dist/services/payment/ensure/chargebee-account.js +30 -0
  259. package/dist/services/payment/ensure/chargebee-products.js +359 -0
  260. package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
  261. package/dist/services/payment/ensure/paypal-account.js +36 -0
  262. package/dist/services/payment/ensure/paypal-products.js +374 -0
  263. package/dist/services/payment/ensure/paypal-webhook.js +128 -0
  264. package/dist/services/payment/ensure/stripe-account.js +93 -0
  265. package/dist/services/payment/ensure/stripe-disputes.js +56 -0
  266. package/dist/services/payment/ensure/stripe-products.js +245 -0
  267. package/dist/services/payment/ensure/stripe-radar.js +74 -0
  268. package/dist/services/payment/ensure/stripe-webhook.js +130 -0
  269. package/dist/services/payment/index.js +163 -0
  270. package/dist/services/payment/lib/chargebee-api.js +338 -0
  271. package/dist/services/payment/lib/payment-utils.js +116 -0
  272. package/dist/services/payment/lib/paypal-api.js +354 -0
  273. package/dist/services/payment/lib/provider-setup.js +134 -0
  274. package/dist/services/payment/lib/stripe-api.js +152 -0
  275. package/dist/services/repo/ensure/org.js +70 -0
  276. package/dist/services/repo/ensure/pages.js +72 -0
  277. package/dist/services/repo/ensure/repo.js +90 -0
  278. package/dist/services/repo/index.js +52 -0
  279. package/dist/services/repo/lib/github-api.js +196 -0
  280. package/dist/services/search/ensure/ga-link.js +57 -0
  281. package/dist/services/search/ensure/property.js +137 -0
  282. package/dist/services/search/ensure/sitemaps.js +70 -0
  283. package/dist/services/search/index.js +61 -0
  284. package/dist/services/search/lib/search-console-api.js +92 -0
  285. package/dist/services/seo/ensure/github-repos.js +271 -0
  286. package/dist/services/seo/index.js +55 -0
  287. package/dist/services/seo/lib/gh-api.js +161 -0
  288. package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
  289. package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
  290. package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
  291. package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
  292. package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
  293. package/dist/services/seo/templates/index.js +57 -0
  294. package/dist/services/server/ensure/brands.js +63 -0
  295. package/dist/services/server/index.js +42 -0
  296. package/dist/services/testing/ensure/target-checks.js +97 -0
  297. package/dist/services/testing/index.js +11 -0
  298. package/dist/services/testing/lib/checks.js +467 -0
  299. package/dist/services/update/index.js +20 -0
  300. package/dist/services/update/lib/cache.js +59 -0
  301. package/dist/services/update/lib/fingerprint.js +157 -0
  302. package/dist/services/update/write/targets.js +206 -0
  303. package/dist/services/workspace/ensure/agents.js +42 -0
  304. package/dist/services/workspace/ensure/claude-settings.js +34 -0
  305. package/dist/services/workspace/ensure/config.js +54 -0
  306. package/dist/services/workspace/ensure/defaults.js +60 -0
  307. package/dist/services/workspace/ensure/env-keys.js +55 -0
  308. package/dist/services/workspace/ensure/env-order.js +66 -0
  309. package/dist/services/workspace/ensure/env-rules.js +97 -0
  310. package/dist/services/workspace/ensure/gitignore.js +18 -0
  311. package/dist/services/workspace/ensure/scripts.js +126 -0
  312. package/dist/services/workspace/ensure/structure.js +97 -0
  313. package/dist/services/workspace/ensure/translation-sdk.js +148 -0
  314. package/dist/services/workspace/ensure/workflows.js +54 -0
  315. package/dist/services/workspace/index.js +8 -0
  316. package/dist/vendor/account/engine.js +182 -0
  317. package/dist/vendor/account/features.js +220 -0
  318. package/dist/vendor/account/index.js +53 -0
  319. package/dist/vendor/account/schema.js +272 -0
  320. package/dist/vendor/account/subscription.js +38 -0
  321. package/dist/vendor/config/company.js +31 -0
  322. package/dist/vendor/config/defaults.js +173 -0
  323. package/dist/vendor/config/demo.js +18 -0
  324. package/dist/vendor/config/desktop-artifacts.js +110 -0
  325. package/dist/vendor/config/edit.js +769 -0
  326. package/dist/vendor/config/env-delivery.js +145 -0
  327. package/dist/vendor/config/env-rules.js +93 -0
  328. package/dist/vendor/config/env-schema.js +1078 -0
  329. package/dist/vendor/config/env.js +445 -0
  330. package/dist/vendor/config/hooks.js +97 -0
  331. package/dist/vendor/config/index.js +237 -0
  332. package/dist/vendor/config/instances.js +208 -0
  333. package/dist/vendor/config/load.js +490 -0
  334. package/dist/vendor/config/merge.js +68 -0
  335. package/dist/vendor/config/order.js +139 -0
  336. package/dist/vendor/config/ports.js +374 -0
  337. package/dist/vendor/config/providers.js +32 -0
  338. package/dist/vendor/config/repo.js +142 -0
  339. package/dist/vendor/config/retired-keys.js +430 -0
  340. package/dist/vendor/config/schema.js +1610 -0
  341. package/dist/vendor/config/secrets.js +50 -0
  342. package/dist/vendor/config/seed.js +34 -0
  343. package/dist/vendor/config/site-global.js +205 -0
  344. package/dist/vendor/config/validate.js +554 -0
  345. package/dist/vendor/config/winback.js +61 -0
  346. package/dist/vendor/devkit/attach-log-file.js +262 -0
  347. package/dist/vendor/devkit/certs.js +199 -0
  348. package/dist/vendor/devkit/ci-workflows.js +520 -0
  349. package/dist/vendor/devkit/cli-router.js +156 -0
  350. package/dist/vendor/devkit/command-path.js +46 -0
  351. package/dist/vendor/devkit/deploy-record.js +180 -0
  352. package/dist/vendor/devkit/flows.js +317 -0
  353. package/dist/vendor/devkit/local-https.js +360 -0
  354. package/dist/vendor/devkit/local.js +1905 -0
  355. package/dist/vendor/devkit/logger.js +128 -0
  356. package/dist/vendor/devkit/omega-bin.js +345 -0
  357. package/dist/vendor/devkit/prompt.js +187 -0
  358. package/dist/vendor/devkit/safe-install.js +18 -0
  359. package/dist/vendor/devkit/stop-signals.js +28 -0
  360. package/dist/vendor/devkit/test/scope.js +162 -0
  361. package/dist/vendor/devkit/translate/cache.js +84 -0
  362. package/dist/vendor/devkit/translate/engine.js +243 -0
  363. package/dist/vendor/devkit/translate/index.js +49 -0
  364. package/dist/vendor/devkit/translate/languages.js +134 -0
  365. package/dist/vendor/devkit/translate/providers.js +188 -0
  366. package/dist/vendor/devkit/update.js +569 -0
  367. package/docs/AGENTS.md +200 -0
  368. package/docs/account.md +45 -0
  369. package/docs/advertising.md +46 -0
  370. package/docs/ai.md +34 -0
  371. package/docs/analytics.md +69 -0
  372. package/docs/assets.md +57 -0
  373. package/docs/bookmark.md +26 -0
  374. package/docs/brand.md +151 -0
  375. package/docs/campaigns.md +96 -0
  376. package/docs/captcha.md +46 -0
  377. package/docs/certificates.md +68 -0
  378. package/docs/chat.md +47 -0
  379. package/docs/cloud.md +110 -0
  380. package/docs/company.md +64 -0
  381. package/docs/directory.md +140 -0
  382. package/docs/disperse.md +46 -0
  383. package/docs/domain.md +56 -0
  384. package/docs/edge.md +234 -0
  385. package/docs/email.md +42 -0
  386. package/docs/forms.md +48 -0
  387. package/docs/index.md +109 -0
  388. package/docs/migration.md +203 -0
  389. package/docs/migrations.md +56 -0
  390. package/docs/monitoring.md +41 -0
  391. package/docs/newsletter.md +48 -0
  392. package/docs/payment.md +83 -0
  393. package/docs/repo.md +48 -0
  394. package/docs/search.md +44 -0
  395. package/docs/seo.md +34 -0
  396. package/docs/server.md +37 -0
  397. package/docs/shared/agent-docs.md +89 -0
  398. package/docs/shared/analytics.md +612 -0
  399. package/docs/shared/brands.md +51 -0
  400. package/docs/shared/breaking-changes.md +497 -0
  401. package/docs/shared/config.md +1387 -0
  402. package/docs/shared/deploys.md +215 -0
  403. package/docs/shared/icons.md +201 -0
  404. package/docs/shared/local-dev.md +147 -0
  405. package/docs/shared/logging.md +202 -0
  406. package/docs/shared/monitoring.md +153 -0
  407. package/docs/shared/publishing.md +183 -0
  408. package/docs/shared/rulings.md +34 -0
  409. package/docs/shared/testing.md +147 -0
  410. package/docs/shared/theming.md +604 -0
  411. package/docs/shared/translation.md +291 -0
  412. package/docs/shared/updates.md +61 -0
  413. package/docs/testing.md +34 -0
  414. package/docs/update.md +36 -0
  415. package/docs/workspace.md +64 -0
  416. package/package.json +88 -0
@@ -0,0 +1,140 @@
1
+ # The directory service — a brand's entry in its parent's directory
2
+
3
+ The `directory` service ([#246](https://github.com/Omega-JS-Stack/omega/issues/246)) is
4
+ how a brand keeps its own listing current in the project ABOVE it. During the manage
5
+ walk the brand PUSHES an entry into the parent project's Firestore `brands` collection at
6
+ `brands/{brand.id}` — identity plus whatever opt-in blocks it declares — and whatever the
7
+ parent runs on top of that collection reads a directory that is never stale.
8
+
9
+ **The boundary.** The framework owes the fresh ENTRY. It owes nothing else. The
10
+ marketplace, hub or admin surface that CONSUMES the directory is brand code and never
11
+ enters the framework — ITW's guest-post sponsorship marketplace is the first consumer, and
12
+ its routes, pricing UI and moderation stay in the ITW brand. That split is why the write is
13
+ a merge, not a replace: the framework owns its own sections of the entry and the hub owns
14
+ everything else it keeps on the same document.
15
+
16
+ Legacy omega-manager wrote this collection centrally, from the brand configs it held in
17
+ `.brands/*/config.json`. A brand migrated onto OMEGA has no central config to be written
18
+ FROM, so the direction inverts: each brand pushes for itself, from its own omega.json5.
19
+
20
+ ## The config a brand writes
21
+
22
+ Opt in at the shared (brand) level of `config/omega.json5`:
23
+
24
+ ```json5
25
+ {
26
+ // Which project's directory this brand belongs in. Required — no parent
27
+ // relationship, no directory to push into.
28
+ parent: 'https://itwcreativeworks.com',
29
+
30
+ // Opt in. Absent, `false`, or an empty block all mean "do not push": the
31
+ // parent publishes the collection for anyone to read, so participation is
32
+ // never implicit.
33
+ directory: {
34
+ enabled: true,
35
+ },
36
+
37
+ // The first BLOCK. Declared → published; absent → the block is removed from
38
+ // the entry on the next walk.
39
+ sponsorships: {
40
+ acceptable: ['productivity', 'business', 'social media'],
41
+ unacceptable: ['link-in-bio services', 'social media link services'],
42
+ prices: {
43
+ 'guest-post': 20,
44
+ 'link-insertion': 10,
45
+ },
46
+ },
47
+ }
48
+ ```
49
+
50
+ Both keys are schema-known in `@omega.js/config`. **Public facts only**: the entry lands in
51
+ a collection the parent declares world-readable (the one operator step below), and the
52
+ validator's secret-shape guard is the hard floor on what can go in them. Credentials never
53
+ appear here — the service authenticates with
54
+ `DIRECTORY_SERVICE_ACCOUNT` in the brand `.env`, a path to the PARENT project's Firebase
55
+ service-account JSON (absolute, or relative to the brand root), the `server` service's
56
+ mechanism exactly. The service account names the target project.
57
+
58
+ ## What lands in the entry
59
+
60
+ ```json5
61
+ // brands/{brand.id} in the parent project
62
+ {
63
+ brand: { id, name, url }, // identity — `url` only when configured
64
+ github: { owner, name, repo }, // repo slugs, omitted unless BOTH halves resolve
65
+ sponsorships: { … }, // the declared block, verbatim
66
+ }
67
+ ```
68
+
69
+ `github` is `@omega.js/config`'s ONE repo derivation
70
+ ([#290](https://github.com/Omega-JS-Stack/omega/issues/290)) — the same `{ owner, name,
71
+ repo }` a backend app reads as `config.resolved.github`. The service never re-derives it,
72
+ and a half-resolved repo (an owner with no name, or the reverse) is omitted rather than
73
+ pushed broken.
74
+
75
+ ## The read rule the parent adds (the one operator step)
76
+
77
+ Nothing in the framework grants the public read. A compiled-rules project is admin-only by
78
+ default — the framework half's `match /{document=**}` is the floor — so the PARENT, the
79
+ project the entries land in, declares the collection public itself. Once, by hand, in its
80
+ own `firestore.rules` source (the brand half, under "Your rules"):
81
+
82
+ ```
83
+ // The brand directory: every brand's own entry, readable by anyone.
84
+ // Writes stay closed — the pushing brand authenticates with a service
85
+ // account and bypasses rules.
86
+ match /brands/{id} {
87
+ allow read: if true;
88
+ allow write: if false;
89
+ }
90
+ ```
91
+
92
+ `omega build` compiles that into the parent's `dist/firestore.rules` and `omega deploy`
93
+ ships it. Skipping the step costs the push nothing (a service account writes past rules
94
+ either way) — it costs every CLIENT that tries to read the directory, which is the whole
95
+ point of the collection.
96
+
97
+ ## The gates and the diff
98
+
99
+ Three gates, each a clean skip, in order: `directory.enabled: true`, a `parent` to push
100
+ into, and not a `demo-*` (emulator-only) brand — the cloud service's gate, for the same
101
+ reason: an offline fixture must never reach a live project. A missing
102
+ `DIRECTORY_SERVICE_ACCOUNT` skips too.
103
+
104
+ The push reads first and writes only on drift, so an unchanged config performs **zero
105
+ writes** on every subsequent walk. Drift is compared against the framework-owned slice of
106
+ the document only, so a hub-owned field appearing beside the entry is not drift and is
107
+ never rewritten. The write is a `patchDoc` whose updateMask names every owned section —
108
+ including the ones with no value this run, which is how a block the brand drops from config
109
+ disappears from the directory instead of lingering.
110
+
111
+ ## Legacy → doc field mapping
112
+
113
+ Legacy source: omega-manager `.brands/<brand-id>/config.json` (read-only reference).
114
+ Target: `brands/{brand.id}` in the parent project.
115
+
116
+ | Legacy `.brands/<id>/config.json` | Directory entry field | Note |
117
+ |---|---|---|
118
+ | `brand.id` | document id `brands/{brand.id}`, and `brand.id` | Unchanged; denormalized into the entry as legacy did |
119
+ | `brand.name` | `brand.name` | Unchanged — the ITW routes read `brand.brand.name` |
120
+ | `brand.url` | `brand.url` | Unchanged — the ITW routes derive the brand's API host from it |
121
+ | `github.orgMain` / `github.orgWebsite` + `github.templates.website.{useOrgWebsite,suffix}` | `github.owner` | **Derived, not carried.** Legacy pushed the raw org pair and the CONSUMER picked between them; the entry now carries the already-resolved owner from `repo.providers.github` ([#290](https://github.com/Omega-JS-Stack/omega/issues/290)) |
122
+ | `brand.id` + `github.templates.website.suffix` (`'-website'` default), composed by the consumer | `github.name` | **Derived, not carried.** One repo per brand now (the brand monorepo), so the `<id><suffix>` composition retires with the split-repo era |
123
+ | — | `github.repo` | New: the `owner/name` slug the GitHub API takes, so no consumer composes it |
124
+ | `sponsorships.acceptable` | `sponsorships.acceptable` | Verbatim |
125
+ | `sponsorships.unacceptable` | `sponsorships.unacceptable` | Verbatim |
126
+ | `sponsorships.prices['guest-post']` | `sponsorships.prices['guest-post']` | Verbatim (USD) |
127
+ | `sponsorships.prices['link-insertion']` | `sponsorships.prices['link-insertion']` | Verbatim (USD) |
128
+ | `parent` | — | Not published: it names WHERE the entry goes, it is not part of the entry |
129
+
130
+ The two placement keys are the only ones the 23 legacy brands with a `sponsorships` block
131
+ ever used. `prices` is an open map, not an enum — a new placement type is a config line, not
132
+ a framework change. `marketing.newsletter.*.sponsorships` in the legacy configs is a
133
+ different thing entirely (newsletter ad slots) and never entered this collection.
134
+
135
+ ## Adding the next block
136
+
137
+ One entry in `packages/manager/src/services/directory/lib/blocks.js` naming the brand-config
138
+ section it reads, plus its schema line in `@omega.js/config`. A block crosses only when the
139
+ brand declares its section, and `OWNED_SECTIONS` picks it up automatically, so removal
140
+ converges for free.
@@ -0,0 +1,46 @@
1
+ # The disperse service — signing artifacts into the targets
2
+
3
+ The `disperse` service is what remains of legacy omega-manager's disperse after the config
4
+ hierarchy dissolved file dispersal: targets read `config/omega.json5` directly, so there is no
5
+ `.brands/` mirror and no per-repo config writes. What cannot ride the hierarchy is exactly
6
+ what this service still moves — binary signing artifacts.
7
+
8
+ It runs on the DELIVERY lane (`BOOT_SERVICES`): every `omega dev` boot, every brand-root
9
+ `omega deploy`, and the full manage walk — after `certificates` (the artifacts must exist) and
10
+ before `update` (builds read the cert files).
11
+
12
+ ## What it delivers
13
+
14
+ One operation, `certs`: the certificates service's signing tree —
15
+ `{companyRoot||brandRoot}/.omega/certificates/apple/` — copied into each desktop/mobile
16
+ target's certs dir. The copy itself is devkit's (`@omega.js/devkit/certs`,
17
+ [#678](https://github.com/Omega-JS-Stack/omega/issues/678)), the ONE delivery step every verb
18
+ rides, so a desktop build reaches the same artifacts through the same rules. What lives here is
19
+ the manage-lane framing: which targets get a pass, the config gate, and the per-rule reporting.
20
+
21
+ Optional rules skip silently when their source is missing; REQUIRED rules warn (the build
22
+ would be unsigned) and the operation returns warned with the count. A brand with no Apple
23
+ artifacts at all — certificates disabled, or never run — is a quiet note, not a warning.
24
+
25
+ A miss is WARN-ONLY — the delivery never deletes, which is why certs is NOT one of the
26
+ reconcile surfaces (those are [assets](assets.md) and [workspace](workspace.md)). Every dest is
27
+ a documented HUMAN drop point (desktop's [signing guide](../../packages/desktop/docs/signing.md)
28
+ tells an operator to `cp` their `.p12`/`.p8` straight into `config/certs/`), and
29
+ `omega company init` scaffolds `.omega/certificates/apple/` EMPTY — which passes the tree guard
30
+ above. A delete-on-missing pass would therefore eat hand-placed signing material on the very
31
+ next build, so a dest with no source in the tree is reported and left exactly where the
32
+ operator put it.
33
+
34
+ ## Config
35
+
36
+ - `certificates.enabled: false` (or `certificates: false`) — nothing to disperse.
37
+ - The target set is every dir mapping to a framework with a cert file map (desktop, mobile).
38
+
39
+ ## Gotcha: no machine composes a target `.env`
40
+
41
+ `.env` composition is GONE ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)). The
42
+ brand-root `.env` is the ONE file humans and the manager edit, and every verb composes its
43
+ target's runtime env from the cascade by schema. A CUSTOM target has no `@omega.js/config` to
44
+ walk the cascade for it, so it INHERITS instead: `manage.js` loads the env chain into
45
+ `process.env` before it spawns anything, which is why a custom target started by `omega dev`
46
+ or `omega deploy` has the brand keys and a standalone run inside the target dir does not.
package/docs/domain.md ADDED
@@ -0,0 +1,56 @@
1
+ # The domain service — the registrar points at Cloudflare
2
+
3
+ The `domain` service does one thing: make the registrar's nameservers point at the brand's
4
+ Cloudflare zone. It runs right after the `edge` service, because the values it writes are the
5
+ nameservers that zone was assigned.
6
+
7
+ ## What it reconciles
8
+
9
+ One operation, `nameservers`:
10
+
11
+ 1. Read the Cloudflare zone to learn its assigned nameservers.
12
+ 2. **namecheap** (the one API registrar today) — read the current nameservers and set them
13
+ when they mismatch.
14
+ 3. **Manual registrars** — an ACTIVE zone proves the nameservers are already set (success); a
15
+ pending zone prints the exact values to set and returns warned.
16
+
17
+ Since [#662](https://github.com/Omega-JS-Stack/omega/issues/662) a brand-new zone that has no
18
+ nameservers assigned yet is WAITED on rather than deferred: a bounded poll (six reads, 5s
19
+ apart) gives Cloudflare the seconds it needs, and only a zone still bare after that — or a run
20
+ with no TTY — reports warned with the rerun message.
21
+
22
+ ## Config
23
+
24
+ `domain` carries TWO roles, each with its own providers block
25
+ ([#425](https://github.com/Omega-JS-Stack/omega/issues/425)):
26
+
27
+ - `domain.providers.<registrar>` — the registrar. Presence picks one; no entry means nothing
28
+ chosen and this service skips. `namecheap` is reconciled via API, everything else
29
+ (`squarespace`, …) is manual guidance.
30
+ - `domain.email.providers.<provider>` and `domain.email.forwarding` — the MAILBOX provider.
31
+ This service does not read them: the `edge` service's `dns-records` and `email-routing`
32
+ operations do.
33
+
34
+ `domain.enabled: false` skips the service.
35
+
36
+ ## Credentials
37
+
38
+ - `CLOUDFLARE_TOKEN` — always, even for a manual registrar: the required nameserver values
39
+ come off the zone.
40
+ - `NAMECHEAP_USERNAME` + `NAMECHEAP_API_KEY` — only when the registrar is namecheap (their
41
+ registry entries carry a `when` that drops them otherwise).
42
+
43
+ ## Gotchas
44
+
45
+ - **Namecheap's API is IP-whitelisted.** The client detects its own public IP via
46
+ `api.ipify.org`; that address must be whitelisted in the Namecheap dashboard or every call
47
+ is refused. Since [#698](https://github.com/Omega-JS-Stack/omega/issues/698) a refusal is
48
+ walked rather than warned: an interactive run names the IP to add, gates on Enter to open
49
+ the [API access page](https://ap.www.namecheap.com/settings/tools/apiaccess/) (enable, key
50
+ reset and whitelist all live there), and rechecks the refused call until it passes — ENTER
51
+ checks now, `s` steps aside. Non-interactive and dry runs warn and continue as before.
52
+ There is no JSON variant of the API — it is the XML query API.
53
+ - **Nameservers live at the REGISTRABLE domain.** For a subdomain project
54
+ (`playground.omegajs.dev`) that is the parent (`omegajs.dev`) — the zone the edge service
55
+ manages and the domain the registrar actually holds. The split uses the public suffix list,
56
+ not a last-dot-label pop, so `mybrand.co.uk` resolves correctly.
package/docs/edge.md ADDED
@@ -0,0 +1,234 @@
1
+ # The edge service — the brand's Cloudflare zone
2
+
3
+ The `edge` service reconciles the brand's zone to `edge.providers.cloudflare` in
4
+ `config/omega.json5`: the zone itself, DNS records, email routing, zone settings, the five
5
+ rulesets, scheduled speed tests, and workers. It runs early — third in the walk — because
6
+ every DNS-dependent service after it needs the zone to exist. Every handler follows
7
+ read → diff → write (Cloudflare requires fetching current state before patching), and each
8
+ read step is cached to `.omega/cache/cloudflare/{op}.json` as a debugging aid.
9
+
10
+ ## What it reconciles
11
+
12
+ | Operation | What it does |
13
+ |---|---|
14
+ | `zone` | The zone exists; created when missing. A pending zone reports its nameservers, and an interactive run on a manual registrar opens the registrar's nameserver page and polls until the zone activates. The resolved id lands in `edge.providers.cloudflare.zone` ([#434](https://github.com/Omega-JS-Stack/omega/issues/434)) — the id `omega purge` targets from a build, where no Cloudflare token is in play — and rides the run as state for every later operation. |
15
+ | `dns-records` | The required platform record set (GitHub Pages, `www`, the email provider's MX/SPF, DMARC; BIMI and the SendGrid CNAMEs only when configured) plus `dns.records` custom entries, diff-synced: create, update, delete. |
16
+ | `email-routing` | Cloudflare Email Routing, only when `domain.email.providers` names cloudflare; rules come from `domain.email.forwarding` (`[{ from: 'support' \| '*', to: 'inbox@…' }]`). |
17
+ | `zone-settings` | A flat map matching Cloudflare's own setting IDs, diffed in one bulk read and patched per changed setting (Cloudflare has no bulk PATCH). Addon settings (`speed_brain`, `fonts`) need their own GET each. Read-only and absent settings are skipped, and one setting's failure never blocks the rest. |
18
+ | `cache-rules` | The `http_request_cache_settings` entrypoint ruleset, rules matched by description. The framework ships the whole default set (below), so a brand that declares nothing still gets both cache lifetimes. |
19
+ | `rules-managed-transforms` | Cloudflare's managed request/response headers, enabled or disabled per config name, PATCHed in one call. |
20
+ | `rules-redirect` | The `http_request_dynamic_redirect` ruleset — the ONE home for a brand's templated redirects ([#466](https://github.com/Omega-JS-Stack/omega/issues/466), below). |
21
+ | `rules-configuration` | The `http_config_settings` ruleset. |
22
+ | `rules-response-headers` | The `http_response_headers_transform` ruleset (always PUT — at the ruleset's id when it exists, at the phase entrypoint when it does not). |
23
+ | `rules-security` | Custom firewall rules in `http_request_firewall_custom`. |
24
+ | `speed-scheduled-tests` | The homepage's Speed Test schedule (a create-or-replace POST on the custom Speed API endpoint). |
25
+ | `workers` | Worker scripts + routes from `edge.providers.cloudflare.workers` — `script` names a file in the service's own `workers/` dir, `route` supports `{ domain }` templating. Unconfigured workers and routes are removed. |
26
+
27
+ ## Config
28
+
29
+ Everything sits under `edge.providers.cloudflare`:
30
+
31
+ - `enabled: false` — the tri-state opt-out; the whole service skips.
32
+ - `zone` — the resolved zone id, written back by the `zone` operation.
33
+ - `dns` — `spf` (`'strict'`/`'soft'`), `dmarcPolicy`, `spfIncludes`, `dmarcReports.{rua,ruf}`,
34
+ `bimiLogo`, and `records[]` for custom entries such as verification TXTs. The SendGrid
35
+ domain-auth values are NOT here — they are read live off SendGrid every run
36
+ ([#692](https://github.com/Omega-JS-Stack/omega/issues/692), below).
37
+ - `settings` — the zone-settings map, keyed by Cloudflare's setting IDs exactly.
38
+ - `cacheRules[]`, `rules.{managedTransforms,redirect,configuration,responseHeaders,security}`,
39
+ `speedTest.{frequency,region}`, `workers[]`.
40
+
41
+ The manager's defaults hold PLATFORM answers only; company-specific values (DMARC report
42
+ addresses, the BIMI logo, extra CSP hosts) belong in the company or brand layer.
43
+
44
+ **Credential**: `CLOUDFLARE_TOKEN` in the brand `.env`, asked for through the shared setup
45
+ contract. No `brand.url` → the service skips.
46
+
47
+ ## Cache lifetimes: /assets for a year, HTML for a minute
48
+
49
+ The manager's defaults carry the whole `cacheRules` set, so a brand that declares no rules
50
+ still reconciles both of them ([#751](https://github.com/Omega-JS-Stack/omega/issues/751)):
51
+
52
+ | Rule | Matches | Edge TTL | Browser TTL |
53
+ |---|---|---|---|
54
+ | `Assets: Cache for 1 Year` | `/assets/*` plus `/__/auth/iframe.js`, on any host | 1 year | 1 year |
55
+ | `HTML: Short Browser Cache` | a SITE host (not `api.`), outside `/assets`, with no file extension or ending `.html` | 2 hours | 1 minute |
56
+
57
+ The safety in the one-year rule is the CONTENT HASH, and only the CSS and JS bundles carry
58
+ one (`main-39ce99d8.css`, `first-paint-A7T6PAIK.js`): new bytes get a new URL, so the old
59
+ one can be held forever. **Fonts and images under `/assets` are NOT hashed** — they land at
60
+ stable names by design (`assets/fonts/inter-normal-latin.woff2`,
61
+ `assets/images/brand/brandmark-640px.webp`), because `@font-face` src URLs are written into
62
+ theme CSS. So replacing a font file or a logo in place pins the OLD file in visitors'
63
+ browsers for up to a year, and a purge cannot reach a browser copy: ship such a replacement
64
+ under a new filename, or accept the year.
65
+
66
+ HTML is the opposite of a hashed bundle: its URL never changes, so whatever a browser holds
67
+ IS what a returning visitor sees until it expires. One minute is short enough that a deploy
68
+ is visible almost immediately; the rule sets its own 2-hour edge TTL (a cache rule overrides
69
+ the zone's `edge_cache_ttl` setting) and a deploy purges it.
70
+
71
+ **A cache rule is ZONE-scoped, so the HTML rule is guarded by host.** The zone serves the
72
+ brand's site AND `api.<domain>`, whose Firebase rewrites answer extensionless, user-scoped
73
+ GETs (`/authorize`, `/token`, `/omega/**`, `/mcp/**`) — edge-caching one of those would hand
74
+ one user's answer to the next. The zone also serves `emailurl.<domain>`, the proxied SendGrid
75
+ link-tracking CNAME, whose extensionless click and open URLs must reach SendGrid on every hit or
76
+ campaign counts undercount. The guard is `not starts_with(http.host, "api.")` plus the same for
77
+ `emailurl.`, rather than an equality on the site host, because every OTHER host on the zone is
78
+ a site host: the apex, `www`, and a subdomain project served under the parent zone. Those two
79
+ are the non-site hosts the stack creates, at every shape it builds (`api.brand.com`,
80
+ `api.app.brand.com` — `packages/manager/src/services/cloud/ensure/hosting.js`;
81
+ `emailurl.<domain>` — `packages/manager/src/services/edge/lib/dns-records-helpers.js`). A
82
+ hand-added `dns.records` host for some other service is not covered; declare your own
83
+ `cacheRules` in that case. The assets rule needs no guard: both of its paths are static files
84
+ wherever they are served from.
85
+
86
+ The two rules cannot both match one request — the HTML expression excludes `/assets` across
87
+ BOTH of its path shapes (extensionless and `.html`), not just the extensionless one.
88
+ Extensionless is the normal page shape here, since the default redirect rule strips trailing
89
+ slashes (`/about`), and `not … contains "."` is how the free plan says "no file extension"
90
+ (`matches` needs Business).
91
+
92
+ `cacheRules` is an ARRAY, and arrays REPLACE across the config merge chain: a brand
93
+ declaring its own `edge.providers.cloudflare.cacheRules` replaces the framework set whole
94
+ (the same doctrine as `rules.redirect`), so its block has to carry any platform rule it still
95
+ wants. A subdomain project never runs this operation at all (below) — the parent brand's
96
+ zone owns the rules its subdomains are served under. A TTL of `0` is a legal value that
97
+ reaches Cloudflare as declared rather than falling back to the default
98
+ ([#754](https://github.com/Omega-JS-Stack/omega/issues/754)): `browserTtl: 0` is `max-age=0`,
99
+ revalidate on every request, and a `0` edge TTL means whatever Cloudflare's own rules grammar
100
+ makes of it (a full edge bypass is a MODE there, not a TTL).
101
+
102
+ **Static hosting contributes nothing here.** The web target publishes to GitHub Pages, which
103
+ has no header configuration, and nothing in the stack writes a hosting config for the built
104
+ site — so the edge is the ONE place a brand's cache lifetimes are set
105
+ ([docs/shared/deploys.md](shared/deploys.md)).
106
+
107
+ ## Templated redirects live here, not in the web config
108
+
109
+ A redirect whose destination is COMPUTED from the request path — DashQR's printed QR codes
110
+ point at `/c/<id>` for unbounded ids, and every one of them must land on `/code?id=<id>` —
111
+ cannot be enumerated as a page, and static hosting has no server to answer it with. It needs
112
+ edge computing, so `edge.providers.cloudflare.rules.redirect` is its one home
113
+ ([#466](https://github.com/Omega-JS-Stack/omega/issues/466)): the web target's own
114
+ `targets.web.redirects` block shipped in 0.45.0 and is retired, and a config still carrying
115
+ it fails validation naming this key.
116
+
117
+ An entry is `{ name, expression, statusCode, preserveQueryString, targetUrl, enabled }`, in
118
+ Cloudflare's own filter language — nothing is translated, because the edge is what evaluates
119
+ it:
120
+
121
+ ```json5
122
+ {
123
+ name: 'Redirect: QR short code',
124
+ expression: '(starts_with(http.request.uri.path, "/c/"))',
125
+ statusCode: 301,
126
+ // The target carries its OWN `?id=`, so an inbound querystring must not be appended
127
+ preserveQueryString: false,
128
+ targetUrl: { expression: 'concat("https://", http.host, "/code?id=", substring(http.request.uri.path, 3))' },
129
+ enabled: true,
130
+ }
131
+ ```
132
+
133
+ `targetUrl` takes `{ value }` for a fixed destination and `{ expression }` for a computed
134
+ one. The ruleset is reconciled whole — a configured rule is created or updated by `name`,
135
+ and a rule in Cloudflare that config does not name is REMOVED — so the block is the complete
136
+ desired set, the manager's platform defaults (the trailing-slash rule) included.
137
+
138
+ A redirect whose URLs CAN be enumerated is not this: it is a redirect PAGE in the web target
139
+ (`redirect.url` in frontmatter on the `modules/utilities/redirect` layout,
140
+ [docs/web/index.md](../web/index.md)).
141
+
142
+ **`omega dev` does not answer these routes** (the manager call, 2026-08-30). The edge owns
143
+ them, so `/c/<id>` is a plain 404 in dev, exactly as it is against the built output — a
144
+ local mirror of Cloudflare's filter language would exist only to disagree with production.
145
+ Verify a rule against the zone.
146
+
147
+ ## Subdomain projects use the parent zone
148
+
149
+ When `brand.url` is not an apex (`playground.omegajs.dev`), the service works on the PARENT
150
+ zone (`omegajs.dev`) and runs only `zone` and `dns-records` — zone-level settings, rules and
151
+ workers belong to the parent brand. `dns-records` then only touches records belonging to the
152
+ subdomain. Creating the parent zone is inert until the registrar's nameservers point at it,
153
+ which the `domain` service handles right after.
154
+
155
+ ## Gotcha: a subdomain project's API host stays DNS-only, so it gets no Cloudflare geo headers
156
+
157
+ Cloudflare's Universal SSL — every plan, free included — covers the apex and exactly ONE
158
+ label below it (`*.omegajs.dev`). A subdomain project's API host is two labels deep
159
+ (`api.playground.omegajs.dev`), so no certificate at the proxy can terminate TLS for it, and
160
+ the `cloud` service's hosting ensure leaves that CNAME **DNS-only** (grey cloud) with
161
+ Firebase serving the certificate instead (`packages/manager/src/services/cloud/ensure/hosting.js`,
162
+ `zoneTlsCoverage`). Traffic therefore never passes through Cloudflare, and Cloudflare's
163
+ visitor-location headers — the ones the `addVisitorLocationHeaders` managed transform turns
164
+ on — never reach that API. Region and city read null in the backend
165
+ ([#638](https://github.com/Omega-JS-Stack/omega/issues/638)) and no edge setting fixes it:
166
+ nothing is missing, the name is simply outside the certificate.
167
+
168
+ - **Top-level brands are unaffected**: `api.brand.com` is one label under the zone, Universal
169
+ SSL covers it, the record is proxied, and the geo headers arrive.
170
+ - **The fix for a subdomain project is the paid Cloudflare add-on** — Advanced Certificate
171
+ Manager / Total TLS on the zone. The gate already recognizes it: the hosting ensure asks the
172
+ zone for its ACTUAL coverage (`/acm/total_tls`, then active certificate packs) and proxies
173
+ the name the moment a certificate covers it, printing which coverage let it through. A
174
+ lookup failure falls back to not-covered — a DNS-only record works on every plan, a wrongly
175
+ proxied one never does.
176
+ - **The log line names it either way**: verified-but-uncovered prints "Universal SSL stops at
177
+ `*.<zone>`; no Total TLS or deeper cert pack on this zone — CNAME stays DNS-only, Firebase
178
+ serves the certificate".
179
+
180
+ ## The SendGrid records come from SendGrid, not from config
181
+
182
+ `emailauth.<domain>`, the `<id>.<domain>` owner CNAME, `emailurl.<domain>` and both DKIM keys
183
+ are all built from one host, `u<id>.<whitelabel>.sendgrid.net`. Those values are SendGrid's own
184
+ observed facts about the domain, so `dns-records` READS them per run
185
+ (`GET /v3/whitelabel/domains`, the same client the campaigns service uses, `SENDGRID_API_KEY`
186
+ from the brand `.env`) instead of carrying a config copy that nothing ever wrote back
187
+ ([#692](https://github.com/Omega-JS-Stack/omega/issues/692)).
188
+
189
+ Every no-answer skips the whole SendGrid set and prints the reason: no `SENDGRID_API_KEY`, no
190
+ authenticated domain for this brand's domain (the campaigns service creates it), an unreachable
191
+ SendGrid, or a `mail_cname` that is not the `u<id>.<whitelabel>.sendgrid.net` shape — that last
192
+ one names the host it got. A dry run does the same read and names the host in its plan; a
193
+ subdomain project never asks, because the apex record set belongs to the parent brand.
194
+
195
+ ## Gotcha: the branded-link CNAME is grey until SendGrid validates it
196
+
197
+ `emailurl.<domain>` is the one SendGrid record that rides Cloudflare's proxy. SendGrid
198
+ validates a branded link by resolving that host as a CNAME to `sendgrid.net`, and a
199
+ Cloudflare-PROXIED record answers with the edge's own addresses instead — so proxying it
200
+ before validation locks the branding out of ever validating and every emailed link stays
201
+ broken ([#646](https://github.com/Omega-JS-Stack/omega/issues/646)). The handler therefore
202
+ asks SendGrid first (`GET /v3/whitelabel/links`) and only proxies once that host reports
203
+ `valid: true`.
204
+
205
+ Since [#662](https://github.com/Omega-JS-Stack/omega/issues/662) the walk WAITS for that
206
+ answer instead of leaving the flip to a later run: `pollWithSpinner` re-asks SendGrid every
207
+ 10s (ENTER checks now, `s` skips) and the same pass proxies the CNAME. A skipped wait, or a
208
+ run with no TTY, keeps the record grey and returns **warned** with the reason — "SendGrid has
209
+ not validated the branded link — the emailurl CNAME stays unproxied" — so the run summary
210
+ names what is still owed. An unreachable SendGrid or a subdomain project answer NO the same
211
+ way: the unproxied record is the safe half of the pair (with no key or no authenticated
212
+ domain, the SendGrid records are skipped outright).
213
+
214
+ A host with NO link-branding entry is the one NO that never waits — the `campaigns` service
215
+ creates and validates the branding LATER IN THE SAME WALK, when campaigns is enabled for the
216
+ brand ([#693](https://github.com/Omega-JS-Stack/omega/issues/693)), so waiting on it here would
217
+ never end. The line says so, the record lands grey (exactly the state SendGrid validates
218
+ against), and the step is not **warned**: there is nothing pending for this service to finish.
219
+ When the campaigns service's validation passes, it flips that same record to proxied itself;
220
+ this handler's next live read agrees, because a valid branding desires a proxied record. On a
221
+ brand's FIRST walk this handler writes no SendGrid records at all — the live domain-auth read
222
+ finds nothing yet — so the campaigns service writes both link CNAMEs itself.
223
+
224
+ ## Other gotchas
225
+
226
+ - **The Speed API rejects a zone that is not active yet** (`speed.errors.zone_not_active`) —
227
+ the operation skips until activation instead of failing.
228
+ - **A never-used ruleset phase** answers "could not find entrypoint ruleset"; that is detected,
229
+ and the first write POSTs a new ruleset rather than PUTting a missing one.
230
+ - **Rules are matched by their `name`** (Cloudflare's `description`): rename a rule in config
231
+ and the old one is removed and a new one created.
232
+ - **`omega-api-proxy.js`** is for a brand whose api host fronts a dedicated non-Firebase
233
+ backend: the worker carves `/omega` (and the legacy `/backend-manager` alias) out at the
234
+ edge and proxies it to the brand's Cloud Functions, passing everything else through.
package/docs/email.md ADDED
@@ -0,0 +1,42 @@
1
+ # The email service — the brand's Replyify agent
2
+
3
+ The `email` service keeps the brand's customer-service email agent on
4
+ [Replyify](https://replyify.app) in sync — its Gmail filter and its knowledge — and sets the
5
+ agent-owner account to the configured plan. Agents live in Replyify's OWN Firestore, so this
6
+ is an OPERATOR service, with the same clean-skip contract as forms and chat.
7
+
8
+ Not to be confused with `domain.email` (mailbox routing, owned by the `edge` service) or the
9
+ marketing lanes (`campaigns`, `newsletter`).
10
+
11
+ ## What it reconciles
12
+
13
+ - **`agent`** — the FILTER is the brand's Gmail query (the `---filter---` section of the brand
14
+ repo's `config/replyify.md`, or auto-generated as `to:(@domain)`) ANDed with the packaged
15
+ baseline exclusion filter; the KNOWLEDGE is the packaged baseline plus that file's knowledge
16
+ section. Diff-synced and patched with a leaf mask, so Replyify-owned fields survive.
17
+ - **`user`** — the agent owner's subscription set to
18
+ `inbound.email.providers.replyify.plan` (Replyify's Max top tier by default) through the
19
+ shared owner-plan reconciliation.
20
+
21
+ ## Config
22
+
23
+ | Key | Meaning |
24
+ |---|---|
25
+ | `inbound.email.providers.replyify.enabled: false` (or `replyify: false`) | Skip. |
26
+ | `inbound.email.providers.replyify.agentId` | The agent. Missing → minted or asked for. |
27
+ | `inbound.email.providers.replyify.templateAgentId` | The shape donor for create-on-missing. |
28
+ | `inbound.email.providers.replyify.plan` | The owner account's plan. |
29
+ | `inbound.email.providers.replyify.updateAgentInfo: false` | A shared agent another brand manages. |
30
+
31
+ ## The three auth tiers (Ian 2026-07-13)
32
+
33
+ 1. **Operator SA** — `REPLYIFY_SERVICE_ACCOUNT` in the brand `.env`: full create + manage,
34
+ minting the brand's own agent from `templateAgentId` and converging filter/knowledge/plan in
35
+ the same run.
36
+ 2. **User API key** — `REPLYIFY_API_KEY` is recognized; product-API management is not wired yet.
37
+ 3. **Dashboard** — an interactive run opens replyify.app and takes the pasted agent id, with a
38
+ Disable option.
39
+
40
+ ## Gotcha
41
+
42
+ A wrong `agentId` is a visible error, never a silently created orphan document.
package/docs/forms.md ADDED
@@ -0,0 +1,48 @@
1
+ # The forms service — the brand's Slapform contact form
2
+
3
+ The `forms` service keeps the brand's contact form on [Slapform](https://slapform.com) in sync
4
+ and sets the form-owner account to the configured plan, so the brand has full access. Forms
5
+ live in Slapform's OWN Firestore, which makes this an OPERATOR service: it needs a service
6
+ account for Slapform's Firebase project. Every other brand answers "Disable permanently" once
7
+ and is never asked again — that clean skip is the sanctioned outcome, which is why the
8
+ credential never gates a run.
9
+
10
+ ## What it reconciles
11
+
12
+ - **`form`** — the form document's name (`Contact Form - {brand.name}`) and `settings.enabled`,
13
+ diff-synced: read first, patched only on drift.
14
+ - **`user`** — the form owner's account subscription set to
15
+ `forms.providers.slapform.plan` (Slapform's top tier by default), through the shared
16
+ owner-plan reconciliation. The form document carries the owner UID.
17
+
18
+ ## Config
19
+
20
+ | Key | Meaning |
21
+ |---|---|
22
+ | `forms.providers.slapform.enabled: false` (or `slapform: false`) | Skip. |
23
+ | `forms.providers.slapform.formId` | The form. Missing → minted (below) or asked for interactively. |
24
+ | `forms.providers.slapform.templateFormId` | The company layer's shape donor for create-on-missing. |
25
+ | `forms.providers.slapform.plan` | The owner account's plan (`plan.id` required). |
26
+ | `forms.providers.slapform.updateFormInfo: false` | The form is shared and another brand owns its branding — never renamed or re-enabled from here. |
27
+
28
+ The service also requires a `web` target: the contact form lives on the brand's website.
29
+
30
+ ## The three auth tiers (Ian 2026-07-13)
31
+
32
+ 1. **Operator SA** — `SLAPFORM_SERVICE_ACCOUNT` in the brand `.env` (path to the
33
+ service-account JSON, absolute or brand-root-relative). Full create + manage: a missing
34
+ `formId` with a `templateFormId` configured MINTS the brand's own form — a product user
35
+ (email = brand contact email, password through the account service's owner channels) plus a
36
+ doc shape-templated from the donor — writes the new id back into omega.json5, and the
37
+ ensures converge name/settings/plan in the SAME run.
38
+ 2. **User API key** — `SLAPFORM_API_KEY` is recognized, but management through the product's
39
+ public API lands when those routes are verified; today it is a named skip.
40
+ 3. **Dashboard** — an interactive run opens slapform.com and takes the pasted form id
41
+ (comment-preserving writeback, with a Disable option). Non-interactive and dry runs skip
42
+ cleanly.
43
+
44
+ ## Gotcha
45
+
46
+ A `formId` that points at no document is an ERROR, not a create: omega-manager silently
47
+ created a name-only orphan document in that case. Fix the id, or let the operator tier mint
48
+ one.