Chapter 9
Networking & the Perimeter
See every device on your LAN, read your router's live state, and reach a service from the outside world — with or without opening a single port.
A Mac that runs services lives inside a network, and that network is where most real-world trouble starts: a router whose WAN link flapped, a forgotten device on the wrong subnet, a port you opened months ago and never closed. FrontierStack treats the network and its perimeter — the boundary between your LAN and the internet — as first-class objects. It reads your router directly, scans the LAN to build an inventory, draws a data-flow map, and gives you several disciplined ways to expose a service to the outside.
This chapter covers the network around your servers. For linking the servers themselves over SSH see Chapter 8; for the host firewall, fail2ban and the security audit see Chapter 10; and for Cloudflare DNS and TLS see Chapter 7.
9.1Routers & firewalls you can read and drive
FrontierStack talks to network gear through its own management API where one exists, and over SSH where it does not. The two tightest integrations are OPNsense (its full REST API) and Cloudflare (covered in Chapter 7); a wider set of routers and firewalls report live status. Add a device in the Router & Network pane with its address, vendor and API key, and the AI Administrator's router_info tool can then read its version and model, uptime, CPU and memory, WAN links and gateways (up or down), interfaces and client count — plus the raw API JSON for any field FrontierStack does not map. It is read-only: it queries the device's own API, never SSH.
| Device | How FrontierStack reaches it |
|---|---|
| OPNsense | Full REST API — live status, firewall rules, port-forward (rdr) for the perimeter |
| pfSense | REST via the API package |
| MikroTik RouterOS | REST API (RouterOS v7+) |
| Ubiquiti UniFi / EdgeOS | Controller / gateway API key |
| OpenWrt | LuCI / ubus API |
| DD-WRT | Web admin / SSH |
| ASUSWRT | Router web API / SSH |
| FRITZ!Box | TR-064 / web admin |
| TP-Link Omada | Controller API |
router_info still reports that it is pinned, where, and whether it is reachable — and tells you to add its API key in Router & Network to read version, gateways, WAN and interfaces live.For OPNsense and pfSense, an API key unlocks a continuous firewall monitor (the toggle in the pinned device's pane): every Alerts sweep polls the firewall itself for gateway state, interface carrier and service liveness — so a WAN line that dies while a multi-WAN router silently fails over (servers browse fine, inbound to your sites is dead) raises an alert naming the gateway and the port that lost carrier, instead of an unexplained sea of red site dots. Nothing is installed on the router: OPNsense's REST API is built in, and pfSense needs only its REST API v2 package. The same pane can also join an OPNsense router to your tailnet: sign in to Tailscale on your Mac (Google accounts work), mint a pre-auth key, paste it in the device pane's Tailscale section — FrontierStack installs the os-tailscale plugin if needed, saves the key and login server (Headscale too), and restarts the service over the API, no browser login on the router required.
Attack and failure signals. The same sweep that watches gateways also watches for trouble on the router itself, and a router in distress turns orange with a warning triangle in the sidebar — hover for the reason. On OPNsense with the Intrusion Detection plugin enabled, FrontierStack reads its recent Suricata alerts over the same API and shows the count and top signature (an attempted attack surfaced without opening the firewall UI; pfSense's REST API does not expose IDS uniformly, so this is OPNsense-only for now). Across every monitored router — OPNsense, pfSense, OpenWrt and the rest — each sweep also flags an unexpected reboot (uptime dropped sharply: a crash, a power event, or an attack), a brute-force spike on the router's admin/SSH (repeated failed logins, with the busiest source IP named from the router's own log), resource stress (memory or load dangerously high — a DoS, a runaway or compromised process), and an available firmware update (outdated firmware means known holes). Each raises an alert; signals a given platform cannot report are simply skipped, never a false all-clear. OPNsense and OpenWrt report the most; pfSense's API is more limited.
Driving and diagnosing a router. The AI Administrator can now do more than read: reboot_router restarts an appliance the same way it reboots a server (OPNsense/pfSense via API, OpenWrt over SSH), gated behind "Allow changes"; and when a task is genuinely better done in the router's own console, the assistant offers to open its Web UI with open_web_ui, resolving the address from the device pane rather than guessing. FrontierStack itself deliberately does not edit these appliances' firewall rules — OPNsense/pfSense/OpenWrt manage rules through their own config system, so the pane and the assistant point you at the native firewall page instead (right-click a router ▸ Open firewall rules… deep-links it). Two pane conveniences round this out: a Diagnostics button runs an API-free reachability check (ping, the common admin ports 80/443/8443/53, reverse DNS, gateway) so you can triage a router even without an API key; and for a router or firewall, Discover Services offers to open the Web UI rather than port-scanning it (a scan of your own perimeter device is rarely what you want). If SSH to an OPNsense or pfSense box is refused, SSH is either disabled or — more often — only allowed on certain interfaces (commonly the LAN, not the WAN): reaching it over the LAN, e.g. through a VPN, usually works, while from outside it is typically blocked. Enable SSH and its listening interfaces in the web UI (System ▸ Settings ▸ Administration ▸ Secure Shell on OPNsense). The SSH button itself is only hidden for consumer gateways that have no admin shell at all (NTT, some TP-Link/FRITZ!Box), where the Web UI is the only way in.
9.2Device Discovery: an inventory of your LAN
Open Device Discovery and press Find Monitors (or pick a subnet first). FrontierStack sweeps the chosen /24 using every method available at once, so routers, access points, switches, NAS boxes (Synology, QNAP, ZimaCube/ZimaOS…), printers, cameras and other servers all surface:
- Ping / ARP — an ICMP sweep, with ARP/MAC giving each host's vendor; turn on TCP Probe to find hosts that filter ICMP.
- Bonjour (mDNS) and SSDP / UPnP — advertised services and their friendly names.
- SNMP and LLDP / CDP — add model, port and neighbour details when
net-snmpandlldpdare installed (buttons in the pane install them). - Windows service ports — a machine that filters ICMP is still found by probing its distinctive ports (RDP, WinRM, SMB and friends), so Windows PCs no longer hide from the sweep.
The scan also notes how each host can be reached. An AnyDesk badge appears when AnyDesk is running locally or answering on its default port 7070, and a Tailscale badge — green when the peer is online — is built from this Mac's own tailscale status, matching mesh peers to discovered devices (only endpoints, hostnames and OS are kept; login names are never retained). On a directly-connected LAN, the Macs and devices macOS already knows through Bonjour appear before the slower IP sweep, so the list fills in quickly. A device's detail view carries matching AnyDesk and Tailscale rows with an Open app shortcut.
The AI Administrator's discover_devices tool runs the same scan and auto-pins the devices whose type it can identify confidently. Anything ambiguous is listed back rather than guessed at, so the assistant can ask you what it is and then pin it with pin_device. A pinned (monitored) device appears in its type's sidebar section; double-click it to open its pane, or use Open Web UI, Discover Services, Reclassify or Remove from its row. If you already know an address, the Add a device by IP field probes and pins it directly — no full scan needed.
9.3The Data Map
The Data Map pane draws a per-location data-flow diagram: where your data lives and how it moves across the devices and services at one site. FrontierStack serialises the location's inventory — the discovered devices, their roles, and (optionally) scanned ports — scrubs it of secrets, and hands it to your subscription Claude through the local claude CLI (not the metered API the in-app harness uses). The reply comes back as a Mermaid flowchart plus a short narrative, rendered offline in the pane; the last diagram for each location is kept, and you can save it to your Obsidian vault as a runbook. Locations themselves are explained in Chapter 8.
9.4Opening a port: UPnP and NAT-PMP/PCP
To reach a service from the internet the classic way, a port on the router has to forward to your Mac. Rather than make you log into the router by hand, FrontierStack can ask the router for a mapping using the standard protocols it advertises:
- UPnP IGD (via
miniupnpc) — the common consumer-router method. - NAT-PMP / PCP (via
libnatpmp) — Apple's and the modern equivalent. - OPNsense — a perimeter rule and port-forward through its API directly.
These tools are installed on demand the first time you use them. A mapping made for a temporary session (see Debug Share) is torn down automatically when the session ends.
9.5Dynamic DNS: following a changing IP
Home and small-office connections rarely have a static IP, so a hostname you point at today's address goes stale tomorrow. The Dynamic DNS pane keeps a hostname tracking your current public IP. Add an entry with a Name, a Hostname (for example myhost.duckdns.org) and the account data for your provider; press Update Now to publish immediately, or turn on Update automatically. You choose whether the updater runs only while FrontierStack is running or permanently as a system service, so the record stays current even when the app is closed. Cloudflare users get a tighter path: a Cloudflare DDNS A record that follows your public IP, driven by the same Cloudflare integration as Chapter 7; a generic interval-based updater covers other providers via an update URL.
9.6Tunnels and mesh: reaching in without opening up
The better way to reach a service from outside is to skip inbound ports entirely. A tunnel makes an outbound connection that an outside endpoint rides back in on, traversing NAT with nothing forwarded:
- Cloudflare Tunnel — a Quick Tunnel gives you a public
*.trycloudflare.comURL with no account, or a named tunnel for a permanent hostname. - Tailscale — Serve keeps a service private inside your tailnet; Funnel publishes it to the internet through your Tailscale node.
For ongoing connectivity between machines, a mesh or VPN network gives every node a stable private address wherever it roams. FrontierStack manages the common ones — by API on a router that supports it (OPNsense), or over SSH on a server:
| Network | What it is |
|---|---|
| WireGuard | Modern, fast kernel VPN tunnels — the base layer most of the rest build on |
| Tailscale | WireGuard mesh with exit nodes and subnet routers, set up over SSH or the OPNsense API |
| Headscale | Self-hosted, open-source Tailscale control server |
| NetBird | Open-source zero-trust networking, self-hostable |
| Nebula | Lightweight overlay mesh (Slack / Defined Networking) |
| ZeroTier | Zero-trust SD-WAN / virtual networks — agent plus API |
9.11.1Tailnet: the whole-network view of Tailscale
The per-server Tailscale controls live inside each server's own pane and work over SSH: they answer “is the daemon up on this box”. The Tailnet pane answers the questions that span the whole network, which only Tailscale's own API can: paste an API access token (device read is enough — the pane never writes) and it lists every device with its addresses, OS, client version, owner, tags and last-seen time.
What earns the pane its place is the Needs attention list. It surfaces node keys that are about to expire — a scheduled outage, because when a key lapses the device silently drops off the tailnet until somebody re-authenticates it — devices waiting for approval, devices advertising routes nobody approved (the usual reason a subnet is unreachable even though the node is plainly up), and devices that stopped checking in weeks ago. Key expiry and pending approvals also raise Alerts.
9.11.2Running your own ZeroTier controller
ZeroTier's client is only the member side; networks are normally administered at my.zerotier.com. But every zerotier-one install can also be the controller for its own networks — fully self-hosted, no third party holding your membership list — and that path ships with no local administration UI at all. The ZeroTier Controller pane is that missing interface.
It shows the controller's identity and the networks it owns, and works the member-authorisation queue with one-click authorise/deauthorise. Members authorised but unseen for a fortnight are flagged, because an unused grant is a standing hole in the network. You can edit a network's name, privacy, routes and IP assignment pool, and write flow rules in ZeroTier's own rules language — compiled locally as you type, with templates for the common cases and a warning before you save a rule set that would lock everybody out.
Everything runs over SSH against the controller's own loopback API. The controller listens on 127.0.0.1:9993 and authenticates from a root-readable token, so FrontierStack never needs — and actively discourages — a publicly reachable controller API; the built-in audit flags it if one has been exposed anyway, along with loose file permissions and a missing backup. There is also a Prometheus export, a warm-standby backup/restore, and an assistant for migrating networks out of ZeroTier Central.
identity.secret irreplaceable: lose it and every network ID it owns is orphaned. Back it up encrypted, off the box.9.7UniFi: Cloud Gateways and Dream Machines
Ubiquiti's consoles get their own pane, because a UniFi gateway is doing more than a generic router probe can see. The UniFi pane speaks both of Ubiquiti's APIs, and they answer different questions. A Site Manager key — one key, created at unifi.ui.com — lists every console on your account with its model, firmware and online state. That is the only way to learn a remote site has lost power or its internet connection, because a console that is off the network cannot tell you anything locally. A local console key, created on the console itself under Settings ▸ Admins & Users, unlocks what the gateway is doing right now.
Locally the pane shows WAN links with their ISP, latency and throughput, and marks which link is actually carrying traffic. That last detail matters: when a primary line drops, UniFi fails over silently to the backup, which is usually slower and often metered, and the normal way people discover this is the following month's bill. It also lists adopted devices and their firmware, firewall rules in the order the gateway evaluates them, the client list, and every port forward. Each enabled forward is a deliberate hole through the firewall, so the pane flags the ones that accept connections from any source address — correct for a public web server, worth a second look for anything else.
Alerts cover a console dropping offline, a WAN link going down, running on the backup link, a device leaving the network, and internet-exposed port forwards. The pane is deliberately read-only: rule and network changes stay in the UniFi console, where their validation lives. PoE switch ports are handled in the next section, and plain console reachability still appears in Router & Network alongside the other vendors.
9.8Power over Ethernet: the power button for devices that have none
Access points, cameras, door controllers and desk phones have no power switch. Their only supply is the switch port they are plugged into, which means the recovery move for a wedged one — power-cycle it — normally involves a vendor web UI, or a walk to the cabinet.
The Power over Ethernet pane makes the port the button. Add a managed switch by IP and you get, per port: its PoE state (delivering power, searching, fault), powered-device class, priority and wattage where the switch reports it — plus power off/on and a proper power cycle. It also shows the switch's power budget: total watts, watts in use, and a warning when you cross the switch's own usage threshold, past which it starts shedding low priority ports — so port priority is editable here too.
This works broadly because PoE is standardised: RFC 3621's POWER-ETHERNET-MIB is implemented by essentially every managed PoE switch — Cisco, Aruba/HPE, Netgear, TP-Link/Omada, Ubiquiti, MikroTik, D-Link, Zyxel. Reading uses the SNMP community from Device Discovery. Switching a port needs a read-write community, added per switch and kept in your Keychain: cutting power to whatever is plugged in deserves a deliberate opt-in, and most switches keep that community separate and often disabled until you set it. With Monitor on, a port in fault state or a switch over its power budget raises an Alert — both otherwise stay invisible until someone notices a camera has gone dark.
9.9Internet Health and speed tests
The Internet Health pane is your view of the link itself. It continuously pings a list of Targets (Cloudflare's 1.1.1.1 and others, which you can edit), showing latency and packet loss and re-testing every 30 seconds; hosts that block ICMP fall back to a TCP-connect latency on :443. Live Traffic graphs real-time throughput across every active interface; Addresses & Networks lists each interface, its subnet (a network you can reach directly) and the gateway. A built-in traceroute shows the ISP and transit providers your traffic crosses. Press Test Now in the Speed Test section — or ask the AI with internet_speed — to measure download, upload, latency and responsiveness, using the Ookla speedtest CLI if installed and Apple's networkQuality otherwise.
9.10Debug Share: expose a localhost server, briefly
When you need to show a colleague or a phone a dev server running on localhost, the Debug Share pane exposes it for a session and then closes itself. Enter the port (or press Scan localhost to find a running server), choose how to Expose via, set an Auto-close time, and press Open debug session. Every share also closes automatically when FrontierStack quits.
| Method | Reach |
|---|---|
| Cloudflare Quick Tunnel | Public *.trycloudflare.com URL, no account — ideal for a quick client or phone preview |
| Tailscale Serve | Private — reachable only inside your tailnet |
| Tailscale Funnel | Public, through your Tailscale node (Funnel must be enabled in the tailnet) |
| LAN forwarder | A 0.0.0.0:<auto> → 127.0.0.1:port bridge so other machines on this LAN can reach a loopback-only server |
The LAN forwarder has two extras. If the host firewall is on, the pane shows Open port in firewall to allow that port in the pf rules just for the session. And an Outside access picker can map a WAN port via UPnP, NAT-PMP or the OPNsense API and pair it with a DDNS host, so a LAN share becomes reachable from the public internet — the mapping is removed when the share closes. The AI Administrator can open, list and close sessions too (debug_share_open / _list / _close), including over MCP when "Allow changes" is on.
9.11Named tunnels: a permanent public hostname
A Quick Tunnel is deliberately throwaway. When you want a service on this Mac to be reachable permanently at a real address — a self-hosted app, an internal dashboard, a webhook receiver — use a named tunnel instead. It gives the service a stable hostname on one of your own Cloudflare zones (say app.example.com), survives reboots, and — like every tunnel — opens no inbound port: the Mac dials out to Cloudflare's edge, so there is nothing to forward and nothing for a port scan to find.
Create one in the Cloudflare pane's Named Tunnels section: New Tunnel…, then a hostname under one of your zones and the local port it fronts. FrontierStack drives the whole setup through the Cloudflare API using the token you already hold — it creates the tunnel, writes its ingress rule (hostname → http://localhost:PORT), adds a proxied CNAME pointing the hostname at <id>.cfargotunnel.com, and then installs the persistent daemon with cloudflared service install. That last step needs a one-time admin prompt (it writes a LaunchDaemon under /Library); cloudflared itself is fetched from Homebrew on first use. Untick Run on this Mac now if you only want the tunnel and DNS created, to run cloudflared on some other host. Delete unwinds all of it — daemon, DNS record and the account-side tunnel. The AI Administrator has the same three verbs (tunnel_create / tunnel_list / tunnel_delete), with creating one gated behind a red confirmation because it publishes a service to the internet.
cloudflared service install runs a single system daemon per Mac, so one named tunnel runs locally at a time. You can still create and route additional named tunnels through the app and run their cloudflared connectors on other machines — the account, the hostname and the DNS are all set up either way.FrontierStack User Manual · Version 1.0.0 · Chapter 9