@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.
- package/.claude-plugin/marketplace.json +14 -0
- package/LICENSE +98 -0
- package/README.md +165 -0
- package/bin/omega +2 -0
- package/bin/omg +2 -0
- package/claude-plugin/.claude-plugin/plugin.json +11 -0
- package/claude-plugin/.mcp.json +9 -0
- package/claude-plugin/README.md +139 -0
- package/claude-plugin/hooks/gate/mark.sh +58 -0
- package/claude-plugin/hooks/gate/run.sh +97 -0
- package/claude-plugin/hooks/guard/run.sh +249 -0
- package/claude-plugin/hooks/hooks.json +63 -0
- package/claude-plugin/hooks/inject/run.sh +127 -0
- package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
- package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
- package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
- package/claude-plugin/hooks/quality/run.sh +132 -0
- package/claude-plugin/hooks/shape/run.sh +45 -0
- package/claude-plugin/mcp-router-launch.js +29 -0
- package/claude-plugin/skills/README.md +48 -0
- package/claude-plugin/skills/accessibility/SKILL.md +31 -0
- package/claude-plugin/skills/analytics/SKILL.md +30 -0
- package/claude-plugin/skills/backend/SKILL.md +25 -0
- package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
- package/claude-plugin/skills/browser/SKILL.md +64 -0
- package/claude-plugin/skills/client/SKILL.md +24 -0
- package/claude-plugin/skills/desktop/SKILL.md +25 -0
- package/claude-plugin/skills/extension/SKILL.md +25 -0
- package/claude-plugin/skills/main/SKILL.md +54 -0
- package/claude-plugin/skills/manager/SKILL.md +25 -0
- package/claude-plugin/skills/seo/SKILL.md +36 -0
- package/claude-plugin/skills/theme/SKILL.md +33 -0
- package/claude-plugin/skills/web/SKILL.md +29 -0
- package/dist/cli-run.js +34 -0
- package/dist/cli.js +29 -0
- package/dist/commands/build.js +17 -0
- package/dist/commands/clean.js +16 -0
- package/dist/commands/company.js +46 -0
- package/dist/commands/deploy.js +143 -0
- package/dist/commands/dev.js +437 -0
- package/dist/commands/devlog.js +25 -0
- package/dist/commands/help.js +48 -0
- package/dist/commands/manage.js +53 -0
- package/dist/commands/migrate.js +76 -0
- package/dist/commands/onboard.js +31 -0
- package/dist/commands/pipeline.js +319 -0
- package/dist/commands/test.js +380 -0
- package/dist/commands/update.js +128 -0
- package/dist/commands/version.js +8 -0
- package/dist/company-init.js +167 -0
- package/dist/company.js +283 -0
- package/dist/config.js +1359 -0
- package/dist/devlog/index.js +226 -0
- package/dist/devlog/lib/collect-commits.js +136 -0
- package/dist/devlog/lib/generate-post.js +201 -0
- package/dist/devlog/lib/ghostii.js +122 -0
- package/dist/devlog/lib/project-map.js +98 -0
- package/dist/devlog/lib/publish-website.js +88 -0
- package/dist/index.js +45 -0
- package/dist/lib/agents-md.js +201 -0
- package/dist/lib/analytics-secret.js +22 -0
- package/dist/lib/argv.js +21 -0
- package/dist/lib/auth-admin.js +120 -0
- package/dist/lib/automation-client.js +210 -0
- package/dist/lib/backend-marketing.js +49 -0
- package/dist/lib/brand.js +240 -0
- package/dist/lib/bundle-id.js +53 -0
- package/dist/lib/claude-settings.js +125 -0
- package/dist/lib/company-scaffold.js +213 -0
- package/dist/lib/company.js +227 -0
- package/dist/lib/config-flow.js +350 -0
- package/dist/lib/config-write.js +41 -0
- package/dist/lib/custom-target.js +75 -0
- package/dist/lib/domain-utils.js +19 -0
- package/dist/lib/duration.js +39 -0
- package/dist/lib/env-order.js +236 -0
- package/dist/lib/env-secret.js +78 -0
- package/dist/lib/firestore-rest.js +326 -0
- package/dist/lib/framework-bin.js +156 -0
- package/dist/lib/gitignore.js +63 -0
- package/dist/lib/google-auth.js +495 -0
- package/dist/lib/google-token.js +66 -0
- package/dist/lib/jwt.js +31 -0
- package/dist/lib/legacy-oauth.js +59 -0
- package/dist/lib/node-version.js +127 -0
- package/dist/lib/owner-plan.js +78 -0
- package/dist/lib/package-scripts.js +152 -0
- package/dist/lib/preflight.js +470 -0
- package/dist/lib/product-create.js +144 -0
- package/dist/lib/run-command.js +74 -0
- package/dist/lib/run-gates.js +70 -0
- package/dist/lib/run-output.js +42 -0
- package/dist/lib/run-summary.js +384 -0
- package/dist/lib/scaffold.js +427 -0
- package/dist/lib/service-input.js +207 -0
- package/dist/lib/service-runner.js +466 -0
- package/dist/lib/stale.js +24 -0
- package/dist/lib/target-selection.js +155 -0
- package/dist/lib/verb-fanout.js +128 -0
- package/dist/lib/verify-live.js +188 -0
- package/dist/manage.js +311 -0
- package/dist/omega-bin.js +7 -0
- package/dist/onboard.js +532 -0
- package/dist/services/account/ensure/users.js +259 -0
- package/dist/services/account/index.js +97 -0
- package/dist/services/account/lib/backend-client.js +107 -0
- package/dist/services/account/lib/password.js +36 -0
- package/dist/services/account/lib/resolve-password.js +100 -0
- package/dist/services/advertising/ensure/sites.js +101 -0
- package/dist/services/advertising/index.js +101 -0
- package/dist/services/advertising/lib/adsense-api.js +41 -0
- package/dist/services/ai/ensure/keys.js +21 -0
- package/dist/services/ai/index.js +36 -0
- package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
- package/dist/services/analytics/ensure/google-streams.js +256 -0
- package/dist/services/analytics/ensure/meta-pixel.js +22 -0
- package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
- package/dist/services/analytics/index.js +134 -0
- package/dist/services/analytics/lib/analytics-api.js +156 -0
- package/dist/services/analytics/lib/meta-api.js +83 -0
- package/dist/services/analytics/lib/pixel-account.js +78 -0
- package/dist/services/analytics/lib/pixel-provision.js +152 -0
- package/dist/services/analytics/lib/pixel-specs.js +57 -0
- package/dist/services/analytics/lib/pixel-token.js +88 -0
- package/dist/services/analytics/lib/property-flow.js +77 -0
- package/dist/services/analytics/lib/tiktok-api.js +157 -0
- package/dist/services/analytics/lib/tiktok-auth.js +153 -0
- package/dist/services/assets/ensure/logo-gen.js +68 -0
- package/dist/services/assets/index.js +109 -0
- package/dist/services/assets/lib/assets-config.js +168 -0
- package/dist/services/assets/lib/brandmark-api.js +160 -0
- package/dist/services/assets/lib/derived.js +113 -0
- package/dist/services/assets/lib/font-loader.js +67 -0
- package/dist/services/assets/lib/reconcile.js +122 -0
- package/dist/services/assets/lib/reset.js +118 -0
- package/dist/services/assets/lib/svg-logo-generator.js +154 -0
- package/dist/services/assets/lib/svg-to-black.js +32 -0
- package/dist/services/assets/write/favicons.js +82 -0
- package/dist/services/assets/write/icons.js +64 -0
- package/dist/services/assets/write/process.js +96 -0
- package/dist/services/assets/write/reconcile.js +30 -0
- package/dist/services/assets/write/social-icons.js +108 -0
- package/dist/services/assets/write/templates.js +345 -0
- package/dist/services/bookmark/ensure/sync.js +255 -0
- package/dist/services/bookmark/index.js +17 -0
- package/dist/services/campaigns/ensure/contact-person.js +40 -0
- package/dist/services/campaigns/ensure/custom-fields.js +84 -0
- package/dist/services/campaigns/ensure/domain-auth.js +92 -0
- package/dist/services/campaigns/ensure/event-webhook.js +89 -0
- package/dist/services/campaigns/ensure/link-branding.js +187 -0
- package/dist/services/campaigns/ensure/list.js +50 -0
- package/dist/services/campaigns/ensure/segments.js +109 -0
- package/dist/services/campaigns/ensure/sender-identity.js +153 -0
- package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
- package/dist/services/campaigns/index.js +79 -0
- package/dist/services/campaigns/lib/dns-sync.js +81 -0
- package/dist/services/campaigns/lib/segment-query.js +138 -0
- package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
- package/dist/services/captcha/ensure/site-key.js +83 -0
- package/dist/services/captcha/index.js +78 -0
- package/dist/services/captcha/lib/console-url.js +45 -0
- package/dist/services/captcha/lib/recaptcha-api.js +36 -0
- package/dist/services/certificates/ensure/api-key.js +22 -0
- package/dist/services/certificates/ensure/bundle-ids.js +115 -0
- package/dist/services/certificates/ensure/certificates.js +187 -0
- package/dist/services/certificates/ensure/profiles.js +143 -0
- package/dist/services/certificates/index.js +225 -0
- package/dist/services/certificates/lib/apple-api.js +183 -0
- package/dist/services/certificates/lib/certificate-manager.js +252 -0
- package/dist/services/certificates/lib/identifier-manager.js +107 -0
- package/dist/services/certificates/lib/keychain.js +135 -0
- package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
- package/dist/services/certificates/lib/profile-manager.js +115 -0
- package/dist/services/chat/data/baseline-knowledge.md +96 -0
- package/dist/services/chat/ensure/chat.js +106 -0
- package/dist/services/chat/ensure/user.js +45 -0
- package/dist/services/chat/index.js +139 -0
- package/dist/services/chat/lib/baseline-knowledge.js +137 -0
- package/dist/services/cloud/ensure/authentication.js +267 -0
- package/dist/services/cloud/ensure/billing.js +120 -0
- package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
- package/dist/services/cloud/ensure/database.js +60 -0
- package/dist/services/cloud/ensure/firestore.js +79 -0
- package/dist/services/cloud/ensure/functions.js +49 -0
- package/dist/services/cloud/ensure/hosting.js +398 -0
- package/dist/services/cloud/ensure/oauth-consent.js +248 -0
- package/dist/services/cloud/ensure/project-settings.js +72 -0
- package/dist/services/cloud/ensure/sdk-config.js +95 -0
- package/dist/services/cloud/ensure/service-account.js +152 -0
- package/dist/services/cloud/ensure/services.js +103 -0
- package/dist/services/cloud/ensure/storage.js +47 -0
- package/dist/services/cloud/index.js +130 -0
- package/dist/services/cloud/lib/access-heal.js +130 -0
- package/dist/services/cloud/lib/firebase-api.js +662 -0
- package/dist/services/cloud/lib/project-flow.js +102 -0
- package/dist/services/directory/ensure/entry.js +48 -0
- package/dist/services/directory/index.js +64 -0
- package/dist/services/directory/lib/blocks.js +104 -0
- package/dist/services/disperse/index.js +45 -0
- package/dist/services/disperse/write/certs.js +117 -0
- package/dist/services/domain/ensure/nameservers.js +179 -0
- package/dist/services/domain/index.js +69 -0
- package/dist/services/domain/lib/namecheap-api.js +167 -0
- package/dist/services/domain/lib/registrars.js +46 -0
- package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
- package/dist/services/edge/ensure/cache-rules.js +105 -0
- package/dist/services/edge/ensure/dns-records.js +304 -0
- package/dist/services/edge/ensure/email-routing.js +292 -0
- package/dist/services/edge/ensure/rules-configuration.js +98 -0
- package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
- package/dist/services/edge/ensure/rules-redirect.js +107 -0
- package/dist/services/edge/ensure/rules-response-headers.js +90 -0
- package/dist/services/edge/ensure/rules-security.js +142 -0
- package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
- package/dist/services/edge/ensure/workers.js +219 -0
- package/dist/services/edge/ensure/zone-settings.js +108 -0
- package/dist/services/edge/ensure/zone.js +183 -0
- package/dist/services/edge/index.js +73 -0
- package/dist/services/edge/lib/cloudflare-api.js +84 -0
- package/dist/services/edge/lib/dns-records-helpers.js +518 -0
- package/dist/services/edge/lib/read-cache.js +16 -0
- package/dist/services/edge/lib/ruleset-helper.js +108 -0
- package/dist/services/edge/workers/omega-api-proxy.js +43 -0
- package/dist/services/email/data/baseline-filter.md +4 -0
- package/dist/services/email/data/baseline-knowledge.md +86 -0
- package/dist/services/email/ensure/agent.js +107 -0
- package/dist/services/email/ensure/user.js +45 -0
- package/dist/services/email/index.js +138 -0
- package/dist/services/email/lib/baseline.js +82 -0
- package/dist/services/email/lib/knowledge-file.js +53 -0
- package/dist/services/forms/ensure/form.js +46 -0
- package/dist/services/forms/ensure/user.js +45 -0
- package/dist/services/forms/index.js +138 -0
- package/dist/services/migrations/ensure/notifications.js +189 -0
- package/dist/services/migrations/ensure/orders.js +34 -0
- package/dist/services/migrations/ensure/payment-provider.js +132 -0
- package/dist/services/migrations/ensure/payments-intents.js +34 -0
- package/dist/services/migrations/ensure/state-retirement.js +266 -0
- package/dist/services/migrations/ensure/targets-rename.js +124 -0
- package/dist/services/migrations/ensure/users.js +1176 -0
- package/dist/services/migrations/index.js +101 -0
- package/dist/services/migrations/lib/attribution-touch.js +64 -0
- package/dist/services/migrations/lib/ensure-metadata.js +144 -0
- package/dist/services/migrations/lib/migration-runner.js +473 -0
- package/dist/services/migrations/lib/sanitize-strings.js +112 -0
- package/dist/services/migrations/lib/schema-validator.js +154 -0
- package/dist/services/monitoring/ensure/dsn.js +54 -0
- package/dist/services/monitoring/ensure/projects.js +135 -0
- package/dist/services/monitoring/index.js +54 -0
- package/dist/services/monitoring/lib/sentry-api.js +94 -0
- package/dist/services/newsletter/ensure/custom-fields.js +90 -0
- package/dist/services/newsletter/ensure/publication.js +94 -0
- package/dist/services/newsletter/ensure/segments.js +140 -0
- package/dist/services/newsletter/ensure/webhook.js +108 -0
- package/dist/services/newsletter/index.js +64 -0
- package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
- package/dist/services/newsletter/lib/segment-automation.js +577 -0
- package/dist/services/payment/ensure/chargebee-account.js +30 -0
- package/dist/services/payment/ensure/chargebee-products.js +359 -0
- package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
- package/dist/services/payment/ensure/paypal-account.js +36 -0
- package/dist/services/payment/ensure/paypal-products.js +374 -0
- package/dist/services/payment/ensure/paypal-webhook.js +128 -0
- package/dist/services/payment/ensure/stripe-account.js +93 -0
- package/dist/services/payment/ensure/stripe-disputes.js +56 -0
- package/dist/services/payment/ensure/stripe-products.js +245 -0
- package/dist/services/payment/ensure/stripe-radar.js +74 -0
- package/dist/services/payment/ensure/stripe-webhook.js +130 -0
- package/dist/services/payment/index.js +163 -0
- package/dist/services/payment/lib/chargebee-api.js +338 -0
- package/dist/services/payment/lib/payment-utils.js +116 -0
- package/dist/services/payment/lib/paypal-api.js +354 -0
- package/dist/services/payment/lib/provider-setup.js +134 -0
- package/dist/services/payment/lib/stripe-api.js +152 -0
- package/dist/services/repo/ensure/org.js +70 -0
- package/dist/services/repo/ensure/pages.js +72 -0
- package/dist/services/repo/ensure/repo.js +90 -0
- package/dist/services/repo/index.js +52 -0
- package/dist/services/repo/lib/github-api.js +196 -0
- package/dist/services/search/ensure/ga-link.js +57 -0
- package/dist/services/search/ensure/property.js +137 -0
- package/dist/services/search/ensure/sitemaps.js +70 -0
- package/dist/services/search/index.js +61 -0
- package/dist/services/search/lib/search-console-api.js +92 -0
- package/dist/services/seo/ensure/github-repos.js +271 -0
- package/dist/services/seo/index.js +55 -0
- package/dist/services/seo/lib/gh-api.js +161 -0
- package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
- package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
- package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
- package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
- package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
- package/dist/services/seo/templates/index.js +57 -0
- package/dist/services/server/ensure/brands.js +63 -0
- package/dist/services/server/index.js +42 -0
- package/dist/services/testing/ensure/target-checks.js +97 -0
- package/dist/services/testing/index.js +11 -0
- package/dist/services/testing/lib/checks.js +467 -0
- package/dist/services/update/index.js +20 -0
- package/dist/services/update/lib/cache.js +59 -0
- package/dist/services/update/lib/fingerprint.js +157 -0
- package/dist/services/update/write/targets.js +206 -0
- package/dist/services/workspace/ensure/agents.js +42 -0
- package/dist/services/workspace/ensure/claude-settings.js +34 -0
- package/dist/services/workspace/ensure/config.js +54 -0
- package/dist/services/workspace/ensure/defaults.js +60 -0
- package/dist/services/workspace/ensure/env-keys.js +55 -0
- package/dist/services/workspace/ensure/env-order.js +66 -0
- package/dist/services/workspace/ensure/env-rules.js +97 -0
- package/dist/services/workspace/ensure/gitignore.js +18 -0
- package/dist/services/workspace/ensure/scripts.js +126 -0
- package/dist/services/workspace/ensure/structure.js +97 -0
- package/dist/services/workspace/ensure/translation-sdk.js +148 -0
- package/dist/services/workspace/ensure/workflows.js +54 -0
- package/dist/services/workspace/index.js +8 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/certs.js +199 -0
- package/dist/vendor/devkit/ci-workflows.js +520 -0
- package/dist/vendor/devkit/cli-router.js +156 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/flows.js +317 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/prompt.js +187 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/translate/cache.js +84 -0
- package/dist/vendor/devkit/translate/engine.js +243 -0
- package/dist/vendor/devkit/translate/index.js +49 -0
- package/dist/vendor/devkit/translate/languages.js +134 -0
- package/dist/vendor/devkit/translate/providers.js +188 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/docs/AGENTS.md +200 -0
- package/docs/account.md +45 -0
- package/docs/advertising.md +46 -0
- package/docs/ai.md +34 -0
- package/docs/analytics.md +69 -0
- package/docs/assets.md +57 -0
- package/docs/bookmark.md +26 -0
- package/docs/brand.md +151 -0
- package/docs/campaigns.md +96 -0
- package/docs/captcha.md +46 -0
- package/docs/certificates.md +68 -0
- package/docs/chat.md +47 -0
- package/docs/cloud.md +110 -0
- package/docs/company.md +64 -0
- package/docs/directory.md +140 -0
- package/docs/disperse.md +46 -0
- package/docs/domain.md +56 -0
- package/docs/edge.md +234 -0
- package/docs/email.md +42 -0
- package/docs/forms.md +48 -0
- package/docs/index.md +109 -0
- package/docs/migration.md +203 -0
- package/docs/migrations.md +56 -0
- package/docs/monitoring.md +41 -0
- package/docs/newsletter.md +48 -0
- package/docs/payment.md +83 -0
- package/docs/repo.md +48 -0
- package/docs/search.md +44 -0
- package/docs/seo.md +34 -0
- package/docs/server.md +37 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/testing.md +34 -0
- package/docs/update.md +36 -0
- package/docs/workspace.md +64 -0
- 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.
|
package/docs/disperse.md
ADDED
|
@@ -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.
|