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 */}
+
+
+
+
+
+
+
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}
+
+ ))}
+
+
+ ))}
+
)}