Files
bluejay-infra/apps/intranet/intranet.yaml

134 lines
120 KiB
YAML

apiVersion: v1
kind: Namespace
metadata:
labels:
app.kubernetes.io/part-of: bluejay-infra
name: intranet
---
apiVersion: v1
data:
default.conf: "server {\n listen 80;\n server_name _;\n root /usr/share/nginx/html;\n index index.html;\n\n location / {\n try_files $uri $uri/ =404;\n }\n\n location /healthz {\n access_log off;\n return 200 \"ok\";\n add_header Content-Type text/plain;\n }\n}\n"
kind: ConfigMap
metadata:
name: intranet-nginx-conf
namespace: intranet
---
apiVersion: v1
data:
index.html: "<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"dark\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Blue Jay Lab Intranet - FlowerCore</title>\n<style>\n:root, [data-theme=\"dark\"] {\n --bg: #0A1628; --bg-gradient: linear-gradient(180deg, #0A1628 0%, #0E1E36 100%);\n --surface: #111D33; --surface2: #162844; --surface-hover: #1C3354;\n --border: #1E3A5F; --border-accent: #2B8AFF;\n --text: #E8EDF5; --text-heading: #FFFFFF; --text-muted: #8899B3;\n --accent: #2B8AFF; --accent-light: #5BA3FF; --accent-bright: #82BBFF;\n --gold: #FFB300; --gold-light: #FFCA40; --gold-dim: #CC8F00;\n --green: #3DB86A; --green-bg: rgba(61,184,106,0.12); --green-border: rgba(61,184,106,0.25);\n --yellow: #FFB300; --yellow-bg: rgba(255,179,0,0.12); --yellow-border: rgba(255,179,0,0.25);\n --red: #E84545; --red-bg: rgba(232,69,69,0.12); --red-border: rgba(232,69,69,0.25);\n --purple: #9B7ED8; --purple-bg: rgba(155,126,216,0.12); --purple-border: rgba(155,126,216,0.25);\n --orange: #E8883E; --cyan: #39D2C0; --pink: #F778BA;\n --navy: #0E1E36; --royal: #1A3A6A;\n --code-bg: #0C1829; --code-text: #FFB300;\n --card-shadow: 0 2px 6px rgba(0,0,0,0.35);\n --header-gradient: linear-gradient(135deg, #0E1E36 0%, #1A3A6A 40%, #2B8AFF 100%);\n --header-text-shadow: 0 2px 16px rgba(43,138,255,0.35);\n --summary-border-top: 3px solid var(--gold);\n --note-bg: rgba(43,138,255,0.08); --note-warn-bg: rgba(255,179,0,0.08);\n --topo-bg: #0C1829; --topo-border: #1E3A5F; --topo-text: #5BA3FF;\n --copy-bg: rgba(43,138,255,0.15); --copy-hover: rgba(43,138,255,0.3);\n --zebra: rgba(255,255,255,0.02);\n}\n[data-theme=\"light\"] {\n --bg: #F8FAFE; --bg-gradient: linear-gradient(180deg, #F8FAFE 0%, #EBF0F9 100%);\n --surface: #FFFFFF; --surface2: #EDF2FA; --surface-hover: #DCE5F4;\n --border: #C0D0E5; --border-accent: #2B7FE0;\n --text: #1A2744; --text-heading: #0D1B2E; --text-muted: #5A6F8A;\n --accent: #1565C0; --accent-light: #2B7FE0; --accent-bright: #1565C0;\n --gold: #E6A100; --gold-light: #FFB300; --gold-dim: #B38000;\n --green: #2A8A4A; --green-bg: rgba(42,138,74,0.1); --green-border: rgba(42,138,74,0.25);\n --yellow: #E6A100; --yellow-bg: rgba(230,161,0,0.1); --yellow-border: rgba(230,161,0,0.25);\n --red: #C03030; --red-bg: rgba(192,48,48,0.08); --red-border: rgba(192,48,48,0.2);\n --purple: #6B4FA0; --purple-bg: rgba(107,79,160,0.1); --purple-border: rgba(107,79,160,0.2);\n --orange: #C07020; --cyan: #288A7A; --pink: #C05090;\n --navy: #1A2744; --royal: #1565C0;\n --code-bg: #EDF2FA; --code-text: #C07020;\n --card-shadow: 0 1px 4px rgba(26,39,68,0.1);\n --header-gradient: linear-gradient(135deg, #0D1B2E 0%, #1565C0 40%, #2B7FE0 100%);\n --header-text-shadow: 0 1px 8px rgba(0,0,0,0.2);\n --summary-border-top: 3px solid var(--gold);\n --note-bg: rgba(21,101,192,0.05); --note-warn-bg: rgba(230,161,0,0.05);\n --topo-bg: #EDF2FA; --topo-border: #C0D0E5; --topo-text: #1565C0;\n --copy-bg: rgba(21,101,192,0.08); --copy-hover: rgba(21,101,192,0.15);\n --zebra: rgba(0,0,0,0.02);\n}\n* { box-sizing: border-box; margin: 0; padding: 0; }\nbody { font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; background: var(--bg); color: var(--text); line-height: 1.65; padding: 2rem; max-width: 1500px; margin: 0 auto; transition: background 0.3s, color 0.3s; }\nh1 { font-size: 2.2rem; margin-bottom: 0.25rem; font-weight: 800; color: var(--text-heading); letter-spacing: 0.5px; }\nh2 { font-size: 1.35rem; color: var(--gold); margin: 2.5rem 0 1rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 2px solid var(--gold-dim); padding-bottom: 0.5rem; }\nh3 { font-size: 1.05rem; color: var(--purple); margin: 1.5rem 0 0.75rem; font-weight: 700; }\nh4 { font-size: 0.95rem; color: var(--accent-light); margin: 1rem 0 0.5rem; font-weight: 600; }\n.subtitle { color: var(--gold-light); margin-bottom: 0.5rem; font-size: 1.05rem; }\n.last-updated { color: rgba(255,255,255,0.5); font-size: 0.82rem; }\ncode { background: var(--code-bg); padding: 2px 7px; border-radius: 4px; font-size: 0.83rem; color: var(--code-text); border: 1px solid var(--border); font-family: 'Cascadia Code','Fira Code','Consolas',monospace; }\na { color: var(--accent-light); text-decoration: none; }\na:hover { text-decoration: underline; color: var(--accent-bright); }\n.brand-header { background: var(--header-gradient); border: none; border-radius: 10px; padding: 2.5rem 2.5rem 2rem; margin-bottom: 1.5rem; position: relative; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.2); }\n.brand-header::before { content: ''; position: absolute; top: -40%; right: -10%; width: 50%; height: 180%; background: radial-gradient(ellipse, rgba(255,255,255,0.04) 0%, transparent 70%); pointer-events: none; }\n.brand-header h1 { color: #fff; text-shadow: var(--header-text-shadow); font-size: 2.4rem; letter-spacing: 1px; text-transform: uppercase; }\n.status-badge { display: inline-block; background: rgba(255,179,0,0.2); border: 1px solid rgba(255,179,0,0.4); color: #FFCA40; padding: 4px 14px; border-radius: 14px; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase; margin-top: 0.5rem; }\n.theme-toggle { position: absolute; top: 1.25rem; right: 1.5rem; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.15); border-radius: 20px; padding: 6px 14px; color: rgba(255,255,255,0.8); cursor: pointer; font-size: 0.78rem; font-weight: 600; letter-spacing: 0.3px; transition: background 0.2s; backdrop-filter: blur(4px); display: flex; align-items: center; gap: 6px; }\n.theme-toggle:hover { background: rgba(255,255,255,0.18); border-color: rgba(255,255,255,0.25); }\n.theme-toggle svg { width: 14px; height: 14px; fill: currentColor; }\nnav { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 0.75rem 1rem; margin-bottom: 1.5rem; position: sticky; top: 0; z-index: 100; box-shadow: var(--card-shadow); }\nnav ul { list-style: none; display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; }\nnav li { display: inline; }\nnav a { color: var(--accent-light); text-decoration: none; padding: 6px 14px; border-radius: 6px; font-size: 0.8rem; font-weight: 500; transition: all 0.2s; border: 1px solid transparent; cursor: pointer; display: inline-block; }\nnav a:hover { background: var(--surface2); border-color: var(--border); text-decoration: none; color: var(--accent-bright); }\nnav a.active { background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 700; }\n.summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.85rem; margin: 1.25rem 0; }\n.summary-card { background: var(--surface); border: 1px solid var(--border); border-top: var(--summary-border-top); border-radius: 8px; padding: 1.25rem; text-align: center; box-shadow: var(--card-shadow); transition: border-color 0.2s, transform 0.15s; }\n.summary-card:hover { border-color: var(--border-accent); transform: translateY(-1px); }\n.summary-number { font-size: 2rem; font-weight: 800; color: var(--accent-light); }\n.summary-label { color: var(--text-muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.3px; margin-top: 2px; }\n.b { display: inline-block; padding: 2px 10px; border-radius: 10px; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.4px; }\n.b-online { background: var(--green-bg); color: var(--green); border: 1px solid var(--green-border); }\n.b-planned { background: var(--yellow-bg); color: var(--yellow); border: 1px solid var(--yellow-border); }\n.b-offline { background: var(--red-bg); color: var(--red); border: 1px solid var(--red-border); }\n.b-mgmt { background: rgba(43,138,255,0.12); color: var(--accent-light); border: 1px solid rgba(43,138,255,0.25); }\n.b-prod { background: var(--green-bg); color: var(--green); border: 1px solid var(--green-border); }\n.b-home { background: var(--purple-bg); color: var(--purple); border: 1px solid var(--purple-border); }\n.b-tenant { background: rgba(232,136,62,0.12); color: var(--orange); border: 1px solid rgba(232,136,62,0.25); }\n.b-wifi { background: rgba(57,210,192,0.12); color: var(--cyan); border: 1px solid rgba(57,210,192,0.25); }\n.b-voip { background: var(--yellow-bg); color: var(--yellow); border: 1px solid var(--yellow-border); }\n.b-spare { background: var(--surface2); color: var(--text-muted); border: 1px solid var(--border); }\n.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }\n.dot-green { background: var(--green); box-shadow: 0 0 4px var(--green); }\n.dot-yellow { background: var(--yellow); box-shadow: 0 0 4px var(--yellow); }\n.dot-gray { background: var(--text-muted); }\n.dot-red { background: var(--red); box-shadow: 0 0 4px var(--red); }\ntable { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.87rem; }\nth, td { padding: 0.55rem 0.75rem; border: 1px solid var(--border); text-align: left; }\nth { background: var(--surface2); color: var(--text-muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.3px; }\ntr:nth-child(even) { background: var(--zebra); }\ntr:hover { background: rgba(43,138,255,0.05); }\n.ip { font-family: 'Cascadia Code','Fira Code','Consolas',monospace; color: var(--accent-light); font-weight: 600; font-size: 0.85rem; }\n.pw { font-family: 'Cascadia Code','Fira Code','Consolas',monospace; color: var(--orange); font-size: 0.82rem; }\n.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; margin: 1rem 0; }\n.card { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px; box-shadow: var(--card-shadow); transition: border-color 0.2s; }\n.card:hover { border-color: var(--border-accent); }\n.card-title { font-weight:\
\ 700; font-size: 1rem; color: var(--text-heading); margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }\n.card p, .card li { color: var(--text-muted); font-size: 0.85rem; margin: 4px 0; }\n.card ul { list-style: none; padding: 0; }\n.card li { padding: 4px 0; border-bottom: 1px solid var(--border); }\n.card li:last-child { border-bottom: none; }\n.topology { background: var(--topo-bg); border: 2px solid var(--topo-border); border-radius: 8px; padding: 2rem; margin: 1rem 0; overflow-x: auto; }\n.topology pre { font-family: 'Cascadia Code','Fira Code','Consolas',monospace; font-size: 0.82rem; color: var(--topo-text); line-height: 1.5; white-space: pre; }\n.copy-btn { background: var(--copy-bg); border: 1px solid var(--border); border-radius: 4px; color: var(--accent-light); cursor: pointer; padding: 2px 8px; font-size: 0.72rem; font-family: inherit; transition: background 0.2s; margin-left: 6px; vertical-align: middle; }\n.copy-btn:hover { background: var(--copy-hover); }\n.copy-btn.copied { color: var(--green); border-color: var(--green-border); }\n.note { background: var(--note-bg); border-left: 3px solid var(--accent); padding: 0.75rem 1rem; margin: 1rem 0; border-radius: 0 6px 6px 0; font-size: 0.87rem; }\n.note-warn { background: var(--note-warn-bg); border-left-color: var(--yellow); }\n.quick-links { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; margin: 1rem 0; }\n.quick-link { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 10px 14px; display: flex; align-items: center; gap: 8px; transition: all 0.2s; text-decoration: none; color: var(--text); }\n.quick-link:hover { border-color: var(--accent); background: var(--surface-hover); text-decoration: none; }\n.quick-link .ql-name { font-weight: 600; font-size: 0.85rem; color: var(--text-heading); }\n.quick-link .ql-url { font-size: 0.72rem; color: var(--text-muted); font-family: 'Cascadia Code',monospace; }\n.tab-content { display: none; }\n.tab-content.active { display: block; }\n.wifi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; margin: 1.5rem 0; }\n.wifi-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; box-shadow: var(--card-shadow); transition: border-color 0.2s, transform 0.15s; }\n.wifi-card:hover { border-color: var(--border-accent); transform: translateY(-2px); }\n.wifi-card-header { padding: 14px 16px 10px; border-bottom: 3px solid var(--border); }\n.wifi-ssid { font-size: 1.15rem; font-weight: 800; color: var(--text-heading); letter-spacing: 0.3px; }\n.wifi-vlan { margin-top: 4px; }\n.wifi-qr { display: flex; justify-content: center; align-items: center; padding: 16px; background: #ffffff; min-height: 180px; }\n.wifi-qr canvas { border-radius: 4px; }\n.wifi-qr-placeholder { background: var(--surface2) !important; border: 2px dashed var(--border); min-height: 180px; }\n.wifi-qr-placeholder .qr-placeholder-box { display: flex; flex-direction: column; align-items: center; gap: 12px; color: var(--text-muted); padding: 16px; text-align: center; }\n.wifi-qr-placeholder .qr-placeholder-box svg { color: var(--accent); opacity: 0.6; }\n.wifi-qr-placeholder .qr-placeholder-text { font-size: 0.82rem; font-weight: 600; letter-spacing: 0.3px; color: var(--accent-light); }\n.wifi-qr-open { border-color: var(--green-border); }\n.wifi-qr-open .qr-placeholder-box svg { color: var(--green); }\n.wifi-qr-open .qr-placeholder-text { color: var(--green); }\n.wifi-details { padding: 12px 16px 16px; }\n.wifi-field { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 0.85rem; }\n.wifi-field:last-child { border-bottom: none; }\n.wifi-label { color: var(--text-muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.3px; min-width: 80px; }\n.wifi-value { color: var(--text); text-align: right; }\n.print-qr-btn { background: var(--accent); color: #fff; border: none; border-radius: 6px; padding: 8px 18px; font-size: 0.85rem; font-weight: 600; cursor: pointer; margin-left: 12px; transition: background 0.2s; }\n.print-qr-btn:hover { background: var(--accent-light); }\n@media print {\n body { background: #fff !important; color: #000 !important; padding: 0 !important; }\n nav, .brand-header, .theme-toggle, .copy-btn, .print-qr-btn, .note, .tab-content:not(.print-active) { display: none !important; }\n .tab-content.print-active { display: block !important; }\n .wifi-grid { grid-template-columns: repeat(2, 1fr) !important; gap: 20px !important; }\n .wifi-card { break-inside: avoid; border: 2px solid #333 !important; box-shadow: none !important; page-break-inside: avoid; }\n .wifi-card-header { border-bottom-color: #333 !important; }\n .wifi-ssid { color: #000 !important; }\n .wifi-qr { background: #fff !important; padding: 12px !important; }\n .wifi-qr-placeholder { background: #f5f5f5 !important; border-color: #999 !important; }\n .wifi-qr-placeholder .qr-placeholder-box svg { color: #333 !important; }\n .wifi-qr-placeholder .qr-placeholder-text { color: #333 !important; }\n .wifi-details { color: #000 !important; }\n .wifi-field { border-bottom-color: #ccc !important; }\n .wifi-label { color: #555 !important; }\n .wifi-value, .wifi-value code { color: #000 !important; background: #eee !important; }\n .b { border-color: #999 !important; color: #333 !important; background: #eee !important; }\n}\n@media (max-width: 768px) {\n body { padding: 1rem; }\n .brand-header { padding: 1.5rem; }\n .brand-header h1 { font-size: 1.5rem; }\n .summary-grid { grid-template-columns: repeat(3, 1fr); }\n .card-grid { grid-template-columns: 1fr; }\n nav a { font-size: 0.72rem; padding: 4px 8px; }\n .topology pre { font-size: 0.7rem; }\n table { font-size: 0.78rem; }\n th, td { padding: 0.4rem 0.5rem; }\n .quick-links { grid-template-columns: 1fr 1fr; }\n .wifi-grid { grid-template-columns: 1fr; }\n .wifi-qr canvas { width: 140px !important; height: 140px !important; }\n}\n</style>\n</head>\n<body>\n\n<div class=\"brand-header\">\n <button class=\"theme-toggle\" onclick=\"toggleTheme()\" title=\"Switch theme\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.389 5.389 0 0 1-4.4 2.26 5.403 5.403 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1z\"/></svg>\n <span id=\"themeLabel\">Light</span>\n </button>\n <h1>Blue Jay Lab Intranet</h1>\n <p class=\"subtitle\">BlueJay Network Infrastructure &mdash; 13 VLANs | 6 Nodes | Bare-Metal RKE2 | 5 WiFi SSIDs | 18 Domains | All Services Live</p>\n <p class=\"last-updated\">Last updated: 2026-03-10</p>\n <div class=\"status-badge\">REBUILD COMPLETE &mdash; All Services Live on Bare-Metal RKE2</div>\n</div>\n\n<nav>\n <ul>\n <li><a class=\"tab-btn active\" onclick=\"switchTab('overview',this)\">Overview</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('isp',this)\">ISP &amp; WAN</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('pfsense',this)\">pfSense</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('switching',this)\">Switch &amp; WiFi</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('dns',this)\">DNS Directory</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('k8s',this)\">Kubernetes</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('noc',this)\">NOC Services</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('vpn',this)\">VPN &amp; Security</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('edge',this)\">Edge Nodes</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('wifi',this)\">WiFi</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('planned',this)\">Planned</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('topology',this)\">Topology</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('domains',this)\">Domains</a></li>\n <li><a class=\"tab-btn\" onclick=\"switchTab('credentials',this)\">Credentials</a></li>\n </ul>\n</nav>\n\n<!-- ===== TAB: OVERVIEW ===== -->\n<div id=\"tab-overview\" class=\"tab-content active\">\n<h2>Overview</h2>\n<div class=\"summary-grid\">\n <div class=\"summary-card\"><div class=\"summary-number\">12</div><div class=\"summary-label\">VLANs</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">6</div><div class=\"summary-label\">Physical Nodes</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">10</div><div class=\"summary-label\">ArgoCD Apps</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">43+</div><div class=\"summary-label\">DNS Entries</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">17</div><div class=\"summary-label\">Guacamole Conns</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">5</div><div class=\"summary-label\">WiFi SSIDs</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">8</div><div class=\"summary-label\">VPN Tunnels</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">13</div><div class=\"summary-label\">Public IPs</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">18</div><div class=\"summary-label\">Domains</div></div>\n</div>\n\n<div class=\"note note\"><strong>Network Status: REBUILD COMPLETE.</strong> All 13 phases complete. Bare-metal RKE2 3-node cluster, 10 ArgoCD apps healthy, Cloudflare DNS on /28, 1Password Connect wired. All services live.</div>\n\n<h3>Quick Links &mdash; Web UIs</h3>\n<div class=\"quick-links\">\n <a class=\"quick-link\" href=\"https://10.0.56.1\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>pfSense</div><div class=\"ql-url\">https://10.0.56.1</div></div></a>\n <a class=\"quick-link\" href=\"https://10.0.56.3\" target=\"_blank\"\
><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>UniFi Cloud Key</div><div class=\"ql-url\">https://10.0.56.3</div></div></a>\n <a class=\"quick-link\" href=\"http://10.0.58.2:8000\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Synology WiFi (SRM)</div><div class=\"ql-url\">http://10.0.58.2:8000</div></div></a>\n <a class=\"quick-link\" href=\"https://traefik.iamworkin.lan\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Traefik Dashboard</div><div class=\"ql-url\">https://traefik.iamworkin.lan</div></div></a>\n <a class=\"quick-link\" href=\"https://10.0.56.10:9090\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Cockpit (noc1)</div><div class=\"ql-url\">https://10.0.56.10:9090</div></div></a>\n <a class=\"quick-link\" href=\"http://10.0.56.10:3000\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Grafana</div><div class=\"ql-url\">http://10.0.56.10:3000</div></div></a>\n <a class=\"quick-link\" href=\"http://10.0.56.10:9091\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Prometheus</div><div class=\"ql-url\">http://10.0.56.10:9091</div></div></a>\n <a class=\"quick-link\" href=\"http://10.0.56.10:30080/guacamole/\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Guacamole</div><div class=\"ql-url\">http://10.0.56.10:30080/guacamole/</div></div></a>\n <a class=\"quick-link\" href=\"http://pki.iamworkin.lan:30081\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>PKI Web</div><div class=\"ql-url\">http://pki.iamworkin.lan:30081</div></div></a>\n <a class=\"quick-link\" href=\"https://argocd.iamworkin.lan\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>ArgoCD</div><div class=\"ql-url\">https://argocd.iamworkin.lan</div></div></a>\n <a class=\"quick-link\" href=\"https://zabbix.iamworkin.lan\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Zabbix</div><div class=\"ql-url\">https://zabbix.iamworkin.lan</div></div></a>\n <a class=\"quick-link\" href=\"https://gitea.iamworkin.lan\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Gitea</div><div class=\"ql-url\">https://gitea.iamworkin.lan</div></div></a>\n <a class=\"quick-link\" href=\"http://192.168.254.254\" target=\"_blank\"><div><div class=\"ql-name\"><span class=\"dot dot-green\"></span>Frontier Modem</div><div class=\"ql-url\">http://192.168.254.254</div></div></a>\n</div>\n\n<h3>Phase Progress</h3>\n<table>\n<thead><tr><th>Phase</th><th>Description</th><th>Status</th><th>Progress</th></tr></thead>\n<tbody>\n<tr><td>1</td><td>Frontier Modem Config</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>2</td><td>pfSense Base (WAN, LAN, VIPs)</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>3</td><td>VLAN Configuration (12 VLANs)</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>4</td><td>Firewall Rules &amp; Aliases</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>5</td><td>Bare-Metal RKE2 Cluster</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>6</td><td>OpenVPN (8 servers)</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>7</td><td>NAT Configuration</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>8</td><td>Traffic Shaper</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>9</td><td>DNS + NTP + SNMP</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>10</td><td>Switch + WiFi Config</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>11</td><td>NOC1 + Bare-Metal RKE2</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>12</td><td>GitOps + ArgoCD</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n<tr><td>13</td><td>Documentation Sync</td><td><span class=\"b b-online\">Done</span></td><td>100%</td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: ISP & WAN ===== -->\n<div id=\"tab-isp\" class=\"tab-content\">\n<h2>ISP &amp; WAN</h2>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>ISP: Frontier Communications</div>\n <ul>\n <li><strong>Service:</strong> 1000/1000 Mbps fiber</li>\n <li><strong>Account:</strong> 952-431-5646-020421-7</li>\n <li><strong>Measured:</strong> 925 down / 677 up (MGMT VLAN)</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Modem: NVG468MQ</div>\n <ul>\n <li><strong>Web:</strong> <a href=\"http://192.168.254.254\" target=\"_blank\">http://192.168.254.254</a> <button class=\"copy-btn\" onclick=\"copyText('192.168.254.254')\">copy</button></li>\n <li><strong>Credentials:</strong> <code>admin</code> / <code class=\"pw\">5108967609</code> <button class=\"copy-btn\" onclick=\"copyText('5108967609')\">copy</button></li>\n <li><strong>Serial:</strong> 184795207512112</li>\n <li><strong>Firmware:</strong> 9.3.0h7d91</li>\n <li><strong>WAN IP:</strong> <span class=\"ip\">74.32.187.152/22</span> <button class=\"copy-btn\" onclick=\"copyText('74.32.187.152')\">copy</button></li>\n <li><strong>Config:</strong> DMZ to pfSense, WiFi OFF, firewall OFF</li>\n </ul>\n </div>\n</div>\n<h3>WAN Status</h3>\n<table>\n<thead><tr><th>Property</th><th>Value</th></tr></thead>\n<tbody>\n<tr><td>pfSense WAN Interface</td><td>ix3 (DHCP from modem)</td></tr>\n<tr><td>pfSense WAN IP</td><td><span class=\"ip\">192.168.254.122</span> <button class=\"copy-btn\" onclick=\"copyText('192.168.254.122')\">copy</button> (double NAT intentional)</td></tr>\n<tr><td>Public /28 Block</td><td><span class=\"ip\">74.40.140.16/28</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.16/28')\">copy</button></td></tr>\n<tr><td>Gateway</td><td><span class=\"ip\">74.40.140.30</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.30')\">copy</button></td></tr>\n<tr><td>Usable Range</td><td><span class=\"ip\">74.40.140.17 &ndash; 74.40.140.29</span> (13 IPs)</td></tr>\n</tbody>\n</table>\n<div class=\"note note-warn\"><strong>ISP /28 Routing: LIVE.</strong> Public /28 (74.40.140.16/28) fully operational. 13 VIPs, 12 outbound NAT rules, 18 port forwards. Cloudflare DNS on /28 IPs.</div>\n<h3>Modem Static Routes</h3>\n<table>\n<thead><tr><th>Name</th><th>Destination</th><th>Gateway</th><th>Interface</th></tr></thead>\n<tbody>\n<tr><td>pfSense-Public-28</td><td><span class=\"ip\">74.40.140.16/28</span></td><td><span class=\"ip\">192.168.254.122</span></td><td>LAN</td></tr>\n<tr><td>pfSense-Private-Subnets</td><td><span class=\"ip\">10.0.0.0/8</span></td><td><span class=\"ip\">192.168.254.122</span></td><td>LAN</td></tr>\n</tbody>\n</table>\n<h3>Public IP Allocation (13 usable)</h3>\n<table>\n<thead><tr><th>IP</th><th>Full Address</th><th>Assignment</th><th>VLAN(s)</th><th>Services</th></tr></thead>\n<tbody>\n<tr><td>.16</td><td><span class=\"ip\">74.40.140.16</span></td><td><em>Network address</em></td><td>&mdash;</td><td>Unusable</td></tr>\n<tr><td>.17</td><td><span class=\"ip\">74.40.140.17</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.17')\">copy</button></td><td>ANDREW + VPN</td><td>60</td><td>Andrew tenant primary + VPN :1194/:1195</td></tr>\n<tr><td>.18</td><td><span class=\"ip\">74.40.140.18</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.18')\">copy</button></td><td>MATT + VPN</td><td>61</td><td>Matt tenant primary + VPN :1194/:1195</td></tr>\n<tr><td>.19</td><td><span class=\"ip\">74.40.140.19</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.19')\">copy</button></td><td>DUSTIN + VPN</td><td>62</td><td>Dustin tenant primary + VPN :1194/:1195</td></tr>\n<tr><td>.20</td><td><span class=\"ip\">74.40.140.20</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.20')\">copy</button></td><td>ERIK + VPN</td><td>63</td><td>Erik tenant primary + VPN :1194/:1195</td></tr>\n<tr><td>.21</td><td><span class=\"ip\">74.40.140.21</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.21')\">copy</button></td><td>PROD</td><td>57</td><td>K8s Traefik shared ingress (flowercore.io)</td></tr>\n<tr><td>.22</td><td><span class=\"ip\">74.40.140.22</span></td><td><em>Reserved</em></td><td>&mdash;</td><td>&mdash;</td></tr>\n<tr><td>.23</td><td><span class=\"ip\">74.40.140.23</span></td><td><em>Reserved</em></td><td>&mdash;</td><td>&mdash;</td></tr>\n<tr><td>.24</td><td><span class=\"ip\">74.40.140.24</span></td><td><em>Reserved</em></td><td>&mdash;</td><td>&mdash;</td></tr>\n<tr><td>.25</td><td><span class=\"ip\">74.40.140.25</span></td><td><em>Reserved</em></td><td>&mdash;</td><td>&mdash;</td></tr>\n<tr><td>.26</td><td><span class=\"ip\">74.40.140.26</span></td><td><em>Reserved</em></td><td>&mdash;</td><td>&mdash;</td></tr>\n<tr><td>.27</td><td><span class=\"ip\">74.40.140.27</span></td><td><em>Reserved</em></td><td>&mdash;</td><td>&mdash;</td></tr>\n<tr><td>.28</td><td><span class=\"ip\">74.40.140.28</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.28')\">copy</button></td><td>SHARED</td><td>59,64,65,66,67</td><td>WORK+SCHOOL+GUEST+VOIP+EMPLOYEE outbound</td></tr>\n<tr><td>.29</td><td><span class=\"ip\">74.40.140.29</span> <button class=\"copy-btn\" onclick=\"copyText('74.40.140.29')\">copy</button></td><td>HOME</td><td>58</td><td>Home traffic + Nintendo Switch static port NAT</td></tr>\n<tr><td>.30</td><td><span class=\"ip\">74.40.140.30</span></td><td><em>Gateway (Frontier)</em></td><td>&mdash;</td><td>ISP\
\ router</td></tr>\n<tr><td>.31</td><td><span class=\"ip\">74.40.140.31</span></td><td><em>Broadcast</em></td><td>&mdash;</td><td>Unusable</td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: PFSENSE ===== -->\n<div id=\"tab-pfsense\" class=\"tab-content\">\n<h2>pfSense Firewall</h2>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Netgate 4100</div>\n <ul>\n <li><strong>Web:</strong> <a href=\"https://10.0.56.1\" target=\"_blank\">https://10.0.56.1</a> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.1')\">copy</button></li>\n <li><strong>Credentials:</strong> <code>admin</code> / <code class=\"pw\">SCOOBY_entry1latimer</code> <button class=\"copy-btn\" onclick=\"copyText('SCOOBY_entry1latimer')\">copy</button></li>\n <li><strong>SSH:</strong> <code>admin@10.0.56.1</code> <button class=\"copy-btn\" onclick=\"copyText('ssh admin@10.0.56.1')\">copy</button></li>\n <li><strong>Hardware:</strong> 2x SFP+, 4x 2.5GbE (igc0-3)</li>\n <li><strong>WAN:</strong> ix3 &mdash; LAN: igc0 (802.1Q trunk)</li>\n <li><strong>Domain:</strong> iamworkin.lan</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">Firewall Stats</div>\n <ul>\n <li><strong>Aliases:</strong> 36 (16 port, 5 host, 15 network)</li>\n <li><strong>Rules:</strong> 90 active</li>\n <li><strong>Policy:</strong> Air-gapped default &mdash; deny all, explicit allow</li>\n <li><strong>SNMP:</strong> community <code class=\"pw\">bluejay_monitor</code> <button class=\"copy-btn\" onclick=\"copyText('bluejay_monitor')\">copy</button></li>\n <li><strong>SNMP Modules:</strong> mibII, netgraph, pf, hostres, bridge</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">Services</div>\n <ul>\n <li><strong>DNS:</strong> Unbound (DNSSEC, WAN-only outgoing, prefetch)</li>\n <li><strong>DHCP:</strong> dhcpd on all 12 VLAN interfaces (.100-.199)</li>\n <li><strong>NTP:</strong> ntpd on all VLAN interfaces, DHCP option 42</li>\n <li><strong>Traffic Shaper:</strong> 24 dummynet pipes, fq_codel</li>\n </ul>\n </div>\n</div>\n<h3>VLAN Configuration (12 VLANs)</h3>\n<table>\n<thead><tr><th>VLAN</th><th>Name</th><th>Subnet</th><th>DHCP Range</th><th>Down/Up (Mbps)</th><th>Priority</th><th>Public IP</th></tr></thead>\n<tbody>\n<tr><td>56</td><td><span class=\"b b-mgmt\">MGMT</span></td><td><span class=\"ip\">10.0.56.0/24</span></td><td>.100-.199</td><td>500 / 500</td><td>5</td><td>WAN DHCP</td></tr>\n<tr><td>57</td><td><span class=\"b b-prod\">PROD</span></td><td><span class=\"ip\">10.0.57.0/24</span></td><td>.100-.199</td><td>500 / 500</td><td>5</td><td>.21</td></tr>\n<tr><td>58</td><td><span class=\"b b-home\">HOME</span></td><td><span class=\"ip\">10.0.58.0/24</span></td><td>.100-.199</td><td>800 / 800</td><td>3</td><td>.29</td></tr>\n<tr><td>59</td><td><span class=\"b b-wifi\">EMPLOYEE</span></td><td><span class=\"ip\">10.0.59.0/24</span></td><td>.100-.199</td><td>500 / 500</td><td>3</td><td>.28 (shared)</td></tr>\n<tr><td>60</td><td><span class=\"b b-tenant\">ANDREW</span></td><td><span class=\"ip\">10.0.60.0/24</span></td><td>.100-.199</td><td>300 / 300</td><td>3</td><td>.17</td></tr>\n<tr><td>61</td><td><span class=\"b b-tenant\">MATT</span></td><td><span class=\"ip\">10.0.61.0/24</span></td><td>.100-.199</td><td>300 / 300</td><td>3</td><td>.18</td></tr>\n<tr><td>62</td><td><span class=\"b b-tenant\">DUSTIN</span></td><td><span class=\"ip\">10.0.62.0/24</span></td><td>.100-.199</td><td>300 / 300</td><td>3</td><td>.19</td></tr>\n<tr><td>63</td><td><span class=\"b b-tenant\">ERIK</span></td><td><span class=\"ip\">10.0.63.0/24</span></td><td>.100-.199</td><td>300 / 300</td><td>3</td><td>.20</td></tr>\n<tr><td>64</td><td><span class=\"b b-wifi\">WORK</span></td><td><span class=\"ip\">10.0.64.0/24</span></td><td>.100-.199</td><td>500 / 500</td><td>3</td><td>.28 (shared)</td></tr>\n<tr><td>65</td><td><span class=\"b b-wifi\">SCHOOL</span></td><td><span class=\"ip\">10.0.65.0/24</span></td><td>.100-.199</td><td>200 / 200</td><td>1</td><td>.28 (shared)</td></tr>\n<tr><td>66</td><td><span class=\"b b-spare\">GUEST</span></td><td><span class=\"ip\">10.0.66.0/24</span></td><td>.100-.199</td><td>100 / 50</td><td>1</td><td>.28 (shared)</td></tr>\n<tr><td>67</td><td><span class=\"b b-voip\">VOIP</span></td><td><span class=\"ip\">10.0.67.0/24</span></td><td>.100-.199</td><td>100 / 100</td><td>7</td><td>.28 (shared)</td></tr>\n</tbody>\n</table>\n<div class=\"note\"><strong>Firewall Policy:</strong> MGMT has full access. HOME/WORK/SCHOOL get general internet. GUEST isolated except PROD web. Tenants fully isolated from each other &mdash; only PROD, DNS, NAS, and internet. VOIP is SIP-only outbound.</div>\n</div>\n\n<!-- ===== TAB: SWITCHING & WIFI ===== -->\n<div id=\"tab-switching\" class=\"tab-content\">\n<h2>Switching &amp; WiFi</h2>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>UniFi Switch USW-Lite-16-PoE</div>\n <ul>\n <li><strong>IP:</strong> <span class=\"ip\">10.0.56.2</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.2')\">copy</button></li>\n <li><strong>MAC:</strong> 74:ac:b9:e3:93:ba</li>\n <li><strong>SSH:</strong> <code>GOShDkH@10.0.56.2</code> <button class=\"copy-btn\" onclick=\"copyText('ssh GOShDkH@10.0.56.2')\">copy</button></li>\n <li><strong>SSH Password:</strong> <code class=\"pw\">6IDvT8vmQH6QqsP</code> <button class=\"copy-btn\" onclick=\"copyText('6IDvT8vmQH6QqsP')\">copy</button></li>\n <li><strong>Firmware:</strong> 7.2.123.16565</li>\n <li><strong>CLI:</strong> Type <code>cli</code> for Realtek switch CLI</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>UniFi Cloud Key G2</div>\n <ul>\n <li><strong>Web:</strong> <a href=\"https://10.0.56.3\" target=\"_blank\">https://10.0.56.3</a> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.3')\">copy</button></li>\n <li><strong>Credentials:</strong> <code>admin</code> / <code class=\"pw\">nest7BEGGAR*revoked</code> <button class=\"copy-btn\" onclick=\"copyText('nest7BEGGAR*revoked')\">copy</button></li>\n <li><strong>SSH:</strong> <code>root@10.0.56.3</code> / <code class=\"pw\">6IDvT8vmQH6QqsP</code></li>\n <li><strong>Network Version:</strong> 7.1.61</li>\n <li><strong>MongoDB:</strong> port 27117 (database <code>ace</code>)</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Synology RT6600AX (AP Mode)</div>\n <ul>\n <li><strong>Web:</strong> <a href=\"http://10.0.58.2:8000\" target=\"_blank\">http://10.0.58.2:8000</a> <button class=\"copy-btn\" onclick=\"copyText('10.0.58.2')\">copy</button></li>\n <li><strong>Credentials:</strong> <code>bluejay</code> / <code class=\"pw\">galileo_parisian8ADMIRE</code> <button class=\"copy-btn\" onclick=\"copyText('galileo_parisian8ADMIRE')\">copy</button></li>\n <li><strong>SSH:</strong> <code>bluejay@10.0.58.2</code> port 22</li>\n <li><strong>MAC:</strong> 90:09:d0:3d:64:ae</li>\n <li><strong>Mode:</strong> AP (bridge), all trunk ports enabled</li>\n </ul>\n </div>\n</div>\n<h3>Switch Port Assignments</h3>\n<table>\n<thead><tr><th>Port</th><th>Device</th><th>Mode</th><th>VLAN</th><th>Status</th></tr></thead>\n<tbody>\n<tr><td>1</td><td>pfSense Uplink</td><td>Trunk (All)</td><td>56-67</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>2</td><td>harvester3</td><td>Trunk (All)</td><td>56-67</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>3</td><td>WiFi Uplink (Synology)</td><td>Trunk, native 58</td><td>57-67</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>4</td><td>harvester2</td><td>Trunk (All)</td><td>56-67</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>5</td><td>Cloud Key G2</td><td>Access</td><td>56 (MGMT)</td><td><span class=\"dot dot-green\"></span>UP (PoE)</td></tr>\n<tr><td>6</td><td>harvester1</td><td>Trunk (All)</td><td>56-67</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>7</td><td><em>Available</em></td><td>&mdash;</td><td>&mdash;</td><td><span class=\"dot dot-gray\"></span>Down</td></tr>\n<tr><td>8</td><td>noc1</td><td>Trunk (All)</td><td>56-67</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>9</td><td>Workstation</td><td>Access</td><td>56 (MGMT)</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>10</td><td><em>Available</em></td><td>&mdash;</td><td>&mdash;</td><td><span class=\"dot dot-gray\"></span>Down</td></tr>\n<tr><td>11</td><td>edge2 (Pi 4)</td><td>Access</td><td>57 (PROD)</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>12</td><td><em>Available</em></td><td>&mdash;</td><td>&mdash;</td><td><span class=\"dot dot-gray\"></span>Down</td></tr>\n<tr><td>13</td><td>edge1 (Pi 5)</td><td>Access</td><td>57 (PROD)</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>14</td><td>Synology NAS</td><td>Access</td><td>58 (HOME)</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n<tr><td>15</td><td><em>Available</em></td><td>&mdash;</td><td>&mdash;</td><td><span class=\"dot dot-gray\"></span>Down</td></tr>\n<tr><td>16</td><td>Synology 2</td><td>Access</td><td>58 (HOME)</td><td><span class=\"dot dot-green\"></span>UP</td></tr>\n</tbody>\n</table>\n<h3>WiFi SSIDs</h3>\n<table>\n<thead><tr><th>SSID</th><th>Bridge</th><th>VLAN</th><th>Type</th><th>Password</th></tr></thead>\n<tbody>\n<tr><td><strong>BlueJay-Home</strong></td><td>br0</td><td>untagged (58)</td><td>Primary</td><td><code class=\"pw\">Starling-Tundra-Condor-Coral</code> <button class=\"copy-btn\" onclick=\"copyText('Starling-Tundra-Condor-Coral')\">copy</button></td></tr>\n<tr><td><strong>BlueJay-Employee</strong></td><td>br2</td><td>59</td><td>Custom</td><td><code\
\ class=\"pw\">Merlin~Ivory~Oakleaf~Bramble</code> <button class=\"copy-btn\" onclick=\"copyText('Merlin~Ivory~Oakleaf~Bramble')\">copy</button></td></tr>\n<tr><td><strong>BlueJay-Work</strong></td><td>br3</td><td>64</td><td>Custom</td><td><code class=\"pw\">Forge.Hawk.Oakleaf.Topaz</code> <button class=\"copy-btn\" onclick=\"copyText('Forge.Hawk.Oakleaf.Topaz')\">copy</button></td></tr>\n<tr><td><strong>BlueJay-School</strong></td><td>br4</td><td>65</td><td>Custom</td><td><code class=\"pw\">Harbor-Eagle-Condor-Topaz</code> <button class=\"copy-btn\" onclick=\"copyText('Harbor-Eagle-Condor-Topaz')\">copy</button></td></tr>\n<tr><td><strong>BlueJay-Guest</strong></td><td>gbr0</td><td>66</td><td>Guest (isolation+NAT)</td><td><code class=\"pw\">Eagle.Oriole.Osprey.Silver</code> <button class=\"copy-btn\" onclick=\"copyText('Eagle.Oriole.Osprey.Silver')\">copy</button></td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: DNS ===== -->\n<div id=\"tab-dns\" class=\"tab-content\">\n<h2>DNS Directory</h2>\n<div class=\"note\">All entries are pfSense Unbound host overrides under <code>iamworkin.lan</code>. 42+ total entries configured.</div>\n<h3>Management Devices</h3>\n<table>\n<thead><tr><th>Hostname</th><th>IP</th><th>Role</th></tr></thead>\n<tbody>\n<tr><td>pfsense.iamworkin.lan</td><td><span class=\"ip\">10.0.56.1</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.1')\">copy</button></td><td>pfSense firewall</td></tr>\n<tr><td>switch.iamworkin.lan</td><td><span class=\"ip\">10.0.56.2</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.2')\">copy</button></td><td>UniFi PoE Switch</td></tr>\n<tr><td>unifi.iamworkin.lan</td><td><span class=\"ip\">10.0.56.3</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.3')\">copy</button></td><td>UniFi Cloud Key G2</td></tr>\n<tr><td>wifi.iamworkin.lan</td><td><span class=\"ip\">10.0.58.2</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.58.2')\">copy</button></td><td>Synology WiFi Router (AP)</td></tr>\n<tr><td>nas.iamworkin.lan</td><td><span class=\"ip\">10.0.58.3</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.58.3')\">copy</button></td><td>Synology NAS</td></tr>\n</tbody>\n</table>\n<h3>Harvester Cluster</h3>\n<table>\n<thead><tr><th>Hostname</th><th>IP</th><th>Role</th></tr></thead>\n<tbody>\n<tr><td>harvester.iamworkin.lan</td><td><span class=\"ip\">10.0.56.14</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.14')\">copy</button></td><td>Harvester VIP (cluster dashboard)</td></tr>\n<tr><td>harvester1.iamworkin.lan</td><td><span class=\"ip\">10.0.56.11</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.11')\">copy</button></td><td>Harvester node 1</td></tr>\n<tr><td>harvester2.iamworkin.lan</td><td><span class=\"ip\">10.0.56.12</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.12')\">copy</button></td><td>Harvester node 2</td></tr>\n<tr><td>harvester3.iamworkin.lan</td><td><span class=\"ip\">10.0.56.13</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.13')\">copy</button></td><td>Harvester node 3</td></tr>\n</tbody>\n</table>\n<h3>NOC Services (noc1)</h3>\n<table>\n<thead><tr><th>Hostname</th><th>IP</th><th>Role</th></tr></thead>\n<tbody>\n<tr><td>noc1.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.10')\">copy</button></td><td>NOC management node (K3s)</td></tr>\n<tr><td>acme.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>step-ca ACME CA</td></tr>\n<tr><td>pki.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>PKI cert/CRL distribution</td></tr>\n<tr><td>guac.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Apache Guacamole</td></tr>\n<tr><td>grafana.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Grafana monitoring</td></tr>\n<tr><td>prometheus.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Prometheus metrics</td></tr>\n<tr><td>cockpit.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Cockpit web console</td></tr>\n<tr><td>traefik.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Traefik dashboard (K3s)</td></tr>\n<tr><td>gitea.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Gitea Git hosting (K3s, :3000/SSH :30022)</td></tr>\n<tr><td>irc.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>UnrealIRCd IRC server (K3s, TLS 6697)</td></tr>\n<tr><td>intranet.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Lab intranet dashboard (K3s + Traefik TLS)</td></tr>\n<tr><td>zabbix.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>Zabbix monitoring (K3s, :30083, 10 hosts)</td></tr>\n<tr><td>op-connect.iamworkin.lan</td><td><span class=\"ip\">10.0.56.10</span></td><td>1Password Connect (planned)</td></tr>\n</tbody>\n</table>\n<h3>RKE2 Workload Cluster</h3>\n<table>\n<thead><tr><th>Hostname</th><th>IP</th><th>Role</th></tr></thead>\n<tbody>\n<tr><td>rke2.iamworkin.lan</td><td><span class=\"ip\">10.0.56.118</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.118')\">copy</button></td><td>RKE2 API server</td></tr>\n<tr><td>rke2-node1.iamworkin.lan</td><td><span class=\"ip\">10.0.56.118</span></td><td>RKE2 node 1 (on harvester2)</td></tr>\n<tr><td>rke2-node2.iamworkin.lan</td><td><span class=\"ip\">10.0.56.119</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.119')\">copy</button></td><td>RKE2 node 2 (on harvester1)</td></tr>\n<tr><td>rke2-node3.iamworkin.lan</td><td><span class=\"ip\">10.0.56.120</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.120')\">copy</button></td><td>RKE2 node 3 (on harvester3)</td></tr>\n<tr><td>traefik.iamworkin.lan</td><td><span class=\"ip\">10.0.56.200</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.200')\">copy</button></td><td>Traefik LoadBalancer (MetalLB)</td></tr>\n<tr><td>rke2-ingress.iamworkin.lan</td><td><span class=\"ip\">10.0.56.200</span></td><td>RKE2 ingress (MetalLB)</td></tr>\n<tr><td>argocd.iamworkin.lan</td><td><span class=\"ip\">10.0.56.200</span></td><td>ArgoCD GitOps (RKE2 via Traefik)</td></tr>\n<tr><td>test.iamworkin.lan</td><td><span class=\"ip\">10.0.56.200</span></td><td>RKE2 test workload</td></tr>\n</tbody>\n</table>\n<h3>Production / Edge Nodes</h3>\n<table>\n<thead><tr><th>Hostname</th><th>IP</th><th>Role</th></tr></thead>\n<tbody>\n<tr><td>macmini.iamworkin.lan</td><td><span class=\"ip\">10.0.57.50</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.50')\">copy</button></td><td>Mac Mini build node (Xcode)</td></tr>\n<tr><td>edge1.iamworkin.lan</td><td><span class=\"ip\">10.0.57.15</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.15')\">copy</button></td><td>Pi 5 + Hailo AI HAT+ 2</td></tr>\n<tr><td>edge2.iamworkin.lan</td><td><span class=\"ip\">10.0.57.16</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.16')\">copy</button></td><td>Pi 4 (Argon ONE, CI runner)</td></tr>\n</tbody>\n</table>\n<h3>Planned / Windows (pre-registered)</h3>\n<table>\n<thead><tr><th>Hostname</th><th>IP</th><th>Role</th></tr></thead>\n<tbody>\n<tr><td>dc1.iamworkin.lan</td><td><span class=\"ip\">10.0.56.20</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.20')\">copy</button></td><td>AD Domain Controller (planned)</td></tr>\n<tr><td>wac1.iamworkin.lan</td><td><span class=\"ip\">10.0.56.21</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.21')\">copy</button></td><td>Windows Admin Center (planned)</td></tr>\n<tr><td>rds1.iamworkin.lan</td><td><span class=\"ip\">10.0.57.20</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.20')\">copy</button></td><td>Remote Desktop Services (planned)</td></tr>\n<tr><td>iis1.iamworkin.lan</td><td><span class=\"ip\">10.0.57.21</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.21')\">copy</button></td><td>IIS Web Server (planned)</td></tr>\n<tr><td>proxy.iamworkin.lan</td><td><span class=\"ip\">10.0.56.22</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.22')\">copy</button></td><td>Squid Authenticated Proxy (planned)</td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: KUBERNETES ===== -->\n<div id=\"tab-k8s\" class=\"tab-content\">\n<h2>Kubernetes Clusters</h2>\n<h3>K3s (noc1 &mdash; Management Services)</h3>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>K3s on noc1</div>\n <ul>\n <li><strong>Node:</strong> <span class=\"ip\">10.0.56.10</span> (single-node)</li>\n <li><strong>Version:</strong> K3s v1.34.5</li>\n <li><strong>Traefik:</strong> Disabled (using NodePort)</li>\n <li><strong>ServiceLB:</strong> Disabled</li>\n <li><strong>Tools:</strong> kubectl v1.35.2, virtctl v1.7.0, helm v3.20.0</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">K3s Services</div>\n <ul>\n <li><span class=\"dot dot-green\"></span>Guacamole &mdash; :30080</li>\n <li><span class=\"dot dot-green\"></span>PKI Web &mdash; :30081</li>\n <li><span class=\"dot dot-green\"></span>Gitea &mdash; :3000 (SSH :30022)</li>\n <li><span class=\"dot dot-green\"></span>UnrealIRCd + Anope &mdash; :6667/:6697</li>\n <li><span class=\"dot dot-green\"></span>Zabbix &mdash; :30083</li>\n </ul>\n </div>\n</div>\n<h3>Harvester HCI (VM Platform)</h3>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Harvester Cluster</div>\n <ul>\n <li><strong>Dashboard:</strong> <a href=\"https://10.0.56.14\" target=\"_blank\">https://10.0.56.14</a> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.14')\">copy</button></li>\n <li><strong>Credentials:</strong> <code>admin</code> / <code class=\"pw\">getup-billion4AGAINST</code> <button class=\"copy-btn\" onclick=\"copyText('getup-billion4AGAINST')\">copy</button></li>\n <li><strong>Version:</strong>\
\ DECOMMISSIONED &mdash; Replaced by Bare-Metal RKE2</li>\n <li><strong>Kubeconfig:</strong> <code>/home/stoltz/.kube/rke2.yaml (WSL)</code> on noc1</li>\n <li><strong>Cluster Token:</strong> <code class=\"pw\">See 1Password</code></li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">Harvester Nodes</div>\n <ul>\n <li><span class=\"dot dot-green\"></span><strong>rke2-server:</strong> <span class=\"ip\">10.0.56.11</span> &mdash; i7-1260P / 64GB</li>\n <li><span class=\"dot dot-green\"></span><strong>rke2-agent1:</strong> <span class=\"ip\">10.0.56.12</span> &mdash; i7-1260P / 64GB</li>\n <li><span class=\"dot dot-green\"></span><strong>rke2-agent2:</strong> <span class=\"ip\">10.0.56.13</span> &mdash; i5-1340P / 64GB (bare-metal openSUSE Leap 16)</li>\n <li><strong>SSH:</strong> <code>root@10.0.56.{11,12,13}</code> (ed25519 key auth)</li>\n <li><strong>Password:</strong> <span class=\"b b-offline\">SSH Key Only</span> &mdash; See 1Password</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">Harvester Resources</div>\n <ul>\n <li><strong>VM Images:</strong> 11 (Ubuntu, openSUSE, Windows, SQL Server)</li>\n <li><strong>VM Networks:</strong> 13 (12 VLAN bridges + mgmt-untagged)</li>\n <li><strong>Storage:</strong> Longhorn (replica count 2)</li>\n <li><strong>Active VMs:</strong> rke2-node1, rke2-node2, rke2-node3</li>\n </ul>\n </div>\n</div>\n<h3>RKE2 (Workload Cluster)</h3>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>RKE2 Cluster</div>\n <ul>\n <li><strong>Version:</strong> RKE2 v1.34.5+rke2r1</li>\n <li><strong>OS:</strong> openSUSE Leap 16.0 (bare-metal)</li>\n <li><strong>CNI:</strong> Calico (VXLAN mode)</li>\n <li><strong>Pod CIDR:</strong> <span class=\"ip\">10.42.0.0/16</span></li>\n <li><strong>Service CIDR:</strong> <span class=\"ip\">10.43.0.0/16</span></li>\n <li><strong>Kubeconfig:</strong> <code>/root/.kube/rke2.yaml</code> on noc1</li>\n <li><strong>Token:</strong> <code class=\"pw\">bluejay-rke2-2026</code></li>\n <li><strong>Root Password:</strong> <code class=\"pw\">BlueJay-RKE2-2026</code> <button class=\"copy-btn\" onclick=\"copyText('BlueJay-RKE2-2026')\">copy</button></li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">RKE2 Nodes</div>\n <ul>\n <li><span class=\"dot dot-green\"></span><strong>rke2-server:</strong> <span class=\"ip\">10.0.56.11</span> (bare-metal)</li>\n <li><span class=\"dot dot-green\"></span><strong>rke2-agent1:</strong> <span class=\"ip\">10.0.56.12</span> (bare-metal)</li>\n <li><span class=\"dot dot-green\"></span><strong>rke2-agent2:</strong> <span class=\"ip\">10.0.56.13</span> (bare-metal)</li>\n <li><strong>Specs:</strong> Full NUC hardware (bare-metal)</li>\n <li><strong>SSH Key:</strong> <code>ed25519 key (stoltz@IAMWORKIN-WS)</code> (ed25519)</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">RKE2 Infrastructure</div>\n <ul>\n <li><span class=\"dot dot-green\"></span><strong>MetalLB:</strong> v0.14.9, L2 mode, pool <span class=\"ip\">10.0.56.200-220</span></li>\n <li><span class=\"dot dot-green\"></span><strong>Traefik:</strong> v3.6.9, 2 replicas, LB <span class=\"ip\">10.0.56.200</span></li>\n <li><strong>Namespaces:</strong> fc-system, fc-tenant-andrew, fc-tenant-matt, fc-tenant-dustin, fc-tenant-erik, test, traefik-system, metallb-system</li>\n <li><strong>NetworkPolicies:</strong> Applied to all 5 tenant namespaces</li>\n <li><strong>Test:</strong> nginx + IngressRoute on test.iamworkin.lan verified</li>\n </ul>\n </div>\n</div>\n<h3>pfSense Static Routes (K8s)</h3>\n<table>\n<thead><tr><th>Destination</th><th>Gateway</th><th>Purpose</th></tr></thead>\n<tbody>\n<tr><td><span class=\"ip\">10.42.0.0/16</span></td><td><span class=\"ip\">10.0.56.11</span> (rke2-server)</td><td>Pod CIDR routing</td></tr>\n<tr><td><span class=\"ip\">10.43.0.0/16</span></td><td><span class=\"ip\">10.0.56.11</span> (rke2-server)</td><td>Service CIDR routing</td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: NOC SERVICES ===== -->\n<div id=\"tab-noc\" class=\"tab-content\">\n<h2>NOC Services (noc1)</h2>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>noc1 Host</div>\n <ul>\n <li><strong>IP:</strong> <span class=\"ip\">10.0.56.10</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.56.10')\">copy</button></li>\n <li><strong>SSH:</strong> <code>root@10.0.56.10</code> <button class=\"copy-btn\" onclick=\"copyText('ssh root@10.0.56.10')\">copy</button></li>\n <li><strong>Password:</strong> <code class=\"pw\">harbor-badge-kitten-valley-falcon</code> <button class=\"copy-btn\" onclick=\"copyText('harbor-badge-kitten-valley-falcon')\">copy</button></li>\n <li><strong>OS:</strong> openSUSE Leap Micro 6.2 (immutable)</li>\n <li><strong>CPU:</strong> Intel Celeron N5105 (4C/4T)</li>\n <li><strong>RAM:</strong> 32 GB</li>\n <li><strong>Disk:</strong> 1TB NVMe (929GB free)</li>\n <li><strong>Runtimes:</strong> Podman 5.4.2, K3s v1.34.5</li>\n </ul>\n </div>\n</div>\n<h3>Service Directory</h3>\n<table>\n<thead><tr><th>Service</th><th>URL</th><th>Port</th><th>Credentials</th><th>Status</th></tr></thead>\n<tbody>\n<tr><td>Cockpit</td><td><a href=\"https://10.0.56.10:9090\" target=\"_blank\">https://10.0.56.10:9090</a></td><td>9090</td><td><code>root</code> / <code class=\"pw\">harbor-badge-kitten-valley-falcon</code></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Prometheus</td><td><a href=\"http://10.0.56.10:9091\" target=\"_blank\">http://10.0.56.10:9091</a></td><td>9091</td><td><em>No auth</em> (90-day retention)</td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Grafana</td><td><a href=\"http://10.0.56.10:3000\" target=\"_blank\">http://10.0.56.10:3000</a></td><td>3000</td><td><code>admin</code> / <code class=\"pw\">holly-pine-atlas-crane</code> <button class=\"copy-btn\" onclick=\"copyText('holly-pine-atlas-crane')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Node Exporter</td><td>http://10.0.56.10:9100</td><td>9100</td><td><em>Metrics only</em></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>SNMP Exporter</td><td>http://10.0.56.10:9116</td><td>9116</td><td><em>pfSense SNMP scraper</em></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Guacamole</td><td><a href=\"http://10.0.56.10:30080/guacamole/\" target=\"_blank\">http://10.0.56.10:30080/guacamole/</a></td><td>30080</td><td><code>guacadmin</code> / <code class=\"pw\">fern-anchor-amber-viper</code> <button class=\"copy-btn\" onclick=\"copyText('fern-anchor-amber-viper')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>step-ca ACME</td><td><a href=\"https://acme.iamworkin.lan:9443\" target=\"_blank\">https://acme.iamworkin.lan:9443</a></td><td>9443</td><td>Password: <code class=\"pw\">BlueJay-StepCA-2026</code> <button class=\"copy-btn\" onclick=\"copyText('BlueJay-StepCA-2026')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>PKI Web</td><td><a href=\"http://pki.iamworkin.lan:30081\" target=\"_blank\">http://pki.iamworkin.lan:30081</a></td><td>30081</td><td><em>Public (CRL/certs)</em></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Gitea</td><td><a href=\"https://gitea.iamworkin.lan\" target=\"_blank\">https://gitea.iamworkin.lan</a></td><td>3000</td><td><code>bluejay</code> / <code class=\"pw\">maple-wren-slate-anvil</code> <button class=\"copy-btn\" onclick=\"copyText('maple-wren-slate-anvil')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>UnrealIRCd</td><td>irc.iamworkin.lan:6697 (TLS)</td><td>6697</td><td>OPER: <code>bluejay</code> / NickServ: <code class=\"pw\">willow-heron-hawk-haven</code> <button class=\"copy-btn\" onclick=\"copyText('willow-heron-hawk-haven')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Zabbix</td><td><a href=\"https://zabbix.iamworkin.lan\" target=\"_blank\">https://zabbix.iamworkin.lan</a></td><td>30083</td><td><code>Admin</code> / <code class=\"pw\">fossil-ruby-kestrel-canyon</code> <button class=\"copy-btn\" onclick=\"copyText('fossil-ruby-kestrel-canyon')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n</tbody>\n</table>\n<h3>RKE2 Services</h3>\n<table>\n<thead><tr><th>Service</th><th>URL</th><th>Port</th><th>Credentials</th><th>Status</th></tr></thead>\n<tbody>\n<tr><td>ArgoCD</td><td><a href=\"https://argocd.iamworkin.lan\" target=\"_blank\">https://argocd.iamworkin.lan</a></td><td>443 (via Traefik)</td><td><code>admin</code> / <code class=\"pw\">6KJcJtH3SCAPrWVQ</code> <button class=\"copy-btn\" onclick=\"copyText('6KJcJtH3SCAPrWVQ')\">copy</button> &mdash; <code>bluejay</code> / <code class=\"pw\">6KJcJtH3SCAPrWVQ</code> <button class=\"copy-btn\" onclick=\"copyText('6KJcJtH3SCAPrWVQ')\">copy</button></td><td><span class=\"b b-online\">Online</span></td></tr>\n<tr><td>Traefik</td><td><a href=\"https://traefik.iamworkin.lan\" target=\"_blank\">https://traefik.iamworkin.lan</a></td><td>80/443 (MetalLB 10.0.56.200)</td><td><code>admin</code> / <code class=\"pw\">zenith-turret-falcon-umber</code> (BasicAuth)</td><td><span class=\"b b-online\">Online</span></td></tr>\n</tbody>\n<h3>Prometheus Alerting (8 rules)</h3>\n<p style=\"color:var(--text-muted);font-size:0.87rem;\">NodeDown, PfSenseDown, HighCPU, HighMemory, DiskSpaceLow, and 3 additional rules. 2 Grafana dashboards: Node Exporter Full + BlueJay Network Overview.</p>\n<h3>Guacamole Connection Groups (16 connections)</h3>\n<table>\n<thead><tr><th>Group</th><th>Connections</th><th>Protocol</th></tr></thead>\n<tbody>\n<tr><td><strong>Kubernetes</strong> (6)</td><td>rke2-server, rke2-agent1, rke2-agent2,\
\ noc1</td><td>SSH</td></tr>\n<tr><td><strong>Network Devices</strong> (4)</td><td>pfSense, UniFi Cloud Key, UniFi Switch, Synology WiFi</td><td>SSH</td></tr>\n<tr><td><strong>Servers</strong> (5)</td><td>noc1, Mac Mini (SSH+VNC), Edge1 Pi5, Synology NAS, Edge2 Pi4</td><td>SSH/VNC</td></tr>\n<tr><td><strong>Web Consoles</strong> (1)</td><td>Traefik Dashboard</td><td>HTTPS</td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: VPN & SECURITY ===== -->\n<div id=\"tab-vpn\" class=\"tab-content\">\n<h2>VPN &amp; Security</h2>\n<div class=\"note note-warn\"><strong>OpenVPN Status:</strong> 8 servers were configured and verified but have been <strong>cleaned out</strong> pending ISP /28 fix. CA and certificates remain in pfSense config. Will re-create bound to new tenant VIPs (.17-.20) after Frontier restores /28 routing.</div>\n<h3>OpenVPN Configuration</h3>\n<table>\n<thead><tr><th>Tenant</th><th>VIP</th><th>TUN Port</th><th>TAP Port</th><th>Tunnel (TUN)</th><th>Tunnel (TAP)</th><th>VLAN</th></tr></thead>\n<tbody>\n<tr><td><span class=\"b b-tenant\">ANDREW</span></td><td><span class=\"ip\">.17</span></td><td>1194/UDP</td><td>1195/UDP</td><td><span class=\"ip\">10.0.68.0/27</span></td><td><span class=\"ip\">10.0.68.128/27</span></td><td>60</td></tr>\n<tr><td><span class=\"b b-tenant\">MATT</span></td><td><span class=\"ip\">.18</span></td><td>1194/UDP</td><td>1195/UDP</td><td><span class=\"ip\">10.0.68.32/27</span></td><td><span class=\"ip\">10.0.68.160/27</span></td><td>61</td></tr>\n<tr><td><span class=\"b b-tenant\">DUSTIN</span></td><td><span class=\"ip\">.19</span></td><td>1194/UDP</td><td>1195/UDP</td><td><span class=\"ip\">10.0.68.64/27</span></td><td><span class=\"ip\">10.0.68.192/27</span></td><td>62</td></tr>\n<tr><td><span class=\"b b-tenant\">ERIK</span></td><td><span class=\"ip\">.20</span></td><td>1194/UDP</td><td>1195/UDP</td><td><span class=\"ip\">10.0.68.96/27</span></td><td><span class=\"ip\">10.0.68.224/27</span></td><td>63</td></tr>\n</tbody>\n</table>\n<h3>VPN Certificate Infrastructure</h3>\n<table>\n<thead><tr><th>Component</th><th>Details</th></tr></thead>\n<tbody>\n<tr><td>CA</td><td>BlueJay VPN CA (4096-bit RSA, SHA-256, 10-year)</td></tr>\n<tr><td>Server Certs</td><td>8 (one per VPN instance, 2048-bit RSA)</td></tr>\n<tr><td>Client Certs</td><td>4 (one per tenant, 2048-bit RSA)</td></tr>\n<tr><td>TLS Auth</td><td>Shared HMAC key across all servers</td></tr>\n<tr><td>Data Ciphers</td><td>AES-256-GCM, AES-128-GCM, CHACHA20-POLY1305</td></tr>\n</tbody>\n</table>\n<h3>IPsec Site-to-Site (Planned)</h3>\n<table>\n<thead><tr><th>Tunnel</th><th>Local</th><th>Remote</th><th>Phase 1</th><th>Phase 2 SAs</th></tr></thead>\n<tbody>\n<tr><td>Matt</td><td>.29 (pfSense WAN)</td><td>Matt's public IP</td><td>IKEv2, AES-256-GCM, DH 14+</td><td>MATT (10.0.61.0/24) + PROD (10.0.57.0/24)</td></tr>\n<tr><td>Dustin</td><td>.29 (pfSense WAN)</td><td>Dustin's public IP</td><td>IKEv2, AES-256-GCM, DH 14+</td><td>DUSTIN (10.0.62.0/24) + PROD (10.0.57.0/24)</td></tr>\n</tbody>\n</table>\n<h3>PKI Hierarchy</h3>\n<table>\n<thead><tr><th>CA</th><th>Status</th><th>Purpose</th></tr></thead>\n<tbody>\n<tr><td>Root CA (IAmWorkin ACME CA)</td><td><span class=\"b b-online\">Operational</span></td><td>Trust anchor, ECDSA P-256, expires 2036</td></tr>\n<tr><td>ACME CA (step-ca on noc1)</td><td><span class=\"b b-online\">Operational</span></td><td>Automated cert issuance via ACME protocol</td></tr>\n<tr><td>Network CA</td><td><span class=\"b b-planned\">Planned</span></td><td>Switch, AP, pfSense device certs</td></tr>\n<tr><td>Windows AD CS CA</td><td><span class=\"b b-planned\">Planned</span></td><td>Domain-joined machine/user certs</td></tr>\n<tr><td>Internal Services CA</td><td><span class=\"b b-planned\">Planned</span></td><td>K8s service mesh, inter-service mTLS</td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: EDGE NODES ===== -->\n<div id=\"tab-edge\" class=\"tab-content\">\n<h2>Edge Nodes</h2>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>edge1 &mdash; Raspberry Pi 5 + Hailo AI</div>\n <ul>\n <li><strong>IP:</strong> <span class=\"ip\">10.0.57.15</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.15')\">copy</button> (PROD VLAN 57)</li>\n <li><strong>SSH:</strong> <code>stoltz@10.0.57.15</code> <button class=\"copy-btn\" onclick=\"copyText('ssh stoltz@10.0.57.15')\">copy</button></li>\n <li><strong>Password:</strong> <code class=\"pw\">lemon-torch-ruby-raven</code> <button class=\"copy-btn\" onclick=\"copyText('lemon-torch-ruby-raven')\">copy</button></li>\n <li><strong>Hardware:</strong> Pi 5 16GB + Hailo-10H 40 TOPS</li>\n <li><strong>OS:</strong> Debian 13 (trixie) aarch64</li>\n <li><strong>PCIe:</strong> Gen 3 x1 (8.0 GT/s)</li>\n <li><strong>Power:</strong> 27W USB-C</li>\n <li><strong>.NET SDK:</strong> 10.0.103</li>\n <li><strong>GitHub Runner:</strong> v2.332.0 (labels: pi5, hailo)</li>\n <li><strong>Node Exporter:</strong> :9100</li>\n <li><strong>Switch Port:</strong> 13</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>edge2 &mdash; Raspberry Pi 4 (Argon ONE)</div>\n <ul>\n <li><strong>IP:</strong> <span class=\"ip\">10.0.57.16</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.16')\">copy</button> (PROD VLAN 57)</li>\n <li><strong>SSH:</strong> <code>stoltz@10.0.57.16</code> <button class=\"copy-btn\" onclick=\"copyText('ssh stoltz@10.0.57.16')\">copy</button></li>\n <li><strong>Password:</strong> <code class=\"pw\">nebula-cipher-indigo-tango</code></li>\n <li><strong>Hardware:</strong> Pi 4 Model B 4GB, Argon ONE case</li>\n <li><strong>OS:</strong> Debian 13 (trixie) aarch64</li>\n <li><strong>Fan Control:</strong> argononed.service</li>\n <li><strong>.NET SDK:</strong> 10.0.103</li>\n <li><strong>GitHub Runner:</strong> v2.332.0 (labels: pi4, ci-runner)</li>\n <li><strong>Node Exporter:</strong> :9100</li>\n <li><strong>Switch Port:</strong> 11</li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Mac Mini (Build/Test Node)</div>\n <ul>\n <li><strong>IP:</strong> <span class=\"ip\">10.0.57.50</span> <button class=\"copy-btn\" onclick=\"copyText('10.0.57.50')\">copy</button> (PROD VLAN 57)</li>\n <li><strong>SSH:</strong> <code>bluejay@10.0.57.50</code> <button class=\"copy-btn\" onclick=\"copyText('ssh bluejay@10.0.57.50')\">copy</button></li>\n <li><strong>Password:</strong> <code class=\"pw\">indigene-new-neptune-nuthatch</code> <button class=\"copy-btn\" onclick=\"copyText('indigene-new-neptune-nuthatch')\">copy</button></li>\n <li><strong>VNC Password:</strong> <code class=\"pw\">tacokisses</code> <button class=\"copy-btn\" onclick=\"copyText('tacokisses')\">copy</button></li>\n <li><strong>Role:</strong> Xcode builds, automated browser/app testing</li>\n </ul>\n </div>\n</div>\n</div>\n\n<!-- ===== TAB: WIFI ===== -->\n<div id=\"tab-wifi\" class=\"tab-content\">\n<h2>WiFi Networks</h2>\n<div class=\"note\"><strong>Credentials:</strong> All WiFi passwords are stored in the <strong>IAmWorkin</strong> vault on 1Password. To connect a device, open the 1Password app, find the WiFi entry, and scan the QR code from there. Passwords are not stored in this page for security.</div>\n<div class=\"note note-warn\"><strong>QR Code Connection:</strong> Open 1Password &rarr; search for the SSID name &rarr; tap &ldquo;Show QR Code&rdquo; &rarr; scan with your device camera. The QR code encodes the full <code>WIFI:T:WPA;S:{SSID};P:{PASSWORD};;;</code> connection string.</div>\n\n<div class=\"wifi-grid\" id=\"wifiGrid\">\n <!-- BlueJay-Home -->\n <div class=\"wifi-card\" data-ssid=\"BlueJay-Home\" data-vlan=\"58\">\n <div class=\"wifi-card-header\" style=\"border-color: var(--purple);\">\n <div class=\"wifi-ssid\">BlueJay-Home</div>\n <div class=\"wifi-vlan\"><span class=\"b b-home\">HOME (VLAN 58)</span></div>\n </div>\n <div class=\"wifi-qr wifi-qr-placeholder\">\n <div class=\"qr-placeholder-box\">\n <svg viewBox=\"0 0 24 24\" width=\"36\" height=\"36\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>\n <rect x=\"14\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"14\" y=\"18\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"18\" width=\"3\" height=\"3\"/>\n <rect x=\"5\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"16\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"5\" y=\"16\" width=\"3\" height=\"3\"/>\n </svg>\n <span class=\"qr-placeholder-text\">Scan from 1Password app</span>\n </div>\n </div>\n <div class=\"wifi-details\">\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">SSID</span>\n <span class=\"wifi-value\"><code>BlueJay-Home</code></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">VLAN</span>\n <span class=\"wifi-value\">58 (untagged on AP)</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Security</span>\n <span class=\"wifi-value\">WPA2/WPA3</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Password</span>\n <span class=\"wifi-value\"><span class=\"b b-planned\">See 1Password</span></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Purpose</span>\n <span class=\"wifi-value\">Home network &mdash; personal / family use</span>\n </div>\n <div class=\"\
wifi-field\">\n <span class=\"wifi-label\">Bandwidth</span>\n <span class=\"wifi-value\">800 / 800 Mbps</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Public IP</span>\n <span class=\"wifi-value\"><span class=\"ip\">74.40.140.29</span></span>\n </div>\n </div>\n </div>\n\n <!-- BlueJay-Employee -->\n <div class=\"wifi-card\" data-ssid=\"BlueJay-Employee\" data-vlan=\"59\">\n <div class=\"wifi-card-header\" style=\"border-color: var(--cyan);\">\n <div class=\"wifi-ssid\">BlueJay-Employee</div>\n <div class=\"wifi-vlan\"><span class=\"b b-wifi\">EMPLOYEE (VLAN 59)</span></div>\n </div>\n <div class=\"wifi-qr wifi-qr-placeholder\">\n <div class=\"qr-placeholder-box\">\n <svg viewBox=\"0 0 24 24\" width=\"36\" height=\"36\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>\n <rect x=\"14\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"14\" y=\"18\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"18\" width=\"3\" height=\"3\"/>\n <rect x=\"5\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"16\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"5\" y=\"16\" width=\"3\" height=\"3\"/>\n </svg>\n <span class=\"qr-placeholder-text\">Scan from 1Password app</span>\n </div>\n </div>\n <div class=\"wifi-details\">\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">SSID</span>\n <span class=\"wifi-value\"><code>BlueJay-Employee</code></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">VLAN</span>\n <span class=\"wifi-value\">59</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Security</span>\n <span class=\"wifi-value\">WPA2/WPA3</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Password</span>\n <span class=\"wifi-value\"><span class=\"b b-planned\">See 1Password</span></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Purpose</span>\n <span class=\"wifi-value\">Employee network &mdash; staff device access</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Bandwidth</span>\n <span class=\"wifi-value\">500 / 500 Mbps</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Public IP</span>\n <span class=\"wifi-value\"><span class=\"ip\">74.40.140.28</span> (shared)</span>\n </div>\n </div>\n </div>\n\n <!-- BlueJay-Work -->\n <div class=\"wifi-card\" data-ssid=\"BlueJay-Work\" data-vlan=\"64\">\n <div class=\"wifi-card-header\" style=\"border-color: var(--accent);\">\n <div class=\"wifi-ssid\">BlueJay-Work</div>\n <div class=\"wifi-vlan\"><span class=\"b b-wifi\">WORK (VLAN 64)</span></div>\n </div>\n <div class=\"wifi-qr wifi-qr-placeholder\">\n <div class=\"qr-placeholder-box\">\n <svg viewBox=\"0 0 24 24\" width=\"36\" height=\"36\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>\n <rect x=\"14\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"14\" y=\"18\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"18\" width=\"3\" height=\"3\"/>\n <rect x=\"5\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"16\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"5\" y=\"16\" width=\"3\" height=\"3\"/>\n </svg>\n <span class=\"qr-placeholder-text\">Scan from 1Password app</span>\n </div>\n </div>\n <div class=\"wifi-details\">\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">SSID</span>\n <span class=\"wifi-value\"><code>BlueJay-Work</code></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">VLAN</span>\n <span class=\"wifi-value\">64</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Security</span>\n <span class=\"wifi-value\">WPA2/WPA3</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Password</span>\n <span class=\"wifi-value\"><span class=\"b b-planned\">See 1Password</span></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Purpose</span>\n <span class=\"wifi-value\">Work network &mdash; business devices</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Bandwidth</span>\n <span class=\"wifi-value\">500 / 500 Mbps</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Public IP</span>\n <span class=\"wifi-value\"><span class=\"ip\">74.40.140.28</span> (shared)</span>\n </div>\n </div>\n </div>\n\n <!-- BlueJay-School -->\n <div class=\"wifi-card\" data-ssid=\"BlueJay-School\" data-vlan=\"65\">\n <div class=\"wifi-card-header\" style=\"border-color: var(--green);\">\n <div class=\"wifi-ssid\">BlueJay-School</div>\n <div class=\"wifi-vlan\"><span class=\"b b-wifi\">SCHOOL (VLAN 65)</span></div>\n </div>\n <div class=\"wifi-qr wifi-qr-placeholder\">\n <div class=\"qr-placeholder-box\">\n <svg viewBox=\"0 0 24 24\" width=\"36\" height=\"36\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>\n <rect x=\"14\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"14\" y=\"18\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"18\" width=\"3\" height=\"3\"/>\n <rect x=\"5\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"16\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"5\" y=\"16\" width=\"3\" height=\"3\"/>\n </svg>\n <span class=\"qr-placeholder-text\">Scan from 1Password app</span>\n </div>\n </div>\n <div class=\"wifi-details\">\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">SSID</span>\n <span class=\"wifi-value\"><code>BlueJay-School</code></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">VLAN</span>\n <span class=\"wifi-value\">65</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Security</span>\n <span class=\"wifi-value\">WPA2/WPA3</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Password</span>\n <span class=\"wifi-value\"><span class=\"b b-planned\">See 1Password</span></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Purpose</span>\n <span class=\"wifi-value\">School network &mdash; student devices</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Bandwidth</span>\n <span class=\"wifi-value\">200 / 200 Mbps</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Public IP</span>\n <span class=\"wifi-value\"><span class=\"ip\">74.40.140.28</span> (shared)</span>\n </div>\n </div>\n </div>\n\n <!-- BlueJay-Guest -->\n <div class=\"wifi-card\" data-ssid=\"BlueJay-Guest\" data-vlan=\"66\">\n <div class=\"wifi-card-header\" style=\"border-color: var(--gold);\">\n <div class=\"wifi-ssid\">BlueJay-Guest</div>\n <div class=\"wifi-vlan\"><span class=\"b b-spare\">GUEST (VLAN 66)</span></div>\n </div>\n <div class=\"wifi-qr wifi-qr-placeholder wifi-qr-open\">\n <div class=\"qr-placeholder-box\">\n <svg viewBox=\"0 0 24 24\" width=\"36\" height=\"36\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\"/>\n <rect x=\"14\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"14\" width=\"3\" height=\"3\"/><rect x=\"14\" y=\"18\" width=\"3\" height=\"3\"/><rect x=\"18\" y=\"18\" width=\"3\" height=\"3\"/>\n <rect x=\"5\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"16\" y=\"5\" width=\"3\" height=\"3\"/><rect x=\"5\" y=\"16\" width=\"3\" height=\"3\"/>\n </svg>\n <span class=\"qr-placeholder-text\">Open network &mdash; no password required</span>\n </div>\n </div>\n <div class=\"wifi-details\">\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">SSID</span>\n <span class=\"wifi-value\"><code>BlueJay-Guest</code></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">VLAN</span>\n <span class=\"wifi-value\">66</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Security</span>\n <span class=\"wifi-value\">Open / Captive Portal</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Password</span>\n <span class=\"wifi-value\"><span class=\"b b-online\">None (open)</span></span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Purpose</span>\n <span class=\"wifi-value\"\
>Guest WiFi &mdash; fully isolated, NAT only</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Bandwidth</span>\n <span class=\"wifi-value\">100 / 50 Mbps</span>\n </div>\n <div class=\"wifi-field\">\n <span class=\"wifi-label\">Public IP</span>\n <span class=\"wifi-value\"><span class=\"ip\">74.40.140.28</span> (shared)</span>\n </div>\n </div>\n </div>\n</div>\n\n<h3>WiFi Access Point</h3>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Synology RT6600AX (AP Mode)</div>\n <ul>\n <li><strong>Management:</strong> <a href=\"http://10.0.58.2:8000\" target=\"_blank\">http://10.0.58.2:8000</a></li>\n <li><strong>Credentials:</strong> <code>bluejay</code> / <code class=\"pw\">galileo_parisian8ADMIRE</code> <button class=\"copy-btn\" onclick=\"copyText('galileo_parisian8ADMIRE')\">copy</button></li>\n <li><strong>Mode:</strong> Access Point (bridge mode), all trunk ports enabled</li>\n <li><strong>Bands:</strong> Wi-Fi 6E (2.4 GHz + 5 GHz + 6 GHz)</li>\n <li><strong>Switch Port:</strong> 3 (trunk, native VLAN 58)</li>\n </ul>\n </div>\n</div>\n\n<div class=\"note\"><strong>Network Isolation:</strong> Each SSID maps to a separate VLAN with independent firewall rules and bandwidth limits. GUEST is fully isolated with NAT &mdash; no access to internal resources. EMPLOYEE, WORK, and SCHOOL share public IP .28 with traffic shaping.</div>\n</div>\n\n<!-- ===== TAB: CREDENTIALS ===== -->\n<div id=\"tab-credentials\" class=\"tab-content\">\n<h2>Credentials &amp; 1Password</h2>\n<div class=\"card-grid\">\n <div class=\"card\" style=\"border-top: 3px solid var(--accent);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>1Password Connect Server</div>\n <ul>\n <li><strong>API:</strong> <span class=\"ip\">http://10.0.56.10:8180</span> <button class=\"copy-btn\" onclick=\"copyText('http://10.0.56.10:8180')\">copy</button></li>\n <li><strong>Sync:</strong> <span class=\"ip\">http://10.0.56.10:8181</span></li>\n <li><strong>Host:</strong> noc1 (Podman containers)</li>\n <li><strong>Status:</strong> <span class=\"b b-online\">Online</span></li>\n </ul>\n </div>\n <div class=\"card\" style=\"border-top: 3px solid var(--accent);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>1Password K8s Operator</div>\n <ul>\n <li><strong>Namespace:</strong> <code>onepassword-system</code></li>\n <li><strong>Chart:</strong> 1password/connect v2.3.0</li>\n <li><strong>Operator:</strong> v1.11.0</li>\n <li><strong>Poll Interval:</strong> 600s</li>\n <li><strong>Status:</strong> <span class=\"b b-online\">Online</span></li>\n </ul>\n </div>\n <div class=\"card\" style=\"border-top: 3px solid var(--gold);\">\n <div class=\"card-title\">IAmWorkin Vault</div>\n <ul>\n <li><strong>Vault Name:</strong> IAmWorkin</li>\n <li><strong>Items:</strong> 26+ credentials</li>\n <li><strong>Rotation:</strong> Quarterly (Jan/Apr/Jul/Oct)</li>\n <li><strong>Script:</strong> <code>/opt/scripts/rotate-credentials.sh</code></li>\n <li><strong>Timer:</strong> <code>credential-rotation.timer</code></li>\n </ul>\n </div>\n</div>\n\n<div class=\"note\"><strong>All infrastructure credentials are managed in 1Password.</strong> The IAmWorkin vault contains credentials for every service listed on this intranet. K8s workloads (Zabbix, Matrix, Guacamole, Mail, IRC, Gitea, ArgoCD) sync secrets automatically via OnePasswordItem CRDs. Credential rotation runs quarterly via systemd timer.</div>\n\n<h3>K8s Secret Sync (OnePasswordItem CRDs)</h3>\n<table>\n<thead><tr><th>Namespace</th><th>Secret Name</th><th>Source (1Password Item)</th><th>Status</th></tr></thead>\n<tbody>\n<tr><td>zabbix</td><td>zabbix-credentials</td><td>Zabbix Monitoring</td><td><span class=\"b b-online\">Synced</span></td></tr>\n<tr><td>matrix</td><td>matrix-credentials</td><td>Matrix Synapse</td><td><span class=\"b b-online\">Synced</span></td></tr>\n<tr><td>guacamole</td><td>guacamole-credentials</td><td>Apache Guacamole</td><td><span class=\"b b-online\">Synced</span></td></tr>\n<tr><td>mail</td><td>mail-credentials</td><td>Mail Server</td><td><span class=\"b b-online\">Synced</span></td></tr>\n<tr><td>irc</td><td>irc-credentials</td><td>IRC Services</td><td><span class=\"b b-online\">Synced</span></td></tr>\n<tr><td>gitea</td><td>gitea-credentials</td><td>Gitea</td><td><span class=\"b b-online\">Synced</span></td></tr>\n<tr><td>argocd</td><td>argocd-credentials</td><td>ArgoCD</td><td><span class=\"b b-online\">Synced</span></td></tr>\n</tbody>\n</table>\n\n<h3>Credential Rotation</h3>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\">Rotation Script</div>\n <ul>\n <li><strong>Path:</strong> <code>/opt/scripts/rotate-credentials.sh</code></li>\n <li><strong>Usage:</strong> <code>rotate-credentials.sh {service|all} [--dry-run]</code></li>\n <li><strong>Services:</strong> grafana, guacamole, zabbix, argocd, gitea, snappymail, traefik, matrix, harvester</li>\n <li><strong>Schedule:</strong> Quarterly (1st of Jan/Apr/Jul/Oct at 03:00 UTC)</li>\n <li><strong>Log:</strong> <code>/var/log/credential-rotation.log</code></li>\n </ul>\n </div>\n</div>\n</div>\n\n<!-- ===== TAB: PLANNED ===== -->\n<div id=\"tab-planned\" class=\"tab-content\">\n<h2>Planned Services</h2>\n<table>\n<thead><tr><th>Service</th><th>IP</th><th>Host</th><th>Role</th><th>Status</th></tr></thead>\n<tbody>\n<tr><td>Windows DC1</td><td><span class=\"ip\">10.0.56.20</span></td><td>Harvester VM</td><td>AD Domain Controller (iamworkin.lan)</td><td><span class=\"b b-planned\">Planned</span></td></tr>\n<tr><td>Windows WAC1</td><td><span class=\"ip\">10.0.56.21</span></td><td>Harvester VM</td><td>Windows Admin Center</td><td><span class=\"b b-planned\">Planned</span></td></tr>\n<tr><td>Windows RDS1</td><td><span class=\"ip\">10.0.57.20</span></td><td>Harvester VM</td><td>Remote Desktop Services</td><td><span class=\"b b-planned\">Planned</span></td></tr>\n<tr><td>Windows IIS1</td><td><span class=\"ip\">10.0.57.21</span></td><td>Harvester VM</td><td>IIS Web Server</td><td><span class=\"b b-planned\">Planned</span></td></tr>\n<tr><td>Gitea</td><td><span class=\"ip\">10.0.56.10</span></td><td>K3s (noc1)</td><td>Git hosting (gitea.iamworkin.lan)</td><td><span class=\"b b-live\">Live</span></td></tr>\n<tr><td>UnrealIRCd</td><td><span class=\"ip\">10.0.56.10</span></td><td>K3s (noc1)</td><td>IRC server (irc.iamworkin.lan:6697)</td><td><span class=\"b b-live\">Live</span></td></tr>\n<tr><td>Zabbix</td><td><span class=\"ip\">10.0.56.10</span></td><td>K3s (noc1)</td><td>Network monitoring (zabbix.iamworkin.lan, 10 hosts)</td><td><span class=\"b b-live\">Live</span></td></tr>\n<tr><td>1Password Connect</td><td><span class=\"ip\">10.0.56.10</span></td><td>noc1</td><td>Secrets management API</td><td><span class=\"b b-planned\">Planned</span></td></tr>\n<tr><td>Squid Proxy</td><td><span class=\"ip\">10.0.56.22</span></td><td>Harvester VM</td><td>Authenticated web proxy (Kerberos/LDAP)</td><td><span class=\"b b-planned\">Planned</span></td></tr>\n<tr><td>ArgoCD</td><td><span class=\"ip\">10.0.56.200</span></td><td>RKE2 (via Traefik)</td><td>GitOps for K8s workloads (argocd.iamworkin.lan)</td><td><span class=\"b b-live\">Live</span></td></tr>\n</tbody>\n</table>\n</div>\n\n<!-- ===== TAB: TOPOLOGY ===== -->\n<div id=\"tab-topology\" class=\"tab-content\">\n<h2>Network Topology</h2>\n<div style=\"display:flex;flex-direction:column;align-items:center;gap:8px;padding:1.5rem;\">\n\n<div class=\"card\" style=\"text-align:center;max-width:280px;border-color:var(--text-muted);\"><div class=\"card-title\" style=\"justify-content:center;\">Internet</div></div>\n<div style=\"width:2px;height:16px;background:var(--border-accent);\"></div>\n<div class=\"card\" style=\"text-align:center;max-width:320px;\"><div class=\"card-title\" style=\"justify-content:center;\"><span class=\"dot dot-green\"></span>Frontier ONT + NVG468MQ Modem</div><p><span class=\"ip\">WAN: 74.32.187.152</span> &bull; <span class=\"ip\">/28: .17-.29</span></p><p style=\"font-size:0.78rem;\">192.168.254.254 &bull; DMZ to pfSense</p></div>\n<div style=\"width:2px;height:16px;background:var(--border-accent);\"></div>\n<div class=\"card\" style=\"text-align:center;max-width:360px;border-color:var(--accent);\"><div class=\"card-title\" style=\"justify-content:center;\"><span class=\"dot dot-green\"></span>pfSense Netgate 4100</div><p><span class=\"ip\">WAN: ix3 (.122)</span> &bull; <span class=\"ip\">LAN: igc0</span> (802.1Q trunk)</p><p style=\"font-size:0.78rem;\">12 VLANs &bull; 36 aliases &bull; 90 rules &bull; DNS/DHCP/NTP/SNMP</p></div>\n<div style=\"width:2px;height:16px;background:var(--border-accent);\"></div>\n<div class=\"card\" style=\"text-align:center;max-width:340px;\"><div class=\"card-title\" style=\"justify-content:center;\"><span class=\"dot dot-green\"></span>UniFi USW-Lite-16-PoE Switch</div><p><span class=\"ip\">10.0.56.2</span> &bull; 16 ports &bull; VLANs 56-67</p></div>\n<div style=\"width:2px;height:8px;background:var(--border-accent);\"></div>\n\n<div style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;width:100%;max-width:1200px;\">\n <div class=\"card\" style=\"border-left:3px solid var(--accent);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>noc1</div>\n <p><span class=\"ip\">10.0.56.10</span> <span class=\"b b-mgmt\">MGMT</span></p>\n <p style=\"font-size:0.78rem;\">Celeron N5105 &bull; 32GB &bull; K3s + Podman</p>\n <ul style=\"font-size:0.78rem;\">\n <li>Guacamole :30080</li><li>Grafana :3000</li><li>Prometheus :9091</li>\n <li>step-ca :9443</li><li>Gitea :3000</li><li>IRC :6697</li>\n <li>Zabbix :30083</li><li>Cockpit\
\ :9090</li><li>Puppet :8140</li>\n </ul>\n </div>\n <div class=\"card\" style=\"border-left:3px solid var(--green);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Harvester Cluster</div>\n <p><span class=\"ip\">VIP: 10.0.56.14</span> <span class=\"b b-mgmt\">MGMT</span></p>\n <ul style=\"font-size:0.78rem;\">\n <li>harvester1: <span class=\"ip\">.11</span> (i7-1260P/64GB)</li>\n <li>harvester2: <span class=\"ip\">.12</span> (i7-1260P/64GB)</li>\n <li>harvester3: <span class=\"ip\">.13</span> (i5-1340P/64GB)</li>\n </ul>\n <p style=\"font-size:0.78rem;\">Harvester v1.7.1 &bull; Longhorn &bull; Rancher embedded</p>\n </div>\n <div class=\"card\" style=\"border-left:3px solid var(--purple);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>RKE2 Workload Cluster</div>\n <p><span class=\"ip\">Traefik: 10.0.56.200</span> <span class=\"b b-mgmt\">MGMT</span></p>\n <ul style=\"font-size:0.78rem;\">\n <li>rke2-node1: <span class=\"ip\">.118</span> (on harvester2)</li>\n <li>rke2-node2: <span class=\"ip\">.119</span> (on harvester1)</li>\n <li>rke2-node3: <span class=\"ip\">.120</span> (on harvester3)</li>\n </ul>\n <p style=\"font-size:0.78rem;\">Calico &bull; MetalLB &bull; Traefik v3.6.10 &bull; ArgoCD</p>\n </div>\n <div class=\"card\" style=\"border-left:3px solid var(--cyan);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>WiFi (Synology RT6600AX)</div>\n <p><span class=\"ip\">10.0.58.2</span> <span class=\"b b-home\">HOME</span></p>\n <ul style=\"font-size:0.78rem;\">\n <li>BlueJay-Home (untagged)</li><li>BlueJay-Employee (VLAN 59)</li>\n <li>BlueJay-Work (VLAN 64)</li><li>BlueJay-School (VLAN 65)</li>\n <li>BlueJay-Guest (VLAN 66)</li>\n </ul>\n </div>\n <div class=\"card\" style=\"border-left:3px solid var(--orange);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>PROD Nodes</div>\n <p><span class=\"b b-prod\">PROD VLAN 57</span></p>\n <ul style=\"font-size:0.78rem;\">\n <li>Mac Mini: <span class=\"ip\">10.0.57.50</span> (Xcode)</li>\n <li>edge1 Pi5: <span class=\"ip\">10.0.57.15</span> (Hailo AI)</li>\n <li>edge2 Pi4: <span class=\"ip\">10.0.57.16</span> (CI runner)</li>\n </ul>\n </div>\n <div class=\"card\" style=\"border-left:3px solid var(--gold);\">\n <div class=\"card-title\"><span class=\"dot dot-green\"></span>Network Devices</div>\n <ul style=\"font-size:0.78rem;\">\n <li>Cloud Key: <span class=\"ip\">10.0.56.3</span></li>\n <li>NAS: <span class=\"ip\">10.0.58.3</span></li>\n <li>Modem: <span class=\"ip\">192.168.254.254</span></li>\n </ul>\n </div>\n</div>\n</div>\n</div>\n\n<!-- ===== TAB: DOMAINS ===== -->\n<div id=\"tab-domains\" class=\"tab-content\">\n<h2>Domains</h2>\n<div class=\"summary-grid\">\n <div class=\"summary-card\"><div class=\"summary-number\">17</div><div class=\"summary-label\">Registered Domains</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">1</div><div class=\"summary-label\">Internal Domain</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">1</div><div class=\"summary-label\">Blog Hosting (DreamHost)</div></div>\n <div class=\"summary-card\"><div class=\"summary-number\">18</div><div class=\"summary-label\">Total Domains</div></div>\n</div>\n\n<h3>FlowerCore Domains</h3>\n<table>\n<thead><tr><th>Domain</th><th>Category</th><th>Owner</th><th>Purpose</th><th>DNS Provider</th><th>Registrar</th></tr></thead>\n<tbody>\n<tr><td><strong>flowercore.io</strong></td><td><span class=\"b b-prod\">FlowerCore</span></td><td>Andrew</td><td>Production API</td><td>Cloudflare</td><td>Namecheap</td></tr>\n<tr><td><strong>flowerinsider.xyz</strong></td><td><span class=\"b b-prod\">FlowerCore</span></td><td>Andrew</td><td>Dev/staging</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>flowerinsider.com</strong></td><td><span class=\"b b-prod\">FlowerCore Co</span></td><td>Andrew</td><td>Company site</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>flowerinsider.nl</strong></td><td><span class=\"b b-prod\">FlowerCore Co</span></td><td>Andrew</td><td>Dutch site</td><td>Namecheap</td><td>Namecheap</td></tr>\n</tbody>\n</table>\n\n<h3>Work Domains</h3>\n<table>\n<thead><tr><th>Domain</th><th>Category</th><th>Owner</th><th>Purpose</th><th>DNS Provider</th><th>Registrar</th></tr></thead>\n<tbody>\n<tr><td><strong>iamwork.in</strong></td><td><span class=\"b b-wifi\">Work</span></td><td>Andrew</td><td>Employee portal, IVR</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>iamworkin.com</strong></td><td><span class=\"b b-wifi\">Work</span></td><td>Andrew</td><td>Redirect</td><td>Namecheap</td><td>Namecheap</td></tr>\n</tbody>\n</table>\n\n<h3>Personal &amp; Tenant Domains</h3>\n<table>\n<thead><tr><th>Domain</th><th>Category</th><th>Owner</th><th>Purpose</th><th>DNS Provider</th><th>Registrar</th></tr></thead>\n<tbody>\n<tr><td><strong>ackeroni.com</strong></td><td><span class=\"b b-tenant\">Erik</span></td><td>Erik</td><td>Personal</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>erckak.com</strong></td><td><span class=\"b b-tenant\">Erik</span></td><td>Erik</td><td>Personal</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>erckak.dev</strong></td><td><span class=\"b b-tenant\">Erik</span></td><td>Erik</td><td>Developer portfolio</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>digirido.com</strong></td><td><span class=\"b b-spare\">Random</span></td><td>Andrew</td><td>DigiKey testing</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>timeforta.co</strong></td><td><span class=\"b b-tenant\">Dustin</span></td><td>Dustin</td><td>Personal</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>shenanjia.com</strong></td><td><span class=\"b b-home\">Wife</span></td><td>Wife</td><td>Personal site</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>bluejay.api</strong></td><td><span class=\"b b-mgmt\">Personal Fun</span></td><td>Andrew</td><td>API experiments</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>bluejay.dev</strong></td><td><span class=\"b b-mgmt\">Personal Fun</span></td><td>Andrew</td><td>Dev projects</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>jayblue.dev</strong></td><td><span class=\"b b-mgmt\">Personal Fun</span></td><td>Andrew</td><td>Dev projects</td><td>Namecheap</td><td>Namecheap</td></tr>\n<tr><td><strong>z.orb</strong></td><td><span class=\"b b-spare\">Random</span></td><td>Andrew</td><td>Short URL</td><td>Namecheap</td><td>Namecheap</td></tr>\n</tbody>\n</table>\n\n<h3>Blog &amp; Content Domains</h3>\n<table>\n<thead><tr><th>Domain</th><th>Category</th><th>Owner</th><th>Purpose</th><th>DNS Provider</th><th>Registrar</th></tr></thead>\n<tbody>\n<tr><td><strong>pebbleandpeanut.com</strong></td><td><span class=\"b b-home\">Blog</span></td><td>Andrew</td><td>Personal blog</td><td>DreamHost</td><td>Namecheap</td></tr>\n<tr><td><strong>pebblesandpeanuts.com</strong></td><td><span class=\"b b-home\">Blog</span></td><td>Andrew</td><td>Alt redirect</td><td>Namecheap</td><td>Namecheap</td></tr>\n</tbody>\n</table>\n\n<h3>Internal Domain</h3>\n<table>\n<thead><tr><th>Domain</th><th>Category</th><th>Owner</th><th>Purpose</th><th>DNS Provider</th><th>Notes</th></tr></thead>\n<tbody>\n<tr><td><strong>iamworkin.lan</strong></td><td><span class=\"b b-mgmt\">Internal</span></td><td>Andrew</td><td>Internal infrastructure, future AD DS</td><td>pfSense Unbound</td><td>43+ host overrides, not publicly registered</td></tr>\n</tbody>\n</table>\n\n<h3>Namecheap API</h3>\n<div class=\"card-grid\">\n <div class=\"card\">\n <div class=\"card-title\">API Configuration</div>\n <ul>\n <li><strong>Base URL:</strong> <code>https://api.namecheap.com/xml.response</code> <button class=\"copy-btn\" onclick=\"copyText('https://api.namecheap.com/xml.response')\">copy</button></li>\n <li><strong>API User:</strong> <code>astoltz</code> <button class=\"copy-btn\" onclick=\"copyText('astoltz')\">copy</button></li>\n <li><strong>API Key:</strong> <code class=\"pw\">e36f347844fb4cc3a82d4e0f4e4af82e</code> <button class=\"copy-btn\" onclick=\"copyText('e36f347844fb4cc3a82d4e0f4e4af82e')\">copy</button></li>\n <li><strong>Sandbox URL:</strong> <code>https://api.sandbox.namecheap.com/xml.response</code> <button class=\"copy-btn\" onclick=\"copyText('https://api.sandbox.namecheap.com/xml.response')\">copy</button></li>\n </ul>\n </div>\n <div class=\"card\">\n <div class=\"card-title\">Dynamic DNS</div>\n <ul>\n <li><strong>Hostname:</strong> <code>gateway.iamwork.in</code> <button class=\"copy-btn\" onclick=\"copyText('gateway.iamwork.in')\">copy</button></li>\n <li><strong>Points to:</strong> pfSense WAN IP (auto-updated)</li>\n <li><strong>DDNS Endpoint:</strong> <code>https://dynamicdns.park-your-domain.com/update?host=gateway&domain=iamwork.in&password=d8ad7194c9224c26bc50cfa5feb8764e</code> <button class=\"copy-btn\" onclick=\"copyText('https://dynamicdns.park-your-domain.com/update?host=gateway&domain=iamwork.in&password=d8ad7194c9224c26bc50cfa5feb8764e')\">copy</button></li>\n <li><strong>Update Method:</strong> pfSense Dynamic DNS client or cron</li>\n </ul>\n </div>\n</div>\n\n<h3>Internal DNS Architecture</h3>\n<div class=\"note\"><strong>Split-Horizon DNS (planned):</strong> External requests to flowercore.io resolve via Cloudflare to public IP .21. Internal requests resolve via pfSense Unbound to K8s MetalLB VIP (10.0.56.200), avoiding NAT hairpin. All internal infrastructure uses iamworkin.lan zone.</div>\n\n<h3>Planned IPv6 (ULA)</h3>\n<table>\n<thead><tr><th>Prefix</th><th>Scheme</th><th>Method</th></tr></thead>\n<tbody>\n<tr><td><code>fdbc:56:XX::/64</code></td><td>XX = VLAN ID (e.g., fdbc:56:56::/64 for MGMT)</td><td>SLAAC + DHCPv6 (servers),\
\ SLAAC-only (clients)</td></tr>\n</tbody>\n</table>\n</div>\n\n<script>\n/* ===== QR Code Generator (inline, air-gap safe) ===== */\n/* Minimal QR code encoder — generates canvas-based QR codes. Based on public domain QR algorithms. */\nvar QRCode=(function(){\nvar PAD0=0xEC,PAD1=0x11;\nfunction QRCode(typeNumber,errorCorrectionLevel){this.typeNumber=typeNumber;this.errorCorrectionLevel=errorCorrectionLevel;this.modules=null;this.moduleCount=0;this.dataCache=null;this.dataList=[];}\nQRCode.prototype={addData:function(data){this.dataList.push(new QR8bitByte(data));this.dataCache=null;},make:function(){if(this.typeNumber<1){var tn=1;for(tn=1;tn<40;tn++){var rs=QRRSBlock.getRSBlocks(tn,this.errorCorrectionLevel);var buf=new QRBitBuffer();for(var i=0;i<this.dataList.length;i++){var d=this.dataList[i];buf.put(d.mode,4);buf.put(d.getLength(),QRUtil.getLengthInBits(d.mode,tn));d.write(buf);}var totalDataCount=0;for(i=0;i<rs.length;i++){totalDataCount+=rs[i].dataCount;}if(buf.getLengthInBits()<=totalDataCount*8)break;}this.typeNumber=tn;}this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}if(this.dataCache==null){this.dataCache=QRCode.createData(this.typeNumber,this.errorCorrectionLevel,this.dataList);}this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}return pattern;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null)continue;this.modules[r][6]=(r%2==0);}for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null)continue;this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null)continue;for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}for(i=0;i<18;i++){mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectionLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}for(i=0;i<15;i++){mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};\nQRCode.createData=function(typeNumber,errorCorrectionLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectionLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}var totalDataCount=0;for(i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}if(buffer.getLengthInBits()>totalDataCount*8){throw new Error('code length overflow. ('+buffer.getLengthInBits()+'>'+totalDataCount*8+')');}if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}while(true){if(buffer.getLengthInBits()>=totalDataCount*8)break;buffer.put(PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8)break;buffer.put(PAD1,8);}return QRCode.createBytes(buffer,rsBlocks);};\nQRCode.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}var totalCodeCount=0;for(i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}var data=new Array(totalCodeCount);var index=0;for(i=0;i<maxDcCount;i++){for(r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}for(i=0;i<maxEcCount;i++){for(r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}return data;};\nfunction QR8bitByte(data){this.mode=4;this.data=data;}QR8bitByte.prototype={getLength:function(){return this.data.length;},write:function(buffer){for(var i=0;i<this.data.length;i++){buffer.put(this.data.charCodeAt(i),8);}}};\nvar QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case 0:return(i+j)%2==0;case 1:return i%2==0;case 2:return j%3==0;case 3:return(i+j)%3==0;case 4:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case 5:return(i*j)%2+(i*j)%3==0;case 6:return((i*j)%2+(i*j)%3)%2==0;case 7:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error('bad maskPattern:'+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){return 8;}else if(type<27){return 16;}else if(type<41){return 16;}throw new Error('type:'+type);},getLostPoint:function(qrCode){var moduleCount=qrCode.moduleCount;var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.modules[row][col];for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r)continue;for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c)continue;if(r==0&&c==0)continue;if(dark==qrCode.modules[row+r][col+c]){sameCount++;}}}if(sameCount>5){lostPoint+=(3+sameCount-5);}}}for(row=0;row<moduleCount-1;row++){for(col=0;col<moduleCount-1;col++){var count=0;if(qrCode.modules[row][col])count++;if(qrCode.modules[row+1][col])count++;if(qrCode.modules[row][col+1])count++;if(qrCode.modules[row+1][col+1])count++;if(count==0||count==4){lostPoint+=3;}}}for(row=0;row<moduleCount;row++){for(col=0;col<moduleCount-6;col++){if(qrCode.modules[row][col]&&!qrCode.modules[row][col+1]&&qrCode.modules[row][col+2]&&qrCode.modules[row][col+3]&&qrCode.modules[row][col+4]&&!qrCode.modules[row][col+5]&&qrCode.modules[row][col+6]){lostPoint+=40;}}}for(col=0;col<moduleCount;col++){for(row=0;row<moduleCount-6;row++){if(qrCode.modules[row][col]&&!qrCode.modules[row+1][col]&&qrCode.modules[row+2][col]&&qrCode.modules[row+3][col]&&qrCode.modules[row+4][col]&&!qrCode.modules[row+5][col]&&qrCode.modules[row+6][col]){lostPoint+=40;}}}var\
\ darkCount=0;for(col=0;col<moduleCount;col++){for(row=0;row<moduleCount;row++){if(qrCode.modules[row][col]){darkCount++;}}}var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};\nvar QRMath={glog:function(n){if(n<1)throw new Error('glog('+n+')');return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}while(n>=256){n-=255;}return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}for(i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}for(i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}\nfunction QRPolynomial(num,shift){if(num.length==undefined)throw new Error(num.length+'/'+shift);var offset=0;while(offset<num.length&&num[offset]==0){offset++;}this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0)return this;var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}for(i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}return new QRPolynomial(num,0).mod(e);}};\nfunction QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}\nQRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];\nQRRSBlock.getRSBlocks=function(typeNumber,errorCorrectionLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,errorCorrectionLevel);if(rsBlock==undefined){throw new Error('bad rs block @ typeNumber:'+typeNumber+'/errorCorrectionLevel:'+errorCorrectionLevel);}var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}return list;};\nQRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectionLevel){switch(errorCorrectionLevel){case 1:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case 0:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case 3:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case 2:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};\nfunction QRBitBuffer(){this.buffer=[];this.length=0;}QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}this.length++;}};\nreturn QRCode;})();\n\n/* ===== WiFi QR Code Rendering ===== */\n/* QR codes with passwords are generated via 1Password app, not stored in this page.\n The inline QR library above is retained for potential future use (e.g., guest open network QR). */\nfunction initWifiQRCodes() {\n /* Placeholder — QR codes are now served from 1Password app */\n}\n\nfunction printQRCodes() {\n /* Switch to WiFi tab, mark it for print, print, then restore */\n var allTabs = document.querySelectorAll('.tab-content');\n var wifiTab = document.getElementById('tab-wifi');\n allTabs.forEach(function(t) { t.classList.remove('print-active'); });\n if (wifiTab) { wifiTab.classList.add('print-active'); }\n window.print();\n if (wifiTab) { wifiTab.classList.remove('print-active'); }\n}\n\n/* ===== Existing Functions ===== */\nfunction toggleTheme(){var h=document.documentElement,c=h.getAttribute('data-theme'),n=c==='dark'?'light':'dark';h.setAttribute('data-theme',n);document.getElementById('themeLabel').textContent=n==='dark'?'Light':'Dark';try{localStorage.setItem('bjTheme',n)}catch(e){}}\n(function(){try{var s=localStorage.getItem('bjTheme');if(s){document.documentElement.setAttribute('data-theme',s);var l=document.getElementById('themeLabel');if(l)l.textContent=s==='dark'?'Light':'Dark'}}catch(e){}})();\nvar ALL_TABS=['overview','isp','pfsense','switching','dns','k8s','noc','vpn','edge','wifi','planned','topology','domains','credentials'];\nfunction switchTab(id,el){document.querySelectorAll('.tab-content').forEach(function(t){t.classList.remove('active')});document.querySelectorAll('.tab-btn').forEach(function(b){b.classList.remove('active')});var tgt=document.getElementById('tab-'+id);if(tgt)tgt.classList.add('active');if(el)el.classList.add('active');history.replaceState(null,null,'#'+id);window.scrollTo({top:0,behavior:'smooth'});if(id==='wifi'){setTimeout(initWifiQRCodes,50);}}\n(function(){var h=location.hash.replace('#','');if(h){var tgt=document.getElementById('tab-'+h);if(tgt){document.querySelectorAll('.tab-content').forEach(function(t){t.classList.remove('active')});document.querySelectorAll('.tab-btn').forEach(function(b){b.classList.remove('active')});tgt.classList.add('active');var idx=ALL_TABS.indexOf(h);var btns=document.querySelectorAll('.tab-btn');if(idx>=0&&btns[idx])btns[idx].classList.add('active');if(h==='wifi'){setTimeout(initWifiQRCodes,100);}}}})();\nfunction copyText(t){var btn=event.target;if(navigator.clipboard){navigator.clipboard.writeText(t).then(function(){btn.textContent='copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='copy';btn.classList.remove('copied')},1500)})}else{var ta=document.createElement('textarea');ta.value=t;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.select();try{document.execCommand('copy')}catch(e){}document.body.removeChild(ta);btn.textContent='copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='copy';btn.classList.remove('copied')},1500)}}\ndocument.addEventListener('keydown',function(e){if(e.altKey){var k=parseInt(e.key);if(k>=1&&k<=9){e.preventDefault();var idx=k-1;if(idx<ALL_TABS.length){var btns=document.querySelectorAll('.tab-btn');btns.forEach(function(b){b.classList.remove('active')});if(btns[idx])btns[idx].classList.add('active');document.querySelectorAll('.tab-content').forEach(function(t){t.classList.remove('active')});var tgt=document.getElementById('tab-'+ALL_TABS[idx]);if(tgt)tgt.classList.add('active');if(ALL_TABS[idx]==='wifi'){setTimeout(initWifiQRCodes,50);}}}if(e.key==='0'){e.preventDefault();var btns=document.querySelectorAll('.tab-btn');btns.forEach(function(b){b.classList.remove('active')});if(btns[9])btns[9].classList.add('active');document.querySelectorAll('.tab-content').forEach(function(t){t.classList.remove('active')});var tgt=document.getElementById('tab-'+ALL_TABS[9]);if(tgt)tgt.classList.add('active');if(ALL_TABS[9]==='wifi'){setTimeout(initWifiQRCodes,50);}}}});\n\
</script>\n</body>\n</html>\n"
kind: ConfigMap
metadata:
name: intranet-html
namespace: intranet
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: intranet
name: intranet
namespace: intranet
spec:
replicas: 1
selector:
matchLabels:
app: intranet
template:
metadata:
labels:
app: intranet
spec:
containers:
- image: nginx:alpine
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10
name: nginx
ports:
- containerPort: 80
name: http
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 3
periodSeconds: 5
resources:
limits:
cpu: 50m
memory: 64Mi
requests:
cpu: 5m
memory: 16Mi
volumeMounts:
- mountPath: /etc/nginx/conf.d/default.conf
name: nginx-conf
subPath: default.conf
- mountPath: /usr/share/nginx/html
name: html
volumes:
- configMap:
name: intranet-nginx-conf
name: nginx-conf
- configMap:
name: intranet-html
name: html
---
apiVersion: v1
kind: Service
metadata:
name: intranet
namespace: intranet
spec:
ports:
- name: http
port: 80
targetPort: 80
selector:
app: intranet
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: intranet-tls
namespace: intranet
spec:
dnsNames:
- intranet.iamworkin.lan
issuerRef:
kind: ClusterIssuer
name: step-ca-acme
secretName: intranet-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: intranet
namespace: intranet
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`intranet.iamworkin.lan`)
services:
- name: intranet
port: 80
tls:
secretName: intranet-tls