This website
Personal project, 2026
A static portfolio I built to present my profile for an internship abroad in summer 2027, and run like a production service: infrastructure as code, automated deployment, no long-lived credentials, a few euros a month.
At a glance
- A+Mozilla Observatory, 12/12 tests
- ≥ 95Lighthouse, enforced in CI
- 0 keysstored: deploys via OIDC
- 2 USDmonthly budget alert
Architecture
Deployment pipeline
- 1BuildAstro static site
- 2Scan codeSAST, secret detection
- 3Scan infraCheckov on Terraform
- 4LighthouseEvery category ≥ 95
- 5HTML & linksValid, no broken links
- 6Deploymain, via OIDC
- 7InvalidateCloudFront cache
- Merge requests run every check, never deploy
- One deployment at a time
- Terraform state in S3, locked
- Renovate weekly, releases ≥ 14 days old
Security
- Private S3 bucket
Public access blocked; CloudFront only, via OAC.
- Strict CSP and headers
default-src 'none', HSTS for 2 years, no Server header.
- No inline code
No inline script or style, so no CSP exceptions.
- HTTPS only
TLS 1.2 minimum, ACM certificate.
- Deploys without keys
OIDC role limited to this project and main.
- SAST and secret detection
GitLab SAST and secret scan on every pipeline.
- Mozilla Observatory A+
130 out of 100, all 12 tests passed.
- Checkov on Terraform
Skipped rules documented; new findings fail.
Cost
- Hosting
- Fully static site: no server, no database, nothing that bills while idle.
- CDN
- CloudFront PriceClass_100, the cheapest edge locations (Europe and North America).
- Routing
- A CloudFront Function serves index.html for sub-pages, well within the free tier.
- Monitoring
- Everything stays within the AWS free tier.
- Guardrail
- An AWS Budgets alert emails me at 80% of the limit, and when the forecast exceeds it.
- Budget limit
- 2 USD / month
Monitoring
- every 5 minUptime check
A small Python Lambda checks that the site answers 200 with the expected content.
- dailyBrowser canary
A CloudWatch Synthetics canary opens the home page and this case study in a real browser and fails on any console error, including CSP violations.
- on failureEmail alarms
Each check has a CloudWatch alarm that emails me through SNS, and again when the site recovers.
Visit statistics come from CloudFront's built-in reports: no tracking script, no cookies, no consent banner.
Quality
- Lighthouse CI
Audits every page before deployment and fails the pipeline if any category drops below 95.
- Valid HTML
Every page is checked for valid, accessible HTML and for broken links.
- Dark mode
Follows the system setting, remembers the visitor's choice without a flash, and keeps every colour pair above WCAG AA contrast.
- Print stylesheet
A clean PDF for recruiters: no navigation, light colours, contact details written out.
All technical details
Goal
- Present my profile to recruiters for an internship abroad in summer 2027.
- Run it the way I would run a production service: infrastructure as code, automated deployment, no long-lived credentials, and a monthly budget of a few euros.
Security
- Private S3 bucket: all public access is blocked and only my CloudFront distribution can read it, through Origin Access Control.
- Strict Content Security Policy (default-src 'none', scripts, styles and fonts from 'self' only), HSTS for two years, X-Frame-Options DENY, a restrictive Permissions-Policy, and the Server header removed.
- The site ships no inline script or style, so the CSP needs no exceptions.
- HTTPS only, TLS 1.2 minimum, with an ACM certificate for the domain and www.
- GitLab CI deploys through OIDC: no AWS key is stored anywhere. The role is limited to this project and the main branch, and can only write to this bucket and invalidate this distribution.
- GitLab's SAST and secret detection run on every pipeline.
- Mozilla Observatory grades the security headers A+ (130 out of 100, all 12 tests passed).
- Checkov scans the Terraform code on every pipeline. Each rule I skip is documented with its reason, usually cost (WAF, KMS) or not applicable to a static site; any new finding fails the build.
Cost
- Fully static site: no server, no database, nothing that bills while idle.
- CloudFront PriceClass_100, the cheapest edge locations (Europe and North America).
- An AWS Budgets alert emails me at 80% of a 2 USD monthly limit, and when the forecast exceeds it.
- A CloudFront Function serves index.html for sub-pages, well within the free tier.
Deployment
- All AWS resources are defined in Terraform.
- Every push to main builds the site, runs the security scans, then syncs it to S3 and invalidates the CloudFront cache.
- Hashed assets are cached for a year; HTML is revalidated on every visit, so a new version is live right after the deployment.
- Only one deployment can run at a time.
- The Terraform state lives in S3: versioned, encrypted, with native locking so two applies can never run at once.
- Merge requests run every check but never deploy; only main can assume the deployment role.
- Renovate opens a merge request every week for outdated dependencies (npm, CI images, Terraform providers), only for releases at least 14 days old.
Quality
- Lighthouse CI audits every page before deployment and fails the pipeline if any category drops below 95.
- Every page is checked for valid, accessible HTML and for broken links.
- Dark mode follows the system setting, remembers the visitor's choice without a flash, and keeps every colour pair above WCAG AA contrast.
- A print stylesheet gives recruiters a clean PDF: no navigation, light colours, contact details written out.
Monitoring
- Every 5 minutes, a small Python Lambda checks that the site answers 200 with the expected content.
- Once a day, a CloudWatch Synthetics canary opens the home page and this case study in a real browser and fails on any console error, including CSP violations.
- Each check has a CloudWatch alarm that emails me through SNS, and again when the site recovers.
- Visit statistics come from CloudFront's built-in reports: no tracking script, no cookies, no consent banner.
- Everything stays within the AWS free tier.
What I'd improve next
- Quote a LinkedIn recommendation from my EDF supervisor, once I have one.
Contact
Email meLinkedInBased in Lyon, France
Last updated