ops: sync dispatcharr tooling and pause noisy workflows
This commit is contained in:
@@ -28,6 +28,13 @@ UNIFI_PASSWORD=***
|
||||
UNIFI_SITE=default
|
||||
UNIFI_VERIFY_TLS=false
|
||||
|
||||
# Pangolin integration API
|
||||
PANGOLIN_API_URL=https://api.paccoco.com/v1
|
||||
PANGOLIN_API_KEY=CHANGE_ME
|
||||
PANGOLIN_ORG_ID=paccoco
|
||||
PANGOLIN_SITE_ID=4
|
||||
PANGOLIN_DOMAIN_ID=domain1
|
||||
|
||||
# Backup automation scaffolding
|
||||
SERENITY_BACKUP_HOST=root@10.5.30.5
|
||||
SERENITY_BACKUP_ROOT=/mnt/user/backups/plausible-deniability
|
||||
|
||||
251
automation/bin/pangolin_upsert_dispatcharr.py
Normal file
251
automation/bin/pangolin_upsert_dispatcharr.py
Normal file
@@ -0,0 +1,251 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create or update the Pangolin resource/target for Dispatcharr.
|
||||
|
||||
Expected env vars:
|
||||
- PANGOLIN_API_URL e.g. https://api.paccoco.com/v1
|
||||
- PANGOLIN_API_KEY bearer token for Pangolin integration API
|
||||
- PANGOLIN_ORG_ID e.g. paccoco
|
||||
Optional env defaults:
|
||||
- PANGOLIN_SITE_ID default 4 (Plausible Deniability)
|
||||
- PANGOLIN_DOMAIN_ID default domain1
|
||||
|
||||
Default desired resource:
|
||||
- host/domain: tv.paccoco.com
|
||||
- site: Plausible Deniability (siteId 4)
|
||||
- target: dispatcharr:9191
|
||||
- Pangolin auth: disabled (sso=false)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
from urllib import error, parse, request
|
||||
|
||||
DEFAULT_API_URL = "https://api.paccoco.com/v1"
|
||||
DEFAULT_ORG_ID = "paccoco"
|
||||
DEFAULT_DOMAIN_ID = "domain1"
|
||||
DEFAULT_SITE_ID = 4
|
||||
DEFAULT_NAME = "dispatcharr"
|
||||
DEFAULT_SUBDOMAIN = "tv"
|
||||
DEFAULT_TARGET_IP = "dispatcharr"
|
||||
DEFAULT_TARGET_PORT = 9191
|
||||
USER_AGENT = "doris-pangolin-dispatcharr/1.0"
|
||||
|
||||
|
||||
def env(name: str, default: str | None = None) -> str | None:
|
||||
value = os.environ.get(name)
|
||||
if value is None or value == "":
|
||||
return default
|
||||
return value
|
||||
|
||||
|
||||
def api_request(base_url: str, api_key: str, method: str, path: str, body: dict[str, Any] | None = None) -> tuple[int, Any]:
|
||||
url = base_url.rstrip("/") + path
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": USER_AGENT,
|
||||
}
|
||||
data = None
|
||||
if body is not None:
|
||||
data = json.dumps(body).encode("utf-8")
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = request.Request(url, data=data, headers=headers, method=method.upper())
|
||||
try:
|
||||
with request.urlopen(req, timeout=30) as resp:
|
||||
raw = resp.read().decode("utf-8", "replace")
|
||||
return resp.status, json.loads(raw) if raw else None
|
||||
except error.HTTPError as exc:
|
||||
raw = exc.read().decode("utf-8", "replace")
|
||||
try:
|
||||
payload = json.loads(raw)
|
||||
except Exception:
|
||||
payload = raw
|
||||
return exc.code, payload
|
||||
|
||||
|
||||
def fail(msg: str, payload: Any = None) -> int:
|
||||
print(msg, file=sys.stderr)
|
||||
if payload is not None:
|
||||
if isinstance(payload, (dict, list)):
|
||||
print(json.dumps(payload, indent=2, sort_keys=True), file=sys.stderr)
|
||||
else:
|
||||
print(payload, file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def find_resource(resources: list[dict[str, Any]], *, name: str, full_domain: str) -> dict[str, Any] | None:
|
||||
for resource in resources:
|
||||
if resource.get("fullDomain") == full_domain:
|
||||
return resource
|
||||
for resource in resources:
|
||||
if resource.get("name") == name:
|
||||
return resource
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Upsert Pangolin Dispatcharr resource")
|
||||
parser.add_argument("--api-url", default=env("PANGOLIN_API_URL", DEFAULT_API_URL))
|
||||
parser.add_argument("--api-key", default=env("PANGOLIN_API_KEY"))
|
||||
parser.add_argument("--org-id", default=env("PANGOLIN_ORG_ID", DEFAULT_ORG_ID))
|
||||
site_id_default = env("PANGOLIN_SITE_ID", str(DEFAULT_SITE_ID)) or str(DEFAULT_SITE_ID)
|
||||
parser.add_argument("--site-id", type=int, default=int(site_id_default))
|
||||
parser.add_argument("--domain-id", default=env("PANGOLIN_DOMAIN_ID", DEFAULT_DOMAIN_ID))
|
||||
parser.add_argument("--name", default=DEFAULT_NAME)
|
||||
parser.add_argument("--subdomain", default=DEFAULT_SUBDOMAIN)
|
||||
parser.add_argument("--target-ip", default=DEFAULT_TARGET_IP)
|
||||
parser.add_argument("--target-port", type=int, default=DEFAULT_TARGET_PORT)
|
||||
parser.add_argument("--hc-path", default="/")
|
||||
parser.add_argument("--dry-run", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.api_key:
|
||||
return fail("Missing PANGOLIN_API_KEY / --api-key")
|
||||
|
||||
full_domain = f"{args.subdomain}.paccoco.com"
|
||||
|
||||
status, resources_resp = api_request(
|
||||
args.api_url,
|
||||
args.api_key,
|
||||
"GET",
|
||||
f"/org/{parse.quote(args.org_id)}/resources?pageSize=200",
|
||||
)
|
||||
if status != 200:
|
||||
return fail("Failed to list Pangolin resources", resources_resp)
|
||||
|
||||
resources = resources_resp.get("data", {}).get("resources", [])
|
||||
resource = find_resource(resources, name=args.name, full_domain=full_domain)
|
||||
|
||||
create_body = {
|
||||
"name": args.name,
|
||||
"subdomain": args.subdomain,
|
||||
"http": True,
|
||||
"protocol": "tcp",
|
||||
"domainId": args.domain_id,
|
||||
}
|
||||
|
||||
if resource is None:
|
||||
if args.dry_run:
|
||||
print(json.dumps({"action": "create_resource", "body": create_body}, indent=2))
|
||||
resource_id = None
|
||||
else:
|
||||
status, create_resp = api_request(
|
||||
args.api_url,
|
||||
args.api_key,
|
||||
"PUT",
|
||||
f"/org/{parse.quote(args.org_id)}/resource",
|
||||
create_body,
|
||||
)
|
||||
if status not in (200, 201):
|
||||
return fail("Failed to create Pangolin resource", create_resp)
|
||||
resource_id = create_resp.get("data", {}).get("resourceId") or create_resp.get("data", {}).get("resource", {}).get("resourceId")
|
||||
if not resource_id:
|
||||
return fail("Create resource succeeded but resourceId was missing", create_resp)
|
||||
status, resource_resp = api_request(args.api_url, args.api_key, "GET", f"/resource/{resource_id}")
|
||||
if status != 200:
|
||||
return fail("Failed to read back created resource", resource_resp)
|
||||
resource = resource_resp.get("data", {}).get("resource") or resource_resp.get("data") or {}
|
||||
else:
|
||||
resource_id = resource["resourceId"]
|
||||
|
||||
resource_update = {
|
||||
"name": args.name,
|
||||
"niceId": resource.get("niceId") if resource else None,
|
||||
"subdomain": args.subdomain,
|
||||
"ssl": True,
|
||||
"sso": False,
|
||||
"blockAccess": False,
|
||||
"emailWhitelistEnabled": False,
|
||||
"applyRules": False,
|
||||
"domainId": args.domain_id,
|
||||
"enabled": True,
|
||||
"stickySession": False,
|
||||
"tlsServerName": None,
|
||||
"setHostHeader": None,
|
||||
"skipToIdpId": None,
|
||||
"headers": None,
|
||||
"maintenanceModeEnabled": False,
|
||||
"maintenanceModeType": "forced",
|
||||
"maintenanceTitle": None,
|
||||
"maintenanceMessage": None,
|
||||
"maintenanceEstimatedTime": None,
|
||||
"postAuthPath": None,
|
||||
}
|
||||
|
||||
target_body = {
|
||||
"siteId": args.site_id,
|
||||
"ip": args.target_ip,
|
||||
"port": args.target_port,
|
||||
"enabled": True,
|
||||
"hcEnabled": True,
|
||||
"hcPath": args.hc_path,
|
||||
"hcScheme": "http",
|
||||
"hcMode": "http",
|
||||
"hcHostname": args.target_ip,
|
||||
"hcPort": args.target_port,
|
||||
"hcInterval": 30,
|
||||
"hcUnhealthyInterval": 30,
|
||||
"hcTimeout": 10,
|
||||
"hcHeaders": None,
|
||||
"hcFollowRedirects": True,
|
||||
"hcMethod": "GET",
|
||||
"hcStatus": 200,
|
||||
"hcTlsServerName": None,
|
||||
"hcHealthyThreshold": 1,
|
||||
"hcUnhealthyThreshold": 3,
|
||||
"path": None,
|
||||
"pathMatchType": None,
|
||||
"rewritePath": None,
|
||||
"rewritePathType": None,
|
||||
}
|
||||
|
||||
existing_targets = resource.get("targets", []) if resource else []
|
||||
matching_target = None
|
||||
for target in existing_targets:
|
||||
if target.get("siteId") == args.site_id:
|
||||
matching_target = target
|
||||
break
|
||||
|
||||
if args.dry_run:
|
||||
print(json.dumps({
|
||||
"resource_id": resource_id,
|
||||
"full_domain": full_domain,
|
||||
"resource_update": resource_update,
|
||||
"target_action": "update" if matching_target else "create",
|
||||
"target_id": matching_target.get("targetId") if matching_target else None,
|
||||
"target_body": target_body,
|
||||
}, indent=2))
|
||||
return 0
|
||||
|
||||
if resource_id is not None:
|
||||
status, update_resp = api_request(args.api_url, args.api_key, "POST", f"/resource/{resource_id}", resource_update)
|
||||
if status not in (200, 201):
|
||||
return fail("Failed to update Pangolin resource", update_resp)
|
||||
|
||||
if matching_target:
|
||||
target_id = matching_target["targetId"]
|
||||
status, target_resp = api_request(args.api_url, args.api_key, "POST", f"/target/{target_id}", target_body)
|
||||
if status not in (200, 201):
|
||||
return fail("Failed to update Pangolin target", target_resp)
|
||||
else:
|
||||
status, target_resp = api_request(args.api_url, args.api_key, "PUT", f"/resource/{resource_id}/target", target_body)
|
||||
if status not in (200, 201):
|
||||
return fail("Failed to create Pangolin target", target_resp)
|
||||
|
||||
status, final_resp = api_request(args.api_url, args.api_key, "GET", f"/resource/{resource_id}")
|
||||
if status != 200:
|
||||
return fail("Failed to read back final Pangolin resource", final_resp)
|
||||
payload = final_resp.get("data", {}).get("resource") or final_resp.get("data") or final_resp
|
||||
print(json.dumps(payload, indent=2))
|
||||
return 0
|
||||
|
||||
return fail("Unexpected state: resource_id is missing")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -15,9 +15,11 @@ sudo -n docker compose --env-file .env up -d
|
||||
|
||||
Access:
|
||||
- Direct LAN URL: `http://10.5.30.6:9191`
|
||||
- Intended public URL: `https://tv.paccoco.com` via Pangolin with Pangolin auth disabled
|
||||
|
||||
Notes:
|
||||
- Uses Dispatcharr's recommended all-in-one container (`DISPATCHARR_ENV=aio`).
|
||||
- Data is persisted at `/mnt/docker-ssd/docker/appdata/dispatcharr`.
|
||||
- First-run setup creates the initial local Dispatcharr user in the web UI.
|
||||
- IPTV credentials and EPG sources are added later through the UI.
|
||||
- Pangolin automation helper: `automation/bin/pangolin_upsert_dispatcharr.py` (requires Pangolin integration API key).
|
||||
|
||||
358
docs/architecture/network-policy-lanes-2026-05-23.html
Normal file
358
docs/architecture/network-policy-lanes-2026-05-23.html
Normal file
@@ -0,0 +1,358 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Paccoco VLAN Policy Lanes — 2026-05-23</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg: #020617;
|
||||
--panel: rgba(15, 23, 42, 0.84);
|
||||
--panel-soft: rgba(15, 23, 42, 0.72);
|
||||
--border: #1e293b;
|
||||
--text: #e2e8f0;
|
||||
--muted: #94a3b8;
|
||||
--cyan: #22d3ee;
|
||||
--emerald: #34d399;
|
||||
--amber: #fbbf24;
|
||||
--rose: #fb7185;
|
||||
--violet: #a78bfa;
|
||||
--slate: #94a3b8;
|
||||
}
|
||||
body {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
padding: 24px;
|
||||
background-image:
|
||||
linear-gradient(rgba(30,41,59,0.45) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(30,41,59,0.45) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
.container { max-width: 1560px; margin: 0 auto; }
|
||||
.header {
|
||||
margin-bottom: 18px;
|
||||
padding: 18px 20px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(15,23,42,0.92), rgba(15,23,42,0.70));
|
||||
box-shadow: 0 24px 60px rgba(0,0,0,0.28);
|
||||
}
|
||||
.header-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.pulse-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
background: var(--emerald);
|
||||
box-shadow: 0 0 18px rgba(52, 211, 153, 0.5);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(0.9); opacity: 0.58; }
|
||||
}
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: #f8fafc;
|
||||
line-height: 1.15;
|
||||
}
|
||||
.subtitle {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
max-width: 1200px;
|
||||
}
|
||||
.badge-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.badge {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 6px 10px;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
background: rgba(2,6,23,0.7);
|
||||
}
|
||||
.shell {
|
||||
background: var(--panel-soft);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 20px;
|
||||
padding: 18px;
|
||||
overflow-x: auto;
|
||||
box-shadow: 0 24px 60px rgba(0,0,0,0.28);
|
||||
}
|
||||
svg {
|
||||
width: 100%;
|
||||
min-width: 1460px;
|
||||
display: block;
|
||||
}
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 16px 18px;
|
||||
}
|
||||
.card h3 {
|
||||
font-size: 13px;
|
||||
color: #f8fafc;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.card ul {
|
||||
list-style: none;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.card li { margin-bottom: 6px; }
|
||||
.tip, .footer {
|
||||
text-align: center;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.cards { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header-top">
|
||||
<div class="pulse-dot"></div>
|
||||
<h1>Paccoco VLAN Policy Lanes</h1>
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
Simplified operator view focused on VLAN identity and firewall intent as of 2026-05-23. This intentionally strips most host-by-host detail so the important story is obvious: which lanes are internal, which are untrusted, where DNS now lands, and which paths are allowed, blocked, or preserved only as narrow exceptions.
|
||||
</div>
|
||||
<div class="badge-row">
|
||||
<div class="badge">Internal zone: Management + Trusted + Servers</div>
|
||||
<div class="badge">Untrusted zone: IoT + Camera + Old IoT</div>
|
||||
<div class="badge">Hotspot: Guest internet-only</div>
|
||||
<div class="badge">Restricted-lane DNS target: 10.5.30.53</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shell">
|
||||
<svg viewBox="0 0 1500 980" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="VLAN policy lanes diagram">
|
||||
<defs>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#22d3ee" />
|
||||
</marker>
|
||||
<marker id="arrow-emerald" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#34d399" />
|
||||
</marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#fbbf24" />
|
||||
</marker>
|
||||
<marker id="arrow-rose" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#fb7185" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect width="1500" height="980" fill="#020617" rx="18" />
|
||||
|
||||
<rect x="30" y="24" width="1440" height="120" rx="18" fill="rgba(8,51,68,0.08)" stroke="#22d3ee" stroke-width="1.2" stroke-dasharray="8,4" />
|
||||
<text x="54" y="50" fill="#22d3ee" font-size="12" font-weight="700">Control plane and zone definitions</text>
|
||||
|
||||
<rect x="54" y="68" width="280" height="52" rx="10" fill="#0f172a" />
|
||||
<rect x="54" y="68" width="280" height="52" rx="10" fill="rgba(136,19,55,0.38)" stroke="#fb7185" stroke-width="1.4" />
|
||||
<text x="194" y="89" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Gateway / UDM Pro</text>
|
||||
<text x="194" y="108" fill="#94a3b8" font-size="10" text-anchor="middle">Mgmt IP 10.5.0.1 • control surface lives only in Management</text>
|
||||
|
||||
<rect x="374" y="68" width="320" height="52" rx="10" fill="#0f172a" />
|
||||
<rect x="374" y="68" width="320" height="52" rx="10" fill="rgba(6,78,59,0.38)" stroke="#34d399" stroke-width="1.4" />
|
||||
<text x="534" y="89" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Internal Zone</text>
|
||||
<text x="534" y="108" fill="#94a3b8" font-size="10" text-anchor="middle">Management + Trusted + Servers can initiate where needed</text>
|
||||
|
||||
<rect x="734" y="68" width="320" height="52" rx="10" fill="#0f172a" />
|
||||
<rect x="734" y="68" width="320" height="52" rx="10" fill="rgba(120,53,15,0.28)" stroke="#fbbf24" stroke-width="1.4" />
|
||||
<text x="894" y="89" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Untrusted Zone</text>
|
||||
<text x="894" y="108" fill="#94a3b8" font-size="10" text-anchor="middle">IoT + Camera + Old IoT get DNS help but no broad gateway reach</text>
|
||||
|
||||
<rect x="1094" y="68" width="352" height="52" rx="10" fill="#0f172a" />
|
||||
<rect x="1094" y="68" width="352" height="52" rx="10" fill="rgba(76,29,149,0.36)" stroke="#a78bfa" stroke-width="1.4" />
|
||||
<text x="1270" y="89" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Shared DNS Anchor</text>
|
||||
<text x="1270" y="108" fill="#94a3b8" font-size="10" text-anchor="middle">Pi-hole HA VIP 10.5.30.53 advertised to restricted lanes by DHCP</text>
|
||||
|
||||
<rect x="30" y="168" width="670" height="650" rx="18" fill="rgba(6,78,59,0.10)" stroke="#34d399" stroke-width="1.2" stroke-dasharray="8,4" />
|
||||
<text x="54" y="194" fill="#34d399" font-size="12" font-weight="700">Internal lanes</text>
|
||||
|
||||
<rect x="730" y="168" width="740" height="650" rx="18" fill="rgba(120,53,15,0.08)" stroke="#fbbf24" stroke-width="1.2" stroke-dasharray="8,4" />
|
||||
<text x="754" y="194" fill="#fbbf24" font-size="12" font-weight="700">Restricted / edge lanes</text>
|
||||
|
||||
<line x1="700" y1="492" x2="730" y2="492" stroke="#34d399" stroke-width="3" marker-end="url(#arrow-emerald)" />
|
||||
<text x="715" y="480" fill="#34d399" font-size="9" text-anchor="middle">policy edge</text>
|
||||
|
||||
<!-- Internal column -->
|
||||
<rect x="60" y="224" width="190" height="235" rx="16" fill="rgba(136,19,55,0.16)" stroke="#fb7185" stroke-width="1.4" />
|
||||
<text x="155" y="254" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Management</text>
|
||||
<text x="155" y="274" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.0.0/24</text>
|
||||
<text x="155" y="292" fill="#94a3b8" font-size="10" text-anchor="middle">control plane</text>
|
||||
<text x="155" y="334" fill="#fb7185" font-size="11" font-weight="700" text-anchor="middle">Lives here</text>
|
||||
<text x="155" y="354" fill="#94a3b8" font-size="9" text-anchor="middle">UDM Pro 10.5.0.1</text>
|
||||
<text x="155" y="370" fill="#94a3b8" font-size="9" text-anchor="middle">switches, AP management</text>
|
||||
<text x="155" y="408" fill="#34d399" font-size="10" text-anchor="middle">Operator-only admin surface</text>
|
||||
|
||||
<rect x="275" y="224" width="200" height="235" rx="16" fill="rgba(8,51,68,0.16)" stroke="#22d3ee" stroke-width="1.4" />
|
||||
<text x="375" y="254" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Trusted</text>
|
||||
<text x="375" y="274" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.1.0/24 • VLAN 51</text>
|
||||
<text x="375" y="292" fill="#94a3b8" font-size="10" text-anchor="middle">people + daily drivers</text>
|
||||
<text x="375" y="334" fill="#ffffff" font-size="11" font-weight="700" text-anchor="middle">Purpose</text>
|
||||
<text x="375" y="354" fill="#94a3b8" font-size="9" text-anchor="middle">phones, laptops, tablets</text>
|
||||
<text x="375" y="370" fill="#94a3b8" font-size="9" text-anchor="middle">normal admin origin points</text>
|
||||
<text x="375" y="408" fill="#34d399" font-size="10" text-anchor="middle">Can initiate toward Servers and Untrusted</text>
|
||||
|
||||
<rect x="500" y="224" width="170" height="235" rx="16" fill="rgba(6,78,59,0.16)" stroke="#34d399" stroke-width="1.4" />
|
||||
<text x="585" y="254" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Servers</text>
|
||||
<text x="585" y="274" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.30.0/24 • VLAN 30</text>
|
||||
<text x="585" y="292" fill="#94a3b8" font-size="10" text-anchor="middle">core services</text>
|
||||
<text x="585" y="334" fill="#ffffff" font-size="11" font-weight="700" text-anchor="middle">Anchor services</text>
|
||||
<text x="585" y="354" fill="#94a3b8" font-size="9" text-anchor="middle">PD .6 • Serenity .5</text>
|
||||
<text x="585" y="370" fill="#94a3b8" font-size="9" text-anchor="middle">Nomad .7 • Rocinante .112</text>
|
||||
<text x="585" y="386" fill="#a78bfa" font-size="9" text-anchor="middle">Pi-hole VIP 10.5.30.53</text>
|
||||
<text x="585" y="408" fill="#34d399" font-size="10" text-anchor="middle">Service destination for the rest of the network</text>
|
||||
|
||||
<!-- Internal policy notes -->
|
||||
<rect x="60" y="500" width="610" height="276" rx="16" fill="rgba(15,23,42,0.84)" stroke="#1e293b" stroke-width="1.2" />
|
||||
<text x="86" y="530" fill="#f8fafc" font-size="13" font-weight="700">Internal policy summary</text>
|
||||
|
||||
<line x1="210" y1="610" x2="500" y2="610" stroke="#34d399" stroke-width="2.4" marker-end="url(#arrow-emerald)" />
|
||||
<text x="355" y="596" fill="#34d399" font-size="10" text-anchor="middle">Trusted/admin workflows can reach Servers</text>
|
||||
|
||||
<line x1="400" y1="646" x2="740" y2="646" stroke="#34d399" stroke-width="2.4" marker-end="url(#arrow-emerald)" />
|
||||
<text x="566" y="632" fill="#34d399" font-size="10" text-anchor="middle">Internal can initiate into restricted lanes when needed</text>
|
||||
|
||||
<line x1="585" y1="680" x2="1130" y2="680" stroke="#a78bfa" stroke-width="2.2" marker-end="url(#arrow-emerald)" />
|
||||
<text x="855" y="666" fill="#a78bfa" font-size="10" text-anchor="middle">Servers host shared DNS target used by restricted lanes</text>
|
||||
|
||||
<text x="86" y="718" fill="#94a3b8" font-size="10">• Management remains the only rightful home of gateway administration.</text>
|
||||
<text x="86" y="740" fill="#94a3b8" font-size="10">• Trusted is where John/admin clients live and where most deliberate control starts.</text>
|
||||
<text x="86" y="762" fill="#94a3b8" font-size="10">• Servers is the service center, not a client junk drawer.</text>
|
||||
|
||||
<!-- Restricted columns -->
|
||||
<rect x="760" y="224" width="190" height="250" rx="16" fill="rgba(120,53,15,0.16)" stroke="#fbbf24" stroke-width="1.4" />
|
||||
<text x="855" y="254" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">IoT</text>
|
||||
<text x="855" y="274" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.10.0/24 • VLAN 510</text>
|
||||
<text x="855" y="292" fill="#94a3b8" font-size="10" text-anchor="middle">appliances + smart-home gear</text>
|
||||
<text x="855" y="334" fill="#ffffff" font-size="11" font-weight="700" text-anchor="middle">Kept</text>
|
||||
<text x="855" y="354" fill="#a78bfa" font-size="9" text-anchor="middle">DNS -> 10.5.30.53</text>
|
||||
<text x="855" y="370" fill="#94a3b8" font-size="9" text-anchor="middle">DHCP preserved</text>
|
||||
<text x="855" y="386" fill="#94a3b8" font-size="9" text-anchor="middle">mDNS preserved where needed</text>
|
||||
<text x="855" y="426" fill="#fb7185" font-size="10" text-anchor="middle">No broad gateway reach</text>
|
||||
|
||||
<rect x="980" y="224" width="190" height="250" rx="16" fill="rgba(120,53,15,0.16)" stroke="#fbbf24" stroke-width="1.4" />
|
||||
<text x="1075" y="254" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Camera</text>
|
||||
<text x="1075" y="274" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.20.0/24 • VLAN 520</text>
|
||||
<text x="1075" y="292" fill="#94a3b8" font-size="10" text-anchor="middle">Protect chimes + camera-adjacent gear</text>
|
||||
<text x="1075" y="334" fill="#ffffff" font-size="11" font-weight="700" text-anchor="middle">Kept</text>
|
||||
<text x="1075" y="354" fill="#a78bfa" font-size="9" text-anchor="middle">DNS -> 10.5.30.53</text>
|
||||
<text x="1075" y="370" fill="#94a3b8" font-size="9" text-anchor="middle">DHCP preserved</text>
|
||||
<text x="1075" y="386" fill="#94a3b8" font-size="9" text-anchor="middle">same shield posture as IoT</text>
|
||||
<text x="1075" y="426" fill="#fb7185" font-size="10" text-anchor="middle">No broad gateway reach</text>
|
||||
|
||||
<rect x="1200" y="224" width="230" height="250" rx="16" fill="rgba(136,19,55,0.16)" stroke="#fb7185" stroke-width="1.4" />
|
||||
<text x="1315" y="254" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Old IoT / Legacy</text>
|
||||
<text x="1315" y="274" fill="#94a3b8" font-size="10" text-anchor="middle">192.168.1.0/24 • VLAN 2</text>
|
||||
<text x="1315" y="292" fill="#94a3b8" font-size="10" text-anchor="middle">shrinking-only containment lane</text>
|
||||
<text x="1315" y="334" fill="#ffffff" font-size="11" font-weight="700" text-anchor="middle">Still tolerated</text>
|
||||
<text x="1315" y="354" fill="#a78bfa" font-size="9" text-anchor="middle">DNS -> 10.5.30.53</text>
|
||||
<text x="1315" y="370" fill="#94a3b8" font-size="9" text-anchor="middle">same shield posture as other untrusted lanes</text>
|
||||
<text x="1315" y="386" fill="#fbbf24" font-size="9" text-anchor="middle">no new joins if avoidable</text>
|
||||
<text x="1315" y="426" fill="#fb7185" font-size="10" text-anchor="middle">Exception-only future</text>
|
||||
|
||||
<rect x="870" y="522" width="450" height="100" rx="16" fill="rgba(30,41,59,0.26)" stroke="#94a3b8" stroke-width="1.3" />
|
||||
<text x="1095" y="552" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Guest / Hotspot</text>
|
||||
<text x="1095" y="572" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.90.0/24 • VLAN 590</text>
|
||||
<text x="1095" y="592" fill="#94a3b8" font-size="10" text-anchor="middle">internet-only lane • kept separate from the internal story</text>
|
||||
|
||||
<!-- Policy flows -->
|
||||
<rect x="760" y="656" width="670" height="120" rx="16" fill="rgba(15,23,42,0.84)" stroke="#1e293b" stroke-width="1.2" />
|
||||
<text x="786" y="686" fill="#f8fafc" font-size="13" font-weight="700">Firewall intent</text>
|
||||
|
||||
<line x1="815" y1="720" x2="1035" y2="720" stroke="#a78bfa" stroke-width="2.2" marker-end="url(#arrow-emerald)" />
|
||||
<text x="925" y="708" fill="#a78bfa" font-size="10" text-anchor="middle">Restricted lanes get DNS service path</text>
|
||||
|
||||
<line x1="1060" y1="742" x2="240" y2="120" stroke="#fb7185" stroke-width="2" stroke-dasharray="6,5" marker-end="url(#arrow-rose)" />
|
||||
<text x="802" y="550" fill="#fb7185" font-size="10" text-anchor="middle">Blocked: Untrusted -> Gateway admin / broad gateway access</text>
|
||||
|
||||
<line x1="1310" y1="470" x2="1180" y2="620" stroke="#fbbf24" stroke-width="2" stroke-dasharray="4,4" marker-end="url(#arrow-amber)" />
|
||||
<text x="1250" y="560" fill="#fbbf24" font-size="10" text-anchor="middle">Only proven exceptions survive</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<rect x="30" y="850" width="1440" height="100" rx="18" fill="rgba(15,23,42,0.88)" stroke="#1e293b" stroke-width="1.2" />
|
||||
<text x="56" y="880" fill="#ffffff" font-size="12" font-weight="700">Legend</text>
|
||||
|
||||
<rect x="56" y="898" width="16" height="10" rx="2" fill="rgba(8,51,68,0.40)" stroke="#22d3ee" stroke-width="1" />
|
||||
<text x="82" y="907" fill="#94a3b8" font-size="9">trusted / operator lane</text>
|
||||
|
||||
<rect x="242" y="898" width="16" height="10" rx="2" fill="rgba(6,78,59,0.40)" stroke="#34d399" stroke-width="1" />
|
||||
<text x="268" y="907" fill="#94a3b8" font-size="9">servers / service core</text>
|
||||
|
||||
<rect x="432" y="898" width="16" height="10" rx="2" fill="rgba(120,53,15,0.30)" stroke="#fbbf24" stroke-width="1" />
|
||||
<text x="458" y="907" fill="#94a3b8" font-size="9">restricted edge lane</text>
|
||||
|
||||
<rect x="632" y="898" width="16" height="10" rx="2" fill="rgba(136,19,55,0.40)" stroke="#fb7185" stroke-width="1" />
|
||||
<text x="658" y="907" fill="#94a3b8" font-size="9">management / sensitive control surface</text>
|
||||
|
||||
<line x1="930" y1="903" x2="972" y2="903" stroke="#34d399" stroke-width="2.2" marker-end="url(#arrow-emerald)" />
|
||||
<text x="986" y="907" fill="#94a3b8" font-size="9">allowed path</text>
|
||||
|
||||
<line x1="1102" y1="903" x2="1144" y2="903" stroke="#fb7185" stroke-width="2.2" stroke-dasharray="6,5" marker-end="url(#arrow-rose)" />
|
||||
<text x="1158" y="907" fill="#94a3b8" font-size="9">blocked path</text>
|
||||
|
||||
<line x1="1274" y1="903" x2="1316" y2="903" stroke="#fbbf24" stroke-width="2.2" stroke-dasharray="4,4" marker-end="url(#arrow-amber)" />
|
||||
<text x="1330" y="907" fill="#94a3b8" font-size="9">narrow preserved exception</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<h3>Read this diagram as policy, not inventory</h3>
|
||||
<ul>
|
||||
<li>• Left side is where control and deliberate administration belong.</li>
|
||||
<li>• Right side is where devices live when they need containment more than trust.</li>
|
||||
<li>• The important service export from Servers is shared DNS at 10.5.30.53.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>The two rules that matter most</h3>
|
||||
<ul>
|
||||
<li>• Internal can initiate outward when needed.</li>
|
||||
<li>• Untrusted does not get broad gateway access back in, especially not UDM admin TCP.</li>
|
||||
<li>• Any exception has to earn its keep with a real failing device.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>What still deserves live validation</h3>
|
||||
<ul>
|
||||
<li>• Renew a DHCP lease on one client from each restricted lane.</li>
|
||||
<li>• Confirm that client resolves through 10.5.30.53.</li>
|
||||
<li>• Confirm gateway UI/admin ports still fail from those lanes.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tip">Open locally with: xdg-open /home/fizzlepoof/repos/truenas-stacks/docs/architecture/network-policy-lanes-2026-05-23.html</div>
|
||||
<div class="footer">Focused simplification derived from the broader network topology document created on 2026-05-23.</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
424
docs/architecture/network-topology-2026-05-23.html
Normal file
424
docs/architecture/network-topology-2026-05-23.html
Normal file
@@ -0,0 +1,424 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Paccoco Homelab Network Topology — 2026-05-23</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
background: #020617;
|
||||
color: #e2e8f0;
|
||||
min-height: 100vh;
|
||||
padding: 24px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1480px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.pulse-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
background: #22d3ee;
|
||||
animation: pulse 2s infinite;
|
||||
box-shadow: 0 0 18px rgba(34, 211, 238, 0.55);
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.55; transform: scale(0.92); }
|
||||
}
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
color: #f8fafc;
|
||||
}
|
||||
.subtitle {
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
margin-left: 24px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.diagram-shell {
|
||||
background: rgba(15, 23, 42, 0.72);
|
||||
border: 1px solid #1e293b;
|
||||
border-radius: 18px;
|
||||
padding: 18px;
|
||||
overflow-x: auto;
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
svg {
|
||||
width: 100%;
|
||||
min-width: 1380px;
|
||||
display: block;
|
||||
}
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.card {
|
||||
background: rgba(15, 23, 42, 0.72);
|
||||
border: 1px solid #1e293b;
|
||||
border-radius: 14px;
|
||||
padding: 16px 18px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.card-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
.cyan { background: #22d3ee; }
|
||||
.emerald { background: #34d399; }
|
||||
.rose { background: #fb7185; }
|
||||
.amber { background: #fbbf24; }
|
||||
.violet { background: #a78bfa; }
|
||||
.slate { background: #94a3b8; }
|
||||
.card h3 {
|
||||
font-size: 13px;
|
||||
color: #f8fafc;
|
||||
}
|
||||
.card ul {
|
||||
list-style: none;
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.card li { margin-bottom: 6px; }
|
||||
.footer {
|
||||
text-align: center;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.tip {
|
||||
margin-top: 10px;
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.cards { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header-row">
|
||||
<div class="pulse-dot"></div>
|
||||
<h1>Paccoco Homelab Network Topology</h1>
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
Current-state operator map as of 2026-05-23. Reflects live UniFi segmentation, the new Servers VLAN,
|
||||
restricted-lane DNS cutover to 10.5.30.53, and the broader Untrusted → Gateway shield with DHCP/mDNS preservation.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="diagram-shell">
|
||||
<svg viewBox="0 0 1500 1080" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Homelab network topology diagram">
|
||||
<defs>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.6" />
|
||||
</pattern>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#22d3ee" />
|
||||
</marker>
|
||||
<marker id="arrow-emerald" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#34d399" />
|
||||
</marker>
|
||||
<marker id="arrow-amber" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#fbbf24" />
|
||||
</marker>
|
||||
<marker id="arrow-rose" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#fb7185" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect width="1500" height="1080" fill="#020617" />
|
||||
<rect width="1500" height="1080" fill="url(#grid)" />
|
||||
|
||||
<!-- Top boundaries -->
|
||||
<rect x="30" y="24" width="1440" height="130" rx="16" fill="rgba(120,53,15,0.10)" stroke="#fbbf24" stroke-width="1.2" stroke-dasharray="8,4" />
|
||||
<text x="50" y="48" fill="#fbbf24" font-size="12" font-weight="700">External access + WAN edge</text>
|
||||
|
||||
<rect x="30" y="176" width="1440" height="214" rx="16" fill="rgba(8,51,68,0.08)" stroke="#22d3ee" stroke-width="1.2" stroke-dasharray="8,4" />
|
||||
<text x="50" y="200" fill="#22d3ee" font-size="12" font-weight="700">UniFi control plane + switching fabric</text>
|
||||
|
||||
<rect x="30" y="420" width="1440" height="520" rx="16" fill="rgba(30,41,59,0.14)" stroke="#64748b" stroke-width="1.2" stroke-dasharray="8,4" />
|
||||
<text x="50" y="444" fill="#cbd5e1" font-size="12" font-weight="700">Segmentation lanes</text>
|
||||
|
||||
<!-- External row -->
|
||||
<rect x="70" y="62" width="190" height="62" rx="8" fill="#0f172a" />
|
||||
<rect x="70" y="62" width="190" height="62" rx="8" fill="rgba(30,41,59,0.55)" stroke="#94a3b8" stroke-width="1.5" />
|
||||
<text x="165" y="87" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Internet / Clients</text>
|
||||
<text x="165" y="106" fill="#94a3b8" font-size="10" text-anchor="middle">trusted users • phones • browsers</text>
|
||||
|
||||
<rect x="350" y="62" width="220" height="62" rx="8" fill="#0f172a" />
|
||||
<rect x="350" y="62" width="220" height="62" rx="8" fill="rgba(120,53,15,0.30)" stroke="#fbbf24" stroke-width="1.5" />
|
||||
<text x="460" y="87" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Pangolin VPS + Newt</text>
|
||||
<text x="460" y="106" fill="#94a3b8" font-size="10" text-anchor="middle">reverse proxy for public subdomains</text>
|
||||
|
||||
<rect x="660" y="62" width="180" height="62" rx="8" fill="#0f172a" />
|
||||
<rect x="660" y="62" width="180" height="62" rx="8" fill="rgba(136,19,55,0.40)" stroke="#fb7185" stroke-width="1.5" />
|
||||
<text x="750" y="87" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Tailscale</text>
|
||||
<text x="750" y="106" fill="#94a3b8" font-size="10" text-anchor="middle">Serenity: 100.94.87.79</text>
|
||||
|
||||
<rect x="950" y="62" width="250" height="62" rx="8" fill="#0f172a" />
|
||||
<rect x="950" y="62" width="250" height="62" rx="8" fill="rgba(30,41,59,0.55)" stroke="#94a3b8" stroke-width="1.5" />
|
||||
<text x="1075" y="87" fill="#ffffff" font-size="13" font-weight="700" text-anchor="middle">Cloudflare DNS</text>
|
||||
<text x="1075" y="106" fill="#94a3b8" font-size="10" text-anchor="middle">paccoco.com records point to Pangolin VPS</text>
|
||||
|
||||
<!-- Core row -->
|
||||
<rect x="100" y="242" width="220" height="92" rx="10" fill="#0f172a" />
|
||||
<rect x="100" y="242" width="220" height="92" rx="10" fill="rgba(136,19,55,0.40)" stroke="#fb7185" stroke-width="1.6" />
|
||||
<text x="210" y="268" fill="#ffffff" font-size="14" font-weight="700" text-anchor="middle">UDM Pro</text>
|
||||
<text x="210" y="288" fill="#94a3b8" font-size="10" text-anchor="middle">gateway + UniFi controller</text>
|
||||
<text x="210" y="305" fill="#94a3b8" font-size="10" text-anchor="middle">Mgmt IP: 10.5.0.1</text>
|
||||
<text x="210" y="322" fill="#fb7185" font-size="9" text-anchor="middle">Admin TCP blocked from Untrusted</text>
|
||||
|
||||
<rect x="410" y="230" width="250" height="116" rx="10" fill="#0f172a" />
|
||||
<rect x="410" y="230" width="250" height="116" rx="10" fill="rgba(120,53,15,0.28)" stroke="#fbbf24" stroke-width="1.6" />
|
||||
<text x="535" y="256" fill="#ffffff" font-size="14" font-weight="700" text-anchor="middle">Switch Fabric</text>
|
||||
<text x="535" y="278" fill="#94a3b8" font-size="10" text-anchor="middle">USW-24-PoE — 10.5.0.10</text>
|
||||
<text x="535" y="296" fill="#94a3b8" font-size="10" text-anchor="middle">USW Pro HD 24 — 10.5.0.135</text>
|
||||
<text x="535" y="314" fill="#fbbf24" font-size="9" text-anchor="middle">10G links for PD + Serenity • 2.5G lanes for Nomad/FlyingDutchman/Rocinante</text>
|
||||
|
||||
<rect x="760" y="230" width="250" height="116" rx="10" fill="#0f172a" />
|
||||
<rect x="760" y="230" width="250" height="116" rx="10" fill="rgba(8,51,68,0.42)" stroke="#22d3ee" stroke-width="1.6" />
|
||||
<text x="885" y="256" fill="#ffffff" font-size="14" font-weight="700" text-anchor="middle">Wi-Fi Access Points</text>
|
||||
<text x="885" y="278" fill="#94a3b8" font-size="10" text-anchor="middle">U7 Pro — 10.5.0.21 — active</text>
|
||||
<text x="885" y="296" fill="#94a3b8" font-size="10" text-anchor="middle">U6 LR — 10.5.0.20</text>
|
||||
<text x="885" y="314" fill="#fb7185" font-size="9" text-anchor="middle">currently disconnected in UniFi</text>
|
||||
|
||||
<rect x="1110" y="230" width="280" height="116" rx="10" fill="#0f172a" />
|
||||
<rect x="1110" y="230" width="280" height="116" rx="10" fill="rgba(6,78,59,0.40)" stroke="#34d399" stroke-width="1.6" />
|
||||
<text x="1250" y="256" fill="#ffffff" font-size="14" font-weight="700" text-anchor="middle">Policy Zones</text>
|
||||
<text x="1250" y="278" fill="#94a3b8" font-size="10" text-anchor="middle">Internal = Mgmt + Trusted + Servers</text>
|
||||
<text x="1250" y="296" fill="#94a3b8" font-size="10" text-anchor="middle">Untrusted = IoT + Camera + Old IoT</text>
|
||||
<text x="1250" y="314" fill="#94a3b8" font-size="10" text-anchor="middle">Hotspot = Guest</text>
|
||||
|
||||
<!-- Arrows top/core -->
|
||||
<line x1="260" y1="93" x2="348" y2="93" stroke="#94a3b8" stroke-width="1.5" marker-end="url(#arrow-amber)" />
|
||||
<line x1="570" y1="93" x2="658" y2="93" stroke="#94a3b8" stroke-width="1.5" marker-end="url(#arrow-rose)" />
|
||||
<line x1="838" y1="93" x2="948" y2="93" stroke="#94a3b8" stroke-width="1.5" marker-end="url(#arrow-amber)" />
|
||||
<line x1="210" y1="124" x2="210" y2="240" stroke="#22d3ee" stroke-width="1.8" marker-end="url(#arrow-cyan)" />
|
||||
<line x1="535" y1="346" x2="535" y2="438" stroke="#22d3ee" stroke-width="1.8" marker-end="url(#arrow-cyan)" />
|
||||
<line x1="885" y1="346" x2="885" y2="438" stroke="#22d3ee" stroke-width="1.8" marker-end="url(#arrow-cyan)" />
|
||||
<line x1="210" y1="334" x2="410" y2="288" stroke="#22d3ee" stroke-width="1.6" marker-end="url(#arrow-cyan)" />
|
||||
<line x1="660" y1="288" x2="760" y2="288" stroke="#22d3ee" stroke-width="1.6" marker-end="url(#arrow-cyan)" />
|
||||
<line x1="1010" y1="288" x2="1110" y2="288" stroke="#22d3ee" stroke-width="1.6" marker-end="url(#arrow-cyan)" />
|
||||
|
||||
<!-- VLAN columns -->
|
||||
<!-- Management -->
|
||||
<rect x="56" y="482" width="250" height="390" rx="14" fill="rgba(136,19,55,0.16)" stroke="#fb7185" stroke-width="1.3" />
|
||||
<text x="181" y="512" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Management</text>
|
||||
<text x="181" y="531" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.0.0/24</text>
|
||||
<text x="181" y="548" fill="#94a3b8" font-size="10" text-anchor="middle">UDM Pro • switches • AP management</text>
|
||||
|
||||
<rect x="82" y="578" width="198" height="78" rx="8" fill="#0f172a" />
|
||||
<rect x="82" y="578" width="198" height="78" rx="8" fill="rgba(136,19,55,0.40)" stroke="#fb7185" stroke-width="1.4" />
|
||||
<text x="181" y="604" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">Core Infra</text>
|
||||
<text x="181" y="623" fill="#94a3b8" font-size="9" text-anchor="middle">UDM Pro 10.5.0.1</text>
|
||||
<text x="181" y="639" fill="#94a3b8" font-size="9" text-anchor="middle">USW-24-PoE 10.5.0.10 • USW Pro HD 10.5.0.135</text>
|
||||
|
||||
<rect x="82" y="690" width="198" height="82" rx="8" fill="#0f172a" />
|
||||
<rect x="82" y="690" width="198" height="82" rx="8" fill="rgba(8,51,68,0.40)" stroke="#22d3ee" stroke-width="1.4" />
|
||||
<text x="181" y="716" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">AP Control</text>
|
||||
<text x="181" y="735" fill="#94a3b8" font-size="9" text-anchor="middle">U7 Pro active</text>
|
||||
<text x="181" y="751" fill="#94a3b8" font-size="9" text-anchor="middle">U6 LR disconnected but still modeled</text>
|
||||
|
||||
<text x="181" y="812" fill="#fb7185" font-size="10" text-anchor="middle">Management stays operator-only</text>
|
||||
|
||||
<!-- Trusted -->
|
||||
<rect x="336" y="482" width="250" height="390" rx="14" fill="rgba(8,51,68,0.16)" stroke="#22d3ee" stroke-width="1.3" />
|
||||
<text x="461" y="512" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Trusted</text>
|
||||
<text x="461" y="531" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.1.0/24 • VLAN 51</text>
|
||||
<text x="461" y="548" fill="#94a3b8" font-size="10" text-anchor="middle">laptops • phones • tablets • daily-driver clients</text>
|
||||
|
||||
<rect x="362" y="578" width="198" height="82" rx="8" fill="#0f172a" />
|
||||
<rect x="362" y="578" width="198" height="82" rx="8" fill="rgba(8,51,68,0.40)" stroke="#22d3ee" stroke-width="1.4" />
|
||||
<text x="461" y="604" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">User Devices</text>
|
||||
<text x="461" y="623" fill="#94a3b8" font-size="9" text-anchor="middle">operator/admin endpoints live here</text>
|
||||
<text x="461" y="639" fill="#94a3b8" font-size="9" text-anchor="middle">can initiate into servers + untrusted lanes</text>
|
||||
|
||||
<rect x="362" y="690" width="198" height="82" rx="8" fill="#0f172a" />
|
||||
<rect x="362" y="690" width="198" height="82" rx="8" fill="rgba(30,41,59,0.55)" stroke="#94a3b8" stroke-width="1.4" />
|
||||
<text x="461" y="716" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">Wi-Fi SSID</text>
|
||||
<text x="461" y="735" fill="#94a3b8" font-size="9" text-anchor="middle">primary trusted network</text>
|
||||
<text x="461" y="751" fill="#94a3b8" font-size="9" text-anchor="middle">legacy compatibility cleanup still ongoing</text>
|
||||
|
||||
<text x="461" y="812" fill="#34d399" font-size="10" text-anchor="middle">Allowed to initiate toward Untrusted</text>
|
||||
|
||||
<!-- Servers -->
|
||||
<rect x="616" y="482" width="290" height="390" rx="14" fill="rgba(6,78,59,0.16)" stroke="#34d399" stroke-width="1.3" />
|
||||
<text x="761" y="512" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Servers</text>
|
||||
<text x="761" y="531" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.30.0/24 • VLAN 30</text>
|
||||
<text x="761" y="548" fill="#94a3b8" font-size="10" text-anchor="middle">core services + app stacks + AI + storage-adjacent hosts</text>
|
||||
|
||||
<rect x="642" y="572" width="238" height="112" rx="8" fill="#0f172a" />
|
||||
<rect x="642" y="572" width="238" height="112" rx="8" fill="rgba(6,78,59,0.40)" stroke="#34d399" stroke-width="1.4" />
|
||||
<text x="761" y="598" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">Core Hosts</text>
|
||||
<text x="761" y="617" fill="#94a3b8" font-size="9" text-anchor="middle">PlausibleDeniability — 10.5.30.6</text>
|
||||
<text x="761" y="633" fill="#94a3b8" font-size="9" text-anchor="middle">Serenity — 10.5.30.5</text>
|
||||
<text x="761" y="649" fill="#94a3b8" font-size="9" text-anchor="middle">N.O.M.A.D. — 10.5.30.7</text>
|
||||
<text x="761" y="665" fill="#94a3b8" font-size="9" text-anchor="middle">Rocinante — 10.5.30.112</text>
|
||||
|
||||
<rect x="642" y="706" width="238" height="98" rx="8" fill="#0f172a" />
|
||||
<rect x="642" y="706" width="238" height="98" rx="8" fill="rgba(76,29,149,0.40)" stroke="#a78bfa" stroke-width="1.4" />
|
||||
<text x="761" y="732" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">Shared Services</text>
|
||||
<text x="761" y="751" fill="#94a3b8" font-size="9" text-anchor="middle">Pi-hole HA VIP / DNS: 10.5.30.53</text>
|
||||
<text x="761" y="767" fill="#94a3b8" font-size="9" text-anchor="middle">Gitea • Home Assistant • n8n • OpenWebUI • Qdrant</text>
|
||||
<text x="761" y="783" fill="#94a3b8" font-size="9" text-anchor="middle">LiteLLM • monitoring • media • productivity stacks</text>
|
||||
|
||||
<text x="761" y="832" fill="#34d399" font-size="10" text-anchor="middle">Internal services reachable by trusted/admin workflows</text>
|
||||
|
||||
<!-- IoT -->
|
||||
<rect x="936" y="482" width="250" height="390" rx="14" fill="rgba(120,53,15,0.16)" stroke="#fbbf24" stroke-width="1.3" />
|
||||
<text x="1061" y="512" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">IoT</text>
|
||||
<text x="1061" y="531" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.10.0/24 • VLAN 510</text>
|
||||
<text x="1061" y="548" fill="#94a3b8" font-size="10" text-anchor="middle">ecobees • MyQ • fridge • smart appliances</text>
|
||||
|
||||
<rect x="962" y="584" width="198" height="84" rx="8" fill="#0f172a" />
|
||||
<rect x="962" y="584" width="198" height="84" rx="8" fill="rgba(120,53,15,0.30)" stroke="#fbbf24" stroke-width="1.4" />
|
||||
<text x="1061" y="610" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">DHCP DNS</text>
|
||||
<text x="1061" y="629" fill="#94a3b8" font-size="9" text-anchor="middle">hands out 10.5.30.53</text>
|
||||
<text x="1061" y="645" fill="#94a3b8" font-size="9" text-anchor="middle">no longer depends on gateway-default DNS</text>
|
||||
|
||||
<rect x="962" y="696" width="198" height="92" rx="8" fill="#0f172a" />
|
||||
<rect x="962" y="696" width="198" height="92" rx="8" fill="rgba(136,19,55,0.40)" stroke="#fb7185" stroke-width="1.4" />
|
||||
<text x="1061" y="722" fill="#ffffff" font-size="12" font-weight="700" text-anchor="middle">Gateway Access</text>
|
||||
<text x="1061" y="741" fill="#94a3b8" font-size="9" text-anchor="middle">general gateway access blocked</text>
|
||||
<text x="1061" y="757" fill="#94a3b8" font-size="9" text-anchor="middle">DHCP + mDNS preserved</text>
|
||||
<text x="1061" y="773" fill="#94a3b8" font-size="9" text-anchor="middle">UDM admin TCP explicitly blocked</text>
|
||||
|
||||
<!-- Camera -->
|
||||
<rect x="1216" y="482" width="250" height="180" rx="14" fill="rgba(120,53,15,0.16)" stroke="#fbbf24" stroke-width="1.3" />
|
||||
<text x="1341" y="512" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Camera</text>
|
||||
<text x="1341" y="531" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.20.0/24 • VLAN 520</text>
|
||||
<text x="1341" y="548" fill="#94a3b8" font-size="10" text-anchor="middle">Protect chimes + camera-adjacent gear</text>
|
||||
<text x="1341" y="582" fill="#94a3b8" font-size="9" text-anchor="middle">Same shield posture as IoT</text>
|
||||
<text x="1341" y="599" fill="#94a3b8" font-size="9" text-anchor="middle">DNS -> 10.5.30.53 • DHCP/mDNS preserved</text>
|
||||
<text x="1341" y="616" fill="#94a3b8" font-size="9" text-anchor="middle">general gateway access blocked</text>
|
||||
|
||||
<!-- Guest -->
|
||||
<rect x="1216" y="686" width="250" height="86" rx="14" fill="rgba(30,41,59,0.26)" stroke="#94a3b8" stroke-width="1.3" />
|
||||
<text x="1341" y="715" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Guest / Hotspot</text>
|
||||
<text x="1341" y="734" fill="#94a3b8" font-size="10" text-anchor="middle">10.5.90.0/24 • VLAN 590 • internet-only lane</text>
|
||||
|
||||
<!-- Old IoT -->
|
||||
<rect x="1216" y="794" width="250" height="96" rx="14" fill="rgba(136,19,55,0.18)" stroke="#fb7185" stroke-width="1.3" />
|
||||
<text x="1341" y="823" fill="#ffffff" font-size="15" font-weight="700" text-anchor="middle">Old IoT / Legacy Quarantine</text>
|
||||
<text x="1341" y="842" fill="#94a3b8" font-size="10" text-anchor="middle">192.168.1.0/24 • VLAN 2</text>
|
||||
<text x="1341" y="859" fill="#94a3b8" font-size="9" text-anchor="middle">shrinking-only containment lane</text>
|
||||
<text x="1341" y="875" fill="#94a3b8" font-size="9" text-anchor="middle">same DNS/gateway shield pattern as IoT/Camera</text>
|
||||
|
||||
<!-- Inter-lane arrows / policy cues -->
|
||||
<line x1="510" y1="636" x2="935" y2="636" stroke="#34d399" stroke-width="1.8" marker-end="url(#arrow-emerald)" />
|
||||
<text x="726" y="626" fill="#34d399" font-size="9" text-anchor="middle">Allow Internal → Untrusted</text>
|
||||
|
||||
<line x1="1061" y1="584" x2="761" y2="756" stroke="#a78bfa" stroke-width="1.8" marker-end="url(#arrow-emerald)" />
|
||||
<text x="930" y="699" fill="#a78bfa" font-size="9" text-anchor="middle">DNS only → 10.5.30.53</text>
|
||||
|
||||
<line x1="1061" y1="696" x2="210" y2="334" stroke="#fb7185" stroke-width="1.6" stroke-dasharray="6,5" marker-end="url(#arrow-rose)" />
|
||||
<text x="705" y="520" fill="#fb7185" font-size="9" text-anchor="middle">Blocked: general Untrusted → Gateway</text>
|
||||
|
||||
<line x1="1341" y1="572" x2="210" y2="306" stroke="#fb7185" stroke-width="1.4" stroke-dasharray="5,5" marker-end="url(#arrow-rose)" />
|
||||
<text x="770" y="394" fill="#fb7185" font-size="9" text-anchor="middle">Blocked: UDM admin TCP from Camera / IoT / Old IoT</text>
|
||||
|
||||
<line x1="1061" y1="788" x2="761" y2="786" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="4,4" marker-end="url(#arrow-amber)" />
|
||||
<text x="908" y="779" fill="#fbbf24" font-size="9" text-anchor="middle">Only proven exceptions stay</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<rect x="52" y="958" width="1396" height="90" rx="12" fill="rgba(15,23,42,0.85)" stroke="#1e293b" stroke-width="1.2" />
|
||||
<text x="78" y="986" fill="#ffffff" font-size="12" font-weight="700">Legend</text>
|
||||
|
||||
<rect x="78" y="1000" width="16" height="10" rx="2" fill="rgba(8,51,68,0.40)" stroke="#22d3ee" stroke-width="1" />
|
||||
<text x="103" y="1009" fill="#94a3b8" font-size="9">Trusted / operator lanes</text>
|
||||
|
||||
<rect x="270" y="1000" width="16" height="10" rx="2" fill="rgba(6,78,59,0.40)" stroke="#34d399" stroke-width="1" />
|
||||
<text x="295" y="1009" fill="#94a3b8" font-size="9">Servers / core services</text>
|
||||
|
||||
<rect x="468" y="1000" width="16" height="10" rx="2" fill="rgba(120,53,15,0.30)" stroke="#fbbf24" stroke-width="1" />
|
||||
<text x="493" y="1009" fill="#94a3b8" font-size="9">IoT / camera lanes</text>
|
||||
|
||||
<rect x="640" y="1000" width="16" height="10" rx="2" fill="rgba(136,19,55,0.40)" stroke="#fb7185" stroke-width="1" />
|
||||
<text x="665" y="1009" fill="#94a3b8" font-size="9">Management / blocked security surfaces</text>
|
||||
|
||||
<line x1="910" y1="1005" x2="948" y2="1005" stroke="#34d399" stroke-width="1.8" marker-end="url(#arrow-emerald)" />
|
||||
<text x="960" y="1009" fill="#94a3b8" font-size="9">allowed path</text>
|
||||
|
||||
<line x1="1088" y1="1005" x2="1126" y2="1005" stroke="#fb7185" stroke-width="1.8" stroke-dasharray="5,5" marker-end="url(#arrow-rose)" />
|
||||
<text x="1138" y="1009" fill="#94a3b8" font-size="9">blocked path</text>
|
||||
|
||||
<line x1="1262" y1="1005" x2="1300" y2="1005" stroke="#fbbf24" stroke-width="1.6" stroke-dasharray="4,4" marker-end="url(#arrow-amber)" />
|
||||
<text x="1312" y="1009" fill="#94a3b8" font-size="9">narrow preserved exception</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot emerald"></div>
|
||||
<h3>Core hosts + routing facts</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• PD 10.5.30.6 is the main Docker/control host.</li>
|
||||
<li>• Serenity 10.5.30.5 handles NAS + ARR + Tailscale reachability.</li>
|
||||
<li>• N.O.M.A.D. 10.5.30.7 runs local services and game/server workloads.</li>
|
||||
<li>• Rocinante 10.5.30.112 carries heavy Ollama inference.</li>
|
||||
<li>• UDM Pro stays at 10.5.0.1 on the management subnet.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot amber"></div>
|
||||
<h3>What changed recently</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• Servers VLAN is live at 10.5.30.0/24 (VLAN 30).</li>
|
||||
<li>• IoT, Camera, and Old IoT now DHCP-advertise 10.5.30.53 for DNS.</li>
|
||||
<li>• Broader Untrusted → Gateway block is live.</li>
|
||||
<li>• DHCP and mDNS are still preserved for restricted lanes.</li>
|
||||
<li>• UDM Pro admin TCP ports are explicitly blocked from Untrusted.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-dot rose"></div>
|
||||
<h3>Still worth validating on real clients</h3>
|
||||
</div>
|
||||
<ul>
|
||||
<li>• Renew one DHCP lease per restricted lane.</li>
|
||||
<li>• Confirm DNS resolution works through 10.5.30.53.</li>
|
||||
<li>• Confirm gateway admin/UI access fails from those lanes.</li>
|
||||
<li>• Keep Google/cast validation as a separate laptop-present test wave.</li>
|
||||
<li>• Treat any new exception as earned only by a real failing device.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tip">Open locally with: xdg-open /home/fizzlepoof/repos/truenas-stacks/docs/architecture/network-topology-2026-05-23.html</div>
|
||||
<div class="footer">Generated from current repo docs + live UniFi changes documented on 2026-05-23.</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-09T19:58:19.095Z",
|
||||
"id": "fmywVBIanfxOfDlI",
|
||||
"name": "Grafana Alert → AI → Gotify v1.4",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-06T23:25:29.005Z",
|
||||
"id": "gYyOggC4J98keLbj",
|
||||
"name": "RAG Pipeline v1.0",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-06T23:25:29.005Z",
|
||||
"id": "gYyOggC4J98keLbj",
|
||||
"name": "RAG Pipeline — Ingest & Query",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-07T20:24:40.153Z",
|
||||
"id": "dQ1d5deV4mF0s5eP",
|
||||
"name": "Paperless AI Processing v1.0",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-07T20:24:40.153Z",
|
||||
"id": "dQ1d5deV4mF0s5eP",
|
||||
"name": "Paperless AI Processing v1.0",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-09T22:31:02.480Z",
|
||||
"id": "9qfbZJJmSJqmg5sX",
|
||||
"name": "Whisper Audio Transcription",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-07T00:48:49.355Z",
|
||||
"id": "4YKnRQ9rOiQCeYEU",
|
||||
"name": "Paperless \u2192 RAG v1.0",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-07T00:48:49.355Z",
|
||||
"id": "4YKnRQ9rOiQCeYEU",
|
||||
"name": "Paperless \u2192 RAG Ingest",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-07T00:49:43.226Z",
|
||||
"id": "CFYWbBRx9RPf4HjS",
|
||||
"name": "Git Commit → AI Summary → Gotify v1.0",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-07T00:49:43.226Z",
|
||||
"id": "CFYWbBRx9RPf4HjS",
|
||||
"name": "Git Commit \u2192 AI Summary \u2192 Gotify",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-10T02:04:29.809Z",
|
||||
"id": "g9JRtBA5lR0OAwVo",
|
||||
"name": "Class Recording → Transcribe → RAG",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-10T02:04:47.115Z",
|
||||
"id": "sg7nRrhim0omh8oQ",
|
||||
"name": "Class Transcript Ingest v1.1",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-13T16:06:39.571Z",
|
||||
"id": "FIhrfDfL1NgakDwl",
|
||||
"name": "Paperless Intake Triage → Doris Review Queue",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"createdAt": "2026-05-13T21:26:54.606Z",
|
||||
"id": "7MlM3DjAMf4eSl0s",
|
||||
"name": "Paperless School Intake Metadata Enrichment v1.1",
|
||||
"description": null,
|
||||
"active": true,
|
||||
"description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
|
||||
"active": false,
|
||||
"isArchived": false,
|
||||
"nodes": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user