# TailMux technical SEO, GEO and AI discoverability audit

**Audit date:** 2026-07-10
**Repository revision inspected:** `d2b7a26` (`origin/main`)
**Canonical production domain:** <https://tailmux.app>
**Scope:** public marketing site, technical documentation, machine-readable alternates, and crawl policy.

This document preserves the repository and production baseline observed before
implementation. The resulting state and validation evidence are recorded in
docs/seo-geo-implementation-report.md.

## Executive findings

TailMux already has a strong technical foundation for search and answer-engine
discovery:

- public marketing, guides, and documentation are server-side rendered;
- every main content route has a route-level title, description, canonical URL,
  Open Graph data, and a single H1;
- the homepage already emits `SoftwareApplication`, `Organization`,
  `WebSite`, and visible-FAQ `FAQPage` JSON-LD;
- guide routes emit `TechArticle`, breadcrumb, and visible-FAQ JSON-LD;
- the project exposes noindex Markdown mirrors for guides and docs; and
- the site has a dynamic sitemap, a robots file, `llms.txt`, and
  `llms-full.txt`.

The main gaps are architectural rather than cosmetic:

1. product facts are split between `site.ts`, `product.ts`, FAQ strings,
   route copy, and two hand-maintained LLM files;
2. `llms.txt` and `llms-full.txt` are static files in `site/public`, so
   they can drift from the canonical route data;
3. the main sitemap includes `llms.txt` and `llms-full.txt`, even though it
   should contain only indexable HTML canonicals;
4. Markdown mirrors correctly send `X-Robots-Tag: noindex`, but the HTML
   canonicals do not expose a `rel="alternate" type="text/markdown"` link or
   a visible alternate link, and the Markdown response has no canonical
   `Link` header;
5. documentation pages only emit breadcrumbs, not `TechArticle` data;
6. robots currently has one generic allow policy. It neither declares the
   training-vs-search/retrieval decision nor protects against a future
   bot-specific group accidentally overriding the private-path rules;
