Two coordinated polish moves:
1. **<PulseLink> / <PulseButton>** — new `apps/web/components/pulse.tsx`.
Click anywhere on a wrapped link or button and a small indigo dot
detonates from the click point, scaling 1x→80x over 650ms before
fading to transparent. Same visual language as the hero load-in
glow — the click effectively says "this is the brand reaching back."
The dot lives in a `pointer-events: none` overlay, so it never
blocks the underlying navigation. `overflow-hidden + relative` are
added to the host so the bloom stays inside the rounded shape.
`glow-pulse` keyframe sits in globals.css next to the existing
`pulse-dot` / `shimmer` / `fade-in` definitions; reduced-motion
suppresses the animation to instant-opacity-0 so the click flow
is preserved without the bloom.
Wired into the highest-conversion CTAs only — the user explicitly
asked "wo's Sinn macht":
- Hero "Start building free" + "Read the docs"
- Marketing header Login / Dashboard button
- Dashboard header "+ New server" pill
Deliberately NOT applied to dashboard nav links, logout, destructive
buttons, form internals, carousel dots — pulse on every click would
be noise.
2. **Hero fills 100svh − nav** (`min-height: calc(100svh - 3rem)`).
`svh` (small viewport height) instead of `vh` so the hero doesn't
jump when the mobile address bar hides/shows. The 3rem subtracts
the sticky marketing nav (h-12 = 48px), so the hero ends right at
the loadscreen's natural bottom edge.
`flex items-center` plus the inner grid's existing `md:items-center`
keep the content vertically centred inside the tall section. The
ParticleHero background now has cinematic-scale room and the indigo
radial-glow + dot-mask read as the dominant background motif —
which is the effect the user loved at load-in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The /v1/servers/preview route ran claude-opus-4-7 synchronously; full spec
generation routinely exceeded Cloudflare's ~100s proxy cap, so the browser
received a headerless 524 and reported it as a CORS failure.
- preview now uses claude-sonnet-4-6 with a 45s per-attempt timeout and one
retry — comfortably inside the proxy budget
- generateSpec maps an exhausted timeout to SpecTimeoutError; the route
returns a clean 504 (with CORS headers) instead of a stalled connection
- analyze step: live elapsed-seconds counter as freeze-proof, plus a
reduced-motion exception so the loading spinner keeps spinning (a status
indicator, which WCAG exempts from reduced-motion)
- textarea resize grip restyled to dark theme (light hatch on dark square)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Hero h1 was a fixed text-[44px] — overflowed narrow phones. Now
text-[30px] sm:text-[40px] md:text-[56px].
- Hero grid children get min-w-0 so the code blocks' overflow-x-auto
actually constrains instead of widening the page.
- Marketing nav: the inline links were hidden below md with no fallback.
Added a hamburger MobileMenu; "Sign in" collapses into it on the
smallest screens.
- Section vertical padding is now responsive (py-14 sm:py-20).
- globals.css: overflow-x: clip on <html> as a safety net.
- docs: the 240px sidebar is hidden below lg, article gets min-w-0.
- dashboard header: nav labels collapse to icons on small screens.
Verified: next build passes (40/40 pages).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P0 — three critical issues found by tracing every attack vector on the template
publish + fork + render path. All three fixed and verified with attack tests.
FIX A — Takedown actually stops malicious containers
PATCH /v1/admin/templates with status=takedown previously only updated
mcp_servers.status to 'paused' in the DB. The Docker container kept running
and serving traffic on its allocated port — takedown was cosmetic. Now the
endpoint enumerates every fork's container, calls 'docker rm -f' on each,
clears container_id/public_url/host_port in the DB, and returns the
stoppedContainers count. New apps/api/src/lib/docker.ts owns the stop logic.
Verified: takedown stopped container f5632962, port 4109 connection refused.
FIX B — Reject specEdit on fork
A hand-crafted POST /v1/servers with {templateId, previewId, specEdit} would
enter the spec-edit branch, merge edits into the cached spec, but the worker
reads the pre-built template code (separate cache key), ignoring the merged
spec entirely. User thinks they changed something; deployed container behaves
as the original. Now returns 400 spec_edit_forbidden_on_fork with an explainer
pointing to the Iterate flow.
FIX C — templateId validation via Redis fork-ref
templateId on POST /v1/servers was user-controlled and unvalidated:
fork_count of any template could be pumped, mcp_servers got garbage
template_id rows, takedown cascade would miss the bogus rows. Fork endpoint
now writes a Redis key fork-ref:<previewId> -> templateId (5min TTL).
Server-create requires the ref to exist AND match the submitted templateId.
Verified attack: fake templateId without fork-ref returns 410 fork_ref_expired.
DEFENSE-IN-DEPTH — Hardened static checks
Banned patterns (added):
Function\s*\(['"`] — Function('code')() form, no 'new' needed
\bimport\s*\( — dynamic import escapes bundle scope
\bsetTimeout\s*\(['"`] — setTimeout('code', ms) eval form
\bsetInterval\s*\(['"`]
\bfs\s*\.\s*(unlink|rmdir|rm)\b
\bprocess\s*\.\s*kill\b
you are now in (developer|jailbreak|dan) mode — extra jailbreak markers
Hardcoded-credential patterns (new — scanForLeakedSecrets):
sk-ant-(api|sid)… — Anthropic
sk-… — OpenAI
sk_(live|test)_… — Stripe
ghp_… — GitHub PAT
github_pat_… — GitHub fine-grained
xox[bpoasr]-… — Slack
AKIA[0-9A-Z]{16} — AWS
-----BEGIN…PRIVATE KEY----- — RSA / SSH / GPG
Triggered when a publisher pasted their key into the prompt and Claude
embedded it literally in the generated code. Publish-blocking.
Verified attack: smuggled 'Function("return 1")' into a build's
generated_code, attempted publish → 422 publish_blocked.
Slug regex tightened — fork + detail routes now require
^[a-z0-9][a-z0-9-]{0,63}$ (was loose min(1).max(64) — letting through
'../admin', long strings, mixed case).
UI warning — Publish-as-template form now shows an amber callout listing
what's scanned and explicitly stating egress allowlisting is roadmap, not
enforced today (was misleading: the field was collected, never enforced).
TEMPLATE_SECURITY_AUDIT.md added — documents all 20 audited vectors with
severity, status, and rationale for what's deferred.
UI polish
globals.css — select/input/textarea/button get color-scheme: dark + custom
chevron + option styling so Chrome's native popdown stops rendering as a
white OS-themed widget on dark pages. The /templates category dropdown was
the immediate trigger; same rule applies system-wide.