Skip to content

Serve the site at www.termca.de and gate every host claim - #13

Merged
nicodes merged 2 commits into
mainfrom
issue-36-www-termca-de
Aug 21, 2026
Merged

Serve the site at www.termca.de and gate every host claim#13
nicodes merged 2 commits into
mainfrom
issue-36-www-termca-de

Conversation

@nicodes

@nicodes nicodes commented Aug 21, 2026

Copy link
Copy Markdown
Member

Refs aviorstudio/termcade-be#36

What

The marketing domain is termca.de. Vercel redirects the apex 308 to www in the project's Domains settings (already live; unchanged here), so the canonical technical URL is https://www.termca.de/. The build still emitted termcade.com in every host claim.

  • astro.config.mjs: site: 'https://www.termca.de' — the one place the host is configured.
  • src/layouts/Full.astro: canonical link, og:url and the SoftwareApplication JSON-LD url now derive from Astro.site instead of the duplicated termcade.com literals.
  • public/robots.txt, public/sitemap.xml: literal host updated (public/ files are copied as-is).
  • tools/check_domain.py + CI step "Every host claim is www.termca.de": built-output regression gate checking canonical, og:url, JSON-LD url, the robots Sitemap line and the sitemap <loc> against https://www.termca.de, plus a no-termcade.com-anywhere-in-dist/ sweep.
  • README domain section rewritten: apex→www 308 is a Vercel dashboard setting, host is set once in site, app.termca.de/api.termca.de claims unchanged.

Verification (local, exact parameters)

  • Build: bun install --frozen-lockfile, bun run build (Astro 5.18.2, static, 1 page) — pass.
  • Domain gate: python3 tools/check_domain.py dist — pass.
  • Deliberate failure proof: restored Sitemap: https://termcade.com/sitemap.xml in public/robots.txt, rebuilt, gate exited 1 with:
    dist: host claims are wrong — the canonical host is https://www.termca.de:
      dist/robots.txt: no 'Sitemap: https://www.termca.de/sitemap.xml'
      dist/robots.txt: still names the old host termcade.com
    
    Then restored the fix and confirmed exit 0.
  • No-script gates: find dist -name '*.js' empty; python3 tools/check_scripts.py dist — pass (1 page, ld+json allowed).
  • Browser quality: node tools/browser-check.mjs dist (viewports 1280x800 and 390x844 mobile/touch, link timeout 10000ms, zero error tolerance) — pass.
  • Capture freshness: pinned release v0.0.7 archive verified against committed SHA-256, python3 tools/capture.py --check /tmp/termcade — pass (pixels-*.html byte-for-byte; hero/index structure).

Rollout

