bash — simo@dev:~/projects

simo@dev:~/projects$ git clone portfolio

Cloning into 'portfolio'...

fatal: could not read from remote repository: 'portfolio' is private.

# The source code is private for now.

simo@dev:~/projects$ cat ~/case-studies/portfolio.md

# Web · Infrastructure · 2026

simonedimauro.com

This site. A static Astro build served by one Cloudflare Worker, with a hardened contact API (Turnstile, validation, rate limiting, Resend), a staging Worker, no framework JavaScript, a real shell on every page and a text profile for curl.

./live_demo source: private
year
2026
type
Web · Infrastructure
status
Live
source
private
role
Design, development and infrastructure
  • Astro
  • TypeScript
  • Tailwind CSS
  • Cloudflare Workers
  • Turnstile
  • Resend
  • Vitest
  • GitHub Actions

simo@dev:~/projects/portfolio$ ./benchmark --summary

  • 16 KB

    of JavaScript on the homepage, gzipped, interactive terminal included, no framework runtime

  • 0

    inline scripts, so the CSP needs no unsafe-inline for scripts

  • 85

    tests for the Worker and the shell, run on every push with a deploy dry-run

  • 2

    Workers, production and a noindex staging copy

From a React SPA to static HTML

The first version was a React single-page app built with Vite, animated with framer-motion and sending email through EmailJS straight from the browser. The rewrite keeps the same design, content and animations, but ships static HTML generated by Astro. None of the interactions needed a framework: the boot sequence, the Matrix transition, the htop table and the packet capture are plain TypeScript modules and CSS animations. The homepage loads about 16 KB of gzipped JavaScript, the interactive terminal included, and no inline script, which keeps the Content Security Policy strict.

One Worker for the site and the API

The build output is served as static assets by a single Cloudflare Worker. In production the Worker code runs only for /api/* and for /; every other request is answered directly from the assets, with a custom 404 page and security headers. There is no SSR adapter and no server to maintain.

A shell on every page, and an answer for curl

Every terminal window on the site used to be an animation. Now one of them is real: the >_ button, the ~ key or Ctrl+K opens a shell that knows the whole site. ls -la projects, cat README.md, open mafdet, neofetch, tab completion, history, and the easter eggs a developer tries first. The filesystem it explores is generated at build time from the same content collections as the pages and shipped as a JSON data block, so the CSP did not change and nothing is maintained twice. The interpreter has no DOM dependency and is tested like the Worker.

The Worker runs on / for one reason: when the client is curl, wget or HTTPie, it answers with a coloured text profile rendered at build time instead of the HTML, with Vary: User-Agent so caches keep the two apart. Browsers get the static page untouched. The boot log also ends with three lines about the visitor’s own connection, as the edge sees it: city and country, carrier, TLS version and protocol. They come from a small endpoint that returns coarse fields only, never the IP address, stores nothing and is never cached.

A contact endpoint that assumes hostile input

POST /api/contact replaces EmailJS, so no email credentials ever reach the browser. Each request goes through the same pipeline, and nothing is sent unless every step passes:

  1. Method and Content-Type checks, then a 16 KB body limit.
  2. JSON parsing and strict validation of every field, rejecting control characters and line breaks that could inject email headers.
  3. A hidden honeypot field: bots that fill it get a plausible success and nothing is sent.
  4. A per-IP rate limit through the Workers Rate Limiting binding.
  5. Cloudflare Turnstile verified server-side, including the hostname and the widget action, so a token issued elsewhere or replayed is rejected.
  6. Delivery through the Resend API, with recipient and sender taken only from server configuration and the visitor’s address used only as Reply-To.

Errors come back as small JSON objects with generic messages. The logs record what failed, never the API key or the visitor’s data.

Staging, CI and a bug only the runtime could show

Staging is a second Worker on its own subdomain, with the same strict checks, its own secrets and a noindex header on every response. Its allowed hostname differs from production, so a Turnstile token from one environment is rejected by the other. GitHub Actions runs the typecheck, lint, the Worker and shell tests, the build and a Wrangler dry-run for both environments on every push.

The first real email on staging failed with a network error while every test was green. The email client kept the global fetch as an object property and called it as a method. Node accepts that, the Workers runtime does not, and the error was being swallowed. Reproducing it in the local Workers runtime pinned it down in minutes. The fix calls fetch directly, adds a log that records the real error name, message and cause, and adds a regression test that enforces the runtime’s rule.

Email without touching the existing mailboxes

The domain’s mailboxes stay with the existing provider. Outgoing mail uses a dedicated subdomain verified in Resend, so its SPF and DKIM records never collide with the main domain’s MX and SPF.

# Want something like this built or reviewed?