migrate CRA to Next.js 16 + React 19 + TypeScript + Tailwind v4
- replace CRA scaffold with Next.js static export config - add app router pages: home, homelab, archive - add components: Nav, Footer, Hero, Skills, ProjectCard, ThemeScript - add ThemeContext for dark mode with FOUC prevention - add data layer: projects.ts, services.ts - update .gitignore for Next.js (ignore .next/, out/, next-env.d.ts) - add docs: handoff plan, homelab decisions, style reference
This commit is contained in:
30
docs/handoff-plan.md
Normal file
30
docs/handoff-plan.md
Normal file
@@ -0,0 +1,30 @@
|
||||
What's done
|
||||
git tag cra-legacy — old CRA code preserved
|
||||
Next.js 16 + React 19 + TypeScript + Tailwind v4 scaffolded
|
||||
next.config.ts — output: 'export', trailingSlash: true, images: { unoptimized: true }
|
||||
package.json — deploy scripts, postbuild writes out/.nojekyll, gh-pages@^6
|
||||
globals.css — full design system: colors, fonts, breakpoints, keyframes (Tailwind v4 CSS-first config)
|
||||
ThemeScript.tsx — FOUC-prevention blocking script
|
||||
ThemeContext.tsx — dark mode provider + useTheme hook, localStorage key lerko96-dark-mode
|
||||
Nav.tsx — sticky header, active link detection, dark mode toggle
|
||||
Footer.tsx — dynamic copyright year, social links
|
||||
layout.tsx — Montserrat + Source Code Pro via next/font, Font Awesome CDN, ThemeProvider wrapping
|
||||
src/data/projects.ts — all 8 projects typed, featured + archive split
|
||||
src/data/services.ts — 20+ homelab services with categories
|
||||
src/app/page.tsx — home page with Hero, Skills, ProjectCards
|
||||
src/app/homelab/page.tsx — full homelab page: at-a-glance, VLAN table, services grid, 6 ADRs, GitHub CTA
|
||||
src/app/archive/page.tsx — archive grid with all older projects
|
||||
public/CNAME — www.lerko96.com
|
||||
public/images/headshot-tyler_koenig.png — restored from git tag
|
||||
What's left
|
||||
Test build — npm run build and fix any TypeScript/lint errors (hasn't been run yet)
|
||||
Local preview — npx serve out and verify all 3 routes load correctly, dark mode toggle works, no broken assets
|
||||
Email address — Footer has tylerkoenig96@gmail.com hardcoded — confirm that's correct or update
|
||||
Deploy — npm run deploy pushes out/ to master
|
||||
Post-deploy checks — confirm www.lerko96.com loads, /homelab and /archive routes work via direct URL, _next/ assets load (not 404'd)
|
||||
Screenshots — swap gradient placeholders for real project screenshots when ready (just drop into public/images/ and update projects.ts)
|
||||
CLAUDE.md update — update stack, commands, and architecture notes to reflect Next.js (currently still says CRA)
|
||||
Key gotcha to remember
|
||||
Tailwind v4 is CSS-first — no tailwind.config.ts. All custom tokens live in globals.css under @theme {}. Dark mode variant is defined as @variant dark (&:where(.dark, .dark *)).
|
||||
|
||||
Memory has been saved to /home/lerko/.claude/projects/-home-lerko-Code-lerko96-github-io/memory/ — the next session will have full context automatically.
|
||||
85
docs/homelab-decision.md
Normal file
85
docs/homelab-decision.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# DECISIONS
|
||||
|
||||
# decisions
|
||||
|
||||
> Short notes on *why* things are configured the way they are.\nYou'll forget this in 6 months. Future-you will thank present-you.\nFormat: title, date, context, decision, alternatives considered.
|
||||
|
||||
|
||||
## AT&T Gateway kept in-line (not bypassed)
|
||||
|
||||
**Date:** \[date\]
|
||||
|
||||
**Context:** AT&T Fiber requires 802.1X certificate authentication locked to their gateway hardware. No way to replace it with a standard modem.
|
||||
|
||||
**Decision:** Keep BGW320 in-line with IP Passthrough mode (DHCPS-fixed to pfSense WAN MAC). pfSense gets the public IP directly. Gateway WiFi disabled.
|
||||
|
||||
**Alternatives considered:**
|
||||
|
||||
* EAP proxy bypass — more complex, breaks on AT&T firmware updates, only saves 1-2ms latency. Not worth it.
|
||||
* True bridge mode — AT&T gateways don't support it.
|
||||
|
||||
|
||||
## Caddy as reverse proxy with Cloudflare DNS challenge
|
||||
|
||||
**Date:** \[date\]
|
||||
|
||||
**Context:** Needed valid SSL certs for all homelab services without exposing port 80/443 to the internet for HTTP challenge validation.
|
||||
|
||||
**Decision:** Caddy with `caddy-dns/cloudflare` plugin. DNS-01 challenge via Cloudflare API. All subdomains on [lerkolabs.com](http://lerkolabs.com) point to Caddy (10.2.0.20) in Pi-hole. Caddy terminates SSL and proxies to backends.
|
||||
|
||||
**Alternatives considered:**
|
||||
|
||||
* Self-signed certs — browser warnings, annoying on mobile/VPN clients.
|
||||
* Nginx Proxy Manager — more UI overhead, same result.
|
||||
* Traefik — more complex config for the same outcome here.
|
||||
|
||||
|
||||
## WireGuard over OpenVPN
|
||||
|
||||
**Date:** \[date\]
|
||||
|
||||
**Context:** Needed remote access to homelab services.
|
||||
|
||||
**Decision:** WireGuard on pfSense, UDP 51820, VPN subnet 10.200.0.0/24. Clients get same access as LAN for Homelab and MGMT, blocked from Guest/IoT/WFH.
|
||||
|
||||
**Alternatives considered:**
|
||||
|
||||
* OpenVPN — slower, more complex config, worse battery on mobile. No advantage here.
|
||||
* Tailscale — adds external dependency and relay for what can be direct.
|
||||
|
||||
|
||||
## Pi-hole in Homelab VLAN, not MGMT
|
||||
|
||||
**Date:** \[date\]
|
||||
|
||||
**Context:** Pi-hole serves DNS to all VLANs. Needed to decide where to host it.
|
||||
|
||||
**Decision:** VLAN 1020 (Homelab) at 10.2.0.11. Firewall rules allow port 53 inbound from all VLANs. MGMT VLAN uses pfSense as primary DNS (more reliable for network equipment).
|
||||
|
||||
**Alternatives considered:**
|
||||
|
||||
* MGMT VLAN — cleaner separation, but creates firewall complexity allowing all VLANs to reach MGMT.
|
||||
* DMZ — no reason to put a DNS server public-facing.
|
||||
|
||||
|
||||
## WFH VLAN uses pfSense DNS only (not Pi-hole)
|
||||
|
||||
**Date:** \[date\]
|
||||
|
||||
**Context:** Work laptop on WFH VLAN should be maximally isolated from personal infrastructure.
|
||||
|
||||
**Decision:** WFH DHCP hands out pfSense (10.5.0.1) as the only DNS server. Work device can't reach Pi-hole at 10.2.0.11 and doesn't need to.
|
||||
|
||||
|
||||
## N100 for pfSense
|
||||
|
||||
**Date:** \[date\]
|
||||
|
||||
**Context:** Needed hardware that handles 1Gbps routing + WireGuard VPN without bottlenecking.
|
||||
|
||||
**Decision:** Intel N100 mini PC. 4-core 3.4GHz, \~6W idle. Handles 2-3Gbps routing, 600-900Mbps WireGuard. Adequate for 1Gbps AT&T fiber with room to grow.
|
||||
|
||||
**Alternatives considered:**
|
||||
|
||||
* Raspberry Pi — insufficient for 1Gbps + VPN.
|
||||
* Full rack server — overkill power draw for this role.
|
||||
171
docs/homelab-readme.md
Normal file
171
docs/homelab-readme.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# README
|
||||
|
||||
# 🏠 lerkolabs — Home Infrastructure Lab
|
||||
|
||||
> Personal infrastructure environment for learning, self-hosting, and operational practice. Running 24/7 on production-grade hardware with real network segmentation, SSO, monitoring, and IaC-style documentation.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## ⚡ At a Glance
|
||||
|
||||
| | |
|
||||
|-----|-----|
|
||||
| **Hypervisor** | Proxmox VE |
|
||||
| **Firewall** | pfSense (Intel N100) |
|
||||
| **Switching** | TP-Link Omada (managed, VLANs) |
|
||||
| **ISP** | AT&T Fiber 1Gbps — IP Passthrough → pfSense WAN |
|
||||
| **VPN** | WireGuard (pfSense) |
|
||||
| **Reverse Proxy** | Caddy + Cloudflare DNS-01 — wildcard SSL on `*.lerkolabs.com` |
|
||||
| **Auth** | Authentik SSO — OIDC + forward auth across all services |
|
||||
| **DNS** | Pi-hole → pfSense Unbound → Cloudflare |
|
||||
| **Containers** | 9 LXC containers + 2 VMs |
|
||||
| **Self-hosted services** | 20+ |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Network Architecture
|
||||
|
||||
**8 isolated VLANs** — strict inter-VLAN firewall policy (default deny):
|
||||
|
||||
| VLAN | Name | Subnet | Purpose |
|
||||
|------|------|--------|---------|
|
||||
| 1000 | MGMT | `10.0.0.0/24` | Network equipment only |
|
||||
| 1010 | LAN | `10.1.0.0/24` | Trusted personal devices |
|
||||
| 1020 | Homelab | `10.2.0.0/24` | All self-hosted services |
|
||||
| 1030 | Guests | `10.3.0.0/24` | Internet only, RFC1918 blocked |
|
||||
| 1040 | IoT | `10.4.0.0/24` | Smart home, isolated |
|
||||
| 1050 | WFH | `10.5.0.0/24` | Work devices, no personal access |
|
||||
| 1 | DMZ | `10.99.0.0/24` | Public-facing, hard-blocked internally |
|
||||
| — | VPN | `10.200.0.0/24` | WireGuard clients = LAN access |
|
||||
|
||||
Work devices (VLAN 1050) are isolated from *all* personal infrastructure — they use pfSense DNS only, never Pi-hole. WireGuard VPN clients get full LAN-equivalent access without being on the physical network.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ Compute — LXC / VM Layout
|
||||
|
||||
| Container | IP | Cores | RAM | What Runs |
|
||||
|-----------|-----|-------|-----|-----------|
|
||||
| `pihole` | 10.2.0.5 | 1 | 512MB | Pi-hole DNS + ad blocking |
|
||||
| `auth` | 10.2.0.25 | 1 | 512MB | Authentik SSO + Redis |
|
||||
| `infra` | 10.2.0.20 | 2 | 1GB | Caddy reverse proxy, ntfy |
|
||||
| `monitor` | 10.2.0.51 | 4 | 4GB | Victoria Metrics, Grafana, Beszel |
|
||||
| `apps` | 10.2.0.60 | 4 | 6GB | 15+ productivity apps (Docker) |
|
||||
| `vault` | 10.2.0.X | 1 | 256MB | Vaultwarden (isolated) |
|
||||
| `servarr` (VM) | — | 4 | 8GB | Plex, Jellyfin, \*arr stack, qBittorrent |
|
||||
| `haos` (VM) | — | 2 | 4GB | Home Assistant OS |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Self-Hosted Services
|
||||
|
||||
### Core Infrastructure
|
||||
|
||||
| Service | Role |
|
||||
|---------|------|
|
||||
| pfSense | Firewall, DHCP, routing, WireGuard VPN |
|
||||
| Pi-hole | Network-wide DNS + ad blocking |
|
||||
| Caddy | Reverse proxy, automatic wildcard TLS |
|
||||
| Authentik | SSO provider — OIDC + forward auth |
|
||||
| Vaultwarden | Self-hosted password manager |
|
||||
| Victoria Metrics + Grafana | Metrics, dashboards, alerting |
|
||||
| Beszel | Container + host monitoring |
|
||||
| ntfy | Push notifications |
|
||||
|
||||
### Productivity (`apps` LXC — all behind Authentik SSO)
|
||||
|
||||
| Service | URL | Purpose |
|
||||
|---------|-----|---------|
|
||||
| Outline | `outline.lerkolabs.com` | Team wiki |
|
||||
| Gitea | `gitea.lerkolabs.com` | Personal Git |
|
||||
| Vikunja | `tasks.lerkolabs.com` | Task management |
|
||||
| Ghostfolio | `finance.lerkolabs.com` | Portfolio tracking |
|
||||
| Hoarder | `hoarder.lerkolabs.com` | Bookmark manager |
|
||||
| Grist | `grist.lerkolabs.com` | Spreadsheets / data |
|
||||
| Actual Budget | `budget.lerkolabs.com` | Personal budgeting |
|
||||
| FreshRSS | `rss.lerkolabs.com` | RSS reader |
|
||||
| Memos | `memos.lerkolabs.com` | Quick notes |
|
||||
| Traggo | `time.lerkolabs.com` | Time tracking |
|
||||
| Baikal | `dav.lerkolabs.com` | CalDAV / CardDAV |
|
||||
| Glance | `glance.lerkolabs.com` | Homepage dashboard |
|
||||
| Filebrowser | `files.lerkolabs.com` | File management |
|
||||
|
||||
### Media
|
||||
|
||||
| Service | Purpose |
|
||||
|---------|---------|
|
||||
| Plex + Jellyfin | Media streaming |
|
||||
| Sonarr / Radarr / Lidarr | Automated media management |
|
||||
| Prowlarr + Bazarr | Indexer aggregation + subtitles |
|
||||
| qBittorrent (VPN-gated) | Downloads via Gluetun |
|
||||
| Calibre-Web Automated | Book library with auto-ingest |
|
||||
| Kavita | E-reader |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security Design
|
||||
|
||||
* All services require Authentik authentication — no anonymous access
|
||||
* Caddy handles TLS termination; internal services run HTTP only
|
||||
* VPN-only admin access to pfSense, Proxmox, Pi-hole
|
||||
* IoT devices can only reach the internet and Home Assistant
|
||||
* Guests are RFC1918 hard-blocked — internet only
|
||||
* WFH laptop on isolated VLAN, DNS from pfSense only
|
||||
* Vaultwarden isolated in its own LXC — no shared container
|
||||
* Zero open ports for management traffic (all VPN-gated)
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Architecture Decisions
|
||||
|
||||
Short-form ADRs documenting *why* things are built this way:
|
||||
|
||||
* **AT&T IP Passthrough over EAP bypass** — AT&T locks 802.1X to their gateway; passthrough mode gives pfSense the real public IP without brittle workarounds.
|
||||
* **Caddy over NGINX Proxy Manager** — single Caddyfile, auto-cert via Cloudflare DNS-01, no UI overhead.
|
||||
* **WireGuard over OpenVPN** — lower latency, better mobile battery life, \~600Mbps on the N100.
|
||||
* **Authentik over Authelia** — full OIDC provider + forward auth in one. Lets services like Outline, Gitea, and Vikunja use real SSO rather than just a login gate.
|
||||
* **Shared Postgres + Redis in** `**apps**` **LXC** — one instance, multiple databases. Avoids 15 separate DB containers; a single init script provisions all schemas on first run.
|
||||
* **N100 for pfSense** — 6W idle, handles 2–3Gbps routing + 600Mbps WireGuard. Right-sized for 1Gbps fiber.
|
||||
* **Pi-hole in Homelab VLAN, not MGMT** — MGMT access from all VLANs would be a larger attack surface than filtering DNS traffic.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 📁 Repo Structure
|
||||
|
||||
```bash
|
||||
homelab/
|
||||
├── docs/
|
||||
│ ├── README.md # quick reference — IPs, URLs, hardware
|
||||
│ ├── SERVICES.md # full service registry (IP, port, VLAN, status)
|
||||
│ ├── NETWORK.md # VLAN map, firewall policy, DNS architecture
|
||||
│ ├── DECISIONS.md # ADRs — why things are configured the way they are
|
||||
│ ├── RUNBOOKS.md # break-fix procedures
|
||||
│ ├── SECURITY.md # open ports, update cadence, known debt
|
||||
│ └── setup/ # one-time deploy guides (archived, read-only)
|
||||
├── configs/
|
||||
│ ├── caddy/ # Caddyfile + compose
|
||||
│ ├── pfsense/ # config.xml exports
|
||||
│ ├── pihole/ # teleporter backups
|
||||
│ └── lxc/ # per-container configs
|
||||
└── scripts/ # automation, health checks
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Contact
|
||||
|
||||
|
||||
\
|
||||
 
|
||||
|
||||
|
||||
\
|
||||
> *This lab is actively maintained and evolving. Documentation lives alongside the infrastructure.*
|
||||
19
docs/style-reference.md
Normal file
19
docs/style-reference.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Design this UI in the style of a modern developer-focused changelog/content feed. Use the following style principles:
|
||||
Layout & Structure
|
||||
Use a single-column, left-aligned content feed with generous vertical whitespace between entries. Group items under clear date/month headers that act as visual anchors. Keep the overall page width constrained (max ~800–900px) and centered, so the content feels focused and readable rather than sprawling.
|
||||
Typography
|
||||
Use a clean sans-serif system font stack. Headings should be bold and concise — favor short, scannable titles over long descriptive ones. Date labels and category tags should be small, muted, and secondary. Use size contrast (not just weight) to establish hierarchy: large section headers → medium entry titles → small metadata.
|
||||
Color & Theme
|
||||
Support both light and dark modes natively. Use a near-black/off-white background with high-contrast body text. Accent colors should be minimal and purposeful — one primary action color (e.g., a muted blue or purple) for links and interactive elements. Avoid decorative gradients; let whitespace and typography do the visual work.
|
||||
Entry/Card Pattern
|
||||
Each entry is a flat, borderless row — no card shadows or heavy containers. Include: a date label (left-aligned, muted), an entry type badge (e.g., "Release", "Improvement", "Retired") as a small pill/tag with a corresponding icon, a bold clickable title, and one or more category filter tags rendered as small inline chips with subtle background tints.
|
||||
Tags & Filtering
|
||||
Render category tags as small rounded pill buttons with very low-contrast background fills (e.g., light gray or transparent with a border). On a filter/sidebar, use checkboxes or toggle-style selectors. Show an active filter count as a badge. Include a visible "Clear all" affordance.
|
||||
Navigation & Header
|
||||
Minimal top navigation bar: site logo/name on the left, a small set of text links in the center or right, and one prominent CTA button (e.g., "Try X"). The header should be sticky or at minimum feel anchored. Below the page title, include secondary utility links (RSS, social follow) at small size.
|
||||
Interaction & Hover States
|
||||
Links and entry titles should have subtle underline-on-hover behavior. Tags/chips should lighten or highlight on hover. No heavy animations — transitions should be under 150ms and feel instantaneous.
|
||||
Footer
|
||||
Multi-column link footer with section headings (Product, Platform, Support, Company). Use small, muted text. Social icons should be monochrome and sized consistently. Include legal links (Terms, Privacy) as inline text at the very bottom.
|
||||
Spacing System
|
||||
Use an 8px base grid. Section gaps should be 48–64px. Entry gaps should be 24–32px. Tag/chip padding: 4px vertical, 10px horizontal. Comfortable line-height (1.5–1.6) throughout.
|
||||
Reference in New Issue
Block a user