# TailMux SEO, GEO and AI discoverability implementation report

**Completed:** 2026-07-11
**Implementation baseline:** `d2b7a26` (`origin/main`)
**Validated deployment target:** local production build of the site
**Canonical public domain:** <https://tailmux.app>

## Result

The website now has one explicit, technically constrained description of
TailMux: a macOS and Linux tool for accessing multiple independent Tailscale
tailnets simultaneously through isolated, hostname-selected application paths.
The description consistently says that TailMux does not create a second native
system-wide Tailscale VPN interface and does not change the separately managed
official Tailscale client session.

The implementation makes the product easier to identify as a distinct software
entity, gives the principal non-branded query its own useful guide, and makes
machine-readable outputs derive from the same product and route registries as
the site.

## What the audit found

### Already working well

- The marketing site, guides, and documentation are server rendered.
- Main content routes already had route-level metadata, canonical URLs, one
  clear H1, and a strong structured-data foundation.
- Guides and docs already exposed Markdown representations that preserve
  content without navigation chrome.
- The site already had a sitemap, robots policy, LLM-readable files, OpenPanel
  client instrumentation, and a clear technical documentation corpus.
- Core implementation and internal docs provided enough evidence to write
  accurate limits: isolated profiles, hostname-suffix selection, no cross-profile
  fallback, loopback listeners, browser PAC/proxy paths, SSH support, and TCP
  local tunnels.

### Gaps addressed

- Product facts were duplicated across configuration, copy, FAQ, JSON-LD, and
  static LLM files.
- The homepage did not lead with the literal multi-tailnet problem statement.
- No route owned the broad cross-platform query “connect to multiple Tailscale
  tailnets simultaneously.”
- Static `llms.txt`, `llms-full.txt`, and robots files could drift from
  canonical content.
- Markdown mirrors were noindex but were not advertised by their HTML
  canonicals and did not return an HTTP canonical relationship.
- The sitemap included non-HTML LLM text files.
- Documentation pages lacked article-level structured data; guide relationships
  were registry-order driven rather than intent driven.
- No deterministic repository-level SEO audit existed.
- A command-reference statement implied that `profile login --reset` worked
  with the default tsnet backend, although the implementation limits that flag
  to the compatibility backend.

## Changes made

### Canonical product entity and factual consistency

- Added `site/src/config/product-identity.ts` as the source for the official
  name, canonical domain, concise and extended descriptions, operating systems,
  publisher, official URLs, release link, logo, repository, supported
  mechanisms, non-affiliation wording, and tmux-product disambiguation.
- Reused that object in site configuration, product configuration, Open Graph
  fallbacks, JSON-LD, footer, LLM outputs, sitemap validation, and public
  descriptions.
- Rewrote over-broad copy so “simultaneous” means simultaneous supported
  application access, rather than two generic native VPN interfaces or
  unrestricted system routing.
- Added a consistent product terminology glossary and accuracy findings to
  `docs/seo-geo-audit.md`.

### Indexable content and information architecture

- Revised the homepage H1 and answer-first copy to state that TailMux accesses
  multiple Tailscale tailnets simultaneously, while retaining the existing
  product visual language.
- Added
  `/guides/connect-to-multiple-tailscale-tailnets`, the canonical
  cross-platform guide for the highest-priority non-branded question. It
  explains the routing model, fair alternatives, realistic workflows, limits,
  security boundaries, and visible FAQ.
- Added `/docs/limitations` as the canonical routing-boundary reference:
  no TUN or broad kernel routes, hostname/suffix selection, PAC/proxy scope,
  local TCP tunnel scope, raw-IP behavior, DNS/MagicDNS boundaries, and
  official-client coexistence.
- Refined macOS, Linux, fast-user-switching, getting-started, work/personal,
  and command/use-case content so that each page owns a distinct intent and
  links to the right conceptual source.
- Changed guide relationships from “first two registry entries” to explicit
  intent-aware relationships. The homepage, broad guide, platform guides, use
  cases, footer, and limitations reference now form an intentional internal
  link graph.
- Simplified the homepage conversion hierarchy to one primary download action,
  followed by two lower-emphasis paths for setup documentation and architectural
  comparison. On mobile, the download action spans the content width and its
  platform and localized-price details form two centered lines.
- Corrected the desktop header alignment and added a navigable mobile Docs
  drill-down so documentation sections are reachable without leaving the menu.
- Replaced hard-coded display pricing with a reusable Polar-backed localized
  price component: EUR 4.99 for euro countries and USD 5.99 for the United
  States and the rest of the world. Minor-unit fallbacks are centralized, and
  Polar fetch or validation failures are reported to Better Stack before the
  fallback is rendered.

