Skip to content

feat: serve docs on docs.<apex> from the landing container - #100

Merged
vreshch merged 1 commit into
masterfrom
feature/docs-host-routing
Aug 23, 2026
Merged

feat: serve docs on docs.<apex> from the landing container#100
vreshch merged 1 commit into
masterfrom
feature/docs-host-routing

Conversation

@vreshch

@vreshch vreshch commented Aug 23, 2026

Copy link
Copy Markdown
Member

What

docs.agentage.io is now served by the same landing container as the apex.

  • packages/shared/src/origins.ts - links().docs derived from the one SITE_FQDN (like dashboard/api): https://docs.<fqdn> in prod, http://localhost:3000/docs locally (localhost has no subdomains). New docsHost(fqdn) returns the dedicated hostname, or undefined locally.
  • docker-compose.yml - landing-docs-https router: Host(\docs.${SITE_FQDN}`), priority 26, TLS + letsencrypt, service=landing, landing-secure-headers. Mirrors the existing landing-apex-https/landing-www-https` labels.
  • packages/landing/src/middleware.ts (new, first middleware in this repo) - thin adapter over the pure src/docs/host-routing.ts:
    • on the docs host: / -> /docs, /<path> -> /docs/<path>, never double-prefixing an already /docs* path, and passing _next / api / schemas / .well-known / health / OG images and every dotted asset (robots.txt, sitemap.xml, *.png, *.ico) straight through. .md mirrors are treated as docs content, so /rest-api.md still resolves.
    • on the apex (and www.): 308 /docs -> docs origin /, /docs/<x> -> /<x>, /docs.md -> /docs.md, query string preserved.
    • no split when there is no real FQDN (localhost / unconfigured) - see the gate note below.
  • SEO - doc canonicals and og:url are absolute docs-origin URLs (docs/seo.ts), and the docs entries in sitemap.xml, llms.txt, llms-full.txt, /docs.md and the breadcrumb JSON-LD emit docs-origin URLs. One sitemap still lists both hosts (robots.txt cross-submission). HTML stays canonical; the .md mirrors keep X-Robots-Tag: noindex.

Nothing hardcodes agentage.io - everything derives from SITE_FQDN (with the build-baked NEXT_PUBLIC_SITE_FQDN sentinel as the fallback that docker/runtime-env.sh rewrites).

Why

Docs get their own host without a second image, deploy or stack.

e2e-gate safety

deploy.yml's e2e-gate boots the image with no SITE_FQDN (entrypoint defaults it to agentage.io) and curls it on localhost:3100. Since the request Host is neither the apex nor the docs host, the middleware returns next() - /docs serves normally, no redirect, no loop. Verified by running the gate for real (below).

Verification

npm run verify - green (type-check, lint, format, 59 unit tests, coverage thresholds, build).
npm run test:e2e (dev server) - 36 passed.
Gate simulation: standalone build (NEXT_PUBLIC_SITE_FQDN=agentage.io) served with SITE_FQDN=agentage.io on localhost:3100, then LANDING_BASE_URL=http://localhost:3100 LANDING_EXPECTED_HOST=agentage.io npm run test:e2e - 35 passed, 1 skipped (the prod-robots skip, same as before).

Curl against that same server:

A) No Host override (localhost:3100) - the deploy e2e-gate scenario, unchanged:
  /                    200
  /docs                200
  /docs/rest-api       200
  /docs/rest-api.md    200
  /docs.md             200
  /robots.txt          200
  /sitemap.xml         200
  /health              200

B) Host: docs.agentage.io - rewrite to the docs tree:
  /                    200
  /rest-api            200
  /rest-api.md         200
  /docs                200
  /docs/rest-api       200
  /docs.md             200
  /robots.txt          200
  /sitemap.xml         200
  /favicon.ico         200
  /nope                404

C) Host: agentage.io - 308 to the docs origin:
  /                    200
  /blog                200
  /docs                308 https://docs.agentage.io/
  /docs/rest-api       308 https://docs.agentage.io/rest-api
  /docs/rest-api.md    308 https://docs.agentage.io/rest-api.md
  /docs.md             308 https://docs.agentage.io/docs.md
  /docs/cli?x=1        308 https://docs.agentage.io/cli?x=1
  /docs-md/rest-api    200
  /docs/cli (www)      308 https://docs.agentage.io/cli

D) Canonicals:
  docs-host /          <link rel="canonical" href="https://docs.agentage.io"
  docs-host /rest-api  <link rel="canonical" href="https://docs.agentage.io/rest-api"
  .md X-Robots-Tag     x-robots-tag: noindex

Content checks: docs.agentage.io/ renders <title>Docs - Agentage Memory</title>, /rest-api renders <title>REST API - docs - Agentage Memory</title>, /rest-api.md returns text/markdown starting # REST API. Sitemap <loc>s: https://agentage.io for site pages, https://docs.agentage.io[/<slug>] for docs.

Rollout

DNS first. This needs the docs CNAME from the infra PR to exist before the landing deploy, otherwise Traefik cannot solve the ACME challenge for docs.agentage.io and the router stays certless.

Order: infra DNS apply -> merge here -> landing deploy. Until then the apex /docs* 308s point at a host that does not resolve, so do not merge ahead of DNS.

docs.agentage.io is routed to the same landing service: the app rewrites the
docs tree onto that host's root and 308s the apex /docs* URLs over to it.
Canonical, sitemap and llms.txt docs entries now emit docs-origin URLs.
Localhost / unknown hosts are untouched, so the deploy e2e gate is unaffected.
@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

CI - Verify PASSED

Commit: cc3e3e825cc9451bb25d0832164345c32a6c8ec2 · Branch: feature/docs-host-routing

  • ✅ Type check
  • ✅ Lint
  • ✅ Format check
  • ✅ Unit tests + coverage
  • ✅ Build
  • ✅ Landing e2e

🔗 View workflow run

@vreshch
vreshch marked this pull request as ready for review August 23, 2026 14:03
@vreshch
vreshch merged commit 2ed2b70 into master Aug 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant