Compare commits
2 Commits
5f56fe75d8
...
c5ab126513
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5ab126513 | ||
|
|
8c72a0c287 |
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
## Where We Left Off
|
## Where We Left Off
|
||||||
|
|
||||||
Phase 2 (AI stack) is fully deployed and verified on PlausibleDeniability (10.5.1.6). We were about to verify Gotify (Phase 1) and then deploy n8n (Phase 3).
|
Phase 2 (AI stack) is fully deployed and verified on PlausibleDeniability (10.5.30.6). We were about to verify Gotify (Phase 1) and then deploy n8n (Phase 3).
|
||||||
|
|
||||||
## What's Done
|
## What's Done
|
||||||
|
|
||||||
### Phase 2 — AI Stack (COMPLETE)
|
### Phase 2 — AI Stack (COMPLETE)
|
||||||
All 6 containers running and verified on PD (10.5.1.6):
|
All 6 containers running and verified on PD (10.5.30.6):
|
||||||
- **Ollama:** port 11434 — qwen2.5:14b loaded
|
- **Ollama:** port 11434 — qwen2.5:14b loaded
|
||||||
- **OpenWebUI:** port 8282 (→8080)
|
- **OpenWebUI:** port 8282 (→8080)
|
||||||
- **Qdrant:** port 6333 (HTTP), 6334 (gRPC)
|
- **Qdrant:** port 6333 (HTTP), 6334 (gRPC)
|
||||||
@@ -16,7 +16,7 @@ All 6 containers running and verified on PD (10.5.1.6):
|
|||||||
- **Whisper:** port 8786 (→8000)
|
- **Whisper:** port 8786 (→8000)
|
||||||
|
|
||||||
### Key Fixes Applied (reference for future troubleshooting)
|
### Key Fixes Applied (reference for future troubleshooting)
|
||||||
1. **TrueNAS Scale Docker quirk:** `curl localhost:PORT` returns 000. Always use `curl 10.5.1.6:PORT`.
|
1. **TrueNAS Scale Docker quirk:** `curl localhost:PORT` returns 000. Always use `curl 10.5.30.6:PORT`.
|
||||||
2. **Reranker image:** Use `cpu-latest` not `cpu-1.5` (hf-hub bug).
|
2. **Reranker image:** Use `cpu-latest` not `cpu-1.5` (hf-hub bug).
|
||||||
3. **LiteLLM config:** Remove `database_url` (newer versions try Prisma/PostgreSQL). Set `master_key: "os.environ/LITELLM_MASTER_KEY"` to read from env var.
|
3. **LiteLLM config:** Remove `database_url` (newer versions try Prisma/PostgreSQL). Set `master_key: "os.environ/LITELLM_MASTER_KEY"` to read from env var.
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ docker ps --filter name=gotify --format "table {{.Names}}\t{{.Image}}\t{{.Status
|
|||||||
docker logs gotify --tail 20
|
docker logs gotify --tail 20
|
||||||
docker inspect gotify --format '{{json .Mounts}}' | python3 -m json.tool
|
docker inspect gotify --format '{{json .Mounts}}' | python3 -m json.tool
|
||||||
docker inspect gotify --format '{{json .NetworkSettings.Networks}}' | python3 -m json.tool
|
docker inspect gotify --format '{{json .NetworkSettings.Networks}}' | python3 -m json.tool
|
||||||
curl -sw "\n%{http_code}" http://10.5.1.6:8484/health
|
curl -sw "\n%{http_code}" http://10.5.30.6:8484/health
|
||||||
ls -la /mnt/docker-ssd/docker/compose/automation/
|
ls -la /mnt/docker-ssd/docker/compose/automation/
|
||||||
cat /mnt/docker-ssd/docker/compose/automation/docker-compose.yaml
|
cat /mnt/docker-ssd/docker/compose/automation/docker-compose.yaml
|
||||||
```
|
```
|
||||||
@@ -57,9 +57,9 @@ Per HOMELAB_BUILDOUT_PLAN.md Phase 3:
|
|||||||
| pangolin | Reverse proxy / external exposure |
|
| pangolin | Reverse proxy / external exposure |
|
||||||
|
|
||||||
## Machine IPs
|
## Machine IPs
|
||||||
- **PlausibleDeniability (PD):** 10.5.1.6 — TrueNAS Scale, RTX 2080 Ti
|
- **PlausibleDeniability (PD):** 10.5.30.6 — TrueNAS Scale, RTX 2080 Ti
|
||||||
- **ROCINANTE:** 10.5.1.112 — RTX 4090
|
- **ROCINANTE:** 10.5.1.112 — RTX 4090
|
||||||
- **N.O.M.A.D.:** 10.5.1.16 — Ubuntu, GTX 1080
|
- **N.O.M.A.D.:** 10.5.30.7 — Ubuntu, GTX 1080
|
||||||
|
|
||||||
## LiteLLM .env Secrets (on PD at /mnt/docker-ssd/docker/compose/ai/.env)
|
## LiteLLM .env Secrets (on PD at /mnt/docker-ssd/docker/compose/ai/.env)
|
||||||
Already configured — do not regenerate. Master key is in the .env file as LITELLM_MASTER_KEY.
|
Already configured — do not regenerate. Master key is in the .env file as LITELLM_MASTER_KEY.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,9 @@ Docker Compose stacks for PlausibleDeniability (TrueNAS Scale), plus documentati
|
|||||||
|
|
||||||
| Server | Role | OS | IP |
|
| Server | Role | OS | IP |
|
||||||
|--------|------|----|----|
|
|--------|------|----|----|
|
||||||
| Serenity | NAS / Media ingestion | Unraid 7.2.4 | 10.5.1.5 |
|
| Serenity | NAS / Media ingestion | Unraid 7.2.4 | 10.5.30.5 |
|
||||||
| PlausibleDeniability | Primary Docker host | TrueNAS Scale 25.10.2.1 | 10.5.1.6 |
|
| PlausibleDeniability | Primary Docker host | TrueNAS Scale 25.10.2.1 | 10.5.30.6 |
|
||||||
| N.O.M.A.D. | Offline knowledge + game servers | Ubuntu 25.10 | 10.5.1.16 |
|
| N.O.M.A.D. | Offline knowledge + game servers | Ubuntu 25.10 | 10.5.30.7 |
|
||||||
|
|
||||||
## Repo Layout
|
## Repo Layout
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
model_list:
|
model_list:
|
||||||
# ---- Reranker (Serenity, 10.5.1.5) ----
|
# ---- Reranker (Serenity, 10.5.30.5) ----
|
||||||
- model_name: "reranker"
|
- model_name: "reranker"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "huggingface/BAAI/bge-reranker-v2-m3"
|
model: "huggingface/BAAI/bge-reranker-v2-m3"
|
||||||
api_base: "http://10.5.1.5:9787"
|
api_base: "http://10.5.30.5:9787"
|
||||||
# ---- ROCINANTE (RTX 4090, 24GB) — heavy reasoning ----
|
# ---- ROCINANTE (RTX 4090, 24GB) — heavy reasoning ----
|
||||||
- model_name: "heavy"
|
- model_name: "heavy"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
@@ -38,14 +38,14 @@ model_list:
|
|||||||
- model_name: "light"
|
- model_name: "light"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "ollama/deepseek-r1"
|
model: "ollama/deepseek-r1"
|
||||||
api_base: "http://10.5.1.16:11434"
|
api_base: "http://10.5.30.7:11434"
|
||||||
timeout: 60
|
timeout: 60
|
||||||
stream_timeout: 60
|
stream_timeout: 60
|
||||||
|
|
||||||
- model_name: "light"
|
- model_name: "light"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "ollama/qwen2.5:3b"
|
model: "ollama/qwen2.5:3b"
|
||||||
api_base: "http://10.5.1.16:11434"
|
api_base: "http://10.5.30.7:11434"
|
||||||
timeout: 60
|
timeout: 60
|
||||||
stream_timeout: 60
|
stream_timeout: 60
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ model_list:
|
|||||||
- model_name: "embed"
|
- model_name: "embed"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "ollama/nomic-embed-text:v1.5"
|
model: "ollama/nomic-embed-text:v1.5"
|
||||||
api_base: "http://10.5.1.16:11434"
|
api_base: "http://10.5.30.7:11434"
|
||||||
|
|
||||||
# ---- Direct model access (bypass tier routing) ----
|
# ---- Direct model access (bypass tier routing) ----
|
||||||
- model_name: "ollama/qwen3.6:latest"
|
- model_name: "ollama/qwen3.6:latest"
|
||||||
@@ -79,17 +79,17 @@ model_list:
|
|||||||
- model_name: "ollama/deepseek-r1"
|
- model_name: "ollama/deepseek-r1"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "ollama/deepseek-r1"
|
model: "ollama/deepseek-r1"
|
||||||
api_base: "http://10.5.1.16:11434"
|
api_base: "http://10.5.30.7:11434"
|
||||||
|
|
||||||
- model_name: "ollama/qwen2.5:3b"
|
- model_name: "ollama/qwen2.5:3b"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "ollama/qwen2.5:3b"
|
model: "ollama/qwen2.5:3b"
|
||||||
api_base: "http://10.5.1.16:11434"
|
api_base: "http://10.5.30.7:11434"
|
||||||
|
|
||||||
- model_name: "ollama/nomic-embed-text:v1.5"
|
- model_name: "ollama/nomic-embed-text:v1.5"
|
||||||
litellm_params:
|
litellm_params:
|
||||||
model: "ollama/nomic-embed-text:v1.5"
|
model: "ollama/nomic-embed-text:v1.5"
|
||||||
api_base: "http://10.5.1.16:11434"
|
api_base: "http://10.5.30.7:11434"
|
||||||
|
|
||||||
litellm_settings:
|
litellm_settings:
|
||||||
drop_params: true
|
drop_params: true
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ UNIFI_SITE=default
|
|||||||
UNIFI_VERIFY_TLS=false
|
UNIFI_VERIFY_TLS=false
|
||||||
|
|
||||||
# Backup automation scaffolding
|
# Backup automation scaffolding
|
||||||
SERENITY_BACKUP_HOST=root@10.5.1.5
|
SERENITY_BACKUP_HOST=root@10.5.30.5
|
||||||
SERENITY_BACKUP_ROOT=/mnt/user/backups/plausible-deniability
|
SERENITY_BACKUP_ROOT=/mnt/user/backups/plausible-deniability
|
||||||
PD_APPDATA_ROOT=/mnt/docker-ssd/docker/appdata
|
PD_APPDATA_ROOT=/mnt/docker-ssd/docker/appdata
|
||||||
PD_DATABASES_ROOT=/mnt/docker-ssd/docker/databases
|
PD_DATABASES_ROOT=/mnt/docker-ssd/docker/databases
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Repo-side deployment prerequisites remain:
|
|||||||
|
|
||||||
1. real values in `.env`
|
1. real values in `.env`
|
||||||
2. a writable dump destination on PD
|
2. a writable dump destination on PD
|
||||||
3. SSH trust / key path for Serenity (`root@10.5.1.5` by default)
|
3. SSH trust / key path for Serenity (`root@10.5.30.5` by default)
|
||||||
- recommended known_hosts path on PD: `/home/truenas_admin/.ssh/known_hosts`
|
- recommended known_hosts path on PD: `/home/truenas_admin/.ssh/known_hosts`
|
||||||
4. a real scheduler on the live host
|
4. a real scheduler on the live host
|
||||||
|
|
||||||
|
|||||||
270
automation/bin/unifi_stage_low_risk_objects.py
Executable file
270
automation/bin/unifi_stage_low_risk_objects.py
Executable file
@@ -0,0 +1,270 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Stage low-risk UniFi objects for the network cutover.
|
||||||
|
|
||||||
|
Default mode is read-only dry-run. Pass --apply to actually create missing objects.
|
||||||
|
Currently stages:
|
||||||
|
- Servers network object
|
||||||
|
- Servers port profile
|
||||||
|
|
||||||
|
This is intentionally idempotent: if the objects already exist, it reports them and exits cleanly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
from http.cookiejar import CookieJar
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
from urllib import error, request
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 20
|
||||||
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
|
REPO_ROOT = SCRIPT_DIR.parent.parent
|
||||||
|
DEFAULT_ENV_PATH = REPO_ROOT / 'automation' / '.env'
|
||||||
|
|
||||||
|
|
||||||
|
def load_env_file(path: Path) -> None:
|
||||||
|
if not path.exists():
|
||||||
|
return
|
||||||
|
for raw_line in path.read_text(encoding='utf-8').splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith('#') or '=' not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split('=', 1)
|
||||||
|
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||||
|
|
||||||
|
|
||||||
|
def env_bool(name: str, default: bool) -> bool:
|
||||||
|
value = os.environ.get(name)
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
return value.strip().lower() in {'1', 'true', 'yes', 'on'}
|
||||||
|
|
||||||
|
|
||||||
|
class UniFiClient:
|
||||||
|
def __init__(self, base_url: str, username: str, password: str, *, verify_tls: bool = True, timeout: int = DEFAULT_TIMEOUT) -> None:
|
||||||
|
self.base_url = base_url.rstrip('/')
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.timeout = timeout
|
||||||
|
self.cookie_jar = CookieJar()
|
||||||
|
self.csrf_token: str | None = None
|
||||||
|
handlers: list[Any] = [request.HTTPCookieProcessor(self.cookie_jar)]
|
||||||
|
if not verify_tls:
|
||||||
|
handlers.append(request.HTTPSHandler(context=ssl._create_unverified_context()))
|
||||||
|
self.opener = request.build_opener(*handlers)
|
||||||
|
|
||||||
|
def _json_request(self, method: str, path: str, body: dict[str, Any] | None = None) -> tuple[int, Any]:
|
||||||
|
url = self.base_url + path
|
||||||
|
data = None
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'doris-unifi-stage/1.0',
|
||||||
|
}
|
||||||
|
if method.upper() != 'GET' and self.csrf_token:
|
||||||
|
headers['X-Csrf-Token'] = self.csrf_token
|
||||||
|
if body is not None:
|
||||||
|
data = json.dumps(body).encode('utf-8')
|
||||||
|
req = request.Request(url, data=data, method=method.upper(), headers=headers)
|
||||||
|
try:
|
||||||
|
with self.opener.open(req, timeout=self.timeout) as resp:
|
||||||
|
if method.upper() == 'POST':
|
||||||
|
self.csrf_token = resp.headers.get('X-Csrf-Token', self.csrf_token)
|
||||||
|
raw = resp.read().decode('utf-8', 'replace')
|
||||||
|
ctype = resp.headers.get('Content-Type', '')
|
||||||
|
payload = json.loads(raw) if 'json' in ctype and raw else raw
|
||||||
|
return resp.status, payload
|
||||||
|
except error.HTTPError as exc:
|
||||||
|
raw = exc.read().decode('utf-8', 'replace')
|
||||||
|
try:
|
||||||
|
payload = json.loads(raw) if raw else {}
|
||||||
|
except Exception:
|
||||||
|
payload = raw
|
||||||
|
return exc.code, payload
|
||||||
|
|
||||||
|
def login(self) -> None:
|
||||||
|
status, payload = self._json_request('POST', '/api/auth/login', {
|
||||||
|
'username': self.username,
|
||||||
|
'password': self.password,
|
||||||
|
'rememberMe': True,
|
||||||
|
})
|
||||||
|
if status >= 300:
|
||||||
|
raise SystemExit(f'UniFi login failed: HTTP {status} {payload!r}')
|
||||||
|
|
||||||
|
def get(self, path: str) -> Any:
|
||||||
|
status, payload = self._json_request('GET', path)
|
||||||
|
if status >= 300:
|
||||||
|
raise SystemExit(f'UniFi GET {path} failed: HTTP {status} {payload!r}')
|
||||||
|
return payload.get('data', payload) if isinstance(payload, dict) else payload
|
||||||
|
|
||||||
|
def post(self, path: str, body: dict[str, Any]) -> Any:
|
||||||
|
status, payload = self._json_request('POST', path, body)
|
||||||
|
if status >= 300:
|
||||||
|
raise SystemExit(f'UniFi POST {path} failed: HTTP {status} {payload!r}')
|
||||||
|
return payload.get('data', payload) if isinstance(payload, dict) else payload
|
||||||
|
|
||||||
|
def site_path(self, site: str, suffix: str) -> str:
|
||||||
|
suffix = suffix.lstrip('/')
|
||||||
|
return f'/proxy/network/api/s/{site}/{suffix}'
|
||||||
|
|
||||||
|
|
||||||
|
def find_by_name(items: list[dict[str, Any]], name: str) -> dict[str, Any] | None:
|
||||||
|
return next((item for item in items if item.get('name') == name), None)
|
||||||
|
|
||||||
|
|
||||||
|
def servers_network_payload() -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
'name': 'Servers',
|
||||||
|
'purpose': 'corporate',
|
||||||
|
'enabled': True,
|
||||||
|
'networkgroup': 'LAN',
|
||||||
|
'setting_preference': 'manual',
|
||||||
|
'vlan_enabled': True,
|
||||||
|
'vlan': 30,
|
||||||
|
'ip_subnet': '10.5.30.1/24',
|
||||||
|
'dhcpd_enabled': True,
|
||||||
|
'dhcpd_start': '10.5.30.100',
|
||||||
|
'dhcpd_stop': '10.5.30.199',
|
||||||
|
'dhcpd_leasetime': 86400,
|
||||||
|
'dhcpd_gateway_enabled': False,
|
||||||
|
'dhcpd_dns_enabled': False,
|
||||||
|
'dhcpd_ntp_enabled': False,
|
||||||
|
'dhcpd_wins_enabled': False,
|
||||||
|
'dhcpd_time_offset_enabled': False,
|
||||||
|
'dhcpd_boot_enabled': False,
|
||||||
|
'dhcpd_conflict_checking': True,
|
||||||
|
'dhcp_relay_enabled': False,
|
||||||
|
'dhcp_relay_servers': [],
|
||||||
|
'domain_name': '',
|
||||||
|
'ipv6_interface_type': 'none',
|
||||||
|
'ipv6_setting_preference': 'manual',
|
||||||
|
'ipv6_aliases': [],
|
||||||
|
'ip_aliases': [],
|
||||||
|
'internet_access_enabled': True,
|
||||||
|
'network_isolation_enabled': False,
|
||||||
|
'gateway_type': 'default',
|
||||||
|
'nat_outbound_ip_addresses': [],
|
||||||
|
'mdns_enabled': True,
|
||||||
|
'lte_lan_enabled': True,
|
||||||
|
'auto_scale_enabled': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_servers_port_profile_payload(template: dict[str, Any], servers_network_id: str) -> dict[str, Any]:
|
||||||
|
payload = {
|
||||||
|
k: v for k, v in template.items()
|
||||||
|
if k not in {'_id', 'site_id', 'name', 'native_networkconf_id'}
|
||||||
|
}
|
||||||
|
payload['name'] = 'Servers'
|
||||||
|
payload['native_networkconf_id'] = servers_network_id
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description='Stage low-risk UniFi objects for the cutover.')
|
||||||
|
parser.add_argument('--env-file', default=str(DEFAULT_ENV_PATH), help='Path to .env containing UNIFI_* vars')
|
||||||
|
parser.add_argument('--site', help='UniFi site name; defaults to UNIFI_SITE or default')
|
||||||
|
parser.add_argument('--apply', action='store_true', help='Actually create missing objects. Default is dry-run only.')
|
||||||
|
parser.add_argument('--timeout', type=int, default=DEFAULT_TIMEOUT)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
load_env_file(Path(args.env_file))
|
||||||
|
base_url = os.environ.get('UNIFI_BASE_URL', 'https://10.5.0.1')
|
||||||
|
username = os.environ.get('UNIFI_USERNAME')
|
||||||
|
password = os.environ.get('UNIFI_PASSWORD')
|
||||||
|
site = args.site or os.environ.get('UNIFI_SITE', 'default')
|
||||||
|
verify_tls = env_bool('UNIFI_VERIFY_TLS', False)
|
||||||
|
|
||||||
|
if not username or not password:
|
||||||
|
raise SystemExit('Missing UNIFI_USERNAME or UNIFI_PASSWORD in env file/environment.')
|
||||||
|
|
||||||
|
client = UniFiClient(base_url, username, password, verify_tls=verify_tls, timeout=args.timeout)
|
||||||
|
client.login()
|
||||||
|
|
||||||
|
networks = client.get(client.site_path(site, 'rest/networkconf'))
|
||||||
|
port_profiles = client.get(client.site_path(site, 'rest/portconf'))
|
||||||
|
|
||||||
|
result: dict[str, Any] = {
|
||||||
|
'mode': 'apply' if args.apply else 'dry-run',
|
||||||
|
'site': site,
|
||||||
|
'changes': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
servers_network = find_by_name(networks, 'Servers')
|
||||||
|
if servers_network:
|
||||||
|
result['changes'].append({
|
||||||
|
'object': 'network',
|
||||||
|
'name': 'Servers',
|
||||||
|
'status': 'exists',
|
||||||
|
'id': servers_network.get('_id'),
|
||||||
|
'vlan': servers_network.get('vlan'),
|
||||||
|
'ip_subnet': servers_network.get('ip_subnet'),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
payload = servers_network_payload()
|
||||||
|
if args.apply:
|
||||||
|
created = client.post(client.site_path(site, 'rest/networkconf'), payload)
|
||||||
|
if isinstance(created, list):
|
||||||
|
created = created[0] if created else {}
|
||||||
|
servers_network = created
|
||||||
|
result['changes'].append({
|
||||||
|
'object': 'network',
|
||||||
|
'name': 'Servers',
|
||||||
|
'status': 'created',
|
||||||
|
'id': created.get('_id'),
|
||||||
|
'vlan': created.get('vlan'),
|
||||||
|
'ip_subnet': created.get('ip_subnet'),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
servers_network = {'_id': '<created-network-id>'}
|
||||||
|
result['changes'].append({
|
||||||
|
'object': 'network',
|
||||||
|
'name': 'Servers',
|
||||||
|
'status': 'would_create',
|
||||||
|
'payload': payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
servers_port_profile = find_by_name(port_profiles, 'Servers')
|
||||||
|
if servers_port_profile:
|
||||||
|
result['changes'].append({
|
||||||
|
'object': 'port_profile',
|
||||||
|
'name': 'Servers',
|
||||||
|
'status': 'exists',
|
||||||
|
'id': servers_port_profile.get('_id'),
|
||||||
|
'native_networkconf_id': servers_port_profile.get('native_networkconf_id'),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
trusted_template = find_by_name(port_profiles, 'Trusted')
|
||||||
|
if not trusted_template:
|
||||||
|
raise SystemExit('Could not find existing Trusted port profile to clone.')
|
||||||
|
payload = build_servers_port_profile_payload(trusted_template, servers_network['_id'])
|
||||||
|
if args.apply:
|
||||||
|
created = client.post(client.site_path(site, 'rest/portconf'), payload)
|
||||||
|
if isinstance(created, list):
|
||||||
|
created = created[0] if created else {}
|
||||||
|
result['changes'].append({
|
||||||
|
'object': 'port_profile',
|
||||||
|
'name': 'Servers',
|
||||||
|
'status': 'created',
|
||||||
|
'id': created.get('_id'),
|
||||||
|
'native_networkconf_id': created.get('native_networkconf_id'),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
result['changes'].append({
|
||||||
|
'object': 'port_profile',
|
||||||
|
'name': 'Servers',
|
||||||
|
'status': 'would_create',
|
||||||
|
'payload': payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
print(json.dumps(result, indent=2, sort_keys=True))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -27,7 +27,7 @@ resources:
|
|||||||
- docker-host
|
- docker-host
|
||||||
- ai-light-tier
|
- ai-light-tier
|
||||||
labels:
|
labels:
|
||||||
lan-ip: 10.5.1.6
|
lan-ip: 10.5.30.6
|
||||||
os: TrueNAS Scale 25.10.2.1
|
os: TrueNAS Scale 25.10.2.1
|
||||||
docker-root: /mnt/docker-ssd/docker/docker-data
|
docker-root: /mnt/docker-ssd/docker/docker-data
|
||||||
compose-root: /mnt/docker-ssd/docker/compose
|
compose-root: /mnt/docker-ssd/docker/compose
|
||||||
@@ -58,7 +58,7 @@ resources:
|
|||||||
- storage
|
- storage
|
||||||
- reranker
|
- reranker
|
||||||
labels:
|
labels:
|
||||||
lan-ip: 10.5.1.5
|
lan-ip: 10.5.30.5
|
||||||
tailscale-ip: 100.94.87.79
|
tailscale-ip: 100.94.87.79
|
||||||
os: Unraid 7.2.4
|
os: Unraid 7.2.4
|
||||||
notes: |-
|
notes: |-
|
||||||
@@ -99,7 +99,7 @@ resources:
|
|||||||
- offline-knowledge
|
- offline-knowledge
|
||||||
- game-server
|
- game-server
|
||||||
labels:
|
labels:
|
||||||
lan-ip: 10.5.1.16
|
lan-ip: 10.5.30.7
|
||||||
os: Ubuntu 25.10
|
os: Ubuntu 25.10
|
||||||
notes: |-
|
notes: |-
|
||||||
Offline knowledge, game server, and local services host.
|
Offline knowledge, game server, and local services host.
|
||||||
@@ -244,7 +244,7 @@ resources:
|
|||||||
os: truenas-scale-25.10.2.1
|
os: truenas-scale-25.10.2.1
|
||||||
cores: 16
|
cores: 16
|
||||||
ram: 32
|
ram: 32
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
runsOn:
|
runsOn:
|
||||||
- plausibledeniability
|
- plausibledeniability
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ resources:
|
|||||||
os: unraid-7.2.4
|
os: unraid-7.2.4
|
||||||
cores: 12
|
cores: 12
|
||||||
ram: 102
|
ram: 102
|
||||||
ip: 10.5.1.5
|
ip: 10.5.30.5
|
||||||
runsOn:
|
runsOn:
|
||||||
- serenity
|
- serenity
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ resources:
|
|||||||
os: ubuntu-25.10
|
os: ubuntu-25.10
|
||||||
cores: 4
|
cores: 4
|
||||||
ram: 32
|
ram: 32
|
||||||
ip: 10.5.1.16
|
ip: 10.5.30.7
|
||||||
runsOn:
|
runsOn:
|
||||||
- nomad
|
- nomad
|
||||||
|
|
||||||
@@ -319,37 +319,37 @@ resources:
|
|||||||
- kind: Service
|
- kind: Service
|
||||||
name: homepage
|
name: homepage
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3300
|
port: 3300
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3300
|
url: http://10.5.30.6:3300
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: dockhand
|
name: dockhand
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3230
|
port: 3230
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3230
|
url: http://10.5.30.6:3230
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: uptime-kuma
|
name: uptime-kuma
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3001
|
port: 3001
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3001
|
url: http://10.5.30.6:3001
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: gotify
|
name: gotify
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8443
|
port: 8443
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: https://gotify.paccoco.com
|
url: https://gotify.paccoco.com
|
||||||
@@ -359,50 +359,50 @@ resources:
|
|||||||
- kind: Service
|
- kind: Service
|
||||||
name: gitea
|
name: gitea
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3002
|
port: 3002
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3002
|
url: http://10.5.30.6:3002
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: gitea-ssh
|
name: gitea-ssh
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 2222
|
port: 2222
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: ssh://git@10.5.1.6:2222/
|
url: ssh://git@10.5.30.6:2222/
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: shlink
|
name: shlink
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8087
|
port: 8087
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8087
|
url: http://10.5.30.6:8087
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: shlink-web-client
|
name: shlink-web-client
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8088
|
port: 8088
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8088
|
url: http://10.5.30.6:8088
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: rackpeek
|
name: rackpeek
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8283
|
port: 8283
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8283
|
url: http://10.5.30.6:8283
|
||||||
tags:
|
tags:
|
||||||
- source-of-truth-target
|
- source-of-truth-target
|
||||||
runsOn:
|
runsOn:
|
||||||
@@ -425,7 +425,7 @@ resources:
|
|||||||
- kind: Service
|
- kind: Service
|
||||||
name: authelia
|
name: authelia
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 9091
|
port: 9091
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: https://auth.paccoco.com
|
url: https://auth.paccoco.com
|
||||||
@@ -435,60 +435,60 @@ resources:
|
|||||||
- kind: Service
|
- kind: Service
|
||||||
name: litellm
|
name: litellm
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 4000
|
port: 4000
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:4000
|
url: http://10.5.30.6:4000
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: openwebui
|
name: openwebui
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8282
|
port: 8282
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8282
|
url: http://10.5.30.6:8282
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: qdrant
|
name: qdrant
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 6333
|
port: 6333
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:6333
|
url: http://10.5.30.6:6333
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: searxng
|
name: searxng
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8888
|
port: 8888
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8888
|
url: http://10.5.30.6:8888
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: ollama-light-tier
|
name: ollama-light-tier
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 11434
|
port: 11434
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:11434
|
url: http://10.5.30.6:11434
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: whisper-cpu
|
name: whisper-cpu
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.16
|
ip: 10.5.30.7
|
||||||
port: 8786
|
port: 8786
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.16:8786
|
url: http://10.5.30.7:8786
|
||||||
runsOn:
|
runsOn:
|
||||||
- nomad-ubuntu
|
- nomad-ubuntu
|
||||||
|
|
||||||
@@ -519,207 +519,207 @@ resources:
|
|||||||
- kind: Service
|
- kind: Service
|
||||||
name: reranker
|
name: reranker
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.5
|
ip: 10.5.30.5
|
||||||
port: 9787
|
port: 9787
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.5:9787
|
url: http://10.5.30.5:9787
|
||||||
runsOn:
|
runsOn:
|
||||||
- serenity-unraid
|
- serenity-unraid
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: plex
|
name: plex
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 32400
|
port: 32400
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:32400
|
url: http://10.5.30.6:32400
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: tautulli
|
name: tautulli
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8181
|
port: 8181
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8181
|
url: http://10.5.30.6:8181
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: audiobookshelf
|
name: audiobookshelf
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 13358
|
port: 13358
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:13358
|
url: http://10.5.30.6:13358
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: calibre-web
|
name: calibre-web
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8183
|
port: 8183
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8183
|
url: http://10.5.30.6:8183
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: seerr
|
name: seerr
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 5055
|
port: 5055
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:5055
|
url: http://10.5.30.6:5055
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: immich
|
name: immich
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 2283
|
port: 2283
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:2283
|
url: http://10.5.30.6:2283
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: paperless-ngx
|
name: paperless-ngx
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8083
|
port: 8083
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8083
|
url: http://10.5.30.6:8083
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: karakeep
|
name: karakeep
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3100
|
port: 3100
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3100
|
url: http://10.5.30.6:3100
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: n8n
|
name: n8n
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 5678
|
port: 5678
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:5678
|
url: http://10.5.30.6:5678
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: kitchenowl
|
name: kitchenowl
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8086
|
port: 8086
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8086
|
url: http://10.5.30.6:8086
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: donetick
|
name: donetick
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 2021
|
port: 2021
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:2021
|
url: http://10.5.30.6:2021
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: dakboard-bridge
|
name: dakboard-bridge
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 5087
|
port: 5087
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:5087
|
url: http://10.5.30.6:5087
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: qui
|
name: qui
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 7476
|
port: 7476
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:7476
|
url: http://10.5.30.6:7476
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: home-assistant
|
name: home-assistant
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8123
|
port: 8123
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8123
|
url: http://10.5.30.6:8123
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: kima-hub
|
name: kima-hub
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3333
|
port: 3333
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3333
|
url: http://10.5.30.6:3333
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: grafana
|
name: grafana
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 3000
|
port: 3000
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:3000
|
url: http://10.5.30.6:3000
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: prometheus
|
name: prometheus
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 9090
|
port: 9090
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:9090
|
url: http://10.5.30.6:9090
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: meshmonitor
|
name: meshmonitor
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8081
|
port: 8081
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8081
|
url: http://10.5.30.6:8081
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: tileserver-gl
|
name: tileserver-gl
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.6
|
ip: 10.5.30.6
|
||||||
port: 8082
|
port: 8082
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: http://10.5.1.6:8082
|
url: http://10.5.30.6:8082
|
||||||
runsOn:
|
runsOn:
|
||||||
- pd-truenas
|
- pd-truenas
|
||||||
|
|
||||||
- kind: Service
|
- kind: Service
|
||||||
name: pelican-panel
|
name: pelican-panel
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.16
|
ip: 10.5.30.7
|
||||||
port: 80
|
port: 80
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: https://panel.paccoco.com
|
url: https://panel.paccoco.com
|
||||||
@@ -729,7 +729,7 @@ resources:
|
|||||||
- kind: Service
|
- kind: Service
|
||||||
name: wings-node-api
|
name: wings-node-api
|
||||||
network:
|
network:
|
||||||
ip: 10.5.1.16
|
ip: 10.5.30.7
|
||||||
port: 8443
|
port: 8443
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
url: https://node1.paccoco.com
|
url: https://node1.paccoco.com
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ Full homelab stack as of 2026-05-09. All 6 expansion phases are complete and dep
|
|||||||
|
|
||||||
| Server | OS | IP | Role |
|
| Server | OS | IP | Role |
|
||||||
|--------|----|----|------|
|
|--------|----|----|------|
|
||||||
| **PlausibleDeniability (PD)** | TrueNAS Scale 25.10.2.1 | 10.5.1.6 | Primary Docker host — all compose stacks |
|
| **PlausibleDeniability (PD)** | TrueNAS Scale 25.10.2.1 | 10.5.30.6 | Primary Docker host — all compose stacks |
|
||||||
| **Serenity** | Unraid 7.2.4 | 10.5.1.5 | NAS, ARR stack, CPU reranker |
|
| **Serenity** | Unraid 7.2.4 | 10.5.30.5 | NAS, ARR stack, CPU reranker |
|
||||||
| **N.O.M.A.D.** | Ubuntu 25.10 | 10.5.1.16 | Offline knowledge, game servers, and standalone local services |
|
| **N.O.M.A.D.** | Ubuntu 25.10 | 10.5.30.7 | Offline knowledge, game servers, and standalone local services |
|
||||||
| **Rocinante** | (bare metal) | 10.5.1.112 | Heavy Ollama models (RTX 4090) |
|
| **Rocinante** | (bare metal) | 10.5.1.112 | Heavy Ollama models (RTX 4090) |
|
||||||
|
|
||||||
## Network
|
## Network
|
||||||
@@ -24,7 +24,7 @@ Full homelab stack as of 2026-05-09. All 6 expansion phases are complete and dep
|
|||||||
- **Homepage** (3300) — dashboard / service index
|
- **Homepage** (3300) — dashboard / service index
|
||||||
- **Uptime Kuma** (3001) — monitoring and alerting
|
- **Uptime Kuma** (3001) — monitoring and alerting
|
||||||
- **Gotify** (8443) — push notifications
|
- **Gotify** (8443) — push notifications
|
||||||
- **Gitea** (3002 web / 2222 SSH) — self-hosted git; internal SSH remote uses `ssh://git@10.5.1.6:2222/<owner>/<repo>.git`
|
- **Gitea** (3002 web / 2222 SSH) — self-hosted git; internal SSH remote uses `ssh://git@10.5.30.6:2222/<owner>/<repo>.git`
|
||||||
- **Dockhand** (3230) — Docker management UI
|
- **Dockhand** (3230) — Docker management UI
|
||||||
|
|
||||||
### AI Layer
|
### AI Layer
|
||||||
@@ -61,4 +61,4 @@ Full homelab stack as of 2026-05-09. All 6 expansion phases are complete and dep
|
|||||||
3. **External Docker networks** — `ai-services` bridges all AI containers across compose files; `ix-databases_shared-databases` provides shared DB access
|
3. **External Docker networks** — `ai-services` bridges all AI containers across compose files; `ix-databases_shared-databases` provides shared DB access
|
||||||
4. **NFS for Immich** — photo library lives on Serenity's large array; PD mounts on demand (not at boot)
|
4. **NFS for Immich** — photo library lives on Serenity's large array; PD mounts on demand (not at boot)
|
||||||
5. **TrueNAS manages Docker** — never use `systemctl restart docker` on PD; use `docker compose` or `docker restart` for individual containers
|
5. **TrueNAS manages Docker** — never use `systemctl restart docker` on PD; use `docker compose` or `docker restart` for individual containers
|
||||||
6. **Credential hygiene** — Gitea pushes from PD/NOMAD use internal SSH (`ssh://git@10.5.1.6:2222/<owner>/<repo>.git`); GitHub remote uses SSH key (`/root/.ssh/id_gitea`)
|
6. **Credential hygiene** — Gitea pushes from PD/NOMAD use internal SSH (`ssh://git@10.5.30.6:2222/<owner>/<repo>.git`); GitHub remote uses SSH key (`/root/.ssh/id_gitea`)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Serenity (NAS / Media Ingestion)
|
## Serenity (NAS / Media Ingestion)
|
||||||
- **OS:** Unraid 7.2.4
|
- **OS:** Unraid 7.2.4
|
||||||
- **IP:** 10.5.1.5 | Tailscale: 100.94.87.79
|
- **IP:** 10.5.30.5 | Tailscale: 100.94.87.79
|
||||||
- **CPU:** Dual Xeon X5690 (6c/12t each — 24 threads total)
|
- **CPU:** Dual Xeon X5690 (6c/12t each — 24 threads total)
|
||||||
- **RAM:** 102GB
|
- **RAM:** 102GB
|
||||||
- **GPU:** None (Matrox IPMI only)
|
- **GPU:** None (Matrox IPMI only)
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
## N.O.M.A.D. (Offline Knowledge + Game Servers)
|
## N.O.M.A.D. (Offline Knowledge + Game Servers)
|
||||||
- **OS:** Ubuntu 25.10 (bare metal)
|
- **OS:** Ubuntu 25.10 (bare metal)
|
||||||
- **IP:** 10.5.1.16
|
- **IP:** 10.5.30.7
|
||||||
- **CPU:** Intel Core i7-4770K @ 3.50GHz (4c/8t, up to 3.9GHz)
|
- **CPU:** Intel Core i7-4770K @ 3.50GHz (4c/8t, up to 3.9GHz)
|
||||||
- **RAM:** 32GB DDR3 1600MHz (4×8GB)
|
- **RAM:** 32GB DDR3 1600MHz (4×8GB)
|
||||||
- **GPU:** NVIDIA GeForce GTX 1080 (8GB VRAM)
|
- **GPU:** NVIDIA GeForce GTX 1080 (8GB VRAM)
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
- **Main LAN:** 10.5.1.x (Trusted VLAN)
|
- **Main LAN:** 10.5.1.x (Trusted VLAN)
|
||||||
- **Management VLAN:** 10.5.0.x
|
- **Management VLAN:** 10.5.0.x
|
||||||
- **Other VLANs:** Cameras, Guest, IoT / Old IoT
|
- **Other VLANs:** Cameras, Guest, IoT / Old IoT
|
||||||
- **Serenity IP:** 10.5.1.5
|
- **Serenity IP:** 10.5.30.5
|
||||||
- **N.O.M.A.D. IP:** 10.5.1.16
|
- **N.O.M.A.D. IP:** 10.5.30.7
|
||||||
- **PlausibleDeniability:** 10.5.1.6 (static on LAN)
|
- **PlausibleDeniability:** 10.5.30.6 (static on LAN)
|
||||||
|
|
||||||
## Remote Access
|
## Remote Access
|
||||||
- **Pangolin VPS + Newt:** Services exposed as subdomains via reverse proxy. Newt agents run on PD and N.O.M.A.D.
|
- **Pangolin VPS + Newt:** Services exposed as subdomains via reverse proxy. Newt agents run on PD and N.O.M.A.D.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ All homelab services, their hosts, ports, and current status.
|
|||||||
| Dockhand | 3230 | http://pd:3230 | ✅ Active |
|
| Dockhand | 3230 | http://pd:3230 | ✅ Active |
|
||||||
| Uptime Kuma | 3001 | http://pd:3001 | ✅ Active |
|
| Uptime Kuma | 3001 | http://pd:3001 | ✅ Active |
|
||||||
| Gotify | 8443 | https://gotify.paccoco.com | ✅ Active |
|
| Gotify | 8443 | https://gotify.paccoco.com | ✅ Active |
|
||||||
| Gitea | 3002 / 2222 | http://pd:3002 / http://10.5.1.6:3002 (SSH: `ssh://git@10.5.1.6:2222/<owner>/<repo>.git`) | ✅ Active |
|
| Gitea | 3002 / 2222 | http://pd:3002 / http://10.5.30.6:3002 (SSH: `ssh://git@10.5.30.6:2222/<owner>/<repo>.git`) | ✅ Active |
|
||||||
| Shlink | 8087 | http://pd:8087 | ✅ Active |
|
| Shlink | 8087 | http://pd:8087 | ✅ Active |
|
||||||
| Shlink Web Client | 8088 | http://pd:8088 | ✅ Active |
|
| Shlink Web Client | 8088 | http://pd:8088 | ✅ Active |
|
||||||
| RackPeek | 8283 | http://pd:8283 | ✅ Active |
|
| RackPeek | 8283 | http://pd:8283 | ✅ Active |
|
||||||
@@ -33,12 +33,12 @@ All homelab services, their hosts, ports, and current status.
|
|||||||
| LiteLLM | PD | 4000 | ✅ Active |
|
| LiteLLM | PD | 4000 | ✅ Active |
|
||||||
| OpenWebUI | PD | 8282 | ✅ Active |
|
| OpenWebUI | PD | 8282 | ✅ Active |
|
||||||
| Qdrant | PD | 6333 (HTTP) / 6334 (gRPC) | ✅ Active |
|
| Qdrant | PD | 6333 (HTTP) / 6334 (gRPC) | ✅ Active |
|
||||||
| Whisper (faster-whisper, CPU) | N.O.M.A.D. (10.5.1.16) | 8786 | ✅ Active |
|
| Whisper (faster-whisper, CPU) | N.O.M.A.D. (10.5.30.7) | 8786 | ✅ Active |
|
||||||
| Whisper (faster-whisper, CUDA large-v3) | Rocinante (10.5.1.112) | 8787 | ⚠️ Unreachable from N.O.M.A.D. during 2026-05-21 Doris validation |
|
| Whisper (faster-whisper, CUDA large-v3) | Rocinante (10.5.1.112) | 8787 | ⚠️ Unreachable from N.O.M.A.D. during 2026-05-21 Doris validation |
|
||||||
| SearXNG | PD | 8888 | ✅ Active |
|
| SearXNG | PD | 8888 | ✅ Active |
|
||||||
| Ollama — light tier | PD (10.5.1.6) | 11434 | ✅ Active |
|
| Ollama — light tier | PD (10.5.30.6) | 11434 | ✅ Active |
|
||||||
| Ollama — heavy tier | Rocinante (10.5.1.112) | 11434 | ⚠️ Unreachable from N.O.M.A.D. during 2026-05-21 Doris validation |
|
| Ollama — heavy tier | Rocinante (10.5.1.112) | 11434 | ⚠️ Unreachable from N.O.M.A.D. during 2026-05-21 Doris validation |
|
||||||
| Reranker (TEI) | Serenity (10.5.1.5) | 9787 | ✅ Active |
|
| Reranker (TEI) | Serenity (10.5.30.5) | 9787 | ✅ Active |
|
||||||
|
|
||||||
OpenClaw is no longer treated as an active PD service in the operator inventory. Only helper-artifact references remain under the school-intake planning docs.
|
OpenClaw is no longer treated as an active PD service in the operator inventory. Only helper-artifact references remain under the school-intake planning docs.
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ OpenClaw is no longer treated as an active PD service in the operator inventory.
|
|||||||
|
|
||||||
| Service | Host | Port | Status |
|
| Service | Host | Port | Status |
|
||||||
|---------|------|------|--------|
|
|---------|------|------|--------|
|
||||||
| MeshCore to MQTT relay | N.O.M.A.D. (10.5.1.16) | outbound MQTT only | ✅ Running as Docker container `mctomqtt` (`mctomqtt:latest`); config bind-mounted from `/etc/mctomqtt` |
|
| MeshCore to MQTT relay | N.O.M.A.D. (10.5.30.7) | outbound MQTT only | ✅ Running as Docker container `mctomqtt` (`mctomqtt:latest`); config bind-mounted from `/etc/mctomqtt` |
|
||||||
|
|
||||||
## Mapping / Geo (PD)
|
## Mapping / Geo (PD)
|
||||||
|
|
||||||
@@ -95,11 +95,11 @@ OpenClaw is no longer treated as an active PD service in the operator inventory.
|
|||||||
|---------|------|--------|
|
|---------|------|--------|
|
||||||
| TileServer GL | 8082 | ✅ Active |
|
| TileServer GL | 8082 | ✅ Active |
|
||||||
|
|
||||||
## Game Servers (N.O.M.A.D. — 10.5.1.16)
|
## Game Servers (N.O.M.A.D. — 10.5.30.7)
|
||||||
|
|
||||||
| Service | Port | URL | Status | Notes |
|
| Service | Port | URL | Status | Notes |
|
||||||
|---------|------|-----|--------|-------|
|
|---------|------|-----|--------|-------|
|
||||||
| Pelican Panel | 80 / 443 | https://panel.paccoco.com | ✅ Active | For local health/probe use `http://10.5.1.16:8080`; local TLS path is self-signed and `panel.paccoco.com` resolves to `127.0.0.1` on N.O.M.A.D. |
|
| Pelican Panel | 80 / 443 | https://panel.paccoco.com | ✅ Active | For local health/probe use `http://10.5.30.7:8080`; local TLS path is self-signed and `panel.paccoco.com` resolves to `127.0.0.1` on N.O.M.A.D. |
|
||||||
| Wings Node API | 8443 / 443 | https://node1.paccoco.com | ✅ Active | `401` from the public endpoint is expected and means the daemon is reachable/auth-protected |
|
| Wings Node API | 8443 / 443 | https://node1.paccoco.com | ✅ Active | `401` from the public endpoint is expected and means the daemon is reachable/auth-protected |
|
||||||
| Minecraft Server 25565 | 25565/tcp+udp | managed via Pelican | ✅ Active | Wings-managed Java server |
|
| Minecraft Server 25565 | 25565/tcp+udp | managed via Pelican | ✅ Active | Wings-managed Java server |
|
||||||
| Minecraft Server 25566 | 25566/tcp+udp | managed via Pelican | ✅ Active | Wings-managed Java/NeoForge server |
|
| Minecraft Server 25566 | 25566/tcp+udp | managed via Pelican | ✅ Active | Wings-managed Java/NeoForge server |
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Deploy the PD → Serenity backup runner from the real compose path on PD:
|
|||||||
## Suggested `.env` values to review
|
## Suggested `.env` values to review
|
||||||
|
|
||||||
```dotenv
|
```dotenv
|
||||||
SERENITY_BACKUP_HOST=root@10.5.1.5
|
SERENITY_BACKUP_HOST=root@10.5.30.5
|
||||||
SERENITY_BACKUP_ROOT=/mnt/user/backups/plausible-deniability
|
SERENITY_BACKUP_ROOT=/mnt/user/backups/plausible-deniability
|
||||||
PD_APPDATA_ROOT=/mnt/docker-ssd/docker/appdata
|
PD_APPDATA_ROOT=/mnt/docker-ssd/docker/appdata
|
||||||
PD_DATABASES_ROOT=/mnt/docker-ssd/docker/databases
|
PD_DATABASES_ROOT=/mnt/docker-ssd/docker/databases
|
||||||
@@ -79,7 +79,7 @@ Quarterly restore verification:
|
|||||||
```bash
|
```bash
|
||||||
tail -100 /mnt/tank/docker/backups/pd-backups.log
|
tail -100 /mnt/tank/docker/backups/pd-backups.log
|
||||||
ls -lh /mnt/tank/docker/backups/db-dumps
|
ls -lh /mnt/tank/docker/backups/db-dumps
|
||||||
ssh root@10.5.1.5 'find /mnt/user/backups/plausible-deniability -maxdepth 2 -type d | sort | head -40'
|
ssh root@10.5.30.5 'find /mnt/user/backups/plausible-deniability -maxdepth 2 -type d | sort | head -40'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Restore verification
|
## Restore verification
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ All `.env` files live alongside their `docker-compose.yaml`:
|
|||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Gitea repo** | `https://gitea.paccoco.com/fizzlepoof/homelab-secrets` (private) |
|
| **Gitea repo** | `https://gitea.paccoco.com/fizzlepoof/homelab-secrets` (private) |
|
||||||
| **SSH remote** | `ssh://git@10.5.1.6:2222/fizzlepoof/homelab-secrets.git` |
|
| **SSH remote** | `ssh://git@10.5.30.6:2222/fizzlepoof/homelab-secrets.git` |
|
||||||
| **Local path on PD** | `/mnt/docker-ssd/docker/secrets/` |
|
| **Local path on PD** | `/mnt/docker-ssd/docker/secrets/` |
|
||||||
| **Symmetric key location** | `/mnt/docker-ssd/docker/secrets/.git-crypt-secrets.key` (if copied there) or retrieve from password manager |
|
| **Symmetric key location** | `/mnt/docker-ssd/docker/secrets/.git-crypt-secrets.key` (if copied there) or retrieve from password manager |
|
||||||
| **Key backup** | Password manager + `C:\Users\Fizzlepoof\Downloads\.git-crypt-secrets.key` |
|
| **Key backup** | Password manager + `C:\Users\Fizzlepoof\Downloads\.git-crypt-secrets.key` |
|
||||||
@@ -150,7 +150,7 @@ sudo cp /root/bin/git-crypt /mnt/docker-ssd/bin/git-crypt
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install git-crypt first (see above), then:
|
# Install git-crypt first (see above), then:
|
||||||
git clone ssh://git@10.5.1.6:2222/fizzlepoof/homelab-secrets.git /mnt/docker-ssd/docker/secrets
|
git clone ssh://git@10.5.30.6:2222/fizzlepoof/homelab-secrets.git /mnt/docker-ssd/docker/secrets
|
||||||
cd /mnt/docker-ssd/docker/secrets
|
cd /mnt/docker-ssd/docker/secrets
|
||||||
git-crypt unlock /path/to/.git-crypt-secrets.key
|
git-crypt unlock /path/to/.git-crypt-secrets.key
|
||||||
```
|
```
|
||||||
@@ -166,7 +166,7 @@ On a fresh PD after reinstalling TrueNAS:
|
|||||||
1. **Install git-crypt** (Option 1 above — docker is available immediately after TrueNAS install)
|
1. **Install git-crypt** (Option 1 above — docker is available immediately after TrueNAS install)
|
||||||
2. **Restore secrets repo:**
|
2. **Restore secrets repo:**
|
||||||
```bash
|
```bash
|
||||||
git clone ssh://git@10.5.1.6:2222/fizzlepoof/homelab-secrets.git /mnt/docker-ssd/docker/secrets
|
git clone ssh://git@10.5.30.6:2222/fizzlepoof/homelab-secrets.git /mnt/docker-ssd/docker/secrets
|
||||||
cd /mnt/docker-ssd/docker/secrets
|
cd /mnt/docker-ssd/docker/secrets
|
||||||
git-crypt unlock /path/to/.git-crypt-secrets.key
|
git-crypt unlock /path/to/.git-crypt-secrets.key
|
||||||
```
|
```
|
||||||
@@ -178,7 +178,7 @@ On a fresh PD after reinstalling TrueNAS:
|
|||||||
```
|
```
|
||||||
4. **Clone the main stacks repo:**
|
4. **Clone the main stacks repo:**
|
||||||
```bash
|
```bash
|
||||||
git clone ssh://git@10.5.1.6:2222/fizzlepoof/truenas-stacks.git /mnt/docker-ssd/docker/compose
|
git clone ssh://git@10.5.30.6:2222/fizzlepoof/truenas-stacks.git /mnt/docker-ssd/docker/compose
|
||||||
```
|
```
|
||||||
5. **Redeploy stacks** per `DEPLOYMENT_CHECKLIST.md`
|
5. **Redeploy stacks** per `DEPLOYMENT_CHECKLIST.md`
|
||||||
6. **After any future live `.env` edit**, immediately run:
|
6. **After any future live `.env` edit**, immediately run:
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ NOMAD exception: standalone live deployments belong under `/opt/<service>` rathe
|
|||||||
|
|
||||||
Remotes:
|
Remotes:
|
||||||
- GitHub: `git@github.com:Paccoco/truenas-stacks.git`
|
- GitHub: `git@github.com:Paccoco/truenas-stacks.git`
|
||||||
- Gitea web: `http://10.5.1.6:3002/fizzlepoof/truenas-stacks`
|
- Gitea web: `http://10.5.30.6:3002/fizzlepoof/truenas-stacks`
|
||||||
- Gitea SSH: `ssh://git@10.5.1.6:2222/fizzlepoof/truenas-stacks.git`
|
- Gitea SSH: `ssh://git@10.5.30.6:2222/fizzlepoof/truenas-stacks.git`
|
||||||
|
|
||||||
Use the internal Gitea SSH remote from PD/NOMAD. Port `2222` is SSH, not HTTP.
|
Use the internal Gitea SSH remote from PD/NOMAD. Port `2222` is SSH, not HTTP.
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ After changing a healthcheck, must `down` then `up` (not just restart).
|
|||||||
cd /mnt/docker-ssd/docker/compose
|
cd /mnt/docker-ssd/docker/compose
|
||||||
git add .
|
git add .
|
||||||
git commit -m "description"
|
git commit -m "description"
|
||||||
git push ssh://git@10.5.1.6:2222/fizzlepoof/truenas-stacks.git main
|
git push ssh://git@10.5.30.6:2222/fizzlepoof/truenas-stacks.git main
|
||||||
```
|
```
|
||||||
|
|
||||||
All `.env` files are gitignored. Only compose files, `.env.example`, and init scripts are committed.
|
All `.env` files are gitignored. Only compose files, `.env.example`, and init scripts are committed.
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ sudo docker logs --tail 60 kima
|
|||||||
|
|
||||||
## Post-deploy checks
|
## Post-deploy checks
|
||||||
|
|
||||||
1. Open `http://10.5.1.6:3333`
|
1. Open `http://10.5.30.6:3333`
|
||||||
2. Confirm the app starts and initial setup loads
|
2. Confirm the app starts and initial setup loads
|
||||||
3. Confirm `/music` is visible to Kima
|
3. Confirm `/music` is visible to Kima
|
||||||
4. Confirm Kima creates and uses `/data` persistence correctly
|
4. Confirm Kima creates and uses `/data` persistence correctly
|
||||||
@@ -103,7 +103,7 @@ sudo docker logs --tail 60 kima
|
|||||||
|
|
||||||
Verified on 2026-05-15:
|
Verified on 2026-05-15:
|
||||||
- container `kima` running and healthy
|
- container `kima` running and healthy
|
||||||
- `http://10.5.1.6:3333/api/health` returned status OK
|
- `http://10.5.30.6:3333/api/health` returned status OK
|
||||||
- live mount narrowed to `/mnt/unraid/data/media/music:/music`
|
- live mount narrowed to `/mnt/unraid/data/media/music:/music`
|
||||||
|
|
||||||
## Optional integrations / secrets
|
## Optional integrations / secrets
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
# Pi-hole Deployment Plan
|
# Pi-hole Deployment Plan
|
||||||
|
|
||||||
**Status:** PD primary and NOMAD backup are live and working; DNS clients are pointed at VIP `10.5.1.53`. RPi4 remains optional future BACKUP2 work.
|
**Status:** Historical Trusted-VLAN design. PD primary is still valid, but after NOMAD moved to Servers (`10.5.30.7`) its participation in the old Trusted-side VIP `10.5.30.53` was disabled to restore correct routing. Do not reuse this document for NOMAD as-is until a same-subnet HA redesign exists.
|
||||||
**Architecture:** 3-node HA cluster with Keepalived VIP, Unbound recursive DNS, Nebula Sync
|
**Architecture:** 3-node HA cluster with Keepalived VIP, Unbound recursive DNS, Nebula Sync
|
||||||
|
|
||||||
|
> **Warning:** The examples below describe the original Trusted-side plan where NOMAD was `10.5.30.7` and shared VIP `10.5.30.53/24`. They are retained as historical deployment notes, not the current NOMAD runtime truth.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
```
|
```
|
||||||
[All LAN clients]
|
[All LAN clients]
|
||||||
↓ DNS: 10.5.1.53 (Virtual IP — floats to whichever node is MASTER)
|
↓ DNS: 10.5.30.53 (Virtual IP — floats to whichever node is MASTER)
|
||||||
↓
|
↓
|
||||||
┌─────┴──────────────────────────────────┐
|
┌─────┴──────────────────────────────────┐
|
||||||
│ Keepalived VIP │
|
│ Keepalived VIP │
|
||||||
│ 10.5.1.53/24 │
|
│ 10.5.30.53/24 │
|
||||||
└──────┬──────────────┬──────────────────┘
|
└──────┬──────────────┬──────────────────┘
|
||||||
│ │ │
|
│ │ │
|
||||||
PD (MASTER) N.O.M.A.D. (BACKUP1) RPi4 (BACKUP2)
|
PD (MASTER) N.O.M.A.D. (BACKUP1) RPi4 (BACKUP2)
|
||||||
10.5.1.6 10.5.1.16 10.5.1.X
|
10.5.30.6 10.5.30.7 10.5.1.X
|
||||||
priority 100 priority 90 priority 80
|
priority 100 priority 90 priority 80
|
||||||
Pi-hole :53 Pi-hole :53 Pi-hole :53
|
Pi-hole :53 Pi-hole :53 Pi-hole :53
|
||||||
Unbound :5335 Unbound :5335 Unbound :5335
|
Unbound :5335 Unbound :5335 Unbound :5335
|
||||||
@@ -28,11 +30,11 @@
|
|||||||
(recursive — no third-party upstream)
|
(recursive — no third-party upstream)
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Keepalived** floats the VIP `10.5.1.53` to whichever node is alive with the highest priority
|
- **Keepalived** floats the VIP `10.5.30.53` to whichever node is alive with the highest priority
|
||||||
- **Unbound** on each node acts as the local recursive resolver (replaces Cloudflare/Quad9)
|
- **Unbound** on each node acts as the local recursive resolver (replaces Cloudflare/Quad9)
|
||||||
- **Pi-hole** on each node forwards upstream queries to its local Unbound (`127.0.0.1#5335`)
|
- **Pi-hole** on each node forwards upstream queries to its local Unbound (`127.0.0.1#5335`)
|
||||||
- **Nebula Sync** (running on PD) syncs all Pi-hole configs from primary → replicas daily
|
- **Nebula Sync** (running on PD) syncs all Pi-hole configs from primary → replicas daily
|
||||||
- Current live replica target is NOMAD at `10.5.1.16:8954`
|
- Current live replica target is NOMAD at `10.5.30.7:8954`
|
||||||
|
|
||||||
All nodes use `network_mode: host` for Pi-hole, Unbound, and Keepalived — this is required for Keepalived to manipulate the VIP and for Pi-hole to bind cleanly to port 53.
|
All nodes use `network_mode: host` for Pi-hole, Unbound, and Keepalived — this is required for Keepalived to manipulate the VIP and for Pi-hole to bind cleanly to port 53.
|
||||||
|
|
||||||
@@ -40,7 +42,7 @@ All nodes use `network_mode: host` for Pi-hole, Unbound, and Keepalived — this
|
|||||||
|
|
||||||
## Pre-Deploy: Network Planning
|
## Pre-Deploy: Network Planning
|
||||||
|
|
||||||
1. **Pick a free static IP for the VIP** — `10.5.1.53` is recommended (memorable for DNS). Reserve it in UniFi so DHCP never assigns it.
|
1. **Pick a free static IP for the VIP** — `10.5.30.53` is recommended (memorable for DNS). Reserve it in UniFi so DHCP never assigns it.
|
||||||
2. **Find the LAN interface name** on each node:
|
2. **Find the LAN interface name** on each node:
|
||||||
```bash
|
```bash
|
||||||
# PD / RPi4:
|
# PD / RPi4:
|
||||||
@@ -171,7 +173,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./keepalived.conf:/container/environment/01-custom/keepalived.conf
|
- ./keepalived.conf:/container/environment/01-custom/keepalived.conf
|
||||||
environment:
|
environment:
|
||||||
KEEPALIVED_VIRTUAL_IPS: "#PYTHON2BASH:['10.5.1.53/24']"
|
KEEPALIVED_VIRTUAL_IPS: "#PYTHON2BASH:['10.5.30.53/24']"
|
||||||
```
|
```
|
||||||
|
|
||||||
### keepalived.conf (PD — MASTER)
|
### keepalived.conf (PD — MASTER)
|
||||||
@@ -195,7 +197,7 @@ vrrp_instance VI_DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual_ipaddress {
|
virtual_ipaddress {
|
||||||
10.5.1.53/24
|
10.5.30.53/24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -323,7 +325,7 @@ vrrp_instance VI_DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual_ipaddress {
|
virtual_ipaddress {
|
||||||
10.5.1.53/24
|
10.5.30.53/24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -409,7 +411,7 @@ vrrp_instance VI_DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual_ipaddress {
|
virtual_ipaddress {
|
||||||
10.5.1.53/24
|
10.5.30.53/24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -434,7 +436,7 @@ sudo systemctl start keepalived
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
PRIMARY: "http://127.0.0.1:8953|${PIHOLE_PASSWORD}"
|
PRIMARY: "http://127.0.0.1:8953|${PIHOLE_PASSWORD}"
|
||||||
REPLICAS: "http://10.5.1.16:8954|${PIHOLE_PASSWORD_NOMAD},http://10.5.1.X:8955|${PIHOLE_PASSWORD_RPI}"
|
REPLICAS: "http://10.5.30.7:8954|${PIHOLE_PASSWORD_NOMAD},http://10.5.1.X:8955|${PIHOLE_PASSWORD_RPI}"
|
||||||
RUN_GRAVITY: "true"
|
RUN_GRAVITY: "true"
|
||||||
FULL_SYNC: "false"
|
FULL_SYNC: "false"
|
||||||
CRON: "0 3 * * *"
|
CRON: "0 3 * * *"
|
||||||
@@ -461,7 +463,7 @@ sudo systemctl start keepalived
|
|||||||
Point **only the VIP** as DNS:
|
Point **only the VIP** as DNS:
|
||||||
|
|
||||||
1. UniFi Network → Settings → Networks → **LAN** → DHCP Name Server: **Manual**
|
1. UniFi Network → Settings → Networks → **LAN** → DHCP Name Server: **Manual**
|
||||||
2. **DNS Server 1:** `10.5.1.53` (VIP — floats between nodes)
|
2. **DNS Server 1:** `10.5.30.53` (VIP — floats between nodes)
|
||||||
3. **DNS Server 2:** *(leave blank or set a public fallback like `1.1.1.1` only if you want internet DNS as last resort)*
|
3. **DNS Server 2:** *(leave blank or set a public fallback like `1.1.1.1` only if you want internet DNS as last resort)*
|
||||||
4. Save and renew DHCP leases
|
4. Save and renew DHCP leases
|
||||||
|
|
||||||
@@ -472,9 +474,9 @@ Point **only the VIP** as DNS:
|
|||||||
|
|
||||||
## Current live endpoints
|
## Current live endpoints
|
||||||
|
|
||||||
- **PD admin UI:** `http://10.5.1.6:8953/admin/`
|
- **PD admin UI:** `http://10.5.30.6:8953/admin/`
|
||||||
- **NOMAD admin UI:** `http://10.5.1.16:8954/admin/`
|
- **NOMAD admin UI:** `http://10.5.30.7:8954/admin/`
|
||||||
- **VIP for DNS clients:** `10.5.1.53`
|
- **VIP for DNS clients:** `10.5.30.53`
|
||||||
|
|
||||||
## Post-Deploy
|
## Post-Deploy
|
||||||
|
|
||||||
@@ -482,11 +484,11 @@ Point **only the VIP** as DNS:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check which node holds the VIP
|
# Check which node holds the VIP
|
||||||
ip addr show | grep 10.5.1.53 # Run on each node — only MASTER shows it
|
ip addr show | grep 10.5.30.53 # Run on each node — only MASTER shows it
|
||||||
|
|
||||||
# Test DNS through VIP
|
# Test DNS through VIP
|
||||||
nslookup google.com 10.5.1.53
|
nslookup google.com 10.5.30.53
|
||||||
nslookup doubleclick.net 10.5.1.53 # Should return 0.0.0.0 (blocked)
|
nslookup doubleclick.net 10.5.30.53 # Should return 0.0.0.0 (blocked)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test failover
|
### Test failover
|
||||||
@@ -495,7 +497,7 @@ nslookup doubleclick.net 10.5.1.53 # Should return 0.0.0.0 (blocked)
|
|||||||
# On PD, bring down keepalived
|
# On PD, bring down keepalived
|
||||||
sudo docker stop keepalived
|
sudo docker stop keepalived
|
||||||
# Wait ~3 seconds, then check from another machine:
|
# Wait ~3 seconds, then check from another machine:
|
||||||
nslookup google.com 10.5.1.53 # Should still work — N.O.M.A.D. took over VIP
|
nslookup google.com 10.5.30.53 # Should still work — N.O.M.A.D. took over VIP
|
||||||
# Restore
|
# Restore
|
||||||
sudo docker start keepalived
|
sudo docker start keepalived
|
||||||
```
|
```
|
||||||
@@ -516,8 +518,8 @@ Add in Pi-hole (primary) → Local DNS → DNS Records:
|
|||||||
|
|
||||||
| Domain | IP |
|
| Domain | IP |
|
||||||
|--------|----|
|
|--------|----|
|
||||||
| pd.lan | 10.5.1.6 |
|
| pd.lan | 10.5.30.6 |
|
||||||
| nomad.lan | 10.5.1.16 |
|
| nomad.lan | 10.5.30.7 |
|
||||||
| rocinante.lan | 10.5.1.112 |
|
| rocinante.lan | 10.5.1.112 |
|
||||||
| rpi4.lan | 10.5.1.X |
|
| rpi4.lan | 10.5.1.X |
|
||||||
|
|
||||||
@@ -527,8 +529,8 @@ Nebula Sync will propagate these to all replicas.
|
|||||||
|
|
||||||
| Subdomain | Target |
|
| Subdomain | Target |
|
||||||
|-----------|--------|
|
|-----------|--------|
|
||||||
| `pihole.paccoco.com` | `http://10.5.1.6:8953` (primary) |
|
| `pihole.paccoco.com` | `http://10.5.30.6:8953` (primary) |
|
||||||
| `pihole2.paccoco.com` | `http://10.5.1.16:8954` (N.O.M.A.D.) |
|
| `pihole2.paccoco.com` | `http://10.5.30.7:8954` (N.O.M.A.D.) |
|
||||||
|
|
||||||
Use `/admin/` on the public hostname for the Pi-hole admin UI path.
|
Use `/admin/` on the public hostname for the Pi-hole admin UI path.
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ Generated: 2026-05-09
|
|||||||
### 12 — Pi-hole Stats Digest
|
### 12 — Pi-hole Stats Digest
|
||||||
**Trigger:** Schedule (daily at 8 AM)
|
**Trigger:** Schedule (daily at 8 AM)
|
||||||
**What it does:**
|
**What it does:**
|
||||||
1. Query Pi-hole API on PD (`http://10.5.1.6:8953/api/stats/summary`)
|
1. Query Pi-hole API on PD (`http://10.5.30.6:8953/api/stats/summary`)
|
||||||
2. Query secondary Pi-hole on Serenity
|
2. Query secondary Pi-hole on Serenity
|
||||||
3. LiteLLM summarizes notable blocked domains and compares primary vs secondary query load
|
3. LiteLLM summarizes notable blocked domains and compares primary vs secondary query load
|
||||||
4. Push digest to Gotify (Pi-hole app) with 24h block count, top blocked domains, and any unusual spikes
|
4. Push digest to Gotify (Pi-hole app) with 24h block count, top blocked domains, and any unusual spikes
|
||||||
@@ -136,7 +136,7 @@ Generated: 2026-05-09
|
|||||||
### 16 — NFS Mount Watchdog + Auto-Heal
|
### 16 — NFS Mount Watchdog + Auto-Heal
|
||||||
**Trigger:** Schedule (every 5 min)
|
**Trigger:** Schedule (every 5 min)
|
||||||
**What it does:**
|
**What it does:**
|
||||||
1. SSH into PD (10.5.1.6) and probe each expected NFS mount path with `mountpoint -q`:
|
1. SSH into PD (10.5.30.6) and probe each expected NFS mount path with `mountpoint -q`:
|
||||||
- `/mnt/unraid/data/media` (Plex, Audiobookshelf)
|
- `/mnt/unraid/data/media` (Plex, Audiobookshelf)
|
||||||
- `/mnt/unraid/data/photos` (Immich)
|
- `/mnt/unraid/data/photos` (Immich)
|
||||||
- Any other mounts defined in the mount script
|
- Any other mounts defined in the mount script
|
||||||
@@ -153,7 +153,7 @@ Generated: 2026-05-09
|
|||||||
- Escalation: priority 9, list which paths are still unavailable
|
- Escalation: priority 9, list which paths are still unavailable
|
||||||
|
|
||||||
**Implementation notes:**
|
**Implementation notes:**
|
||||||
- Uses n8n SSH node connecting to `10.5.1.6` with an SSH credential (key-based auth from n8n container)
|
- Uses n8n SSH node connecting to `10.5.30.6` with an SSH credential (key-based auth from n8n container)
|
||||||
- SSH node runs a single compound command: `mountpoint -q /mnt/unraid/data/media && echo MEDIA_OK || echo MEDIA_MISSING` etc.
|
- SSH node runs a single compound command: `mountpoint -q /mnt/unraid/data/media && echo MEDIA_OK || echo MEDIA_MISSING` etc.
|
||||||
- Parse output in a Code node to determine which mounts failed
|
- Parse output in a Code node to determine which mounts failed
|
||||||
- Use separate SSH nodes for: (1) running the mount script, (2) restarting containers — keeps the flow readable
|
- Use separate SSH nodes for: (1) running the mount script, (2) restarting containers — keeps the flow readable
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
- [x] Regenerate N.O.M.A.D. Newt secret (was exposed in chat) — completed 2026-05-15
|
- [x] Regenerate N.O.M.A.D. Newt secret (was exposed in chat) — completed 2026-05-15
|
||||||
|
|
||||||
## Infrastructure
|
## Infrastructure
|
||||||
- [x] Deploy Pi-hole HA on PD + N.O.M.A.D. with VIP `10.5.1.53` — completed 2026-05-15; see docs/planning/DEPLOY_PIHOLE.md
|
- [x] Deploy Pi-hole HA on PD + N.O.M.A.D. with VIP `10.5.30.53` — completed 2026-05-15; historical Trusted-VLAN design, later superseded after NOMAD moved to Servers (`10.5.30.7`) and its Trusted-side Keepalived role was disabled pending a same-subnet HA redesign; see docs/planning/DEPLOY_PIHOLE.md
|
||||||
- PD primary live at `/mnt/docker-ssd/docker/compose/pihole`
|
- PD primary live at `/mnt/docker-ssd/docker/compose/pihole`
|
||||||
- NOMAD backup live at `/opt/pihole-nomad`
|
- NOMAD backup live at `/opt/pihole-nomad`
|
||||||
- RPi4 remains optional future BACKUP2 work if you want a third node later
|
- RPi4 remains optional future BACKUP2 work if you want a third node later
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ See the individual docs in this repo for detailed information:
|
|||||||
### Three Servers
|
### Three Servers
|
||||||
| Server | Role | OS | IP |
|
| Server | Role | OS | IP |
|
||||||
|--------|------|----|----|
|
|--------|------|----|----|
|
||||||
| Serenity | NAS / Media ingestion | Unraid 7.2.4 | 10.5.1.5 |
|
| Serenity | NAS / Media ingestion | Unraid 7.2.4 | 10.5.30.5 |
|
||||||
| PlausibleDeniability | Primary Docker host | TrueNAS Scale 25.10.2.1 | 10.5.1.6 |
|
| PlausibleDeniability | Primary Docker host | TrueNAS Scale 25.10.2.1 | 10.5.30.6 |
|
||||||
| N.O.M.A.D. | Offline knowledge + game servers | Ubuntu 25.10 | 10.5.1.16 |
|
| N.O.M.A.D. | Offline knowledge + game servers | Ubuntu 25.10 | 10.5.30.7 |
|
||||||
|
|
||||||
### Deployment Pattern (PD)
|
### Deployment Pattern (PD)
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Offline survival/knowledge server and game server host.
|
|||||||
|
|
||||||
## Specs
|
## Specs
|
||||||
- **OS:** Ubuntu 25.10 (bare metal)
|
- **OS:** Ubuntu 25.10 (bare metal)
|
||||||
- **IP:** 10.5.1.16
|
- **IP:** 10.5.30.7
|
||||||
- **CPU:** Intel Core i7-4770K @ 3.50GHz (4c/8t)
|
- **CPU:** Intel Core i7-4770K @ 3.50GHz (4c/8t)
|
||||||
- **RAM:** 32GB DDR3 1600MHz
|
- **RAM:** 32GB DDR3 1600MHz
|
||||||
- **GPU:** NVIDIA GeForce GTX 1080 (8GB VRAM)
|
- **GPU:** NVIDIA GeForce GTX 1080 (8GB VRAM)
|
||||||
@@ -309,7 +309,7 @@ Removing them should not be treated as a Project N.O.M.A.D. change or a Wings/Pe
|
|||||||
- `panel.paccoco.com` → `127.0.0.1` in `/etc/hosts` for NAT loopback
|
- `panel.paccoco.com` → `127.0.0.1` in `/etc/hosts` for NAT loopback
|
||||||
- nginx on 80 and 443 (self-signed cert) for local Wings → Panel
|
- nginx on 80 and 443 (self-signed cert) for local Wings → Panel
|
||||||
- Wings uses `--ignore-certificate-errors` for self-signed cert
|
- Wings uses `--ignore-certificate-errors` for self-signed cert
|
||||||
- For local operator-safe Pelican health checks, use `http://10.5.1.16:8080` instead of the self-signed TLS path; it redirects cleanly to `/home` without tripping certificate validation.
|
- For local operator-safe Pelican health checks, use `http://10.5.30.7:8080` instead of the self-signed TLS path; it redirects cleanly to `/home` without tripping certificate validation.
|
||||||
- Pangolin resource `node1.paccoco.com` has auth disabled
|
- Pangolin resource `node1.paccoco.com` has auth disabled
|
||||||
|
|
||||||
## Known Quirks
|
## Known Quirks
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ RTX 2080 Ti used by:
|
|||||||
- immich-ml (CUDA face recognition)
|
- immich-ml (CUDA face recognition)
|
||||||
- Ollama light-tier inference for the main AI stack and local Honcho backend
|
- Ollama light-tier inference for the main AI stack and local Honcho backend
|
||||||
|
|
||||||
> **Note:** Whisper moved to N.O.M.A.D. (10.5.1.16:8786) as CPU inference — avoids VRAM conflict with Ollama. See [NOMAD.md](NOMAD.md).
|
> **Note:** Whisper moved to N.O.M.A.D. (10.5.30.7:8786) as CPU inference — avoids VRAM conflict with Ollama. See [NOMAD.md](NOMAD.md).
|
||||||
|
|
||||||
Recommended models for 11GB VRAM:
|
Recommended models for 11GB VRAM:
|
||||||
| Model | VRAM | Best for |
|
| Model | VRAM | Best for |
|
||||||
@@ -45,8 +45,8 @@ Recommended models for 11GB VRAM:
|
|||||||
| `phi4` | ~8GB | Analytical tasks |
|
| `phi4` | ~8GB | Analytical tasks |
|
||||||
|
|
||||||
Current light-tier notes:
|
Current light-tier notes:
|
||||||
- PD Ollama is the active light-tier endpoint for LiteLLM on `http://10.5.1.6:11434`
|
- PD Ollama is the active light-tier endpoint for LiteLLM on `http://10.5.30.6:11434`
|
||||||
- Local Honcho currently points at PD Ollama over LAN (`http://10.5.1.6:11434/v1`) instead of using a separate local model host
|
- Local Honcho currently points at PD Ollama over LAN (`http://10.5.30.6:11434/v1`) instead of using a separate local model host
|
||||||
- Current Honcho routing split keeps embeddings on `nomic-embed-text:latest`, cheap/default paths on `qwen2.5:7b`, and deeper high/max paths on `qwen2.5:14b`
|
- Current Honcho routing split keeps embeddings on `nomic-embed-text:latest`, cheap/default paths on `qwen2.5:7b`, and deeper high/max paths on `qwen2.5:14b`
|
||||||
|
|
||||||
## Remote Access
|
## Remote Access
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ NAS, media ingestion, and CPU-based AI inference.
|
|||||||
|
|
||||||
| Component | Details |
|
| Component | Details |
|
||||||
|-----------|---------|
|
|-----------|---------|
|
||||||
| **IP (LAN)** | 10.5.1.5 |
|
| **IP (LAN)** | 10.5.30.5 |
|
||||||
| **IP (Tailscale)** | 100.94.87.79 |
|
| **IP (Tailscale)** | 100.94.87.79 |
|
||||||
| **OS** | Unraid 7.2.4 |
|
| **OS** | Unraid 7.2.4 |
|
||||||
| **CPU** | Dual Intel Xeon X5690 (6c/12t each — 24 threads total) |
|
| **CPU** | Dual Intel Xeon X5690 (6c/12t each — 24 threads total) |
|
||||||
@@ -44,7 +44,7 @@ Text Embeddings Inference reranker for OpenWebUI RAG pipeline.
|
|||||||
| **Port** | `9787` |
|
| **Port** | `9787` |
|
||||||
| **Volume** | `/mnt/user/appdata/reranker` |
|
| **Volume** | `/mnt/user/appdata/reranker` |
|
||||||
| **LiteLLM model name** | `reranker` |
|
| **LiteLLM model name** | `reranker` |
|
||||||
| **API base** | `http://10.5.1.5:9787` |
|
| **API base** | `http://10.5.30.5:9787` |
|
||||||
|
|
||||||
> Moved from PlausibleDeniability (port 8787) to Serenity (port 9787) on 2026-05-09.
|
> Moved from PlausibleDeniability (port 8787) to Serenity (port 9787) on 2026-05-09.
|
||||||
> CPU-only inference is acceptable for RAG reranking workloads.
|
> CPU-only inference is acceptable for RAG reranking workloads.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ KARAKEEP_DB_WAL_MODE=true
|
|||||||
KARAKEEP_LOG_LEVEL=notice
|
KARAKEEP_LOG_LEVEL=notice
|
||||||
|
|
||||||
# Optional local AI later:
|
# Optional local AI later:
|
||||||
# KARAKEEP_OLLAMA_BASE_URL=http://10.5.1.6:11434
|
# KARAKEEP_OLLAMA_BASE_URL=http://10.5.30.6:11434
|
||||||
# KARAKEEP_INFERENCE_TEXT_MODEL=llama3.2
|
# KARAKEEP_INFERENCE_TEXT_MODEL=llama3.2
|
||||||
# KARAKEEP_INFERENCE_ENABLE_AUTO_TAGGING=true
|
# KARAKEEP_INFERENCE_ENABLE_AUTO_TAGGING=true
|
||||||
# KARAKEEP_INFERENCE_ENABLE_AUTO_SUMMARIZATION=false
|
# KARAKEEP_INFERENCE_ENABLE_AUTO_SUMMARIZATION=false
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
<strong>Doris Constabulary</strong>
|
<strong>Doris Constabulary</strong>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-links" aria-label="Doris Barbell navigation">
|
<nav class="nav-links" aria-label="Doris Barbell navigation">
|
||||||
<a class="nav-link" href="http://10.5.1.16:8787/">Dashboard</a>
|
<a class="nav-link" href="http://10.5.30.7:8787/">Dashboard</a>
|
||||||
<a class="nav-link" href="http://10.5.1.16:8092/">Kitchen</a>
|
<a class="nav-link" href="http://10.5.30.7:8092/">Kitchen</a>
|
||||||
<a class="nav-link" href="https://schoolhouse.paccoco.com">Schoolhouse</a>
|
<a class="nav-link" href="https://schoolhouse.paccoco.com">Schoolhouse</a>
|
||||||
<a class="nav-link" href="/">Barbell</a>
|
<a class="nav-link" href="/">Barbell</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ The dashboard turns that into one opinionated, local-only page that Doris can tu
|
|||||||
- **Repo source:** `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard`
|
- **Repo source:** `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard`
|
||||||
- **Live runtime:** `/opt/doris-dashboard`
|
- **Live runtime:** `/opt/doris-dashboard`
|
||||||
- **Generated page:** `/opt/doris-dashboard/public/index.html`
|
- **Generated page:** `/opt/doris-dashboard/public/index.html`
|
||||||
- **LAN URL:** `http://10.5.1.16:8787/`
|
- **LAN URL:** `http://10.5.30.7:8787/`
|
||||||
- **Exposure rule:** local-only unless John explicitly asks otherwise
|
- **Exposure rule:** local-only unless John explicitly asks otherwise
|
||||||
|
|
||||||
## What it shows
|
## What it shows
|
||||||
@@ -143,7 +143,7 @@ This dashboard is local-only on NOMAD unless John explicitly asks to expose it.
|
|||||||
The dashboard is exposed on NOMAD's LAN IP via systemd:
|
The dashboard is exposed on NOMAD's LAN IP via systemd:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
http://10.5.1.16:8787/
|
http://10.5.30.7:8787/
|
||||||
```
|
```
|
||||||
|
|
||||||
Service file:
|
Service file:
|
||||||
@@ -185,7 +185,7 @@ The generated page still lives at:
|
|||||||
And the LAN service remains:
|
And the LAN service remains:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
http://10.5.1.16:8787/
|
http://10.5.30.7:8787/
|
||||||
```
|
```
|
||||||
|
|
||||||
Verification commands:
|
Verification commands:
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ SERVICE_DIRECTORY = [
|
|||||||
'items': [
|
'items': [
|
||||||
{'name': 'Doris Dashboard', 'url': 'index.html', 'health_url': 'http://127.0.0.1:8787/', 'purpose': 'Morning briefing, alerts, and operator homepage.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8787'},
|
{'name': 'Doris Dashboard', 'url': 'index.html', 'health_url': 'http://127.0.0.1:8787/', 'purpose': 'Morning briefing, alerts, and operator homepage.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8787'},
|
||||||
{'name': 'Services Directory', 'url': 'services.html', 'health_url': 'http://127.0.0.1:8787/services.html', 'purpose': 'Front door into the rest of the stack.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8787'},
|
{'name': 'Services Directory', 'url': 'services.html', 'health_url': 'http://127.0.0.1:8787/services.html', 'purpose': 'Front door into the rest of the stack.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8787'},
|
||||||
{'name': 'Doris Kitchen Imports', 'url': 'http://10.5.1.16:8092/imports', 'health_url': 'http://10.5.1.16:8092/imports', 'purpose': 'Flag bad KitchenOwl recipe imports for Doris to repair later.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8092'},
|
{'name': 'Doris Kitchen Imports', 'url': 'http://10.5.30.7:8092/imports', 'health_url': 'http://10.5.30.7:8092/imports', 'purpose': 'Flag bad KitchenOwl recipe imports for Doris to repair later.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8092'},
|
||||||
{'name': 'Homepage', 'url': 'http://10.5.1.6:3300', 'health_url': 'http://10.5.1.6:3300', 'purpose': 'Legacy PD portal and bookmarks page for the broader stack.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3300'},
|
{'name': 'Homepage', 'url': 'http://10.5.30.6:3300', 'health_url': 'http://10.5.30.6:3300', 'purpose': 'Legacy PD portal and bookmarks page for the broader stack.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3300'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'Shared Datastores',
|
'group': 'Shared Datastores',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'PostgreSQL', 'url': '', 'purpose': 'Primary shared Postgres backing Paperless, n8n, and other stateful stacks.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '5432/tcp', 'tcp_host': '10.5.1.6', 'tcp_port': 5432, 'badge': 'Healthy'},
|
{'name': 'PostgreSQL', 'url': '', 'purpose': 'Primary shared Postgres backing Paperless, n8n, and other stateful stacks.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '5432/tcp', 'tcp_host': '10.5.30.6', 'tcp_port': 5432, 'badge': 'Healthy'},
|
||||||
{'name': 'MariaDB', 'url': '', 'purpose': 'Shared MariaDB service for stacks that still need MySQL-compatible storage.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '3306/tcp', 'tcp_host': '10.5.1.6', 'tcp_port': 3306, 'badge': 'Healthy'},
|
{'name': 'MariaDB', 'url': '', 'purpose': 'Shared MariaDB service for stacks that still need MySQL-compatible storage.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '3306/tcp', 'tcp_host': '10.5.30.6', 'tcp_port': 3306, 'badge': 'Healthy'},
|
||||||
{'name': 'Redis', 'url': '', 'purpose': 'Shared cache and queue backend used across the homelab.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '6379/tcp', 'tcp_host': '10.5.1.6', 'tcp_port': 6379, 'badge': 'Healthy'},
|
{'name': 'Redis', 'url': '', 'purpose': 'Shared cache and queue backend used across the homelab.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '6379/tcp', 'tcp_host': '10.5.30.6', 'tcp_port': 6379, 'badge': 'Healthy'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -57,41 +57,41 @@ SERVICE_DIRECTORY = [
|
|||||||
{'name': 'DoneTick', 'url': 'https://donetick.paccoco.com', 'health_url': 'https://donetick.paccoco.com', 'purpose': 'Chores, recurring work, and household task coordination.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '2021'},
|
{'name': 'DoneTick', 'url': 'https://donetick.paccoco.com', 'health_url': 'https://donetick.paccoco.com', 'purpose': 'Chores, recurring work, and household task coordination.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '2021'},
|
||||||
{'name': 'Paperless-NGX', 'url': 'https://paperless.paccoco.com', 'health_url': 'https://paperless.paccoco.com', 'purpose': 'Document archive, OCR intake, and household paperwork triage.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8083'},
|
{'name': 'Paperless-NGX', 'url': 'https://paperless.paccoco.com', 'health_url': 'https://paperless.paccoco.com', 'purpose': 'Document archive, OCR intake, and household paperwork triage.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8083'},
|
||||||
{'name': 'n8n', 'url': 'https://n8n.paccoco.com', 'health_url': 'https://n8n.paccoco.com', 'purpose': 'Automation bus for webhooks, alerts, and cross-service glue.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '5678'},
|
{'name': 'n8n', 'url': 'https://n8n.paccoco.com', 'health_url': 'https://n8n.paccoco.com', 'purpose': 'Automation bus for webhooks, alerts, and cross-service glue.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '5678'},
|
||||||
{'name': 'Immich', 'url': 'http://10.5.1.6:2283', 'health_url': 'http://10.5.1.6:2283', 'purpose': 'Photo backup, search, and household media timeline.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '2283'},
|
{'name': 'Immich', 'url': 'http://10.5.30.6:2283', 'health_url': 'http://10.5.30.6:2283', 'purpose': 'Photo backup, search, and household media timeline.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '2283'},
|
||||||
{'name': 'Karakeep', 'url': 'http://10.5.1.6:3100', 'health_url': 'http://10.5.1.6:3100/signin', 'purpose': 'Self-hosted bookmark capture and later-read archive.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3100'},
|
{'name': 'Karakeep', 'url': 'http://10.5.30.6:3100', 'health_url': 'http://10.5.30.6:3100/signin', 'purpose': 'Self-hosted bookmark capture and later-read archive.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3100'},
|
||||||
{'name': 'KitchenOwl', 'url': 'http://10.5.1.6:8086', 'health_url': 'http://10.5.1.6:8086', 'purpose': 'Shared shopping lists and meal planning workflows.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8086'},
|
{'name': 'KitchenOwl', 'url': 'http://10.5.30.6:8086', 'health_url': 'http://10.5.30.6:8086', 'purpose': 'Shared shopping lists and meal planning workflows.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8086'},
|
||||||
{'name': 'Qui', 'url': 'http://10.5.1.6:7476', 'health_url': 'http://10.5.1.6:7476', 'purpose': 'Household utility app and quick operator tooling surface.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '7476'},
|
{'name': 'Qui', 'url': 'http://10.5.30.6:7476', 'health_url': 'http://10.5.30.6:7476', 'purpose': 'Household utility app and quick operator tooling surface.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '7476'},
|
||||||
{'name': 'Dakboard Bridge', 'url': '', 'health_url': 'http://10.5.1.6:5087', 'purpose': 'Authenticated bridge feeding Dakboard or display-side workflows.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '5087'},
|
{'name': 'Dakboard Bridge', 'url': '', 'health_url': 'http://10.5.30.6:5087', 'purpose': 'Authenticated bridge feeding Dakboard or display-side workflows.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': '5087'},
|
||||||
{'name': 'Doris Schoolhouse', 'url': 'https://schoolhouse.paccoco.com', 'health_url': 'https://schoolhouse.paccoco.com', 'purpose': 'Class workflows, uploads, recordings, and school intake.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '8091'},
|
{'name': 'Doris Schoolhouse', 'url': 'https://schoolhouse.paccoco.com', 'health_url': 'https://schoolhouse.paccoco.com', 'purpose': 'Class workflows, uploads, recordings, and school intake.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '8091'},
|
||||||
{'name': 'Doris Kitchen', 'url': 'http://10.5.1.16:8092', 'health_url': 'http://10.5.1.16:8092', 'purpose': 'Meal planning, grocery workflows, and kitchen-side operator tasks.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8092'},
|
{'name': 'Doris Kitchen', 'url': 'http://10.5.30.7:8092', 'health_url': 'http://10.5.30.7:8092', 'purpose': 'Meal planning, grocery workflows, and kitchen-side operator tasks.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8092'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'AI / Search',
|
'group': 'AI / Search',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'LiteLLM', 'url': 'http://10.5.1.6:4000', 'health_url': 'http://10.5.1.6:4000/v1/models', 'purpose': 'Shared model gateway for automations, Schoolhouse, and local tools.', 'exposure': 'LAN', 'state': 'attention', 'host': 'PD', 'port': '4000'},
|
{'name': 'LiteLLM', 'url': 'http://10.5.30.6:4000', 'health_url': 'http://10.5.30.6:4000/v1/models', 'purpose': 'Shared model gateway for automations, Schoolhouse, and local tools.', 'exposure': 'LAN', 'state': 'attention', 'host': 'PD', 'port': '4000'},
|
||||||
{'name': 'OpenWebUI', 'url': 'https://openwebui.paccoco.com', 'health_url': 'http://10.5.1.6:8282', 'purpose': 'Chat UI for the lab models, docs, and retrieval workflows.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8282'},
|
{'name': 'OpenWebUI', 'url': 'https://openwebui.paccoco.com', 'health_url': 'http://10.5.30.6:8282', 'purpose': 'Chat UI for the lab models, docs, and retrieval workflows.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8282'},
|
||||||
{'name': 'Qdrant', 'url': 'http://10.5.1.6:6333/dashboard', 'health_url': 'http://10.5.1.6:6333/healthz', 'purpose': 'Vector store for Project NOMAD and local retrieval systems.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '6333/6334'},
|
{'name': 'Qdrant', 'url': 'http://10.5.30.6:6333/dashboard', 'health_url': 'http://10.5.30.6:6333/healthz', 'purpose': 'Vector store for Project NOMAD and local retrieval systems.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '6333/6334'},
|
||||||
{'name': 'Whisper (CPU)', 'url': 'http://10.5.1.16:8786/docs', 'health_url': 'http://10.5.1.16:8786/health', 'purpose': 'Fast local CPU transcription endpoint on NOMAD.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8786'},
|
{'name': 'Whisper (CPU)', 'url': 'http://10.5.30.7:8786/docs', 'health_url': 'http://10.5.30.7:8786/health', 'purpose': 'Fast local CPU transcription endpoint on NOMAD.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8786'},
|
||||||
{'name': 'Whisper (CUDA)', 'url': 'http://10.5.1.112:8787/docs', 'health_url': 'http://10.5.1.112:8787/health', 'purpose': 'Heavy GPU transcription endpoint on Rocinante.', 'exposure': 'LAN', 'state': 'ok', 'host': 'ROCINANTE', 'port': '8787'},
|
{'name': 'Whisper (CUDA)', 'url': 'http://10.5.1.112:8787/docs', 'health_url': 'http://10.5.1.112:8787/health', 'purpose': 'Heavy GPU transcription endpoint on Rocinante.', 'exposure': 'LAN', 'state': 'ok', 'host': 'ROCINANTE', 'port': '8787'},
|
||||||
{'name': 'SearXNG', 'url': 'http://10.5.1.6:8888', 'health_url': 'http://10.5.1.6:8888', 'purpose': 'Private metasearch box for research and agent-side browsing.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8888'},
|
{'name': 'SearXNG', 'url': 'http://10.5.30.6:8888', 'health_url': 'http://10.5.30.6:8888', 'purpose': 'Private metasearch box for research and agent-side browsing.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8888'},
|
||||||
{'name': 'Ollama — light tier', 'url': 'http://10.5.1.6:11434/api/tags', 'health_url': 'http://10.5.1.6:11434/api/tags', 'purpose': 'PD light-tier Ollama runtime backing cheaper/default model paths.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '11434'},
|
{'name': 'Ollama — light tier', 'url': 'http://10.5.30.6:11434/api/tags', 'health_url': 'http://10.5.30.6:11434/api/tags', 'purpose': 'PD light-tier Ollama runtime backing cheaper/default model paths.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '11434'},
|
||||||
{'name': 'Ollama — heavy tier', 'url': 'http://10.5.1.112:11434/api/tags', 'health_url': 'http://10.5.1.112:11434/api/tags', 'purpose': 'Rocinante heavy-tier Ollama runtime for larger model requests.', 'exposure': 'LAN', 'state': 'ok', 'host': 'ROCINANTE', 'port': '11434'},
|
{'name': 'Ollama — heavy tier', 'url': 'http://10.5.1.112:11434/api/tags', 'health_url': 'http://10.5.1.112:11434/api/tags', 'purpose': 'Rocinante heavy-tier Ollama runtime for larger model requests.', 'exposure': 'LAN', 'state': 'ok', 'host': 'ROCINANTE', 'port': '11434'},
|
||||||
{'name': 'Reranker (TEI)', 'url': 'http://10.5.1.5:9787', 'health_url': 'http://10.5.1.5:9787', 'purpose': 'Serenity-hosted reranker used by retrieval flows.', 'exposure': 'LAN', 'state': 'ok', 'host': 'SERENITY', 'port': '9787'},
|
{'name': 'Reranker (TEI)', 'url': 'http://10.5.30.5:9787', 'health_url': 'http://10.5.30.5:9787', 'purpose': 'Serenity-hosted reranker used by retrieval flows.', 'exposure': 'LAN', 'state': 'ok', 'host': 'SERENITY', 'port': '9787'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'Homelab / Control Plane',
|
'group': 'Homelab / Control Plane',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'Grafana', 'url': 'https://grafana.paccoco.com', 'health_url': 'https://grafana.paccoco.com', 'purpose': 'Metrics, dashboards, and alert drill-down.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '3000'},
|
{'name': 'Grafana', 'url': 'https://grafana.paccoco.com', 'health_url': 'https://grafana.paccoco.com', 'purpose': 'Metrics, dashboards, and alert drill-down.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '3000'},
|
||||||
{'name': 'Prometheus', 'url': 'http://10.5.1.6:9090', 'health_url': 'http://10.5.1.6:9090/-/healthy', 'purpose': 'Metrics scrape and alert evaluation backend.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '9090'},
|
{'name': 'Prometheus', 'url': 'http://10.5.30.6:9090', 'health_url': 'http://10.5.30.6:9090/-/healthy', 'purpose': 'Metrics scrape and alert evaluation backend.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '9090'},
|
||||||
{'name': 'Gitea', 'url': 'https://gitea.paccoco.com', 'health_url': 'https://gitea.paccoco.com', 'purpose': 'Repos, issues, and source control.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '3002 / 2222'},
|
{'name': 'Gitea', 'url': 'https://gitea.paccoco.com', 'health_url': 'https://gitea.paccoco.com', 'purpose': 'Repos, issues, and source control.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '3002 / 2222'},
|
||||||
{'name': 'Dockhand', 'url': 'http://10.5.1.6:3230', 'health_url': 'http://10.5.1.6:3230', 'purpose': 'Container housekeeping and image/update operations.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3230'},
|
{'name': 'Dockhand', 'url': 'http://10.5.30.6:3230', 'health_url': 'http://10.5.30.6:3230', 'purpose': 'Container housekeeping and image/update operations.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3230'},
|
||||||
{'name': 'Uptime Kuma', 'url': 'http://10.5.1.6:3001', 'health_url': 'http://10.5.1.6:3001', 'purpose': 'Availability checks and service-watch status board.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3001'},
|
{'name': 'Uptime Kuma', 'url': 'http://10.5.30.6:3001', 'health_url': 'http://10.5.30.6:3001', 'purpose': 'Availability checks and service-watch status board.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3001'},
|
||||||
{'name': 'Gotify', 'url': 'https://gotify.paccoco.com', 'health_url': 'https://gotify.paccoco.com', 'purpose': 'Push notification hub for alerts and workflow fan-out.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8443'},
|
{'name': 'Gotify', 'url': 'https://gotify.paccoco.com', 'health_url': 'https://gotify.paccoco.com', 'purpose': 'Push notification hub for alerts and workflow fan-out.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8443'},
|
||||||
{'name': 'RackPeek', 'url': 'http://10.5.1.6:8283', 'health_url': 'http://10.5.1.6:8283', 'purpose': 'Rack/device inventory and operator reference panel.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8283'},
|
{'name': 'RackPeek', 'url': 'http://10.5.30.6:8283', 'health_url': 'http://10.5.30.6:8283', 'purpose': 'Rack/device inventory and operator reference panel.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8283'},
|
||||||
{'name': 'Shlink', 'url': 'http://10.5.1.6:8087', 'health_url': 'http://10.5.1.6:8087/rest/health', 'purpose': 'Short-link API and redirect backend.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8087'},
|
{'name': 'Shlink', 'url': 'http://10.5.30.6:8087', 'health_url': 'http://10.5.30.6:8087/rest/health', 'purpose': 'Short-link API and redirect backend.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8087'},
|
||||||
{'name': 'Shlink Web Client', 'url': 'http://10.5.1.6:8088', 'health_url': 'http://10.5.1.6:8088', 'purpose': 'Short-link management UI for the Shlink backend.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8088'},
|
{'name': 'Shlink Web Client', 'url': 'http://10.5.30.6:8088', 'health_url': 'http://10.5.30.6:8088', 'purpose': 'Short-link management UI for the Shlink backend.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8088'},
|
||||||
{'name': 'Loki', 'url': '', 'purpose': 'Log aggregation backend for Grafana and the monitoring stack.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': 'internal', 'probe': False, 'badge': 'Manual', 'detail': 'documented internal monitoring component; verify from Grafana/compose when needed'},
|
{'name': 'Loki', 'url': '', 'purpose': 'Log aggregation backend for Grafana and the monitoring stack.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': 'internal', 'probe': False, 'badge': 'Manual', 'detail': 'documented internal monitoring component; verify from Grafana/compose when needed'},
|
||||||
{'name': 'Promtail', 'url': '', 'purpose': 'Log shipping agent feeding Loki from the homelab nodes.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': 'internal', 'probe': False, 'badge': 'Manual', 'detail': 'documented internal monitoring component; verify from Grafana/compose when needed'},
|
{'name': 'Promtail', 'url': '', 'purpose': 'Log shipping agent feeding Loki from the homelab nodes.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': 'internal', 'probe': False, 'badge': 'Manual', 'detail': 'documented internal monitoring component; verify from Grafana/compose when needed'},
|
||||||
{'name': 'cAdvisor', 'url': '', 'purpose': 'Container metrics exporter feeding Prometheus.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': 'internal', 'probe': False, 'badge': 'Manual', 'detail': 'documented internal monitoring component; verify from Prometheus targets when needed'},
|
{'name': 'cAdvisor', 'url': '', 'purpose': 'Container metrics exporter feeding Prometheus.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'PD', 'port': 'internal', 'probe': False, 'badge': 'Manual', 'detail': 'documented internal monitoring component; verify from Prometheus targets when needed'},
|
||||||
@@ -100,47 +100,47 @@ SERVICE_DIRECTORY = [
|
|||||||
{
|
{
|
||||||
'group': 'Media / Library',
|
'group': 'Media / Library',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'Plex', 'url': 'http://10.5.1.6:32400/web/index.html', 'health_url': 'http://10.5.1.6:32400/identity', 'purpose': 'Primary media server, playback hub, and GPU-backed transcoding surface.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '32400'},
|
{'name': 'Plex', 'url': 'http://10.5.30.6:32400/web/index.html', 'health_url': 'http://10.5.30.6:32400/identity', 'purpose': 'Primary media server, playback hub, and GPU-backed transcoding surface.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '32400'},
|
||||||
{'name': 'Tautulli', 'url': 'http://10.5.1.6:8181', 'health_url': 'http://10.5.1.6:8181', 'purpose': 'Plex activity analytics, user history, and watch-session drill-down.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8181'},
|
{'name': 'Tautulli', 'url': 'http://10.5.30.6:8181', 'health_url': 'http://10.5.30.6:8181', 'purpose': 'Plex activity analytics, user history, and watch-session drill-down.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8181'},
|
||||||
{'name': 'Audiobookshelf', 'url': 'http://10.5.1.6:13358', 'health_url': 'http://10.5.1.6:13358', 'purpose': 'Audiobook and podcast library with self-hosted playback and progress sync.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '13358'},
|
{'name': 'Audiobookshelf', 'url': 'http://10.5.30.6:13358', 'health_url': 'http://10.5.30.6:13358', 'purpose': 'Audiobook and podcast library with self-hosted playback and progress sync.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '13358'},
|
||||||
{'name': 'Calibre-Web', 'url': 'http://10.5.1.6:8183', 'health_url': 'http://10.5.1.6:8183', 'purpose': 'Ebook library browser and metadata management front-end.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8183'},
|
{'name': 'Calibre-Web', 'url': 'http://10.5.30.6:8183', 'health_url': 'http://10.5.30.6:8183', 'purpose': 'Ebook library browser and metadata management front-end.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8183'},
|
||||||
{'name': 'Seerr (Overseerr)', 'url': 'http://10.5.1.6:5055', 'health_url': 'http://10.5.1.6:5055', 'purpose': 'Request intake for media additions and library demand tracking.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '5055'},
|
{'name': 'Seerr (Overseerr)', 'url': 'http://10.5.30.6:5055', 'health_url': 'http://10.5.30.6:5055', 'purpose': 'Request intake for media additions and library demand tracking.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '5055'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'Smart Home / Access',
|
'group': 'Smart Home / Access',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'Home Assistant', 'url': '', 'purpose': 'Smart-home control hub documented on PD, but Doris does not yet have a stable operator-safe route.', 'exposure': 'PRIVATE', 'state': 'attention', 'host': 'PD', 'port': '8123', 'probe': False, 'badge': 'Manual', 'detail': 'documented active on PD; local port currently refuses from NOMAD, so keep this manual until routed cleanly'},
|
{'name': 'Home Assistant', 'url': '', 'purpose': 'Smart-home control hub documented on PD, but Doris does not yet have a stable operator-safe route.', 'exposure': 'PRIVATE', 'state': 'attention', 'host': 'PD', 'port': '8123', 'probe': False, 'badge': 'Manual', 'detail': 'documented active on PD; local port currently refuses from NOMAD, so keep this manual until routed cleanly'},
|
||||||
{'name': 'Kima Hub', 'url': 'http://10.5.1.6:3333', 'health_url': 'http://10.5.1.6:3333', 'purpose': 'Custom IoT gateway and smart-home integration surface.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3333'},
|
{'name': 'Kima Hub', 'url': 'http://10.5.30.6:3333', 'health_url': 'http://10.5.30.6:3333', 'purpose': 'Custom IoT gateway and smart-home integration surface.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '3333'},
|
||||||
{'name': 'Pi-hole VIP', 'url': 'http://10.5.1.53/ui/', 'health_url': 'http://10.5.1.53/admin/', 'purpose': 'Floating DNS/admin surface for the HA Pi-hole pair your clients should target.', 'exposure': 'LAN', 'state': 'ok', 'host': 'VIP', 'port': '53 / 80'},
|
{'name': 'Pi-hole VIP', 'url': 'http://10.5.30.53/ui/', 'health_url': 'http://10.5.30.53/admin/', 'purpose': 'Floating DNS/admin surface for the HA Pi-hole pair your clients should target.', 'exposure': 'LAN', 'state': 'ok', 'host': 'VIP', 'port': '53 / 80'},
|
||||||
{'name': 'Pi-hole PD', 'url': 'http://10.5.1.6/ui/', 'health_url': 'http://10.5.1.6/admin/', 'purpose': 'Primary PD Pi-hole node behind the floating DNS VIP.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '53 / 80'},
|
{'name': 'Pi-hole PD', 'url': 'http://10.5.30.6/ui/', 'health_url': 'http://10.5.30.6/admin/', 'purpose': 'Primary PD Pi-hole node behind the floating DNS VIP.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '53 / 80'},
|
||||||
{'name': 'Pi-hole NOMAD', 'url': 'https://10.5.1.16/login', 'purpose': 'NOMAD backup Pi-hole replica for the HA DNS pair.', 'exposure': 'LAN', 'state': 'attention', 'host': 'NOMAD', 'port': '53 / 80/443', 'probe': False, 'badge': 'Manual', 'detail': 'documented active; local admin currently presents a self-signed TLS flow from NOMAD so Doris leaves this manual'},
|
{'name': 'Pi-hole NOMAD', 'url': 'https://10.5.30.7/login', 'purpose': 'NOMAD backup Pi-hole replica for the HA DNS pair.', 'exposure': 'LAN', 'state': 'attention', 'host': 'NOMAD', 'port': '53 / 80/443', 'probe': False, 'badge': 'Manual', 'detail': 'documented active; local admin currently presents a self-signed TLS flow from NOMAD so Doris leaves this manual'},
|
||||||
{'name': 'Authelia', 'url': 'http://10.5.1.6:9091', 'health_url': 'http://10.5.1.6:9091', 'purpose': 'Identity and auth gate backing protected public routes.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '9091'},
|
{'name': 'Authelia', 'url': 'http://10.5.30.6:9091', 'health_url': 'http://10.5.30.6:9091', 'purpose': 'Identity and auth gate backing protected public routes.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '9091'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'Mapping / Radio / Mesh',
|
'group': 'Mapping / Radio / Mesh',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'MeshMonitor', 'url': 'https://meshmonitor.paccoco.com', 'health_url': 'http://10.5.1.6:8081', 'purpose': 'Meshtastic monitoring, node views, and map/radio telemetry.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8081'},
|
{'name': 'MeshMonitor', 'url': 'https://meshmonitor.paccoco.com', 'health_url': 'http://10.5.30.6:8081', 'purpose': 'Meshtastic monitoring, node views, and map/radio telemetry.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'PD', 'port': '8081'},
|
||||||
{'name': 'TileServer GL', 'url': 'http://10.5.1.6:8082', 'health_url': 'http://10.5.1.6:8082', 'purpose': 'Map tile serving for local geo and radio/mapping workflows.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8082'},
|
{'name': 'TileServer GL', 'url': 'http://10.5.30.6:8082', 'health_url': 'http://10.5.30.6:8082', 'purpose': 'Map tile serving for local geo and radio/mapping workflows.', 'exposure': 'LAN', 'state': 'ok', 'host': 'PD', 'port': '8082'},
|
||||||
{'name': 'MeshCore to MQTT relay', 'url': '', 'purpose': 'Containerized relay moving MeshCore data into MQTT for the radio stack.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'NOMAD', 'port': 'outbound MQTT only', 'probe': False, 'badge': 'Manual', 'detail': 'documented running as mctomqtt; verify via container/runtime logs when needed'},
|
{'name': 'MeshCore to MQTT relay', 'url': '', 'purpose': 'Containerized relay moving MeshCore data into MQTT for the radio stack.', 'exposure': 'PRIVATE', 'state': 'ok', 'host': 'NOMAD', 'port': 'outbound MQTT only', 'probe': False, 'badge': 'Manual', 'detail': 'documented running as mctomqtt; verify via container/runtime logs when needed'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'Game Servers',
|
'group': 'Game Servers',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'Pelican Panel', 'url': 'https://panel.paccoco.com', 'health_url': 'http://10.5.1.16:8080', 'purpose': 'Game-server management panel for the NOMAD Wings stack.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '443'},
|
{'name': 'Pelican Panel', 'url': 'https://panel.paccoco.com', 'health_url': 'http://10.5.30.7:8080', 'purpose': 'Game-server management panel for the NOMAD Wings stack.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '443'},
|
||||||
{'name': 'Wings Node API', 'url': 'https://node1.paccoco.com', 'health_url': 'https://node1.paccoco.com', 'purpose': 'Public-facing Wings node endpoint; 401 means the daemon is reachable and auth-protected.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '8443 / 443'},
|
{'name': 'Wings Node API', 'url': 'https://node1.paccoco.com', 'health_url': 'https://node1.paccoco.com', 'purpose': 'Public-facing Wings node endpoint; 401 means the daemon is reachable and auth-protected.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '8443 / 443'},
|
||||||
{'name': 'Minecraft Server 25565', 'url': '', 'purpose': 'First Wings-managed Java server on NOMAD.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '25565/tcp+udp', 'tcp_host': '10.5.1.16', 'tcp_port': 25565, 'badge': 'Healthy'},
|
{'name': 'Minecraft Server 25565', 'url': '', 'purpose': 'First Wings-managed Java server on NOMAD.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '25565/tcp+udp', 'tcp_host': '10.5.30.7', 'tcp_port': 25565, 'badge': 'Healthy'},
|
||||||
{'name': 'Minecraft Server 25566', 'url': '', 'purpose': 'Second Wings-managed Java/NeoForge server on NOMAD.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '25566/tcp+udp', 'tcp_host': '10.5.1.16', 'tcp_port': 25566, 'badge': 'Healthy'},
|
{'name': 'Minecraft Server 25566', 'url': '', 'purpose': 'Second Wings-managed Java/NeoForge server on NOMAD.', 'exposure': 'PUBLIC', 'state': 'ok', 'host': 'NOMAD', 'port': '25566/tcp+udp', 'tcp_host': '10.5.30.7', 'tcp_port': 25566, 'badge': 'Healthy'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'group': 'Nomad Local Apps',
|
'group': 'Nomad Local Apps',
|
||||||
'items': [
|
'items': [
|
||||||
{'name': 'Dozzle', 'url': 'http://10.5.1.16:9999', 'health_url': 'http://10.5.1.16:9999', 'purpose': 'Live Docker logs on NOMAD without opening a shell.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '9999'},
|
{'name': 'Dozzle', 'url': 'http://10.5.30.7:9999', 'health_url': 'http://10.5.30.7:9999', 'purpose': 'Live Docker logs on NOMAD without opening a shell.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '9999'},
|
||||||
{'name': 'Flatnotes', 'url': 'http://10.5.1.16:8200', 'health_url': 'http://10.5.1.16:8200', 'purpose': 'Quick notes and lightweight knowledge capture.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8200'},
|
{'name': 'Flatnotes', 'url': 'http://10.5.30.7:8200', 'health_url': 'http://10.5.30.7:8200', 'purpose': 'Quick notes and lightweight knowledge capture.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8200'},
|
||||||
{'name': 'Kiwix', 'url': 'http://10.5.1.16:8090', 'health_url': 'http://10.5.1.16:8090', 'purpose': 'Offline library / knowledge mirror for disconnected use.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8090'},
|
{'name': 'Kiwix', 'url': 'http://10.5.30.7:8090', 'health_url': 'http://10.5.30.7:8090', 'purpose': 'Offline library / knowledge mirror for disconnected use.', 'exposure': 'LAN', 'state': 'ok', 'host': 'NOMAD', 'port': '8090'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -866,7 +866,7 @@ def inject_dashboard_todos(items:list[dict[str,Any]])->list[dict[str,Any]]:
|
|||||||
|
|
||||||
def load_paperless_review()->list[dict[str,Any]]:
|
def load_paperless_review()->list[dict[str,Any]]:
|
||||||
p=DATA/'paperless_review.json'
|
p=DATA/'paperless_review.json'
|
||||||
remote=os.environ.get('PAPERLESS_TRIAGE_REMOTE_QUEUE','truenas_admin@10.5.1.6:/mnt/docker-ssd/docker/appdata/n8n/paperless-triage/paperless_review.json')
|
remote=os.environ.get('PAPERLESS_TRIAGE_REMOTE_QUEUE','truenas_admin@10.5.30.6:/mnt/docker-ssd/docker/appdata/n8n/paperless-triage/paperless_review.json')
|
||||||
ssh_key=os.environ.get('PAPERLESS_TRIAGE_SSH_KEY','/home/fizzlepoof/.ssh/id_ed25519_andys')
|
ssh_key=os.environ.get('PAPERLESS_TRIAGE_SSH_KEY','/home/fizzlepoof/.ssh/id_ed25519_andys')
|
||||||
if remote:
|
if remote:
|
||||||
tmp=DATA/'paperless_review.remote.tmp'
|
tmp=DATA/'paperless_review.remote.tmp'
|
||||||
|
|||||||
@@ -0,0 +1,608 @@
|
|||||||
|
{
|
||||||
|
"captured_at": "2026-05-21T21:46:53-05:00",
|
||||||
|
"site": "default",
|
||||||
|
"networkconf": [
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"purpose": "wan",
|
||||||
|
"external_id": "d488cbbc-05dd-4bab-9efb-4b58340df685",
|
||||||
|
"wan_type_v6": "disabled",
|
||||||
|
"routing_table_id": 201,
|
||||||
|
"ipv6_wan_delegation_type": "none",
|
||||||
|
"wan_dhcpv6_pd_size_auto": true,
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618e0",
|
||||||
|
"igmp_proxy_upstream": false,
|
||||||
|
"mac_override_enabled": false,
|
||||||
|
"wan_load_balance_type": "failover-only",
|
||||||
|
"wan_failover_priority": 2,
|
||||||
|
"wan_ipv6_dns_preference": "auto",
|
||||||
|
"wan_networkgroup": "WAN",
|
||||||
|
"wan_dslite_remote_host_auto": false,
|
||||||
|
"wan_smartq_enabled": false,
|
||||||
|
"wan_dns_preference": "auto",
|
||||||
|
"wan_vlan_enabled": false,
|
||||||
|
"wan_load_balance_weight": 99,
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"name": "Internet 1",
|
||||||
|
"report_wan_event": false,
|
||||||
|
"_id": "6963c5831131084f05460929",
|
||||||
|
"attr_no_delete": true,
|
||||||
|
"wan_type": "dhcp",
|
||||||
|
"attr_hidden_id": "WAN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"purpose": "wan",
|
||||||
|
"wan_dhcp_cos": 0,
|
||||||
|
"external_id": "c10cd8c4-7f9c-4d12-a7ea-e1a34ab8ead7",
|
||||||
|
"wan_type_v6": "disabled",
|
||||||
|
"routing_table_id": 202,
|
||||||
|
"enabled": true,
|
||||||
|
"wan_dhcp_options": [],
|
||||||
|
"ipv6_wan_delegation_type": "none",
|
||||||
|
"wan_dhcpv6_pd_size_auto": true,
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618e0",
|
||||||
|
"igmp_proxy_upstream": false,
|
||||||
|
"mac_override_enabled": false,
|
||||||
|
"wan_load_balance_type": "weighted",
|
||||||
|
"wan_failover_priority": 1,
|
||||||
|
"ipv6_setting_preference": "manual",
|
||||||
|
"wan_ipv6_dns_preference": "auto",
|
||||||
|
"single_network_lan": "",
|
||||||
|
"wan_dns2": "9.9.9.9",
|
||||||
|
"wan_ipv6_dns1": "",
|
||||||
|
"wan_dns1": "10.5.1.53",
|
||||||
|
"wan_ipv6_dns2": "",
|
||||||
|
"wan_networkgroup": "WAN2",
|
||||||
|
"wan_dslite_remote_host_auto": false,
|
||||||
|
"wan_provider_capabilities": {
|
||||||
|
"upload_kilobits_per_second": 2409000,
|
||||||
|
"download_kilobits_per_second": 2120000
|
||||||
|
},
|
||||||
|
"wan_ip_aliases": [],
|
||||||
|
"wan_smartq_enabled": false,
|
||||||
|
"wan_dns_preference": "manual",
|
||||||
|
"wan_vlan_enabled": false,
|
||||||
|
"wan_load_balance_weight": 50,
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"name": "Internet 2",
|
||||||
|
"report_wan_event": false,
|
||||||
|
"_id": "6963c5831131084f0546092a",
|
||||||
|
"wan_type": "dhcp",
|
||||||
|
"attr_no_delete": true,
|
||||||
|
"igmp_proxy_for": "none"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"dhcpdv6_dns_auto": true,
|
||||||
|
"ipv6_pd_stop": "::7d1",
|
||||||
|
"dhcpd_gateway_enabled": false,
|
||||||
|
"ipv6_client_address_assignment": "slaac",
|
||||||
|
"network_isolation_enabled": false,
|
||||||
|
"dhcp_relay_servers": [],
|
||||||
|
"dhcpd_start": "10.5.0.100",
|
||||||
|
"dhcpd_unifi_controller": "",
|
||||||
|
"ipv6_ra_enabled": true,
|
||||||
|
"domain_name": "localdomain",
|
||||||
|
"ip_subnet": "10.5.0.1/24",
|
||||||
|
"ipv6_interface_type": "none",
|
||||||
|
"dhcpd_wins_2": "",
|
||||||
|
"dhcpdv6_stop": "::7d1",
|
||||||
|
"is_nat": true,
|
||||||
|
"dhcpd_wins_1": "",
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"internet_access_enabled": true,
|
||||||
|
"nat_outbound_ip_addresses": [],
|
||||||
|
"dhcp_relay_enabled": false,
|
||||||
|
"dhcpd_conflict_checking": true,
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"dhcpdv6_leasetime": 86400,
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"name": "Management",
|
||||||
|
"_id": "6963c5831131084f0546092b",
|
||||||
|
"lte_lan_enabled": true,
|
||||||
|
"purpose": "corporate",
|
||||||
|
"dhcpd_leasetime": 86400,
|
||||||
|
"dhcpguard_enabled": false,
|
||||||
|
"dhcpd_time_offset_enabled": false,
|
||||||
|
"external_id": "652279eb-41e5-440d-aad5-231ca9683890",
|
||||||
|
"ipv6_ra_preferred_lifetime": 14400,
|
||||||
|
"dhcpd_stop": "10.5.0.199",
|
||||||
|
"is_smart_subnet_detected": false,
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_enabled": true,
|
||||||
|
"dhcpd_wpad_url": "",
|
||||||
|
"networkgroup": "LAN",
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618df",
|
||||||
|
"dhcpdv6_start": "::2",
|
||||||
|
"vlan_enabled": false,
|
||||||
|
"ipv6_setting_preference": "auto",
|
||||||
|
"ipv6_aliases": [],
|
||||||
|
"gateway_type": "default",
|
||||||
|
"ipv6_ra_priority": "high",
|
||||||
|
"dhcpd_boot_enabled": false,
|
||||||
|
"ipv6_pd_start": "::2",
|
||||||
|
"upnp_lan_enabled": false,
|
||||||
|
"dhcpd_ntp_enabled": false,
|
||||||
|
"mdns_enabled": true,
|
||||||
|
"ip_aliases": [],
|
||||||
|
"attr_no_delete": true,
|
||||||
|
"attr_hidden_id": "LAN",
|
||||||
|
"dhcpd_tftp_server": "",
|
||||||
|
"auto_scale_enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"dhcpd_gateway_enabled": false,
|
||||||
|
"network_isolation_enabled": false,
|
||||||
|
"dhcp_relay_servers": [],
|
||||||
|
"dhcpd_dns_1": "192.168.1.10",
|
||||||
|
"dhcpd_start": "10.5.1.100",
|
||||||
|
"dhcpd_unifi_controller": "",
|
||||||
|
"domain_name": "",
|
||||||
|
"ip_subnet": "10.5.1.1/24",
|
||||||
|
"dhcpd_dns_4": "",
|
||||||
|
"ipv6_interface_type": "none",
|
||||||
|
"dhcpd_dns_2": "",
|
||||||
|
"dhcpd_dns_3": "",
|
||||||
|
"dhcpd_wins_2": "",
|
||||||
|
"is_nat": true,
|
||||||
|
"dhcpd_wins_1": "",
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"internet_access_enabled": true,
|
||||||
|
"nat_outbound_ip_addresses": [],
|
||||||
|
"dhcp_relay_enabled": false,
|
||||||
|
"dhcpd_conflict_checking": true,
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"name": "Trusted",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "6963cb201131084f05461635",
|
||||||
|
"lte_lan_enabled": true,
|
||||||
|
"dhcpd_leasetime": 86400,
|
||||||
|
"purpose": "corporate",
|
||||||
|
"dhcpd_time_offset_enabled": false,
|
||||||
|
"dhcpguard_enabled": false,
|
||||||
|
"external_id": "87b66e27-93a9-4fb7-93d2-3b7bca9e6414",
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_stop": "10.5.1.199",
|
||||||
|
"dhcpd_enabled": true,
|
||||||
|
"vlan": 51,
|
||||||
|
"dhcpd_wpad_url": "",
|
||||||
|
"networkgroup": "LAN",
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618df",
|
||||||
|
"vlan_enabled": true,
|
||||||
|
"ipv6_setting_preference": "manual",
|
||||||
|
"ipv6_aliases": [],
|
||||||
|
"gateway_type": "default",
|
||||||
|
"dhcpd_boot_enabled": false,
|
||||||
|
"upnp_lan_enabled": false,
|
||||||
|
"dhcpd_ntp_enabled": false,
|
||||||
|
"mdns_enabled": true,
|
||||||
|
"ip_aliases": [],
|
||||||
|
"dhcpd_tftp_server": "",
|
||||||
|
"auto_scale_enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"dhcpd_leasetime": 86400,
|
||||||
|
"purpose": "corporate",
|
||||||
|
"dhcpd_gateway_enabled": false,
|
||||||
|
"dhcpd_time_offset_enabled": false,
|
||||||
|
"dhcpguard_enabled": false,
|
||||||
|
"network_isolation_enabled": true,
|
||||||
|
"dhcp_relay_servers": [],
|
||||||
|
"dhcpd_start": "10.5.10.6",
|
||||||
|
"dhcpd_unifi_controller": "",
|
||||||
|
"external_id": "88180bc3-2c34-4f30-9b57-aed15da2a12a",
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_stop": "10.5.10.254",
|
||||||
|
"domain_name": "",
|
||||||
|
"dhcpd_enabled": true,
|
||||||
|
"ip_subnet": "10.5.10.1/24",
|
||||||
|
"vlan": 510,
|
||||||
|
"dhcpd_wpad_url": "",
|
||||||
|
"ipv6_interface_type": "none",
|
||||||
|
"networkgroup": "LAN",
|
||||||
|
"firewall_zone_id": "6963d5a91131084f05461a0d",
|
||||||
|
"vlan_enabled": true,
|
||||||
|
"dhcpd_wins_2": "",
|
||||||
|
"ipv6_setting_preference": "manual",
|
||||||
|
"dhcpd_wins_1": "",
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"ipv6_aliases": [],
|
||||||
|
"internet_access_enabled": true,
|
||||||
|
"gateway_type": "default",
|
||||||
|
"nat_outbound_ip_addresses": [],
|
||||||
|
"dhcpd_boot_enabled": false,
|
||||||
|
"dhcp_relay_enabled": false,
|
||||||
|
"dhcpd_conflict_checking": true,
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"dhcpd_ntp_enabled": false,
|
||||||
|
"name": "IoT",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"mdns_enabled": true,
|
||||||
|
"ip_aliases": [],
|
||||||
|
"_id": "6963cb461131084f05461642",
|
||||||
|
"lte_lan_enabled": true,
|
||||||
|
"dhcpd_tftp_server": "",
|
||||||
|
"auto_scale_enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"dhcpd_leasetime": 86400,
|
||||||
|
"purpose": "guest",
|
||||||
|
"dhcpd_gateway_enabled": false,
|
||||||
|
"dhcpd_time_offset_enabled": false,
|
||||||
|
"dhcpguard_enabled": false,
|
||||||
|
"network_isolation_enabled": false,
|
||||||
|
"dhcp_relay_servers": [],
|
||||||
|
"dhcpd_start": "10.5.90.6",
|
||||||
|
"dhcpd_unifi_controller": "",
|
||||||
|
"external_id": "2986eea1-bf02-4879-a7b0-7d9f91e45c53",
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_stop": "10.5.90.254",
|
||||||
|
"domain_name": "",
|
||||||
|
"dhcpd_enabled": true,
|
||||||
|
"ip_subnet": "10.5.90.1/24",
|
||||||
|
"vlan": 590,
|
||||||
|
"dhcpd_wpad_url": "",
|
||||||
|
"ipv6_interface_type": "none",
|
||||||
|
"networkgroup": "LAN",
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618e3",
|
||||||
|
"vlan_enabled": true,
|
||||||
|
"dhcpd_wins_2": "",
|
||||||
|
"ipv6_setting_preference": "manual",
|
||||||
|
"is_nat": true,
|
||||||
|
"dhcpd_wins_1": "",
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"ipv6_aliases": [],
|
||||||
|
"internet_access_enabled": true,
|
||||||
|
"gateway_type": "default",
|
||||||
|
"nat_outbound_ip_addresses": [],
|
||||||
|
"dhcpd_boot_enabled": false,
|
||||||
|
"dhcp_relay_enabled": false,
|
||||||
|
"dhcpd_conflict_checking": true,
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"upnp_lan_enabled": false,
|
||||||
|
"dhcpd_ntp_enabled": false,
|
||||||
|
"name": "Guest",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"mdns_enabled": true,
|
||||||
|
"ip_aliases": [],
|
||||||
|
"_id": "6963cb941131084f054616af",
|
||||||
|
"lte_lan_enabled": true,
|
||||||
|
"dhcpd_tftp_server": "",
|
||||||
|
"auto_scale_enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"dhcpd_leasetime": 86400,
|
||||||
|
"purpose": "corporate",
|
||||||
|
"dhcpd_gateway_enabled": false,
|
||||||
|
"dhcpd_time_offset_enabled": false,
|
||||||
|
"dhcpguard_enabled": false,
|
||||||
|
"network_isolation_enabled": false,
|
||||||
|
"dhcp_relay_servers": [],
|
||||||
|
"dhcpd_start": "10.5.20.6",
|
||||||
|
"dhcpd_unifi_controller": "",
|
||||||
|
"external_id": "98f2aff1-b394-493d-908f-ced4c83bde99",
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_stop": "10.5.20.254",
|
||||||
|
"domain_name": "",
|
||||||
|
"dhcpd_enabled": true,
|
||||||
|
"ip_subnet": "10.5.20.1/24",
|
||||||
|
"vlan": 520,
|
||||||
|
"dhcpd_wpad_url": "",
|
||||||
|
"ipv6_interface_type": "none",
|
||||||
|
"networkgroup": "LAN",
|
||||||
|
"firewall_zone_id": "6963d5a91131084f05461a0d",
|
||||||
|
"vlan_enabled": true,
|
||||||
|
"dhcpd_wins_2": "",
|
||||||
|
"ipv6_setting_preference": "manual",
|
||||||
|
"is_nat": true,
|
||||||
|
"dhcpd_wins_1": "",
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"ipv6_aliases": [],
|
||||||
|
"internet_access_enabled": true,
|
||||||
|
"gateway_type": "default",
|
||||||
|
"nat_outbound_ip_addresses": [],
|
||||||
|
"dhcpd_boot_enabled": false,
|
||||||
|
"dhcp_relay_enabled": false,
|
||||||
|
"dhcpd_conflict_checking": true,
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"upnp_lan_enabled": false,
|
||||||
|
"dhcpd_ntp_enabled": false,
|
||||||
|
"name": "Camera",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"mdns_enabled": true,
|
||||||
|
"ip_aliases": [],
|
||||||
|
"_id": "6963cbbf1131084f054616c1",
|
||||||
|
"lte_lan_enabled": true,
|
||||||
|
"dhcpd_tftp_server": "",
|
||||||
|
"auto_scale_enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"dhcpd_leasetime": 86400,
|
||||||
|
"purpose": "corporate",
|
||||||
|
"dhcpd_gateway_enabled": false,
|
||||||
|
"dhcpd_time_offset_enabled": false,
|
||||||
|
"dhcpguard_enabled": false,
|
||||||
|
"network_isolation_enabled": false,
|
||||||
|
"dhcp_relay_servers": [],
|
||||||
|
"dhcpd_start": "192.168.1.100",
|
||||||
|
"dhcpd_unifi_controller": "",
|
||||||
|
"external_id": "9bd0ee41-3303-4c03-ad81-305248301921",
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_stop": "192.168.1.199",
|
||||||
|
"domain_name": "",
|
||||||
|
"dhcpd_enabled": true,
|
||||||
|
"ip_subnet": "192.168.1.1/24",
|
||||||
|
"vlan": 2,
|
||||||
|
"dhcpd_wpad_url": "",
|
||||||
|
"ipv6_interface_type": "none",
|
||||||
|
"networkgroup": "LAN",
|
||||||
|
"firewall_zone_id": "6963d5a91131084f05461a0d",
|
||||||
|
"vlan_enabled": true,
|
||||||
|
"dhcpd_wins_2": "",
|
||||||
|
"ipv6_setting_preference": "manual",
|
||||||
|
"is_nat": true,
|
||||||
|
"dhcpd_wins_1": "",
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"ipv6_aliases": [],
|
||||||
|
"internet_access_enabled": true,
|
||||||
|
"gateway_type": "default",
|
||||||
|
"nat_outbound_ip_addresses": [],
|
||||||
|
"dhcpd_boot_enabled": false,
|
||||||
|
"dhcp_relay_enabled": false,
|
||||||
|
"dhcpd_conflict_checking": true,
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"upnp_lan_enabled": false,
|
||||||
|
"dhcpd_ntp_enabled": false,
|
||||||
|
"name": "Old IoT",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"mdns_enabled": true,
|
||||||
|
"ip_aliases": [],
|
||||||
|
"_id": "6963e11c1131084f054622f1",
|
||||||
|
"lte_lan_enabled": true,
|
||||||
|
"dhcpd_tftp_server": "",
|
||||||
|
"auto_scale_enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wireguard_client_configuration_filename": "Unifi2-US-IL-267.conf",
|
||||||
|
"purpose": "vpn-client",
|
||||||
|
"wireguard_client_mode": "file",
|
||||||
|
"external_id": "8673a0e4-4f1b-42d6-9563-e6f6440065de",
|
||||||
|
"interface_mtu_enabled": false,
|
||||||
|
"routing_table_id": 178,
|
||||||
|
"enabled": true,
|
||||||
|
"vpn_type": "wireguard-client",
|
||||||
|
"mss_clamp": "auto",
|
||||||
|
"ip_subnet": "10.2.0.2/32",
|
||||||
|
"name": "Proton Chicago",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"wireguard_id": 1,
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618e0",
|
||||||
|
"wireguard_client_configuration_file": "[Interface]\n# Key for Unifi2\n# Bouncing = 6\n# NetShield = 2\n# Moderate NAT = off\n# NAT-PMP (Port Forwarding) = off\n# VPN Accelerator = on\nPrivateKey = ABSrYvjSF+I3FgZPgq2pwYfAqLwKgOJ3nsFC5m/RhG0=\nAddress = 10.2.0.2/32\nDNS = 10.2.0.1\n\n[Peer]\n# US-IL#267\nPublicKey = qT0lxDVbWEIyrL2A40FfCXRlUALvnryRz2aQdD6gUDs=\nAllowedIPs = 0.0.0.0/0, ::/0\nEndpoint = 89.187.180.40:51820",
|
||||||
|
"_id": "696411c51131084f05465f8d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"wireguard_interface_binding_mode_ip_version": "v4",
|
||||||
|
"purpose": "remote-user-vpn",
|
||||||
|
"local_port": 51820,
|
||||||
|
"x_wireguard_private_key": "4PK12mmYHHr9nuJO0G683UwQJQMJMumRCZccutNWEHk=",
|
||||||
|
"external_id": "c625964c-8663-4ef0-bf04-53a5466a168c",
|
||||||
|
"enabled": true,
|
||||||
|
"wireguard_local_wan_ip": "any",
|
||||||
|
"vpn_type": "wireguard-server",
|
||||||
|
"ip_subnet": "192.168.2.1/24",
|
||||||
|
"wireguard_interface": "wan2",
|
||||||
|
"name": "One-Click VPN",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"wireguard_id": 1,
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618e2",
|
||||||
|
"_id": "69a31399822347a1daadae2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"wireguard_interface_binding_mode_ip_version": "v4",
|
||||||
|
"purpose": "remote-user-vpn",
|
||||||
|
"x_wireguard_private_key": "uNpzMwinbLyFEb2vRMCh+85L9fD20QAxhuZi42X3pko=",
|
||||||
|
"dhcpd_start": "192.168.3.2",
|
||||||
|
"interface_mtu_enabled": false,
|
||||||
|
"external_id": "2431e2ed-2d1f-48ed-9b2f-3ed5b8021504",
|
||||||
|
"enabled": true,
|
||||||
|
"dhcpd_stop": "192.168.3.254",
|
||||||
|
"vpn_type": "wireguard-server",
|
||||||
|
"ip_subnet": "192.168.3.1/24",
|
||||||
|
"wireguard_interface": "wan2",
|
||||||
|
"wireguard_id": 2,
|
||||||
|
"firewall_zone_id": "6963d42a1131084f054618e2",
|
||||||
|
"vpn_client_configuration_remote_ip_override_enabled": false,
|
||||||
|
"dhcpd_dns_enabled": false,
|
||||||
|
"local_port": 51821,
|
||||||
|
"wireguard_local_wan_ip": "any",
|
||||||
|
"mss_clamp": "auto",
|
||||||
|
"dhcpd_wins_enabled": false,
|
||||||
|
"vpn_binding_mode": "any",
|
||||||
|
"name": "Slate 7",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "69dd799da02014d2f4acc4e9",
|
||||||
|
"mss_clamp_ipv6": "auto"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"portconf": [
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"stormctrl_ucast_rate": 100,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"stormctrl_mcast_enabled": false,
|
||||||
|
"lldpmed_notify_enabled": false,
|
||||||
|
"tagged_vlan_mgmt": "auto",
|
||||||
|
"multicast_router_networkconf_ids": [],
|
||||||
|
"stormctrl_bcast_enabled": false,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"stormctrl_mcast_rate": 100,
|
||||||
|
"native_networkconf_id": "6963c5831131084f0546092b",
|
||||||
|
"qos_profile": {
|
||||||
|
"qos_profile_mode": "custom",
|
||||||
|
"qos_policies": []
|
||||||
|
},
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"dot1x_idle_timeout": 300,
|
||||||
|
"op_mode": "switch",
|
||||||
|
"poe_mode": "auto",
|
||||||
|
"forward": "all",
|
||||||
|
"stormctrl_ucast_enabled": false,
|
||||||
|
"stormctrl_bcast_rate": 100,
|
||||||
|
"isolation": false,
|
||||||
|
"voice_networkconf_id": "",
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"name": "Management",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "6963cdbf1131084f0546177c",
|
||||||
|
"autoneg": true,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"dot1x_ctrl": "force_authorized"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"stormctrl_ucast_rate": 100,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"stormctrl_mcast_enabled": false,
|
||||||
|
"lldpmed_notify_enabled": false,
|
||||||
|
"tagged_vlan_mgmt": "auto",
|
||||||
|
"multicast_router_networkconf_ids": [],
|
||||||
|
"stormctrl_bcast_enabled": false,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"excluded_networkconf_ids": [],
|
||||||
|
"stormctrl_mcast_rate": 100,
|
||||||
|
"native_networkconf_id": "6963cb201131084f05461635",
|
||||||
|
"qos_profile": {
|
||||||
|
"qos_profile_mode": "custom",
|
||||||
|
"qos_policies": []
|
||||||
|
},
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"dot1x_idle_timeout": 300,
|
||||||
|
"op_mode": "switch",
|
||||||
|
"poe_mode": "auto",
|
||||||
|
"forward": "customize",
|
||||||
|
"stormctrl_ucast_enabled": false,
|
||||||
|
"stormctrl_bcast_rate": 100,
|
||||||
|
"isolation": false,
|
||||||
|
"voice_networkconf_id": "",
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"name": "Trusted",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "6963cdfe1131084f0546178d",
|
||||||
|
"autoneg": true,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"dot1x_ctrl": "force_authorized"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"stormctrl_ucast_rate": 100,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"stormctrl_mcast_enabled": false,
|
||||||
|
"lldpmed_notify_enabled": false,
|
||||||
|
"tagged_vlan_mgmt": "block_all",
|
||||||
|
"multicast_router_networkconf_ids": [],
|
||||||
|
"stormctrl_bcast_enabled": false,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"stormctrl_mcast_rate": 100,
|
||||||
|
"native_networkconf_id": "6963cb461131084f05461642",
|
||||||
|
"qos_profile": {
|
||||||
|
"qos_profile_mode": "custom",
|
||||||
|
"qos_policies": []
|
||||||
|
},
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"dot1x_idle_timeout": 300,
|
||||||
|
"op_mode": "switch",
|
||||||
|
"poe_mode": "auto",
|
||||||
|
"forward": "native",
|
||||||
|
"stormctrl_ucast_enabled": false,
|
||||||
|
"stormctrl_bcast_rate": 100,
|
||||||
|
"isolation": false,
|
||||||
|
"voice_networkconf_id": "",
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"name": "IoT",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "6963ce221131084f05461791",
|
||||||
|
"autoneg": true,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"dot1x_ctrl": "force_authorized"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"stormctrl_ucast_rate": 100,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"stormctrl_mcast_enabled": false,
|
||||||
|
"lldpmed_notify_enabled": false,
|
||||||
|
"tagged_vlan_mgmt": "block_all",
|
||||||
|
"multicast_router_networkconf_ids": [],
|
||||||
|
"stormctrl_bcast_enabled": false,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"stormctrl_mcast_rate": 100,
|
||||||
|
"native_networkconf_id": "6963cbbf1131084f054616c1",
|
||||||
|
"qos_profile": {
|
||||||
|
"qos_profile_mode": "custom",
|
||||||
|
"qos_policies": []
|
||||||
|
},
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"dot1x_idle_timeout": 300,
|
||||||
|
"op_mode": "switch",
|
||||||
|
"poe_mode": "auto",
|
||||||
|
"forward": "native",
|
||||||
|
"stormctrl_ucast_enabled": false,
|
||||||
|
"stormctrl_bcast_rate": 100,
|
||||||
|
"isolation": false,
|
||||||
|
"voice_networkconf_id": "",
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"name": "Camera",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "6963ce3b1131084f05461798",
|
||||||
|
"autoneg": true,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"dot1x_ctrl": "force_authorized"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"stormctrl_ucast_rate": 100,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"stormctrl_mcast_enabled": false,
|
||||||
|
"lldpmed_notify_enabled": false,
|
||||||
|
"tagged_vlan_mgmt": "block_all",
|
||||||
|
"multicast_router_networkconf_ids": [],
|
||||||
|
"stormctrl_bcast_enabled": false,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"stormctrl_mcast_rate": 100,
|
||||||
|
"native_networkconf_id": "6963e11c1131084f054622f1",
|
||||||
|
"qos_profile": {
|
||||||
|
"qos_profile_mode": "custom",
|
||||||
|
"qos_policies": []
|
||||||
|
},
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"dot1x_idle_timeout": 300,
|
||||||
|
"op_mode": "switch",
|
||||||
|
"poe_mode": "auto",
|
||||||
|
"forward": "native",
|
||||||
|
"stormctrl_ucast_enabled": false,
|
||||||
|
"stormctrl_bcast_rate": 100,
|
||||||
|
"isolation": false,
|
||||||
|
"voice_networkconf_id": "",
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"name": "Old IoT",
|
||||||
|
"site_id": "6963c5321131084f05460911",
|
||||||
|
"_id": "6963e3511131084f054624cf",
|
||||||
|
"autoneg": true,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"dot1x_ctrl": "force_authorized"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
"captured_at": "2026-05-22",
|
||||||
|
"device_id": "698776451131084f059d572b",
|
||||||
|
"device_mac": "8c:30:66:d0:9f:48",
|
||||||
|
"device_name": "USW Pro HD 24",
|
||||||
|
"port_overrides": [
|
||||||
|
{
|
||||||
|
"name": "U7 Pro (Upstairs)",
|
||||||
|
"port_idx": 1,
|
||||||
|
"portconf_id": "6963cdbf1131084f0546177c",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autoneg": false,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"excluded_networkconf_ids": [],
|
||||||
|
"forward": "customize",
|
||||||
|
"full_duplex": true,
|
||||||
|
"isolation": false,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"multicast_router_mode": "NONE",
|
||||||
|
"name": "Port 2",
|
||||||
|
"native_networkconf_id": "6963cb201131084f05461635",
|
||||||
|
"port_idx": 2,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"speed": 2500,
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"tagged_vlan_mgmt": "auto",
|
||||||
|
"voice_networkconf_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FlyingDutchman",
|
||||||
|
"port_idx": 3,
|
||||||
|
"portconf_id": "6963cdfe1131084f0546178d",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Port 4",
|
||||||
|
"port_idx": 4,
|
||||||
|
"portconf_id": "6963cdbf1131084f0546177c",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autoneg": false,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"excluded_networkconf_ids": [],
|
||||||
|
"forward": "customize",
|
||||||
|
"full_duplex": true,
|
||||||
|
"isolation": false,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"multicast_router_mode": "NONE",
|
||||||
|
"name": "Port 6",
|
||||||
|
"native_networkconf_id": "6963cb201131084f05461635",
|
||||||
|
"port_idx": 6,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"port_security_enabled": false,
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"setting_preference": "manual",
|
||||||
|
"speed": 2500,
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"tagged_vlan_mgmt": "auto",
|
||||||
|
"voice_networkconf_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"autoneg": true,
|
||||||
|
"egress_rate_limit_kbps_enabled": false,
|
||||||
|
"forward": "disabled",
|
||||||
|
"isolation": false,
|
||||||
|
"lldpmed_enabled": true,
|
||||||
|
"name": "Port 14",
|
||||||
|
"native_networkconf_id": "",
|
||||||
|
"port_idx": 14,
|
||||||
|
"port_keepalive_enabled": false,
|
||||||
|
"port_security_enabled": true,
|
||||||
|
"port_security_mac_address": [],
|
||||||
|
"setting_preference": "auto",
|
||||||
|
"stp_port_mode": true,
|
||||||
|
"tagged_vlan_mgmt": "block_all",
|
||||||
|
"voice_networkconf_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Port 22",
|
||||||
|
"port_idx": 22,
|
||||||
|
"portconf_id": "6963cdfe1131084f0546178d",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Docker Server",
|
||||||
|
"port_idx": 23,
|
||||||
|
"portconf_id": "6963cdfe1131084f0546178d",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Serenity 10G (1)",
|
||||||
|
"port_idx": 24,
|
||||||
|
"portconf_id": "6963cdfe1131084f0546178d",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Port 27",
|
||||||
|
"port_idx": 27,
|
||||||
|
"portconf_id": "6963cdbf1131084f0546177c",
|
||||||
|
"setting_preference": "manual"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
151
home/doris-dashboard/docs/legacy-cia-triage-worksheet.md
Normal file
151
home/doris-dashboard/docs/legacy-cia-triage-worksheet.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Legacy CIA Via Device Triage Worksheet
|
||||||
|
|
||||||
|
> For Doris: this worksheet exists so tomorrow does not devolve into vibes and guessing. Every device on Legacy CIA gets one label: migrate, quarantine, or kill.
|
||||||
|
|
||||||
|
Goal: classify every remaining device on the legacy CIA Via VLAN/SSID so we can make deliberate decisions during and after the cutover.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- No new devices join Legacy CIA.
|
||||||
|
- If a device has a clean future home, move it.
|
||||||
|
- If a device is still useful but cannot be re-homed cleanly, quarantine it.
|
||||||
|
- If nobody knows what it is or nobody misses it, kill it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Decision Criteria
|
||||||
|
|
||||||
|
### Migrate
|
||||||
|
Use `migrate` when all or most are true:
|
||||||
|
- device still matters
|
||||||
|
- app/admin control still exists
|
||||||
|
- owner is known
|
||||||
|
- function is understood
|
||||||
|
- a target VLAN/SSID is obvious
|
||||||
|
- there is a reasonable validation method after moving
|
||||||
|
|
||||||
|
### Quarantine
|
||||||
|
Use `quarantine` when all or most are true:
|
||||||
|
- device still provides value
|
||||||
|
- current owner/function is known enough
|
||||||
|
- moving it is risky, annoying, or impossible right now
|
||||||
|
- app/pairing/discovery state is fragile
|
||||||
|
- acceptable to leave it internet-only or nearly so
|
||||||
|
|
||||||
|
### Kill
|
||||||
|
Use `kill` when all or most are true:
|
||||||
|
- nobody knows what it is
|
||||||
|
- nobody can still administer it
|
||||||
|
- nobody notices when it is offline
|
||||||
|
- it duplicates something better
|
||||||
|
- it exists only because history happened
|
||||||
|
|
||||||
|
## 2. Recommended First-Pass Classification
|
||||||
|
|
||||||
|
### Migrate tomorrow if practical
|
||||||
|
- Main-Floor ecobee -> IoT VLAN 40
|
||||||
|
- Upstairs ecobee -> IoT VLAN 40
|
||||||
|
- MyQ -> IoT VLAN 40
|
||||||
|
- Samsung FamilyHub -> IoT VLAN 40
|
||||||
|
- LG dryer -> IoT VLAN 40
|
||||||
|
- doorbell -> Cameras VLAN 60
|
||||||
|
- Protect chimes -> Cameras VLAN 60
|
||||||
|
|
||||||
|
### Migrate later / careful handling
|
||||||
|
- Google Home Mini -> IoT VLAN 40 after discovery testing
|
||||||
|
- Chromecast-class devices -> IoT VLAN 40 after discovery testing
|
||||||
|
- any Google cast/speaker/display weirdness -> probably last batch
|
||||||
|
|
||||||
|
### Quarantine on Legacy CIA
|
||||||
|
- old bulbs still doing useful work
|
||||||
|
- old plugs still doing useful work
|
||||||
|
- retained mystery devices with known household function but bad migration prospects
|
||||||
|
- any device that still works but has no sane re-pair workflow tonight
|
||||||
|
|
||||||
|
### Likely kill candidates
|
||||||
|
- unknown stale MACs
|
||||||
|
- orphaned historical smart-home junk
|
||||||
|
- dead bulbs/plugs nobody notices
|
||||||
|
- duplicate or long-gone clients still mentally treated as active
|
||||||
|
|
||||||
|
## 3. Live Worksheet Table
|
||||||
|
|
||||||
|
Fill one row per client discovered on Legacy CIA.
|
||||||
|
|
||||||
|
| Device name | MAC | IP | Vendor | Physical location | What it does | Owner | Current control path | Target lane | Disposition | Why | Test after move/block | Result |
|
||||||
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||
|
| Main-Floor ecobee | | | ecobee | main floor | thermostat | household | ecobee app | IoT 40 | migrate | important + controllable | thermostat still controllable | |
|
||||||
|
| Upstairs ecobee | | | ecobee | upstairs | thermostat | household | ecobee app | IoT 40 | migrate | important + controllable | thermostat still controllable | |
|
||||||
|
| MyQ | | | Chamberlain/LiftMaster | garage | garage door | household | MyQ app | IoT 40 | migrate | meaningful device | app works after move | |
|
||||||
|
| Samsung FamilyHub | | | Samsung | kitchen | fridge/display | household | Samsung app/local | IoT 40 | migrate | meaningful appliance | app works after move | |
|
||||||
|
| LG dryer | | | LG | laundry | dryer telemetry/control | household | LG app | IoT 40 | migrate | low-risk appliance | app works after move | |
|
||||||
|
| Google Home Mini | | | Google | | voice/cast | household | Google Home app | IoT 40 | migrate later / quarantine | discovery pain | cast/discovery tested | |
|
||||||
|
| Chromecast / Cast device | | | Google | | casting | household | Google Home/app casting | IoT 40 | migrate later / quarantine | discovery pain | cast still works | |
|
||||||
|
| Legacy bulb A | | | | | light | | unknown/old app | Legacy CIA | quarantine or kill | depends if anyone misses it | light still works or nobody complains | |
|
||||||
|
| Legacy plug A | | | | | smart plug | | unknown/old app | Legacy CIA | quarantine or kill | depends if still useful | plug use validated or not | |
|
||||||
|
| Unknown client 1 | | | | | unknown | unknown | none | none | kill candidate | unidentified | nobody notices after block | |
|
||||||
|
|
||||||
|
## 4. Fast Triage Questions During The Window
|
||||||
|
|
||||||
|
Ask these for each questionable device:
|
||||||
|
1. What is it?
|
||||||
|
2. Who cares if it breaks?
|
||||||
|
3. Can we still control it?
|
||||||
|
4. Does it need local LAN access or just internet?
|
||||||
|
5. Is its correct long-term home obvious?
|
||||||
|
6. Can we test success in under 2 minutes?
|
||||||
|
|
||||||
|
If answers are bad, it does not earn migration tomorrow.
|
||||||
|
|
||||||
|
## 5. Specific Handling Guidance
|
||||||
|
|
||||||
|
### ecobees
|
||||||
|
- Move if app/control confidence is decent.
|
||||||
|
- Validate both thermostats immediately after move.
|
||||||
|
- If one gets weird, revert that one only.
|
||||||
|
|
||||||
|
### MyQ / fridge / dryer
|
||||||
|
- These are good early IoT wins.
|
||||||
|
- Minimal emotional attachment, clear owner, clear expected validation.
|
||||||
|
|
||||||
|
### Google / Chromecast ecosystem
|
||||||
|
- Treat as suspicious until proven civilized.
|
||||||
|
- Do not write broad allow rules just because casting sulks.
|
||||||
|
- Prefer leaving these in quarantine over contaminating clean policy.
|
||||||
|
|
||||||
|
### Protect chimes / doorbell
|
||||||
|
- These are not Legacy CIA end-state residents.
|
||||||
|
- Move to Cameras/Security as early cleanup.
|
||||||
|
|
||||||
|
### old bulbs / old plugs
|
||||||
|
- If still useful but irrecoverable, quarantine.
|
||||||
|
- If their purpose is forgotten, block first, then kill if nobody notices.
|
||||||
|
|
||||||
|
## 6. Quarantine Policy Reminder
|
||||||
|
|
||||||
|
Legacy CIA devices that remain should get:
|
||||||
|
- DHCP
|
||||||
|
- DNS
|
||||||
|
- NTP
|
||||||
|
- internet outbound if needed
|
||||||
|
- no Management access
|
||||||
|
- no Trusted access
|
||||||
|
- no Servers access by default
|
||||||
|
- no Cameras access
|
||||||
|
- no new joins
|
||||||
|
|
||||||
|
## 7. Post-Window Cleanup Queue
|
||||||
|
|
||||||
|
After tomorrow, every leftover Legacy CIA row should get one next action:
|
||||||
|
- retry migration
|
||||||
|
- collect pairing/reset docs
|
||||||
|
- physically inspect location
|
||||||
|
- disable/block and observe
|
||||||
|
- remove permanently
|
||||||
|
|
||||||
|
## 8. Success Condition
|
||||||
|
|
||||||
|
The Legacy CIA sheet is successful when:
|
||||||
|
- every device has a named disposition
|
||||||
|
- no unknown active clients are left unclassified
|
||||||
|
- only genuinely hard leftovers remain in quarantine
|
||||||
|
- you can explain why each remaining device is still there
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Network Cutover Live Change Log Template
|
||||||
|
|
||||||
|
Use this during the live window.
|
||||||
|
|
||||||
|
Rule:
|
||||||
|
- every meaningful change gets a row
|
||||||
|
- if something breaks, the last row is your first suspect
|
||||||
|
- do not trust memory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session header
|
||||||
|
|
||||||
|
Date:
|
||||||
|
Operator:
|
||||||
|
Primary control box: Rocinante
|
||||||
|
Fallback admin path:
|
||||||
|
Start time:
|
||||||
|
Stop time:
|
||||||
|
|
||||||
|
In scope tonight:
|
||||||
|
- Google: yes, but narrow/reversible only
|
||||||
|
- U6 LR: only if needed
|
||||||
|
- FlyingDutchman: stay Trusted unless explicitly reclassified
|
||||||
|
- Server batch: Nomad + Serenity + PD
|
||||||
|
|
||||||
|
Out of scope / defer if ugly:
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Live log
|
||||||
|
|
||||||
|
| Time | Change ID | Area | Exact change made | Expected result | Validation performed | Result | Rollback needed? | Notes |
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
| | 001 | baseline | captured screenshots/exports | rollback baseline exists | exports saved | pass/fail | n | |
|
||||||
|
| | 002 | management | | | | | y/n | |
|
||||||
|
| | 003 | camera/security | | | | | y/n | |
|
||||||
|
| | 004 | server-batch | move Nomad to Servers | Nomad on correct subnet and reachable | IP + service check | pass/fail | y/n | |
|
||||||
|
| | 005 | server-batch | move Serenity to Servers | Serenity on correct subnet and reachable | IP + service check | pass/fail | y/n | |
|
||||||
|
| | 006 | server-batch | move PD to Servers | PD on correct subnet and reachable | IP + service check | pass/fail | y/n | |
|
||||||
|
| | 007 | iot | | | | | y/n | |
|
||||||
|
| | 008 | google-test | | | | | y/n | |
|
||||||
|
| | 009 | guest/ssid | | | | | y/n | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Batch validation block: Nomad + Serenity + PD
|
||||||
|
|
||||||
|
After the three-host move wave, record this before touching anything else:
|
||||||
|
|
||||||
|
- Nomad subnet/gateway correct: yes/no
|
||||||
|
- Serenity subnet/gateway correct: yes/no
|
||||||
|
- PD subnet/gateway correct: yes/no
|
||||||
|
- Rocinante can still reach PD: yes/no
|
||||||
|
- NFS/share relationships recovered: yes/no
|
||||||
|
- Critical service checks passed: yes/no
|
||||||
|
- Need immediate rollback: yes/no
|
||||||
|
|
||||||
|
If no to any critical line above:
|
||||||
|
- stop
|
||||||
|
- revert last moved host first
|
||||||
|
- unwind in reverse order
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Temporary exceptions added
|
||||||
|
|
||||||
|
| Time | Rule / exception | Why it was added | Narrow enough? | Remove before end? |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| | | | yes/no | yes/no |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Devices intentionally deferred
|
||||||
|
|
||||||
|
| Device | Why deferred | Safe current lane | Follow-up needed |
|
||||||
|
|---|---|---|---|
|
||||||
|
| | | | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## End-of-window summary
|
||||||
|
|
||||||
|
Minimum success criteria:
|
||||||
|
- Management materially cleaner: yes/no
|
||||||
|
- Camera/Security lane sane: yes/no
|
||||||
|
- Nomad/Serenity/PD batch completed or deliberately rolled back: yes/no
|
||||||
|
- Easy IoT wins done: yes/no
|
||||||
|
- Legacy CIA explicitly quarantine/sunset: yes/no
|
||||||
|
- No broad panic rules added: yes/no
|
||||||
|
|
||||||
|
What changed successfully:
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
What was reverted:
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
|
||||||
|
What remains for later:
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
# Network Cutover Master Operator Sheet
|
||||||
|
|
||||||
|
> For Doris: this is the one-page live operator sheet for tomorrow. Use this instead of bouncing between five docs while tired.
|
||||||
|
|
||||||
|
Goal: execute the UniFi network redesign with minimal improvisation, explicit stop/go gates, per-port actions, per-device dispositions, and fast rollback discipline.
|
||||||
|
|
||||||
|
Architecture:
|
||||||
|
- Keep the target lanes clear: Management, Trusted, Servers, IoT, Guest, Cameras.
|
||||||
|
- Keep Legacy CIA alive only as a quarantine/sunset lane.
|
||||||
|
- Move true service hosts out of Trusted while keeping human endpoints where they belong.
|
||||||
|
- Move easy named IoT wins now; quarantine ambiguous junk; Google is in scope, but only through narrow, reversible tests.
|
||||||
|
|
||||||
|
Primary references if deeper detail is needed:
|
||||||
|
- `network-redesign-implementation-runbook.md`
|
||||||
|
- `network-firewall-rule-order.md`
|
||||||
|
- `legacy-cia-triage-worksheet.md`
|
||||||
|
- `unifi-readonly-recon-2026-05-22.md`
|
||||||
|
- `usw-pro-hd-24-cutover-port-sheet.md`
|
||||||
|
- `old-iot-tomorrow-disposition-list.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Live State Snapshot Already Confirmed
|
||||||
|
|
||||||
|
Networks currently present:
|
||||||
|
- Management -> `10.5.0.1/24`
|
||||||
|
- Trusted -> VLAN 51 -> `10.5.1.1/24`
|
||||||
|
- Old IoT -> VLAN 2 -> `192.168.1.1/24`
|
||||||
|
- IoT -> VLAN 510 -> `10.5.10.1/24`
|
||||||
|
- Camera -> VLAN 520 -> `10.5.20.1/24`
|
||||||
|
- Guest -> VLAN 590 -> `10.5.90.1/24`
|
||||||
|
|
||||||
|
SSIDs currently present:
|
||||||
|
- `CIA Via` -> Old IoT
|
||||||
|
- `UNEF's Playhouse` -> Camera
|
||||||
|
- `Whiskey Neat Fuck Ice` -> Trusted
|
||||||
|
- `Yer a Wifi Harry` -> Trusted
|
||||||
|
|
||||||
|
Current client counts:
|
||||||
|
- Management: 2
|
||||||
|
- Old IoT: 14
|
||||||
|
- Camera: 1
|
||||||
|
- Trusted: 14
|
||||||
|
|
||||||
|
Current hazards:
|
||||||
|
- two unnamed `espressif` devices are on Management via `UniFi Wireless` on the U7 Pro
|
||||||
|
- almost all Wi‑Fi clients are on the U7 Pro
|
||||||
|
- there are no custom firewall rules/groups yet, so tomorrow’s segmentation policy is basically greenfield
|
||||||
|
|
||||||
|
## 2. Tomorrow’s Must-Win Outcomes
|
||||||
|
|
||||||
|
By the time we stop, these should be true:
|
||||||
|
- Management is materially cleaned up
|
||||||
|
- Cameras lane exists and is not polluted by junk placement mistakes
|
||||||
|
- Servers lane exists and core hosts are moved or deliberately staged with validation
|
||||||
|
- IoT lane exists and easy-value devices are moved
|
||||||
|
- Legacy CIA is explicitly a quarantine/sunset lane
|
||||||
|
- no broad panic rules were added to compensate for fatigue
|
||||||
|
|
||||||
|
In-scope if conditions support it:
|
||||||
|
- Google/cast migration work, but only through narrow, reversible tests
|
||||||
|
- U6 LR only if needed to stabilize client behavior or complete the design cleanly
|
||||||
|
|
||||||
|
Nice-to-have only if smooth:
|
||||||
|
- SSID simplification
|
||||||
|
|
||||||
|
## 3. Absolute Rules
|
||||||
|
|
||||||
|
- Keep one stable admin path alive the whole time.
|
||||||
|
- Rocinante is the live operator station, so do not move it until the rest of the server batch is done or an alternate admin path is proven.
|
||||||
|
- Change one meaningful thing, then validate.
|
||||||
|
- Do not bulk-edit live server ports blindly.
|
||||||
|
- Nomad, Serenity, and PD can be moved as one controlled batch only because their NFS/shared-service coupling is real; pre-stage everything first, then validate the batch immediately.
|
||||||
|
- Do not try to empty Legacy CIA completely if the window gets noisy.
|
||||||
|
- Do not move unknown junk into Management, Trusted, or Servers.
|
||||||
|
- Do not let Google/cast problems bait you into broad allow rules.
|
||||||
|
- If management reachability degrades, rollback first, think second.
|
||||||
|
|
||||||
|
## 4. Minute-by-Minute Execution Spine
|
||||||
|
|
||||||
|
### T-30 to T-15: prep
|
||||||
|
- [ ] open this file
|
||||||
|
- [ ] open UniFi admin
|
||||||
|
- [ ] open Doris visual artifacts if helpful
|
||||||
|
- [ ] confirm Rocinante is the live control box
|
||||||
|
- [ ] confirm fallback route into UniFi in case Rocinante loses the lane mid-change
|
||||||
|
- [ ] decide whether U6 LR is in scope or explicitly out of scope
|
||||||
|
|
||||||
|
### Minute 0-10: baseline capture
|
||||||
|
- [ ] screenshot/export networks
|
||||||
|
- [ ] screenshot/export SSIDs
|
||||||
|
- [ ] screenshot/export firewall/rule state
|
||||||
|
- [ ] screenshot/export port profiles
|
||||||
|
- [ ] screenshot/export switch port assignments
|
||||||
|
- [ ] screenshot/export AP mappings
|
||||||
|
- [ ] snapshot Management client list
|
||||||
|
- [ ] snapshot Old IoT client list
|
||||||
|
|
||||||
|
Stop/go gate:
|
||||||
|
- [ ] do not proceed until rollback baseline exists
|
||||||
|
|
||||||
|
### Minute 10-20: create/normalize objects only
|
||||||
|
- [ ] create/confirm Servers network object
|
||||||
|
- [ ] create/confirm any missing profiles
|
||||||
|
- [ ] create/confirm staged SSIDs if needed
|
||||||
|
- [ ] create/confirm staged firewall objects/rules
|
||||||
|
|
||||||
|
Stop/go gate:
|
||||||
|
- [ ] no clients should have moved yet
|
||||||
|
- [ ] Trusted admin path still fine
|
||||||
|
|
||||||
|
### Minute 20-35: management cleanup first
|
||||||
|
- [ ] identify/address the 2 Management `espressif` devices
|
||||||
|
- [ ] ensure infra devices remain on Management intent
|
||||||
|
- [ ] remove obvious non-infra junk from Management
|
||||||
|
|
||||||
|
Stop/go gate:
|
||||||
|
- [ ] UniFi still sees gateway/switch/APs
|
||||||
|
- [ ] admin client still reaches controller
|
||||||
|
|
||||||
|
### Minute 35-50: security cleanup
|
||||||
|
- [ ] validate Camera lane
|
||||||
|
- [ ] move Protect chimes if ready
|
||||||
|
- [ ] confirm doorbell remains healthy
|
||||||
|
|
||||||
|
### Minute 50-85: core server move wave
|
||||||
|
- [ ] leave Rocinante in place as the operator station until the main server batch is done
|
||||||
|
- [ ] keep FlyingDutchman on Trusted unless a later decision explicitly reclassifies it
|
||||||
|
- [ ] pre-stage the exact three-port sequence: Nomad, Serenity, PD
|
||||||
|
- [ ] move Nomad, Serenity, and PD in one controlled wave because of NFS/shared-service coupling
|
||||||
|
- [ ] validate the batch immediately before touching anything else
|
||||||
|
|
||||||
|
### Minute 85-110: easy IoT wins
|
||||||
|
- [ ] MyQ
|
||||||
|
- [ ] LG dryer
|
||||||
|
- [ ] Samsung FamilyHub
|
||||||
|
- [ ] Main-Floor ecobee
|
||||||
|
- [ ] Upstairs ecobee
|
||||||
|
|
||||||
|
### Minute 110-125: Legacy CIA quarantine pass
|
||||||
|
- [ ] mark leftovers as quarantine/defer
|
||||||
|
- [ ] do not force migration of unknowns
|
||||||
|
- [ ] apply harsh quarantine posture
|
||||||
|
|
||||||
|
### Minute 125-140: Google test if the window is still stable
|
||||||
|
- [ ] test one Google/cast device first
|
||||||
|
- [ ] if ugly, stop and defer
|
||||||
|
|
||||||
|
### Minute 140-155: guest/SSID cleanup
|
||||||
|
- [ ] validate Guest
|
||||||
|
- [ ] disable only stale SSIDs that are truly no longer needed
|
||||||
|
|
||||||
|
### Minute 155-180: final validation and stop
|
||||||
|
- [ ] Trusted ok
|
||||||
|
- [ ] server services ok
|
||||||
|
- [ ] moved IoT devices ok
|
||||||
|
- [ ] security ok
|
||||||
|
- [ ] Management materially cleaner
|
||||||
|
- [ ] Legacy CIA now explicitly quarantine
|
||||||
|
|
||||||
|
## 5. Management Offender Sheet
|
||||||
|
|
||||||
|
These two devices are currently on Management and need identification or removal from that lane:
|
||||||
|
|
||||||
|
1. `espressif`
|
||||||
|
- IP: `10.5.0.123`
|
||||||
|
- AP: `U7 Pro`
|
||||||
|
- SSID: `UniFi Wireless`
|
||||||
|
- Default call: identify first; if it is smart-junk, move/quarantine off Management
|
||||||
|
|
||||||
|
2. `espressif`
|
||||||
|
- IP: `10.5.0.189`
|
||||||
|
- AP: `U7 Pro`
|
||||||
|
- SSID: `UniFi Wireless`
|
||||||
|
- Default call: identify first; if it is smart-junk, move/quarantine off Management
|
||||||
|
|
||||||
|
Operator note:
|
||||||
|
- these are almost certainly exactly the sort of device that should not live on your infrastructure lane
|
||||||
|
|
||||||
|
## 6. Exact Core Port Move Sheet
|
||||||
|
|
||||||
|
### Leave Alone Unless Positively Necessary
|
||||||
|
- USW Pro HD 24 port 1 -> `U7 Pro (Upstairs)` -> profile `Management`
|
||||||
|
- USW Pro HD 24 port 27 -> likely infra/uplink -> profile `Management`
|
||||||
|
- USW-24-PoE port 24 -> link up on `Management`, inspect before touching
|
||||||
|
|
||||||
|
### Port Move Order
|
||||||
|
|
||||||
|
1. USW Pro HD 24 port 2
|
||||||
|
- Live host: `Rocinante`
|
||||||
|
- Current profile visibility from API: ambiguous
|
||||||
|
- Action: inspect in UI first, then move to `Servers` if confirmed
|
||||||
|
- Validate:
|
||||||
|
- correct subnet/gateway
|
||||||
|
- reachable from Trusted
|
||||||
|
- intended services still work
|
||||||
|
|
||||||
|
2. USW Pro HD 24 port 3
|
||||||
|
- Live host: `FlyingDutchman`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Action: keep on `Trusted` tomorrow unless a later decision explicitly reclassifies it; this is John and Manndra's gaming PC, not a homelab service host by default
|
||||||
|
- Validate:
|
||||||
|
- host still reachable
|
||||||
|
- services behave as expected
|
||||||
|
- Safe default: leave it in Trusted
|
||||||
|
|
||||||
|
3. USW Pro HD 24 port 22
|
||||||
|
- Live host: `Nomad`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Action: move as part of the controlled three-host server batch
|
||||||
|
- Validate:
|
||||||
|
- expected address
|
||||||
|
- services reachable from Trusted
|
||||||
|
|
||||||
|
4. USW Pro HD 24 port 24
|
||||||
|
- Live host: `Serenity`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Action: move as part of the controlled three-host server batch
|
||||||
|
- Validate:
|
||||||
|
- expected address
|
||||||
|
- intended services reachable
|
||||||
|
|
||||||
|
5. USW Pro HD 24 port 23
|
||||||
|
- Live host: `PlausibleDeniability`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Action: move as part of the controlled three-host server batch after Nomad and Serenity are ready
|
||||||
|
- Validate:
|
||||||
|
- expected address
|
||||||
|
- SSH/admin path still works
|
||||||
|
- critical homelab services still work
|
||||||
|
- Warning:
|
||||||
|
- because Nomad, Serenity, and PD are coupled by active shares/services, treat them as one planned wave rather than three unrelated experiments
|
||||||
|
- Rocinante should stay as the control box until that wave is complete
|
||||||
|
|
||||||
|
## 7. Old IoT Device Disposition Sheet
|
||||||
|
|
||||||
|
### Migrate Now
|
||||||
|
1. `MyQ-29B` -> `192.168.1.130` -> target `IoT`
|
||||||
|
2. `LG_Smart_Dryer2_open` -> `192.168.1.186` -> target `IoT`
|
||||||
|
3. `Samsung-FamilyHub` -> `192.168.1.149` -> target `IoT`
|
||||||
|
4. `Main-Floor` ecobee -> `192.168.1.102` -> target `IoT`
|
||||||
|
5. `Upstairs` ecobee -> `192.168.1.131` -> target `IoT`
|
||||||
|
|
||||||
|
Validation for each:
|
||||||
|
- app still works
|
||||||
|
- device online
|
||||||
|
- no broad helper rule was needed
|
||||||
|
|
||||||
|
### Migrate Later Only If Calm
|
||||||
|
6. `Google-Home-Mini` -> `192.168.1.185`
|
||||||
|
7. `3c:8d:20:f3:92:36` -> Google device -> `192.168.1.192`
|
||||||
|
8. `90:ca:fa:b6:7f:6e` -> Google device -> `192.168.1.129`
|
||||||
|
|
||||||
|
Rule:
|
||||||
|
- Google is in scope tomorrow, but start with one Google-class device only unless everything is smooth
|
||||||
|
- if it demands broad discovery hacks, stop and defer
|
||||||
|
|
||||||
|
### Quarantine Tomorrow
|
||||||
|
9. `5c:61:99:41:73:40` -> unknown Cloud Network device
|
||||||
|
10. `60:74:f4:54:fd:ec` -> Private/randomized
|
||||||
|
11. `60:74:f4:7b:6a:11` -> Private/randomized
|
||||||
|
12. `c0:f5:35:20:5d:94` -> AMPAK device
|
||||||
|
13. `d4:ad:fc:60:90:6a` -> Intellirocks
|
||||||
|
14. `d4:ad:fc:ea:7f:65` -> Intellirocks
|
||||||
|
|
||||||
|
Rule:
|
||||||
|
- these do not earn clean-IoT membership by being merely online
|
||||||
|
- leave them on Legacy CIA quarantine until identified better
|
||||||
|
|
||||||
|
## 8. Camera/Security Reality Check
|
||||||
|
|
||||||
|
Already true:
|
||||||
|
- `front-doorbell` is already on Camera at `10.5.20.217`
|
||||||
|
|
||||||
|
Tomorrow’s security work is therefore mainly:
|
||||||
|
- move/clean up chimes
|
||||||
|
- tighten policy
|
||||||
|
- preserve the doorbell’s healthy state
|
||||||
|
|
||||||
|
## 9. Firewall Build Priorities
|
||||||
|
|
||||||
|
Build these first:
|
||||||
|
- established/related allow
|
||||||
|
- invalid drop
|
||||||
|
- management shield
|
||||||
|
- Trusted admin -> Management allow
|
||||||
|
- Trusted -> Servers allow
|
||||||
|
- Guest -> internet only
|
||||||
|
- IoT -> DNS/NTP/internet + specific helpers only
|
||||||
|
- Camera -> DNS/NTP/internet/Protect only
|
||||||
|
- Legacy CIA -> DNS/NTP/internet only + explicit one-offs only
|
||||||
|
- broad internal denies for Guest/IoT/Camera/Legacy CIA
|
||||||
|
|
||||||
|
Do not do tomorrow unless proven necessary:
|
||||||
|
- broad mDNS/cast trust exceptions
|
||||||
|
- loose `IoT -> Servers any`
|
||||||
|
- loose `Legacy CIA -> Trusted any`
|
||||||
|
|
||||||
|
## 10. Validation Gates
|
||||||
|
|
||||||
|
After each meaningful change, check the smallest thing that proves success.
|
||||||
|
|
||||||
|
### After management changes
|
||||||
|
- [ ] UniFi admin still reachable
|
||||||
|
- [ ] gateway/switch/AP still visible
|
||||||
|
- [ ] no surprise loss of wireless control
|
||||||
|
|
||||||
|
### After each server port move
|
||||||
|
- [ ] host gets correct subnet/gateway
|
||||||
|
- [ ] reachable from Trusted
|
||||||
|
- [ ] expected service path works
|
||||||
|
|
||||||
|
### After the Nomad/Serenity/PD batch
|
||||||
|
- [ ] NFS/shared-service relationships recover cleanly
|
||||||
|
- [ ] PD remains reachable from Rocinante/admin path
|
||||||
|
- [ ] no cross-server dependency is hanging half-broken
|
||||||
|
|
||||||
|
### After each IoT move
|
||||||
|
- [ ] device rejoins expected SSID/VLAN
|
||||||
|
- [ ] app/control works
|
||||||
|
- [ ] no broad workaround rule added
|
||||||
|
|
||||||
|
### After security changes
|
||||||
|
- [ ] doorbell/chime online
|
||||||
|
- [ ] expected app/admin behavior works
|
||||||
|
|
||||||
|
### After quarantine posture changes
|
||||||
|
- [ ] Legacy CIA devices still have minimum acceptable functionality
|
||||||
|
- [ ] they do not have new local trust
|
||||||
|
|
||||||
|
## 11. Fast Rollback Strip
|
||||||
|
|
||||||
|
If anything goes sideways:
|
||||||
|
1. revert the last moved device/port first
|
||||||
|
2. revert the last SSID/VLAN assignment second
|
||||||
|
3. revert the last firewall rule/order change third
|
||||||
|
4. restore management-plane reachability before doing anything clever
|
||||||
|
5. do not stack fresh changes on top of confusion
|
||||||
|
|
||||||
|
Immediate rollback triggers:
|
||||||
|
- loss of UniFi management-plane access
|
||||||
|
- AP/switch disappears unexpectedly
|
||||||
|
- moved server loses reachability and root cause is not obvious quickly
|
||||||
|
- critical household function breaks and cannot be explained fast
|
||||||
|
- Google weirdness starts baiting sloppy panic rules
|
||||||
|
|
||||||
|
## 12. Explicit Stop Condition
|
||||||
|
|
||||||
|
Stop when:
|
||||||
|
- Management is materially cleaner
|
||||||
|
- Cameras lane is sane
|
||||||
|
- core server moves are done or deliberately deferred with reasons
|
||||||
|
- easy-value IoT moves are done
|
||||||
|
- Legacy CIA is explicitly a quarantine/sunset lane
|
||||||
|
- the next remaining work item smells like “heroics” instead of “mechanical completion”
|
||||||
|
|
||||||
|
That is success.
|
||||||
|
Not every leftover wart has to die tomorrow.
|
||||||
261
home/doris-dashboard/docs/network-cutover-minute-checklist.md
Normal file
261
home/doris-dashboard/docs/network-cutover-minute-checklist.md
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
# Home Network Redesign Minute-by-Minute Cutover Checklist
|
||||||
|
|
||||||
|
> For Doris: this is the live change-window script. Use it like a pilot checklist, not a brainstorming prompt.
|
||||||
|
|
||||||
|
Goal: execute the redesign tomorrow with deliberate pauses, validation after every block of changes, and clean rollback points.
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
- You have UniFi admin access.
|
||||||
|
- One stable Trusted admin client remains connected the entire time.
|
||||||
|
- You are not trying to empty Legacy CIA completely in one heroic swing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## T-30 to T-15 minutes: staging and sanity
|
||||||
|
|
||||||
|
- [ ] Open the runbook:
|
||||||
|
- `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/network-redesign-implementation-runbook.md`
|
||||||
|
- [ ] Open firewall rule order:
|
||||||
|
- `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/network-firewall-rule-order.md`
|
||||||
|
- [ ] Open Legacy CIA worksheet:
|
||||||
|
- `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/legacy-cia-triage-worksheet.md`
|
||||||
|
- [ ] Open visual artifacts:
|
||||||
|
- `http://10.5.30.7:8787/network-redesign.html`
|
||||||
|
- `http://10.5.30.7:8787/network-web.html`
|
||||||
|
- [ ] Confirm Rocinante is the live control box.
|
||||||
|
- [ ] Confirm fallback route to UniFi exists in case Rocinante loses the lane mid-change.
|
||||||
|
- [ ] Decide whether U6 LR is in or out for tomorrow.
|
||||||
|
- [ ] Do not start until you know where rollback begins.
|
||||||
|
|
||||||
|
## Minute 0-10: baseline capture
|
||||||
|
|
||||||
|
- [ ] Screenshot/export current networks.
|
||||||
|
- [ ] Screenshot/export current SSIDs.
|
||||||
|
- [ ] Screenshot/export firewall rules and order.
|
||||||
|
- [ ] Screenshot/export port profiles.
|
||||||
|
- [ ] Screenshot/export current switch port assignments.
|
||||||
|
- [ ] Screenshot/export AP config/broadcast mappings.
|
||||||
|
- [ ] Record current IP/MAC for:
|
||||||
|
- PD
|
||||||
|
- Serenity
|
||||||
|
- Nomad
|
||||||
|
- Rocinante
|
||||||
|
- doorbell
|
||||||
|
- Protect chimes
|
||||||
|
- ecobees
|
||||||
|
- MyQ
|
||||||
|
- Samsung FamilyHub
|
||||||
|
- LG dryer
|
||||||
|
- known Google/cast devices
|
||||||
|
- [ ] Snapshot which clients are on Management now.
|
||||||
|
- [ ] Snapshot which clients are on Legacy CIA now.
|
||||||
|
|
||||||
|
Stop/go check:
|
||||||
|
- [ ] If baseline capture is incomplete, stop.
|
||||||
|
|
||||||
|
## Minute 10-20: create/normalize objects only
|
||||||
|
|
||||||
|
- [ ] Create or confirm target networks:
|
||||||
|
- Mgmt 10
|
||||||
|
- Trusted 20
|
||||||
|
- Servers 30
|
||||||
|
- IoT 40
|
||||||
|
- Guest 50
|
||||||
|
- Cameras 60
|
||||||
|
- Legacy CIA retained
|
||||||
|
- [ ] Create or confirm port profiles.
|
||||||
|
- [ ] Create or confirm SSIDs:
|
||||||
|
- Trusted
|
||||||
|
- Trusted-Compat if needed
|
||||||
|
- IoT
|
||||||
|
- Guest
|
||||||
|
- Security
|
||||||
|
- CIA Via only if still needed as temporary quarantine SSID
|
||||||
|
- [ ] Create/stage rule objects/groups.
|
||||||
|
- [ ] Enter staged firewall rules in proper order if not already present.
|
||||||
|
|
||||||
|
Stop/go check:
|
||||||
|
- [ ] Nothing should have moved yet.
|
||||||
|
- [ ] Trusted admin client must still be fine.
|
||||||
|
|
||||||
|
## Minute 20-35: management cleanup first
|
||||||
|
|
||||||
|
- [ ] Confirm UDM, switches, and APs are mapped to Management intent.
|
||||||
|
- [ ] Remove obvious non-infrastructure devices from Management.
|
||||||
|
- [ ] Identify any stragglers remaining in Management and list them.
|
||||||
|
- [ ] If Security lane is ready, prepare to move chimes immediately.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- [ ] UniFi still sees gateway, switches, APs.
|
||||||
|
- [ ] Trusted admin client can still reach controller.
|
||||||
|
- [ ] No surprise disconnects.
|
||||||
|
|
||||||
|
Rollback trigger:
|
||||||
|
- [ ] If management reachability degrades, revert the last infra change immediately.
|
||||||
|
|
||||||
|
## Minute 35-50: stand up Cameras / Security lane
|
||||||
|
|
||||||
|
- [ ] Activate/validate Cameras VLAN 60.
|
||||||
|
- [ ] Activate Security SSID if needed.
|
||||||
|
- [ ] Move Protect chimes.
|
||||||
|
- [ ] Move doorbell if ready.
|
||||||
|
- [ ] Confirm no security devices remain polluting Management.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- [ ] device rejoins expected lane
|
||||||
|
- [ ] app/admin/Protect behavior still works
|
||||||
|
- [ ] no broad emergency rules added yet
|
||||||
|
|
||||||
|
Rollback trigger:
|
||||||
|
- [ ] If doorbell/chimes become unrecoverable fast, revert that device only.
|
||||||
|
|
||||||
|
## Minute 50-75: move core servers to Servers VLAN 30
|
||||||
|
|
||||||
|
Order recommendation for this window:
|
||||||
|
1. keep Rocinante in place as the live operator station
|
||||||
|
2. move Nomad
|
||||||
|
3. move Serenity
|
||||||
|
4. move PD
|
||||||
|
5. move Rocinante only after the main batch is validated, or defer it
|
||||||
|
6. leave FlyingDutchman on Trusted unless you explicitly decide to reclassify it later
|
||||||
|
|
||||||
|
For the Nomad/Serenity/PD batch:
|
||||||
|
- [ ] pre-stage the exact three-host move order before the first change
|
||||||
|
- [ ] move Nomad to Servers VLAN/profile
|
||||||
|
- [ ] move Serenity to Servers VLAN/profile
|
||||||
|
- [ ] move PD to Servers VLAN/profile
|
||||||
|
- [ ] verify IP/gateway/subnet for all three
|
||||||
|
- [ ] verify from Rocinante
|
||||||
|
- [ ] verify NFS/shared-service paths recover cleanly
|
||||||
|
- [ ] note any reservation/DNS fix needed before continuing
|
||||||
|
|
||||||
|
Validation after the batch:
|
||||||
|
- [ ] each host responds as expected
|
||||||
|
- [ ] shared services still work
|
||||||
|
- [ ] PD is still reachable from Rocinante
|
||||||
|
- [ ] no accidental management loss
|
||||||
|
|
||||||
|
Rollback trigger:
|
||||||
|
- [ ] If the batch breaks and root cause is not obvious quickly, revert the last moved host first and unwind in reverse order.
|
||||||
|
|
||||||
|
## Minute 75-100: easy-value IoT migration
|
||||||
|
|
||||||
|
Move in this general order:
|
||||||
|
1. MyQ
|
||||||
|
2. Samsung FamilyHub
|
||||||
|
3. LG dryer
|
||||||
|
4. ecobees if confidence remains high
|
||||||
|
|
||||||
|
For each device/class:
|
||||||
|
- [ ] move to IoT VLAN 40 / IoT SSID
|
||||||
|
- [ ] wait for reassociation
|
||||||
|
- [ ] verify cloud/app control
|
||||||
|
- [ ] verify no stupid broad helper rule was needed
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- [ ] app works
|
||||||
|
- [ ] device online
|
||||||
|
- [ ] no Management exposure
|
||||||
|
|
||||||
|
Rollback trigger:
|
||||||
|
- [ ] If household-critical behavior breaks, revert only that device and continue with safer devices.
|
||||||
|
|
||||||
|
## Minute 100-115: Legacy CIA triage pass
|
||||||
|
|
||||||
|
- [ ] Open the triage worksheet.
|
||||||
|
- [ ] For every remaining Legacy CIA client, mark:
|
||||||
|
- migrate later
|
||||||
|
- quarantine
|
||||||
|
- kill candidate
|
||||||
|
- [ ] Keep useful-but-fragile leftovers on Legacy CIA.
|
||||||
|
- [ ] Apply quarantine firewall posture.
|
||||||
|
- [ ] Ensure no new devices are intentionally assigned there.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- [ ] Legacy CIA is now explicitly a quarantine lane, not fake production IoT.
|
||||||
|
- [ ] Remaining devices are explainable.
|
||||||
|
|
||||||
|
## Minute 115-130: Google / discovery problem children only if time remains
|
||||||
|
|
||||||
|
- [ ] Pick one Google/cast device only.
|
||||||
|
- [ ] Try moving it to IoT.
|
||||||
|
- [ ] Test casting/discovery from Trusted.
|
||||||
|
- [ ] If it needs exceptions, create only host-specific narrow ones.
|
||||||
|
- [ ] If it gets ugly, stop and revert that one device.
|
||||||
|
|
||||||
|
Success condition:
|
||||||
|
- [ ] either one working pattern discovered
|
||||||
|
- [ ] or explicit deferral decided without poisoning policy
|
||||||
|
|
||||||
|
## Minute 130-140: Guest and SSID cleanup
|
||||||
|
|
||||||
|
- [ ] Verify Guest SSID -> VLAN 50.
|
||||||
|
- [ ] Test internet-only behavior.
|
||||||
|
- [ ] Disable stale SSIDs that are no longer needed.
|
||||||
|
- [ ] Keep Trusted-Compat only if it solved a real problem.
|
||||||
|
- [ ] Keep CIA Via only if remaining quarantined devices still require it.
|
||||||
|
|
||||||
|
## Minute 140-150: final validation sweep
|
||||||
|
|
||||||
|
From Trusted:
|
||||||
|
- [ ] internet works
|
||||||
|
- [ ] UniFi admin works
|
||||||
|
- [ ] PD/Serenity/Nomad/Rocinante reachable as intended
|
||||||
|
|
||||||
|
From Guest:
|
||||||
|
- [ ] internet works
|
||||||
|
- [ ] local IPs blocked
|
||||||
|
|
||||||
|
From IoT moved devices:
|
||||||
|
- [ ] app control works
|
||||||
|
|
||||||
|
From Cameras/Security:
|
||||||
|
- [ ] doorbell/chimes online as expected
|
||||||
|
|
||||||
|
From Management perspective:
|
||||||
|
- [ ] infra only, or any temporary exception is explicitly documented
|
||||||
|
|
||||||
|
From Legacy CIA perspective:
|
||||||
|
- [ ] only leftovers remain
|
||||||
|
- [ ] quarantine posture is active
|
||||||
|
|
||||||
|
## Minute 150-165: document what survived the window
|
||||||
|
|
||||||
|
- [ ] Record exact devices moved successfully.
|
||||||
|
- [ ] Record exact devices reverted.
|
||||||
|
- [ ] Record temporary exceptions added.
|
||||||
|
- [ ] Record which devices remain on Legacy CIA and why.
|
||||||
|
- [ ] Record whether U6 LR remains out of scope or becomes next task.
|
||||||
|
|
||||||
|
## Minute 165-180: stop, do not improvise
|
||||||
|
|
||||||
|
If minimum success criteria are met, stop.
|
||||||
|
|
||||||
|
Minimum success criteria:
|
||||||
|
- [ ] Management cleaned up materially
|
||||||
|
- [ ] Cameras lane exists
|
||||||
|
- [ ] Servers lane exists and core hosts are moved or clearly staged
|
||||||
|
- [ ] IoT lane exists and easy-value devices moved
|
||||||
|
- [ ] Legacy CIA converted into explicit quarantine/sunset lane
|
||||||
|
- [ ] no broad insecure panic rules were added
|
||||||
|
|
||||||
|
If not met:
|
||||||
|
- [ ] roll back only the broken last-mile changes
|
||||||
|
- [ ] do not keep piling on work while tired
|
||||||
|
|
||||||
|
## Emergency rollback strip
|
||||||
|
|
||||||
|
If something goes sideways:
|
||||||
|
1. revert the last moved client/device
|
||||||
|
2. revert the last SSID/VLAN assignment change
|
||||||
|
3. revert the last firewall rule change/order change
|
||||||
|
4. restore management-plane reachability first
|
||||||
|
5. only continue after stability is back
|
||||||
|
|
||||||
|
## Out of scope unless everything is shockingly smooth
|
||||||
|
|
||||||
|
- full emptying of Legacy CIA
|
||||||
|
- perfect cast/mDNS handling for every Google thing
|
||||||
|
- RF fine-tuning
|
||||||
|
- U6 LR optimization work
|
||||||
|
- cleanup of every stale historical object in UniFi
|
||||||
103
home/doris-dashboard/docs/network-cutover-red-team-card.md
Normal file
103
home/doris-dashboard/docs/network-cutover-red-team-card.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Network Cutover Red Team Card
|
||||||
|
|
||||||
|
Use this during the live window when your brain gets dumb.
|
||||||
|
|
||||||
|
Goal:
|
||||||
|
- make one clean change
|
||||||
|
- validate it
|
||||||
|
- either continue or revert
|
||||||
|
|
||||||
|
If you feel rushed, stop.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DO
|
||||||
|
|
||||||
|
- Keep one known-good Trusted admin client online the entire time.
|
||||||
|
- Keep Rocinante alive as the live control box until the main server batch is done or a fallback admin path is proven.
|
||||||
|
- Keep UniFi open.
|
||||||
|
- Keep the master operator sheet open.
|
||||||
|
- Change one thing at a time.
|
||||||
|
- Validate after every host, SSID, profile, or firewall change.
|
||||||
|
- Revert the last change fast if the break is not obvious.
|
||||||
|
- Write down temporary exceptions immediately.
|
||||||
|
- Treat Legacy CIA as quarantine, not a place to hide problems.
|
||||||
|
|
||||||
|
## DO NOT
|
||||||
|
|
||||||
|
- Do not move unrelated core hosts casually.
|
||||||
|
- Do not move Rocinante early if you are actively driving the migration from it.
|
||||||
|
- Do not touch port 27 casually.
|
||||||
|
- Do not touch port 1 unless intentionally normalizing infra.
|
||||||
|
- Do not "clean up" Google/cast weirdness early.
|
||||||
|
- Do not add broad allow-any-any panic rules.
|
||||||
|
- Do not keep pushing forward while tired or annoyed.
|
||||||
|
- Do not trust memory when the docs already exist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FIRST IF BROKEN
|
||||||
|
|
||||||
|
1. Ask: what was the very last change?
|
||||||
|
2. Revert that exact change first.
|
||||||
|
3. Restore management reachability before anything else.
|
||||||
|
4. Confirm UniFi gateway, switch, and AP visibility.
|
||||||
|
5. Confirm the Trusted admin client still reaches the controller.
|
||||||
|
6. Only then decide whether to retry.
|
||||||
|
|
||||||
|
If management breaks:
|
||||||
|
- revert the last infra/profile/VLAN change immediately
|
||||||
|
- stop until control-plane access is stable
|
||||||
|
|
||||||
|
If the Nomad/Serenity/PD batch breaks:
|
||||||
|
- revert the last moved host first and unwind in reverse order
|
||||||
|
- do not start debugging Google nonsense in the middle of a storage/service outage
|
||||||
|
|
||||||
|
If a server breaks:
|
||||||
|
- revert only that server/port/profile
|
||||||
|
- do not move the next server yet
|
||||||
|
|
||||||
|
If an IoT device breaks:
|
||||||
|
- revert only that device
|
||||||
|
- keep the rest of the window moving
|
||||||
|
|
||||||
|
If Google/cast gets weird:
|
||||||
|
- defer it
|
||||||
|
- do not poison policy for the whole network
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SAFE ORDER
|
||||||
|
|
||||||
|
1. Baseline capture
|
||||||
|
2. Create/confirm objects only
|
||||||
|
3. Management cleanup
|
||||||
|
4. Security/chimes/camera lane
|
||||||
|
5. Server ports one by one
|
||||||
|
6. Easy IoT wins
|
||||||
|
7. Legacy CIA quarantine pass
|
||||||
|
8. Google weirdness only if time and confidence remain
|
||||||
|
9. Guest/SSID cleanup
|
||||||
|
10. Final validation
|
||||||
|
11. Stop
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SUCCESS LOOKS LIKE
|
||||||
|
|
||||||
|
- Management materially cleaner
|
||||||
|
- Camera/Security lane exists
|
||||||
|
- Servers lane exists and core hosts are moved or clearly staged
|
||||||
|
- Easy-value IoT devices are moved
|
||||||
|
- Legacy CIA is now an explicit quarantine/sunset lane
|
||||||
|
- No broad insecure emergency rules were added
|
||||||
|
|
||||||
|
## HARD STOP
|
||||||
|
|
||||||
|
Stop if:
|
||||||
|
- you lose clear management reachability
|
||||||
|
- you are stacking unresolved breakage
|
||||||
|
- you are guessing instead of validating
|
||||||
|
- you catch yourself saying "it’ll probably be fine"
|
||||||
|
|
||||||
|
That sentence means stop.
|
||||||
411
home/doris-dashboard/docs/network-firewall-rule-order.md
Normal file
411
home/doris-dashboard/docs/network-firewall-rule-order.md
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
# Home Network Redesign Firewall Rule Order
|
||||||
|
|
||||||
|
> For Doris: this is the intended UniFi firewall ordering for the redesign. Use groups/comments so every rule is explainable at 2 AM.
|
||||||
|
|
||||||
|
Goal: enforce clean segmentation between Management, Trusted, Servers, IoT, Guest, Cameras, and Legacy CIA quarantine without using broad lazy exceptions.
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
- Final networks:
|
||||||
|
- VLAN 10 Management -> `10.5.10.0/24`
|
||||||
|
- VLAN 20 Trusted -> `10.5.20.0/24`
|
||||||
|
- VLAN 30 Servers -> `10.5.30.0/24`
|
||||||
|
- VLAN 40 IoT -> `10.5.40.0/24`
|
||||||
|
- VLAN 50 Guest -> `10.5.50.0/24`
|
||||||
|
- VLAN 60 Cameras -> `10.5.60.0/24`
|
||||||
|
- Legacy CIA quarantine -> existing legacy subnet/VLAN retained temporarily
|
||||||
|
- Use address/object groups where UniFi allows them.
|
||||||
|
- Apply stateful best practice first: established/related before policy rules.
|
||||||
|
- Specific allows always go above broad denies.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Object / Group Inventory To Create First
|
||||||
|
|
||||||
|
Create these objects before writing rules:
|
||||||
|
|
||||||
|
### Network groups
|
||||||
|
- `NET-MGMT`
|
||||||
|
- `NET-TRUSTED`
|
||||||
|
- `NET-SERVERS`
|
||||||
|
- `NET-IOT`
|
||||||
|
- `NET-GUEST`
|
||||||
|
- `NET-CAMERAS`
|
||||||
|
- `NET-LEGACY-CIA`
|
||||||
|
- `NET-RFC1918-ALL` = all internal subnets above plus any other local internal ranges
|
||||||
|
|
||||||
|
### Device / host groups
|
||||||
|
- `HOST-ADMIN-TRUSTED`
|
||||||
|
- your main laptop
|
||||||
|
- your phone/tablet if you really need admin from it
|
||||||
|
- `HOST-CORE-SERVICES`
|
||||||
|
- PD
|
||||||
|
- Serenity
|
||||||
|
- Nomad
|
||||||
|
- Rocinante
|
||||||
|
- any controller/helper hosts
|
||||||
|
- `HOST-PROTECT-SERVICES`
|
||||||
|
- NVR / Protect endpoints if separate from above
|
||||||
|
- `HOST-DNS`
|
||||||
|
- whichever DNS resolvers clients should use
|
||||||
|
- `HOST-NTP`
|
||||||
|
- if you use local NTP, otherwise this can be omitted
|
||||||
|
- `HOST-IOT-HELPERS`
|
||||||
|
- only services IoT devices are explicitly allowed to hit
|
||||||
|
- `HOST-CAMERA-HELPERS`
|
||||||
|
- only services Cameras are explicitly allowed to hit
|
||||||
|
- `HOST-LEGACY-EXCEPTIONS`
|
||||||
|
- only for ugly one-off CIA quarantine exceptions
|
||||||
|
|
||||||
|
### Port groups
|
||||||
|
- `PORT-DNS` = 53 TCP/UDP
|
||||||
|
- `PORT-DHCP` = 67-68 UDP
|
||||||
|
- `PORT-NTP` = 123 UDP
|
||||||
|
- `PORT-WEB-ADMIN` = 80,443,8443,9443 or whatever you actually use
|
||||||
|
- `PORT-SSH` = 22
|
||||||
|
- `PORT-PROTECT` = exact ports only if needed
|
||||||
|
- `PORT-MDNS` = 5353 UDP
|
||||||
|
- `PORT-CAST` = exact discovery/control ports if later proven necessary
|
||||||
|
|
||||||
|
## 2. Rule Philosophy
|
||||||
|
|
||||||
|
- Trusted is the only lane that gets routine admin rights.
|
||||||
|
- Management is infrastructure-only and should accept traffic only from explicit admin initiators.
|
||||||
|
- Servers accept specific human/service traffic from Trusted and tightly scoped helper traffic from IoT/Cameras.
|
||||||
|
- IoT, Cameras, and Legacy CIA are default-deny internally.
|
||||||
|
- Guest is internet-only.
|
||||||
|
- Legacy CIA is hospice, not production.
|
||||||
|
|
||||||
|
## 3. Recommended Rule Order
|
||||||
|
|
||||||
|
This is ordered top to bottom.
|
||||||
|
|
||||||
|
### Section A: Core state handling
|
||||||
|
1. `ALLOW Established/Related`
|
||||||
|
- Action: Allow
|
||||||
|
- States: Established, Related
|
||||||
|
- Source: Any
|
||||||
|
- Destination: Any
|
||||||
|
- Comment: `baseline stateful return traffic`
|
||||||
|
|
||||||
|
2. `DROP Invalid`
|
||||||
|
- Action: Drop
|
||||||
|
- States: Invalid
|
||||||
|
- Source: Any
|
||||||
|
- Destination: Any
|
||||||
|
- Comment: `drop broken/invalid sessions early`
|
||||||
|
|
||||||
|
### Section B: Management protection
|
||||||
|
3. `ALLOW Trusted Admin -> Management Admin Surfaces`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `HOST-ADMIN-TRUSTED`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Ports: `PORT-WEB-ADMIN`, `PORT-SSH` and anything truly required
|
||||||
|
- Comment: `explicit admin to management`
|
||||||
|
|
||||||
|
4. `ALLOW Trusted Admin -> Gateway Infra Utilities`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `HOST-ADMIN-TRUSTED`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Ports: ICMP plus other explicitly needed management utilities
|
||||||
|
- Comment: `ping/test/manage infra`
|
||||||
|
|
||||||
|
5. `DROP IoT -> Management`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Comment: `iot never initiates to management`
|
||||||
|
|
||||||
|
6. `DROP Cameras -> Management`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Comment: `camera lane never initiates to management`
|
||||||
|
|
||||||
|
7. `DROP Guest -> Management`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-GUEST`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Comment: `guest blocked from management`
|
||||||
|
|
||||||
|
8. `DROP Legacy CIA -> Management`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Comment: `legacy quarantine blocked from management`
|
||||||
|
|
||||||
|
9. `DROP Any Internal -> Management`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-RFC1918-ALL`
|
||||||
|
- Destination: `NET-MGMT`
|
||||||
|
- Comment: `default management shield after explicit allows`
|
||||||
|
|
||||||
|
### Section C: Trusted human lane
|
||||||
|
10. `ALLOW Trusted -> Servers Approved Access`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-TRUSTED`
|
||||||
|
- Destination: `NET-SERVERS`
|
||||||
|
- Ports: Any or explicit service groups depending on how tight you want day one
|
||||||
|
- Comment: `trusted human lane to servers`
|
||||||
|
|
||||||
|
11. `ALLOW Trusted -> Cameras Admin/Viewer Access`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-TRUSTED`
|
||||||
|
- Destination: `NET-CAMERAS`
|
||||||
|
- Ports: exact ports if known, otherwise temporary broader allow during migration
|
||||||
|
- Comment: `trusted operator to camera lane`
|
||||||
|
|
||||||
|
12. `ALLOW Trusted -> IoT Control Exceptions`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-TRUSTED`
|
||||||
|
- Destination: `NET-IOT`
|
||||||
|
- Ports: only what control/discovery really needs
|
||||||
|
- Comment: `trusted control to iot where required`
|
||||||
|
|
||||||
|
### Section D: Server helper traffic
|
||||||
|
13. `ALLOW Servers -> IoT Approved Helpers`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `HOST-IOT-HELPERS` or `NET-SERVERS` narrowed to real helper hosts
|
||||||
|
- Destination: `NET-IOT`
|
||||||
|
- Ports: exact service ports only
|
||||||
|
- Comment: `ha/mqtt/etc only if actually used`
|
||||||
|
|
||||||
|
14. `ALLOW Cameras -> Protect Services`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `HOST-PROTECT-SERVICES`
|
||||||
|
- Ports: exact Protect/NVR ports
|
||||||
|
- Comment: `camera lane to protect only`
|
||||||
|
|
||||||
|
15. `ALLOW IoT -> Approved Server Helpers`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `HOST-IOT-HELPERS`
|
||||||
|
- Ports: exact ports only
|
||||||
|
- Comment: `iot can only hit specific helper services`
|
||||||
|
|
||||||
|
16. `ALLOW Legacy CIA -> Approved One-Off Exception`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: specific host(s) in `NET-LEGACY-CIA`
|
||||||
|
- Destination: specific host(s)
|
||||||
|
- Ports: exact ports only
|
||||||
|
- Disabled by default unless proven needed
|
||||||
|
- Comment: `ugly but narrow quarantine exception`
|
||||||
|
|
||||||
|
### Section E: DNS / NTP baseline for restricted lanes
|
||||||
|
17. `ALLOW IoT -> DNS`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `HOST-DNS`
|
||||||
|
- Ports: `PORT-DNS`
|
||||||
|
- Comment: `iot dns`
|
||||||
|
|
||||||
|
18. `ALLOW Cameras -> DNS`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `HOST-DNS`
|
||||||
|
- Ports: `PORT-DNS`
|
||||||
|
- Comment: `camera dns`
|
||||||
|
|
||||||
|
19. `ALLOW Legacy CIA -> DNS`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `HOST-DNS`
|
||||||
|
- Ports: `PORT-DNS`
|
||||||
|
- Comment: `legacy dns`
|
||||||
|
|
||||||
|
20. `ALLOW IoT -> NTP`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `HOST-NTP` or Any if using public NTP
|
||||||
|
- Ports: `PORT-NTP`
|
||||||
|
- Comment: `iot ntp`
|
||||||
|
|
||||||
|
21. `ALLOW Cameras -> NTP`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `HOST-NTP` or Any if using public NTP
|
||||||
|
- Ports: `PORT-NTP`
|
||||||
|
- Comment: `camera ntp`
|
||||||
|
|
||||||
|
22. `ALLOW Legacy CIA -> NTP`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `HOST-NTP` or Any if using public NTP
|
||||||
|
- Ports: `PORT-NTP`
|
||||||
|
- Comment: `legacy ntp`
|
||||||
|
|
||||||
|
### Section F: Internet access for constrained lanes
|
||||||
|
23. `ALLOW Guest -> Internet`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-GUEST`
|
||||||
|
- Destination: Internet / Not RFC1918
|
||||||
|
- Comment: `guest internet only`
|
||||||
|
|
||||||
|
24. `ALLOW IoT -> Internet`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: Internet / Not RFC1918
|
||||||
|
- Comment: `iot outbound internet`
|
||||||
|
|
||||||
|
25. `ALLOW Cameras -> Internet Updates`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: Internet / Not RFC1918
|
||||||
|
- Comment: `camera updates/cloud only if needed`
|
||||||
|
|
||||||
|
26. `ALLOW Legacy CIA -> Internet`
|
||||||
|
- Action: Allow
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: Internet / Not RFC1918
|
||||||
|
- Comment: `legacy quarantine outbound only`
|
||||||
|
|
||||||
|
### Section G: Broad internal denies for restricted lanes
|
||||||
|
27. `DROP Guest -> RFC1918/Internal`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-GUEST`
|
||||||
|
- Destination: `NET-RFC1918-ALL`
|
||||||
|
- Comment: `guest blocked from local networks`
|
||||||
|
|
||||||
|
28. `DROP IoT -> Trusted`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `NET-TRUSTED`
|
||||||
|
- Comment: `iot blocked from human lane`
|
||||||
|
|
||||||
|
29. `DROP IoT -> Servers`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `NET-SERVERS`
|
||||||
|
- Comment: `iot blocked from servers after helper exceptions`
|
||||||
|
|
||||||
|
30. `DROP IoT -> Cameras`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-IOT`
|
||||||
|
- Destination: `NET-CAMERAS`
|
||||||
|
- Comment: `iot blocked from camera lane`
|
||||||
|
|
||||||
|
31. `DROP Cameras -> Trusted`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `NET-TRUSTED`
|
||||||
|
- Comment: `cameras blocked from human lane`
|
||||||
|
|
||||||
|
32. `DROP Cameras -> Servers`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `NET-SERVERS`
|
||||||
|
- Comment: `cameras blocked from servers except protect`
|
||||||
|
|
||||||
|
33. `DROP Cameras -> IoT`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-CAMERAS`
|
||||||
|
- Destination: `NET-IOT`
|
||||||
|
- Comment: `camera lane blocked from iot`
|
||||||
|
|
||||||
|
34. `DROP Legacy CIA -> Trusted`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `NET-TRUSTED`
|
||||||
|
- Comment: `legacy blocked from human lane`
|
||||||
|
|
||||||
|
35. `DROP Legacy CIA -> Servers`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `NET-SERVERS`
|
||||||
|
- Comment: `legacy blocked from servers after one-offs`
|
||||||
|
|
||||||
|
36. `DROP Legacy CIA -> Cameras`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `NET-CAMERAS`
|
||||||
|
- Comment: `legacy blocked from security lane`
|
||||||
|
|
||||||
|
37. `DROP Legacy CIA -> IoT`
|
||||||
|
- Action: Drop
|
||||||
|
- Source: `NET-LEGACY-CIA`
|
||||||
|
- Destination: `NET-IOT`
|
||||||
|
- Comment: `legacy does not mingle with clean iot`
|
||||||
|
|
||||||
|
### Section H: Optional discovery exceptions
|
||||||
|
38. `ALLOW Trusted -> mDNS Reflector / Discovery Helper`
|
||||||
|
- Only if real testing proves needed
|
||||||
|
- Prefer reflector/helper architecture over broad subnet trust
|
||||||
|
|
||||||
|
39. `ALLOW Specific Google/Cast Device -> Specific Helper`
|
||||||
|
- Host-specific only
|
||||||
|
- Ports exact only
|
||||||
|
- Comment with device name and why it exists
|
||||||
|
|
||||||
|
40. `DROP Any Temporary Discovery Exception Cleanup Marker`
|
||||||
|
- Not a real rule type, just a process note
|
||||||
|
- Every temporary discovery rule gets a unique comment and review date
|
||||||
|
|
||||||
|
## 4. Rule Notes By Lane
|
||||||
|
|
||||||
|
Management:
|
||||||
|
- Most-protected lane.
|
||||||
|
- Admin access only from known trusted admin devices.
|
||||||
|
|
||||||
|
Trusted:
|
||||||
|
- Primary operator lane.
|
||||||
|
- Allowed to talk where humans need to talk, but still not a dumping ground.
|
||||||
|
|
||||||
|
Servers:
|
||||||
|
- Prefer exact service-based exposure later.
|
||||||
|
- For tomorrow, a broader Trusted -> Servers allow is acceptable if you document it and tighten later.
|
||||||
|
|
||||||
|
IoT:
|
||||||
|
- Default deny inward and outward to internal LANs except helper carve-outs.
|
||||||
|
|
||||||
|
Cameras:
|
||||||
|
- Same philosophy as IoT, but even stricter.
|
||||||
|
- If Protect is local, keep camera-to-protect flow exact.
|
||||||
|
|
||||||
|
Legacy CIA:
|
||||||
|
- Outbound internet, DNS, NTP, nothing more unless explicitly justified.
|
||||||
|
- Every exception should feel embarrassing.
|
||||||
|
|
||||||
|
## 5. What To Stage Tomorrow Versus Later
|
||||||
|
|
||||||
|
Stage tomorrow:
|
||||||
|
- established/related
|
||||||
|
- invalid drop
|
||||||
|
- management shield
|
||||||
|
- guest internet-only pattern
|
||||||
|
- IoT/Cameras/Legacy broad internal denies
|
||||||
|
- Trusted admin to Management
|
||||||
|
- Trusted to Servers
|
||||||
|
- DNS/NTP for restricted lanes
|
||||||
|
- Legacy CIA quarantine posture
|
||||||
|
|
||||||
|
Defer if not needed immediately:
|
||||||
|
- mDNS/cast helper rules
|
||||||
|
- fancy service-specific microsegmentation between Trusted and Servers
|
||||||
|
- any discovery exceptions not proven by real failure testing
|
||||||
|
|
||||||
|
## 6. Validation Checklist After Rule Deployment
|
||||||
|
|
||||||
|
- Trusted admin client can still reach UniFi/gateway/switch/AP admin surfaces
|
||||||
|
- Trusted can reach core server services
|
||||||
|
- Guest gets internet and cannot reach local IPs
|
||||||
|
- IoT device can resolve DNS, keep time, and reach vendor cloud if expected
|
||||||
|
- Camera/security device remains online and can reach Protect if required
|
||||||
|
- Legacy CIA device still works only at minimum acceptable level
|
||||||
|
- No restricted lane can initiate into Management
|
||||||
|
|
||||||
|
## 7. Red Flags
|
||||||
|
|
||||||
|
Do not do these:
|
||||||
|
- allow `IoT -> Servers any`
|
||||||
|
- allow `Legacy CIA -> Trusted any`
|
||||||
|
- allow `Guest -> RFC1918 any`
|
||||||
|
- put discovery/broadcast panic rules above security structure without device-specific justification
|
||||||
|
- leave unnamed temporary rules in place after testing
|
||||||
|
|
||||||
|
## 8. First Tightening Pass After Tomorrow
|
||||||
|
|
||||||
|
After the cutover stabilizes:
|
||||||
|
- narrow Trusted -> Servers from broad allow to explicit service sets if useful
|
||||||
|
- delete any temporary discovery exceptions that proved unnecessary
|
||||||
|
- audit whether Cameras even need outbound internet
|
||||||
|
- audit whether Legacy CIA can lose individual devices or the whole SSID
|
||||||
@@ -0,0 +1,419 @@
|
|||||||
|
# Home Network Redesign Implementation Runbook
|
||||||
|
|
||||||
|
> For Doris: use this during the live UniFi change window. Goal is a controlled cutover with explicit validation and rollback at each boundary.
|
||||||
|
|
||||||
|
Goal: deploy the target VLAN/SSID/firewall design with minimum drama, keep legacy CIA Via as a quarantine lane for devices that cannot yet be migrated, and finish the window with an understandable, supportable network.
|
||||||
|
|
||||||
|
Architecture summary:
|
||||||
|
- Final target lanes remain VLAN 10 Management, VLAN 20 Trusted, VLAN 30 Servers, VLAN 40 IoT, VLAN 50 Guest, VLAN 60 Cameras/Security.
|
||||||
|
- Legacy CIA Via remains temporarily alive as a quarantine/sunset VLAN for unmanageable legacy devices.
|
||||||
|
- No non-infrastructure devices stay on Management when the window closes.
|
||||||
|
- No broad trust exceptions are added just to make migration feel easier.
|
||||||
|
|
||||||
|
Tech stack / control surfaces:
|
||||||
|
- UniFi Network controller on UDM Pro
|
||||||
|
- UDM Pro gateway/firewall
|
||||||
|
- USW Pro HD 24 and other UniFi switching
|
||||||
|
- U7 Pro and optional U6 LR APs
|
||||||
|
- Doris artifacts for reference:
|
||||||
|
- `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/public/network-redesign.html`
|
||||||
|
- `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/public/network-web.html`
|
||||||
|
- `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/network-redesign-plan.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Required Access
|
||||||
|
|
||||||
|
Required to implement directly:
|
||||||
|
- UniFi OS / Network write access on the UDM Pro site at `https://10.5.0.1`
|
||||||
|
- Ability to create/edit:
|
||||||
|
- VLANs / networks
|
||||||
|
- Wi-Fi SSIDs
|
||||||
|
- firewall rules
|
||||||
|
- port profiles
|
||||||
|
- switch port assignments
|
||||||
|
- device settings for APs/switches
|
||||||
|
- Ability to see client inventory with MAC/IP/vendor names
|
||||||
|
|
||||||
|
Required for safe validation after each move:
|
||||||
|
- Existing SSH access to PD via `pd` (`truenas_admin@10.5.30.6`)
|
||||||
|
- Reachability to NOMAD-hosted Doris web artifacts at `10.5.30.7:8787`
|
||||||
|
- A Trusted client on-site on Wi‑Fi for live user validation
|
||||||
|
- At least one device on each of these categories available for test:
|
||||||
|
- Trusted client
|
||||||
|
- server service
|
||||||
|
- IoT device/app pair
|
||||||
|
- guest client
|
||||||
|
- camera/security device if moved tomorrow
|
||||||
|
|
||||||
|
Optional but strongly preferred:
|
||||||
|
- Temporary full admin role in UniFi instead of pair-driving through read-only
|
||||||
|
- Access to export or screenshot current config before changes
|
||||||
|
- Ability to check AP/switch port LEDs/physical labels if anything is ambiguous
|
||||||
|
|
||||||
|
Fallback if you do not want to grant write access:
|
||||||
|
- Pair-drive the entire window with you at the keyboard in UniFi while I call every exact change in order
|
||||||
|
- This is slower but workable
|
||||||
|
|
||||||
|
## 2. What Can Be Finished Before Tomorrow
|
||||||
|
|
||||||
|
Pre-work Doris can finish now without write access:
|
||||||
|
- Final runbook and sequence document
|
||||||
|
- Final object naming convention
|
||||||
|
- Final firewall intent matrix
|
||||||
|
- Validation checklist per VLAN
|
||||||
|
- Rollback checklist per phase
|
||||||
|
- Device triage sheet for Legacy CIA Via:
|
||||||
|
- migrate
|
||||||
|
- quarantine
|
||||||
|
- kill
|
||||||
|
|
||||||
|
Pre-work requiring only read access / screenshots / exports from you:
|
||||||
|
- Confirm exact current UniFi object names
|
||||||
|
- Confirm whether VLAN IDs 10/20/30/40/50/60 already exist or must be created
|
||||||
|
- Confirm current SSID names and which APs broadcast each one
|
||||||
|
- Confirm current port-profile names and which switch ports use them
|
||||||
|
- Confirm which clients are currently on Management and CIA Via
|
||||||
|
- Confirm whether U6 LR is physically installed and link-ready or still shelved
|
||||||
|
|
||||||
|
Pre-work requiring write access, if granted tonight:
|
||||||
|
- Create missing target networks/VLAN objects without moving clients yet
|
||||||
|
- Create disabled/staged SSIDs without enabling them yet
|
||||||
|
- Create new port profiles without assigning them yet
|
||||||
|
- Create firewall rules in disabled or low-priority staged form
|
||||||
|
- Label/comment rules and profiles for tomorrow’s change window
|
||||||
|
|
||||||
|
## 3. Hard Rules For The Change Window
|
||||||
|
|
||||||
|
- One operator console on Trusted stays connected the whole time.
|
||||||
|
- One fallback path into UniFi must remain alive before any management change.
|
||||||
|
- Do not move multiple unknown devices at once.
|
||||||
|
- Do not delete Legacy CIA Via tomorrow unless it is truly empty, which is unlikely.
|
||||||
|
- Do not move legacy/unmanageable junk into Management, Trusted, or Servers to “just get done.”
|
||||||
|
- After every change group, stop and validate before continuing.
|
||||||
|
- If management-plane reachability is degraded, roll back immediately before chasing anything else.
|
||||||
|
|
||||||
|
## 4. Target Objects
|
||||||
|
|
||||||
|
### Networks / VLANs
|
||||||
|
- `NET-MGMT` -> VLAN 10 -> `10.5.10.0/24`
|
||||||
|
- `NET-TRUSTED` -> VLAN 20 -> `10.5.20.0/24`
|
||||||
|
- `NET-SERVERS` -> VLAN 30 -> `10.5.30.0/24`
|
||||||
|
- `NET-IOT` -> VLAN 40 -> `10.5.40.0/24`
|
||||||
|
- `NET-GUEST` -> VLAN 50 -> `10.5.50.0/24`
|
||||||
|
- `NET-CAMERAS` -> VLAN 60 -> `10.5.60.0/24`
|
||||||
|
- `NET-LEGACY-CIA` -> existing legacy VLAN/subnet retained temporarily as quarantine lane
|
||||||
|
|
||||||
|
### Wi‑Fi SSIDs
|
||||||
|
- `Trusted` -> VLAN 20
|
||||||
|
- `Trusted-Compat` -> VLAN 20, optional temporary only
|
||||||
|
- `IoT` -> VLAN 40
|
||||||
|
- `Guest` -> VLAN 50
|
||||||
|
- `Security` -> VLAN 60
|
||||||
|
- `CIA Via` -> legacy VLAN, temporary only, no new joins
|
||||||
|
|
||||||
|
### Port profiles
|
||||||
|
- `PP-TRUNK-UPLINK`
|
||||||
|
- `PP-ACCESS-MGMT`
|
||||||
|
- `PP-ACCESS-TRUSTED`
|
||||||
|
- `PP-ACCESS-SERVERS`
|
||||||
|
- `PP-ACCESS-IOT`
|
||||||
|
- `PP-ACCESS-GUEST`
|
||||||
|
- `PP-ACCESS-CAMERAS`
|
||||||
|
- `PP-ACCESS-LEGACY-CIA`
|
||||||
|
|
||||||
|
## 5. Firewall Policy Intent
|
||||||
|
|
||||||
|
Trusted:
|
||||||
|
- allow to Management for admin devices only
|
||||||
|
- allow to Servers for normal human/service use
|
||||||
|
- allow to Cameras for operator/admin use
|
||||||
|
- allow limited control flows to IoT if required
|
||||||
|
|
||||||
|
Servers:
|
||||||
|
- allow explicit services outward as needed
|
||||||
|
- no sloppy east-west broad allow
|
||||||
|
- allow NVR/Protect-related flows to Cameras only if required
|
||||||
|
|
||||||
|
IoT:
|
||||||
|
- allow DHCP/DNS/NTP/internet
|
||||||
|
- deny Management
|
||||||
|
- deny Trusted by default
|
||||||
|
- deny Servers except specific helper/service destinations
|
||||||
|
- explicit discovery exceptions only when proven necessary
|
||||||
|
|
||||||
|
Guest:
|
||||||
|
- internet only
|
||||||
|
- deny all local RFC1918/internal subnets
|
||||||
|
- client isolation on Wi‑Fi
|
||||||
|
|
||||||
|
Cameras/Security:
|
||||||
|
- allow DHCP/DNS/NTP/internet/update paths as needed
|
||||||
|
- deny broad lateral movement
|
||||||
|
- allow only Protect/NVR/admin destinations
|
||||||
|
|
||||||
|
Legacy CIA quarantine lane:
|
||||||
|
- allow DHCP/DNS/NTP/internet
|
||||||
|
- deny Management
|
||||||
|
- deny Trusted
|
||||||
|
- deny Cameras
|
||||||
|
- deny Servers by default
|
||||||
|
- add only device-specific exceptions if absolutely required
|
||||||
|
- no new devices assigned here intentionally
|
||||||
|
|
||||||
|
## 6. Baseline Capture Before First Change
|
||||||
|
|
||||||
|
Capture all of this before changing anything:
|
||||||
|
- Screenshot/export all existing networks
|
||||||
|
- Screenshot/export all SSIDs
|
||||||
|
- Screenshot/export firewall rules in current order
|
||||||
|
- Screenshot/export port profiles
|
||||||
|
- Screenshot/export AP settings
|
||||||
|
- Screenshot/export switch port assignments
|
||||||
|
- Record current IP/gateway/subnet for:
|
||||||
|
- PD
|
||||||
|
- Serenity
|
||||||
|
- Nomad
|
||||||
|
- Rocinante
|
||||||
|
- UDM Pro
|
||||||
|
- U7 Pro
|
||||||
|
- U6 LR if present
|
||||||
|
- Record current MAC/IP list for important Old IoT devices:
|
||||||
|
- Google Home Mini / cast devices
|
||||||
|
- both ecobees
|
||||||
|
- MyQ
|
||||||
|
- Samsung Family Hub
|
||||||
|
- LG dryer
|
||||||
|
- known legacy bulbs/plugs
|
||||||
|
- doorbell
|
||||||
|
- Protect chimes
|
||||||
|
- Confirm current DHCP reservations/static mappings that may break when VLANs move
|
||||||
|
|
||||||
|
## 7. Validation Tests To Run Repeatedly
|
||||||
|
|
||||||
|
Trusted validation:
|
||||||
|
- Trusted Wi‑Fi client gets correct subnet/gateway
|
||||||
|
- internet works
|
||||||
|
- can reach UniFi admin
|
||||||
|
- can reach PD/NOMAD/Serenity services expected from user lane
|
||||||
|
|
||||||
|
Management validation:
|
||||||
|
- UDM, switches, APs remain visible/manageable in UniFi
|
||||||
|
- no unexpected client endpoints remain in Management
|
||||||
|
|
||||||
|
Servers validation:
|
||||||
|
- PD/Serenity/Nomad/Rocinante get correct subnet/gateway after move
|
||||||
|
- critical services respond from Trusted
|
||||||
|
- outbound internet works if required
|
||||||
|
|
||||||
|
IoT validation:
|
||||||
|
- device rejoins expected SSID/VLAN
|
||||||
|
- vendor app control still works
|
||||||
|
- local control still works only if intentionally allowed
|
||||||
|
|
||||||
|
Guest validation:
|
||||||
|
- guest device gets internet
|
||||||
|
- cannot reach local subnets
|
||||||
|
|
||||||
|
Security validation:
|
||||||
|
- doorbell/chimes/camera devices stay online
|
||||||
|
- admin/Protect access works if expected
|
||||||
|
- no reachability into unrelated lanes
|
||||||
|
|
||||||
|
Legacy CIA validation:
|
||||||
|
- quarantined devices still work at the minimum acceptable level
|
||||||
|
- no new devices are added there
|
||||||
|
|
||||||
|
## 8. Change Sequence For Tomorrow
|
||||||
|
|
||||||
|
### Phase A: Safety setup
|
||||||
|
1. Confirm one wired or strongly stable Trusted admin client is connected.
|
||||||
|
2. Open UniFi in one session and keep Doris artifacts open in another.
|
||||||
|
3. Capture all baseline screenshots/exports.
|
||||||
|
4. Confirm rollback path into UniFi if Wi‑Fi changes go bad.
|
||||||
|
5. Confirm whether U6 LR participates tomorrow or stays out of scope.
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- baseline captured
|
||||||
|
- management reachability stable
|
||||||
|
- rollback path confirmed
|
||||||
|
|
||||||
|
### Phase B: Create or normalize objects without moving clients
|
||||||
|
1. Create any missing target networks.
|
||||||
|
2. Create any missing port profiles.
|
||||||
|
3. Create/stage SSIDs:
|
||||||
|
- Trusted
|
||||||
|
- Trusted-Compat if needed
|
||||||
|
- IoT
|
||||||
|
- Guest
|
||||||
|
- Security
|
||||||
|
4. Create/stage Legacy CIA quarantine policy object naming.
|
||||||
|
5. Create staged firewall rules with comments.
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- all objects exist
|
||||||
|
- nothing has moved yet
|
||||||
|
- no current connectivity loss
|
||||||
|
|
||||||
|
### Phase C: Management cleanup first
|
||||||
|
1. Ensure UDM, switches, and AP management surfaces are on VLAN 10 intent.
|
||||||
|
2. Remove obvious non-infrastructure devices from Management.
|
||||||
|
3. Move Protect chimes out of Management as top cleanup item if Security lane is ready.
|
||||||
|
4. Re-validate UniFi visibility of gateway, switches, APs.
|
||||||
|
|
||||||
|
Rollback if:
|
||||||
|
- UniFi loses management of gateway/switch/APs
|
||||||
|
- admin client can no longer reach management plane
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- Management is infrastructure-only or very close to it
|
||||||
|
- no human endpoints or smart accessories remain there except known temporary exceptions tracked in writing
|
||||||
|
|
||||||
|
### Phase D: Build/activate Cameras lane
|
||||||
|
1. Activate/validate `NET-CAMERAS`.
|
||||||
|
2. Activate `Security` SSID if used.
|
||||||
|
3. Move doorbell and Protect chimes to Cameras/Security.
|
||||||
|
4. Validate app/admin behavior.
|
||||||
|
|
||||||
|
Rollback if:
|
||||||
|
- security devices fall offline and cannot be recovered quickly
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- doorbell/chimes are no longer polluting Management
|
||||||
|
- Camera lane exists for future growth
|
||||||
|
|
||||||
|
### Phase E: Move core servers to Servers VLAN 30
|
||||||
|
1. Move one least-scary server first.
|
||||||
|
2. Validate from Trusted.
|
||||||
|
3. Update any reservation/DNS/profile dependency if needed.
|
||||||
|
4. Move remaining core hosts one by one:
|
||||||
|
- PD
|
||||||
|
- Serenity
|
||||||
|
- Nomad
|
||||||
|
- Rocinante
|
||||||
|
5. After each move, validate service reachability and outbound needs.
|
||||||
|
|
||||||
|
Rollback if:
|
||||||
|
- critical service path breaks and root cause is not obvious within a few minutes
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- core hosts reside in Servers lane
|
||||||
|
- Trusted still reaches intended services
|
||||||
|
|
||||||
|
### Phase F: Move easy IoT first
|
||||||
|
1. Activate/validate `IoT` SSID and VLAN 40.
|
||||||
|
2. Move easiest/least-fragile devices first:
|
||||||
|
- MyQ
|
||||||
|
- Samsung Family Hub
|
||||||
|
- LG dryer
|
||||||
|
- ecobees if confidence is high
|
||||||
|
3. Validate each class before moving the next.
|
||||||
|
4. Leave Google/cast pain until the end of IoT work.
|
||||||
|
|
||||||
|
Rollback if:
|
||||||
|
- essential household function breaks and app recovery is not immediate
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- easy-value devices are out of Legacy CIA
|
||||||
|
- no unnecessary broad allow rules were added
|
||||||
|
|
||||||
|
### Phase G: Legacy CIA quarantine handling
|
||||||
|
1. Rename/reclassify CIA Via mentally and operationally as quarantine, not production IoT.
|
||||||
|
2. Leave orphaned bulbs/plugs/mystery devices there.
|
||||||
|
3. Apply strict quarantine firewall posture.
|
||||||
|
4. Build a written list of remaining MACs/devices with disposition:
|
||||||
|
- migrate later
|
||||||
|
- quarantine indefinitely for now
|
||||||
|
- kill/remove when safe
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- Legacy CIA contains only leftovers and quarantined junk
|
||||||
|
- its policy is harsh and explicit
|
||||||
|
|
||||||
|
### Phase H: Google / discovery problem children
|
||||||
|
1. Move one Google/cast-class device only if time and energy remain.
|
||||||
|
2. Test discovery/casting behavior from Trusted.
|
||||||
|
3. Add only narrow exceptions if evidence proves a need.
|
||||||
|
4. If messy, stop. Leave the rest in Legacy CIA quarantine for a later targeted session.
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- either one known-good pattern exists, or the problem is intentionally deferred
|
||||||
|
|
||||||
|
### Phase I: Guest and SSID cleanup
|
||||||
|
1. Validate Guest SSID maps to VLAN 50.
|
||||||
|
2. Confirm internet-only behavior.
|
||||||
|
3. Disable/retire any stale SSIDs not still needed for migration.
|
||||||
|
4. Keep `Trusted-Compat` only if it earns its keep.
|
||||||
|
5. Keep `CIA Via` only as temporary quarantine SSID if required for remaining devices.
|
||||||
|
|
||||||
|
Exit criteria:
|
||||||
|
- SSIDs reflect policy, not history
|
||||||
|
- only necessary temporary migration SSIDs remain
|
||||||
|
|
||||||
|
## 9. Explicit Device Triage
|
||||||
|
|
||||||
|
Move tomorrow if practical:
|
||||||
|
- Protect chimes
|
||||||
|
- doorbell
|
||||||
|
- PD
|
||||||
|
- Serenity
|
||||||
|
- Nomad
|
||||||
|
- Rocinante
|
||||||
|
- MyQ
|
||||||
|
- Samsung Family Hub
|
||||||
|
- LG dryer
|
||||||
|
- both ecobees if low risk
|
||||||
|
|
||||||
|
Probably defer or treat carefully:
|
||||||
|
- Google Home / cast ecosystem
|
||||||
|
- any device requiring odd mDNS/broadcast behavior
|
||||||
|
|
||||||
|
Quarantine in Legacy CIA:
|
||||||
|
- legacy lights
|
||||||
|
- legacy plugs
|
||||||
|
- mystery old smart devices
|
||||||
|
- anything no longer controllable but still physically present
|
||||||
|
|
||||||
|
Kill candidates after observation:
|
||||||
|
- unknown inactive MACs
|
||||||
|
- devices nobody misses after blocking
|
||||||
|
- duplicate/orphaned historical entries
|
||||||
|
|
||||||
|
## 10. Rollback Rules
|
||||||
|
|
||||||
|
Immediate rollback triggers:
|
||||||
|
- loss of UniFi management-plane access
|
||||||
|
- APs/switches vanish or go isolated unexpectedly
|
||||||
|
- Trusted admin client cannot reach controller/gateway
|
||||||
|
- critical household function breaks with no quick diagnosis
|
||||||
|
|
||||||
|
Rollback scope:
|
||||||
|
- revert last moved device first
|
||||||
|
- revert last SSID mapping change second
|
||||||
|
- revert last firewall rule addition/position third
|
||||||
|
- revert network object only if the object itself is wrong
|
||||||
|
- never stack more changes onto a broken state
|
||||||
|
|
||||||
|
## 11. Definition Of Done For Tomorrow
|
||||||
|
|
||||||
|
Minimum successful tomorrow outcome:
|
||||||
|
- Management is cleaned up
|
||||||
|
- Cameras/Security lane exists
|
||||||
|
- Servers lane exists and at least core hosts are moved or staged with confidence
|
||||||
|
- IoT lane exists and at least easy-value devices are moved
|
||||||
|
- Legacy CIA is converted into explicit quarantine/sunset lane
|
||||||
|
- no broad insecure exceptions were added out of fatigue
|
||||||
|
|
||||||
|
Nice-to-have, not mandatory tomorrow:
|
||||||
|
- U6 LR restored and tuned
|
||||||
|
- Google/cast fully migrated
|
||||||
|
- Legacy CIA emptied completely
|
||||||
|
- perfect final SSID simplification
|
||||||
|
|
||||||
|
## 12. Follow-up Work After Tomorrow
|
||||||
|
|
||||||
|
- Build exact firewall rule matrix page/artifact
|
||||||
|
- Commit/push dashboard docs and artifacts into repo properly
|
||||||
|
- Review remaining Legacy CIA devices one by one
|
||||||
|
- Decide whether U6 LR returns based on actual coverage evidence
|
||||||
|
- Remove stale SSIDs, port profiles, and rules once migration dust settles
|
||||||
214
home/doris-dashboard/docs/network-redesign-plan.md
Normal file
214
home/doris-dashboard/docs/network-redesign-plan.md
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# Home Network Redesign Plan
|
||||||
|
|
||||||
|
> For Doris: this is the target architecture and migration plan that the interactive site visualizes.
|
||||||
|
|
||||||
|
Goal: redesign John's home network and homelab into a simpler, future-proof VLAN model that supports camera growth, cleaner firewall policy, better Wi-Fi placement, and easier operations.
|
||||||
|
|
||||||
|
Architecture summary:
|
||||||
|
- Collapse historical clutter into six intentional VLANs.
|
||||||
|
- Separate user devices, servers, infrastructure, IoT, guests, and cameras/security.
|
||||||
|
- Move all non-infrastructure devices off Management.
|
||||||
|
- Retire Old IoT after staged migration.
|
||||||
|
- Add the downstairs U6 LR back into service if coverage or roaming needs it.
|
||||||
|
|
||||||
|
## Target VLANs
|
||||||
|
|
||||||
|
1. Infrastructure / Management
|
||||||
|
- VLAN 10
|
||||||
|
- Subnet: 10.5.10.0/24
|
||||||
|
- Contains: UDM, switches, APs, management IPs, controller-facing infra, OOB/admin surfaces
|
||||||
|
- Policy: only Trusted admin devices and designated server tooling may initiate into this VLAN
|
||||||
|
|
||||||
|
2. Trusted Clients
|
||||||
|
- VLAN 20
|
||||||
|
- Subnet: 10.5.20.0/24
|
||||||
|
- Contains: phones, tablets, laptops, handhelds, Steam Deck, personal endpoints
|
||||||
|
- Policy: can reach Servers and selected admin surfaces; not a dumping ground for random smart devices
|
||||||
|
|
||||||
|
3. Servers / Core Services
|
||||||
|
- VLAN 30
|
||||||
|
- Subnet: 10.5.30.0/24
|
||||||
|
- Contains: PlausibleDeniability, Serenity, Nomad, Rocinante, FlyingDutchman if it is a real service host
|
||||||
|
- Policy: tightly controlled east-west; explicit service exposure to IoT/Camera where needed
|
||||||
|
|
||||||
|
4. IoT / Smart Home
|
||||||
|
- VLAN 40
|
||||||
|
- Subnet: 10.5.40.0/24
|
||||||
|
- Contains: ecobees, appliances, MyQ, Google Home-class devices, legacy smart junk worth keeping
|
||||||
|
- Policy: default deny to internal networks except DNS, NTP, specific server services, and carefully allowed discovery flows
|
||||||
|
|
||||||
|
5. Guest
|
||||||
|
- VLAN 50
|
||||||
|
- Subnet: 10.5.50.0/24
|
||||||
|
- Contains: visitor and untrusted BYOD devices
|
||||||
|
- Policy: internet only, client isolation on Wi-Fi
|
||||||
|
|
||||||
|
6. Cameras / Security
|
||||||
|
- VLAN 60
|
||||||
|
- Subnet: 10.5.60.0/24
|
||||||
|
- Contains: doorbell, Protect chimes, future cameras, NVR-adjacent accessories, security-specific Wi-Fi endpoints
|
||||||
|
- Policy: no broad access to LAN; only specific flows to Protect/NVR services and operator/admin clients
|
||||||
|
|
||||||
|
## SSID Plan
|
||||||
|
|
||||||
|
Preferred steady-state SSIDs:
|
||||||
|
- Trusted: one main trusted SSID
|
||||||
|
- Trusted-Compat: optional temporary fallback for stubborn devices only
|
||||||
|
- IoT: one dedicated smart-home SSID
|
||||||
|
- Guest: one guest SSID
|
||||||
|
- Security: one dedicated SSID for Wi-Fi doorbells/chimes/cameras if needed
|
||||||
|
|
||||||
|
Mapping recommendation:
|
||||||
|
- Trusted SSID -> VLAN 20
|
||||||
|
- Trusted-Compat SSID -> VLAN 20 (temporary or policy-reduced fallback)
|
||||||
|
- IoT SSID -> VLAN 40
|
||||||
|
- Guest SSID -> VLAN 50
|
||||||
|
- Security SSID -> VLAN 60
|
||||||
|
|
||||||
|
Current-name migration suggestion:
|
||||||
|
- Yer a Wifi Harry -> Trusted
|
||||||
|
- Whiskey Neat Fuck Ice -> temporary Trusted-Compat or retire later
|
||||||
|
- CIA Via -> repoint to IoT during migration, then decide whether to keep or rename
|
||||||
|
- UNEF's Playhouse -> Security / Cameras
|
||||||
|
|
||||||
|
## Device Placement Recommendations
|
||||||
|
|
||||||
|
Infrastructure / Management:
|
||||||
|
- UDM Pro
|
||||||
|
- USW Pro HD 24
|
||||||
|
- USW-24-PoE
|
||||||
|
- U7 Pro
|
||||||
|
- U6 LR
|
||||||
|
- other management-only appliance interfaces
|
||||||
|
|
||||||
|
Servers / Core Services:
|
||||||
|
- PlausibleDeniability
|
||||||
|
- Serenity
|
||||||
|
- Nomad
|
||||||
|
- Rocinante
|
||||||
|
- FlyingDutchman if used as a real service host
|
||||||
|
|
||||||
|
Trusted Clients:
|
||||||
|
- phones
|
||||||
|
- tablets
|
||||||
|
- handhelds
|
||||||
|
- laptops/desktops
|
||||||
|
- Steam Deck
|
||||||
|
|
||||||
|
IoT / Smart Home:
|
||||||
|
- Main-Floor ecobee
|
||||||
|
- Upstairs ecobee
|
||||||
|
- MyQ
|
||||||
|
- Samsung FamilyHub
|
||||||
|
- LG dryer
|
||||||
|
- Google Home Mini
|
||||||
|
- Google / Chromecast-class devices
|
||||||
|
- retained legacy smart devices
|
||||||
|
|
||||||
|
Cameras / Security:
|
||||||
|
- front-doorbell
|
||||||
|
- Protect chimes
|
||||||
|
- future wired/PoE cameras
|
||||||
|
- future Wi-Fi security accessories
|
||||||
|
|
||||||
|
## Wi-Fi and AP Placement
|
||||||
|
|
||||||
|
Primary AP design:
|
||||||
|
- U7 Pro remains active as the main upstairs AP.
|
||||||
|
- Reinstall the U6 LR downstairs if downstairs coverage, roaming, or camera/IoT RSSI is weak.
|
||||||
|
- Prefer wired backhaul for both APs.
|
||||||
|
|
||||||
|
Operational stance:
|
||||||
|
- Do not overcomplicate RF tuning on day one.
|
||||||
|
- First restore dual-AP coverage, then evaluate channels, transmit power, and minimum RSSI with real client behavior.
|
||||||
|
- For growing cameras/security footprint, bias toward wired PoE cameras on the Cameras VLAN when practical.
|
||||||
|
|
||||||
|
## Firewall Intent
|
||||||
|
|
||||||
|
Trusted -> Management
|
||||||
|
- allow only designated admin devices/services
|
||||||
|
|
||||||
|
Trusted -> Servers
|
||||||
|
- allow
|
||||||
|
|
||||||
|
Trusted -> IoT
|
||||||
|
- limited allow for control/discovery as needed
|
||||||
|
|
||||||
|
Trusted -> Cameras
|
||||||
|
- allow operator/admin access
|
||||||
|
|
||||||
|
Servers -> IoT
|
||||||
|
- allow only explicitly needed services
|
||||||
|
|
||||||
|
Servers -> Cameras
|
||||||
|
- allow Protect/NVR-related flows only
|
||||||
|
|
||||||
|
IoT -> internal networks
|
||||||
|
- default deny
|
||||||
|
- explicit allow to DNS, NTP, specific server services, and required discovery helpers only
|
||||||
|
|
||||||
|
Cameras -> internal networks
|
||||||
|
- default deny
|
||||||
|
- explicit allow to Protect/NVR, time, DNS, and update paths only
|
||||||
|
|
||||||
|
Guest -> internal networks
|
||||||
|
- deny
|
||||||
|
|
||||||
|
Management -> internet
|
||||||
|
- only what infrastructure actually needs
|
||||||
|
|
||||||
|
## Migration Order
|
||||||
|
|
||||||
|
Phase 0: groundwork
|
||||||
|
- create target VLANs and port profiles
|
||||||
|
- stage firewall rules
|
||||||
|
- stage target SSIDs
|
||||||
|
- document rollback points
|
||||||
|
|
||||||
|
Phase 1: infrastructure cleanup
|
||||||
|
- move all infra to Management VLAN 10
|
||||||
|
- remove non-infra clients from management
|
||||||
|
- verify switch/AP/gateway management reachability
|
||||||
|
|
||||||
|
Phase 2: servers
|
||||||
|
- move core hosts to Servers VLAN 30
|
||||||
|
- confirm service reachability from Trusted
|
||||||
|
- update DNS/static mappings/firewall rules
|
||||||
|
|
||||||
|
Phase 3: cameras/security
|
||||||
|
- move doorbell and chimes to VLAN 60
|
||||||
|
- prepare room for future camera growth
|
||||||
|
|
||||||
|
Phase 4: IoT migration
|
||||||
|
- move active smart-home devices from Old IoT into VLAN 40 in batches
|
||||||
|
- start with easiest cloud-managed devices, then thermostats, then Google ecosystem devices
|
||||||
|
|
||||||
|
Phase 5: SSID simplification
|
||||||
|
- retire Old IoT SSID after validation
|
||||||
|
- collapse or retire temporary Trusted-Compat if not needed
|
||||||
|
|
||||||
|
Phase 6: cleanup
|
||||||
|
- remove Old IoT VLAN 2
|
||||||
|
- remove stale port profiles
|
||||||
|
- remove stale firewall rules and historical exceptions
|
||||||
|
|
||||||
|
## Port Profile Set
|
||||||
|
|
||||||
|
Maintain only intentional profiles:
|
||||||
|
- trunk-uplink
|
||||||
|
- access-management
|
||||||
|
- access-trusted
|
||||||
|
- access-servers
|
||||||
|
- access-iot
|
||||||
|
- access-guest
|
||||||
|
- access-cameras
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- Management contains infrastructure only
|
||||||
|
- Old IoT is retired
|
||||||
|
- Cameras/Security has room to grow now, not later
|
||||||
|
- VLAN purpose is obvious at a glance
|
||||||
|
- Wi-Fi SSIDs reflect policy, not historical accidents
|
||||||
|
- Firewall rules match intent and are explainable
|
||||||
|
- A tired operator can still understand the network at 2 AM
|
||||||
226
home/doris-dashboard/docs/old-iot-tomorrow-disposition-list.md
Normal file
226
home/doris-dashboard/docs/old-iot-tomorrow-disposition-list.md
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
# Old IoT Tomorrow Disposition List
|
||||||
|
|
||||||
|
> For Doris: this is the concrete disposition call for every current live Old IoT client seen during read-only recon. This is the list to use tomorrow unless live validation proves otherwise.
|
||||||
|
|
||||||
|
Source basis:
|
||||||
|
- live UniFi read-only recon on 2026-05-22
|
||||||
|
- all devices listed were active on SSID `CIA Via` through the `U7 Pro`
|
||||||
|
- /home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/unifi-readonly-recon-2026-05-22.md
|
||||||
|
- /home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/legacy-cia-triage-worksheet.md
|
||||||
|
|
||||||
|
Disposition labels:
|
||||||
|
- `migrate-now`
|
||||||
|
- `migrate-later`
|
||||||
|
- `quarantine`
|
||||||
|
- `kill-candidate`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Move tomorrow if practical (`migrate-now`)
|
||||||
|
|
||||||
|
### MyQ-29B
|
||||||
|
- IP: `192.168.1.130`
|
||||||
|
- Vendor: The Chamberlain Group, Inc
|
||||||
|
- Tomorrow disposition: `migrate-now`
|
||||||
|
- Target: `IoT`
|
||||||
|
- Why:
|
||||||
|
- known device
|
||||||
|
- clear owner/function
|
||||||
|
- easy-value migration win
|
||||||
|
- Validation:
|
||||||
|
- MyQ app still works
|
||||||
|
- garage status/control behaves normally
|
||||||
|
|
||||||
|
### Main-Floor
|
||||||
|
- IP: `192.168.1.102`
|
||||||
|
- Vendor: ecobee inc
|
||||||
|
- Tomorrow disposition: `migrate-now`
|
||||||
|
- Target: `IoT`
|
||||||
|
- Why:
|
||||||
|
- known thermostat
|
||||||
|
- clear function
|
||||||
|
- deserves proper IoT placement
|
||||||
|
- Validation:
|
||||||
|
- thermostat visible in app
|
||||||
|
- control/update works
|
||||||
|
|
||||||
|
### Upstairs
|
||||||
|
- IP: `192.168.1.131`
|
||||||
|
- Vendor: ecobee inc
|
||||||
|
- Tomorrow disposition: `migrate-now`
|
||||||
|
- Target: `IoT`
|
||||||
|
- Why:
|
||||||
|
- same reasoning as Main-Floor
|
||||||
|
- Validation:
|
||||||
|
- thermostat visible in app
|
||||||
|
- control/update works
|
||||||
|
|
||||||
|
### LG_Smart_Dryer2_open
|
||||||
|
- IP: `192.168.1.186`
|
||||||
|
- Vendor: LG Innotek
|
||||||
|
- Tomorrow disposition: `migrate-now`
|
||||||
|
- Target: `IoT`
|
||||||
|
- Why:
|
||||||
|
- named appliance
|
||||||
|
- low emotional/operational complexity
|
||||||
|
- good early migration candidate
|
||||||
|
- Validation:
|
||||||
|
- LG app/device connectivity still works
|
||||||
|
|
||||||
|
### Samsung-FamilyHub
|
||||||
|
- IP: `192.168.1.149`
|
||||||
|
- Vendor: Samsung Electronics Co., Ltd
|
||||||
|
- Tomorrow disposition: `migrate-now`
|
||||||
|
- Target: `IoT`
|
||||||
|
- Why:
|
||||||
|
- known appliance
|
||||||
|
- obvious long-term home
|
||||||
|
- Validation:
|
||||||
|
- Samsung app/features still work at the acceptable level
|
||||||
|
|
||||||
|
## 2. Move only if time/energy remains, otherwise defer (`migrate-later`)
|
||||||
|
|
||||||
|
### Google-Home-Mini
|
||||||
|
- IP: `192.168.1.185`
|
||||||
|
- Vendor: Google, Inc.
|
||||||
|
- Tomorrow disposition: `migrate-later`
|
||||||
|
- Target: `IoT`
|
||||||
|
- Why:
|
||||||
|
- discovery/cast pain risk
|
||||||
|
- likely to provoke panic-rule temptation
|
||||||
|
- Tomorrow handling:
|
||||||
|
- move only if the rest of the window is already stable
|
||||||
|
- if ugly, revert or leave on Legacy CIA quarantine
|
||||||
|
- Validation:
|
||||||
|
- app sees device
|
||||||
|
- casting/discovery from Trusted behaves acceptably
|
||||||
|
|
||||||
|
### 3c:8d:20:f3:92:36
|
||||||
|
- IP: `192.168.1.192`
|
||||||
|
- Vendor: Google, Inc.
|
||||||
|
- Tomorrow disposition: `migrate-later`
|
||||||
|
- Target: likely `IoT`
|
||||||
|
- Why:
|
||||||
|
- Google-class unknown-by-name device
|
||||||
|
- same discovery/cast risk profile
|
||||||
|
- Tomorrow handling:
|
||||||
|
- identify what it is before moving if possible
|
||||||
|
- otherwise defer
|
||||||
|
- Validation:
|
||||||
|
- whatever its expected Google behavior is still works
|
||||||
|
|
||||||
|
### 90:ca:fa:b6:7f:6e
|
||||||
|
- IP: `192.168.1.129`
|
||||||
|
- Vendor: Google, Inc.
|
||||||
|
- Tomorrow disposition: `migrate-later`
|
||||||
|
- Target: likely `IoT`
|
||||||
|
- Why:
|
||||||
|
- likely another Google/cast/discovery problem child
|
||||||
|
- Tomorrow handling:
|
||||||
|
- identify first if possible
|
||||||
|
- otherwise defer
|
||||||
|
- Validation:
|
||||||
|
- expected Google behavior survives move
|
||||||
|
|
||||||
|
## 3. Leave on Legacy CIA quarantine tomorrow unless identified better (`quarantine`)
|
||||||
|
|
||||||
|
### 5c:61:99:41:73:40
|
||||||
|
- IP: `192.168.1.172`
|
||||||
|
- Vendor: Cloud Network Technology Singapore Pte. Ltd.
|
||||||
|
- Tomorrow disposition: `quarantine`
|
||||||
|
- Why:
|
||||||
|
- unnamed
|
||||||
|
- unclear function
|
||||||
|
- not enough evidence to grant clean-IoT membership
|
||||||
|
- Tomorrow handling:
|
||||||
|
- leave on Legacy CIA with harsh restrictions
|
||||||
|
- identify later
|
||||||
|
|
||||||
|
### 60:74:f4:54:fd:ec
|
||||||
|
- IP: `192.168.1.136`
|
||||||
|
- Vendor: Private
|
||||||
|
- Tomorrow disposition: `quarantine`
|
||||||
|
- Why:
|
||||||
|
- private/randomized identity
|
||||||
|
- no useful name
|
||||||
|
- function unclear
|
||||||
|
- Tomorrow handling:
|
||||||
|
- quarantine first, not migration first
|
||||||
|
|
||||||
|
### 60:74:f4:7b:6a:11
|
||||||
|
- IP: `192.168.1.117`
|
||||||
|
- Vendor: Private
|
||||||
|
- Tomorrow disposition: `quarantine`
|
||||||
|
- Why:
|
||||||
|
- same reasoning as above
|
||||||
|
|
||||||
|
### c0:f5:35:20:5d:94
|
||||||
|
- IP: `192.168.1.183`
|
||||||
|
- Vendor: AMPAK Technology,Inc.
|
||||||
|
- Tomorrow disposition: `quarantine`
|
||||||
|
- Why:
|
||||||
|
- could be all sorts of embedded junk
|
||||||
|
- no positive identification
|
||||||
|
|
||||||
|
### d4:ad:fc:60:90:6a
|
||||||
|
- IP: `192.168.1.100`
|
||||||
|
- Vendor: Shenzhen Intellirocks Tech co., ltd
|
||||||
|
- Tomorrow disposition: `quarantine`
|
||||||
|
- Why:
|
||||||
|
- unclear function
|
||||||
|
- likely disposable/low-trust smart junk unless proven otherwise
|
||||||
|
|
||||||
|
### d4:ad:fc:ea:7f:65
|
||||||
|
- IP: `192.168.1.101`
|
||||||
|
- Vendor: Shenzhen Intellirocks Tech co., ltd
|
||||||
|
- Tomorrow disposition: `quarantine`
|
||||||
|
- Why:
|
||||||
|
- same reasoning as sister device above
|
||||||
|
|
||||||
|
## 4. Kill-candidate logic
|
||||||
|
|
||||||
|
No current Old IoT client is marked `kill-candidate` immediately from inventory alone because we still saw them as active.
|
||||||
|
|
||||||
|
But these become `kill-candidate` fast if:
|
||||||
|
- nobody can identify them
|
||||||
|
- nobody can say what they do
|
||||||
|
- blocking/quarantining them produces no complaints
|
||||||
|
- they are duplicate Google/embedded junk with no actual value
|
||||||
|
|
||||||
|
Most likely future kill-candidate pool:
|
||||||
|
- the unnamed `Private` devices
|
||||||
|
- the unknown Cloud Network / AMPAK devices
|
||||||
|
- the two unnamed Intellirocks devices
|
||||||
|
- any Google MAC-only device nobody can identify once mapped physically
|
||||||
|
|
||||||
|
## 5. Suggested tomorrow move order inside Old IoT
|
||||||
|
|
||||||
|
Use this order:
|
||||||
|
1. MyQ-29B
|
||||||
|
2. LG_Smart_Dryer2_open
|
||||||
|
3. Samsung-FamilyHub
|
||||||
|
4. Main-Floor ecobee
|
||||||
|
5. Upstairs ecobee
|
||||||
|
6. stop and assess
|
||||||
|
7. only then test one Google device if the window is still calm
|
||||||
|
|
||||||
|
Everything else:
|
||||||
|
- stays on Legacy CIA quarantine
|
||||||
|
- gets documented
|
||||||
|
- gets revisited later with actual identification work
|
||||||
|
|
||||||
|
## 6. Explicit anti-mistakes
|
||||||
|
|
||||||
|
Do not do these tomorrow:
|
||||||
|
- do not migrate unknown MAC-only devices just because they are online
|
||||||
|
- do not broaden IoT trust to appease Google discovery quickly
|
||||||
|
- do not move quarantine-worthy junk into Management or Trusted
|
||||||
|
- do not try to fully empty Old IoT if the window starts getting noisy
|
||||||
|
|
||||||
|
## 7. Success condition for tomorrow
|
||||||
|
|
||||||
|
A successful tomorrow outcome is:
|
||||||
|
- easy-value named devices are moved into IoT
|
||||||
|
- Google/discovery weirdness is either handled narrowly or deferred
|
||||||
|
- all unknown junk remains contained on Legacy CIA
|
||||||
|
- every remaining Old IoT device has an explicit reason it is still there
|
||||||
135
home/doris-dashboard/docs/server-batch-validation-checklist.md
Normal file
135
home/doris-dashboard/docs/server-batch-validation-checklist.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Server Batch Validation Checklist
|
||||||
|
|
||||||
|
Use this immediately after moving Nomad, Serenity, and PD.
|
||||||
|
Do not start Google nonsense, SSID cleanup, or extra cleanup until this is green.
|
||||||
|
|
||||||
|
Primary control path:
|
||||||
|
- operator box: Rocinante
|
||||||
|
- critical dependency group: Nomad + Serenity + PlausibleDeniability
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Before the batch
|
||||||
|
|
||||||
|
- [ ] Rocinante is stable and currently has UniFi/admin access.
|
||||||
|
- [ ] Fallback admin path exists if Rocinante drops off.
|
||||||
|
- [ ] Current port/profile screenshots taken for:
|
||||||
|
- [ ] Port 22 -> Nomad
|
||||||
|
- [ ] Port 24 -> Serenity
|
||||||
|
- [ ] Port 23 -> PD
|
||||||
|
- [ ] Expected target subnet/gateway for Servers VLAN is written down.
|
||||||
|
- [ ] You are ready to unwind in reverse order if needed.
|
||||||
|
|
||||||
|
Move order:
|
||||||
|
1. Nomad
|
||||||
|
2. Serenity
|
||||||
|
3. PD
|
||||||
|
|
||||||
|
Rollback order if ugly:
|
||||||
|
1. PD
|
||||||
|
2. Serenity
|
||||||
|
3. Nomad
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Per-host quick checks during the wave
|
||||||
|
|
||||||
|
### After Nomad move
|
||||||
|
- [ ] Link came back
|
||||||
|
- [ ] Nomad got expected Servers IP/subnet/gateway
|
||||||
|
- [ ] Rocinante can still reach Nomad
|
||||||
|
- [ ] Expected Nomad-hosted service(s) respond
|
||||||
|
- [ ] No obvious share/dependency explosion yet
|
||||||
|
|
||||||
|
### After Serenity move
|
||||||
|
- [ ] Link came back
|
||||||
|
- [ ] Serenity got expected Servers IP/subnet/gateway
|
||||||
|
- [ ] Rocinante can still reach Serenity
|
||||||
|
- [ ] Expected Serenity-hosted service(s) respond
|
||||||
|
- [ ] No obvious share/dependency explosion yet
|
||||||
|
|
||||||
|
### After PD move
|
||||||
|
- [ ] Link came back
|
||||||
|
- [ ] PD got expected Servers IP/subnet/gateway
|
||||||
|
- [ ] Rocinante can still reach PD
|
||||||
|
- [ ] SSH/admin path to PD still works
|
||||||
|
- [ ] Critical PD-hosted homelab services respond
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Immediate batch validation
|
||||||
|
|
||||||
|
Do all of these before touching anything else:
|
||||||
|
|
||||||
|
### Reachability
|
||||||
|
- [ ] Rocinante -> Nomad reachable
|
||||||
|
- [ ] Rocinante -> Serenity reachable
|
||||||
|
- [ ] Rocinante -> PD reachable
|
||||||
|
|
||||||
|
### IP sanity
|
||||||
|
- [ ] Nomad is on the intended Servers subnet
|
||||||
|
- [ ] Serenity is on the intended Servers subnet
|
||||||
|
- [ ] PD is on the intended Servers subnet
|
||||||
|
- [ ] Default gateway is correct for all three
|
||||||
|
|
||||||
|
### Cross-host dependency sanity
|
||||||
|
- [ ] NFS mounts/shares recovered cleanly
|
||||||
|
- [ ] No host is hanging on stale routes or dead mount targets
|
||||||
|
- [ ] Any name-resolution dependency still works as expected
|
||||||
|
- [ ] No authentication/admin path you need for the rest of the window disappeared
|
||||||
|
|
||||||
|
### Service sanity
|
||||||
|
- [ ] Doris/dashboard path(s) you need are still reachable
|
||||||
|
- [ ] Any storage-backed services needed for the live window still work
|
||||||
|
- [ ] Nothing critical is stuck waiting on a dead share
|
||||||
|
|
||||||
|
### Control-plane sanity
|
||||||
|
- [ ] UniFi is still reachable from Rocinante
|
||||||
|
- [ ] Switch/AP visibility still looks normal
|
||||||
|
- [ ] No management-plane surprise coincided with the server batch
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stop conditions
|
||||||
|
|
||||||
|
Stop and unwind if any of these are true:
|
||||||
|
- [ ] PD is not reachable from Rocinante
|
||||||
|
- [ ] NFS/share recovery is unclear after a short wait
|
||||||
|
- [ ] You cannot tell whether the problem is routing, DNS, or service binding
|
||||||
|
- [ ] UniFi/admin access is degraded at the same time
|
||||||
|
- [ ] You are guessing instead of validating
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reverse-order rollback strip
|
||||||
|
|
||||||
|
If the batch is bad and the reason is not immediately obvious:
|
||||||
|
|
||||||
|
1. Revert PD port/profile first.
|
||||||
|
- [ ] PD returns to prior lane/profile
|
||||||
|
- [ ] PD admin path restored
|
||||||
|
2. Revert Serenity port/profile second.
|
||||||
|
- [ ] Serenity returns to prior lane/profile
|
||||||
|
3. Revert Nomad port/profile third.
|
||||||
|
- [ ] Nomad returns to prior lane/profile
|
||||||
|
4. Re-check:
|
||||||
|
- [ ] Rocinante -> PD
|
||||||
|
- [ ] Rocinante -> Serenity
|
||||||
|
- [ ] Rocinante -> Nomad
|
||||||
|
- [ ] UniFi/admin access stable again
|
||||||
|
|
||||||
|
Do not continue the broader migration until this set is stable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When this batch counts as successful
|
||||||
|
|
||||||
|
- [ ] Nomad moved successfully
|
||||||
|
- [ ] Serenity moved successfully
|
||||||
|
- [ ] PD moved successfully
|
||||||
|
- [ ] Rocinante still has the admin path you need
|
||||||
|
- [ ] Shares/services recovered cleanly enough to continue
|
||||||
|
- [ ] No emergency broad firewall workaround was needed
|
||||||
|
|
||||||
|
If all six are true, move on.
|
||||||
|
If not, stop pretending and fix or roll back first.
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# UniFi Live Preflight Snapshot
|
||||||
|
|
||||||
|
Date: 2026-05-22
|
||||||
|
Mode: read-only live pull from PD using the Doris UniFi operator account
|
||||||
|
Controller: `https://10.5.0.1`
|
||||||
|
Site: `default`
|
||||||
|
|
||||||
|
## Why this exists
|
||||||
|
|
||||||
|
This is a fresh, low-risk preflight snapshot taken after the earlier recon so tomorrow does not start from stale assumptions.
|
||||||
|
|
||||||
|
## Networks
|
||||||
|
|
||||||
|
- Internet 1 -> purpose=`wan` vlan=`untagged` subnet=`n/a`
|
||||||
|
- Internet 2 -> purpose=`wan` vlan=`untagged` subnet=`n/a`
|
||||||
|
- One-Click VPN -> purpose=`remote-user-vpn` vlan=`untagged` subnet=`192.168.2.1/24`
|
||||||
|
- Proton Chicago -> purpose=`vpn-client` vlan=`untagged` subnet=`10.2.0.2/32`
|
||||||
|
- Slate 7 -> purpose=`remote-user-vpn` vlan=`untagged` subnet=`192.168.3.1/24`
|
||||||
|
- Old IoT -> purpose=`corporate` vlan=`2` subnet=`192.168.1.1/24`
|
||||||
|
- Trusted -> purpose=`corporate` vlan=`51` subnet=`10.5.1.1/24`
|
||||||
|
- IoT -> purpose=`corporate` vlan=`510` subnet=`10.5.10.1/24`
|
||||||
|
- Camera -> purpose=`corporate` vlan=`520` subnet=`10.5.20.1/24`
|
||||||
|
- Guest -> purpose=`guest` vlan=`590` subnet=`10.5.90.1/24`
|
||||||
|
- Management -> purpose=`corporate` vlan=`untagged` subnet=`10.5.0.1/24`
|
||||||
|
|
||||||
|
## SSIDs currently enabled
|
||||||
|
|
||||||
|
- `CIA Via`
|
||||||
|
- `UNEF's Playhouse`
|
||||||
|
- `Whiskey Neat Fuck Ice`
|
||||||
|
- `Yer a Wifi Harry`
|
||||||
|
|
||||||
|
## Port profiles currently present
|
||||||
|
|
||||||
|
- `Camera`
|
||||||
|
- `IoT`
|
||||||
|
- `Management`
|
||||||
|
- `Old IoT`
|
||||||
|
- `Trusted`
|
||||||
|
|
||||||
|
Operator implication:
|
||||||
|
- `Servers` still does not exist as a live port profile/object visible in this pull.
|
||||||
|
- Tomorrow must not begin server moves until `Servers` is created/confirmed.
|
||||||
|
|
||||||
|
## UniFi device state
|
||||||
|
|
||||||
|
- `U6 LR` -> type=`uap` ip=`10.5.0.20` state=`0` version=`6.7.41.15623`
|
||||||
|
- `U7 Pro` -> type=`uap` ip=`10.5.0.21` state=`1` version=`8.5.21.18681`
|
||||||
|
- `UDM Pro - Old` -> type=`udm` ip=`69.166.182.157` state=`1` version=`5.1.11.33023`
|
||||||
|
- `USW Pro HD 24` -> type=`usw` ip=`10.5.0.135` state=`1` version=`7.4.1.16850`
|
||||||
|
- `USW-24-PoE` -> type=`usw` ip=`10.5.0.10` state=`1` version=`7.4.1.16850`
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- U6 LR still appears disconnected/offline in the current API view.
|
||||||
|
- The active wireless load is still effectively on the U7 Pro.
|
||||||
|
|
||||||
|
## Current client counts by network
|
||||||
|
|
||||||
|
- Camera: 1
|
||||||
|
- Management: 2
|
||||||
|
- Old IoT: 14
|
||||||
|
- Trusted: 14
|
||||||
|
|
||||||
|
## Critical named clients for tomorrow
|
||||||
|
|
||||||
|
- `PlausibleDeniability` -> Trusted -> `10.5.1.6`
|
||||||
|
- `Serenity` -> Trusted -> `10.5.1.5`
|
||||||
|
- `Nomad` -> Trusted -> `10.5.1.16`
|
||||||
|
- `Rocinante` -> Trusted -> `10.5.1.112`
|
||||||
|
- `FlyingDutchman` -> Trusted -> `10.5.1.111`
|
||||||
|
- `MyQ-29B` -> Old IoT -> `192.168.1.130`
|
||||||
|
- `Samsung-FamilyHub` -> Old IoT -> `192.168.1.149`
|
||||||
|
- `LG_Smart_Dryer2_open` -> Old IoT -> `192.168.1.186`
|
||||||
|
- `LG_Smart_Laundry2_open` -> Trusted -> `10.5.1.184`
|
||||||
|
- `Main-Floor` -> Old IoT -> `192.168.1.102`
|
||||||
|
- `Upstairs` -> Old IoT -> `192.168.1.131`
|
||||||
|
- `Google-Home-Mini` -> Old IoT -> `192.168.1.185`
|
||||||
|
- `front-doorbell` -> Camera -> `10.5.20.217`
|
||||||
|
|
||||||
|
## Management offenders still present
|
||||||
|
|
||||||
|
- `58:d6:1f:54:e5:6d` -> `10.5.0.123`
|
||||||
|
- `58:d6:1f:54:e5:af` -> `10.5.0.189`
|
||||||
|
|
||||||
|
These are still the two management-lane ESP-class offenders to identify or evict.
|
||||||
|
|
||||||
|
## Fresh anomalies vs the earlier planning assumptions
|
||||||
|
|
||||||
|
1. `LG_Smart_Laundry2_open` is currently on `Trusted` at `10.5.1.184`.
|
||||||
|
- This was not part of the original Old IoT-first migration set.
|
||||||
|
- Decide tomorrow whether it belongs on IoT or whether it is a separate harmless laundry app endpoint you intentionally trust.
|
||||||
|
|
||||||
|
2. `U6 LR` still shows state `0`.
|
||||||
|
- Treat it as optional only if needed, not as assumed active infrastructure.
|
||||||
|
|
||||||
|
3. The controller still shows zero custom firewall groups/rules/routing objects from the earlier read-only recon.
|
||||||
|
- Firewall staging is still basically greenfield.
|
||||||
|
|
||||||
|
## Safe prep work completed tonight
|
||||||
|
|
||||||
|
- verified UniFi read access still works from PD
|
||||||
|
- refreshed live device/client counts
|
||||||
|
- confirmed named host placement for the server batch and Old IoT migration set
|
||||||
|
- confirmed management offenders remain present
|
||||||
|
- confirmed U6 LR still appears offline/disconnected
|
||||||
|
- captured a raw pre-write baseline of `networkconf` and `portconf` at `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/baselines/unifi-object-baseline-2026-05-22-024653.json`
|
||||||
|
- authored an idempotent staging helper at `/home/fizzlepoof/repos/truenas-stacks/automation/bin/unifi_stage_low_risk_objects.py`
|
||||||
|
- copied that helper to PD at `/mnt/docker-ssd/docker/compose/automation/bin/unifi_stage_low_risk_objects.py`
|
||||||
|
- confirmed the current Doris UniFi account is still read-only for writes: `POST /rest/networkconf` returns `403 Forbidden`
|
||||||
|
|
||||||
|
## Recommended no-surprise start conditions for tomorrow
|
||||||
|
|
||||||
|
- create/confirm `Servers` object/profile before any server port move
|
||||||
|
- keep Rocinante on Trusted as the live control box until the Nomad/Serenity/PD wave is complete
|
||||||
|
- leave FlyingDutchman on Trusted
|
||||||
|
- treat Google as in-scope only through narrow reversible tests
|
||||||
|
- do not assume U6 LR is usable unless you explicitly verify it in the UI
|
||||||
138
home/doris-dashboard/docs/unifi-readonly-recon-2026-05-22.md
Normal file
138
home/doris-dashboard/docs/unifi-readonly-recon-2026-05-22.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# UniFi Read-Only Recon Notes
|
||||||
|
|
||||||
|
Date: 2026-05-22
|
||||||
|
Mode: read-only only
|
||||||
|
Auth status: verified working with upgraded Doris admin login
|
||||||
|
|
||||||
|
## Scope completed
|
||||||
|
- authenticated login verified
|
||||||
|
- network object inventory read
|
||||||
|
- SSID inventory read
|
||||||
|
- port profile inventory read
|
||||||
|
- client inventory read
|
||||||
|
- AP/client mapping read
|
||||||
|
- wired switch-port/client mapping read
|
||||||
|
- firewall/policy endpoint probes read
|
||||||
|
|
||||||
|
## High-value findings
|
||||||
|
|
||||||
|
### 1. Firewall model / current policy state
|
||||||
|
- `firewallrule`: 200, count 0
|
||||||
|
- `firewallgroup`: 200, count 0
|
||||||
|
- `firewallzone`: 400
|
||||||
|
- `trafficrule`: 400
|
||||||
|
- `routing`: 200, count 0
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- there are currently no custom classic firewall rules in the Network app dataset returned by the API
|
||||||
|
- no custom firewall groups are defined there either
|
||||||
|
- zone/traffic-rule endpoints are not active in the way this API helper expected
|
||||||
|
- practical implication: tomorrow’s segmentation policy work is largely greenfield, not cleanup of a big custom ruleset
|
||||||
|
|
||||||
|
### 2. Current network objects
|
||||||
|
- Management -> corporate -> untagged -> `10.5.0.1/24`
|
||||||
|
- Trusted -> corporate -> VLAN 51 -> `10.5.1.1/24`
|
||||||
|
- Old IoT -> corporate -> VLAN 2 -> `192.168.1.1/24`
|
||||||
|
- IoT -> corporate -> VLAN 510 -> `10.5.10.1/24`
|
||||||
|
- Camera -> corporate -> VLAN 520 -> `10.5.20.1/24`
|
||||||
|
- Guest -> guest -> VLAN 590 -> `10.5.90.1/24`
|
||||||
|
|
||||||
|
### 3. Current SSIDs
|
||||||
|
- `CIA Via` -> Old IoT
|
||||||
|
- `UNEF's Playhouse` -> Camera
|
||||||
|
- `Whiskey Neat Fuck Ice` -> Trusted
|
||||||
|
- `Yer a Wifi Harry` -> Trusted
|
||||||
|
|
||||||
|
### 4. Management offenders
|
||||||
|
Both Management-lane clients are Wi‑Fi clients on the U7 Pro and both are unnamed `espressif` devices on the default `UniFi Wireless` SSID.
|
||||||
|
|
||||||
|
- `espressif` -> `10.5.0.123` -> U7 Pro -> SSID `UniFi Wireless`
|
||||||
|
- `espressif` -> `10.5.0.189` -> U7 Pro -> SSID `UniFi Wireless`
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- these are almost certainly stray ESP-class smart devices and exactly the kind of junk that should not live on Management
|
||||||
|
- tomorrow we should either identify and move them, or quarantine them if they are brittle
|
||||||
|
|
||||||
|
### 5. Old IoT live inventory
|
||||||
|
All are currently on SSID `CIA Via` via the U7 Pro.
|
||||||
|
|
||||||
|
Likely migrate-first / easy-value:
|
||||||
|
- `MyQ-29B` -> `192.168.1.130`
|
||||||
|
- `Main-Floor` ecobee -> `192.168.1.102`
|
||||||
|
- `Upstairs` ecobee -> `192.168.1.131`
|
||||||
|
- `LG_Smart_Dryer2_open` -> `192.168.1.186`
|
||||||
|
- `Samsung-FamilyHub` -> `192.168.1.149`
|
||||||
|
|
||||||
|
Likely migrate-later / discovery pain:
|
||||||
|
- `Google-Home-Mini` -> `192.168.1.185`
|
||||||
|
- `3c:8d:20:f3:92:36` -> Google -> `192.168.1.192`
|
||||||
|
- `90:ca:fa:b6:7f:6e` -> Google -> `192.168.1.129`
|
||||||
|
|
||||||
|
Likely quarantine until identified better:
|
||||||
|
- `5c:61:99:41:73:40` -> Cloud Network Technology Singapore -> `192.168.1.172`
|
||||||
|
- `60:74:f4:54:fd:ec` -> Private -> `192.168.1.136`
|
||||||
|
- `60:74:f4:7b:6a:11` -> Private -> `192.168.1.117`
|
||||||
|
- `c0:f5:35:20:5d:94` -> AMPAK -> `192.168.1.183`
|
||||||
|
- `d4:ad:fc:60:90:6a` -> Shenzhen Intellirocks -> `192.168.1.100`
|
||||||
|
- `d4:ad:fc:ea:7f:65` -> Shenzhen Intellirocks -> `192.168.1.101`
|
||||||
|
|
||||||
|
### 6. Camera lane live state
|
||||||
|
- `front-doorbell` is already on Camera network at `10.5.20.217`
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- the doorbell is already where it belongs logically
|
||||||
|
- tomorrow’s security cleanup work is mainly about chimes and future policy tightening
|
||||||
|
|
||||||
|
### 7. Trusted lane still carrying infrastructure-class hosts
|
||||||
|
Trusted currently includes:
|
||||||
|
- `PlausibleDeniability` -> `10.5.1.6`
|
||||||
|
- `Serenity` -> `10.5.1.5`
|
||||||
|
- `Nomad` -> `10.5.1.16`
|
||||||
|
- `Rocinante` -> `10.5.1.112`
|
||||||
|
- `FlyingDutchman` -> `10.5.1.111`
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- tomorrow’s server-lane work is real and necessary
|
||||||
|
- these should not stay blended into the human client lane long-term
|
||||||
|
|
||||||
|
### 8. AP/client mapping
|
||||||
|
- `U7 Pro`: 26 clients
|
||||||
|
- `wired/unknown`: 5 clients
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- almost all current active wireless load is concentrated on the U7 Pro
|
||||||
|
- the U6 LR is presently not carrying client load
|
||||||
|
|
||||||
|
### 9. Wired client to switch-port mapping
|
||||||
|
On `USW Pro HD 24`:
|
||||||
|
- port 2 -> `Rocinante`
|
||||||
|
- port 3 -> `FlyingDutchman`
|
||||||
|
- port 22 -> `Nomad`
|
||||||
|
- port 23 -> `PlausibleDeniability`
|
||||||
|
- port 24 -> `Serenity`
|
||||||
|
|
||||||
|
### 10. Important switch-port assignments
|
||||||
|
On `USW Pro HD 24`:
|
||||||
|
- port 1 -> profile `Management` -> `U7 Pro (Upstairs)`
|
||||||
|
- port 22 -> profile `Trusted` -> `Nomad`
|
||||||
|
- port 23 -> profile `Trusted` -> `Docker Server` / PD
|
||||||
|
- port 24 -> profile `Trusted` -> `Serenity 10G (1)`
|
||||||
|
- port 27 -> profile `Management` -> up at 10G
|
||||||
|
- port 3 -> profile `Trusted` -> `FlyingDutchman`
|
||||||
|
- port 2 -> up at 2.5G with no explicit profile shown -> `Rocinante`
|
||||||
|
|
||||||
|
On `USW-24-PoE`:
|
||||||
|
- port 24 -> profile `Management` -> up at 1G
|
||||||
|
- many inactive preassigned ports exist for `IoT`, `Old IoT`, and `Management`
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
- the core server moves tomorrow are mechanically straightforward because their live ports are visible
|
||||||
|
- PD / Serenity / Nomad / FlyingDutchman are explicitly sitting on Trusted access profiles today
|
||||||
|
- Rocinante needs a quick extra look because the client is live on port 2 but that port did not show an explicit profile name in the returned record
|
||||||
|
|
||||||
|
## Recommended next actions for tomorrow
|
||||||
|
1. identify the two Management `espressif` devices before or during the window
|
||||||
|
2. move/changeprofile for server ports on the USW Pro HD 24 one at a time
|
||||||
|
3. treat Google devices as late-batch or defer
|
||||||
|
4. treat unknown Old IoT clients as quarantine by default, not migration by default
|
||||||
|
5. keep Camera cleanup focused on chimes and policy, because the doorbell is already in the correct lane
|
||||||
209
home/doris-dashboard/docs/usw-pro-hd-24-cutover-port-sheet.md
Normal file
209
home/doris-dashboard/docs/usw-pro-hd-24-cutover-port-sheet.md
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# USW Pro HD 24 Exact Change Sheet
|
||||||
|
|
||||||
|
> For Doris: this is the mechanical switch-port sheet for tomorrow. Do not freestyle. Change one thing, validate, then continue.
|
||||||
|
|
||||||
|
Source basis:
|
||||||
|
- live UniFi read-only recon on 2026-05-22
|
||||||
|
- /home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/unifi-readonly-recon-2026-05-22.md
|
||||||
|
- /home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard/docs/network-redesign-implementation-runbook.md
|
||||||
|
|
||||||
|
## Core principle
|
||||||
|
- Uplink/AP/infra ports stay Management or trunked as appropriate.
|
||||||
|
- Server-class hosts leave Trusted and move to Servers.
|
||||||
|
- Unknown live ports get eyeballed before change.
|
||||||
|
- Do not bulk-edit profiles blindly.
|
||||||
|
- Rocinante is the live operator box, so keep it stable until the main server batch is complete or an alternate admin path is proven.
|
||||||
|
- Nomad, Serenity, and PD may need to move as one controlled wave because of NFS/shared-service coupling; validate the batch immediately.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Desired profile set for tomorrow
|
||||||
|
|
||||||
|
Current relevant profiles seen live:
|
||||||
|
- Management
|
||||||
|
- Trusted
|
||||||
|
- IoT
|
||||||
|
- Camera
|
||||||
|
- Old IoT
|
||||||
|
|
||||||
|
Needed for tomorrow:
|
||||||
|
- Management
|
||||||
|
- Trusted
|
||||||
|
- Servers
|
||||||
|
- IoT
|
||||||
|
- Camera
|
||||||
|
- Guest
|
||||||
|
- Old IoT
|
||||||
|
- trunk/uplink profile if you choose to normalize uplinks now
|
||||||
|
|
||||||
|
If `Servers` does not exist yet:
|
||||||
|
- create it before changing server ports
|
||||||
|
- use it as the target for the core host ports below
|
||||||
|
|
||||||
|
## 2. USW Pro HD 24 live ports that matter
|
||||||
|
|
||||||
|
### Port 1
|
||||||
|
- Name: `U7 Pro (Upstairs)`
|
||||||
|
- Current profile: `Management`
|
||||||
|
- Link: up
|
||||||
|
- Speed: 2.5G
|
||||||
|
- Role: AP uplink / infrastructure
|
||||||
|
- Tomorrow action: leave on Management unless/until you replace with an explicit trunk/uplink profile
|
||||||
|
- Risk: high if changed incorrectly
|
||||||
|
- Validation after any change:
|
||||||
|
- U7 Pro remains online in UniFi
|
||||||
|
- wireless clients stay associated
|
||||||
|
- management plane remains reachable
|
||||||
|
|
||||||
|
### Port 2
|
||||||
|
- Name: `Port 2`
|
||||||
|
- Current observed live client: `Rocinante`
|
||||||
|
- Current explicit profile visibility: not clearly returned by API
|
||||||
|
- Link: up
|
||||||
|
- Speed: 2.5G
|
||||||
|
- Role: server/core host
|
||||||
|
- Tomorrow action: inspect in UniFi UI first, confirm current native network/profile, but keep Rocinante in place as the live operator station until the Nomad/Serenity/PD batch is done or a backup admin path is proven
|
||||||
|
- Risk: medium because profile visibility was ambiguous in API data
|
||||||
|
- Validation after move:
|
||||||
|
- Rocinante gets expected Servers subnet/gateway
|
||||||
|
- reachable from Trusted as intended
|
||||||
|
- any expected service ports work
|
||||||
|
|
||||||
|
### Port 3
|
||||||
|
- Name: `FlyingDutchman`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Link: up
|
||||||
|
- Speed: 2.5G
|
||||||
|
- Role: human endpoint / gaming PC unless proven otherwise
|
||||||
|
- Tomorrow action: leave on `Trusted` tomorrow unless you deliberately reclassify it later; default assumption is that it should stay with trusted human endpoints
|
||||||
|
- Risk: medium
|
||||||
|
- Validation after move:
|
||||||
|
- host gets expected subnet/gateway
|
||||||
|
- reachable from Trusted as intended
|
||||||
|
- only move if this box is actually supposed to live with the homelab service lane
|
||||||
|
|
||||||
|
### Port 22
|
||||||
|
- Name: `Port 22`
|
||||||
|
- Current live client: `Nomad`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Link: up
|
||||||
|
- Speed: 2.5G
|
||||||
|
- Role: core host
|
||||||
|
- Tomorrow action: move to `Servers`
|
||||||
|
- Risk: medium
|
||||||
|
- Validation after move:
|
||||||
|
- Nomad gets expected Servers address
|
||||||
|
- Doris pages / services expected on Nomad still reachable from Trusted
|
||||||
|
|
||||||
|
### Port 23
|
||||||
|
- Name: `Docker Server`
|
||||||
|
- Current live client: `PlausibleDeniability`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Link: up
|
||||||
|
- Speed: 10G
|
||||||
|
- Role: primary core server
|
||||||
|
- Tomorrow action: move to `Servers`
|
||||||
|
- Risk: high-ish because this is central infrastructure
|
||||||
|
- Validation after move:
|
||||||
|
- PD gets expected Servers address
|
||||||
|
- SSH to `pd` still works from Trusted/admin lane
|
||||||
|
- critical services still reachable from Trusted
|
||||||
|
- no surprise breakage to shared homelab functions
|
||||||
|
|
||||||
|
### Port 24
|
||||||
|
- Name: `Serenity 10G (1)`
|
||||||
|
- Current live client: `Serenity`
|
||||||
|
- Current profile: `Trusted`
|
||||||
|
- Link: up
|
||||||
|
- Speed: 10G
|
||||||
|
- Role: core host
|
||||||
|
- Tomorrow action: move to `Servers`
|
||||||
|
- Risk: medium-high
|
||||||
|
- Validation after move:
|
||||||
|
- Serenity gets expected Servers address
|
||||||
|
- intended services still reachable from Trusted
|
||||||
|
|
||||||
|
### Port 27
|
||||||
|
- Name: `Port 27`
|
||||||
|
- Current profile: `Management`
|
||||||
|
- Link: up
|
||||||
|
- Speed: 10G
|
||||||
|
- Role: likely infra/uplink
|
||||||
|
- Tomorrow action: do not touch unless you have positively identified what it is
|
||||||
|
- Risk: very high
|
||||||
|
- Validation if ever touched:
|
||||||
|
- upstream/downstream infra remains healthy
|
||||||
|
- no switch isolation or mgmt loss
|
||||||
|
|
||||||
|
## 3. Recommended exact move order for server ports
|
||||||
|
|
||||||
|
Use this order, not random order:
|
||||||
|
|
||||||
|
1. Port 2 -> Rocinante
|
||||||
|
- inspect/profile-confirm first
|
||||||
|
- do not move it early if it is your live admin path
|
||||||
|
|
||||||
|
2. Port 22 -> Nomad
|
||||||
|
- first member of the controlled three-host batch
|
||||||
|
|
||||||
|
3. Port 24 -> Serenity
|
||||||
|
- second member of the controlled three-host batch
|
||||||
|
|
||||||
|
4. Port 23 -> PlausibleDeniability
|
||||||
|
- third member of the controlled three-host batch
|
||||||
|
|
||||||
|
5. Port 3 -> FlyingDutchman
|
||||||
|
- default action is no move; only revisit if you intentionally decide it belongs in Servers after all
|
||||||
|
|
||||||
|
If you want the most conservative variant:
|
||||||
|
- leave Rocinante on Trusted during the live work
|
||||||
|
- move Nomad, Serenity, and PD as the only server-lane batch
|
||||||
|
- leave FlyingDutchman on Trusted
|
||||||
|
|
||||||
|
## 4. Per-port execution script
|
||||||
|
|
||||||
|
For each independent server port or server batch:
|
||||||
|
1. open switch port in UniFi UI
|
||||||
|
2. confirm live client identity matches expectation
|
||||||
|
3. screenshot current state
|
||||||
|
4. if touching Nomad/Serenity/PD, pre-stage the exact order before the first change
|
||||||
|
5. change profile from `Trusted` (or current) -> `Servers`
|
||||||
|
6. wait for link/client/IP reassignment
|
||||||
|
7. verify host gets expected subnet/gateway
|
||||||
|
8. verify reachability from Rocinante/admin path
|
||||||
|
9. verify expected service behavior
|
||||||
|
10. if it was the three-host batch, verify cross-host dependencies before anything else
|
||||||
|
11. only then proceed to next port
|
||||||
|
|
||||||
|
## 5. Ports to explicitly leave alone tomorrow
|
||||||
|
|
||||||
|
Leave alone unless a later issue forces action:
|
||||||
|
- Port 1 (U7 Pro upstairs)
|
||||||
|
- Port 27 (likely infra/uplink)
|
||||||
|
- all inactive ports
|
||||||
|
- any uplink/trunk-ish port not positively identified
|
||||||
|
|
||||||
|
## 6. Potential follow-up ports outside the main switch
|
||||||
|
|
||||||
|
USW-24-PoE notable live port:
|
||||||
|
- Port 24 -> profile `Management` -> link up at 1G
|
||||||
|
- Tomorrow action: read-only verify what it is before touching
|
||||||
|
- Do not change it casually during the same window unless you have confirmed its role
|
||||||
|
|
||||||
|
## 7. Success criteria for this port sheet
|
||||||
|
|
||||||
|
Successful tomorrow outcome:
|
||||||
|
- Nomad, Serenity, and PD are no longer sitting on Trusted access ports
|
||||||
|
- Rocinante is either still the live admin box by deliberate choice or moved cleanly after the main batch is complete
|
||||||
|
- FlyingDutchman stays on Trusted unless deliberately reclassified with reason
|
||||||
|
- AP/uplink/infra ports remain stable
|
||||||
|
- no server move proceeds without validation
|
||||||
|
|
||||||
|
## 8. Red flags
|
||||||
|
|
||||||
|
Stop immediately if:
|
||||||
|
- a moved host does not pull the expected subnet
|
||||||
|
- SSH/admin access path disappears unexpectedly
|
||||||
|
- a port identity in the UI does not match expected host
|
||||||
|
- changing one port breaks unrelated infra
|
||||||
|
- PD move causes unclear blast radius and you cannot explain it within a couple minutes
|
||||||
642
home/doris-dashboard/public/network-redesign.html
Normal file
642
home/doris-dashboard/public/network-redesign.html
Normal file
@@ -0,0 +1,642 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Doris Network Redesign Plan</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
color-scheme:dark;
|
||||||
|
--hf-red:#d53600;
|
||||||
|
--hf-red-deep:#910f3f;
|
||||||
|
--hf-amber:#f2c14e;
|
||||||
|
--hf-blue:#7b8fb2;
|
||||||
|
--bg:#090b10;
|
||||||
|
--bg-accent:#131824;
|
||||||
|
--panel:#11151d;
|
||||||
|
--panel-soft:#171d27;
|
||||||
|
--panel-raise:#1d2430;
|
||||||
|
--text:#f2efdd;
|
||||||
|
--muted:#cbbda7;
|
||||||
|
--line:rgba(242,239,221,.11);
|
||||||
|
--line-soft:rgba(242,239,221,.06);
|
||||||
|
--accent:var(--hf-red);
|
||||||
|
--warn:#ff7b72;
|
||||||
|
--ok:#82c29a;
|
||||||
|
--attention:#ffd166;
|
||||||
|
--shadow:0 20px 48px rgba(0,0,0,.34);
|
||||||
|
--radius:22px;
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
html{scroll-behavior:smooth}
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left,rgba(213,54,0,.22),transparent 24rem),
|
||||||
|
radial-gradient(circle at top right,rgba(123,143,178,.16),transparent 22rem),
|
||||||
|
linear-gradient(180deg,#07090d 0%,#0d1017 52%,#131824 100%);
|
||||||
|
color:var(--text);
|
||||||
|
}
|
||||||
|
a{color:inherit}
|
||||||
|
.incident-tape{height:14px;background:repeating-linear-gradient(135deg,var(--hf-amber) 0 18px,#111 18px 36px);box-shadow:0 3px 14px rgba(0,0,0,.32)}
|
||||||
|
main{max-width:1400px;margin:0 auto;padding:20px}
|
||||||
|
.hero,.panel,.vlan-card,.ssid-card,.phase-card,.matrix-card,.legend-card,.topology,.coverage-card{
|
||||||
|
background:rgba(17,21,29,.92);
|
||||||
|
border:1px solid var(--line);
|
||||||
|
box-shadow:var(--shadow);
|
||||||
|
border-radius:var(--radius);
|
||||||
|
}
|
||||||
|
.hero{
|
||||||
|
padding:22px;
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:1.4fr .9fr;
|
||||||
|
gap:16px;
|
||||||
|
position:relative;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
.hero:before{
|
||||||
|
content:"";
|
||||||
|
position:absolute;
|
||||||
|
inset:0;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 82% 18%, rgba(213,54,0,.18), transparent 18rem),
|
||||||
|
radial-gradient(circle at 14% 10%, rgba(123,143,178,.16), transparent 16rem),
|
||||||
|
repeating-linear-gradient(0deg, rgba(255,255,255,.018) 0 2px, transparent 2px 5px);
|
||||||
|
pointer-events:none;
|
||||||
|
opacity:.5;
|
||||||
|
}
|
||||||
|
.eyebrow{margin:0 0 8px;color:var(--hf-amber);text-transform:uppercase;letter-spacing:.18em;font-size:.74rem}
|
||||||
|
h1{margin:0;font-size:clamp(2.3rem,5vw,4.1rem);line-height:.95;letter-spacing:-.04em}
|
||||||
|
.sub{margin:12px 0 0;color:var(--muted);max-width:68ch;line-height:1.5}
|
||||||
|
.hero-actions,.filters,.quick-links{display:flex;flex-wrap:wrap;gap:10px}
|
||||||
|
.pill,button.filter,button.toggle{
|
||||||
|
border:1px solid rgba(242,193,78,.26);
|
||||||
|
background:rgba(242,193,78,.12);
|
||||||
|
color:#ffe9b3;
|
||||||
|
border-radius:999px;
|
||||||
|
padding:8px 12px;
|
||||||
|
font-size:.83rem;
|
||||||
|
}
|
||||||
|
button.filter,button.toggle{cursor:pointer}
|
||||||
|
button.filter.active,button.toggle.active{background:rgba(213,54,0,.2);border-color:rgba(213,54,0,.45);color:#fff1e8}
|
||||||
|
.hero-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:16px}
|
||||||
|
.hero-stat{background:rgba(15,18,25,.9);border:1px solid rgba(242,239,221,.08);border-radius:18px;padding:14px}
|
||||||
|
.hero-stat strong{display:block;font-size:1.35rem}
|
||||||
|
.hero-stat small{display:block;color:var(--muted);margin-top:4px;line-height:1.35}
|
||||||
|
.stack{display:grid;gap:16px;margin-top:16px}
|
||||||
|
.panel{padding:18px}
|
||||||
|
.panel h2{margin:0 0 12px;font-size:1.25rem}
|
||||||
|
.section-head{display:flex;justify-content:space-between;align-items:flex-end;gap:12px;flex-wrap:wrap}
|
||||||
|
.section-head p{margin:0;color:var(--muted)}
|
||||||
|
.topology{padding:18px}
|
||||||
|
.topology-grid{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:12px;margin-top:14px;align-items:stretch}
|
||||||
|
.node{background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(255,255,255,.015)),rgba(15,18,25,.96);border:1px solid rgba(242,239,221,.1);border-radius:18px;padding:14px;min-height:120px;position:relative}
|
||||||
|
.node small{display:block;color:var(--muted);margin-top:6px;line-height:1.35}
|
||||||
|
.node .tag{display:inline-block;margin-top:10px;padding:4px 8px;border-radius:999px;font-size:.72rem;border:1px solid var(--line)}
|
||||||
|
.node.current{outline:1px solid rgba(255,123,114,.3)}
|
||||||
|
.node.target{outline:1px solid rgba(130,194,154,.32)}
|
||||||
|
.arrow{display:flex;align-items:center;justify-content:center;color:var(--hf-amber);font-size:1.35rem}
|
||||||
|
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px}
|
||||||
|
.vlan-card,.ssid-card,.phase-card,.matrix-card,.legend-card,.coverage-card{padding:18px;position:relative}
|
||||||
|
.vlan-card h3,.ssid-card h3,.phase-card h3,.matrix-card h3,.coverage-card h3{margin:0 0 8px;font-size:1.1rem}
|
||||||
|
.meta{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px}
|
||||||
|
.chip{padding:5px 9px;border-radius:999px;font-size:.76rem;background:rgba(123,143,178,.15);border:1px solid rgba(123,143,178,.24)}
|
||||||
|
.chip.warn{background:rgba(255,123,114,.14);border-color:rgba(255,123,114,.28)}
|
||||||
|
.chip.ok{background:rgba(130,194,154,.15);border-color:rgba(130,194,154,.28)}
|
||||||
|
.chip.attention{background:rgba(242,193,78,.14);border-color:rgba(242,193,78,.28)}
|
||||||
|
ul{margin:10px 0 0 18px;padding:0}
|
||||||
|
li{margin:5px 0;color:#e4ddcf}
|
||||||
|
.muted{color:var(--muted)}
|
||||||
|
details{margin-top:12px;border-top:1px solid var(--line-soft);padding-top:10px}
|
||||||
|
summary{cursor:pointer;color:#ffe9b3}
|
||||||
|
.hidden-advanced .advanced{display:none !important}
|
||||||
|
.filter-hide{display:none !important}
|
||||||
|
.matrix{display:grid;grid-template-columns:1.2fr repeat(4,minmax(0,1fr));gap:1px;background:var(--line);border-radius:16px;overflow:hidden;margin-top:12px}
|
||||||
|
.cell{background:rgba(15,18,25,.96);padding:12px;font-size:.92rem;line-height:1.35}
|
||||||
|
.cell.head{background:rgba(123,143,178,.16);font-weight:700}
|
||||||
|
.coverage-layout{display:grid;grid-template-columns:1.1fr .9fr;gap:14px}
|
||||||
|
.floor-map{background:rgba(15,18,25,.96);border:1px solid var(--line-soft);border-radius:18px;padding:16px;min-height:280px;position:relative;overflow:hidden}
|
||||||
|
.floor{position:absolute;border:1px dashed rgba(242,239,221,.18);border-radius:18px;padding:10px;color:var(--muted)}
|
||||||
|
.floor.upstairs{left:20px;right:20px;top:20px;height:110px}
|
||||||
|
.floor.downstairs{left:20px;right:20px;bottom:20px;height:110px}
|
||||||
|
.ap{position:absolute;width:56px;height:56px;border-radius:50%;display:grid;place-items:center;font-weight:800;background:rgba(123,143,178,.22);border:1px solid rgba(123,143,178,.45);box-shadow:0 0 0 1px rgba(123,143,178,.1),0 0 45px rgba(123,143,178,.18)}
|
||||||
|
.ap.u7{top:48px;right:80px}
|
||||||
|
.ap.u6{bottom:52px;left:90px;background:rgba(242,193,78,.18);border-color:rgba(242,193,78,.45);box-shadow:0 0 0 1px rgba(242,193,78,.1),0 0 45px rgba(242,193,78,.18)}
|
||||||
|
.ap.off{opacity:.25;filter:grayscale(1)}
|
||||||
|
.coverage-zone{position:absolute;border-radius:999px;filter:blur(2px);opacity:.32}
|
||||||
|
.zone-u7{background:radial-gradient(circle, rgba(123,143,178,.82), transparent 70%);width:260px;height:180px;top:8px;right:8px}
|
||||||
|
.zone-u6{background:radial-gradient(circle, rgba(242,193,78,.82), transparent 70%);width:260px;height:180px;bottom:6px;left:8px}
|
||||||
|
.footer-note{margin-top:18px;color:var(--muted);font-size:.92rem}
|
||||||
|
.nav{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0 0}
|
||||||
|
.nav a{padding:9px 12px;border-radius:999px;background:rgba(255,255,255,.04);border:1px solid var(--line);text-decoration:none}
|
||||||
|
@media (max-width: 1080px){
|
||||||
|
.hero,.coverage-layout{grid-template-columns:1fr}
|
||||||
|
.topology-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
|
||||||
|
}
|
||||||
|
@media (max-width: 760px){
|
||||||
|
main{padding:14px}
|
||||||
|
.hero-grid,.cards,.topology-grid{grid-template-columns:1fr}
|
||||||
|
.matrix{grid-template-columns:1fr}
|
||||||
|
.arrow{display:none}
|
||||||
|
.floor-map{min-height:340px}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="hidden-advanced">
|
||||||
|
<div class="incident-tape"></div>
|
||||||
|
<main>
|
||||||
|
<section class="hero">
|
||||||
|
<div style="position:relative;z-index:1">
|
||||||
|
<p class="eyebrow">Operator evidence board</p>
|
||||||
|
<h1>Home Network Redesign</h1>
|
||||||
|
<p class="sub">This is the proposed future-state design for your house and homelab: six purposeful VLANs, a real camera/security lane now instead of later, cleaner SSIDs, stricter policy boundaries, and a staged migration path that avoids lighting the place on fire.</p>
|
||||||
|
<div class="nav">
|
||||||
|
<a href="#vlan-plan">VLAN plan</a>
|
||||||
|
<a href="#ssid-plan">SSID plan</a>
|
||||||
|
<a href="#firewall">Firewall</a>
|
||||||
|
<a href="#migration">Migration</a>
|
||||||
|
<a href="#coverage">Wi-Fi coverage</a>
|
||||||
|
</div>
|
||||||
|
<div class="hero-grid">
|
||||||
|
<div class="hero-stat">
|
||||||
|
<strong>6 target VLANs</strong>
|
||||||
|
<small>Management, Trusted, Servers, IoT, Guest, Cameras/Security. No more historical junk lanes.</small>
|
||||||
|
</div>
|
||||||
|
<div class="hero-stat">
|
||||||
|
<strong>1 legacy VLAN to kill</strong>
|
||||||
|
<small>Old IoT / VLAN 2 goes away after device migration and validation.</small>
|
||||||
|
</div>
|
||||||
|
<div class="hero-stat">
|
||||||
|
<strong>2 AP posture</strong>
|
||||||
|
<small>Keep U7 Pro as main AP. Reinstall the U6 LR downstairs if coverage or camera RSSI needs it.</small>
|
||||||
|
</div>
|
||||||
|
<div class="hero-stat">
|
||||||
|
<strong>Camera growth ready now</strong>
|
||||||
|
<small>Doorbell, chimes, and future camera gear live on a security lane from day one.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="position:relative;z-index:1" class="panel">
|
||||||
|
<h2>Interactive controls</h2>
|
||||||
|
<div class="stack" style="margin-top:0">
|
||||||
|
<div>
|
||||||
|
<div class="muted" style="margin-bottom:8px">Card filter</div>
|
||||||
|
<div class="filters">
|
||||||
|
<button class="filter active" data-filter="all">All</button>
|
||||||
|
<button class="filter" data-filter="network">Network</button>
|
||||||
|
<button class="filter" data-filter="wifi">Wi-Fi</button>
|
||||||
|
<button class="filter" data-filter="security">Security</button>
|
||||||
|
<button class="filter" data-filter="migration">Migration</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="muted" style="margin-bottom:8px">Detail level</div>
|
||||||
|
<div class="quick-links">
|
||||||
|
<button class="toggle" id="detail-toggle">Show advanced details</button>
|
||||||
|
<button class="toggle" id="u6-toggle">Enable downstairs U6 LR</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="muted" style="margin-bottom:8px">Operator calls</div>
|
||||||
|
<div class="hero-actions">
|
||||||
|
<span class="pill">Move chimes off Management</span>
|
||||||
|
<span class="pill">Retire VLAN 2</span>
|
||||||
|
<span class="pill">Split Servers from Trusted</span>
|
||||||
|
<span class="pill">Pre-stage Camera VLAN now</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="stack">
|
||||||
|
<section class="topology" id="topology">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2 style="margin:0">Current to target topology</h2>
|
||||||
|
<p>What exists now versus the lane structure I would put in place before you hand me admin access.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="topology-grid">
|
||||||
|
<div class="node current" data-tags="network migration">
|
||||||
|
<strong>Current Management</strong>
|
||||||
|
<small>UDM, switches, APs, plus two Protect chimes that do not belong there.</small>
|
||||||
|
<span class="tag">10.5.0.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="node target" data-tags="network security">
|
||||||
|
<strong>Mgmt VLAN 10</strong>
|
||||||
|
<small>Infra only: gateway, switches, APs, management IPs, admin surfaces.</small>
|
||||||
|
<span class="tag">10.5.10.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="node current" data-tags="network wifi">
|
||||||
|
<strong>Trusted + Servers mixed</strong>
|
||||||
|
<small>Main LAN currently holds user clients and service hosts together.</small>
|
||||||
|
<span class="tag">10.5.1.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="node target" data-tags="network">
|
||||||
|
<strong>Trusted VLAN 20</strong>
|
||||||
|
<small>Phones, desktops, laptops, Steam Deck, personal endpoints.</small>
|
||||||
|
<span class="tag">10.5.20.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="node target" data-tags="network">
|
||||||
|
<strong>Servers VLAN 30</strong>
|
||||||
|
<small>PD, Serenity, Nomad, Rocinante, and other service hosts.</small>
|
||||||
|
<span class="tag">10.5.30.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="node current" data-tags="network migration wifi">
|
||||||
|
<strong>Old IoT still real</strong>
|
||||||
|
<small>CIA Via on VLAN 2 is still carrying the meaningful smart-home traffic.</small>
|
||||||
|
<span class="tag">192.168.1.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="node target" data-tags="network migration wifi">
|
||||||
|
<strong>IoT VLAN 40</strong>
|
||||||
|
<small>All retained smart-home gear lands here after phased migration.</small>
|
||||||
|
<span class="tag">10.5.40.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="node current" data-tags="security network">
|
||||||
|
<strong>Camera lane underused</strong>
|
||||||
|
<small>Doorbell is there, but security gear is not consistently placed yet.</small>
|
||||||
|
<span class="tag">10.5.20.0/24 now / repurpose later</span>
|
||||||
|
</div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="node target" data-tags="security network wifi">
|
||||||
|
<strong>Camera VLAN 60</strong>
|
||||||
|
<small>Doorbell, chimes, future cameras, Protect accessories. Built now, not “someday.”</small>
|
||||||
|
<span class="tag">10.5.60.0/24</span>
|
||||||
|
</div>
|
||||||
|
<div class="node target" data-tags="network wifi">
|
||||||
|
<strong>Guest VLAN 50</strong>
|
||||||
|
<small>Pure internet-only guest lane with client isolation.</small>
|
||||||
|
<span class="tag">10.5.50.0/24</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel" id="vlan-plan">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>Target VLAN plan</h2>
|
||||||
|
<p>These cards are the final lane definitions I would work from as your network engineer.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards" id="vlan-cards"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel" id="ssid-plan">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>SSID plan</h2>
|
||||||
|
<p>Fewer SSIDs, clearer meaning, and explicit temporary lanes only where justified.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards" id="ssid-cards"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel" id="firewall">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>Firewall intent matrix</h2>
|
||||||
|
<p>Not every protocol spelled out yet, but the policy posture is explicit and sane.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="matrix" id="fw-matrix"></div>
|
||||||
|
<p class="footer-note advanced">Advanced note: discovery helpers like mDNS reflection, Chromecast flows, and specific Home Assistant / MQTT allowances should be explicit exceptions, never implied by a broad allow-any-to-any rule.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel" id="migration">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>Migration phases</h2>
|
||||||
|
<p>Ordered to reduce blast radius: infrastructure first, then servers, then security, then IoT cleanup.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards" id="phase-cards"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel" id="coverage">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>Wi-Fi coverage and AP posture</h2>
|
||||||
|
<p>The downstairs U6 LR is part of the plan if coverage or camera RSSI says it should be.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="coverage-layout">
|
||||||
|
<div class="coverage-card">
|
||||||
|
<h3>Coverage sketch</h3>
|
||||||
|
<div class="floor-map">
|
||||||
|
<div class="coverage-zone zone-u7"></div>
|
||||||
|
<div class="coverage-zone zone-u6" id="zone-u6"></div>
|
||||||
|
<div class="floor upstairs">Upstairs / main coverage zone / existing U7 Pro area</div>
|
||||||
|
<div class="floor downstairs">Downstairs / camera + IoT pressure zone / candidate U6 LR restore point</div>
|
||||||
|
<div class="ap u7">U7</div>
|
||||||
|
<div class="ap u6 off" id="u6-ap">U6</div>
|
||||||
|
</div>
|
||||||
|
<p class="footer-note">If downstairs coverage is already fine, the U6 can stay cold. If signal loss, roaming pain, or camera RSSI looks weak, install it with wired backhaul and let it carry downstairs 2.4/5 GHz load.</p>
|
||||||
|
</div>
|
||||||
|
<div class="coverage-card" data-tags="wifi security network">
|
||||||
|
<h3>AP guidance</h3>
|
||||||
|
<div class="meta">
|
||||||
|
<span class="chip ok">Preferred: wired backhaul</span>
|
||||||
|
<span class="chip attention">Delay RF micro-tuning</span>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Keep the U7 Pro as the main upstairs AP.</li>
|
||||||
|
<li>Reinstall the U6 LR downstairs if validation shows weak coverage or poor roaming.</li>
|
||||||
|
<li>Bias cameras toward wired PoE whenever practical; do not solve camera growth with “more Wi-Fi” by default.</li>
|
||||||
|
<li>Put both AP management interfaces on Management VLAN 10.</li>
|
||||||
|
</ul>
|
||||||
|
<details class="advanced">
|
||||||
|
<summary>Why not over-tune on day one?</summary>
|
||||||
|
<p class="muted">Because the real first win is correct segmentation and sane physical placement. Fancy channel math before the VLAN model is cleaned up is premature wizardry.</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>Decision summary</h2>
|
||||||
|
<p>The crisp operator answer.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cards">
|
||||||
|
<div class="legend-card" data-tags="network">
|
||||||
|
<h3>Best-fit final shape</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Management</li>
|
||||||
|
<li>Trusted</li>
|
||||||
|
<li>Servers</li>
|
||||||
|
<li>IoT</li>
|
||||||
|
<li>Guest</li>
|
||||||
|
<li>Cameras / Security</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="legend-card" data-tags="migration">
|
||||||
|
<h3>Things to delete</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Old IoT VLAN 2</li>
|
||||||
|
<li>Stale access-port archaeology</li>
|
||||||
|
<li>Non-infra devices on Management</li>
|
||||||
|
<li>Duplicate SSIDs without a real policy reason</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="legend-card" data-tags="security wifi">
|
||||||
|
<h3>Security posture</h3>
|
||||||
|
<ul>
|
||||||
|
<li>IoT and Cameras default-deny toward the rest of the LAN</li>
|
||||||
|
<li>Trusted is the operator lane</li>
|
||||||
|
<li>Servers expose only explicit services</li>
|
||||||
|
<li>Guest is internet-only and isolated</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const vlans = [
|
||||||
|
{
|
||||||
|
name: 'VLAN 10 · Infrastructure / Management',
|
||||||
|
subnet: '10.5.10.0/24',
|
||||||
|
tags: ['network','security'],
|
||||||
|
state: 'ok',
|
||||||
|
chips: ['Infra only','No client junk'],
|
||||||
|
summary: 'Gateway, switches, APs, management interfaces, and admin surfaces. This lane becomes sacred.',
|
||||||
|
bullets: [
|
||||||
|
'Move Protect chimes out of Management.',
|
||||||
|
'Only designated admin clients and tools may initiate into this VLAN.',
|
||||||
|
'Use this as the native management lane for network gear and APs.'
|
||||||
|
],
|
||||||
|
advanced: 'This is where I would park UDM, USW Pro HD 24, USW-24-PoE, U7 Pro, U6 LR, and any OOB or appliance management IPs.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VLAN 20 · Trusted Clients',
|
||||||
|
subnet: '10.5.20.0/24',
|
||||||
|
tags: ['network','wifi'],
|
||||||
|
state: 'ok',
|
||||||
|
chips: ['Phones + personal devices','Main user lane'],
|
||||||
|
summary: 'Your daily-driver devices live here: phones, laptops, desktops, tablets, Steam Deck, handhelds.',
|
||||||
|
bullets: [
|
||||||
|
'Primary trusted SSID maps here.',
|
||||||
|
'Trusted clients can administer servers and selected management surfaces.',
|
||||||
|
'Do not use this lane for “smart” junk just because it is convenient.'
|
||||||
|
],
|
||||||
|
advanced: 'Whiskey Neat Fuck Ice can temporarily remain as a compat/isolation flavor on the same VLAN if you still need it during cleanup.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VLAN 30 · Servers / Core Services',
|
||||||
|
subnet: '10.5.30.0/24',
|
||||||
|
tags: ['network'],
|
||||||
|
state: 'attention',
|
||||||
|
chips: ['Service hosts','Cleaner east-west policy'],
|
||||||
|
summary: 'Separate always-on service hosts from your person-devices so the firewall can speak clearly.',
|
||||||
|
bullets: [
|
||||||
|
'PD, Serenity, Nomad, Rocinante belong here.',
|
||||||
|
'Trusted can reach this lane broadly for admin and use.',
|
||||||
|
'IoT and Cameras get only tightly scoped access into published services.'
|
||||||
|
],
|
||||||
|
advanced: 'If FlyingDutchman is a real service host, it lands here too. Otherwise keep it on Trusted.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VLAN 40 · IoT / Smart Home',
|
||||||
|
subnet: '10.5.40.0/24',
|
||||||
|
tags: ['network','wifi','migration'],
|
||||||
|
state: 'attention',
|
||||||
|
chips: ['Replace Old IoT','Default deny'],
|
||||||
|
summary: 'This is the real home for thermostats, appliances, MyQ, Google Home-class devices, and retained smart-home junk.',
|
||||||
|
bullets: [
|
||||||
|
'Repoint CIA Via here during migration or create a temporary migration SSID.',
|
||||||
|
'Allow DNS, NTP, internet, and only explicit local service exceptions.',
|
||||||
|
'Treat Google/casting discovery as an exception set, not as a reason to flatten the network.'
|
||||||
|
],
|
||||||
|
advanced: 'This replaces Old IoT VLAN 2 entirely once migration is complete.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VLAN 50 · Guest',
|
||||||
|
subnet: '10.5.50.0/24',
|
||||||
|
tags: ['network','wifi'],
|
||||||
|
state: 'ok',
|
||||||
|
chips: ['Internet only','Client isolation'],
|
||||||
|
summary: 'Visitors and untrusted BYOD devices live here and get exactly zero reason to see the rest of the house.',
|
||||||
|
bullets: [
|
||||||
|
'Dedicated guest SSID only.',
|
||||||
|
'Enable client isolation.',
|
||||||
|
'No RFC1918 access back into the lab or house networks.'
|
||||||
|
],
|
||||||
|
advanced: 'Keep this boring. That is the point.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VLAN 60 · Cameras / Security',
|
||||||
|
subnet: '10.5.60.0/24',
|
||||||
|
tags: ['network','security','wifi'],
|
||||||
|
state: 'ok',
|
||||||
|
chips: ['Build now','Ready for camera growth'],
|
||||||
|
summary: 'Doorbell, Protect chimes, future cameras, and security accessories get their own lane now so you do not have to revisit the design later.',
|
||||||
|
bullets: [
|
||||||
|
'Move front-doorbell and both chimes here.',
|
||||||
|
'Prefer wired PoE cameras on this VLAN as the fleet grows.',
|
||||||
|
'Allow only Protect/NVR-related traffic, DNS, NTP, updates, and admin access from Trusted.'
|
||||||
|
],
|
||||||
|
advanced: 'If you add a real NVR or Protect host later, it can stay on Servers VLAN while publishing only the needed interfaces to Cameras/Security.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const ssids = [
|
||||||
|
{
|
||||||
|
name: 'Trusted SSID',
|
||||||
|
tags: ['wifi'],
|
||||||
|
chips: ['Maps to VLAN 20','Main client Wi-Fi'],
|
||||||
|
summary: 'Use one primary trusted SSID for normal day-to-day devices.',
|
||||||
|
bullets: ['Yer a Wifi Harry is the natural candidate to remain the main trusted SSID.', 'Keep 6 GHz here if the client mix supports it.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Trusted-Compat (temporary)',
|
||||||
|
tags: ['wifi','migration'],
|
||||||
|
chips: ['Optional','Retire later if possible'],
|
||||||
|
summary: 'Only keep a second trusted-flavored SSID if it solves a real compatibility or isolation problem.',
|
||||||
|
bullets: ['Whiskey Neat Fuck Ice can serve as a temporary compat/isolation lane.', 'If it is no longer solving a problem, kill it.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'IoT SSID',
|
||||||
|
tags: ['wifi','migration'],
|
||||||
|
chips: ['Maps to VLAN 40','2.4 GHz friendly'],
|
||||||
|
summary: 'This becomes the home for all retained smart-home Wi-Fi devices.',
|
||||||
|
bullets: ['CIA Via can be repointed here during migration to reduce user retraining.', 'Keep broad compatibility; this is not where we get fancy.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Security SSID',
|
||||||
|
tags: ['wifi','security'],
|
||||||
|
chips: ['Maps to VLAN 60','For doorbells/chimes/cameras'],
|
||||||
|
summary: 'A dedicated security Wi-Fi lane lets you separate Protect/security traffic from the general IoT pile.',
|
||||||
|
bullets: ['UNEF\'s Playhouse is the obvious candidate to keep for this purpose.', 'Use it for the doorbell, chimes, and any Wi-Fi cameras/security accessories.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Guest SSID',
|
||||||
|
tags: ['wifi'],
|
||||||
|
chips: ['Maps to VLAN 50','Client isolation'],
|
||||||
|
summary: 'One guest lane. Internet only. No drama.',
|
||||||
|
bullets: ['Keep it separate from your real networks.', 'No internal access exceptions unless there is a compelling reason.']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const phases = [
|
||||||
|
{
|
||||||
|
name: 'Phase 0 · Staging and rollback points',
|
||||||
|
tags: ['migration','network'],
|
||||||
|
chips: ['No cutover yet'],
|
||||||
|
summary: 'Create target VLANs, port profiles, and draft firewall rules before moving anything.',
|
||||||
|
bullets: ['Build trunk-uplink, access-management, access-trusted, access-servers, access-iot, access-guest, access-cameras profiles.', 'Document rollback path before each cutover.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phase 1 · Infrastructure hygiene',
|
||||||
|
tags: ['migration','network','security'],
|
||||||
|
chips: ['Protect mgmt purity'],
|
||||||
|
summary: 'Move all network gear and only network gear into the Management lane.',
|
||||||
|
bullets: ['UDM, switches, APs, management surfaces.', 'Get the Protect chimes out of Management.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phase 2 · Servers split from Trusted',
|
||||||
|
tags: ['migration','network'],
|
||||||
|
chips: ['Service boundary'],
|
||||||
|
summary: 'Move core service hosts into Servers VLAN 30 and validate name resolution and reachability.',
|
||||||
|
bullets: ['PD, Serenity, Nomad, Rocinante first.', 'Update static addressing, DNS, ACLs, and any service bindings.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phase 3 · Security lane now',
|
||||||
|
tags: ['migration','security','wifi'],
|
||||||
|
chips: ['Camera growth ready'],
|
||||||
|
summary: 'Move the doorbell and chimes into Cameras/Security before you expand the camera fleet.',
|
||||||
|
bullets: ['This builds the right landing zone now.', 'Future camera rollout becomes additive rather than redesign work.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phase 4 · Retire Old IoT',
|
||||||
|
tags: ['migration','wifi'],
|
||||||
|
chips: ['Most user-visible phase'],
|
||||||
|
summary: 'Batch-migrate retained smart-home devices off CIA Via / VLAN 2 and onto the new IoT lane.',
|
||||||
|
bullets: ['Easy cloud devices first.', 'Thermostats next.', 'Google/casting devices last because they are the most likely to be annoying.']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phase 5 · Clean SSIDs and stale rules',
|
||||||
|
tags: ['migration','wifi','network'],
|
||||||
|
chips: ['Final cleanup'],
|
||||||
|
summary: 'Disable the old SSID, delete VLAN 2, and remove historical special cases.',
|
||||||
|
bullets: ['Retire temporary compat SSIDs if not needed.', 'Delete stale firewall rules and stale port assignments.']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const fwRows = [
|
||||||
|
['Source / Target','Management','Servers','IoT','Cameras'],
|
||||||
|
['Trusted','Admin only','Allow','Selective allow','Allow for operators'],
|
||||||
|
['Servers','Rare and explicit','Internal service mesh only','Explicit services only','Protect / NVR flows only'],
|
||||||
|
['IoT','Deny','Explicit exceptions only','Same VLAN only','Deny'],
|
||||||
|
['Cameras','Deny','Protect / NVR only','Deny','Same VLAN only']
|
||||||
|
];
|
||||||
|
|
||||||
|
const stateClass = { ok:'ok', attention:'attention', warn:'warn' };
|
||||||
|
|
||||||
|
function cardTemplate(item, kind='vlan-card') {
|
||||||
|
return `
|
||||||
|
<article class="${kind}" data-tags="${item.tags.join(' ')}">
|
||||||
|
<h3>${item.name}</h3>
|
||||||
|
${item.subnet ? `<div class="meta"><span class="chip ${stateClass[item.state] || ''}">${item.subnet}</span>${item.chips.map(c=>`<span class="chip">${c}</span>`).join('')}</div>` : `<div class="meta">${item.chips.map(c=>`<span class="chip">${c}</span>`).join('')}</div>`}
|
||||||
|
<p class="muted">${item.summary}</p>
|
||||||
|
<ul>${item.bullets.map(b=>`<li>${b}</li>`).join('')}</ul>
|
||||||
|
${item.advanced ? `<details class="advanced"><summary>Advanced detail</summary><p class="muted">${item.advanced}</p></details>` : ''}
|
||||||
|
</article>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('vlan-cards').innerHTML = vlans.map(v => cardTemplate(v)).join('');
|
||||||
|
document.getElementById('ssid-cards').innerHTML = ssids.map(v => cardTemplate(v,'ssid-card')).join('');
|
||||||
|
document.getElementById('phase-cards').innerHTML = phases.map(v => cardTemplate(v,'phase-card')).join('');
|
||||||
|
document.getElementById('fw-matrix').innerHTML = fwRows.flatMap((row, rIdx) => row.map((cell, cIdx) => `<div class="cell ${(rIdx===0||cIdx===0)?'head':''}">${cell}</div>`)).join('');
|
||||||
|
|
||||||
|
const filters = document.querySelectorAll('.filter');
|
||||||
|
const filterable = document.querySelectorAll('[data-tags]');
|
||||||
|
filters.forEach(btn => btn.addEventListener('click', () => {
|
||||||
|
filters.forEach(b => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
const tag = btn.dataset.filter;
|
||||||
|
filterable.forEach(el => {
|
||||||
|
if (tag === 'all') el.classList.remove('filter-hide');
|
||||||
|
else el.classList.toggle('filter-hide', !(el.dataset.tags || '').split(' ').includes(tag));
|
||||||
|
});
|
||||||
|
localStorage.setItem('network-plan-filter', tag);
|
||||||
|
}));
|
||||||
|
|
||||||
|
const detailToggle = document.getElementById('detail-toggle');
|
||||||
|
detailToggle.addEventListener('click', () => {
|
||||||
|
document.body.classList.toggle('hidden-advanced');
|
||||||
|
const advancedVisible = !document.body.classList.contains('hidden-advanced');
|
||||||
|
detailToggle.textContent = advancedVisible ? 'Hide advanced details' : 'Show advanced details';
|
||||||
|
detailToggle.classList.toggle('active', advancedVisible);
|
||||||
|
localStorage.setItem('network-plan-advanced', advancedVisible ? '1' : '0');
|
||||||
|
});
|
||||||
|
|
||||||
|
const u6Toggle = document.getElementById('u6-toggle');
|
||||||
|
const u6Ap = document.getElementById('u6-ap');
|
||||||
|
const u6Zone = document.getElementById('zone-u6');
|
||||||
|
function setU6(on){
|
||||||
|
u6Ap.classList.toggle('off', !on);
|
||||||
|
u6Zone.style.display = on ? 'block' : 'none';
|
||||||
|
u6Toggle.textContent = on ? 'Disable downstairs U6 LR' : 'Enable downstairs U6 LR';
|
||||||
|
u6Toggle.classList.toggle('active', on);
|
||||||
|
localStorage.setItem('network-plan-u6', on ? '1' : '0');
|
||||||
|
}
|
||||||
|
u6Toggle.addEventListener('click', () => setU6(u6Ap.classList.contains('off')));
|
||||||
|
|
||||||
|
const savedFilter = localStorage.getItem('network-plan-filter');
|
||||||
|
if (savedFilter) {
|
||||||
|
const btn = [...filters].find(b => b.dataset.filter === savedFilter);
|
||||||
|
if (btn) btn.click();
|
||||||
|
}
|
||||||
|
if (localStorage.getItem('network-plan-advanced') === '1') detailToggle.click();
|
||||||
|
setU6(localStorage.getItem('network-plan-u6') === '1');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
641
home/doris-dashboard/public/network-web.html
Normal file
641
home/doris-dashboard/public/network-web.html
Normal file
@@ -0,0 +1,641 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Doris Network Web</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
color-scheme:dark;
|
||||||
|
--bg:#06080d;
|
||||||
|
--panel:#101620;
|
||||||
|
--panel-soft:#151d2a;
|
||||||
|
--line:rgba(239,233,219,.10);
|
||||||
|
--text:#f2efdd;
|
||||||
|
--muted:#c7bba8;
|
||||||
|
--trusted:#6ea8fe;
|
||||||
|
--mgmt:#f2c14e;
|
||||||
|
--servers:#e07a5f;
|
||||||
|
--iot:#7bd389;
|
||||||
|
--guest:#b392f0;
|
||||||
|
--camera:#ff7b72;
|
||||||
|
--legacy:#7f8796;
|
||||||
|
--topology:#7dd3fc;
|
||||||
|
--flow:#f87171;
|
||||||
|
--allow:#7bd389;
|
||||||
|
--deny:#ff7b72;
|
||||||
|
--migrate:#f2c14e;
|
||||||
|
--shadow:0 22px 52px rgba(0,0,0,.36);
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box}
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 10% 0%, rgba(242,193,78,.12), transparent 22rem),
|
||||||
|
radial-gradient(circle at 100% 10%, rgba(110,168,254,.10), transparent 24rem),
|
||||||
|
radial-gradient(circle at 60% 100%, rgba(224,122,95,.10), transparent 22rem),
|
||||||
|
linear-gradient(180deg,#06080d 0%,#0a1018 50%,#0d131d 100%);
|
||||||
|
color:var(--text);
|
||||||
|
min-height:100vh;
|
||||||
|
}
|
||||||
|
a{color:inherit}
|
||||||
|
.wrap{max-width:1680px;margin:0 auto;padding:20px}
|
||||||
|
.hero,.canvas-shell,.panel,.stage-card,.layer-card{
|
||||||
|
background:rgba(16,22,32,.92);
|
||||||
|
border:1px solid var(--line);
|
||||||
|
border-radius:24px;
|
||||||
|
box-shadow:var(--shadow);
|
||||||
|
}
|
||||||
|
.hero{
|
||||||
|
padding:22px;
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:1.2fr .8fr;
|
||||||
|
gap:18px;
|
||||||
|
margin-bottom:18px;
|
||||||
|
}
|
||||||
|
.eyebrow{margin:0 0 8px;color:var(--mgmt);text-transform:uppercase;letter-spacing:.18em;font-size:.74rem}
|
||||||
|
h1{margin:0;font-size:clamp(2.4rem,5vw,4.4rem);line-height:.93;letter-spacing:-.04em}
|
||||||
|
.sub{margin:12px 0 0;max-width:76ch;color:var(--muted);line-height:1.55}
|
||||||
|
.chips{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}
|
||||||
|
.chip{padding:7px 11px;border-radius:999px;border:1px solid var(--line);background:rgba(255,255,255,.04);font-size:.82rem}
|
||||||
|
.summary-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
|
||||||
|
.summary-card{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:18px;padding:14px}
|
||||||
|
.summary-card strong{display:block;font-size:1.05rem}
|
||||||
|
.summary-card small{display:block;margin-top:4px;color:var(--muted);line-height:1.35}
|
||||||
|
.layout{display:grid;grid-template-columns:minmax(0,1.6fr) 380px;gap:18px}
|
||||||
|
.canvas-shell{padding:14px;position:relative;overflow:hidden}
|
||||||
|
.toolbar,.subtoolbar{display:flex;flex-wrap:wrap;gap:10px}
|
||||||
|
.subtoolbar{margin-top:10px}
|
||||||
|
button{
|
||||||
|
appearance:none;border:1px solid var(--line);background:rgba(255,255,255,.04);color:var(--text);
|
||||||
|
padding:9px 12px;border-radius:999px;cursor:pointer;font:inherit;transition:background .18s ease,border-color .18s ease,transform .18s ease
|
||||||
|
}
|
||||||
|
button:hover{transform:translateY(-1px)}
|
||||||
|
button.active{background:rgba(242,193,78,.16);border-color:rgba(242,193,78,.35)}
|
||||||
|
.subtoolbar button.active[data-layer="topology"]{background:rgba(125,211,252,.16);border-color:rgba(125,211,252,.35)}
|
||||||
|
.subtoolbar button.active[data-layer="flow"]{background:rgba(248,113,113,.16);border-color:rgba(248,113,113,.35)}
|
||||||
|
.subtoolbar button.active[data-layer="migration"]{background:rgba(242,193,78,.16);border-color:rgba(242,193,78,.35)}
|
||||||
|
.panel{padding:16px}
|
||||||
|
.panel h2{margin:0 0 12px;font-size:1.15rem}
|
||||||
|
.panel p{margin:0;color:var(--muted);line-height:1.45}
|
||||||
|
.legend{display:grid;gap:10px;margin-top:14px}
|
||||||
|
.legend-row{display:grid;grid-template-columns:14px 1fr;gap:10px;align-items:center}
|
||||||
|
.swatch{width:14px;height:14px;border-radius:50%}
|
||||||
|
.stroke-swatch{width:14px;height:14px;border-radius:50%;background:transparent;border:2px solid currentColor}
|
||||||
|
.info h3{margin:0 0 6px;font-size:1.08rem}
|
||||||
|
.info .meta{display:flex;gap:8px;flex-wrap:wrap;margin:10px 0}
|
||||||
|
.info .meta span{padding:5px 8px;border-radius:999px;border:1px solid var(--line);font-size:.78rem;background:rgba(255,255,255,.04)}
|
||||||
|
.info ul{margin:10px 0 0 18px;padding:0}
|
||||||
|
.info li{margin:6px 0;color:#e6dece}
|
||||||
|
.caption{margin-top:10px;font-size:.88rem;color:var(--muted)}
|
||||||
|
svg{
|
||||||
|
display:block;width:100%;height:min(76vh,980px);border-radius:18px;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 50% 50%, rgba(123,211,137,.04), transparent 24rem),
|
||||||
|
linear-gradient(180deg, rgba(255,255,255,.015), rgba(255,255,255,.005));
|
||||||
|
}
|
||||||
|
.ring{fill:none;stroke:rgba(255,255,255,.05);stroke-dasharray:4 8}
|
||||||
|
.mode-fade{opacity:.18}
|
||||||
|
.link{stroke:rgba(255,255,255,.15);stroke-width:2.4;fill:none;transition:stroke .18s ease, opacity .18s ease, stroke-width .18s ease}
|
||||||
|
.link.cross{stroke-dasharray:8 10;opacity:.62}
|
||||||
|
.link.highlight{stroke:#fff2cf;stroke-width:3.8;opacity:1}
|
||||||
|
.link.dim{opacity:.10}
|
||||||
|
.node{cursor:pointer;transition:opacity .18s ease, transform .18s ease}
|
||||||
|
.node.dim{opacity:.14}
|
||||||
|
.node-core{stroke:rgba(255,255,255,.16);stroke-width:2}
|
||||||
|
.node-label{font-size:14px;font-weight:700;fill:var(--text);text-anchor:middle;pointer-events:none}
|
||||||
|
.node-sub{font-size:11px;fill:var(--muted);text-anchor:middle;pointer-events:none}
|
||||||
|
.node.active .node-core{stroke:#fff2cf;stroke-width:3}
|
||||||
|
.cluster-title{font-size:15px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;fill:rgba(242,239,221,.52)}
|
||||||
|
.layer-line{fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:.95;transition:opacity .18s ease}
|
||||||
|
.layer-line.dim{opacity:.12}
|
||||||
|
.layer-line.topology{stroke:var(--topology);stroke-width:4;stroke-dasharray:2 10}
|
||||||
|
.layer-line.flow{stroke:var(--flow);stroke-width:4.2;marker-end:url(#arrow-flow)}
|
||||||
|
.layer-line.flow.allow{stroke:var(--allow)}
|
||||||
|
.layer-line.flow.restrict{stroke:var(--migrate);stroke-dasharray:12 10;marker-end:url(#arrow-migrate)}
|
||||||
|
.layer-line.flow.deny{stroke:var(--deny);stroke-dasharray:5 10;marker-end:url(#arrow-deny)}
|
||||||
|
.layer-line.migration{stroke:var(--migrate);stroke-width:4;stroke-dasharray:12 12;marker-end:url(#arrow-migrate)}
|
||||||
|
.layer-label{font-size:11px;fill:#f4ead7;font-weight:700;letter-spacing:.03em;paint-order:stroke;stroke:#0b1017;stroke-width:4px}
|
||||||
|
.layer-label.dim{opacity:.12}
|
||||||
|
.stage-track{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:14px}
|
||||||
|
.stage-card,.layer-card{padding:12px 14px;background:rgba(255,255,255,.03);border-radius:18px}
|
||||||
|
.stage-card.active{border-color:rgba(242,193,78,.35);background:rgba(242,193,78,.08)}
|
||||||
|
.stage-card h3,.layer-card h3,.note h3{margin:0 0 8px;font-size:1rem}
|
||||||
|
.stage-card p,.layer-card p,.note li{color:#e6dece}
|
||||||
|
.stage-card p,.layer-card p{margin:0;font-size:.92rem;line-height:1.45}
|
||||||
|
.layer-grid,.note-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:18px}
|
||||||
|
.note{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:18px;padding:14px}
|
||||||
|
.note ul{margin:0 0 0 18px;padding:0}
|
||||||
|
.stage-badge{display:inline-flex;align-items:center;gap:6px;font-size:.78rem;padding:4px 8px;border-radius:999px;border:1px solid rgba(255,255,255,.08);margin-bottom:8px}
|
||||||
|
.stage-badge::before{content:'';display:block;width:8px;height:8px;border-radius:50%;background:var(--migrate)}
|
||||||
|
.small{font-size:.82rem;color:var(--muted)}
|
||||||
|
@media (max-width:1200px){
|
||||||
|
.hero,.layout{grid-template-columns:1fr}
|
||||||
|
}
|
||||||
|
@media (max-width:820px){
|
||||||
|
.wrap{padding:14px}
|
||||||
|
.summary-grid,.note-grid,.layer-grid,.stage-track{grid-template-columns:1fr}
|
||||||
|
svg{height:72vh}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<section class="hero">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Operator map / graphical redesign artifact</p>
|
||||||
|
<h1>Network Web v2</h1>
|
||||||
|
<p class="sub">Now it is the actual operator map instead of just a conceptual graph. Same core redesign, but with three extra layers: physical/topology intent, firewall-flow reasoning, and staged migration sequencing. Click nodes or switch modes to inspect where the pain is and what the end state should look like.</p>
|
||||||
|
<div class="chips">
|
||||||
|
<span class="chip">6 target VLANs</span>
|
||||||
|
<span class="chip">Topology overlay</span>
|
||||||
|
<span class="chip">Firewall flow layer</span>
|
||||||
|
<span class="chip">4-stage migration path</span>
|
||||||
|
<span class="chip">Optional downstairs U6 LR</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-grid">
|
||||||
|
<div class="summary-card"><strong>Topology layer</strong><small>Shows the cabling/placement story: operator, gateway, switch fabric, APs, and where endpoints really hang.</small></div>
|
||||||
|
<div class="summary-card"><strong>Firewall layer</strong><small>Shows allow, deny, and exception paths so policy intent is visible instead of implied.</small></div>
|
||||||
|
<div class="summary-card"><strong>Migration layer</strong><small>Shows what moves first, what is annoying, and what can wait until the end.</small></div>
|
||||||
|
<div class="summary-card"><strong>Operator map</strong><small>Use this to reason about design, staging, and “what breaks if I move this” before touching UniFi.</small></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="layout">
|
||||||
|
<div class="canvas-shell">
|
||||||
|
<div class="toolbar">
|
||||||
|
<button class="active" data-mode="all">Show full web</button>
|
||||||
|
<button data-mode="vlan">VLAN cores only</button>
|
||||||
|
<button data-mode="migration">Migration pain</button>
|
||||||
|
<button data-mode="wifi">Wi-Fi / coverage</button>
|
||||||
|
<button data-mode="topology">Topology view</button>
|
||||||
|
<button data-mode="policy">Firewall policy</button>
|
||||||
|
<button id="u6Toggle">Toggle downstairs U6 LR</button>
|
||||||
|
</div>
|
||||||
|
<div class="subtoolbar">
|
||||||
|
<button class="active" data-layer="topology">Topology overlay</button>
|
||||||
|
<button class="active" data-layer="flow">Firewall flows</button>
|
||||||
|
<button class="active" data-layer="migration">Migration arrows</button>
|
||||||
|
<button data-stage="0" class="active">All stages</button>
|
||||||
|
<button data-stage="1">Stage 1</button>
|
||||||
|
<button data-stage="2">Stage 2</button>
|
||||||
|
<button data-stage="3">Stage 3</button>
|
||||||
|
<button data-stage="4">Stage 4</button>
|
||||||
|
</div>
|
||||||
|
<svg id="networkSvg" viewBox="0 0 1280 960" aria-label="Home network redesign graph">
|
||||||
|
<defs>
|
||||||
|
<marker id="arrow-flow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto" markerUnits="userSpaceOnUse">
|
||||||
|
<path d="M0,0 L12,6 L0,12 z" fill="var(--flow)"></path>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-deny" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto" markerUnits="userSpaceOnUse">
|
||||||
|
<path d="M0,0 L12,6 L0,12 z" fill="var(--deny)"></path>
|
||||||
|
</marker>
|
||||||
|
<marker id="arrow-migrate" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto" markerUnits="userSpaceOnUse">
|
||||||
|
<path d="M0,0 L12,6 L0,12 z" fill="var(--migrate)"></path>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<circle class="ring" cx="640" cy="470" r="125"></circle>
|
||||||
|
<circle class="ring" cx="640" cy="470" r="270"></circle>
|
||||||
|
<circle class="ring" cx="640" cy="470" r="415"></circle>
|
||||||
|
<text class="cluster-title" x="640" y="125" text-anchor="middle">Operator / routing core</text>
|
||||||
|
<text class="cluster-title" x="640" y="245" text-anchor="middle">Target VLANs and policy lanes</text>
|
||||||
|
<text class="cluster-title" x="640" y="900" text-anchor="middle">Endpoints, legacy scar tissue, migration pressure, and future growth</text>
|
||||||
|
<g id="baseLinks"></g>
|
||||||
|
<g id="layerLines"></g>
|
||||||
|
<g id="layerLabels"></g>
|
||||||
|
<g id="nodes"></g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="stage-track" id="stageTrack">
|
||||||
|
<div class="stage-card" data-stage-card="1">
|
||||||
|
<div class="stage-badge">Stage 1</div>
|
||||||
|
<h3>Stop obvious sins</h3>
|
||||||
|
<p>Move Protect chimes out of Management, stand up Camera VLAN 60, and make Management clean again.</p>
|
||||||
|
</div>
|
||||||
|
<div class="stage-card" data-stage-card="2">
|
||||||
|
<div class="stage-badge">Stage 2</div>
|
||||||
|
<h3>Split infrastructure from life</h3>
|
||||||
|
<p>Move PD, Serenity, Nomad, and Rocinante into Servers VLAN 30 so trusted clients stop sharing a lane with infrastructure.</p>
|
||||||
|
</div>
|
||||||
|
<div class="stage-card" data-stage-card="3">
|
||||||
|
<div class="stage-badge">Stage 3</div>
|
||||||
|
<h3>Drain old IoT</h3>
|
||||||
|
<p>Migrate ecobees, appliances, and low-drama devices first. Leave Google discovery weirdness for last inside IoT.</p>
|
||||||
|
</div>
|
||||||
|
<div class="stage-card" data-stage-card="4">
|
||||||
|
<div class="stage-badge">Stage 4</div>
|
||||||
|
<h3>Refine radio and policy</h3>
|
||||||
|
<p>Decide whether the downstairs U6 LR returns, clean up SSIDs, then tune firewall exceptions with evidence instead of superstition.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layer-grid">
|
||||||
|
<div class="layer-card">
|
||||||
|
<h3>Topology interpretation</h3>
|
||||||
|
<p>The UDM is routing brains, the USW is the practical center of gravity, and the APs are infrastructure nodes carrying policy-bound SSIDs. That means client traffic categories should be separated by VLAN intent, not by whatever happened to be easiest last year.</p>
|
||||||
|
</div>
|
||||||
|
<div class="layer-card">
|
||||||
|
<h3>Firewall interpretation</h3>
|
||||||
|
<p>Trusted gets explicit admin reach into Management and Servers. IoT and Cameras only get DNS/NTP/internet plus tightly scoped local-service exceptions. Guest gets nothing but internet. Old IoT gets a one-way ticket to deletion.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note-grid">
|
||||||
|
<div class="note">
|
||||||
|
<h3>What this graph is saying now</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Management is not a junk drawer.</li>
|
||||||
|
<li>Servers are infrastructure and deserve their own lane.</li>
|
||||||
|
<li>Cameras/security is a first-class lane because future growth is already obvious.</li>
|
||||||
|
<li>Migration difficulty lives mostly in discovery-heavy IoT, not in the easy appliances.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="note">
|
||||||
|
<h3>What changes first</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Move chimes out of Management.</li>
|
||||||
|
<li>Create Camera VLAN 60 now.</li>
|
||||||
|
<li>Pull core servers out of Trusted.</li>
|
||||||
|
<li>Drain and kill Old IoT / VLAN 2.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="panel info">
|
||||||
|
<h2>Selected node</h2>
|
||||||
|
<div id="infoBox">
|
||||||
|
<h3>Whole design</h3>
|
||||||
|
<p>Click a node in the graph. I will show what it is, what it should talk to, and where it sits in the migration sequence.</p>
|
||||||
|
<div class="meta">
|
||||||
|
<span>Tip: click Mgmt</span>
|
||||||
|
<span>Tip: click Cameras</span>
|
||||||
|
<span>Tip: click Old IoT</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--mgmt)"></span><span>Management / infrastructure</span></div>
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--trusted)"></span><span>Trusted clients / operator lane</span></div>
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--servers)"></span><span>Servers / core services</span></div>
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--iot)"></span><span>IoT / smart home</span></div>
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--guest)"></span><span>Guest</span></div>
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--camera)"></span><span>Cameras / security</span></div>
|
||||||
|
<div class="legend-row"><span class="swatch" style="background:var(--legacy)"></span><span>Legacy / migration pain</span></div>
|
||||||
|
<div class="legend-row" style="color:var(--topology)"><span class="stroke-swatch"></span><span>Topology / physical intent overlay</span></div>
|
||||||
|
<div class="legend-row" style="color:var(--allow)"><span class="stroke-swatch"></span><span>Allowed policy path</span></div>
|
||||||
|
<div class="legend-row" style="color:var(--deny)"><span class="stroke-swatch"></span><span>Denied / blocked path</span></div>
|
||||||
|
<div class="legend-row" style="color:var(--migrate)"><span class="stroke-swatch"></span><span>Migration or restricted exception path</span></div>
|
||||||
|
</div>
|
||||||
|
<p class="caption">This is no longer just a decision picture. It is an operator map for topology, policy, and staged cutover thinking.</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const palette = {
|
||||||
|
mgmt: getComputedStyle(document.documentElement).getPropertyValue('--mgmt').trim(),
|
||||||
|
trusted: getComputedStyle(document.documentElement).getPropertyValue('--trusted').trim(),
|
||||||
|
servers: getComputedStyle(document.documentElement).getPropertyValue('--servers').trim(),
|
||||||
|
iot: getComputedStyle(document.documentElement).getPropertyValue('--iot').trim(),
|
||||||
|
guest: getComputedStyle(document.documentElement).getPropertyValue('--guest').trim(),
|
||||||
|
camera: getComputedStyle(document.documentElement).getPropertyValue('--camera').trim(),
|
||||||
|
legacy: getComputedStyle(document.documentElement).getPropertyValue('--legacy').trim(),
|
||||||
|
core: '#f2efdd'
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodes = [
|
||||||
|
{id:'operator', label:'John / Doris', sub:'operator lane', x:640, y:470, r:32, color:palette.core, group:'core', tags:['all'], stage:0,
|
||||||
|
info:{title:'Operator / control point', meta:['Human admin','Design center'], text:'The network is supposed to serve an operator, not confuse one. Trusted becomes the homeowner lane and the place from which management and service access are explicitly granted.', bullets:['Trusted becomes the human/operator lane.','Admin access is explicit, not accidental.']}}
|
||||||
|
,{id:'udm', label:'UDM Pro', sub:'gateway / routing', x:640, y:320, r:24, color:palette.mgmt, group:'core', tags:['all','vlan','topology','policy'], stage:0,
|
||||||
|
info:{title:'UDM Pro', meta:['Gateway','Routing core'], text:'Routing brain. Terminates VLANs and enforces policy. Management plane belongs in VLAN 10, not mixed into random endpoint space.', bullets:['Core of routing and firewall policy.','Upstream of every lane.']}}
|
||||||
|
,{id:'prohd', label:'USW Pro HD 24', sub:'main switch fabric', x:790, y:425, r:22, color:palette.mgmt, group:'core', tags:['all','vlan','topology'], stage:0,
|
||||||
|
info:{title:'USW Pro HD 24', meta:['Main access/core switch'], text:'This is the physical gravity well of the live network. Trunks to APs and servers should reflect deliberate lane design.', bullets:['Carries trunks to all major lanes.','Physical center of gravity.']}}
|
||||||
|
,{id:'u7', label:'U7 Pro', sub:'main upstairs AP', x:760, y:605, r:20, color:palette.mgmt, group:'core', tags:['all','wifi','topology'], stage:4,
|
||||||
|
info:{title:'U7 Pro', meta:['Primary AP','Infrastructure'], text:'The U7 stays as the primary AP. Infrastructure device in Management, serving SSIDs that map to real policy lanes.', bullets:['Broadcasts Trusted, IoT, Guest, Security SSIDs.','Management interface remains isolated.']}}
|
||||||
|
,{id:'u6', label:'U6 LR', sub:'downstairs / optional restore', x:500, y:610, r:18, color:palette.mgmt, group:'core optional-u6', tags:['all','wifi','topology'], hidden:true, stage:4,
|
||||||
|
info:{title:'U6 LR', meta:['Optional second AP','Coverage lever'], text:'Add back if coverage, roaming, or future camera/security RSSI actually needs it. Wired backhaul preferred.', bullets:['Treat this as a design lever, not a guess.','Verify with real coverage pain, not vibes.']}}
|
||||||
|
,{id:'mgmt', label:'Mgmt VLAN 10', sub:'10.5.10.0/24', x:640, y:195, r:28, color:palette.mgmt, group:'vlan', tags:['all','vlan','policy'], stage:1,
|
||||||
|
info:{title:'Management VLAN 10', meta:['Infra only','No junk'], text:'Gateway, switches, APs, management interfaces, and admin surfaces. This lane should be sacred and boring.', bullets:['No smart-home accessories.','No chimes.','Only explicit admin initiators reach here.']}}
|
||||||
|
,{id:'trusted', label:'Trusted VLAN 20', sub:'10.5.20.0/24', x:890, y:355, r:28, color:palette.trusted, group:'vlan', tags:['all','vlan','wifi','policy'], stage:0,
|
||||||
|
info:{title:'Trusted VLAN 20', meta:['Operator lane','Main user devices'], text:'Phones, laptops, desktops, tablets, Steam Deck, and daily-driver endpoints. This is where the humans live.', bullets:['Can administer Management and Servers with explicit rules.','Not a dumping ground for semi-smart junk.']}}
|
||||||
|
,{id:'servers', label:'Servers VLAN 30', sub:'10.5.30.0/24', x:890, y:585, r:28, color:palette.servers, group:'vlan', tags:['all','vlan','policy'], stage:2,
|
||||||
|
info:{title:'Servers VLAN 30', meta:['Core services','Homelab spine'], text:'PD, Serenity, Nomad, Rocinante, and other real service hosts belong here so policy is clean and intent is obvious.', bullets:['Split from Trusted for cleaner firewalling.','Expose only required services to other lanes.']}}
|
||||||
|
,{id:'iot', label:'IoT VLAN 40', sub:'10.5.40.0/24', x:640, y:745, r:28, color:palette.iot, group:'vlan', tags:['all','vlan','migration','wifi','policy'], stage:3,
|
||||||
|
info:{title:'IoT VLAN 40', meta:['Smart home','Default deny'], text:'Real landing zone for thermostats, appliances, Google devices, and retained smart-home junk. Explicit local-service exceptions only.', bullets:['Replaces Old IoT.','Allow DNS/NTP/internet plus specific helpers only.']}}
|
||||||
|
,{id:'guest', label:'Guest VLAN 50', sub:'10.5.50.0/24', x:390, y:585, r:26, color:palette.guest, group:'vlan', tags:['all','vlan','wifi','policy'], stage:0,
|
||||||
|
info:{title:'Guest VLAN 50', meta:['Internet only','Client isolation'], text:'Visitors and random untrusted devices get internet and nothing else. Boring is correct here.', bullets:['Pure guest SSID.','No internal access.']}}
|
||||||
|
,{id:'camera', label:'Camera VLAN 60', sub:'10.5.60.0/24', x:390, y:355, r:28, color:palette.camera, group:'vlan', tags:['all','vlan','migration','security','wifi','policy'], stage:1,
|
||||||
|
info:{title:'Camera / Security VLAN 60', meta:['Build now','Future growth'], text:'Doorbell, Protect chimes, future cameras, and other security devices get a first-class lane now instead of being bolted on later.', bullets:['Prefer wired PoE cameras as you grow.','Allow only NVR/Protect/admin flows.']}}
|
||||||
|
,{id:'pd', label:'PD', sub:'core server', x:1090, y:635, r:18, color:palette.servers, group:'device', tags:['all','vlan','topology'], stage:2,
|
||||||
|
info:{title:'PlausibleDeniability', meta:['Server VLAN resident'], text:'Infrastructure host. Belongs in Servers VLAN 30, not mixed into the human client lane.', bullets:['Keep service exposure explicit.','Infrastructure, not a casual endpoint.']}}
|
||||||
|
,{id:'serenity', label:'Serenity', sub:'core server', x:985, y:740, r:17, color:palette.servers, group:'device', tags:['all','vlan','topology'], stage:2,
|
||||||
|
info:{title:'Serenity', meta:['Server VLAN resident'], text:'Lives with the rest of the homelab spine.', bullets:['Cleaner trust boundary.']}}
|
||||||
|
,{id:'nomad', label:'Nomad', sub:'core server', x:855, y:790, r:17, color:palette.servers, group:'device', tags:['all','vlan','topology'], stage:2,
|
||||||
|
info:{title:'Nomad', meta:['Server VLAN resident'], text:'Part of the real service lane, not the personal device lane.', bullets:['Reachable from Trusted with explicit policy.']}}
|
||||||
|
,{id:'rocinante', label:'Rocinante', sub:'core host', x:1145, y:530, r:17, color:palette.servers, group:'device', tags:['all','vlan','topology'], stage:2,
|
||||||
|
info:{title:'Rocinante', meta:['Server/core host'], text:'Another device that reads as infrastructure rather than a casual client.', bullets:['Belongs in Servers unless proven otherwise.']}}
|
||||||
|
,{id:'phones', label:'Phones / laptops', sub:'trusted clients', x:1060, y:280, r:19, color:palette.trusted, group:'device', tags:['all','wifi','topology'], stage:0,
|
||||||
|
info:{title:'Trusted client devices', meta:['Trusted VLAN'], text:'Your daily-driver human devices stay on Trusted.', bullets:['Admin from here.','User experience from here.']}}
|
||||||
|
,{id:'steamdeck', label:'Steam Deck / handhelds', sub:'trusted clients', x:975, y:185, r:16, color:palette.trusted, group:'device', tags:['all','wifi'], stage:0,
|
||||||
|
info:{title:'Trusted handhelds', meta:['Trusted VLAN'], text:'Still trusted, still yours, still not IoT.', bullets:['Keep on Trusted unless an edge-case really demands otherwise.']}}
|
||||||
|
,{id:'ecobee', label:'ecobees', sub:'thermostats', x:620, y:870, r:17, color:palette.iot, group:'device', tags:['all','migration'], stage:3,
|
||||||
|
info:{title:'ecobees', meta:['IoT lane'], text:'Meaningful smart devices that belong in IoT, not in leftover legacy space.', bullets:['Validate app control after migration.']}}
|
||||||
|
,{id:'google', label:'Google Home / cast', sub:'voice + discovery pain', x:455, y:840, r:18, color:palette.iot, group:'device', tags:['all','migration','wifi'], stage:3,
|
||||||
|
info:{title:'Google ecosystem devices', meta:['IoT lane','Discovery exceptions'], text:'These are the annoying part of migration because discovery/casting can want special treatment.', bullets:['Likely the last IoT batch.','Use helpers/exceptions, not trust flattening.']}}
|
||||||
|
,{id:'appliances', label:'Appliances / MyQ', sub:'fridge dryer garage', x:760, y:845, r:17, color:palette.iot, group:'device', tags:['all','migration'], stage:3,
|
||||||
|
info:{title:'Appliances and garage', meta:['IoT lane'], text:'Easy low-drama migration targets. Keep them but do not trust them.', bullets:['Good early migration batch.']}}
|
||||||
|
,{id:'doorbell', label:'Doorbell', sub:'security endpoint', x:225, y:305, r:17, color:palette.camera, group:'device', tags:['all','security','wifi'], stage:1,
|
||||||
|
info:{title:'Doorbell', meta:['Camera VLAN'], text:'Security-class device. Belongs with the security lane and future camera footprint.', bullets:['Separate from generic IoT where possible.']}}
|
||||||
|
,{id:'chimes', label:'Protect chimes', sub:'currently misplaced', x:190, y:405, r:17, color:palette.legacy, group:'legacy', tags:['all','migration','security'], stage:1,
|
||||||
|
info:{title:'Protect chimes', meta:['Migration pain','Move now'], text:'Poster children for why Management should not be a junk drawer. Move these into Camera VLAN 60 first.', bullets:['One of the first obvious cleanups.']}}
|
||||||
|
,{id:'futurecams', label:'Future cameras', sub:'PoE preferred', x:260, y:500, r:18, color:palette.camera, group:'device', tags:['all','security','topology'], stage:1,
|
||||||
|
info:{title:'Future cameras', meta:['Growth justification'], text:'You already know more cameras are coming. That makes VLAN 60 real, not theoretical.', bullets:['Prefer wired PoE when practical.','This is why the security lane exists now.']}}
|
||||||
|
,{id:'guests', label:'Visitors', sub:'internet only', x:255, y:640, r:16, color:palette.guest, group:'device', tags:['all','wifi'], stage:0,
|
||||||
|
info:{title:'Guest devices', meta:['Guest VLAN'], text:'Boring by design. That is healthy.', bullets:['No lateral access.']}}
|
||||||
|
,{id:'oldiot', label:'Old IoT / VLAN 2', sub:'kill this', x:345, y:780, r:20, color:palette.legacy, group:'legacy', tags:['all','migration'], stage:3,
|
||||||
|
info:{title:'Old IoT / VLAN 2', meta:['Legacy pain','Retire completely'], text:'Historical scar tissue. Drain it in phases, then delete it and its stale profiles for good.', bullets:['Drain it in phases.','Delete once empty.']}}
|
||||||
|
,{id:'ssids', label:'SSID cleanup', sub:'fewer, clearer names', x:990, y:95, r:18, color:palette.trusted, group:'legacy', tags:['all','wifi','migration'], stage:4,
|
||||||
|
info:{title:'SSID cleanup', meta:['Wi-Fi policy cleanup'], text:'Long-term goal: one Trusted, one IoT, one Guest, one Security. Add more only if they solve a real problem.', bullets:['Either collapse duplicate trusted SSIDs or rename them to reflect real policy.']}}
|
||||||
|
];
|
||||||
|
|
||||||
|
const baseLinks = [
|
||||||
|
['operator','trusted'],['operator','mgmt'],['operator','servers'],
|
||||||
|
['udm','mgmt'],['udm','trusted'],['udm','servers'],['udm','iot'],['udm','guest'],['udm','camera'],
|
||||||
|
['prohd','udm'],['u7','prohd'],['u6','prohd'],
|
||||||
|
['mgmt','prohd'],['mgmt','u7'],['mgmt','u6'],
|
||||||
|
['trusted','phones'],['trusted','steamdeck'],['trusted','ssids'],
|
||||||
|
['servers','pd'],['servers','serenity'],['servers','nomad'],['servers','rocinante'],
|
||||||
|
['iot','ecobee'],['iot','google'],['iot','appliances'],['iot','oldiot','cross'],
|
||||||
|
['camera','doorbell'],['camera','futurecams'],['camera','chimes','cross'],
|
||||||
|
['guest','guests'],
|
||||||
|
['mgmt','chimes','cross'],
|
||||||
|
['trusted','servers','cross'],
|
||||||
|
['u7','trusted','cross'],['u7','iot','cross'],['u7','guest','cross'],['u7','camera','cross'],
|
||||||
|
['u6','trusted','cross optional-u6'],['u6','iot','cross optional-u6'],['u6','camera','cross optional-u6']
|
||||||
|
];
|
||||||
|
|
||||||
|
const overlays = [
|
||||||
|
{id:'topo-1', type:'topology', a:'operator', b:'udm', text:'admin path'},
|
||||||
|
{id:'topo-2', type:'topology', a:'udm', b:'prohd', text:'main trunk'},
|
||||||
|
{id:'topo-3', type:'topology', a:'prohd', b:'u7', text:'AP uplink'},
|
||||||
|
{id:'topo-4', type:'topology', a:'prohd', b:'u6', text:'optional AP uplink', optional:true},
|
||||||
|
{id:'topo-5', type:'topology', a:'prohd', b:'pd', text:'server uplink'},
|
||||||
|
{id:'topo-6', type:'topology', a:'prohd', b:'rocinante', text:'server uplink'},
|
||||||
|
{id:'flow-1', type:'flow allow', a:'trusted', b:'mgmt', text:'admin allowed'},
|
||||||
|
{id:'flow-2', type:'flow allow', a:'trusted', b:'servers', text:'service/admin allowed'},
|
||||||
|
{id:'flow-3', type:'flow allow', a:'camera', b:'servers', text:'Protect / NVR only'},
|
||||||
|
{id:'flow-4', type:'flow allow', a:'iot', b:'servers', text:'specific local helpers'},
|
||||||
|
{id:'flow-5', type:'flow deny', a:'guest', b:'mgmt', text:'blocked'},
|
||||||
|
{id:'flow-6', type:'flow deny', a:'guest', b:'servers', text:'blocked'},
|
||||||
|
{id:'flow-7', type:'flow deny', a:'iot', b:'mgmt', text:'blocked'},
|
||||||
|
{id:'flow-8', type:'flow restrict', a:'google', b:'phones', text:'discovery exception pain'},
|
||||||
|
{id:'mig-1', type:'migration', a:'chimes', b:'camera', text:'stage 1', stage:1},
|
||||||
|
{id:'mig-2', type:'migration', a:'pd', b:'servers', text:'stage 2', stage:2},
|
||||||
|
{id:'mig-3', type:'migration', a:'serenity', b:'servers', text:'stage 2', stage:2},
|
||||||
|
{id:'mig-4', type:'migration', a:'nomad', b:'servers', text:'stage 2', stage:2},
|
||||||
|
{id:'mig-5', type:'migration', a:'rocinante', b:'servers', text:'stage 2', stage:2},
|
||||||
|
{id:'mig-6', type:'migration', a:'oldiot', b:'iot', text:'stage 3', stage:3},
|
||||||
|
{id:'mig-7', type:'migration', a:'ecobee', b:'iot', text:'stage 3', stage:3},
|
||||||
|
{id:'mig-8', type:'migration', a:'appliances', b:'iot', text:'stage 3', stage:3},
|
||||||
|
{id:'mig-9', type:'migration', a:'google', b:'iot', text:'stage 3 last', stage:3},
|
||||||
|
{id:'mig-10', type:'migration', a:'ssids', b:'u7', text:'stage 4', stage:4},
|
||||||
|
{id:'mig-11', type:'migration', a:'ssids', b:'u6', text:'stage 4 option', stage:4, optional:true}
|
||||||
|
];
|
||||||
|
|
||||||
|
const svgNodes = document.getElementById('nodes');
|
||||||
|
const svgBaseLinks = document.getElementById('baseLinks');
|
||||||
|
const layerLines = document.getElementById('layerLines');
|
||||||
|
const layerLabels = document.getElementById('layerLabels');
|
||||||
|
const infoBox = document.getElementById('infoBox');
|
||||||
|
const stageCards = [...document.querySelectorAll('[data-stage-card]')];
|
||||||
|
const nodeMap = Object.fromEntries(nodes.map(n => [n.id,n]));
|
||||||
|
let selected = null;
|
||||||
|
let mode = 'all';
|
||||||
|
let u6Enabled = false;
|
||||||
|
let activeStage = 0;
|
||||||
|
const layerState = {topology:true, flow:true, migration:true};
|
||||||
|
|
||||||
|
function modeVisible(node){
|
||||||
|
if (node.hidden && !u6Enabled) return false;
|
||||||
|
if (mode === 'all') return true;
|
||||||
|
if (mode === 'vlan') return node.group === 'vlan' || node.group === 'core';
|
||||||
|
if (mode === 'migration') return node.tags.includes('migration') || node.group === 'legacy' || node.id === 'operator' || node.id === 'camera' || node.id === 'servers' || node.id === 'iot';
|
||||||
|
if (mode === 'wifi') return node.tags.includes('wifi') || node.id === 'operator';
|
||||||
|
if (mode === 'topology') return node.tags.includes('topology') || node.group === 'core' || node.group === 'vlan' || node.id === 'operator';
|
||||||
|
if (mode === 'policy') return node.tags.includes('policy') || ['phones','google'].includes(node.id) || node.id === 'operator';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stageVisible(item){
|
||||||
|
return activeStage === 0 || !item.stage || item.stage === activeStage;
|
||||||
|
}
|
||||||
|
|
||||||
|
function midpoint(a,b,dx=0,dy=0){
|
||||||
|
return {x:(a.x+b.x)/2 + dx, y:(a.y+b.y)/2 + dy};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNode(n){
|
||||||
|
const g = document.createElementNS('http://www.w3.org/2000/svg','g');
|
||||||
|
g.setAttribute('class','node');
|
||||||
|
g.dataset.id = n.id;
|
||||||
|
g.innerHTML = `
|
||||||
|
<circle class="node-core" cx="${n.x}" cy="${n.y}" r="${n.r}" fill="${n.color}"></circle>
|
||||||
|
<text class="node-label" x="${n.x}" y="${n.y-4}">${n.label}</text>
|
||||||
|
<text class="node-sub" x="${n.x}" y="${n.y+14}">${n.sub}</text>
|
||||||
|
`;
|
||||||
|
g.addEventListener('click', () => selectNode(n.id));
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBaseLink(link){
|
||||||
|
const [a,b,klass=''] = link;
|
||||||
|
const na = nodeMap[a], nb = nodeMap[b];
|
||||||
|
const line = document.createElementNS('http://www.w3.org/2000/svg','line');
|
||||||
|
line.setAttribute('x1', na.x); line.setAttribute('y1', na.y);
|
||||||
|
line.setAttribute('x2', nb.x); line.setAttribute('y2', nb.y);
|
||||||
|
line.setAttribute('class', `link ${klass}`.trim());
|
||||||
|
line.dataset.a = a; line.dataset.b = b;
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOverlay(overlay){
|
||||||
|
const a = nodeMap[overlay.a], b = nodeMap[overlay.b];
|
||||||
|
const line = document.createElementNS('http://www.w3.org/2000/svg','line');
|
||||||
|
line.setAttribute('x1', a.x); line.setAttribute('y1', a.y);
|
||||||
|
line.setAttribute('x2', b.x); line.setAttribute('y2', b.y);
|
||||||
|
line.setAttribute('class', `layer-line ${overlay.type}`);
|
||||||
|
line.dataset.a = overlay.a; line.dataset.b = overlay.b; line.dataset.overlay = overlay.id;
|
||||||
|
|
||||||
|
const label = document.createElementNS('http://www.w3.org/2000/svg','text');
|
||||||
|
const bump = overlay.type.includes('migration') ? {x:0,y:-12} : overlay.type.includes('flow') ? {x:0,y:16} : {x:0,y:-16};
|
||||||
|
const mid = midpoint(a,b,bump.x,bump.y);
|
||||||
|
label.setAttribute('x', mid.x);
|
||||||
|
label.setAttribute('y', mid.y);
|
||||||
|
label.setAttribute('text-anchor','middle');
|
||||||
|
label.setAttribute('class','layer-label');
|
||||||
|
label.dataset.a = overlay.a; label.dataset.b = overlay.b; label.dataset.overlay = overlay.id;
|
||||||
|
label.textContent = overlay.text;
|
||||||
|
return {line,label};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeEls = new Map();
|
||||||
|
const baseLinkEls = [];
|
||||||
|
const overlayEls = [];
|
||||||
|
nodes.forEach(n => { const el = createNode(n); nodeEls.set(n.id, el); svgNodes.appendChild(el); });
|
||||||
|
baseLinks.forEach(l => { const el = createBaseLink(l); baseLinkEls.push(el); svgBaseLinks.appendChild(el); });
|
||||||
|
overlays.forEach(o => {
|
||||||
|
const pair = createOverlay(o);
|
||||||
|
overlayEls.push({...pair, meta:o});
|
||||||
|
layerLines.appendChild(pair.line);
|
||||||
|
layerLabels.appendChild(pair.label);
|
||||||
|
});
|
||||||
|
|
||||||
|
function isRelatedToSelected(a,b){
|
||||||
|
return selected && (a === selected || b === selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderInfo(n){
|
||||||
|
if (!n) {
|
||||||
|
infoBox.innerHTML = `<h3>Whole design</h3><p>Click a node in the graph. I will show what it is, what it should talk to, and where it sits in the migration sequence.</p><div class="meta"><span>Tip: click Mgmt</span><span>Tip: click Cameras</span><span>Tip: click Old IoT</span></div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const stageText = n.stage ? `Stage ${n.stage}` : 'Always relevant';
|
||||||
|
infoBox.innerHTML = `
|
||||||
|
<h3>${n.info.title}</h3>
|
||||||
|
<p>${n.info.text}</p>
|
||||||
|
<div class="meta">${n.info.meta.map(m => `<span>${m}</span>`).join('')}<span>${stageText}</span></div>
|
||||||
|
<ul>${n.info.bullets.map(b => `<li>${b}</li>`).join('')}</ul>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStageCards(){
|
||||||
|
stageCards.forEach(card => {
|
||||||
|
const stage = Number(card.dataset.stageCard);
|
||||||
|
card.classList.toggle('active', activeStage === 0 || activeStage === stage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGraph(){
|
||||||
|
nodeEls.forEach((el,id) => {
|
||||||
|
const n = nodeMap[id];
|
||||||
|
const visible = modeVisible(n) && stageVisible(n);
|
||||||
|
el.style.display = visible ? 'block' : 'none';
|
||||||
|
el.classList.toggle('active', selected === id);
|
||||||
|
const related = selected && (id === selected || baseLinks.some(([a,b]) => (a===selected && b===id) || (b===selected && a===id)) || overlays.some(o => (o.a===selected && o.b===id) || (o.b===selected && o.a===id)));
|
||||||
|
el.classList.toggle('dim', !!selected && !related);
|
||||||
|
});
|
||||||
|
|
||||||
|
baseLinkEls.forEach(el => {
|
||||||
|
const a = el.dataset.a, b = el.dataset.b;
|
||||||
|
const optional = el.getAttribute('class').includes('optional-u6');
|
||||||
|
const visible = modeVisible(nodeMap[a]) && modeVisible(nodeMap[b]) && stageVisible(nodeMap[a]) && stageVisible(nodeMap[b]) && (!optional || u6Enabled);
|
||||||
|
el.style.display = visible ? 'block' : 'none';
|
||||||
|
const related = isRelatedToSelected(a,b);
|
||||||
|
el.classList.toggle('highlight', !!selected && related);
|
||||||
|
el.classList.toggle('dim', !!selected && !related);
|
||||||
|
});
|
||||||
|
|
||||||
|
overlayEls.forEach(({line,label,meta}) => {
|
||||||
|
const a = nodeMap[meta.a], b = nodeMap[meta.b];
|
||||||
|
const overlayType = meta.type.split(' ')[0];
|
||||||
|
const layerOn = overlayType === 'topology' ? layerState.topology : overlayType === 'flow' ? layerState.flow : layerState.migration;
|
||||||
|
const optional = !!meta.optional;
|
||||||
|
const modeMatch = mode === 'topology' ? overlayType === 'topology' || overlayType === 'migration' : mode === 'policy' ? overlayType === 'flow' || overlayType === 'migration' : true;
|
||||||
|
const visible = layerOn && modeMatch && modeVisible(a) && modeVisible(b) && stageVisible(meta) && stageVisible(a) && stageVisible(b) && (!optional || u6Enabled);
|
||||||
|
line.style.display = visible ? 'block' : 'none';
|
||||||
|
label.style.display = visible ? 'block' : 'none';
|
||||||
|
const related = isRelatedToSelected(meta.a, meta.b);
|
||||||
|
line.classList.toggle('dim', !!selected && !related);
|
||||||
|
label.classList.toggle('dim', !!selected && !related);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selected && (!modeVisible(nodeMap[selected]) || !stageVisible(nodeMap[selected]) || (nodeMap[selected].hidden && !u6Enabled))) {
|
||||||
|
selected = null;
|
||||||
|
renderInfo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStageCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectNode(id){
|
||||||
|
selected = selected === id ? null : id;
|
||||||
|
renderInfo(selected ? nodeMap[selected] : null);
|
||||||
|
updateGraph();
|
||||||
|
localStorage.setItem('network-web-selected', selected || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-mode]').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
document.querySelectorAll('[data-mode]').forEach(b => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
mode = btn.dataset.mode;
|
||||||
|
localStorage.setItem('network-web-mode', mode);
|
||||||
|
updateGraph();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-layer]').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
const layer = btn.dataset.layer;
|
||||||
|
layerState[layer] = !layerState[layer];
|
||||||
|
btn.classList.toggle('active', layerState[layer]);
|
||||||
|
localStorage.setItem(`network-web-layer-${layer}`, layerState[layer] ? '1' : '0');
|
||||||
|
updateGraph();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-stage]').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
document.querySelectorAll('[data-stage]').forEach(b => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
activeStage = Number(btn.dataset.stage);
|
||||||
|
localStorage.setItem('network-web-stage', String(activeStage));
|
||||||
|
updateGraph();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('u6Toggle').addEventListener('click', (e) => {
|
||||||
|
u6Enabled = !u6Enabled;
|
||||||
|
e.currentTarget.classList.toggle('active', u6Enabled);
|
||||||
|
e.currentTarget.textContent = u6Enabled ? 'Hide downstairs U6 LR' : 'Toggle downstairs U6 LR';
|
||||||
|
localStorage.setItem('network-web-u6', u6Enabled ? '1' : '0');
|
||||||
|
updateGraph();
|
||||||
|
});
|
||||||
|
|
||||||
|
const savedMode = localStorage.getItem('network-web-mode');
|
||||||
|
if (savedMode) {
|
||||||
|
const btn = [...document.querySelectorAll('[data-mode]')].find(b => b.dataset.mode === savedMode);
|
||||||
|
if (btn) btn.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
['topology','flow','migration'].forEach(layer => {
|
||||||
|
const saved = localStorage.getItem(`network-web-layer-${layer}`);
|
||||||
|
if (saved === '0') {
|
||||||
|
layerState[layer] = false;
|
||||||
|
const btn = document.querySelector(`[data-layer="${layer}"]`);
|
||||||
|
if (btn) btn.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const savedStage = localStorage.getItem('network-web-stage');
|
||||||
|
if (savedStage) {
|
||||||
|
const btn = [...document.querySelectorAll('[data-stage]')].find(b => b.dataset.stage === savedStage);
|
||||||
|
if (btn) btn.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
u6Enabled = localStorage.getItem('network-web-u6') === '1';
|
||||||
|
if (u6Enabled) {
|
||||||
|
const btn = document.getElementById('u6Toggle');
|
||||||
|
btn.classList.add('active');
|
||||||
|
btn.textContent = 'Hide downstairs U6 LR';
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedSelected = localStorage.getItem('network-web-selected');
|
||||||
|
if (savedSelected && nodeMap[savedSelected]) selected = savedSelected;
|
||||||
|
renderInfo(selected ? nodeMap[selected] : null);
|
||||||
|
updateGraph();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<strong>Doris Constabulary</strong>
|
<strong>Doris Constabulary</strong>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-links" aria-label="Doris Kitchen navigation">
|
<nav class="nav-links" aria-label="Doris Kitchen navigation">
|
||||||
<a class="nav-link" href="http://10.5.1.16:8787/">Dashboard</a>
|
<a class="nav-link" href="http://10.5.30.7:8787/">Dashboard</a>
|
||||||
<a class="nav-link" href="/">Queue</a>
|
<a class="nav-link" href="/">Queue</a>
|
||||||
<a class="nav-link" href="/search">Search</a>
|
<a class="nav-link" href="/search">Search</a>
|
||||||
<a class="nav-link" href="/planner">Planner</a>
|
<a class="nav-link" href="/planner">Planner</a>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ NOMAD does **not** currently have PD's Docker-only external networks (`pangolin`
|
|||||||
For NOMAD, Schoolhouse should:
|
For NOMAD, Schoolhouse should:
|
||||||
- publish `8091` on the host
|
- publish `8091` on the host
|
||||||
- let Pangolin/Newt target the host port externally
|
- let Pangolin/Newt target the host port externally
|
||||||
- reach shared Postgres on PD over LAN TCP (`10.5.1.6:5432`)
|
- reach shared Postgres on PD over LAN TCP (`10.5.30.6:5432`)
|
||||||
- reach LiteLLM / Qdrant / Paperless / n8n over their existing LAN/public endpoints
|
- reach LiteLLM / Qdrant / Paperless / n8n over their existing LAN/public endpoints
|
||||||
|
|
||||||
## Pre-deploy checklist
|
## Pre-deploy checklist
|
||||||
@@ -80,7 +80,7 @@ Current live helper:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Behavior:
|
Behavior:
|
||||||
- mounts Serenity NFS export `10.5.1.5:/mnt/user/data` at `/mnt/serenity-data` when needed
|
- mounts Serenity NFS export `10.5.30.5:/mnt/user/data` at `/mnt/serenity-data` when needed
|
||||||
- mirrors `/opt/doris-schoolhouse/data/` into `/mnt/serenity-data/backups/nomad/doris-schoolhouse/data/`
|
- mirrors `/opt/doris-schoolhouse/data/` into `/mnt/serenity-data/backups/nomad/doris-schoolhouse/data/`
|
||||||
- writes `last_success_utc.txt` and `source_host.txt` markers beside the mirrored data
|
- writes `last_success_utc.txt` and `source_host.txt` markers beside the mirrored data
|
||||||
|
|
||||||
|
|||||||
@@ -229,8 +229,8 @@ That flow is the reference for refreshing the live runtime from repo source.
|
|||||||
Current verified live behavior:
|
Current verified live behavior:
|
||||||
- `https://n8n.paccoco.com/webhook/school/intake/upload` accepts Doris Schoolhouse multipart assignment uploads and returned a successful live smoke-test response on 2026-05-15.
|
- `https://n8n.paccoco.com/webhook/school/intake/upload` accepts Doris Schoolhouse multipart assignment uploads and returned a successful live smoke-test response on 2026-05-15.
|
||||||
- `https://n8n.paccoco.com/webhook/class/upload` now succeeds in live smoke testing after upstream workflow and LiteLLM fixes on 2026-05-15.
|
- `https://n8n.paccoco.com/webhook/class/upload` now succeeds in live smoke testing after upstream workflow and LiteLLM fixes on 2026-05-15.
|
||||||
- Direct Whisper testing against `http://10.5.1.16:8786/v1/audio/transcriptions` succeeded with a tiny MP3.
|
- Direct Whisper testing against `http://10.5.30.7:8786/v1/audio/transcriptions` succeeded with a tiny MP3.
|
||||||
- Direct LiteLLM testing against `http://10.5.1.6:4000/v1/chat/completions` now succeeds for model `medium` after the PD route fix.
|
- Direct LiteLLM testing against `http://10.5.30.6:4000/v1/chat/completions` now succeeds for model `medium` after the PD route fix.
|
||||||
|
|
||||||
The app now captures webhook HTTP failures as structured responses instead of crashing blindly.
|
The app now captures webhook HTTP failures as structured responses instead of crashing blindly.
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ On NOMAD, Schoolhouse should run as a standalone service from `/opt/doris-school
|
|||||||
Unlike PD stacks, this host does **not** currently use the PD Docker external networks for Pangolin or shared databases. The practical deploy model here is:
|
Unlike PD stacks, this host does **not** currently use the PD Docker external networks for Pangolin or shared databases. The practical deploy model here is:
|
||||||
- publish host port `8091`
|
- publish host port `8091`
|
||||||
- point Pangolin/Newt at that host port
|
- point Pangolin/Newt at that host port
|
||||||
- reach shared Postgres on PD over `10.5.1.6:5432`
|
- reach shared Postgres on PD over `10.5.30.6:5432`
|
||||||
|
|
||||||
Use `.env.nomad.example` as the starting point for the live runtime.
|
Use `.env.nomad.example` as the starting point for the live runtime.
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<strong>Doris Constabulary</strong>
|
<strong>Doris Constabulary</strong>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-links" aria-label="Doris Schoolhouse navigation">
|
<nav class="nav-links" aria-label="Doris Schoolhouse navigation">
|
||||||
<a class="nav-link" href="http://10.5.1.16:8787/">Dashboard</a>
|
<a class="nav-link" href="http://10.5.30.7:8787/">Dashboard</a>
|
||||||
<a class="nav-link" href="/">Dashboard</a>
|
<a class="nav-link" href="/">Dashboard</a>
|
||||||
<a class="nav-link" href="/assignments/upload">Assignment Upload</a>
|
<a class="nav-link" href="/assignments/upload">Assignment Upload</a>
|
||||||
<a class="nav-link" href="/recordings/upload">Recording Upload</a>
|
<a class="nav-link" href="/recordings/upload">Recording Upload</a>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SERENITY_NFS_EXPORT="${SERENITY_NFS_EXPORT:-10.5.1.5:/mnt/user/data}"
|
SERENITY_NFS_EXPORT="${SERENITY_NFS_EXPORT:-10.5.30.5:/mnt/user/data}"
|
||||||
SERENITY_MOUNT_POINT="${SERENITY_MOUNT_POINT:-/mnt/serenity-data}"
|
SERENITY_MOUNT_POINT="${SERENITY_MOUNT_POINT:-/mnt/serenity-data}"
|
||||||
SCHOOLHOUSE_DATA_ROOT="${SCHOOLHOUSE_DATA_ROOT:-/opt/doris-schoolhouse/data}"
|
SCHOOLHOUSE_DATA_ROOT="${SCHOOLHOUSE_DATA_ROOT:-/opt/doris-schoolhouse/data}"
|
||||||
SERENITY_BACKUP_SUBDIR="${SERENITY_BACKUP_SUBDIR:-backups/nomad/doris-schoolhouse}"
|
SERENITY_BACKUP_SUBDIR="${SERENITY_BACKUP_SUBDIR:-backups/nomad/doris-schoolhouse}"
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ cd /mnt/docker-ssd/docker/compose/monitoring
|
|||||||
```bash
|
```bash
|
||||||
docker ps --filter name=prometheus --filter name=grafana --filter name=loki --filter name=promtail --filter name=node-exporter --filter name=cadvisor
|
docker ps --filter name=prometheus --filter name=grafana --filter name=loki --filter name=promtail --filter name=node-exporter --filter name=cadvisor
|
||||||
|
|
||||||
curl -fsS http://10.5.1.6:9090/-/ready
|
curl -fsS http://10.5.30.6:9090/-/ready
|
||||||
curl -fsS http://10.5.1.6:9100/metrics | head -10
|
curl -fsS http://10.5.30.6:9100/metrics | head -10
|
||||||
curl -fsS http://10.5.1.6:9090/api/v1/targets | python3 -m json.tool | head -60
|
curl -fsS http://10.5.30.6:9090/api/v1/targets | python3 -m json.tool | head -60
|
||||||
sudo docker inspect -f '{{.State.Status}}' loki
|
sudo docker inspect -f '{{.State.Status}}' loki
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,13 @@ scrape_configs:
|
|||||||
params:
|
params:
|
||||||
format: [prometheus]
|
format: [prometheus]
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['10.5.1.5:19999']
|
- targets: ['10.5.30.5:19999']
|
||||||
labels:
|
labels:
|
||||||
host: 'serenity'
|
host: 'serenity'
|
||||||
|
|
||||||
# ------- N.O.M.A.D. (via node-exporter) -------
|
# ------- N.O.M.A.D. (via node-exporter) -------
|
||||||
- job_name: 'nomad-node'
|
- job_name: 'nomad-node'
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['10.5.1.16:9100']
|
- targets: ['10.5.30.7:9100']
|
||||||
labels:
|
labels:
|
||||||
host: 'nomad'
|
host: 'nomad'
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points/search",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points/search",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points/search",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points/search",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.6:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.6:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.7:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.7:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.6:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.6:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -170,7 +170,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -170,7 +170,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.6:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.7:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.7:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:11434/v1/embeddings",
|
"url": "http://10.5.30.7:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:8786/v1/audio/transcriptions",
|
"url": "http://10.5.30.7:8786/v1/audio/transcriptions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.16:11434/v1/embeddings",
|
"url": "http://10.5.30.7:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:11434/v1/embeddings",
|
"url": "http://10.5.30.6:11434/v1/embeddings",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "PUT",
|
"method": "PUT",
|
||||||
"url": "http://10.5.1.6:6333/collections/knowledge_base/points",
|
"url": "http://10.5.30.6:6333/collections/knowledge_base/points",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://10.5.1.6:5678/api/v1/executions",
|
"url": "http://10.5.30.6:5678/api/v1/executions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://10.5.1.6:5678/api/v1/executions",
|
"url": "http://10.5.30.6:5678/api/v1/executions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://10.5.1.6:5678/api/v1/workflows",
|
"url": "http://10.5.30.6:5678/api/v1/workflows",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://10.5.1.6:5678/api/v1/executions",
|
"url": "http://10.5.30.6:5678/api/v1/executions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"url": "http://10.5.1.6:5678/api/v1/executions",
|
"url": "http://10.5.30.6:5678/api/v1/executions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -212,7 +212,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "const d = $('Parse Heal Result').first().json;\nconst downList = d.stillDown.map(m => ` • ${m.mount}`).join('\\n');\nconst healedList = d.healed.length > 0\n ? `\\nPartially healed:\\n${d.healed.map(m => ' • ' + m.mount).join('\\n')}`\n : '';\nconst body = `NFS mount script ran but the following paths are still unavailable. Manual intervention required.\\n\\nStill down:\\n${downList}${healedList}\\n\\nCheck Serenity (10.5.1.5) is up and NFS service is running.`;\nreturn [{ json: { title: '🚨 NFS Down — Manual Fix Needed', message: body, priority: 9 } }];\n"
|
"jsCode": "const d = $('Parse Heal Result').first().json;\nconst downList = d.stillDown.map(m => ` • ${m.mount}`).join('\\n');\nconst healedList = d.healed.length > 0\n ? `\\nPartially healed:\\n${d.healed.map(m => ' • ' + m.mount).join('\\n')}`\n : '';\nconst body = `NFS mount script ran but the following paths are still unavailable. Manual intervention required.\\n\\nStill down:\\n${downList}${healedList}\\n\\nCheck Serenity (10.5.30.5) is up and NFS service is running.`;\nreturn [{ json: { title: '🚨 NFS Down — Manual Fix Needed', message: body, priority: 9 } }];\n"
|
||||||
},
|
},
|
||||||
"id": "074b984f-7fc2-45bc-b97f-f5db4d8d589b",
|
"id": "074b984f-7fc2-45bc-b97f-f5db4d8d589b",
|
||||||
"name": "Format Escalation",
|
"name": "Format Escalation",
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "={{($env.LITELLM_BASE_URL || 'http://10.5.1.6:4000').replace(/\\/$/, '') + '/v1/chat/completions'}}",
|
"url": "={{($env.LITELLM_BASE_URL || 'http://10.5.30.6:4000').replace(/\\/$/, '') + '/v1/chat/completions'}}",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"url": "http://10.5.1.6:4000/v1/chat/completions",
|
"url": "http://10.5.30.6:4000/v1/chat/completions",
|
||||||
"sendHeaders": true,
|
"sendHeaders": true,
|
||||||
"headerParameters": {
|
"headerParameters": {
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ cat ~/.ssh/n8n_watchdog.pub >> ~/.ssh/authorized_keys
|
|||||||
cat ~/.ssh/n8n_watchdog # paste this into n8n → Credentials → SSH (Private Key)
|
cat ~/.ssh/n8n_watchdog # paste this into n8n → Credentials → SSH (Private Key)
|
||||||
```
|
```
|
||||||
|
|
||||||
In n8n: Credentials → New → SSH (Private Key) → host `10.5.1.6`, user `truenas_admin`, paste private key. Name it `PD SSH`.
|
In n8n: Credentials → New → SSH (Private Key) → host `10.5.30.6`, user `truenas_admin`, paste private key. Name it `PD SSH`.
|
||||||
|
|
||||||
Workflow 16 also runs `sudo docker restart` and the mount script over SSH. Add a NOPASSWD sudoers rule so it doesn't hang:
|
Workflow 16 also runs `sudo docker restart` and the mount script over SSH. Add a NOPASSWD sudoers rule so it doesn't hang:
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ sudo visudo -f /etc/sudoers.d/n8n-watchdog
|
|||||||
Create the `knowledge_base` collection before using the RAG workflows:
|
Create the `knowledge_base` collection before using the RAG workflows:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT http://10.5.1.6:6333/collections/knowledge_base \
|
curl -X PUT http://10.5.30.6:6333/collections/knowledge_base \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d '{
|
-d '{
|
||||||
"vectors": {
|
"vectors": {
|
||||||
@@ -174,7 +174,7 @@ curl -X PUT http://10.5.1.6:6333/collections/knowledge_base \
|
|||||||
### Pull nomic-embed-text into Ollama (required for workflows 02, 05, 08)
|
### Pull nomic-embed-text into Ollama (required for workflows 02, 05, 08)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl http://10.5.1.6:11434/api/pull -d '{"name": "nomic-embed-text"}'
|
curl http://10.5.30.6:11434/api/pull -d '{"name": "nomic-embed-text"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Paperless Webhook (required for workflows 03, 05, 17, and 19)
|
### Paperless Webhook (required for workflows 03, 05, 17, and 19)
|
||||||
@@ -387,7 +387,7 @@ Add these to `automation/.env` and expose them in the n8n `environment:` block:
|
|||||||
|----------|----------|---------|---------|
|
|----------|----------|---------|---------|
|
||||||
| `PAPERLESS_BASE_URL` | yes | `https://paperless.paccoco.com` | Paperless API/UI base URL |
|
| `PAPERLESS_BASE_URL` | yes | `https://paperless.paccoco.com` | Paperless API/UI base URL |
|
||||||
| `PAPERLESS_API_TOKEN` | yes | none | Paperless API token; used as `Token ...` |
|
| `PAPERLESS_API_TOKEN` | yes | none | Paperless API token; used as `Token ...` |
|
||||||
| `LITELLM_BASE_URL` | yes | `http://10.5.1.6:4000` | LiteLLM OpenAI-compatible endpoint |
|
| `LITELLM_BASE_URL` | yes | `http://10.5.30.6:4000` | LiteLLM OpenAI-compatible endpoint |
|
||||||
| `LITELLM_API_KEY` | yes | none | LiteLLM key |
|
| `LITELLM_API_KEY` | yes | none | LiteLLM key |
|
||||||
| `PAPERLESS_TRIAGE_MODEL` | no | `gpt-4o-mini` | Model for strict JSON triage |
|
| `PAPERLESS_TRIAGE_MODEL` | no | `gpt-4o-mini` | Model for strict JSON triage |
|
||||||
| `PAPERLESS_TRIAGE_REVIEW_QUEUE_PATH` | yes | `/data/paperless_review.json` | Sanitized JSON queue path mounted for Doris Dashboard |
|
| `PAPERLESS_TRIAGE_REVIEW_QUEUE_PATH` | yes | `/data/paperless_review.json` | Sanitized JSON queue path mounted for Doris Dashboard |
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ _Last updated: 2026-05-18_
|
|||||||
**What it does:** Two webhooks — `POST /rag/ingest` chunks + embeds text into Qdrant; `POST /rag/query` embeds query, searches Qdrant, asks LiteLLM, returns answer.
|
**What it does:** Two webhooks — `POST /rag/ingest` chunks + embeds text into Qdrant; `POST /rag/query` embeds query, searches Qdrant, asks LiteLLM, returns answer.
|
||||||
|
|
||||||
**Pre-test checklist:**
|
**Pre-test checklist:**
|
||||||
- [ ] `knowledge_base` collection exists in Qdrant (`GET http://10.5.1.6:6333/collections`)
|
- [ ] `knowledge_base` collection exists in Qdrant (`GET http://10.5.30.6:6333/collections`)
|
||||||
- [ ] `nomic-embed-text` model pulled in Ollama (`ollama list` on PD)
|
- [ ] `nomic-embed-text` model pulled in Ollama (`ollama list` on PD)
|
||||||
- [x] LiteLLM auth now reads `$env.LITELLM_API_KEY`; hardcoded key removed from repo artifacts
|
- [x] LiteLLM auth now reads `$env.LITELLM_API_KEY`; hardcoded key removed from repo artifacts
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# Saves the transcript + notes response as a .md file alongside the recording.
|
# Saves the transcript + notes response as a .md file alongside the recording.
|
||||||
#
|
#
|
||||||
# Install:
|
# Install:
|
||||||
# 1. Copy to PD: scp class-watcher.sh truenas_admin@10.5.1.6:/tmp/class-watcher.sh
|
# 1. Copy to PD: scp class-watcher.sh truenas_admin@10.5.30.6:/tmp/class-watcher.sh
|
||||||
# 2. sudo mv /tmp/class-watcher.sh /mnt/data/class-watcher.sh
|
# 2. sudo mv /tmp/class-watcher.sh /mnt/data/class-watcher.sh
|
||||||
# 3. sudo chmod +x /mnt/data/class-watcher.sh
|
# 3. sudo chmod +x /mnt/data/class-watcher.sh
|
||||||
# 4. sudo crontab -e
|
# 4. sudo crontab -e
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
# └── lecture-01-intro.md ← transcript + notes
|
# └── lecture-01-intro.md ← transcript + notes
|
||||||
|
|
||||||
WATCH_DIR="/mnt/data/class-recordings"
|
WATCH_DIR="/mnt/data/class-recordings"
|
||||||
WEBHOOK_URL="http://10.5.1.6:5678/webhook/class/upload"
|
WEBHOOK_URL="http://10.5.30.6:5678/webhook/class/upload"
|
||||||
|
|
||||||
# Find all .wav files directly inside class subfolders (not in done/)
|
# Find all .wav files directly inside class subfolders (not in done/)
|
||||||
find "$WATCH_DIR" -mindepth 2 -maxdepth 2 -name '*.wav' -type f | while read -r filepath; do
|
find "$WATCH_DIR" -mindepth 2 -maxdepth 2 -name '*.wav' -type f | while read -r filepath; do
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ PIHOLE_PASSWORD=CHANGE_ME
|
|||||||
|
|
||||||
# Keepalived / VIP (VRRP PASS auth is max 8 chars)
|
# Keepalived / VIP (VRRP PASS auth is max 8 chars)
|
||||||
LAN_INTERFACE=eno1
|
LAN_INTERFACE=eno1
|
||||||
VIP_CIDR=10.5.1.53/24
|
VIP_CIDR=10.5.30.53/24
|
||||||
KEEPALIVED_ROUTER_ID=53
|
KEEPALIVED_ROUTER_ID=53
|
||||||
KEEPALIVED_STATE=MASTER
|
KEEPALIVED_STATE=MASTER
|
||||||
KEEPALIVED_PRIORITY=100
|
KEEPALIVED_PRIORITY=100
|
||||||
@@ -19,7 +19,7 @@ KEEPALIVED_NOPREEMPT=true
|
|||||||
|
|
||||||
# Nebula Sync (PD primary -> replicas)
|
# Nebula Sync (PD primary -> replicas)
|
||||||
NEBULA_SYNC_PRIMARY=http://127.0.0.1:8953|CHANGE_ME
|
NEBULA_SYNC_PRIMARY=http://127.0.0.1:8953|CHANGE_ME
|
||||||
NEBULA_SYNC_REPLICAS=http://10.5.1.16:8954|CHANGE_ME
|
NEBULA_SYNC_REPLICAS=http://10.5.30.7:8954|CHANGE_ME
|
||||||
NEBULA_SYNC_FULL_SYNC=false
|
NEBULA_SYNC_FULL_SYNC=false
|
||||||
NEBULA_SYNC_RUN_GRAVITY=true
|
NEBULA_SYNC_RUN_GRAVITY=true
|
||||||
NEBULA_SYNC_CRON=0 3 * * *
|
NEBULA_SYNC_CRON=0 3 * * *
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Primary-node stack for the Pi-hole HA plan.
|
|||||||
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole`
|
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole`
|
||||||
- **Live PD stack path:** `/mnt/docker-ssd/docker/compose/pihole`
|
- **Live PD stack path:** `/mnt/docker-ssd/docker/compose/pihole`
|
||||||
- **Node role:** PD primary / MASTER
|
- **Node role:** PD primary / MASTER
|
||||||
- **VIP target:** `10.5.1.53/24`
|
- **VIP target:** `10.5.30.53/24`
|
||||||
|
|
||||||
This stack intentionally covers the **PD primary** plus PD-hosted sync:
|
This stack intentionally covers the **PD primary** plus PD-hosted sync:
|
||||||
- Pi-hole
|
- Pi-hole
|
||||||
@@ -47,14 +47,14 @@ If that validates cleanly, bring it up:
|
|||||||
- Unbound container: `unbound-pihole-primary`
|
- Unbound container: `unbound-pihole-primary`
|
||||||
- Keepalived container: `keepalived-pihole-primary`
|
- Keepalived container: `keepalived-pihole-primary`
|
||||||
- Nebula Sync container: `nebula-sync`
|
- Nebula Sync container: `nebula-sync`
|
||||||
- LAN admin URL: `http://10.5.1.6:8953/admin/`
|
- LAN admin URL: `http://10.5.30.6:8953/admin/`
|
||||||
- **NOMAD backup**
|
- **NOMAD backup**
|
||||||
- service root: `/opt/pihole-nomad`
|
- service root: `/opt/pihole-nomad`
|
||||||
- Pi-hole container: `pihole-nomad`
|
- Pi-hole container: `pihole-nomad`
|
||||||
- Unbound container: `unbound-pihole-nomad`
|
- Unbound container: `unbound-pihole-nomad`
|
||||||
- Keepalived container: `keepalived-pihole-nomad`
|
- Keepalived container: `keepalived-pihole-nomad` (staged, disabled by default)
|
||||||
- LAN admin URL: `http://10.5.1.16:8954/admin/`
|
- LAN admin URL: `http://10.5.30.7:8954/admin/`
|
||||||
- **VIP for client DNS:** `10.5.1.53`
|
- **VIP for client DNS:** `10.5.30.53`
|
||||||
- **Shared Pi-hole admin password:** stored in the local `.env` files; Nebula and both nodes use the same current value.
|
- **Shared Pi-hole admin password:** stored in the local `.env` files; Nebula and both nodes use the same current value.
|
||||||
|
|
||||||
## Important notes
|
## Important notes
|
||||||
@@ -64,6 +64,7 @@ If that validates cleanly, bring it up:
|
|||||||
- `FTLCONF_dns_listeningMode=LOCAL` is set because TrueNAS binds loopback on port 53.
|
- `FTLCONF_dns_listeningMode=LOCAL` is set because TrueNAS binds loopback on port 53.
|
||||||
- Pi-hole NTP is explicitly disabled here because PD host `chronyd` already owns UDP 123.
|
- Pi-hole NTP is explicitly disabled here because PD host `chronyd` already owns UDP 123.
|
||||||
- Nebula Sync runs on PD and uses `NEBULA_SYNC_PRIMARY` / `NEBULA_SYNC_REPLICAS` from `.env`.
|
- Nebula Sync runs on PD and uses `NEBULA_SYNC_PRIMARY` / `NEBULA_SYNC_REPLICAS` from `.env`.
|
||||||
- Current replica target is NOMAD at `http://10.5.1.16:8954`.
|
- Current replica target is NOMAD at `http://10.5.30.7:8954`.
|
||||||
|
- NOMAD no longer participates in the old Trusted-side `10.5.30.53/24` Keepalived VIP after moving to Servers VLAN. Re-enable Keepalived there only after a same-subnet HA design exists.
|
||||||
- Nebula Sync is intentionally set to selective mode with gravity sync toggles enabled for groups, adlists, and domain lists so replica-specific runtime/network settings are not clobbered.
|
- Nebula Sync is intentionally set to selective mode with gravity sync toggles enabled for groups, adlists, and domain lists so replica-specific runtime/network settings are not clobbered.
|
||||||
- `keepalived.conf` is generated and should stay local/untracked.
|
- `keepalived.conf` is generated and should stay local/untracked.
|
||||||
|
|||||||
@@ -12,8 +12,21 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
set -a
|
set -a
|
||||||
# shellcheck disable=SC1090
|
eval "$(python3 - "$ENV_FILE" <<'PY'
|
||||||
source "$ENV_FILE"
|
from pathlib import Path
|
||||||
|
import shlex
|
||||||
|
import sys
|
||||||
|
|
||||||
|
env_file = Path(sys.argv[1])
|
||||||
|
for raw_line in env_file.read_text(encoding='utf-8').splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith('#') or '=' not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split('=', 1)
|
||||||
|
value = value.strip().strip('"').strip("'")
|
||||||
|
print(f"{key.strip()}={shlex.quote(value)}")
|
||||||
|
PY
|
||||||
|
)"
|
||||||
set +a
|
set +a
|
||||||
|
|
||||||
required_vars=(
|
required_vars=(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ PIHOLE_WEB_PORT=8954
|
|||||||
NOMAD_PIHOLE_WEB_PORT=8954
|
NOMAD_PIHOLE_WEB_PORT=8954
|
||||||
PIHOLE_PASSWORD=CHANGE_ME
|
PIHOLE_PASSWORD=CHANGE_ME
|
||||||
LAN_INTERFACE=enp5s0
|
LAN_INTERFACE=enp5s0
|
||||||
VIP_CIDR=10.5.1.53/24
|
VIP_CIDR=10.5.30.53/24
|
||||||
KEEPALIVED_ROUTER_ID=53
|
KEEPALIVED_ROUTER_ID=53
|
||||||
KEEPALIVED_STATE=BACKUP
|
KEEPALIVED_STATE=BACKUP
|
||||||
KEEPALIVED_PRIORITY=90
|
KEEPALIVED_PRIORITY=90
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
# NOMAD Pi-hole / Unbound / Keepalived Replica
|
# NOMAD Pi-hole / Unbound / staged Keepalived Replica
|
||||||
|
|
||||||
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole/nomad`
|
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole/nomad`
|
||||||
- **Suggested live stack path:** `/opt/pihole-nomad`
|
- **Suggested live stack path:** `/opt/pihole-nomad`
|
||||||
- **Node role:** NOMAD backup / BACKUP1
|
- **Node role:** NOMAD Pi-hole replica on Servers VLAN
|
||||||
- **VIP target:** `10.5.1.53/24`
|
- **Staged future VIP target:** `10.5.30.53/24`
|
||||||
- **LAN interface:** `enp5s0`
|
- **LAN interface:** `enp5s0`
|
||||||
- **NOMAD LAN IP:** `10.5.1.16`
|
- **NOMAD LAN IP:** `10.5.30.7`
|
||||||
|
|
||||||
|
Current state:
|
||||||
|
|
||||||
|
- `pihole-nomad` and `unbound-pihole-nomad` stay up normally on NOMAD.
|
||||||
|
- `keepalived-pihole-nomad` is intentionally disabled by default after NOMAD moved from Trusted to Servers VLAN.
|
||||||
|
- Enable HA later with `COMPOSE_PROFILES=ha` only after a same-subnet peer and VIP design are ready.
|
||||||
|
|
||||||
Bring-up:
|
Bring-up:
|
||||||
|
|
||||||
@@ -34,7 +40,9 @@ Pi-hole on NOMAD is pinned to `enp5s0` with `FTLCONF_dns_interface=enp5s0` and `
|
|||||||
Current live containers:
|
Current live containers:
|
||||||
- `pihole-nomad`
|
- `pihole-nomad`
|
||||||
- `unbound-pihole-nomad`
|
- `unbound-pihole-nomad`
|
||||||
|
|
||||||
|
Staged but disabled-by-default HA container:
|
||||||
- `keepalived-pihole-nomad`
|
- `keepalived-pihole-nomad`
|
||||||
|
|
||||||
Current LAN admin URL:
|
Current LAN admin URL:
|
||||||
- `http://10.5.1.16:8954/admin/`
|
- `http://10.5.30.7:8954/admin/`
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ services:
|
|||||||
image: osixia/keepalived:2.0.20
|
image: osixia/keepalived:2.0.20
|
||||||
container_name: keepalived-pihole-nomad
|
container_name: keepalived-pihole-nomad
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
profiles: ["ha"]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
pihole:
|
pihole:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ vrrp_instance VI_DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual_ipaddress {
|
virtual_ipaddress {
|
||||||
10.5.1.53/24
|
10.5.30.53/24
|
||||||
}
|
}
|
||||||
|
|
||||||
track_script {
|
track_script {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole/rpi4`
|
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole/rpi4`
|
||||||
- **Suggested live stack path:** `~/pihole/stack`
|
- **Suggested live stack path:** `~/pihole/stack`
|
||||||
- **Node role:** BACKUP2
|
- **Node role:** BACKUP2
|
||||||
- **VIP target:** `10.5.1.53/24`
|
- **VIP target:** `10.5.30.53/24`
|
||||||
|
|
||||||
This node still needs its actual LAN IP confirmed before live deployment.
|
This node still needs its actual LAN IP confirmed before live deployment.
|
||||||
Native keepalived config example is in `keepalived.conf.example`.
|
Native keepalived config example is in `keepalived.conf.example`.
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ vrrp_instance VI_DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual_ipaddress {
|
virtual_ipaddress {
|
||||||
10.5.1.53/24
|
10.5.30.53/24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ APPDATA_UNBOUND_ROOT=/mnt/user/appdata/unbound-secondary
|
|||||||
SERENITY_PIHOLE_WEB_PORT=8954
|
SERENITY_PIHOLE_WEB_PORT=8954
|
||||||
PIHOLE_PASSWORD=CHANGE_ME
|
PIHOLE_PASSWORD=CHANGE_ME
|
||||||
LAN_INTERFACE=br0
|
LAN_INTERFACE=br0
|
||||||
VIP_CIDR=10.5.1.53/24
|
VIP_CIDR=10.5.30.53/24
|
||||||
KEEPALIVED_ROUTER_ID=53
|
KEEPALIVED_ROUTER_ID=53
|
||||||
KEEPALIVED_STATE=BACKUP
|
KEEPALIVED_STATE=BACKUP
|
||||||
KEEPALIVED_PRIORITY=90
|
KEEPALIVED_PRIORITY=90
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole/serenity`
|
- **Repo stack path:** `/home/fizzlepoof/repos/truenas-stacks/pihole/serenity`
|
||||||
- **Suggested live stack path:** `/boot/config/plugins/compose.manager/projects/pihole-ha`
|
- **Suggested live stack path:** `/boot/config/plugins/compose.manager/projects/pihole-ha`
|
||||||
- **Node role:** Serenity backup / BACKUP1
|
- **Node role:** Serenity backup / BACKUP1
|
||||||
- **VIP target:** `10.5.1.53/24`
|
- **VIP target:** `10.5.30.53/24`
|
||||||
- **LAN interface:** `br0`
|
- **LAN interface:** `br0`
|
||||||
|
|
||||||
Bring-up:
|
Bring-up:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ vrrp_instance VI_DNS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual_ipaddress {
|
virtual_ipaddress {
|
||||||
10.5.1.53/24
|
10.5.30.53/24
|
||||||
}
|
}
|
||||||
|
|
||||||
track_script {
|
track_script {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ── CONFIG ────────────────────────────────────────────────────────────────────
|
# ── CONFIG ────────────────────────────────────────────────────────────────────
|
||||||
GITEA_HTTP_URL="http://10.5.1.6:3000" # ← local Gitea HTTP port (check TrueNAS app config)
|
GITEA_HTTP_URL="http://10.5.30.6:3000" # ← local Gitea HTTP port (check TrueNAS app config)
|
||||||
GITEA_SSH_PORT="2222" # ← Gitea SSH port
|
GITEA_SSH_PORT="2222" # ← Gitea SSH port
|
||||||
GITEA_USER="fizzlepoof"
|
GITEA_USER="fizzlepoof"
|
||||||
GITEA_TOKEN="" # ← set this before use; regenerate with write:repository + write:user scopes
|
GITEA_TOKEN="" # ← set this before use; regenerate with write:repository + write:user scopes
|
||||||
@@ -61,7 +61,7 @@ else
|
|||||||
die "Gitea API returned HTTP ${HTTP_CODE}. Check token and URL."
|
die "Gitea API returned HTTP ${HTTP_CODE}. Check token and URL."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
REPO_URL="ssh://git@10.5.1.6:${GITEA_SSH_PORT}/${GITEA_USER}/${REPO_NAME}.git"
|
REPO_URL="ssh://git@10.5.30.6:${GITEA_SSH_PORT}/${GITEA_USER}/${REPO_NAME}.git"
|
||||||
|
|
||||||
# ── 3. Init local repo ────────────────────────────────────────────────────────
|
# ── 3. Init local repo ────────────────────────────────────────────────────────
|
||||||
info "Setting up local repo at ${LOCAL_PATH}..."
|
info "Setting up local repo at ${LOCAL_PATH}..."
|
||||||
@@ -89,7 +89,7 @@ info "Exporting symmetric key to ${KEY_EXPORT_PATH}..."
|
|||||||
git-crypt export-key "$KEY_EXPORT_PATH"
|
git-crypt export-key "$KEY_EXPORT_PATH"
|
||||||
chmod 600 "$KEY_EXPORT_PATH"
|
chmod 600 "$KEY_EXPORT_PATH"
|
||||||
warn "BACK THIS KEY UP. Without it you cannot decrypt the repo on another machine."
|
warn "BACK THIS KEY UP. Without it you cannot decrypt the repo on another machine."
|
||||||
warn " scp root@10.5.1.6:${KEY_EXPORT_PATH} /safe/location/"
|
warn " scp root@10.5.30.6:${KEY_EXPORT_PATH} /safe/location/"
|
||||||
|
|
||||||
# ── 6. .gitattributes — encrypt everything except meta files ─────────────────
|
# ── 6. .gitattributes — encrypt everything except meta files ─────────────────
|
||||||
info "Writing .gitattributes..."
|
info "Writing .gitattributes..."
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user