Projects

Architected & developed

princelundgren.com

My portfolio — 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 on desktop Lighthouse (98–100 on throttled mobile) and ships zero client-side JavaScript behind an enforced CSP. Underneath, hands-off certificate renewal, CA-locked issuance, 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 zero client-side JavaScript. 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. Accessibility is built in, not bolted on: every text colour is tuned to clear WCAG AA contrast, with a skip link, visible focus rings, reduced-motion support, semantic landmarks, per-page metadata, generated Open Graph cards, and structured data. (Performance and security get their own sections below.)

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.

Performance

A clean 100 on desktop Lighthouse, and 98–100 on throttled mobile (Slow-4G + 4× CPU). The score is a side effect of the build, not a number chased:

  • Zero client-side JavaScript → a Total Blocking Time of 0 ms. There’s no framework runtime to hydrate; the only inline scripts are JSON-LD data blocks.
  • Cumulative Layout Shift of 0 — self-hosted fonts (preloaded for the above-the-fold pair, Latin-subset, with metric fallbacks) and dimensioned images mean nothing reflows as the page settles.
  • First paint under a second — Brotli over HTTP/3, hashed assets cached immutably while HTML revalidates, delivered by a two-pass s3 sync.
  • The illustrated hero is inline SVG, so there’s no image to fetch — it paints with the page. (The one thing throttled mobile dings is Speed Index, as that detailed scene rasterises.)

Security

Security is treated as a product requirement — enforced at the edge, verified in CI:

  • Private origin — S3 blocks all public access; CloudFront reaches it only through Origin Access Control, signing every origin request.
  • Strict transport — HTTPS-only on TLS 1.2 (2021 policy), with HSTS for two years, includeSubDomains + preload (submitted to the browser preload list).
  • CA-locked issuance — a CAA record so only Amazon’s CAs can ever issue a certificate for the domain.
  • Enforced CSP (script-src 'self') plus X-Frame-Options: DENY, X-Content-Type-Options, a strict Referrer-Policy, and a Permissions-Policy that disables camera, mic, and geolocation — all from a CloudFront response-headers policy. A single edge function handles the www→apex redirect, pretty URLs, and first-visit language detection.
  • No long-lived credentials — CI authenticates through GitHub OIDC; cross-account DNS is one least-privilege assumed role, scoped to a handful of record names and types and nothing else.
  • Receipts — a /.well-known/security.txt for disclosure, standard logging v2, a cdk-nag-clean synth, and unit tests asserting the load-bearing security properties.