Commit Graph
47 Commits
Author SHA1 Message Date
Claude 8a9fa02e25 Separate Caddy/services box from VLAN 99 management computer
The management computer (VLAN 99) only runs the switch manager tool
and holds SSH keys/TOTP secrets. Caddy and services (Plex, etc.) run
on a SEPARATE computer on LAN.

Backend:
- New /api/services/config endpoint to store services box LAN IP
- services-config.json persists caddy_ip separately from mgmt_ip
- Port forward creation targets caddy_ip (LAN services box), not
  mgmt_ip (VLAN 99 management computer)
- Deploy endpoint uses caddy_ip for all Caddy/NAT references
- _get_caddy_ip() helper reads from services config

Frontend:
- New "Services Host" panel: configure Caddy box LAN IP
- Checklist shows caddy_ip status, not mgmt_ip
- Port forward and deploy pass caddy_ip to backend
- Clear labels: "Services box" vs "Management computer"

Architecture:
  VLAN 99: management computer (this tool, SSH keys, TOTP)
  LAN: services computer (Caddy, Plex, Docker containers)
  WAN port forward 443 → services computer LAN IP

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 12:45:34 +00:00
Claude 4b28f8cfe5 Auto-set WG peer DNS profile from selected VLAN
- DNS profile no longer hardcoded to "house"
- When selecting a single VLAN for a WG peer, auto-sets the DNS
  profile to that VLAN's name (e.g. select "Staff" VLAN → profile
  becomes "staff")
- Multiple VLANs: keeps current profile, user can change manually
- Input shows context: auto-set vs manual vs needs VLAN selection
- Profile name matches ctrld upstream names so the correct ControlD
  profile applies to VPN DNS queries

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 12:27:29 +00:00
Claude 31f2008153 Wire WireGuard peer DNS to ControlD profiles via ctrld
When creating a WireGuard peer on OPNsense:
- Client config DNS now points to OPNsense's IP (not tunnel gateway)
  so DNS flows: client → OPNsense → Unbound → ctrld → ControlD
- New dns_profile field: select which ControlD profile applies to
  VPN clients (default: "house" for VLAN 99)
- Generates ctrld.toml instructions for WireGuard tunnel subnet
  routing — tells user what to add so ctrld routes VPN DNS queries
  to the correct ControlD profile
- QR modal now shows ControlD setup instructions alongside the
  WireGuard config

This solves the Android Private DNS conflict: WireGuard's DNS setting
overrides Android's Private DNS, pointing to OPNsense which runs
Unbound → ctrld. No Private DNS toggle needed on the phone.