7. the homepage speaks primarily in product language ("Run every tailnet at
   once") rather than the full non-branded answer phrase ("access multiple
   Tailscale tailnets simultaneously"); and
8. the current checkout contains no deterministic site-level SEO validation
   test or `audit:seo` command.

There is a live-source drift to resolve operationally: on 2026-07-10 the
deployed homepage at `tailmux.app` returned a stronger title and description
than revision `d2b7a26`. The deployed page is crawlable, but the deployment
branch/build source is not defined in this repository. Do not assume a commit
is live until the deploy source is identified.

## Method and evidence

This is a repository-grounded audit, supplemented by a read-only check of the
production domain on 2026-07-10.

- Repository source: route files under `site/src/routes`, configuration under
  `site/src/config`, SEO helpers under `site/src/lib`, and the Bun server in
  `site/server.ts`.
- Production samples: `/`, `/robots.txt`, `/sitemap.xml`,
  `/llms.txt`, `/llms-full.txt`, `/docs/getting-started`, its `.md`
  mirror, a flagship guide and its mirror, and `/docs`.
- Search samples: branded and non-branded searches were checked. The observed
  result set was dominated by Tailscale documentation, issue #183,
  workaround articles, and an unrelated multi-tailnet product; it did not
  reliably surface `tailmux.app`. This is a discovery baseline, not a
  ranking measurement.

No Search Console, Bing Webmaster Tools, Cloudflare analytics, CDN firewall
configuration, or external social/repository metadata was available in the
checkout. Those actions appear in
`docs/seo-geo-offsite-checklist.md`.

## 1. Stack and delivery model

| Area | Finding | Source |
|---|---|---|
| Framework | Vite 8, TanStack React Start, React 19, TypeScript | `site/package.json`, `site/vite.config.ts` |
| Router | TanStack file-based routes with SSR handlers | `site/src/routes/**`, `site/src/router.tsx` |
| Rendering | SSR document shell with client-only analytics/error tracking | `site/src/routes/__root.tsx` |
| Styling | Tailwind CSS v4 plus `site/src/styles.css` | `site/vite.config.ts`, `site/src/styles.css` |
| Content source | TSX route bodies plus config registries; no MDX/Markdown authoring layer | `site/src/routes/**`, `site/src/config/**` |
| Documentation UI | `DocPage` / `GuideArticle` components | `site/src/components/docs.tsx`, `site/src/components/guide.tsx` |
| Metadata | `seo()`, `docPageHead()`, `guideHead()` | `site/src/lib/seo.ts`, `site/src/lib/page-head.ts` |
| Structured data | Reusable JSON-LD helpers | `site/src/lib/structured-data.ts` |
| Sitemap | Dynamic XML route built from docs and guide registries | `site/src/routes/sitemap[.]xml.ts` |
| Robots | Static public text file | `site/public/robots.txt` |
| LLM files | Static public text files | `site/public/llms.txt`, `site/public/llms-full.txt` |
| Markdown mirrors | SSR HTML converted to Markdown on request | `site/src/lib/markdown-mirror.ts`, `site/src/routes/**/*[.]md.ts` |
| Analytics | OpenPanel client-side page and conversion tracking | `site/src/lib/openpanel/**` |
| Error monitoring | Sentry client/server wiring | `site/src/lib/error-tracking.*` |
| Database/auth/billing | Postgres + Drizzle, better-auth, Polar | `site/README.md`, `site/src/lib/{db,auth,billing}` |
| Production server | Bun server serving TanStack Start and static assets | `site/server.ts` |
| Deployment configuration | Not present in repository; production is fronted by Cloudflare | production HTTP headers; no deployment workflow in `.github/workflows` |
| Validation today | `bun run check`, `bun run check-types`, `bun run build`; no site test runner | `site/package.json` |

## 2. Verified product facts and terminology

The implementation and developer documentation support these facts:

- TailMux is a Go CLI plus local router. The recommended profile backend is
  embedded `tsnet`; the external userspace `tailscaled` backend remains a
  compatibility/debugging path.
- A profile owns hostname suffixes. Overlapping ownership is rejected, an
  unclassified `.ts.net` hostname is denied, and a failed connection does not
  fall back to another profile.
- HTTP proxy, HTTPS CONNECT, and SOCKS5 **domain CONNECT** requests can be
  routed by suffix. The documented local access mechanisms are browser PAC,
  `tailmux run`/`tailmux env`, `tailmux connect`/`tailmux ssh`, and
  loopback-only TCP tunnels.
- The router does not create a TUN device, inject a second broad kernel route,
  or mutate the official Tailscale GUI account/session.
- Raw IP targets do not identify a tailnet. They are denied by default and may
  be used only in explicit, profile-pinned TCP-tunnel scenarios covered by the
  configured safety model.
- macOS ships a menu-bar app plus CLI; Linux ships the CLI routing core.
- The product is independent of Tailscale and not affiliated with, endorsed
  by, or sponsored by Tailscale Inc.

Terminology must stay precise:

| Term | Use it for | Do not use it as |
|---|---|---|
| **tailnet** | an independent Tailscale network/control-plane identity | a generic LAN or a profile |
| **profile** | a TailMux configuration/runtime identity for one tailnet | a native macOS VPN interface |
| **route** | TailMux suffix classification decision or explicit configured IP ownership | a system-wide kernel route unless explicitly stated |
| **proxy** | HTTP CONNECT/SOCKS5 domain path through the loopback router | a generic VPN |
| **PAC** | browser/CFNetwork auto-proxy selection | all-protocol routing |
| **local tunnel** | loopback-only TCP forward | a transparent system VPN |
| **tsnet** | embedded, profile-scoped Tailscale node | the official client or a TUN interface |
| **official Tailscale client** | the separately installed Tailscale app/CLI session | a TailMux-managed profile |

### Content accuracy risks found

- Several pages say the official client has only one active account/tailnet as
  an unqualified universal statement. Current official Tailscale documentation
  also describes a multiple-tailnet organization feature. TailMux pages should
  describe its own behavior precisely and avoid making an unnecessarily broad
  claim about every current Tailscale account model.
- The public command reference advertises `tailmux profile login --reset`.
  `internal/embedded/manager.go` explicitly rejects `--reset` for the
  recommended `tsnet` backend. The reference needs a conditional limitation
  rather than presenting the flag as universally supported.
- `docs/08-system-wide-access.md` documents that `tailmux run` routes all
  proxy traffic and currently denies non-owned/public destinations. Marketing
  and guides must not imply that it transparently preserves arbitrary mixed
  internet traffic.

## 3. Crawlability, indexability and production observations

### Production checks (2026-07-10)

| URL | Observed result | Implication |
|---|---|---|
| `https://tailmux.app/` | 200, `text/html; charset=utf-8`, SSR content, canonical, one H1, index/follow, product/organization/website/FAQ JSON-LD | Good primary crawl surface |
| `/robots.txt` | 200, `text/plain;charset=utf-8`, Cloudflare cache HIT | Valid serving; static deployment cache was one year at time of check |
| `/sitemap.xml` | 200, `application/xml; charset=utf-8` | Dynamic XML route reachable |
| `/llms.txt`, `/llms-full.txt` | 200, `text/plain; charset=utf-8` | Reachable but static/manual source in checkout |
| `/docs/getting-started` | 200, HTML | Crawlable docs canonical |
| `/docs/getting-started.md` | 200, `text/markdown; charset=utf-8`, `X-Robots-Tag: noindex` | Correct non-competing mirror policy, missing alternate/canonical relationship |
| flagship guide and `.md` mirror | 200 HTML + 200 Markdown/noindex | Same behavior as docs |
| `/docs` | 307 to `/docs/getting-started` | Correctly keeps the docs landing canonical singular |

The production site is HTTPS and Cloudflare-fronted. No repository-managed
redirect policy for HTTP, apex/www, trailing slashes, staging domains, or
Cloudflare bot/WAF rules exists, so those require deployment-level validation.

### Current robots policy

Current `site/public/robots.txt`:

- allows all crawlers by default;
- disallows account, authentication, checkout, and API paths;
- declares the sitemap; and
- explicitly mentions the two LLM files, but has no separate crawler policy.

The generic policy makes search/retrieval and model-training access
indistinguishable. The production policy should instead deliberately:

- allow conventional indexing and retrieval agents needed for the stated goal;
- explicitly block known training-only agents if the product owner wants a
  retrieval-first policy;
- document the Google-Extended trade-off: its rule controls both future Gemini
  training and grounding through Google Search; and
- repeat protected-path directives in every bot-specific group because
  bot-specific robots groups override the generic group.

### Crawlability risks to verify outside this repository

1. Cloudflare Bot Management, WAF, rate limiting, and any challenge must permit
   verified search/retrieval crawlers. A valid robots file does not override a
   403/challenge.
2. Configure and verify one production host and the desired redirect strategy
   for HTTP, `www`, apex, and trailing-slash variants.
3. Confirm previews/staging are `noindex` and not present in sitemap,
   canonical URLs, social tags, or public links.
4. Use Search Console URL Inspection and Bing Webmaster URL Inspection after
   deployment to confirm rendered HTML, canonicals, and sitemap parsing.

## 4. Public route inventory

### Indexable HTML routes

All entries below use a production canonical URL derived by
`canonical(path)` in `site/src/lib/seo.ts`. **Y** in the Markdown column
means a `.md` mirror exists; it is currently noindex. **LLM map** means the
route is explicitly present in the current LLM files, not merely reachable.

| URL | Current title / purpose | Primary search intent | Canonical / indexable | Markdown | Sitemap | LLM map | Structured data | Main internal entry points / overlap |
|---|---|---|---|---|---|---|---|---|
| `/` | TailMux product homepage | multiple tailnets simultaneously; product identity | Y / Y | N | Y | Home | SoftwareApplication, Organization, WebSite, FAQPage | Header/footer; overlaps product language with flagship macOS guide |
| `/pricing` | TailMux pricing | price, license model | Y / Y | N | Y | Start here | SoftwareApplication, BreadcrumbList | Header/footer/CTA; distinct transactional intent |
| `/download` | Download TailMux | install/download macOS/Linux | Y / Y | N | Y | Start here | BreadcrumbList | Header/footer/CTA; adjacent to installation, not duplicate |
| `/guides` | TailMux guides | browse multi-tailnet solutions | Y / Y | N | Y | indirectly | BreadcrumbList | Header/footer; hub avoids duplicate intent when guides are distinct |
| `/guides/multiple-tailscale-tailnets-macos` | How to connect to multiple Tailscale tailnets at the same time on macOS | primary non-branded macOS query | Y / Y | Y | Y | Guides/query map | TechArticle, BreadcrumbList, FAQPage | Guides hub, related guides, LLM map; flagship |
| `/guides/two-tailscale-accounts-one-machine` | Two Tailscale accounts on one machine, both live | two accounts/same machine | Y / Y | Y | Y | Guides/query map | TechArticle, BreadcrumbList, FAQPage | Guides hub/related; overlaps fast-switching guide only if they repeat switch explanation |
| `/guides/tailmux-vs-fast-user-switching` | Fast user switching vs simultaneous tailnets | fast user switching alternative | Y / Y | Y | Y | Guides/query map | TechArticle, BreadcrumbList, FAQPage | Guides hub/related; comparison owns switching intent |
| `/guides/tailmux-vs-orbstack-vm` | OrbStack/Lima VM vs no VM | multiple tailnets without VM | Y / Y | Y | Y | Guides/query map | TechArticle, BreadcrumbList, FAQPage | Guides hub/related; comparison owns VM choice |
| `/guides/multiple-tailnets-linux` | Connect to two Tailscale networks on Linux | multi-tailnet Linux | Y / Y | Y | Y | Guides/query map | TechArticle, BreadcrumbList, FAQPage | Guides hub/related; platform-specific |
| `/guides/tailscale-multiple-tailnets-without-vm-or-daemons` | Why two tailscaled daemons fight | second daemon/userspace architecture | Y / Y | Y | Y | Guides/query map | TechArticle, BreadcrumbList, FAQPage | Guides hub/related; architectural comparison |
| `/guides/headscale-and-tailscale-at-the-same-time` | Headscale and Tailscale at the same time | mixed coordination servers | Y / Y | Y | Y | Guides | TechArticle, BreadcrumbList, FAQPage | Guides hub/related; distinct Headscale intent |
| `/docs/getting-started` | Getting started — TailMux docs | setup/introduction | Y / Y | Y | Y | Start here | BreadcrumbList | Docs nav/footer/LLM map; practical setup reference |
| `/docs/installation` | Installation — TailMux docs | install macOS/Linux | Y / Y | Y | Y | Start here | BreadcrumbList | Docs nav/getting started; supports download, not same intent |
| `/docs/app` | The app — TailMux docs | macOS menu-bar app behavior | Y / Y | Y | Y | Reference | BreadcrumbList | Docs nav; macOS-specific reference |
| `/docs/configuration` | Configuration — TailMux docs | YAML/profile/suffix configuration | Y / Y | Y | Y | Reference | BreadcrumbList | Docs nav/LLM map; source-of-truth configuration |
| `/docs/commands` | Commands — TailMux docs | CLI command reference | Y / Y | Y | Y | Reference | BreadcrumbList | Docs nav/LLM map; needs `--reset` limitation correction |
| `/docs/tunnels` | Tunnels — TailMux docs | local TCP forward | Y / Y | Y | Y | Reference | BreadcrumbList | Docs nav/related use cases; distinct TCP access intent |
| `/docs/ssh` | SSH — TailMux docs | SSH across tailnets / ProxyCommand | Y / Y | Y | Y | Reference/query map | BreadcrumbList | Docs nav/use cases/LLM map; should own SSH detail |
| `/docs/database-clients` | Database & GUI clients — TailMux docs | TablePlus/DBeaver/database tunnel | Y / Y | Y | Y | Use cases/query map | BreadcrumbList | Docs nav/LLM map; distinct GUI database intent |
| `/docs/reverse-proxy` | Behind a reverse proxy — TailMux docs | reverse-proxy upstream access | Y / Y | Y | Y | Reference | BreadcrumbList | Docs nav; distinct infrastructure intent |
| `/docs/diagnostics` | Diagnostics — TailMux docs | diagnose a profile/path | Y / Y | Y | Y | Reference | BreadcrumbList | Docs nav; distinct troubleshooting intent |
| `/docs/use-cases/work-and-personal` | Work & personal tailnet on one Mac — TailMux docs | home + work simultaneously | Y / Y | Y | Y | Use cases/query map | BreadcrumbList | Docs nav/LLM map; overlaps account guide but should lead to exact workflow |
| `/docs/use-cases/ssh-rdp-smb-second-tailnet` | SSH, RDP & SMB over a second tailnet — TailMux docs | non-browser access to second tailnet | Y / Y | Y | Y | Use cases/query map | BreadcrumbList | Docs nav/LLM map; links to SSH/tunnels |
| `/docs/use-cases/curl-git-npm-per-tailnet` | Send curl, git & npm through a specific tailnet — TailMux docs | CLI HTTP routing | Y / Y | Y | Y | Use cases/query map | BreadcrumbList | Docs nav/LLM map; needs mixed-traffic limitation prominence |
| `/docs/use-cases/browser-routing` | Route browser traffic by tailnet (PAC) — TailMux docs | browser/PAC across tailnets | Y / Y | Y | Y | Use cases/query map | BreadcrumbList | Docs nav/LLM map; macOS PAC intent |

### Machine-readable and redirect routes

| URL / pattern | Status and content type | Indexability / canonical policy | Sitemap / discoverability |
|---|---|---|---|
| `/docs` | 307 to `/docs/getting-started` | no independent canonical | not listed |
| `/sitemap.xml` | 200 XML | utility resource | listed by robots; currently incorrectly lists the two LLM text files |
| `/robots.txt` | 200 text/plain | utility resource | root discovery |
| `/llms.txt` | 200 text/plain | machine-readable index, not HTML search target | currently listed in sitemap; should move to robots/LLM references only |
| `/llms-full.txt` | 200 text/plain | machine-readable extended map, not HTML search target | currently listed in sitemap; should move to robots/LLM references only |
| `/docs/$slug.md` | 200 `text/markdown` for supported docs | `X-Robots-Tag: noindex`; no canonical Link header today | mentioned in LLM files, but not alternate-linked by HTML |
| `/docs/use-cases/$slug.md` | same | same | same |
| `/guides/$slug.md` | same | same | same |

### Intentionally non-indexable or non-public routes

| Routes | Current policy |
|---|---|
| `/account`, `/login`, `/signup`, `/forgot-password`, `/reset-password`, `/checkout/success`, `/checkout/failed` | route-level `noindex, nofollow`; account is auth-gated |
| `/api/**` | application endpoints; disallowed in robots |
| `/press` | development-only press frame route; calls `notFound()` outside development and is noindex |

## 5. Markdown mirror policy

### Current behavior

The current system is deliberately better than a static duplicate:

1. `markdownMirror()` fetches the same server-rendered canonical HTML;
2. `pageToMarkdown()` extracts the document H1 and `.prose-doc` body, so
   site navigation chrome is excluded;
3. headings, paragraphs, lists, code blocks, tables, links, callouts, and
   visible FAQ details are converted; and
4. the result includes the canonical source URL and returns
   `text/markdown; charset=utf-8` with `X-Robots-Tag: noindex`.

This preserves the right hierarchy: HTML remains the standard search-engine
canonical while Markdown is a machine-readable alternate. The current system
does **not** expose a visible alternate link, an HTML `rel="alternate"`
relationship, or a Markdown response `Link: <HTML>; rel="canonical"` header.

### Adopted policy for implementation

- **Canonical:** indexable SSR HTML page.
- **Alternate:** route-local Markdown representation at the existing `.md`
  URL pattern.
- **Search-engine duplicate prevention:** `X-Robots-Tag: noindex, nofollow,
  noarchive` on Markdown, no Markdown entries in the main sitemap.
- **Agent discovery:** an HTML `link rel="alternate" type="text/markdown"`,
  a small visible "View as Markdown" link, and clear root LLM-file references.
- **Synchronization:** retain server-rendered conversion; no parallel
  Markdown source is introduced.
- **Canonical response signal:** add an HTTP `Link` header from Markdown to
  the absolute HTML canonical.

Content negotiation is not selected: stable, explicit `.md` URLs are easier
to cite and audit, avoid `Vary: Accept` cache complexity, and already match
the deployed architecture.

## 6. LLM and answer-engine files

### What works

- The root `llms.txt` is compact enough to act as a high-signal map.
- It identifies the product, core guides, use cases, reference docs, pricing,
  platforms, limitations, non-affiliation, and Markdown convention.
- `llms-full.txt` contains useful factual answers and a query-to-page map.

### What must change

- Both files are hand-maintained under `site/public`; their statements can
  diverge from route and config data.
- The opening definition should avoid price as the first product identifier and
  lead with the literal retrieval statement: TailMux is a macOS and Linux tool
  for accessing multiple independent Tailscale tailnets simultaneously without
  repeatedly switching accounts or disconnecting the official client.
- "TailMux" must be disambiguated from terminal/tmux projects in both files.
- `llms-full.txt` should be generated from product identity, FAQ, guide, and
  docs registries at request/build time instead of copied by hand.
- LLM text files must be removed from the standard HTML sitemap.

## 7. Search-result and query-intent baseline

The branded ambiguity is real. A search for TailMux/multi-tailnet concepts
returned official Tailscale documentation, the long-running multiple-tailnets
issue, generic userspace/VM/daemon workaround content, and other products.
This is exactly why entity language and query-owned pages matter.

The full intent mapping and cannibalization plan is in
`docs/seo-geo-query-map.md`. The implementation must keep these boundaries:

- homepage: product entity and broad simultaneous access category;
- flagship macOS guide: the primary "connect multiple tailnets simultaneously"
  how-to;
- account guide: two accounts / work and personal context;
- fast-user-switching page: comparison intent only;
- Linux guide: Linux-specific architecture and commands;
- architecture/daemon page: second-daemon and userspace design decision;
- use-case docs: one concrete workflow each; and
- command/reference docs: durable factual source, not competing landing pages.

## 8. Performance and rendering

Important marketing and documentation content is present in SSR HTML, including
the homepage H1 and explanatory text. The root includes client-only analytics
and error monitoring, but the route content itself does not require JavaScript
to state the product category.

Current server behavior:

- production server emits frame-protection headers;
- static public files are cached;
- hashed Vite assets are immutable;
- the latest commit changed public static caching to one hour in source;
- compression and ETags are supported by `site/server.ts`.

No Lighthouse/PageSpeed-style automated check exists. The new audit command
will add deterministic HTML checks; live performance tooling remains optional
because it depends on a running production-like server and network timing.

## 9. Prioritized implementation plan

1. Add one canonical product identity model and use it in metadata, JSON-LD,
   LLM content, headers, and documentation support copy.
2. Generate robots and LLM files from route/config data; define a documented
   retrieval-first crawler policy and exclude non-HTML LLM files from sitemap.
3. Complete Markdown alternate semantics without changing the underlying
   canonical-source conversion.
4. Strengthen metadata/schema: meaningful homepage answer language,
   documentation `TechArticle`, richer but truthful `SoftwareApplication`,
   stable IDs, and validation.
5. Add a factual "routing model and limitations" reference page, then link it
   from homepage, docs, guides, and LLM files. Do not create SEO clones of
   existing guides.
6. Improve guide-to-guide related links based on intent rather than first-two
   registry order.
7. Add a deterministic `bun run audit:seo` command and CI-safe tests, then
   run the local production build and inspect generated endpoints.
8. Document off-site submission, CDN/WAF, analytics, and webmaster actions
   without pretending those external changes were made.

## 10. Audit acceptance criteria

Implementation is successful when the production build proves:

- one consistent literal product definition;
- unique canonical HTML metadata and one H1 per indexable page;
- valid and non-conflicting JSON-LD;
- a main sitemap containing only indexable HTML canonicals;
- robots points at sitemap and deliberately distinguishes retrieval from
  training policy;
- LLM files are generated from canonical registries;
- Markdown alternates are discoverable but noindex and canonicalized to HTML;
- internal links do not leave a new factual reference orphaned;
- the key multi-tailnet landing page is clearly answer-first and technically
  constrained; and
- validation is deterministic, repository-local, and safe for CI.

## 11. Post-baseline route and artifact delta

The implementation adds the following indexable HTML routes without creating
another overlapping macOS or Linux setup page:

| URL | Purpose | Intent owner | Markdown alternate | Sitemap | Structured data |
|---|---|---|---|---|---|
| /guides/connect-to-multiple-tailscale-tailnets | cross-platform architectural answer | connect to multiple Tailscale tailnets simultaneously | yes, noindex | yes | TechArticle, BreadcrumbList, FAQPage, HowTo |
| /docs/limitations | routing boundaries and security model | limitations, native-routing, raw-IP, and protocol questions | yes, noindex | yes | TechArticle, BreadcrumbList |

The finished route inventory therefore includes the baseline routes above plus
these two additions. Documentation and guide pages now emit TechArticle data;
the homepage and pricing pages retain one stable SoftwareApplication entity
each, rather than duplicated product nodes.

There is no RSS feed, Atom feed, or web-app manifest route in this checkout.
No feed was fabricated merely for SEO; a release or guide feed should be added
only when the publication workflow has durable dates and summaries.
