feat: add architecture diagrams page with archify visualizations
CI / CI (pull_request) Failing after 1m46s
CI / CI (pull_request) Failing after 1m46s
- Create /diagrams page with interactive homelab architecture visualizations - Add deepDive link to homelab project pointing to diagrams - Include 4 archify diagrams: * Talos cluster topology (control planes, GPU worker, storage) * GitOps workflow (ArgoCD + Terraform) * LLM inference stack (vLLM, Ollama, TEI routing) * API request flow sequence diagram - Support full-screen viewer and embedded grid layout - Dark/light theme support with responsive design
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft, Clock, Zap, BookOpen } from 'lucide-react'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const sections = [
|
||||
{
|
||||
chapter: 'Chapter 1',
|
||||
title: 'AWS Step Functions: The Foundation',
|
||||
period: '2022–2024',
|
||||
icon: '🏗️',
|
||||
highlights: [
|
||||
'Owned Distributed-Map end-to-end: design → production across 57+ regions',
|
||||
'Sub-100ms P99 latency, 20x burst handling',
|
||||
'Learned deployment alignment: frontend spec updates must sync with service deployments',
|
||||
'Built Checkpoint recovery: customers resume mid-workflow without re-runs',
|
||||
'Solved distributed edge cases: race conditions, concurrent updates, dedup',
|
||||
'Oncall mastery: CloudWatch dashboards, runbooks, production debugging'
|
||||
],
|
||||
keyLearning: 'Backward compatibility is invisible until it breaks silently. A missed spec change cascades across regions.'
|
||||
},
|
||||
{
|
||||
chapter: 'Chapter 2',
|
||||
title: 'RBC: Infrastructure as Code & Observability',
|
||||
period: 'Nov 2024 – May 2025',
|
||||
icon: '⚡',
|
||||
highlights: [
|
||||
'Problem: Unstable deployments with 500+ resource state files, API rate-limits, race conditions',
|
||||
'Solution: JFrog Artifactory backend + workspace prefixes, plan artifacts, -parallelism=5 throttle',
|
||||
'Result: Zero state corruption, zero pipeline blockage',
|
||||
'Problem: Configuration drift invisible for weeks, massive unreadable diffs',
|
||||
'Solution: Nightly terraform plan -refresh-only -detailed-exitcode → Slack webhook',
|
||||
'Result: Drift visibility from 3 weeks → <24 hours'
|
||||
],
|
||||
keyLearning: 'Terraform wins for things that barely change. For K8s resources that churn (pods, configmaps), you need a different tool—GitOps.'
|
||||
},
|
||||
{
|
||||
chapter: 'Chapter 3',
|
||||
title: 'Homelab: Building AWS from Scratch',
|
||||
period: 'May 2025 – Present',
|
||||
icon: '🚀',
|
||||
highlights: [
|
||||
'Question: How does LLM serving work at scale? Build it at home.',
|
||||
'4 bare-metal machines: 1 GPU node, 1 Dell PowerEdge, 2 mini-desktops',
|
||||
'Discovery: Powerline adapters killed etcd consensus. Ran ethernet to garage.',
|
||||
'Wildcard DNS via Cloudflare, HTTPS for all subdomains (cert-manager + Let\'s Encrypt)',
|
||||
'GitOps split: Terraform for Talos machine config, ArgoCD for K8s resources',
|
||||
'99.2% uptime with Talos Linux, Cilium CNI, Longhorn, PostgreSQL HA, Prometheus+Grafana+Loki'
|
||||
],
|
||||
keyLearning: 'Design infrastructure so AI can modify it easily. GitOps + immutable OS = no surprises.'
|
||||
},
|
||||
{
|
||||
chapter: 'Chapter 4',
|
||||
title: 'Poimen: AI Meets Workflows',
|
||||
period: 'Building',
|
||||
icon: '🤖',
|
||||
highlights: [
|
||||
'Insight: AI is powerful because of context, tool-calling, and memory.',
|
||||
'poimen-memory: Graph-RAG with wiki-link indexing, pgvector + OpenSearch hybrid search',
|
||||
'poimen-workflows: Natural language → executable Temporal workflows via reasoning model',
|
||||
'Activity Knowledge Base informs LLM about timeouts, retries, dependencies',
|
||||
'Generic state machine: JSON workflow spec + JSONPath parameter chaining',
|
||||
'60% latency reduction for LLM serving (vLLM INT4 quantization, custom Go gateway)'
|
||||
],
|
||||
keyLearning: 'Skill factory: each Temporal activity is independently refined. General-purpose workflow orchestration emerges.'
|
||||
}
|
||||
]
|
||||
|
||||
const technologies = {
|
||||
'Infrastructure': ['Talos Linux', 'Kubernetes', 'Cilium CNI', 'Terraform', 'kustomize'],
|
||||
'Data': ['PostgreSQL + HA', 'pgvector', 'Kafka/Redpanda', 'MinIO', 'Longhorn'],
|
||||
'GitOps': ['ArgoCD', 'Forgejo', 'Docker-in-Docker'],
|
||||
'Identity': ['Authentik OIDC', 'RBAC', 'SOPS encrypted secrets', 'cert-manager'],
|
||||
'Observability': ['Prometheus', 'Grafana', 'Loki', 'Tempo', 'OpenTelemetry'],
|
||||
'AI/ML': ['vLLM', 'Ollama', 'TEI', 'KServe', 'Temporal', 'Qwen3-32B'],
|
||||
}
|
||||
|
||||
const keyInsights = [
|
||||
{
|
||||
title: 'Deployment Alignment',
|
||||
description: 'When frontend consumes the latest API, you need backward-compat checks. A missed spec change breaks customers silently across all regions.',
|
||||
},
|
||||
{
|
||||
title: 'Infrastructure as Code Split',
|
||||
description: 'Terraform for things that rarely change (machine config). GitOps for things that churn (pods, configmaps). Different tools, different philosophies.',
|
||||
},
|
||||
{
|
||||
title: 'Network is Critical',
|
||||
description: 'Powerline adapters killed etcd consensus at 200ms latency. Ethernet cable to garage solved it. Hardware matters.',
|
||||
},
|
||||
{
|
||||
title: 'AI-Friendly Infra',
|
||||
description: 'Design systems so AI can modify them. Declarative configs + clear abstractions = easier for models to reason about.',
|
||||
},
|
||||
{
|
||||
title: 'Observability First',
|
||||
description: 'Drift detection <24hr. Prometheus + Grafana dashboards before you have problems. Not post-mortem tools.',
|
||||
},
|
||||
]
|
||||
|
||||
export default function HomelabArticle() {
|
||||
return (
|
||||
<main className="min-h-screen bg-white dark:bg-gray-950">
|
||||
{/* Hero */}
|
||||
<section className="bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-900 border-b border-gray-200 dark:border-gray-800">
|
||||
<div className="max-w-3xl mx-auto px-6 py-16">
|
||||
<Link
|
||||
href="/homelab"
|
||||
className="inline-flex items-center gap-2 text-blue-600 dark:text-blue-400 hover:underline mb-8"
|
||||
>
|
||||
<ArrowLeft size={16} />
|
||||
Back to Homelab
|
||||
</Link>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h1 className="text-5xl font-bold text-gray-900 dark:text-white mb-4 leading-tight">
|
||||
Building AWS at Home:<br />A 3-Year Journey
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-400 mb-6">
|
||||
From Step Functions to LLM inference—how distributed systems knowledge compounds.
|
||||
</p>
|
||||
<div className="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock size={16} />
|
||||
<span>12 min read</span>
|
||||
</div>
|
||||
<span>•</span>
|
||||
<span>May 2025</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content */}
|
||||
<article className="max-w-3xl mx-auto px-6 py-20">
|
||||
{/* Intro */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 prose prose-lg dark:prose-invert max-w-none"
|
||||
>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed mb-4">
|
||||
There's a difference between knowing how systems work in theory and building them in production. I've spent the last 3 years learning this difference the hard way—first at AWS, then at RBC, and now at home.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
This is the story of how I learned distributed systems by owning every layer: from workflow orchestration to hardware networking, from GitOps to AI agents.
|
||||
</p>
|
||||
</motion.section>
|
||||
|
||||
{/* Chapters */}
|
||||
<div className="space-y-24">
|
||||
{sections.map((section, idx) => (
|
||||
<motion.section
|
||||
key={section.chapter}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: idx * 0.1 }}
|
||||
className="border-l-4 border-blue-500 pl-8"
|
||||
>
|
||||
<div className="flex items-baseline gap-3 mb-4">
|
||||
<span className="text-4xl">{section.icon}</span>
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-blue-600 dark:text-blue-400 uppercase tracking-wide">
|
||||
{section.chapter}
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
{section.title}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400 mb-6 font-medium">
|
||||
{section.period}
|
||||
</div>
|
||||
|
||||
{/* Highlights */}
|
||||
<ul className="space-y-3 mb-8">
|
||||
{section.highlights.map((highlight, i) => (
|
||||
<li key={i} className="flex gap-3 text-gray-700 dark:text-gray-300">
|
||||
<span className="text-blue-500 font-bold mt-0.5 flex-shrink-0">•</span>
|
||||
<span>{highlight}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Key Learning */}
|
||||
<div className="bg-blue-50 dark:bg-blue-900/30 border-l-4 border-blue-500 p-4 rounded-r">
|
||||
<div className="font-semibold text-blue-900 dark:text-blue-200 mb-1">Key Learning</div>
|
||||
<p className="text-blue-800 dark:text-blue-100 text-sm">
|
||||
{section.keyLearning}
|
||||
</p>
|
||||
</div>
|
||||
</motion.section>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Key Insights */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mt-24 pt-16 border-t border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-8">
|
||||
<Zap size={24} className="text-yellow-500" />
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Core Insights
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{keyInsights.map((insight, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: idx * 0.05 }}
|
||||
className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<h4 className="font-bold text-gray-900 dark:text-white mb-2">
|
||||
{insight.title}
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{insight.description}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mt-24 pt-16 border-t border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-8">
|
||||
<BookOpen size={24} className="text-purple-500" />
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Technologies Mastered
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{Object.entries(technologies).map(([category, techs]) => (
|
||||
<motion.div key={category}
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h4 className="font-bold text-gray-900 dark:text-white mb-3">
|
||||
{category}
|
||||
</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{techs.map((tech) => (
|
||||
<span
|
||||
key={tech}
|
||||
className="text-xs px-3 py-1.5 rounded-full bg-gradient-to-r from-blue-100 to-purple-100 dark:from-blue-900 dark:to-purple-900 text-gray-800 dark:text-gray-200 font-medium"
|
||||
>
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* CTA */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mt-24 pt-16 border-t border-gray-200 dark:border-gray-800 text-center"
|
||||
>
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Want the full details?
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Ask Poimen any technical question about the architecture, deployment strategies, or lessons learned.
|
||||
</p>
|
||||
<Link
|
||||
href="/#project-homelab"
|
||||
className="inline-block bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg font-semibold transition"
|
||||
>
|
||||
Ask Poimen →
|
||||
</Link>
|
||||
</motion.section>
|
||||
</article>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
+259
-159
@@ -2,6 +2,7 @@
|
||||
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const skillCategories = [
|
||||
{
|
||||
@@ -46,18 +47,10 @@ const skillCategories = [
|
||||
},
|
||||
]
|
||||
|
||||
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 (
|
||||
<main className="min-h-screen bg-white dark:bg-gray-950">
|
||||
<div className="max-w-4xl mx-auto px-6 py-20">
|
||||
<div className="max-w-6xl mx-auto px-6 py-20">
|
||||
<Link
|
||||
href="/#project-homelab"
|
||||
className="inline-flex items-center gap-2 text-blue-600 dark:text-blue-400 hover:underline mb-8"
|
||||
@@ -66,178 +59,285 @@ export default function HomelabPage() {
|
||||
Back to Projects
|
||||
</Link>
|
||||
|
||||
<h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Homelab: Self-Hosted Cloud Platform
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-400 mb-12">
|
||||
AWS rebuilt from scratch at home—full stack from compute to observability.
|
||||
</p>
|
||||
{/* Hero */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-16"
|
||||
>
|
||||
<h1 className="text-5xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Building AWS at Home
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-400 mb-6">
|
||||
A 3-year journey through distributed systems, from Step Functions to LLM inference.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
There's a difference between knowing how systems work in theory and building them in production. I've spent the last 3 years learning this difference the hard way—first at AWS, then at RBC, and now at home. This is the story of how I learned distributed systems by owning every layer: from workflow orchestration to hardware networking, from GitOps to AI agents.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Stats */}
|
||||
<section className="bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 rounded-lg p-6 mb-12">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-center">
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-blue-600 dark:text-blue-400">4</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">Nodes</div>
|
||||
{/* Chapter 1: Homelab */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 border-l-4 border-purple-500 pl-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<div className="text-sm font-semibold text-purple-600 dark:text-purple-400 uppercase tracking-wide mb-2">
|
||||
The Journey
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-blue-600 dark:text-blue-400">20+</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">Services</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Homelab: Building AWS from Scratch
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 font-medium">May 2025 – Present</p>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
The question: <strong>How does LLM serving work at scale?</strong> The only way to answer that was to build an entire cloud-like platform with SaaS fundamentals from scratch. 4 bare-metal machines, 1 GPU node, 1 Dell PowerEdge (now in the garage due to noise), and 2 mini-desktops. All running Talos Linux, a Kubernetes-native OS designed for immutability.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Infrastructure section + diagram */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Infrastructure Layer
|
||||
</h3>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800 mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
||||
4-node bare-metal cluster (3 control plane + 1 worker) running Talos Linux—immutable, API-driven OS designed for Kubernetes. All wired ethernet to avoid etcd consensus issues.
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>Compute:</strong> Talos Linux nodes, machine config via Terraform</li>
|
||||
<li>• <strong>Networking:</strong> Cilium CNI, nginx ingress, Cloudflare Tunnel for zero-trust external access</li>
|
||||
<li>• <strong>Storage:</strong> Longhorn for distributed block storage with disk tagging, MinIO for S3</li>
|
||||
<li>• <strong>GitOps:</strong> ArgoCD with multi-source Applications and sync waves</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-blue-600 dark:text-blue-400">99.2%</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">Uptime</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-blue-600 dark:text-blue-400">60%</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">LLM Latency Cut</div>
|
||||
|
||||
{/* Cluster Topology Diagram */}
|
||||
<div className="mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow">
|
||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Talos Cluster Topology</h4>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">Control planes, worker GPU, storage, and networking architecture</p>
|
||||
</div>
|
||||
<div className="w-full" style={{ height: '700px' }}>
|
||||
<iframe
|
||||
src="/diagrams/homelab-cluster.html"
|
||||
className="w-full h-full border-none"
|
||||
title="Talos Cluster Topology"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* GitOps section + diagram */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
GitOps Evolution
|
||||
</h3>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800 mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
||||
Started with Terraform for everything—but constant reconciliation of pods and configmaps created chaos. Brought in ArgoCD and established a clear split:
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>Terraform:</strong> Talos machine config (barely changes, no drift)</li>
|
||||
<li>• <strong>ArgoCD:</strong> CRD-driven observer pattern for K8s resources (changes sync automatically)</li>
|
||||
<li>• <strong>Design principle:</strong> Infrastructure should be easy for AI to modify. Declarative config + clear abstractions = easier for models to reason about</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* GitOps Workflow Diagram */}
|
||||
<div className="mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow">
|
||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">GitOps Deployment Flow</h4>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">ArgoCD for Kubernetes and Terraform for Talos infrastructure</p>
|
||||
</div>
|
||||
<div className="w-full" style={{ height: '700px' }}>
|
||||
<iframe
|
||||
src="/diagrams/homelab-gitops.html"
|
||||
className="w-full h-full border-none"
|
||||
title="GitOps Workflow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI/ML Platform */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
AI/ML Platform
|
||||
</h3>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800 mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
||||
Self-hosted LLM inference with multi-tenant GPU scheduling and 60% latency reduction.
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>Inference:</strong> vLLM serving Qwen3-32B with INT4 quantization</li>
|
||||
<li>• <strong>Multi-model:</strong> Ollama for smaller models, hot-swapping</li>
|
||||
<li>• <strong>Embeddings:</strong> TEI for text embeddings and reranking</li>
|
||||
<li>• <strong>Orchestration:</strong> KServe + custom Go API gateway</li>
|
||||
<li>• <strong>Scheduling:</strong> GPU node affinity with sm70/Volta constraints</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* LLM Stack Diagram */}
|
||||
<div className="mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow">
|
||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">LLM Inference Architecture</h4>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">Gateway routing to vLLM, Ollama, and TEI on GPU worker node</p>
|
||||
</div>
|
||||
<div className="w-full" style={{ height: '700px' }}>
|
||||
<iframe
|
||||
src="/diagrams/homelab-llm-stack.html"
|
||||
className="w-full h-full border-none"
|
||||
title="LLM Inference Stack"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Request Lifecycle Diagram */}
|
||||
<div className="mb-6 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden hover:shadow-lg transition-shadow">
|
||||
<div className="p-4 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-800 border-b border-gray-200 dark:border-gray-800">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Request Lifecycle</h4>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">Client → nginx → gateway → predictor → streaming response</p>
|
||||
</div>
|
||||
<div className="w-full" style={{ height: '700px' }}>
|
||||
<iframe
|
||||
src="/diagrams/homelab-api-gateway-sequence.html"
|
||||
className="w-full h-full border-none"
|
||||
title="API Request Flow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data & Security */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Data Platform
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>PostgreSQL:</strong> CloudNativePG operator with HA, automated failover</li>
|
||||
<li>• <strong>Vector DB:</strong> pgvector extension for AI embeddings</li>
|
||||
<li>• <strong>Streaming:</strong> Kafka/Redpanda for event-driven architecture</li>
|
||||
<li>• <strong>Workflows:</strong> Temporal for durable, long-running processes</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Identity & Security
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>SSO:</strong> Authentik OIDC provider with custom claims</li>
|
||||
<li>• <strong>RBAC:</strong> Kubernetes RBAC synced with Authentik groups</li>
|
||||
<li>• <strong>Secrets:</strong> SOPS-encrypted secrets in git, decrypted at deploy time</li>
|
||||
<li>• <strong>TLS:</strong> cert-manager with DNS-01 ACME via Cloudflare</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-purple-50 dark:bg-purple-900/30 border-l-4 border-purple-500 p-4 rounded-r">
|
||||
<div className="font-semibold text-purple-900 dark:text-purple-200 mb-1">Key Learnings</div>
|
||||
<ul className="text-purple-800 dark:text-purple-100 text-sm space-y-1">
|
||||
<li>• Powerline adapters killed etcd consensus at 200ms latency. Ethernet to garage solved it.</li>
|
||||
<li>• Network is foundational—hardware matters more than code sometimes.</li>
|
||||
<li>• Design infrastructure so AI can modify it easily. Declarative + immutable = no surprises.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Chapter 4 */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 border-l-4 border-yellow-500 pl-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<div className="text-sm font-semibold text-yellow-600 dark:text-yellow-400 uppercase tracking-wide mb-2">
|
||||
Next: The AI System
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Poimen: The AI Agent System
|
||||
</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 font-medium">Building</p>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Current LLM architecture works seamlessly with lambda/serverless patterns. The key insight: <strong>if AI is powerful because of context, tool-calling, and memory, what if we provide the right context in the right environment?</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-3 text-gray-700 dark:text-gray-300 mb-6">
|
||||
<li className="flex gap-3">
|
||||
<span className="text-yellow-500 font-bold flex-shrink-0">•</span>
|
||||
<span><strong>poimen-memory:</strong> Graph-RAG with wiki-link indexing, 7B model for instruct QA, pgvector + OpenSearch hybrid search</span>
|
||||
</li>
|
||||
<li className="flex gap-3">
|
||||
<span className="text-yellow-500 font-bold flex-shrink-0">•</span>
|
||||
<span><strong>poimen-workflows:</strong> Natural language → executable WorkflowSpec via reasoning model, Temporal for durable execution</span>
|
||||
</li>
|
||||
<li className="flex gap-3">
|
||||
<span className="text-yellow-500 font-bold flex-shrink-0">•</span>
|
||||
<span><strong>Activity Knowledge Base:</strong> Informs LLM about timeouts, retry policies, dependencies before routing</span>
|
||||
</li>
|
||||
<li className="flex gap-3">
|
||||
<span className="text-yellow-500 font-bold flex-shrink-0">•</span>
|
||||
<span><strong>Generic state machine:</strong> JSON workflow spec + JSONPath parameter chaining. Temporal enables redrive + free self-hosted</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/30 border-l-4 border-yellow-500 p-4 rounded-r">
|
||||
<div className="font-semibold text-yellow-900 dark:text-yellow-200 mb-1">The Vision</div>
|
||||
<p className="text-yellow-800 dark:text-yellow-100 text-sm">
|
||||
Build a skill factory where a model generates workflows and completes them. Each Temporal activity is independently refined—enabling a general-purpose workflow orchestrator that improves through iteration.
|
||||
</p>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Skills Grid */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">
|
||||
Technologies & Skills
|
||||
<motion.section
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="mb-12 mt-24 pt-16 border-t border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-12">
|
||||
Technologies & Skills Mastered
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{skillCategories.map((category) => (
|
||||
<div
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{skillCategories.map((category, idx) => (
|
||||
<motion.div
|
||||
key={category.title}
|
||||
className="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 border border-gray-200 dark:border-gray-800"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: idx * 0.05 }}
|
||||
className="bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-800 rounded-lg p-8 border border-gray-200 dark:border-gray-800 hover:shadow-lg transition-shadow h-full"
|
||||
>
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-2">
|
||||
<h3 className="font-bold text-gray-900 dark:text-white mb-6 text-xl">
|
||||
{category.title}
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{category.skills.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
className="text-xs px-2 py-1 rounded bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200"
|
||||
className="text-base px-4 py-2 rounded-full bg-white dark:bg-gray-700 text-blue-700 dark:text-blue-300 font-semibold border border-blue-200 dark:border-blue-600 hover:border-blue-400 dark:hover:border-blue-400 transition-colors"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Core Competencies */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Core Competencies Demonstrated
|
||||
</h2>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<ul className="space-y-2">
|
||||
{coreCompetencies.map((item) => (
|
||||
<li key={item} className="flex items-start gap-2 text-gray-700 dark:text-gray-300">
|
||||
<span className="text-blue-500 mt-1">•</span>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Architecture Sections */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">
|
||||
Architecture Deep Dive
|
||||
</h2>
|
||||
|
||||
{/* Infrastructure */}
|
||||
<div className="mb-6 bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Infrastructure Layer
|
||||
</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-3">
|
||||
4-node bare-metal cluster (3 control plane + 1 worker) running Talos Linux—immutable, API-driven OS designed for Kubernetes.
|
||||
</p>
|
||||
<ul className="space-y-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>Compute:</strong> Talos Linux nodes, machine config via Terraform</li>
|
||||
<li>• <strong>Networking:</strong> Cilium CNI, nginx ingress, Cloudflare Tunnel for zero-trust external access</li>
|
||||
<li>• <strong>Storage:</strong> Longhorn for distributed block storage with disk tagging, MinIO for S3</li>
|
||||
<li>• <strong>GitOps:</strong> ArgoCD with multi-source Applications and sync waves</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Data Platform */}
|
||||
<div className="mb-6 bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Data Platform
|
||||
</h3>
|
||||
<ul className="space-y-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>PostgreSQL:</strong> CloudNativePG operator with HA, automated failover</li>
|
||||
<li>• <strong>Vector DB:</strong> pgvector extension for AI embeddings</li>
|
||||
<li>• <strong>Streaming:</strong> Kafka/Redpanda for event-driven architecture</li>
|
||||
<li>• <strong>Workflows:</strong> Temporal for durable, long-running processes</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* AI/ML */}
|
||||
<div className="mb-6 bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
AI/ML Platform
|
||||
</h3>
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-3">
|
||||
Self-hosted LLM inference with multi-tenant GPU scheduling and 60% latency reduction.
|
||||
</p>
|
||||
<ul className="space-y-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>Inference:</strong> vLLM serving Qwen3-32B with INT4 quantization</li>
|
||||
<li>• <strong>Multi-model:</strong> Ollama for smaller models, hot-swapping</li>
|
||||
<li>• <strong>Embeddings:</strong> TEI for text embeddings and reranking</li>
|
||||
<li>• <strong>Orchestration:</strong> KServe + custom Go API gateway</li>
|
||||
<li>• <strong>Scheduling:</strong> GPU node affinity with sm70/Volta constraints</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Security */}
|
||||
<div className="mb-6 bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Identity & Security
|
||||
</h3>
|
||||
<ul className="space-y-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>SSO:</strong> Authentik OIDC provider with custom claims and group mapping</li>
|
||||
<li>• <strong>RBAC:</strong> Kubernetes RBAC synced with Authentik groups</li>
|
||||
<li>• <strong>Secrets:</strong> SOPS-encrypted secrets in git, decrypted at deploy time</li>
|
||||
<li>• <strong>TLS:</strong> cert-manager with DNS-01 ACME challenges via Cloudflare</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Observability */}
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Observability Stack
|
||||
</h3>
|
||||
<ul className="space-y-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
<li>• <strong>Metrics:</strong> Prometheus with custom recording rules, Grafana dashboards</li>
|
||||
<li>• <strong>Logs:</strong> Loki for aggregation, structured logging from all services</li>
|
||||
<li>• <strong>Traces:</strong> Tempo + OpenTelemetry Collector for distributed tracing</li>
|
||||
<li>• <strong>Alerts:</strong> AlertManager → Slack integration for incident response</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Applications */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Self-Hosted Applications
|
||||
</h2>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 border border-gray-200 dark:border-gray-800">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{['Paperless-ngx', 'Immich', 'Homarr', 'Portainer', 'Forgejo', 'Authentik'].map((app) => (
|
||||
<span
|
||||
key={app}
|
||||
className="text-sm px-3 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200"
|
||||
>
|
||||
{app}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</motion.section>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user