Multi-VLAN access for VPN peers works because the peer is on the
WireGuard interface (not on any VLAN). OPNsense routes between the
tunnel and VLANs per firewall rules. VLAN isolation preserved.

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 12:16:00 +00:00
Claude e486de26b0 Add port forwarding, PoE dashboard, topology tabs + deduplicate firewall
Removed duplicate firewall policy endpoints (kept existing ones at
/api/firewall/* which match the frontend).

Port Forwarding tab:
- Create/delete OPNsense NAT port forwards via API
- Track rule UUIDs for clean removal
- Form: protocol, WAN port, target IP:port, description
- Table: active forwards with one-click remove
- Note: for HTTP services, use Services tab (Caddy) instead

PoE Budget tab:
- Visual power bar: used/total/remaining watts with percentage
- Color-coded thresholds: green (<75%), orange (75-90%), red (>90%)
- Warning banner when budget exceeds 85%
- Per-port power draw grid with status indicators
- Auto-parsed from cached switch PoE status

Network Topology tab:
- Auto-generated from live switch + OPNsense data
- Router node: IP, version, online/offline status
- Switch node: hostname, IP, port up/down counts
- Trunk link visualization between router and switch
- VLAN fan-out cards: port counts, device counts, subnets
- One-click refresh

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 02:27:50 +00:00
Claude 130baf9303 Add firewall policy matrix, port forwarding, topology, PoE dashboard
Firewall policy matrix:
- VLAN-to-VLAN policies: full, internet-only, blocked, service, custom
- Generates both switch ACLs AND OPNsense firewall rules
- Printer VLAN preset: one-way access (staff can print, printers
  can't initiate connections back) on ports 9100/631/443/80
- Additional presets: LAN-access-all, IoT-isolated, Guest-isolated,
  Camera-NVR-only
- Preview endpoint shows generated commands before pushing
- Push endpoint applies to both devices with backup + safety check

Port forwarding:
- Create/delete OPNsense NAT port forwards via API
- Tracks rule UUIDs for clean removal
- Companion firewall rules auto-created

Network topology:
- /api/topology returns router, switch, VLANs, port states, devices
- Auto-generated from live cached data

PoE budget dashboard:
- /api/poe/budget parses cached PoE status
- Total/used/remaining watts, percent used, per-port draw

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 02:24:32 +00:00
Claude 67806d0255 Wire services end-to-end: Caddy reload, NAT reflection, port forward
Fully wired service proxy deployment:

Backend:
- /api/services/status: full checklist (OPNsense API, SSH, NAT
  reflection, port forward 443, Caddyfile.services, service count)
- /api/services/enable-nat-reflection: enables NAT reflection on
  OPNsense via SSH config.xml edit + filter reload
- /api/services/create-port-forward: creates WAN TCP 443 → Caddy
  port forward via OPNsense NAT API, tracks rule UUID
- /api/services/deploy: writes Caddyfile.services, reloads Caddy
  (tries docker compose exec, then restart, then systemctl), checks
  NAT reflection status, verifies port forward exists

Infrastructure:
- docker-compose.yml: mount Caddyfile.services into Caddy container,
  switch-manager volume writable (for writing Caddyfile.services)
- Caddyfile.template: auto-imports /etc/caddy/Caddyfile.services

Frontend:
- Setup Checklist panel with green/red dots for each prerequisite
- Enable NAT Reflection button (one-click)
- Create Port Forward button (one-click)
- Deploy button writes Caddyfile, reloads Caddy, verifies everything
- Caddyfile preview in deploy results

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 01:36:28 +00:00
Claude df9914e1a1 Rewrite services to use Caddy on LAN + NAT reflection for isolated VLANs
Previous approach tried to put a reverse proxy on OPNsense or poke
firewall holes — both wrong. Correct architecture:

- Caddy stays on LAN management computer (where it already is)
- WAN: port 443 forwarded to Caddy. Only port exposed externally.
- LAN devices reach services directly via Caddy
- Isolated VLANs (IoT, Guest) use public FQDNs (plex.mydomain.com)
- OPNsense NAT reflection handles this internally — traffic never
  leaves the network, but IoT is treated exactly like an external user
- Zero cross-VLAN access. No pinholes. Full isolation preserved.

IoT = untrusted = same access as someone on the internet. This is the
correct security model — no exceptions for "just one port."

Deploy endpoint now: writes Caddyfile entries, checks NAT reflection
status, provides setup checklist for port forward + reflection toggle.

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 01:28:30 +00:00
Claude 3cdad0dcb5 Fix service proxy architecture and add VLAN time-based schedules
Service proxy fix:
- DNS now resolves service FQDNs to OPNsense gateway IP (not mgmt box)
- Devices reach services through their own gateway — never touch other
  VLANs. Full VLAN isolation preserved.
- No new firewall rules needed — devices can already reach their gateway
- Deploy tries Caddy on OPNsense first, then HAProxy plugin, then gives
  manual setup instructions
- Removed "allowed VLANs" selector — all VLANs can reach services
  automatically through the gateway reverse proxy

VLAN time-based schedules:
- New vlan_enable/vlan_disable scheduler actions
- Creates/removes OPNsense firewall allow-outbound rules on schedule
- Switch ports stay up so devices reconnect when re-enabled
- Tracked rule UUIDs for clean enable/disable cycles
- VlanScheduleWizard UI component with paired off/on times
- Quick presets: Guest WiFi midnight-6am, Business 6pm-8am weekdays,
  Kids 9pm-7am, IoT 11pm-5am
- ntfy notifications on VLAN enable/disable events

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 01:08:16 +00:00
Claude 7928f6f769 Add firewall policy matrix, service proxy, ntfy alerts, and scheduler
Firewall inter-VLAN policy matrix:
- Visual VLAN-to-VLAN matrix with click-to-set policies
- Policy types: block, allow, one-way, printer, service-ports
- Printer template: other VLANs reach ports 9100/631/443/515, printers
  cannot initiate back — solves the "printer VLAN" use case
- Generates both switch ACLs AND OPNsense firewall rules
- Preview commands before pushing, auto-backup before changes

Service proxy (LAN services via FQDN without inter-VLAN access):
- Register services with FQDN + backend URL + allowed VLANs
- Deploy generates Caddyfile entries, Unbound DNS overrides, and
  firewall rules allowing only port 443 to the proxy
- Pattern: device on VLAN 30 → DNS resolves to mgmt box → Caddy
  proxies to actual LAN server — no VLAN-to-VLAN access needed

ntfy push notifications:
- Configure ntfy.sh or self-hosted ntfy server
- Alert events: connectivity lost/restored, PoE budget >85%,
  backup failures, push failures
- Integrated into poll loop — alerts fire on state transitions
- Test notification button

Scheduled operations:
- Cron-like scheduler for automated backups and connectivity checks
- Background thread checks every 60 seconds
- Per-schedule: name, action, hour, minute, days (mon,wed,fri or *)
- Run-now button for manual trigger
- ntfy notifications on scheduled task completion/failure

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 00:55:29 +00:00
Claude 7f6fa75aff Add unified network management with backup/restore and safety checks
New capabilities:
- Unified Network tab: provision VLANs across switch + OPNsense in one
  operation — select ports, set PoE per-port, auto-configure DHCP and
  firewall rules on OPNsense
- Automatic backup before every change: switch running-config via SSH,
  OPNsense full XML config export via API
- Backup/Restore tab: manual backups, download, restore with safety net
  (creates backup of current state before restoring)
- Connectivity safety checks: pre-change and post-change SSH/API probes
  to both devices — warns if connectivity lost after push
- Safe push endpoint (/api/switch/push-safe) wraps existing push with
  auto-backup and connectivity verification
- Backup pruning (keeps last 50 per device)

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 00:32:45 +00:00
Claude d9b6d05862 Add OPNsense SSH shell access and Unbound management endpoints
Paramiko exec_command() bypasses the OPNsense console menu automatically
(menu only appears for interactive logins) so no human needs to press 8.

New API surface:
  POST /api/opnsense/ssh/generate-key        — create ed25519 key for OPNsense
  POST /api/opnsense/configure-ssh           — save SSH settings + pin host key
  GET  /api/opnsense/ssh-status              — test SSH connectivity
  POST /api/opnsense/ssh/run                 — run arbitrary command (auth-gated)
  GET  /api/opnsense/unbound/status          — read config files + .lan leak test
  POST /api/opnsense/unbound/reload          — unbound-control reload
  POST /api/opnsense/unbound/fix-lan-zone    — write correct local-lan-zone.conf,
                                               verify with unbound-checkconf,
                                               reload, confirm no ControlD leak
  POST /api/opnsense/unbound/write-forward-ctrld — enable/disable ctrld forwarding

SSH key stored at /etc/switch-manager/opnsense_key
Host key pinned to /etc/switch-manager/opnsense_known_hosts
SSH config (key_path, ssh_user) stored alongside existing API creds in opnsense.json

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 18:43:02 +00:00
Claude 8a3ef94310 Fix ctrld/Unbound architecture — Unbound stays on :53, ctrld on 127.0.0.1:5354
Previous code had the architecture completely backwards:
  WRONG: ctrld takes :53, Unbound moves to :5353 as a local resolver
  RIGHT: Unbound stays on :53, ctrld binds localhost:5354, Unbound
         uses Query Forwarding to push external queries through ctrld

This was verified working after reboot with no manual intervention.
The old approach caused a race at boot (whichever service won :53
first would work; the other would fail until manually restarted).

Changes:
- _build_ctrld_toml: router mode listener is now 127.0.0.1:5354
  (not per-VLAN gateway IPs); no split-horizon rules needed since
  Unbound handles all local resolution before queries reach ctrld
- CtrldConfig: unbound_port (5353) → ctrld_port (5354)
- _ctrld_generate_opnsense_cmd: rewritten with correct 5-step guide:
  install ctrld, write toml, configure Unbound Query Forwarding,
  remove home.arpa local-zone (tutorial artifact causing PTR failures),
  verify with dig
- All call sites updated to use ctrld_port instead of unbound_port/local_resolver

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 17:29:13 +00:00
Claude c41e69d875 Add *.home.arpa / in-addr.arpa / ip6.arpa rules to ctrld TOML
ctrld was only forwarding *.lan and *.local to Unbound.
Reverse DNS (PTR) queries and RFC 8375 *.home.arpa names were
leaking upstream instead of being answered by Unbound locally.

Both router-mode and proxy-mode TOML rule blocks now include:
  *.home.arpa  → upstream.local
  *.in-addr.arpa → upstream.local   (IPv4 reverse DNS)
  *.ip6.arpa   → upstream.local    (IPv6 reverse DNS)

This ensures all local/private DNS resolves correctly after reboot
without any manual intervention or unknown/broken state.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 17:20:01 +00:00
Claude 13670000c2 Fix Unbound/ctrld port 53 conflict on OPNsense — coexistence via port 5353
Root cause: OPNsense enables Unbound on 0.0.0.0:53 at boot. ctrld also
needs :53. Whoever starts second loses. After upgrades/reboots Unbound
wins and ctrld silently fails (or vice-versa).

Fix: move Unbound to localhost:5353 only, ctrld owns :53 on VLAN IPs.
Both services now start cleanly after every reboot with zero conflict.

Changes:
- CtrldConfig adds unbound_port (default 5353) and local_domain ("lan")
- _build_ctrld_toml now always receives local_resolver="127.0.0.1:5353"
  in OPNsense/opnsense mode; adds [upstream.local] type=legacy so *.lan,
  *.local, *.home.arpa queries still resolve through Unbound
- _ctrld_generate_opnsense_cmd emits clear step-by-step instructions:
    step1_unbound  — change Unbound port to 5353 + restrict to Localhost
    step2_install  — ctrld install command
    step3_config   — write ctrld.toml (includes local upstream for Unbound)
    step4_dns      — set DHCP option 6 to per-VLAN gateway IP
    step5_verify   — test both internet and local DNS after deploy
  Includes unbound_warning explaining why the order matters
- save-config, toml-preview, and update-profiles all persist and reload
  unbound_port + local_domain from ctrld.json

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 15:17:21 +00:00
Claude 0c977eace4 Add dual-mode ctrld TOML (router vs proxy) and gateway-per-VLAN support
Router mode (deploy_mode="router") — for ctrld running on OPNsense:
  Each VLAN gets its own [listener.N] bound to the VLAN gateway IP
  (e.g. 192.168.10.1 for VLAN 10).  VLAN clients send DNS to their
  gateway; ctrld receives it on that listener and routes it to the
  correct upstream with zero CIDR lookup overhead.  Default when
  mode="opnsense".

Proxy mode (deploy_mode="proxy") — for ctrld on the management host:
  Single [listener.0] on 0.0.0.0:53 with [network.N] CIDR sections
  and a networks= policy array in [listener.0.policy].  Unchanged
  behaviour from before, correct for non-router deployments.

CtrldVlanProfile gains optional gateway field (VLAN gateway IP) used
by router mode to set each listener.N ip.  Falls back to 0.0.0.0 if
not provided so existing configs without it keep working.

CtrldConfig gains deploy_mode field; persisted in ctrld.json so
toml-preview, update-profiles, and future reloads regenerate the same
topology.  All _build_ctrld_toml callers now pass deploy_mode through.

Both modes confirmed against ctrld v1.5.0 (March 2026) TOML spec.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 14:58:32 +00:00
Claude cb6f618514 Fix ctrld TOML generator and add pre-flight endpoint validation
TOML structural fix (critical):
- Remove wrapper [listener]/[network]/[upstream] headers; use flat
  dotted-key notation ([listener.0], [network.0], etc.) that ctrld's
  Go TOML v2 parser requires — the old nested style triggered a table
  redefinition panic at startup
- Add 'name' field to every [upstream.N] section (required by ctrld)
- Add [listener.0.policy] name field

DoH3 and protocol support:
- CtrldVlanProfile gains protocol (default "doh3") and endpoint_url
  fields; endpoint_url overrides the ControlD resolver_id URL if set
- Upstream type now uses the profile's protocol instead of hardcoded
  "doh" — enables DoH3 connection-pool reuse added in ctrld 2025

Endpoint pre-flight validation:
- New _validate_doh_endpoint(): sends RFC 8484 DoH GET query over
  plain HTTPS (works for DoH3 URLs too — ControlD serves both) and
  measures latency; no ctrld binary or Docker required
- New POST /api/ctrld/validate-endpoints: tests all profile endpoints,
  validates TOML syntax via tomllib (Python 3.11+), returns per-profile
  results + toml_preview
- ctrld_save_config now runs validation before writing anything and
  returns HTTP 400 with per-profile probe results on failure — configs
  are never pushed with a broken endpoint

OPNsense plugin verdict: documented in code — the os-controld plugin
kills Unbound and breaks OPNsense DNS advertisement; SSH-based deploy
with our own TOML remains the correct path

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 14:55:22 +00:00
Claude c5d3383abe Bridge Base Software gaps via OPNsense API
- Add POST /api/vlan/provision: end-to-end VLAN wizard that creates the
  switch VLAN, OPNsense VLAN tag, DHCP scope, and allow-outbound firewall
  rule in one call; returns pending_steps for anything needing manual
  OPNsense UI finish (interface assignment when opnsense_if not provided)

- Rewire POST /api/devices/push-reservation to target OPNsense DHCP when
  configured (no Advanced License required); falls back to switch CLI only
  if OPNsense is not set up; uses stored VLAN→interface map for iface lookup

- Rewrite POST /api/devices/push-pinhole to use OPNsense firewall/filter
  API instead of switch ACLs; stores rule UUIDs in pinholes.json for clean
  removal; no longer requires Advanced License

- Remove dead relay endpoints (GET/POST /api/dhcp/relay/*), RelayConfig
  model, and helpers (_get_relay_status, _get_vlan_ips, _build_relay_cmds);
  relay config is irrelevant when OPNsense is the DHCP server

- Add VLAN_IF_MAP_FILE and PINHOLE_FILE with load/save helpers to persist
  the VLAN→OPNsense interface mapping and pinhole rule UUIDs across restarts

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 14:27:21 +00:00
Claude 3f05be1f1d Add GET /api/switch/capabilities endpoint with license detection
Probes the switch using read-only show commands to detect whether the
Advanced Software License is installed. Base Software rejects ACL and
L3 VLAN interface commands with 'Invalid input detected'.

- GET /api/switch/capabilities: non-destructive probe (show ip access-list,
  show interface vlan 1), returns acl/l3_vlan/dhcp_relay_config/
  management_pinholes/dns_enforce_acls flags and license_tier. Cached 5 min.
- _require_advanced_license(): guard helper that raises HTTP 402 with a
  clear message before attempting any ACL push to the switch.
- Applied guard to: POST /api/switch/acl, /api/devices/push-pinhole,
  /api/dhcp/relay/configure, /api/ctrld/dns-enforce-acls.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 14:02:06 +00:00
Claude e2e03b1459 Fix DHCP server commands to use correct 'show ip dhcp-server' syntax
The original commands were missing the 'ip' prefix. Correct ACLI syntax:
  show dhcp-server          -> show ip dhcp-server
  show dhcp-server leases   -> show ip dhcp-server leases
  show dhcp-server static-binding -> show ip dhcp-server static-binding

The ERS 59100GTS-PWR+ has a DHCP server but it may need to be enabled
first ('ip dhcp-server enable' in config mode) or may require an
Advanced License. All DHCP server calls now have try/except so device
discovery falls back to ARP if the feature is not yet active.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 13:26:34 +00:00
Claude 82574ea0fa Fix CLI commands to match ERS 59100GTS-PWR+ actual ACLI syntax
- show ip route default → show ip route (parse 0.0.0.0 row for gateway)
- show ip helper-address → show ip dhcp-relay fwd-path + update parser
- ip helper-address → ip dhcp-relay fwd-path <vlan-ip> <server-ip>
  (add _get_vlan_ips() to resolve VLAN interface IPs before building cmds)
- Remove all show dhcp-server / show dhcp-server leases / show dhcp-server
  static-binding calls — switch has no DHCP server (show ip dhcp ? only
  shows 'client'). Device discovery now uses show arp only.
- push-reservation, sync to_switch/remove_switch → 501 Not Implemented
- _get_switch_reservations() / _get_switch_dhcp_status() return empty/false

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 13:22:01 +00:00
Claude 504a55fb2b Increase screen scrollback buffer to 10000 lines
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 04:23:56 +00:00
Claude 8705d3d7dd Add command reference table and screen scrollback docs
- Table of all read and push commands with confirmed/not-tested/invalid status
- Note that save config caused a reboot on first run
- screen scrollback: Ctrl-A [ to enter copy mode, defscrollback in .screenrc

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 04:21:03 +00:00
Claude f52c2721d0 Note switch default IP (192.168.1.1) means TFTP needs no console prep
Per the ERS 59100 Quick Install Guide, the switch defaults to 192.168.1.1/24
on VLAN 1 if DHCP fails. Method 3 now notes that on a factory/reset switch
you can TFTP without configuring anything via console first.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 04:17:06 +00:00
Claude fe07079916 Add laptop-as-TFTP-server and XMODEM console upgrade methods
- Method 3: laptop with console + Ethernet can act as temporary TFTP server
- Method 4: XMODEM over console cable when USB broken and no Ethernet available
- Note 3-hour transfer time warning for XMODEM at 9600 baud

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 04:07:26 +00:00
Claude b77f6fd19b Add firmware upgrade section covering USB, SFTP, and TFTP methods
- Document current firmware versions (BOSS 7.9.6.015 / Diag 7.5.0.4)
- Note upgrade rules: one version at a time, diag first, no read-only USB files
- USB method via download command and boot menu option 4
- SFTP and TFTP methods via CLI
- Link to Extreme portal and announcements page for latest version

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 04:03:40 +00:00
Claude 32d512f8e1 Complete boot menu documentation with all options (A, B, C)
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:59:22 +00:00
Claude e4777d72fb Document diagnostic boot menu and improve factory reset instructions
- Add boot menu contents (options 1-9, A-C) with explanation of each
- Method 1: boot menu option 5 (works without knowing password)
- Method 2: CLI method (requires working login)
- Note 60-second window and how to continue normal boot if entered by accident

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:58:56 +00:00
Claude fe723ea6e5 Add switch OS description and factory reset instructions to README
- Document BOSS v7.9.6 as the switch firmware in Requirements section
- Add factory reset procedure (boot config flags factory) to Troubleshooting
- Note when a wipe is and is not necessary

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:53:18 +00:00
Claude efd3c327d4 Add OS requirements section at top of README
Management computer must run Linux (Ubuntu/Debian); setup script uses
apt, systemd, picocom, and Docker. Browser UI works from any OS.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:50:21 +00:00
Claude ff6e3920c0 Fix Step 6 commands and explain IP conflict / management VLAN design
- Fix save command: copy running-config nvram:config.cfg -> save config
- Fix port format note: remove incorrect 1/1 slot prefix, both models use bare numbers
- Add note explaining why 192.168.1.1 conflict is not a problem (management VLAN 99 is separate subnet)
- Add console commands to change VLAN 1 IP if needed before VLAN 99 is configured

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:47:09 +00:00
Claude 8ab9cd9cf6 Add console cable plug-in order warning and console vs SSH note
Cable must be connected before powering on for screen/picocom to work.
Also clarify that console is one-time setup only; script uses SSH over Ethernet.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:44:48 +00:00
Claude c4001b0465 Fix read_cmd to use interactive shell with enable mode
exec_command runs in user mode on BOSS v7.9.6; most show commands
(show vlan, show sys-info, show poe-main-status, show config, etc.)
require enable mode. Switch to invoke_shell per read command, sending
terminal length 0 and enable before each command.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:42:27 +00:00
Claude b43eed0546 Use show poe-main-status instead of per-port PoE command
show poe-port status rejects all argument formats on this firmware;
show poe-main-status gives overall PoE power and health data instead.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:35:56 +00:00
Claude ea6203f5e6 Fix interface naming and save command for BOSS v7.9.6
- Use FastEthernet {p} instead of GigabitEthernet {p} for interface commands
- Use save config instead of copy running-config nvram:config.cfg

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:31:26 +00:00
Claude 639f1ea16f Fix SSH commands for BOSS v7.9.6 on ERS 59100GTS-PWR+
- Replace show poe-port-status with show poe-port status ALL
- Replace show vlan members with show vlan
- Replace show running-config with show config
- Fix VLAN port format from 1/{p} to {p} (BOSS uses bare port numbers)
- Fix interface naming from GigabitEthernet 1/{p} to GigabitEthernet {p}
- Add terminal length 0 to push session setup to prevent pagination

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:29:16 +00:00
Claude 12646d4184 Update switch_backend.py for ERS 59100GTS-PWR+ (96+4 port)
Replace ERS 5952 (48+4 port) config with ERS 59100GTS-PWR+:
- Port validation extended to 1–100
- All interfaces now use GigabitEthernet 1/{p} slot notation
- PoE boundary moved from port 48 to port 96
- VLAN commands updated to use 1/{p} port notation
- Key path, TOTP name, and app title updated

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 03:05:44 +00:00
Claude 06b87eaaff Fix Ctrl-Y boot instruction — keypress is required, not optional
The ERS 59100GTS-PWR+ will not continue booting without Ctrl-Y.
Previous instruction was incorrect.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 02:25:35 +00:00
Claude 064d9b358e Add ERS 59100GTS-PWR+ physical setup guide to README
Documents the full first-time hardware setup flow:
- Where the console/service port is on the 59100 and 5952 front panel
- What console cable to buy and how to connect it
- What the Ctrl-Y boot screen means (don't press it, just wait)
- Default login credentials
- Bootstrap VLAN 99 commands including 59100 port numbering (1/1 format)
- Management computer static IP setup
- Consolidates and removes duplicate console/bootstrap sections

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-24 02:21:17 +00:00
Claude 020162c1c8 Add IoT+local services ACL template; fix offline DNS resilience guidance
New ACL template: "IoT VLAN — isolated + access local services via FQDN"
- Permits DNS to the configured resolver (so FQDNs resolve to LAN IPs)
- Permits traffic to the servers VLAN subnet only (NAS, Home Assistant, etc.)
- Blocks all other RFC1918 — users, cameras, management stay isolated
- Permits internet
- serverSubnet param auto-suggested from the vlans list if a VLAN named
  "Servers" exists; otherwise user enters it

Template description explains the full picture:
- How FQDN access works through the ACL (DNS → LAN IP → ACL permits it)
- Offline resilience: ctrld must be on OPNsense (not management PC) to
  survive internet outages; split-horizon in DNS tab makes *.lan resolve
  from local dnsmasq without any internet dependency
- Servers VLAN security: lock down servers VLAN inbound ACL by port
  so IoT can only reach specific service ports, not all server traffic

Strict IoT template description tightened — now clearly says "zero LAN
access" so users pick the right template for their use case.

Modal: serverSubnet field added alongside existing ctrldIp/nvrIp/pbxIp
params; preview correctly passes serverSubnet to the build function.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 17:00:43 +00:00
Claude 5c8536effd Add Dashboard tab as landing page; fix VoIP/IoT template descriptions
Dashboard tab (now default landing page):
- Service status cards: Switch / OPNsense / ctrld DNS / WireGuard
  each clickable to navigate to the relevant tab
- Setup checklist with progress bar: 8 steps from switch connection
  through WireGuard, each uncompleted step is clickable and navigates
  to the tab where that step is configured
- VLAN health grid: per-VLAN card showing device count (from DHCP
  leases), Relay / DNS / ACL status dots, and inline "+ add" nudges
  for anything not yet configured
- Quick Access buttons to all tabs
- All data loaded in parallel from existing API endpoints — no new
  backend endpoints needed

ACL template fixes:
- VoIP template label updated: "local desk phones" clarifies when to use it
- VoIP description now explains: ACL is subnet-based (MAC randomization
  irrelevant), switch IS the L3 enforcement point (no OPNsense inter-VLAN
  rules needed), and gives the correct mobile-softphone recommendation:
  configure with public FQDN via OPNsense port forwarding + NAT reflection
- IoT template label/description updated to explicitly list smart TVs,
  printers, IoT as targets and mention it blocks PBX + all LAN services

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 16:47:16 +00:00
Claude 2ebdb22e0a Add VoIP/SIP ACL template for Asterisk PBX, port range support
ACL backend:
- AclRule gains optional port_end field; build_acl generates
  "range X Y" when both port and port_end are set (needed for RTP)

New ACL template — "SIP Phone VLAN — Asterisk / FreePBX access":
- Permits SIP signaling UDP/TCP 5060 to PBX IP
- Permits SIP/TLS TCP 5061 to PBX IP
- Permits RTP audio UDP range 10000-20000 to PBX IP (uses new range syntax)
- Blocks management VLAN 99
- Permits internet and all other traffic
- Requires entering the Asterisk server IP (restricts SIP/RTP to that
  exact host, not the whole VLAN subnet)

Template description explains:
- Why OPNsense firewall rules are also needed (inter-VLAN routing)
- Exactly which OPNsense rules to add (including return RTP)
- Remote access options: WebRTC via Caddy reverse proxy (recommended)
  and SIP/TLS with fail2ban for traditional SIP clients

Template modal:
- New pbxIp param field shown for VoIP template
- Description box scrollable for longer template descriptions
- Preview renders "range X Y" for port range rules

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 16:15:38 +00:00
Claude 7fe42416d8 VPN tab: prioritize OPNsense WireGuard, contextual descriptions
When OPNsense WG plugin is detected:
- Show recommendation banner at top explaining why router-level VPN
  is better than running it on the management PC
- OPNsense WG section appears first (recommended path)
- Local WG panels relabeled as "backup / this machine only"
- Local WG description changes to explain the limitation (VLAN 99 only)

When OPNsense is not connected:
- Local WG shown normally as the primary option
- Tip nudge added pointing to DHCP tab to unlock router-level VPN
- OPNsense WG section shown below as a setup prompt

In both cases:
- SSH Tunnel renamed to "Emergency Fallback" with clearer description
- Pre-select servers VLAN when OPNsense WG server becomes available
  (most users want remote access to services, not IoT/cameras)
- Connected Peers / Clients panels prefixed "Local WG —" for clarity

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 15:57:13 +00:00
Claude f9bb4b26cc Add OPNsense WireGuard — router-level VPN with per-VLAN access control
Moves WireGuard off the management computer and onto OPNsense so any
device can VPN home without touching the management PC. Each peer is
restricted to only the VLANs you select (e.g. phone gets VLAN 10 only,
laptop gets VLAN 10 + 20). Private keys are generated on the mgmt PC
and never sent to OPNsense — only the public key is registered.

Backend (switch_backend.py):
- /api/opnsense/wireguard/status      — check plugin, server, peers
- /api/opnsense/wireguard/setup-server — create wg1 on OPNsense via API
- DELETE /api/opnsense/wireguard/server — tear down server
- /api/opnsense/wireguard/add-peer    — generate keypair, register peer,
                                        link to server, return .conf
- DELETE /api/opnsense/wireguard/peer/{uuid} — revoke peer
- /api/opnsense/wireguard/peer-config/{name} — fetch saved .conf

Frontend (ers5952-manager.jsx):
- New OPNsenseWGSection component added to VPN tab below local WireGuard
- Progressive UI: not configured → plugin missing → server setup →
  peer management (VLAN checkboxes) → QR/.conf download
- Firewall rules guidance panel auto-generated from active peers showing
  exactly which OPNsense rules to add per VLAN
- vlans prop threaded through to WireGuardTab so VLAN names/colors
  appear on peer badges and in the VLAN selector

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 15:31:53 +00:00
Claude 8b4ed7f331 Add DHCP relay config — all VLANs except 99 relay to OPNsense
- Backend: _get_relay_status() reads current ip helper-address per VLAN
- Backend: _build_relay_cmds() generates ERS 5952 relay CLI commands
- Backend: /api/dhcp/relay/status and /api/dhcp/relay/configure endpoints
- Backend: dhcp_overview now includes relay status in response
- Frontend: VLAN_MAP + VlanBadge + vlanFromIp() helpers for consistent labelling
- Frontend: RelayPanel shows per-VLAN relay status grid with push button;
  VLAN 99 always shown as locked/local, VLANs 10/20/30/40/50 show live
  relay target and purpose note
- Frontend: Reservations table gains VLAN column and inline purpose note
  (from descr/notes or VLAN_MAP fallback)

VLAN 99 is excluded from relay at both backend and UI level — it is the
switch management / OPNsense recovery path.

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 15:04:51 +00:00
Claude b1b2b91905 Fix ctrld setup: ask subnet/domain/resolver, newbie-friendly OPNsense instructions
- setup_ctrld() now asks for each VLAN's actual subnet (was hardcoded
  as 192.168.{id}.0/24, wrong for any non-default addressing)
- Asks for local domain suffix (default: lan) with explanation of what
  it is and where to find it in OPNsense
- Asks for local resolver address (default: 127.0.0.1:5353) with a
  full plain-English explanation of WHY Unbound must move off port 53,
  why both .lan and .local rules are needed, and what breaks without them
- _ctrld_opnsense_setup() now walks through the Unbound port change
  step-by-step with exact OPNsense UI paths, pausing for confirmation
  at each stage before proceeding
- All three install paths (_local, _opnsense, _manual) now pass
  local_domain and local_resolver through to _build_toml()
- Intro text rewritten for someone unfamiliar with DNS filtering,
  VLANs, or Resolver IDs — explains what each thing is before asking

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 14:33:35 +00:00
Claude ed2380edc9 Add .gitignore to exclude embed_for_dist.py backup files
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 13:17:40 +00:00
Claude 360fbb5606 Add DNS enforcement, ACL templates, local hostnames, ctrld format fix
Features added:
- Port 53 conflict resolution: auto-detect/fix systemd-resolved stub listener
  on Linux; instructions for OPNsense Unbound (ctrld auto-terminates it)
- DNS enforcement ACLs: generate ERS 5952 ACL commands that permit DNS only
  to ctrld IP and block all other port 53/853 traffic per VLAN
- Inter-VLAN routing ACL templates: Staff, IoT, Guest, Camera profiles with
  live preview and parameter inputs (ctrld IP, NVR IP, subnet)
- Local hostname resolution: dnsmasq Docker service for .lan split-horizon DNS;
  manage hostname→IP mappings via UI; generates dnsmasq.conf and ctrld.toml
  upstream.local block
- Fix ctrld.toml format: correct [listener.0], [network.N], [upstream.N] table
  notation (was using wrong [[array]] notation); matches official docs format
- Backend docstrings: added docstrings to all previously undocumented functions
- README: new sections for port 53 conflict resolution, DNS enforcement ACLs,
  ACL templates, and local hostname resolution (dnsmasq)
- Fix Python 3.11 f-string syntax errors in Avaya_5952_setup.py (backslash
  in f-string expressions, same-type quote in dict access); embed now succeeds

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
2026-03-23 13:16:59 +00:00