feat(web): AEO foundation — robots, JSON-LD, meta, llms.txt, security headers

Implements the high-signal, content-independent fixes flagged across the
multi-engine AEO audit:

- robots.txt (app/robots.ts): allow mainstream + AI crawlers (GPTBot,
  ClaudeBot, PerplexityBot, Google-Extended, …), disallow /api, link sitemap.
- Organization + WebSite JSON-LD on the root layout; BlogPosting JSON-LD on
  /blog/[slug].
- Richer metadata: descriptive default title, Open Graph + Twitter cards,
  canonical, icons, metadataBase.
- Per-route titles/descriptions for catch-all routes (docs, about, hire-us,
  agent-swarm, …) instead of the generic "LogicSRC".
- /llms.txt (llmstxt.org) and /skill.md capability manifest.
- /.well-known/security.txt (RFC 9116).
- Security headers via next.config: HSTS, X-Content-Type-Options,
  X-Frame-Options, Referrer-Policy, Permissions-Policy (CSP intentionally
  deferred to avoid breaking inline/stats/CoinPay scripts).

Verified in a running build: all routes serve correctly and headers are set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-06-08 12:38:27 +00:00
parent ce79f02211
commit 6518dfb4a9
8 changed files with 285 additions and 19 deletions

View file

@ -6,7 +6,19 @@ import type { NextConfig } from "next";
// webhooks) are filesystem routes and match before these afterFiles rewrites.
const commandboardApiUrl = process.env.COMMANDBOARD_API_URL;
const securityHeaders = [
// HSTS — site is HTTPS-only behind Railway. No `preload` (irreversible).
{ key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
];
const nextConfig: NextConfig = {
async headers() {
return [{ source: "/:path*", headers: securityHeaders }];
},
async rewrites() {
if (!commandboardApiUrl) return [];
const base = commandboardApiUrl.replace(/\/$/, "");