Merging triggers Vercel Production via the external Git integration. Post-merge checks (issue #36 acceptance criteria): Vercel deployment reaches Ready; https://termca.de still answers 308 → https://www.termca.de/; www answers 200 with canonical/og:url/JSON-LD naming https://www.termca.de, robots and sitemap updated; Playwright pass at 1280x800 and 390x844.

The marketing domain is termca.de; Vercel redirects the apex 308 to www
in the project settings, so the canonical technical URL is
https://www.termca.de/. The build still named termcade.com in the
canonical link, og:url, SoftwareApplication JSON-LD, robots.txt and
sitemap.xml.

The host is now set once as `site` in astro.config.mjs and the layout
derives canonical, og:url and the JSON-LD url from Astro.site instead of
a duplicated literal. robots.txt and sitemap.xml are public/ files copied
verbatim, so they carry the host literally.

tools/check_domain.py gates the built output: every generated host claim
must be https://www.termca.de and no termcade.com may survive in dist/.
It is proven to fail: restoring the old host in public/robots.txt makes
it exit 1 with the stale claim named.

app.termca.de and api.termca.de claims are unchanged.

Refs aviorstudio/termcade-be#36
@vercel

vercel Bot commented Aug 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
termcade-web Ready Ready Preview Aug 21, 2026 8:11pm

@nicodes

nicodes commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

Review 1

Findings

  1. P2 — Domain gate permits additional non-canonical sitemap/robots host claims. tools/check_domain.py:86-94 only checks that the expected canonical string occurs at least once. A future change adding <loc>https://other.example/</loc> or an additional Sitemap: https://other.example/sitemap.xml still passes, provided the expected www URL remains. This contradicts the documented “every <loc>” and “every host claim” guarantees and can ship incorrect crawler metadata. Parse and validate all <loc> and Sitemap: values, rejecting every value other than the allowed canonical URL.

Residual risk

Reviewed only pinned range f66f587...a896c22 via the supplied PR diff and GitHub compare (one commit ahead, no base divergence), plus Astro 5 configuration documentation. CI and Vercel preview were reported successful. I did not execute or deliberately alter the gate in this read-only review; its failure path is statically reachable for a missing/incorrect required canonical value, but the additional-claim path above remains uncovered.

Verdict: NEEDS ATTENTION

Review 1: the domain gate searched for the canonical Sitemap line and
<loc> value as substrings, so a build that kept the right claim and added
a foreign one beside it still passed — contradicting the gate's "every
host claim" contract and risking wrong crawler metadata.

robots.txt is now parsed for all Sitemap: directives and sitemap.xml for
all <loc> values (as XML, so a malformed sitemap fails too), and each
must equal exactly the canonical https://www.termca.de claim. Both
failure paths are proven deliberately: an extra <loc>https://other.example/</loc>
and an extra `Sitemap: https://other.example/sitemap.xml` each make the
gate exit 1 naming the foreign value; the fixtures were then restored
and the full suite re-run green.

Refs aviorstudio/termcade-be#36
@nicodes

nicodes commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

Review 1 reply

Finding (P2) — fixed in 9a071bc. The gate no longer substring-searches for the expected claim. robots.txt is parsed for all Sitemap: directives and sitemap.xml is parsed as XML for all <loc> values (malformed XML also fails); each parsed value must equal exactly the canonical https://www.termca.de claim. Any additional foreign claim now fails the gate. Docstring updated so the documented guarantee matches the code.

Deliberate failure proofs (each fixture broken, rebuilt, gate run, fixture restored):

  1. Extra <loc>https://other.example/</loc> appended to public/sitemap.xml → exit 1:
    dist/sitemap.xml: <loc> values are ['https://www.termca.de/', 'https://other.example/'], expected exactly ['https://www.termca.de/']
    
  2. Extra Sitemap: https://other.example/sitemap.xml appended to public/robots.txt → exit 1:
    dist/robots.txt: Sitemap: values are ['https://www.termca.de/sitemap.xml', 'https://other.example/sitemap.xml'], expected exactly ['https://www.termca.de/sitemap.xml']
    

Assertions are reachable: both files are concrete public/ artifacts copied into dist/; a missing file is a distinct failure, and the parsed-value comparison is exercised on every run.

Re-verification after restore (local, same parameters as before): build (bun run build, Astro 5.18.2) pass; python3 tools/check_domain.py dist exit 0; python3 tools/check_scripts.py dist exit 0; no *.js in dist/; capture markers present; node tools/browser-check.mjs dist pass (1280x800 + 390x844 mobile/touch, 10s link timeout, zero error tolerance); python3 tools/capture.py --check against pinned release v0.0.7 (SHA-256 verified) pass.

Checks on 9a071bc: GitHub Actions ci pass (47s; log confirms the strengthened check_domain.py step ran green), Vercel preview deployment completed, no secrets exposed. Ready for Review 2.

@nicodes

nicodes commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

Review 2

No findings.

Reviewed range: f66f58739580d4e09546b9f46c89ec41b75f143d..9a071bcfa31d64e5ee6786010ae8ec5c5a5a9b64. Both PR base and head OIDs matched the pinned values before review and head still matched afterward.

tools/check_domain.py parses all Sitemap: directives and namespaced sitemap <loc> elements, rejects missing/empty/foreign values, exits nonzero for malformed XML, and retains the full-dist old-host scan. The failure branches are reachable by the list comparisons and XML parse exception. Astro 5 documentation confirms site supplies Astro.site and prerendered URLs. CI run 32522267174 passed the build and domain gate on the PR merge ref.

Residual risk: no committed unit fixtures cover malformed, missing, and duplicate/foreign robots or sitemap inputs; current protection is the artifact gate and the documented manual negative checks. Local rebuild was not run because bun is unavailable in this review environment.

Verdict: APPROVE

@nicodes

nicodes commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

Review 2 reply

Approval recorded — thank you. Range reviewed f66f587...9a071bc matches the pinned base/head; no findings.

Residual risk disposition: accepted as-is. The gate's protection for malformed/missing/foreign robots and sitemap inputs is the artifact-level gate plus the documented deliberate negative checks (both foreign-claim cases were broken, proven to exit 1, and restored). Committed unit fixtures for the parser would duplicate what the artifact gate already proves against the real build output; if a second sitemap URL type or multi-file sitemap is ever introduced, adding fixture cases then is the right time. No action in this PR.

Proceeding to merge (single-layer, non-stacked; merge commit flow), then post-merge CI/Vercel production monitoring and playwright production verification at 1280x800 and 390x844 per issue #36.

@nicodes
nicodes merged commit 3af49d6 into main Aug 21, 2026
4 checks 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