feat(content): reposition for security engineer, expand services to 37
Hero subtitle and blurb rewritten to lead with security operations and homelab credentials over generic "builder" framing. Projects: archive plAIground, service-monitor, ThoughtSpace (2025); add open-pact and helm as featured; add risk-ops to archive (2026). Add statusBadge + externalUrl to Project type; wire golf-book-mobile. Services: 24 → 37 — split grouped arr/media entries, add mail relay, gluetun, Home Assistant, Glance, Filebrowser, Prowlarr, Bazarr, nzbget, qBittorrent, Kavita, Openshelf. Drop Calibre-Web. Skills: add Go to Languages. Timeline: update monitoring stack. Homelab ADRs: add Authentik over Authelia.
This commit is contained in:
@@ -74,6 +74,11 @@ const adrs = [
|
||||
"act_runner v0.3.1 on Gitea LXC (10.99.0.22). Push to dev → node:22-alpine container builds Next.js → rsync out/ to Portfolio LXC → SSH docker rebuild.",
|
||||
why: "Keeps the full pipeline internal — no GitHub Actions, no external runners. Build runs in an isolated Alpine container so the Gitea LXC isn't polluted. Portfolio LXC (10.99.0.23) just serves pre-built static files via nginx.",
|
||||
},
|
||||
{
|
||||
title: "Authentik over Authelia",
|
||||
decision: "Authentik as the SSO provider across all self-hosted services.",
|
||||
why: "Full OIDC provider + forward auth in one. Lets services like Outline, Gitea, and Vikunja use real SSO rather than just a login gate. Authelia is forward-auth only — no OIDC provider capability.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function HomelabPage() {
|
||||
|
||||
@@ -8,15 +8,15 @@ export default function Hero() {
|
||||
tyler koenig
|
||||
</p>
|
||||
<p className="font-mono text-sm text-[var(--color-text-label)] mt-0.5">
|
||||
SOC Helpdesk I · Homelab Operator
|
||||
Security Operations · Self-Hosted Infrastructure
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="font-mono text-sm text-[var(--color-text)] leading-relaxed max-w-lg opacity-70">
|
||||
I write software and run infrastructure that goes well past what my
|
||||
job title implies. Games, AI tooling, mobile apps, and a homelab
|
||||
running 20+ self-hosted services on segmented VLANs. Continuously
|
||||
learning by building things that actually work.{' '}
|
||||
Security operations and self-hosted infrastructure. Homelab runs 37
|
||||
services across segmented VLANs — pfSense, Authentik SSO, full
|
||||
observability stack. Write software too: mobile apps, Go backends,
|
||||
open protocols. Daily drivers, all of it.{' '}
|
||||
<span className="animate-cursor text-[var(--color-accent-green)]" aria-hidden="true">█</span>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -22,6 +22,17 @@ export default function ProjectCard({ project }: Props) {
|
||||
{project.stats}
|
||||
</span>
|
||||
)}
|
||||
{project.externalUrl && (
|
||||
<a
|
||||
href={project.externalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={`View ${project.title} externally`}
|
||||
className="font-mono text-sm text-[var(--color-text-label)] hover:text-[var(--color-text)]"
|
||||
>
|
||||
↗
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
href={project.githubUrl}
|
||||
target="_blank"
|
||||
@@ -34,6 +45,12 @@ export default function ProjectCard({ project }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{project.statusBadge && (
|
||||
<span className="font-mono text-xs text-[var(--color-accent-amber,#d4a027)] border border-[var(--color-accent-amber,#d4a027)] px-1.5 py-0.5 w-fit opacity-80">
|
||||
{project.statusBadge}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<p className="font-mono text-sm text-[var(--color-text)] leading-relaxed flex-1 opacity-70">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
@@ -3,7 +3,7 @@ import Widget from "@/components/Widget";
|
||||
const skillGroups = [
|
||||
{
|
||||
label: "Languages",
|
||||
skills: ["JavaScript", "TypeScript", "HTML", "CSS"],
|
||||
skills: ["Go", "JavaScript", "TypeScript", "HTML", "CSS"],
|
||||
},
|
||||
{
|
||||
label: "Frontend",
|
||||
|
||||
@@ -7,6 +7,8 @@ export type Project = {
|
||||
tier: "featured" | "archive";
|
||||
stats?: string;
|
||||
year?: number;
|
||||
statusBadge?: string;
|
||||
externalUrl?: string;
|
||||
};
|
||||
|
||||
export const projects: Project[] = [
|
||||
@@ -20,6 +22,8 @@ export const projects: Project[] = [
|
||||
githubUrl: "https://github.com/lerko96/golf-book-mobile",
|
||||
tier: "featured",
|
||||
stats: "211 commits",
|
||||
statusBadge: "Pending App Store Approval",
|
||||
externalUrl: "#",
|
||||
},
|
||||
{
|
||||
slug: "plaiground",
|
||||
@@ -28,7 +32,8 @@ export const projects: Project[] = [
|
||||
"Cross-platform desktop AI chat app for developers. Supports OpenAI, Anthropic Claude, and Google Gemini in a single interface with real-time cost tracking, conversation export, and automatic code explanation.",
|
||||
tags: ["Electron", "Node.js", "OpenAI", "Claude", "Gemini"],
|
||||
githubUrl: "https://github.com/lerko96/plaiground",
|
||||
tier: "featured",
|
||||
tier: "archive",
|
||||
year: 2025,
|
||||
},
|
||||
{
|
||||
slug: "service-monitor",
|
||||
@@ -37,7 +42,8 @@ export const projects: Project[] = [
|
||||
"Web dashboard for tracking uptime across multiple services with 30-second polling, status history visualization, JWT-authenticated API, and Docker + nginx deployment.",
|
||||
tags: ["React 18", "Vite", "Express", "SQLite", "Docker", "JWT"],
|
||||
githubUrl: "https://github.com/lerko96/service-monitor",
|
||||
tier: "featured",
|
||||
tier: "archive",
|
||||
year: 2025,
|
||||
},
|
||||
{
|
||||
slug: "tht-1.2",
|
||||
@@ -46,10 +52,40 @@ export const projects: Project[] = [
|
||||
"3D visualization platform for exploring and organizing thoughts using a radio-tuning metaphor. Filter ideas by frequency and bandwidth in an instanced Three.js scene with persistent local storage.",
|
||||
tags: ["React", "TypeScript", "Three.js", "React Three Fiber", "Zustand"],
|
||||
githubUrl: "https://github.com/lerko96/tht-1.2",
|
||||
tier: "archive",
|
||||
year: 2025,
|
||||
},
|
||||
|
||||
{
|
||||
slug: "open-pact",
|
||||
title: "open-pact",
|
||||
description:
|
||||
"Open protocol for AI agent identity, delegation, and portable memory. Ed25519 keypair identity, signed delegation warrants, portable signed memory facts. No central registry.",
|
||||
tags: ["TypeScript", "Ed25519", "DID", "npm", "CC0"],
|
||||
githubUrl: "https://github.com/lerko96/open-pact",
|
||||
tier: "featured",
|
||||
},
|
||||
{
|
||||
slug: "helm",
|
||||
title: "helm",
|
||||
description:
|
||||
"Full-stack personal productivity dashboard. Go backend with chi router and SQLite, React + TypeScript frontend. Notes, todos, calendar (CalDAV), clipboard, bookmarks, memos. Self-hosted, single-user, daily use.",
|
||||
tags: ["Go", "React", "TypeScript", "SQLite", "CalDAV"],
|
||||
githubUrl: "https://github.com/lerko96/helm",
|
||||
tier: "featured",
|
||||
},
|
||||
|
||||
// --- Archive ---
|
||||
{
|
||||
slug: "risk-ops",
|
||||
title: "risk-ops",
|
||||
description:
|
||||
"Browser-based strategy dashboard for Risk: Global Domination (SMG Studio). Open one HTML file — no install needed.",
|
||||
tags: ["HTML", "JavaScript"],
|
||||
githubUrl: "#",
|
||||
tier: "archive",
|
||||
year: 2026,
|
||||
},
|
||||
{
|
||||
slug: "twitter-thread-ext",
|
||||
title: "twitter-thread-ext",
|
||||
|
||||
@@ -7,10 +7,13 @@ export type Service = {
|
||||
|
||||
export const services: Service[] = [
|
||||
// Infrastructure
|
||||
{ name: "pfSense", description: "Firewall, DHCP, routing, WireGuard VPN", category: "infrastructure", icon: "fas fa-shield-halved" },
|
||||
{ name: "pfSense", description: "Firewall, DHCP, routing gateway on N100", category: "infrastructure", icon: "fas fa-shield-halved" },
|
||||
{ name: "Caddy", description: "Reverse proxy with automatic wildcard TLS via Cloudflare DNS-01", category: "infrastructure", icon: "fas fa-globe" },
|
||||
{ name: "Pi-hole", description: "Network-wide DNS + ad blocking", category: "infrastructure", icon: "fas fa-filter" },
|
||||
{ name: "WireGuard", description: "VPN — 600–900 Mbps on N100, full LAN access for clients", category: "infrastructure", icon: "fas fa-lock" },
|
||||
{ name: "WireGuard", description: "VPN — full LAN access for remote clients", category: "infrastructure", icon: "fas fa-lock" },
|
||||
{ name: "mail relay", description: "Outbound SMTP relay for self-hosted service notifications", category: "infrastructure", icon: "fas fa-envelope" },
|
||||
{ name: "gluetun", description: "VPN container routing download client traffic", category: "infrastructure", icon: "fas fa-shield" },
|
||||
{ name: "Home Assistant", description: "Smart home automation and device management", category: "infrastructure", icon: "fas fa-house" },
|
||||
|
||||
// Security / Auth
|
||||
{ name: "Authentik", description: "SSO provider — OIDC + forward auth across all services", category: "security", icon: "fas fa-id-badge" },
|
||||
@@ -34,11 +37,21 @@ export const services: Service[] = [
|
||||
{ name: "Traggo", description: "Time tracking", category: "productivity", icon: "fas fa-clock" },
|
||||
{ name: "Baikal", description: "CalDAV / CardDAV server", category: "productivity", icon: "fas fa-calendar" },
|
||||
{ name: "Grist", description: "Spreadsheets and structured data", category: "productivity", icon: "fas fa-table" },
|
||||
{ name: "Glance", description: "Self-hosted start page with feeds and service status", category: "productivity", icon: "fas fa-gauge" },
|
||||
{ name: "Filebrowser", description: "Web-based file manager", category: "productivity", icon: "fas fa-folder-open" },
|
||||
|
||||
// Media
|
||||
{ name: "Plex + Jellyfin", description: "Media streaming", category: "media", icon: "fas fa-film" },
|
||||
{ name: "Sonarr / Radarr / Lidarr", description: "Automated media management", category: "media", icon: "fas fa-download" },
|
||||
{ name: "Calibre-Web", description: "Book library with auto-ingest", category: "media", icon: "fas fa-book-open" },
|
||||
{ name: "Plex", description: "Media streaming — movies, TV, music", category: "media", icon: "fas fa-film" },
|
||||
{ name: "Jellyfin", description: "Open-source media streaming", category: "media", icon: "fas fa-play" },
|
||||
{ name: "Sonarr", description: "Automated TV show management", category: "media", icon: "fas fa-tv" },
|
||||
{ name: "Radarr", description: "Automated movie management", category: "media", icon: "fas fa-video" },
|
||||
{ name: "Lidarr", description: "Automated music management", category: "media", icon: "fas fa-music" },
|
||||
{ name: "Prowlarr", description: "Indexer manager and proxy for the *arr stack", category: "media", icon: "fas fa-magnifying-glass" },
|
||||
{ name: "Bazarr", description: "Automatic subtitle download and management", category: "media", icon: "fas fa-closed-captioning" },
|
||||
{ name: "nzbget", description: "Usenet downloader", category: "media", icon: "fas fa-download" },
|
||||
{ name: "qBittorrent", description: "Torrent client with web UI", category: "media", icon: "fas fa-magnet" },
|
||||
{ name: "Kavita", description: "Self-hosted manga and book reader", category: "media", icon: "fas fa-book-open" },
|
||||
{ name: "Openshelf", description: "Book library with auto-ingest", category: "media", icon: "fas fa-book-open" },
|
||||
];
|
||||
|
||||
export const categoryOrder: Service["category"][] = [
|
||||
|
||||
@@ -41,7 +41,7 @@ export const timeline: TimelineEntry[] = [
|
||||
date: 'ongoing',
|
||||
title: 'Homelab — Proxmox Cluster',
|
||||
type: 'homelab',
|
||||
description: '8-VLAN segmented network, Proxmox VMs/LXCs, SSO via Authentik, full monitoring stack (Grafana + Prometheus + Loki).',
|
||||
description: '8-VLAN segmented network, Proxmox VMs/LXCs, SSO via Authentik, full monitoring stack (VictoriaMetrics + Grafana + Beszel + ntfy).',
|
||||
tags: ['proxmox', 'networking', 'monitoring', 'sso'],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user