ompsure

Hardened Deploy in 22 Minutes

Shipyard preflight + GitHub repo creation + Vercel deploy + live header audit + security header fixes + redeploy. From "clean local build" to "live with A/A+ security headers" in 22 minutes.

From Repo to Production with Hardened Security — 22 Minutes

Shipyard preflight + GitHub repo creation + Vercel deploy + live header audit + security header fixes + redeploy. A 26-minute Design Forge build went from "clean local build" to "live at aigraas.com with A/A+ security headers" in 22 more minutes.

Part 2 of a series. See From Research to 13-Page Website in 26 Minutes for how the site was built before this use case picks up.

What Happened

The AIGRaaS website was built and clean locally at 3:46 PM ET. But a clean build is not a shipped product — there's no GitHub repo, no Vercel project, no custom domain, no PWA manifest, no SEO artifacts, no security headers, and no audit evidence to hand a security reviewer.

In 22 minutes, the workflow closed that gap:

  1. SEO + PWA artifacts — SITE constants, full metadata, sitemap, robots, manifest, favicon, OG image, 4 PWA icon variants
  2. GitHub repo — created private hrconsultnj/aigraas via gh repo create, pushed main
  3. Shipyard preflight — production readiness checklist (env, health, security, performance)
  4. Vercel deploy — CLI vercel link + vercel --prod, then connected GitHub for auto-deploys
  5. Custom domain — DNS records, SSL provisioning, HTTP→HTTPS redirect
  6. Live audit — curl headers, fetch SEO artifacts, inspect meta tags, check SSL expiry
  7. Code quality fixes — React 19 lint rule (set-state-in-effect), unused imports
  8. Security headers — added X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy via next.config.ts
  9. Redeploy + re-audit — commit + push (auto-deploy) + verify new headers live

Total: 22 minutes and 14 seconds from "ready to ship" to "audit complete with A/A+ security grades."

Session Timeline

MilestoneTime (ET)Elapsed
SEO/PWA phase started15:49:330:00
Build verification — 17 static routes15:56:487:15
GitHub repo created + pushed15:59:4910:16
/shipyard:preflight run16:01:0511:32
Vercel CLI link + production deploy16:01:5512:22
Vercel GitHub integration connected16:03:0313:30
Everything live at aigraas.com~16:05:00~15:27
Live header audit started16:05:5516:22
SEO artifacts verified (all 11)16:06:2816:55
Security headers checked16:06:5017:17
Lint errors fixed (2 files)16:08:5019:17
Security headers added to next.config.ts16:09:1819:45
Audit fix commit + push16:09:5420:21
New headers verified live16:10:1020:37
Final audit output16:11:4722:14

Phase 1: SEO + PWA Setup (~7 minutes)

Before a single deploy command, the site needed the production artifacts that SEO, search engines, PWA installers, social sharing, and browser UIs all depend on.

