# The website — gurbanisoul.com The public website gurbanisoul.com: DNS, SEO, Open Graph images, security headers, imagery policy, screenshots and its gates. - Page: - Updated: 2026-10-26 - From the Sri Guru Granth Sahib Ji — Knowledge Base wiki. Sri Guru Granth Sahib Ji is quoted verbatim, cited by Ang, and never altered. --- The public web property is one Astro static site (`frontend/`) served on the product domain **`gurbanisoul.com`**. It carries two distinct things, deliberately kept separate (see [Brand & domains](https://docs.gurbanisoul.com/engineering/brand/)): - **The Gurbani Soul pages** — `/`, `/features`, `/watch`, `/learn` and its articles, `/privacy` and `/support` — use the marketing layout (`Marketing.astro`): the product's home, its policy pages and the Learn articles. - **The Knowledge Base** — `/library` (its home), `/search`, `/reader`, `/nitnem`, `/browse`, `/themes`, `/lineage`, `/trail`, `/analytics` (Insights), `/constellation`, `/raag-clock`, `/divergence` — uses `Base.astro`: the **Sri Guru Granth Sahib Ji Knowledge Base**, the scholarly study site, with its own name, header and theme. Both are built by `astro build` into `frontend/dist/`, synced to `webapp/static/`, and served by the stdlib Python server locally and by Vercel in production. There is no second site and no second copy of the policy pages — `/privacy` and `/support` are Astro pages under `frontend/src/pages/`, and they are the single source of truth for the App Store URLs. --- ## Domains & DNS | Host | Role | Serves | |---|---|---| | `gurbanisoul.com` | **canonical** apex | the site (200, no redirect) | | `www.gurbanisoul.com` | alias | **308** → apex | | `sggs-knowledge-base.vercel.app` | legacy Vercel alias | **308** → apex | - **Registrar:** Hostinger (2-year term, auto-renew on, WHOIS privacy on). - **DNS:** delegated to **Cloudflare** (account label *Company-Domains*), nameservers `jamie.ns.cloudflare.com` / `luke.ns.cloudflare.com`. The apex/`www` records are **CNAME → Vercel, DNS-only (grey cloud, not proxied)** — Vercel terminates TLS and issues the certificate, and HSTS is on. We do **not** proxy through Cloudflare (it would double-terminate TLS and hide Vercel's own caching/headers). - **Zone ID / Account ID / API tokens** live in the Cloudflare dashboard only — never in this repo. **Canonical rule.** The apex is canonical. ``, `og:url` and the sitemap all use bare `https://gurbanisoul.com/…`. `www` and the old `*.vercel.app` alias 308-redirect to it, so there is exactly one indexable host. This is asserted at release time by `sggs-verify-prod` (a no-redirect guard on `/`, `/privacy`, `/support`) and continuously by `uptime.yml`. ### Email Mail for `gurbanisoul.com` is hosted by **Zoho Mail** (since 2026-09-26). `support@gurbanisoul.com` is an alias of the owner's mailbox, and a catch-all delivers any other address at the domain to it. DNS (Cloudflare, DNS only): MX `mx`/`mx2`/`mx3.zoho.com.au`, SPF `v=spf1 include:zohomail.com.au ~all`, DKIM selector `zmail`, and DMARC `v=DMARC1; p=none; rua=mailto:support@gurbanisoul.com` on `_dmarc` (move to `p=quarantine` after two weeks of clean reports). Do not add Cloudflare Email Routing's SPF include: it would break delivery. The address is the public contact in the App Store listing, the EU Digital Services Act trader declaration (verified 2026-09-26), `SECURITY.md`, and the privacy/support pages. See [runbook: support-inbox](https://docs.gurbanisoul.com/process/runbooks/support-inbox/). --- ## SEO `frontend/astro.config.mjs` sets `site: 'https://gurbanisoul.com'`; `frontend/src/site.ts` holds `SITE_URL`, `SUPPORT_EMAIL`, `APP_STORE_URL`, `APP_STORE_ID`, the `APP_STORE_LIVE` switch, and the photo-credit list. The head SEO/social/PWA block is a shared component, **`frontend/src/components/Seo.astro`** (used by both `Marketing.astro` and the Knowledge Base's `Base.astro`). Every page emits: - `` and `og:url` built from `SITE_URL` + the path (bare apex host); - `description`, `og:title/description/type/image/site_name`, `twitter:card=summary_large_image`; - `theme-color` — always one meta that follows the *page* theme, never the OS. The Knowledge Base (`themeColor="page"`, the default) gets `id="kbThemeColor"`: the inline boot script in `Base.astro` sets it to the header's colour for the stored theme before first paint, and `scripts/core.ts` keeps it equal to `--chrome` afterwards; the light-first marketing shell (`themeColor="light"`) gets `id="themeColorMeta"`, pinned to paper and rewritten by `scripts/theme.ts` on toggle. - `apple-mobile-web-app-title`, ``, an `apple-touch-icon`, an SVG favicon, and — once the app is live (`APP_STORE_LIVE`) — the `apple-itunes-app` Smart App Banner. `og:image` defaults to the page's OG card (`/og/.png`, see **OG images** below). `frontend/public/robots.txt` points crawlers at the canonical host and names `/sitemap.xml`, which is now **generated** (see **Routes, sitemap & RSS**). `Base.astro` derives its canonical/OG from `Astro.url.pathname` so every Knowledge Base page is self-consistent without per-page props. The `SeoInvariants` gate checks every built page's canonical/description/`og:image`(exists on disk)/ `theme-color`, and that any `hreflang` alternate is `en`/`x-default` and self-referential (no `/pa/`). ### Routes, sitemap & RSS - **`frontend/src/routes.ts`** is the single manifest of public routes (`{ path, lastmod, changefreq, priority, og }`). It must list **exactly** the pages that build to HTML — the `SitemapInvariants` gate compares the sitemap's `` set to the built HTML route set. `lastmod` is a **fixed** ISO date (never `Date.now()`) so the sitemap is byte-deterministic. - **`frontend/src/pages/sitemap.xml.ts`** emits `/sitemap.xml` over the manifest, each URL self-referencing its `en` + `x-default` hreflang alternates (no `pa` pages ship yet). - **`frontend/src/pages/rss.xml.ts`** emits `/rss.xml` ("Gurbani Soul — Learn"): every non-draft Learn article, newest first. `RssInvariants` checks it parses and links to the site. - The old `frontend/public/sitemap.xml` was deleted (Astro forbids a public file colliding with a page route). ### OG images `frontend/src/pages/og/[slug].png.ts` renders each 1200×630 card at build with **satori** (HTML/CSS → SVG) + **@resvg/resvg-js** (SVG → PNG): warm-ink background, a gold ੴ, the title in Source Serif 4, the "Gurbani Soul" wordmark, a thin gold rule. `getStaticPaths` returns one card per distinct `og` slug in `routes.ts` (at least `home` and `knowledge-base`). satori needs **static** (non-variable) TTFs, so `frontend/src/og/fonts/` holds pinned instances (`*-og.ttf`) of Source Serif 4 and Sant Lipi with their OFL licences; only the single ੴ glyph is Gurmukhi (satori has no Indic shaping). Cards are byte-deterministic and each ≤ 150 KB. `public/og.jpg` remains a hero crop for any fixed-image use. ### Analytics `frontend/src/components/Analytics.astro` is an `is:inline` loader that injects `/_vercel/insights/script.js` **only** when `location.hostname` matches `/(^|\.)gurbanisoul\.com$/` — a true no-op on serve.py, localhost and staging. There is **no** `@vercel/analytics` import and no third-party host. Vercel Web Analytics must also be **enabled in the Vercel project** (owner action) for data to be collected; until then the loader simply fetches nothing. The `ExternalRequestAllowlist` gate proves the marketing HTML/JS only references an approved host set and that the insights loader is always behind the hostname guard. ### Structured data (JSON-LD) Emitted through **`frontend/src/components/JsonLd.astro`**, which serialises the object and escapes `<` so the payload can never break out of its `