Services box LAN IP is now a setting (gear icon → Network section),
not a panel in the Services tab. Set it once, saved via API on blur
when authenticated. Loaded on app startup from /api/services/config.
Removed: Services Host panel, caddyIpInput state, saveCaddyIp function
Added: Network section in SettingsPanel with caddy_ip field
- Auto-saves to backend on blur (requires active TOTP session)
- Loaded from /api/services/config on mount
- Services tab checklist reads from status.caddy_ip
https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
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
- 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
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
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
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
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
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
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
Adds two endpoints:
POST /api/opnsense/nat/fix-webrtc
Fixes WebRTC ICE failures caused by symmetric NAT (the reason Mattermost
calls fail on every VLAN but work through a commercial VPN).
OPNsense default outbound NAT remaps UDP source ports per-destination:
each flow to a different server gets a different external port, so STUN
candidates reported by different servers never match and ICE fails.
Fix: switch outbound NAT to Hybrid mode, then add a UDP static-port
rule for each VLAN subnet. Static-port preserves the source port through
NAT, making STUN candidates consistent regardless of which server reports
them. ICE succeeds, calls work without VPN.
Body: { token, vlans: [{id, name, subnet}], wan_interface: "wan" }
GET /api/opnsense/nat/webrtc-status
Returns current outbound NAT mode and existing static-port rules so the
UI can show whether the fix has been applied.
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
Hard-restores switch_backend.py to d9b6d05 (before this session's changes).
Reverted commits:
4cf7433 Fix per-VLAN DNS: switch to proxy mode, Unbound on :5353
2db5c3b Fix per-VLAN ControlD profiles — use gateway-listener mode
91eaeff Fix local-lan-zone.conf: add server: wrapper + correct local-data syntax
da1d629 Fix .lan NXDOMAIN for custom hostnames (pbx.lan, nas.lan, etc.)
These changes broke a working (mostly) system. Before touching the DNS
and ctrld architecture again, the actual deployed state needs to be
understood first.
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
The gateway-listener approach (ctrld on each VLAN gateway IP:53) requires
Unbound to stop listening on those IPs, but OPNsense has no loopback option
in the Network Interfaces list — only named interfaces (LAN, vlan20, etc.).
Restricting Unbound that way is impractical.
Correct approach: proxy mode — ctrld owns port 53, Unbound moves to a
different port (5353). No interface restrictions needed, no port conflict
regardless of start order, and ctrld sees real client source IPs so
per-VLAN CIDR routing works correctly.
Architecture:
Clients → ctrld (0.0.0.0:53) → ControlD per-VLAN profile
ctrld → Unbound (127.0.0.1:5353) for *.lan / *.local (split-horizon)
Unbound has local-data records for all custom .lan hostnames
Changes:
_build_ctrld_toml: new unbound_port param (default 5353); proxy mode now
adds upstream.local → 127.0.0.1:unbound_port and split-horizon rules
for *.lan / *.local in the listener policy; defaults changed from
deploy_mode="router"/ctrld_port=5354 to deploy_mode="proxy"/ctrld_port=53
CtrldConfig: default deploy_mode="proxy", ctrld_port=53; added unbound_port=5353
_ctrld_generate_opnsense_cmd: proxy mode instructions now say to change
Unbound Listen Port to 5353 in OPNsense GUI (one field change, visible
in Services → Unbound DNS → General) and disable Query Forwarding
All call sites updated to pass unbound_port and use new defaults
OPNsense steps to activate:
1. Services → Unbound DNS → General → Listen Port: 5353 → Apply
2. Services → Unbound DNS → Query Forwarding → disable/remove forward zone
3. Regenerate and push ctrld.toml from DNS Filtering tab
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
The single-localhost-listener architecture (Unbound:53 → ctrld:5354)
fundamentally cannot support per-VLAN ControlD profiles: all queries
arrive at ctrld from Unbound as 127.0.0.1, so ctrld has no way to
distinguish VLANs and routes everything to a single upstream. This
broke Asterisk and IoT isolation — all traffic was hitting the same
ControlD profile regardless of which VLAN it came from.
New architecture when all VLAN profiles have a gateway IP set:
Clients → ctrld on VLAN-gateway-IP:53 → per-VLAN ControlD profile
Unbound stays on 127.0.0.1:53 (loopback only — no port conflict)
ctrld sees real client source IPs → routes correctly per VLAN
ctrld forwards *.lan / *.local → Unbound loopback (local-data)
_build_ctrld_toml changes:
- Detects when all active profiles have a gateway IP
- Generates one [listener.N] per VLAN on its gateway IP:53 instead
of a single [listener.0] on 127.0.0.1:ctrld_port
- Each listener has its own [listener.N.policy] with the correct
upstream.N (that VLAN's ControlD profile)
- Adds upstream.local → 127.0.0.1:53 for .lan/.local split-horizon
- Falls back to single-listener with a clear WARNING comment when
gateways are missing
_ctrld_generate_opnsense_cmd changes:
- Detects which mode was generated and produces matching instructions
- Gateway mode: tells user to restrict Unbound to loopback and
disable Query Forwarding (ctrld is no longer downstream of Unbound)
- Fallback mode: warns that per-VLAN profiles are not working
Required OPNsense change to activate gateway mode:
Services → Unbound DNS → General → Network Interfaces → Loopback only
Services → Unbound DNS → Query Forwarding → disable/remove forward to ctrld
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
Two bugs in the Unbound config file generation:
1. Missing server: wrapper
OPNsense includes /var/unbound/etc/*.conf at the top level of
unbound.conf (via include: or include-toplevel:). Server-level
directives like local-zone: and local-data: must sit inside a
server: {} block — without it they are outside any section and
either silently ignored or rejected by unbound-checkconf.
forward-zone: is a top-level section so forward_to_ctrld.conf
correctly has no wrapper.
Consequence: the original 'local-zone: "lan." static' without a
server: wrapper was never actually applied, meaning the .lan leak
prevention was not working.
2. No local-data records
Even with a correct zone declaration, every .lan name not listed
as local-data gets NXDOMAIN from the static zone. The previous
commit added the local-data records; this commit gives them valid
syntax inside the server: block.
Generated file now looks like:
server:
local-zone: "lan." static
local-data: "switch.mgmt.lan. A <mgmt_ip>"
local-data: "management.lan. A <mgmt_ip>"
local-data: "pbx.lan. A 192.168.50.10"
...
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
After the Unbound/:53 + ctrld/:5354 architecture change, fix-lan-zone
wrote local-lan-zone.conf with only 'local-zone: "lan." static' and no
local-data records. Unbound then returned NXDOMAIN for every .lan name
not explicitly listed — including pbx.lan and any hostname in
local-hostnames.json — because the static zone intercepts all .lan
queries before they can reach dnsmasq.
Fix:
- Add _build_unbound_lan_zone_conf(entries, mgmt_ip) which builds a
complete local-lan-zone.conf: the static zone declaration plus
local-data A records for every entry in local-hostnames.json and the
two built-in management aliases (switch.mgmt.lan, management.lan).
- Update fix-lan-zone to use this helper instead of the bare zone-only
string. Running fix-lan-zone now also pushes all saved hostnames.
- Update save_local_hostnames to push the updated local-lan-zone.conf
to Unbound via SSH and reload if OPNsense SSH is configured, so
adding/editing hostnames in the DNS tab takes effect immediately
without a separate fix-lan-zone call.
https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
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
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
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
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
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
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
- 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
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
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
- 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
- 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
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
- 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
- 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
- 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
- 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
- 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
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
- 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
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