### Metadata and structured data

- Extended the reusable JSON-LD layer with a stable Product
  `SoftwareApplication`, `Organization`, and `WebSite` identity,
  download/install/release/support URLs, supported OS values, feature list,
  publisher linkage, and official repository identity.
- Added `TechArticle` data to documentation routes and retained
  `TechArticle`, breadcrumb, and visible-FAQ data for guides.
- Added `HowTo` only to the new guide, where a real ordered setup procedure
  is visible in the document.
- Kept stable `@id` values tied to canonical URLs and avoided schema-only
  FAQs, invented prices, reviews, affiliations, or unsupported versions.

### Markdown alternate policy

- Kept HTML as the canonical, indexable document.
- Kept Markdown mirrors as machine-readable alternates rather than removing
  them.
- Added `<link rel="alternate" type="text/markdown">` to supported HTML
  guide and documentation pages, plus a small visible “View as Markdown” link
  for people and agents.
- Added an HTTP `Link: <HTML>; rel="canonical"` header and
  `X-Robots-Tag: noindex, nofollow, noarchive` to each Markdown response.
- Kept Markdown URLs out of the standard XML sitemap and listed their policy
  in dynamically generated LLM outputs. This gives agents a discoverable
  alternate without making Markdown compete in ordinary web search.

### Crawl policy, sitemap, and AI-readable outputs

- Replaced hand-maintained public robots and LLM files with dynamic,
  typed routes generated from canonical registries:
  `/robots.txt`, `/llms.txt`, and `/llms-full.txt`.
- Made the root LLM index concise and high-signal. Its opening definition
  identifies TailMux as a multi-tailnet access tool, names macOS and Linux,
  states the official-client boundary, and distinguishes it from unrelated
  tmux terminal products.
- Made `llms-full.txt` derive from the same product, docs, guide, and FAQ
  data rather than a copied static snapshot.
- Removed non-HTML LLM files from the XML sitemap. The sitemap now emits only
  indexable canonical HTML entries from content registries and excludes the
  development-only press route.
- Replaced the generic robots policy with an explicit, documented policy:
  conventional indexing and selected search/retrieval agents are allowed while
  GPTBot and ClaudeBot are declined. Protected application paths are repeated
  in specific bot groups. The Google-Extended trade-off is documented rather
  than silently assumed.
- Preserved generic crawler access for major web search agents and made the
  AI-crawler decision visible in code and audit documentation. CDN/WAF
  enforcement remains an external deployment responsibility.

### Validation and CI

- Added `site/scripts/audit-seo.ts` and the commands
  `bun run audit:seo` and `bun run audit:seo:local`.
- The deterministic audit validates route registries, unique titles and
  descriptions, canonical URLs, one H1, indexability, sitemap composition,
  robots sitemap declaration, Markdown alternate coverage, LLM structure and
  absolute links, structured-data consistency, internal links, social-image
  URLs, and absence of localhost/preview leakage.
- The optional local audit fetches the built local production server, validates
  response status/content types/headers, follows sitemap URLs, checks
  Markdown canonical/noindex headers, and checks generated LLM links.
- Added build, typecheck, and deterministic SEO audit to the existing GitHub
  Actions CI workflow without adding an external live-site dependency.
- Added a regression check for absolute off-site release links in
  `llms-full.txt`; it caught and corrected an initially malformed generated
  release URL during the local audit.

## Routes added or materially revised

| Route | Purpose |
|---|---|
| `/` | Explicit product category answer and links to the broad guide and limitations. |
| `/guides/connect-to-multiple-tailscale-tailnets` | New cross-platform, answer-first guide for simultaneous multi-tailnet access. |
| `/docs/limitations` | New canonical routing and protocol boundary reference. |
| `/robots.txt` | Dynamic, explicit search/retrieval/training crawler policy. |
| `/llms.txt` | Dynamic concise machine-readable product and route map. |
| `/llms-full.txt` | Dynamic extended factual map generated from canonical registries. |
| `/sitemap.xml` | Canonical HTML-only sitemap. |
| `/docs/*.md`, `/docs/use-cases/*.md`, `/guides/*.md` | Preserved Markdown alternates with explicit canonical/noindex policy. |
| Existing macOS, Linux, fast-switching, getting-started, work/personal, CLI, and proxy-compatible use-case pages | Accuracy corrections and contextual internal links. |

## Files changed

### New implementation files

