Technical case studies

Selected infrastructure and security work, documented with the constraints, the approach, and how each change was verified.

Active

Portfolio infrastructure — public-root isolation & deployment contract

Objective
Remove infrastructure-file disclosure and correct soft-404 behaviour on a static site
Role
Infrastructure and deployment owner
Stack
nginx · Docker · Traefik · Node.js · Bash

Problem

The original deployment served the whole repository root as the nginx webroot. An audit against the live site confirmed that docker-compose.yml, Dockerfile, nginx.conf, and DEPLOYMENT.md were each reachable at their own public URLs. The same audit found unknown paths returning HTTP 200, because the server fell back to /index.html on a multi-page site — leaving the custom 404 page unreachable.

Constraints

The site is static HTML with no runtime dependency, so the fix could not add a server-side routing layer or a framework. It had to hold as a build-time contract: whatever is not in the built image cannot be fetched, by construction, rather than by a list of paths someone has to remember to block.

Approach

Served files were moved into a dedicated public/ directory, and the image was rebuilt to COPY only that directory plus the nginx config. The bind mount was removed, which removes the entire class of infrastructure-file leaks rather than blocking individual paths one by one. Routing changed to try_files $uri $uri/ =404 with a real error_page 404.

Cache headers were split by content type — HTML, CSS, and JS revalidate, while content-addressed images are served immutable. Images were re-encoded from archived originals to WebP at their actual rendered dimensions.

Verification

A static analysis gate (tools/check_site.mjs, Node.js standard library only) checks every page for exactly one <h1>, duplicate attributes, inline style or on* handlers, unresolved internal links, missing or orphaned assets, page metadata, unverified claims, and the served-tree boundary. It is deterministic — two runs produce byte-identical output — and returns a non-zero exit code on failure, so it can fail a build instead of only reporting.

Deployment verification is HTTP-level and documented in the repository: site root, an unknown path, a known infrastructure file, the 404 page, and cache headers for CSS and for an image. Each is a command with an expected status code.

Lessons

Treating the container image as the security boundary is more durable than filtering paths. And a check that fails on the state it was written against is what makes it trustworthy — a gate that has never been red has not been shown to detect anything.

Additional case studies will be published as work is completed and documented.