type: decision
status: active
timestamp: 2026-06-21
tags: [architecture, deploy, ci, dns, sentry, sitemap, robots, dashboard, posthog, comments, monetization]

Family deploy architecture — DNS, gating, releases, dashboards

Per-app GH Actions: main to prod, PR to preview, tags to APK/EXE

Family deploy architecture

DNS auto-provisioning

Each -app repo’s GH Actions workflow checks Cloudflare on first push: if <subdomain>.oriz.in CNAME doesn’t exist, the workflow creates it pointing at <repo>.pages.dev. Uses CLOUDFLARE_API_TOKEN org secret. Self-healing — missing DNS records get auto-provisioned.

# .github/workflows/deploy.yml (per app)
- name: Ensure DNS record
  run: |
    curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.CF_ZONE_ID }}/dns_records" \
      -H "Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN }}" \
      -H "Content-Type: application/json" \
      -d '{"type":"CNAME","name":"${{ inputs.subdomain }}","content":"${{ inputs.repo }}.pages.dev","proxied":true}' \
      || echo "DNS record exists or create failed (idempotent)"

Deploy gating

Branch / eventTargetSubdomain
Push to mainCloudflare Pages production<subdomain>.oriz.in
PR openedCloudflare Pages preview<pr-num>.<subdomain>.oriz.in
Tag v*.*.*Production + APK + EXE<subdomain>.oriz.in + GH release artifacts

Tag protection via GitHub branch ruleset: only the org owner can push tags matching v*.*.*.

Family-wide package updates

When any npm package bumps version, workspace umbrella’s cron runs pnpm update --recursive and opens ONE PR per consumer submodule (~24 PRs). Manual merge per app.

Trigger: weekly cron OR npm publish webhook.

npm publish automation

Changesets-based:

  1. PR includes pnpm changeset declaring version bumps + changelogs.
  2. On main merge, changesets/action publishes any bumped packages to npm + creates GH releases.
  3. Uses NPM_TOKEN org secret.

Family-wide one workflow at the workspace umbrella covers all 25 npm-pkg repos.

Sentry single project

ONE Sentry project covers the entire family. All apps share PUBLIC_SENTRY_DSN. Each error tagged by site_name from src/config/site.ts. Free plan: 5K errors/month total across family.

Spike Protection ON to cap at 5K/mo.

Sitemaps

Robots.txt

Per-app /robots.txt:

User-agent: *
Allow: /
Disallow: /admin
Disallow: /api
Disallow: /signin
Disallow: /preview

Sitemap: https://<subdomain>.oriz.in/sitemap.xml

Apex robots.txt additionally references /sitemap-of-sitemaps.xml.

Image CDN

Per decisions/architecture/image-cdn-fallback-chain.md: Cloudflare Images ? wsrv.nl ? ImageKit fallback. astro-chrome’s <Image> wraps this. App icons + screenshots committed to repo are served as-is from CF Pages (no CDN chain needed for repo assets).

Push notifications

Per decisions/architecture/notifications-fcm-plus-knock.md: Knock orchestrates, FCM transports web push. Each app has its own Firebase Messaging registration. Per-app trigger via Knock workflow.

AdSense placement

AdSense Auto Ads enabled on every app EXCEPT me-app + home-app. Google decides placement at runtime; no ad-slot divs in markup per rules/design/no-ad-slots-in-markup.md. Banner ads above the fold disabled.

A/B testing

PostHog feature flags + experiments. Variants in code via useFeatureFlag('experiment-name'). PostHog dashboard for results. Free tier covers solo dev usage.

Comment moderation

Giscus backed by GitHub Discussions on chirag127/. Spam filter = GitHub’s. Moderation = gh discussion close or block user. No third- party spam service.

Analytics dashboard

Single aggregate dashboard at home-app/admin (auth-gated, only the owner can view). Reads from all 5 analytics tools’ APIs + shows per-app and family-wide cards. Built once in astro-chrome, available on every app’s /admin route as a small embedded widget.

Forks

Forks always keep the upstream slug — both locally and on the remote. Local submodule path mirrors upstream slug. No -fork suffix. Upstream attribution stays unambiguous; rename only happens if the fork diverges hard enough to become a distinct product (rare).

Tool UX, history, cross-app sync

Sign-in monetization, public profiles, content

Registrar + domain posture

Email, observability, artifact hosting

Release cadence + hot-fix + versioning + changelog

Captured in full at release-cadence.

Design surface, browser support, a11y/perf gates

CF Worker quota is account-wide, NOT per-Worker

Cloudflare Workers’ free-tier 100K requests/day quota is shared account-wide across all Workers, not per-Worker. Adding more -worker repos does not multiply the quota ceiling. They all draw from the same 100K/day pool.

What IS isolated on the free tier:

What is NOT isolated:

Implication: stay with a single family Worker at api.oriz.in per hono-worker-api-umbrella.md. Per-app Workers buy nothing on the quota axis; they only add operational complexity.

”No subscriptions” rule applies to developer side only

The monetisation/no-subscriptions-anywhere.md constraint binds:

Apps can ship Google-style Free / Pro / Ultra / Max subscription tiers to their users via Razorpay / Lemon Squeezy. See monetisation/no-subscriptions-anywhere.md § Scope clarification.

Games + kids-games suffixes

Two suffix categories alongside the app family:

Play Store enrollment

Play Store enrollment fee ($25 Google Play Developer) paid as a one-time exception. Currently in Play Store verification. See rules/no-card-on-file.md § One-time fees paid.

Cross-refs


Edit on GitHub · Back to index