- `site/src/config/product-identity.ts`
- `site/src/config/crawl-policy.ts`
- `site/src/lib/llms.ts`
- `site/src/routes/robots[.]txt.ts`
- `site/src/routes/llms[.]txt.ts`
- `site/src/routes/llms-full[.]txt.ts`
- `site/src/routes/docs/limitations.tsx`
- `site/src/routes/guides/connect-to-multiple-tailscale-tailnets.tsx`
- `site/scripts/audit-seo.ts`

### Revised implementation files

- SEO/product/structured-data/Markdown helpers, sitemap route, site server,
  docs and guide components, site configuration, FAQ and guide registries,
  Polar price resolution, checkout context, and Better Stack reporting.
- Homepage, footer, selected guides and documentation, root README, site
  header/mobile navigation, pricing page, README, site package scripts, site
  TypeScript configuration, and CI workflow.
- Removed static public `robots.txt`, `llms.txt`, and `llms-full.txt`
  because dynamic routes now generate them from canonical data.

### Required planning and operational deliverables

- `docs/seo-geo-audit.md`
- `docs/seo-geo-query-map.md`
- `docs/seo-geo-content-plan.md`
- `docs/seo-geo-measurement-plan.md`
- `docs/seo-geo-offsite-checklist.md`
- `docs/seo-geo-implementation-report.md`

## Validation evidence

| Check | Result |
|---|---|
| `bun install --frozen-lockfile` in `site/` | Passed |
| `bun run check` in `site/` | Passed |
| `bun run check-types` in `site/` | Passed |
| `bun run audit:seo` in `site/` | Passed |
| `bun run build` in `site/` | Passed |
| `SEO_AUDIT_BASE_URL=http://127.0.0.1:3012 bun run scripts/audit-seo.ts --live` against the built local server | Passed |
| `go test ./...` at repository root | Passed |
| `go vet ./...` at repository root | Passed |
| `git diff --check` | Passed |

The repository-wide formatter was normalized during final verification, so the
complete lint and formatting command now passes without a reduced or targeted
file scope.

The build emits two pre-existing warnings that do not fail the build:

1. TanStack Start reports the existing `$slug.md` file-route naming pattern
   as an invalid parameter name even though the deployed route behavior and
   local Markdown checks succeed.
2. An existing account server function uses deprecated
   `createServerFn().inputValidator()`.

## Remaining manual actions

These operations require access outside this repository and were not
represented as complete.

1. Identify the production deployment source, deploy this revision, and verify
   the production responses for `/`, `/sitemap.xml`, `/robots.txt`,
   `/llms.txt`, `/llms-full.txt`, the new guide, the limitations page, and
   one Markdown mirror.
2. In Google Search Console, add or confirm the Domain property
   `sc-domain:tailmux.app` (or URL-prefix property
   <https://tailmux.app/>), submit
   <https://tailmux.app/sitemap.xml>, inspect the homepage, the new broad
   guide, and `/docs/limitations`, then request indexing when the canonical
   and rendered HTML are accepted.
3. In Bing Webmaster Tools, add or confirm the
   <https://tailmux.app/> property, submit the same sitemap, and inspect the
   homepage and new guide for crawl/index status.
4. In Cloudflare, confirm HTTPS/apex/www redirects, trailing-slash behavior,
   staging/preview noindex policy, and that verified search/retrieval agents
   are not challenged or blocked by WAF/Bot Management. Purge cached robots,
   sitemap, and LLM resources after deployment if cache policy requires it.
5. Follow `docs/seo-geo-offsite-checklist.md` for GitHub repository
   description/topics, releases, package-manager metadata, social profiles,
   and distribution listings. Do not change external profiles without the
   authorized account/session.
6. Implement the server/CDN log views described in
   `docs/seo-geo-measurement-plan.md` to observe crawler user agents,
   machine-readable-resource requests, crawler errors, and answer-engine
   referrals. Client analytics alone cannot measure those requests.

## Risks and follow-up work

- Search result ranking and answer-engine citations require crawl and indexing
  time; this implementation supplies reliable retrieval surfaces but cannot
  claim a ranking outcome before Search Console, Bing, and referral data are
  observed.
- AI crawler identifiers and provider policies evolve. Re-review the explicit
  crawler policy periodically, particularly the documented Google-Extended
  product trade-off.
- Keep content changes routed through the typed product, docs, guide, and FAQ
  registries so LLM files, sitemap, Markdown alternates, and schemas remain
  consistent.
- If the application later gains a publication workflow, reassess an RSS or
  Atom feed. No feed was added because the repository has no current
  authoritative publishing/feed model.
- Consider a future route-convention migration for the Markdown file routes to
  eliminate the TanStack parameter-name build warning without changing their
  externally visible URLs.