ArtifactFilePurpose
SITE constantssrc/lib/constants.tsSingle source of truth: name, description, URL, creator, links
Full metadatasrc/app/layout.tsxviewport, themeColor, metadataBase, 15 keywords, icons, appleWebApp, openGraph, twitter cards
Sitemapsrc/app/sitemap.ts12 routes with lastModified, changeFrequency, priority
Robotssrc/app/robots.tsAllow all, disallow /api/ and /dashboard/
PWA manifestsrc/app/manifest.tsstandalone display, #10B981 theme, 4 icons
Favicon + OGpublic/favicon.svg, og-image.pngTeal-mint lettermark + 1200x630 social share
PWA iconspublic/icons/*.png192/512, each in any and maskable purpose

Next.js 16 handles manifest.ts, sitemap.ts, and robots.ts as route handlers — no need for static files in public/. The routes are auto-registered and appear in the build output as static routes.

Phase 2: Repo + Deploy (~6 minutes)

# 1. Create private GitHub repo + push main in one command
gh repo create aigraas --private --source=. \
  --description "AI Guardrails as a Service — constitutional guardrails..." \
  --push

# 2. Shipyard preflight audit
/shipyard:preflight

# 3. Vercel project link + production deploy
vercel link --yes --project aigraas
vercel --prod

# 4. Connect Vercel to GitHub for auto-deploys

Shipyard preflight flagged the deploy footprint correctly (12MB) and identified context-aware skips — no API routes means no CORS/CSRF/rate limit needs, so those checks are skipped rather than marked as blockers.

Production Readiness: aigraas.com

Environment & Secrets:
  [SKIP] No .env.example needed (0 process.env references)
  [PASS] .env* gitignored, no env files tracked

Security:
  [SKIP] CORS/CSRF/rate limiting (no API routes)
  [NOTE] Run /sentinel:headers --url https://aigraas.vercel.app after deploy

Performance:
  [PASS] Deploy size: 12MB (1.1M static + 11M server)
  [PASS] 0 raw <img> tags (next/image throughout)
  [PASS] Production build clean (17 routes, all static)

Vercel deploy protection gotcha: Private projects from CLI deploys are protected by default (401 on preview URLs). Connecting the custom domain bypasses this for production; preview URLs remain protected unless explicitly disabled in Vercel dashboard → Settings → Deployment Protection.

Phase 3: Live Audit (~3 minutes)

Once the custom domain was live, the audit verified every layer with shell commands:

# Deployment layer
curl -sI https://aigraas.com
curl -sI http://aigraas.com  # Verify 308 redirect to HTTPS
openssl s_client -connect aigraas.com:443 -servername aigraas.com </dev/null

# SEO artifacts (all should be 200 OK)
for path in /robots.txt /sitemap.xml /manifest.webmanifest /favicon.svg /og-image.png; do
  curl -sI "https://aigraas.com$path" | head -1
done

# Security header grading
curl -sI https://aigraas.com | grep -iE 'strict-transport|x-frame|x-content|referrer|permissions|csp'

Initial security header audit showed HSTS was present (Vercel adds it automatically with 2-year max-age, exceeding the 1-year recommendation), but the four standard hardening headers were missing — the gap Sentinel-style grading catches.

Phase 4: Hardening (~4 minutes)

Two classes of fix landed in a single commit:

Code quality fixes:

  • theme-toggle.tsx — Refactored to useSyncExternalStore (React 19 set-state-in-effect lint rule)
  • use-case-detail.tsx — Removed unused StaggerChildren/StaggerItem imports

Security headers added to next.config.ts:

next.config.ts
async headers() {
  return [
    {
      source: "/(.*)",
      headers: [
        { key: "X-Frame-Options", value: "DENY" },
        { key: "X-Content-Type-Options", value: "nosniff" },
        { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
        {
          key: "Permissions-Policy",
          value: "camera=(), microphone=(), geolocation=(), interest-cohort=()"
        },
      ],
    },
  ];
}

Commit + push triggered the Vercel GitHub integration's auto-deploy. No manual redeploy command. Verification at 16:10:10 confirmed the four new headers were live — full audit complete at 16:11:47.

Final Audit Report

Deployment Status

CheckResult
Domainaigraas.com — HTTP/2 200
SSLValid until Jul 3, 2026 (auto-renews)
HTTP → HTTPS308 Permanent Redirect
HSTSmax-age=63072000 (2 years)
CDNVercel edge (iad1, cache HIT)
GitHub integrationConnected, auto-deploys on push to main
Build17 routes, all static, ~12MB footprint

SEO Artifacts (all 200 OK)

ArtifactStatusNotes
/HTML 64KBFull metadata, OG, Twitter cards
/robots.txtValidAllow all, disallow /api/ and /dashboard/
/sitemap.xmlValid12 URLs, lastmod, priority
/manifest.webmanifestValidPWA standalone, #10B981 theme
/favicon.svg200Teal-mint lettermark
/apple-touch-icon.png200180x180
/og-image.png2001200x630
/icons/icon-192x192.png + maskable200PWA any-purpose + maskable
/icons/icon-512x512.png + maskable200PWA any-purpose + maskable

Security Headers (after fix commit)

HeaderValueGrade
strict-transport-securitymax-age=63072000A+ (2yr, exceeds 1yr rec)
x-frame-optionsDENYA
x-content-type-optionsnosniffA
referrer-policystrict-origin-when-cross-originA
permissions-policycamera=(), microphone=(), geolocation=(), interest-cohort=()A
content-security-policyNot setAcceptable (static marketing site, inline fonts)

Code Quality

CheckResult
TypeScriptClean (0 errors)
ESLintClean (was 1 error + 2 warnings — fixed)
BuildClean, 17 static routes

Why This Is Efficient

1. Shipyard preflight caught the right things

The /shipyard:preflight skill didn't dump a generic checklist — it detected that this was a static marketing site with zero API routes and correctly skipped CORS/CSRF/rate limiting checks. It flagged the Sentinel header check as a note, not a blocker, because security headers are a post-deploy activity. Preflight that understands context is preflight that saves time.

2. Fix-in-place beats fresh redeploy

The Vercel GitHub integration meant the audit fixes were a single git push away from production. No manual vercel --prod command, no deploy tokens to juggle, no preview-vs-production URL confusion. Push to main → Vercel rebuilds → live in ~40 seconds.

3. next.config.ts headers() is the right place

Security headers in next.config.ts apply to every route automatically, including sitemap/robots/manifest/static assets. Versus alternatives:

ApproachProsCons
next.config.ts headers()All routes, version-controlled, works locallyNone
Vercel dashboard headersUI-basedNot version-controlled, easy to drift
middleware.tsDynamic per-requestRuntime cost for static sites

4. Final audit is reproducible

Every verification is a curl/openssl command. No GUI. No vendor tooling. Anyone can run the same commands against the live site and get the same grades — including the next auditor or a compliance reviewer.

Key Metrics

MetricValue
Total duration22m 14s
Files created/edited15
Routes deployed17 (all static)
Deploy footprint12 MB (1.1M static + 11M server)
Git commits2 (initial + audit fixes)
Security headers added4
HSTS max-age2 years (exceeds 1-year recommendation)
Final gradesA/A+ across all headers
SEO artifacts11, all 200 OK
Manual clicks in Vercel UI0 (only GitHub connect)

Reproduce This Audit

Every check in the final audit is a shell command. You can run these against any Vercel/Next.js deploy:

DOMAIN="https://your-site.com"

# Deployment
curl -sI "$DOMAIN" | head -20
curl -sI "${DOMAIN/https/http}" | grep -i location

# SSL
echo | openssl s_client -connect "${DOMAIN#https://}:443" \
  -servername "${DOMAIN#https://}" 2>/dev/null | openssl x509 -noout -dates

# SEO artifacts
for path in /robots.txt /sitemap.xml /manifest.webmanifest /favicon.svg /og-image.png; do
  echo -n "$path: "
  curl -sI "$DOMAIN$path" | head -1
done

# Security header grading
curl -sI "$DOMAIN" | grep -iE 'strict-transport|x-frame|x-content|referrer|permissions|csp'

If any of these come back missing or graded below A, you have work to do. If they all come back clean, you've shipped a hardened site.

On this page