From e8dbc2100ceca9bbf1deb51ea1e72f681dfbf93e Mon Sep 17 00:00:00 2001 From: Story Crater Bot <19826264+Riotpiaole@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:39:34 -0700 Subject: [PATCH] feat: consolidate homelab projects, add skills dropdown, update timeline animations - Consolidate 3 homelab projects into single 'Self-Hosted Cloud' card - Add /homelab deep dive page with full tech stack - Update to 4 nodes (3 control plane + 1 worker) - Add organized skills dropdown (6 categories) - Update timeline skills for riotpiao.com experience - Add 'Learn more' links for all major experiences - Change timeline animation to scroll-reveal (y-axis) --- app/homelab/page.tsx | 244 ++++++++++++++++++++++++++++++ app/page.tsx | 24 +-- components/ExperienceTimeline.tsx | 35 ++--- components/Header.tsx | 34 ++++- lib/translations.json | 126 ++++++++++----- 5 files changed, 388 insertions(+), 75 deletions(-) create mode 100644 app/homelab/page.tsx diff --git a/app/homelab/page.tsx b/app/homelab/page.tsx new file mode 100644 index 0000000..e222939 --- /dev/null +++ b/app/homelab/page.tsx @@ -0,0 +1,244 @@ +'use client' + +import Link from 'next/link' +import { ArrowLeft } from 'lucide-react' + +const skillCategories = [ + { + title: 'Infrastructure & Orchestration', + skills: ['Kubernetes (Talos Linux)', 'ArgoCD (GitOps)', 'Terraform (IaC)', 'Kustomize'], + }, + { + title: 'Networking & Ingress', + skills: ['nginx Ingress Controller', 'Cloudflare Tunnel', 'CoreDNS', 'NetworkPolicy'], + }, + { + title: 'Storage', + skills: ['Longhorn (distributed block)', 'MinIO (S3-compatible)'], + }, + { + title: 'Databases', + skills: ['CloudNativePG (PostgreSQL)', 'pgvector (AI embeddings)'], + }, + { + title: 'Identity & Security', + skills: ['Authentik (OIDC SSO)', 'SOPS (encrypted secrets)', 'cert-manager (TLS)'], + }, + { + title: 'Observability', + skills: ['Prometheus', 'Grafana', 'Tempo (tracing)', 'OpenTelemetry', 'Loki (logs)'], + }, + { + title: 'CI/CD', + skills: ['Forgejo (git + Actions)', 'Container Registry', 'DinD Runners'], + }, + { + title: 'AI/ML Platform', + skills: ['vLLM (Qwen3-32B)', 'Ollama', 'TEI (embeddings)', 'KServe'], + }, + { + title: 'Workflow & Messaging', + skills: ['Temporal (durable workflows)', 'Kafka/Redpanda (streaming)'], + }, + { + title: 'Languages & Frameworks', + skills: ['Go (API gateway)', 'Python (ML)', 'Next.js (frontend)'], + }, +] + +const coreCompetencies = [ + 'Multi-tenant GPU scheduling (sm70/Volta constraints)', + 'Zero-downtime GitOps deployments', + 'Service mesh patterns without Istio overhead', + 'Hybrid cloud networking (Cloudflare + bare-metal)', + 'Declarative IAM with OIDC claim mapping', +] + +export default function HomelabPage() { + return ( +
+
+ + + Back to Projects + + +

+ Homelab: Self-Hosted Cloud Platform +

+

+ AWS rebuilt from scratch at home—full stack from compute to observability. +

+ + {/* Stats */} +
+
+
+
4
+
Nodes
+
+
+
20+
+
Services
+
+
+
99.2%
+
Uptime
+
+
+
60%
+
LLM Latency Cut
+
+
+
+ + {/* Skills Grid */} +
+

+ Technologies & Skills +

+
+ {skillCategories.map((category) => ( +
+

+ {category.title} +

+
+ {category.skills.map((skill) => ( + + {skill} + + ))} +
+
+ ))} +
+
+ + {/* Core Competencies */} +
+

+ Core Competencies Demonstrated +

+
+
    + {coreCompetencies.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+
+ + {/* Architecture Sections */} +
+

+ Architecture Deep Dive +

+ + {/* Infrastructure */} +
+

+ Infrastructure Layer +

+

+ 4-node bare-metal cluster (3 control plane + 1 worker) running Talos Linux—immutable, API-driven OS designed for Kubernetes. +

+
    +
  • Compute: Talos Linux nodes, machine config via Terraform
  • +
  • Networking: Cilium CNI, nginx ingress, Cloudflare Tunnel for zero-trust external access
  • +
  • Storage: Longhorn for distributed block storage with disk tagging, MinIO for S3
  • +
  • GitOps: ArgoCD with multi-source Applications and sync waves
  • +
+
+ + {/* Data Platform */} +
+

+ Data Platform +

+
    +
  • PostgreSQL: CloudNativePG operator with HA, automated failover
  • +
  • Vector DB: pgvector extension for AI embeddings
  • +
  • Streaming: Kafka/Redpanda for event-driven architecture
  • +
  • Workflows: Temporal for durable, long-running processes
  • +
+
+ + {/* AI/ML */} +
+

+ AI/ML Platform +

+

+ Self-hosted LLM inference with multi-tenant GPU scheduling and 60% latency reduction. +

+
    +
  • Inference: vLLM serving Qwen3-32B with INT4 quantization
  • +
  • Multi-model: Ollama for smaller models, hot-swapping
  • +
  • Embeddings: TEI for text embeddings and reranking
  • +
  • Orchestration: KServe + custom Go API gateway
  • +
  • Scheduling: GPU node affinity with sm70/Volta constraints
  • +
+
+ + {/* Security */} +
+

+ Identity & Security +

+
    +
  • SSO: Authentik OIDC provider with custom claims and group mapping
  • +
  • RBAC: Kubernetes RBAC synced with Authentik groups
  • +
  • Secrets: SOPS-encrypted secrets in git, decrypted at deploy time
  • +
  • TLS: cert-manager with DNS-01 ACME challenges via Cloudflare
  • +
+
+ + {/* Observability */} +
+

+ Observability Stack +

+
    +
  • Metrics: Prometheus with custom recording rules, Grafana dashboards
  • +
  • Logs: Loki for aggregation, structured logging from all services
  • +
  • Traces: Tempo + OpenTelemetry Collector for distributed tracing
  • +
  • Alerts: AlertManager → Slack integration for incident response
  • +
+
+
+ + {/* Applications */} +
+

+ Self-Hosted Applications +

+
+
+ {['Paperless-ngx', 'Immich', 'Homarr', 'Portainer', 'Forgejo', 'Authentik'].map((app) => ( + + {app} + + ))} +
+
+
+
+
+ ) +} diff --git a/app/page.tsx b/app/page.tsx index 3dbf499..6f6042e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -15,23 +15,25 @@ export default function Home() { const projects = t.projects.items.map((item, index) => ({ ...item, - id: index === 3 - ? 'project-rbc' - : index === 4 - ? 'project-aws' - : undefined, - status: index === 3 + id: index === 0 + ? 'project-homelab' + : index === 1 + ? 'project-rbc' + : index === 2 + ? 'project-aws' + : undefined, + status: index === 1 ? 'completed' as const - : index === 5 + : index === 3 ? 'building' as const : 'live' as const, - media: index === 3 + media: index === 1 ? { type: 'image' as const, url: '/rbc-images.jpeg' } - : index === 4 + : index === 2 ? { type: 'video' as const, url: 'https://www.youtube.com/watch?v=wdJ5DN15jus' } : undefined, - videoUrl: index === 5 ? 'https://github.com/rockliang/go-flink' : '#', - articleUrl: index === 4 ? 'https://lnkd.in/p/gm2PZkWw' : '#', + videoUrl: index === 3 ? 'https://github.com/rockliang/go-flink' : '#', + articleUrl: index === 2 ? 'https://lnkd.in/p/gm2PZkWw' : '#', })) return ( diff --git a/components/ExperienceTimeline.tsx b/components/ExperienceTimeline.tsx index e28a24b..2853799 100644 --- a/components/ExperienceTimeline.tsx +++ b/components/ExperienceTimeline.tsx @@ -12,7 +12,7 @@ const colors = [ ] const skills = [ - ['LangGraph', 'Temporal', 'LLM Ops', 'PyTorch', 'Kafka', 'Observability', 'Go-Flink'], + ['Kubernetes', 'Talos', 'ArgoCD', 'Terraform', 'Authentik', 'Prometheus', 'Grafana', 'vLLM', 'Temporal', 'Kafka', 'PostgreSQL', 'Go'], ['Golang', 'Terraform', 'IaC', 'OpenShift', 'Docker', 'Distributed Systems', 'Grafana'], ['Java', 'AWS', 'DynamoDB', 'CloudWatch', 'gRPC', 'Distributed Systems', 'Ownership', 'Disaster Recovery', 'Observability'], ['C++', 'CMake', 'Golang', 'Docker'], @@ -20,7 +20,7 @@ const skills = [ ] const links = [ - null, + '#project-homelab', // Homelab - link to homelab project showcase '#project-rbc', // RBC - link to RBC project showcase '#project-aws', // AWS - link to AWS project showcase null, @@ -28,29 +28,19 @@ const links = [ ] const linkTexts = [ - '', + 'Learn more →', 'Learn more →', 'Learn more →', '', 'View Product →', ] -const containerVariants = { - hidden: { opacity: 0 }, - visible: { - opacity: 1, - transition: { - staggerChildren: 0.2, - }, - }, -} - const itemVariants = { - hidden: { opacity: 0, x: -20 }, + hidden: { opacity: 0, y: 40 }, visible: { opacity: 1, - x: 0, - transition: { duration: 0.6, ease: 'easeOut' }, + y: 0, + transition: { duration: 0.5, ease: 'easeOut' }, }, } @@ -73,13 +63,7 @@ export function ExperienceTimeline() {

- +
{/* Timeline line */}
@@ -89,6 +73,9 @@ export function ExperienceTimeline() { {/* Timeline dot */} @@ -138,7 +125,7 @@ export function ExperienceTimeline() { ))}
- +
) } diff --git a/components/Header.tsx b/components/Header.tsx index d172eff..e6310e9 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -70,8 +70,21 @@ export default function Header() { {t.header.nav.skills} {skillsOpen && ( -
-
{t.header.skillsPlaceholder}
+
+
+ {Object.entries(t.header.skills).map(([key, category]) => ( +
+
{(category as {title: string}).title}
+
+ {(category as {items: string[]}).items.map((skill) => ( + + {skill} + + ))} +
+
+ ))} +
)}
@@ -234,8 +247,21 @@ export default function Header() { {t.header.nav.skills} {skillsOpen && ( -
-
{t.header.skillsPlaceholder}
+
+
+ {Object.entries(t.header.skills).map(([key, category]) => ( +
+
{(category as {title: string}).title}
+
+ {(category as {items: string[]}).items.map((skill) => ( + + {skill} + + ))} +
+
+ ))} +
)}