Architecture
How I think about building systems — the principles behind every decision, the structure of the delivery platform, and the reasoning that connects security, automation, and simplicity into one coherent approach.
How I think before I build
These are not rules I follow — they are positions I hold. Every architectural decision in this platform traces back to one of them.
Every system I build starts with the question: what is the minimum access needed for this to work? IAM OIDC over long-lived credentials. Private S3 origins over public buckets. OAC over legacy OAI. Least privilege is not a compliance checkbox — it is a design constraint applied from day one.
If the only way to rebuild your infrastructure is to remember what you clicked in the console, it is already broken. Everything I provision is in Terraform — version-controlled, reviewable, and deployable from scratch in minutes. The infrastructure is code. It lives in the repo. It has a history.
Manual deployments introduce variability. The tenth time someone runs a deploy, they will skip a step. CI/CD pipelines enforce the same process every time — lint, type-check, test, build, deploy — regardless of who triggers them or when. Consistency at the cost of flexibility is worth it.
The primary deployment path for this platform is S3 + CloudFront, not Kubernetes. That is intentional. Kubernetes adds real operational cost — nodes to manage, probes to tune, networking to debug. For a static site, that cost buys nothing. Use the simplest tool that meets the requirement. Add complexity only when the problem demands it.
A system you cannot observe is a system you cannot operate. Health probes, structured logging, and deployment audit trails are not optional additions — they are part of the delivery contract. In Kubernetes, liveness and readiness probes are the difference between self-healing infrastructure and a silent failure waiting to be discovered.
The full delivery stack
Every layer of this platform was chosen deliberately. Here is what each one does and why it was chosen over the alternatives.
A alias record routes doraejangue.com to the CloudFront distribution. www redirects to the canonical root domain via a separate S3 redirect bucket and CloudFront distribution.
CloudFront terminates HTTPS, caches static assets at the edge globally, and enforces redirect-to-HTTPS on all viewer requests. The ACM certificate covers both the root and www domains.
The S3 bucket stores the Next.js static export. Public access is fully blocked at the bucket level. Only CloudFront can read from it, authenticated via Origin Access Control with SigV4 signing.
A viewer-request CloudFront Function rewrites clean URLs to their index.html equivalent. /projects/ becomes /projects/index.html before the request hits S3. Without this, direct navigation returns AccessDenied.
All AWS resources — S3, CloudFront, OAC, ACM, Route 53, IAM OIDC — are provisioned by Terraform. The module structure separates reusable resource definitions from environment-specific configuration.
Every push to main triggers a CI run. On success, two independent deploy jobs run in parallel: one syncs the static export to S3 and invalidates CloudFront, the other builds and pushes a Docker image to the registry and deploys to Kubernetes.
Two tracks, one platform
The same codebase deploys via two independent paths. This is a deliberate architectural choice — not duplication, but demonstration of two distinct delivery patterns.
Optimal for static content. No servers to manage, global CDN, sub-100ms TTFB, effectively zero cost at portfolio scale.
Demonstrates orchestration, health management, and platform operations — skills that matter for production workloads at scale.
How access is controlled
Every access boundary in this platform is explicit. Nothing is open by default.
Block Public Access is enabled at the bucket level. No S3 object URL is reachable directly from the internet under any circumstance.
The S3 bucket policy allows only the specific CloudFront distribution ARN to call s3:GetObject. Any other principal — including AWS root — is denied.
CloudFront authenticates every request to S3 using AWS Signature Version 4. This is the current AWS-recommended pattern, replacing the legacy Origin Access Identity.
GitHub Actions authenticates to AWS using IAM OIDC. Each workflow run receives a short-lived token. No AWS access keys exist in GitHub Secrets.
CloudFront is configured with redirect-to-https on all viewer connections. HTTP requests are never served — they are permanently redirected to HTTPS.
The IAM role assumed by GitHub Actions has only the permissions needed: s3:PutObject, s3:DeleteObject, cloudfront:CreateInvalidation. Nothing more.
Each project page documents the architecture decisions, trade-offs, and lessons learned in full detail.