Projects

Architected & developed

princelundgren.com

My portfolio — the one you're reading right now. I designed and built every layer of it myself, from the front end to the cloud it runs on.

AstroTypeScriptAWS CDKCloudFrontS3 (OAC)ACMRoute 53GitHub Actions
Visit princelundgren.com 
princelundgren.com
Screenshot of princelundgren.com
Problem
I wanted my portfolio to be as carefully made as the work it shows — a fast, hand-built site, not a template — running on infrastructure worth showing off. It's served from a dedicated workload account while the princelundgren.com DNS zone stays in a separate one, with zero manual DNS steps and no long-lived credentials anywhere.
Approach
The front end is a static Astro build — a bespoke design system, an illustrated SVG hero, and effectively zero client-side JavaScript — deployed onto a small set of AWS CDK stacks. Cross-account DNS is automated through a tightly-scoped assumed role, and the ACM certificate is owned by a provider-framework Lambda (a plain CloudFormation certificate deadlocks when its validation records live in another account). CloudFront fronts a private S3 bucket via Origin Access Control, with HTTP/3, a strict security-header policy, and standard logging v2. CI deploys through GitHub OIDC — no stored keys — using a two-pass cache strategy and scoped invalidation.
Outcome
A site that posts a clean 100 across Lighthouse and ships zero client-side JavaScript (bar a 384-byte print helper) behind an enforced CSP. Underneath, hands-off certificate renewal, fully automated OIDC deploys, and cdk-nag-clean infrastructure with unit tests on its security properties — the whole architecture written up as a portfolio piece in its own right.

This site is its own case study, in two halves: a fast, hand-built front end, and the cross-account AWS infrastructure it runs on. The front end had to earn its place — built by hand, not from a template — and the platform had a specific brief: serve the apex princelundgren.com from a dedicated workload account while the Route 53 zone stays in a separate one, automated end to end with no long-lived credentials.

The site itself

It’s a hand-built Astro 6 static site — no framework runtime, no page builder, and effectively zero client-side JavaScript (the lone exception is a 384-byte print helper). Even the illustrated hero — me at a desk of clouds, drones, and a 3D printer — is hand-drawn SVG, animated with CSS.

The look is a small bespoke design system — warm paper and ink, a single accent, a display serif, a paper-grain texture, and one orchestrated page-load reveal — so it feels made, not generated. Speed and accessibility come by construction: a clean 100 across Lighthouse, first paint under a second, AA contrast throughout, plus per-page metadata, generated Open Graph cards, and structured data.

Under the hood

That brief is harder than it sounds. An apex domain can’t be delegated to another account the way a subdomain can. So the workload account has to write a couple of records into a zone it doesn’t own: the ACM validation records, and the apex/www alias records pointing at CloudFront. The clean way to bridge that is a single, tightly-scoped IAM role in the DNS account that the workload account assumes — able to change records in exactly one hosted zone and nothing else.

The certificate is the sharp edge. A CloudFormation Certificate resource blocks until it’s validated, but you can’t read its validation record until it exists, and that record has to land in the other account — a deadlock. So the cert is owned by a small provider-framework Lambda that drives the lifecycle over the SDK: request → write the validation records cross-account → poll to issued → clean up on delete. No deadlock, and renewals are hands-off forever after.

The rest of the stack

  • Private S3 + CloudFront with Origin Access Control — the bucket is never public; CloudFront signs every origin request.
  • Hardened edge — HTTP/3, TLS 1.2_2021, a response-headers policy (HSTS preload, CSP, frame and referrer controls), a single edge function that does the www→apex redirect and pretty-URL rewriting, and standard logging v2.
  • No keys in CI — GitHub Actions authenticates with OIDC and can only assume the CDK bootstrap roles; deploys run a two-pass s3 sync (immutable hashed assets, then revalidated HTML) and a scoped cache invalidation.
  • Verified, not vibescdk synth is cdk-nag clean, with unit tests asserting the load-bearing security properties.