Skip to content
All projects
Platform engineering

This Site

A personal site built as a platform exercise: one codebase, two deployment targets, and an AI twin that cannot be forged.

Result
2
deploy targets, one codebase
Where
Personal
2026
Stack
7 tools
Next.js · Docker · Terraform · S3 · CloudFront · Lambda · DynamoDB

Context

A personal site is usually a template. This one is the portfolio piece: the interesting work is the delivery path, not the markup.

The problem

The site had to ship two ways, as a container and as static files on S3, without maintaining two codebases. And the digital twin, which needs a live server, had to work on a static host where no server exists.

  1. step: Browsersame origin
  2. control: CloudFrontroutes by path
  3. data: S3static site, OAC
  4. step: Lambda/api/chat, IAM + OAC
  5. result: Streamed answersigned on the way out
One path serves the site, another serves the twin, on the same origin.

Approach

  1. 01

    One codebase, two build targets

    A build flag switches between a standalone Node server for the container and a fully pre-rendered export for S3. Both are built on every CI run, so neither path rots.

  2. 02

    Distroless runtime

    The final image has no shell and no package manager, runs as a non-root user, and contains only the compiled server. The toolchain that built it never reaches the shipped layer.

  3. 03

    Serve the AI endpoint from the edge, not a second domain

    A streaming Lambda is attached to the same CloudFront distribution as a second origin, so the browser calls a same-origin path. No CORS, no second certificate. The function URL is IAM-authenticated behind Origin Access Control, so nobody can bypass the edge and spend the model credit directly.

  4. 04

    Sign the assistant's own turns

    The chat endpoint accepts conversation history from the browser. Checking that a message claims the assistant role proves nothing about who wrote it, so every answer is returned with an HMAC over its text and verified on replay. A fabricated prior turn is rejected before the model sees it.