Expand description
Would the OS firewall let LAN peers reach us? Pure rule logic, no OS calls.
SPEC_LAN_FIREWALL_SETUP_2026_10_01.md 4.2 and 4.3. On 2026-09-30 narko had LAN enabled, started its listeners, registered its mDNS service, logged no warning, and never saw a peer, because Windows Defender Firewall had no inbound allow rule for the active Private profile (and 606 stale per-path rules, none of them for the running binary). Nothing in AgentMux could say so.
This module decides, from a snapshot of the firewall rules and the adapters’
network categories, whether each interface is covered, and what the status
bar should say. Reading the snapshot is OS-specific and lives elsewhere
(lan_firewall_windows.rs); keeping the decision pure means the cases that
actually bit us are unit tests that run on every platform.
Windows semantics encoded here:
- Inbound traffic is blocked unless a matching enabled allow rule exists, and an enabled matching block rule beats any allow.
- A rule applies per network profile (Domain, Private, Public), and each adapter’s connection has its own category.
- A rule naming a program matches that exact path. The sidecar’s file name
embeds its version (
agentmux-srv-0.59.1-windows.x64.exe) and portable and dev builds sit at a new path every time, so a per-program rule stops matching on every update (spec R7). A port rule does not.
Structs§
- Adapter
- A network adapter and the category of the network it is connected to.
- FwRule
- One firewall rule, with the fields that decide whether it affects us.
- LanFirewall
laninstances:firewall: what the OS firewall would do to LAN peers reaching this machine.- LanFirewall
Adapter - One adapter in a
LanFirewallverdict. - Need
- Something the running srv must be reachable on from the LAN.
- Profile
Settings - One profile’s own switches, which decide whether any rule is needed at all
(ReAgent P1 on #4151: judging from rules alone reported
needs-setupon a profile whose firewall is off, or whose default inbound action is Allow). - Profiles
- Report
- Snapshot
- Everything read from the OS in one go.
Enums§
- Adapter
State - Applies 🔒
- Does a rule apply to
needon this adapter? Three answers, not two. - Category
- A network’s category, as
INetworkListManagerreports it. - Coverage
- Firewall
Status - What the status bar shows about the firewall (spec 4.3).
- Local
Scope - A rule’s
LocalAddresses: which of OUR addresses it applies to. A rule limited to one local IP must only count for the adapter that owns it (Codex P2 on #4151); treating it as “every adapter in the profile” could make an uncovered adapter look covered, or turn an address-scoped block into a false global block. - Port
Spec - A rule’s
LocalPorts. Anything we cannot read exactly isUnparseableand matches nothing: claiming coverage we cannot prove is the worse error. - Proto
- Remote
- A rule’s
RemoteAddresses, reduced to the one question that matters here: does it reach LAN peers in general?
Constants§
- PROFILE_
DOMAIN NET_FW_PROFILE2_*bits.- PROFILE_
PRIVATE - PROFILE_
PUBLIC - WATCH_
TICK 🔒 - How often the firewall is read while LAN discovery is wanted. A read is cheap (about 60 ms for 1,300 rules on narko) and a changed rule or network should show promptly.
Functions§
- adapter_
state_ with - applies_
to 🔒 - category_
wire 🔒 - coverage_
with - Does
category’s profile let LAN peers reachexeon every need? - lan_
needs - The inbound traffic a LAN peer needs: our web and ws ports (TCP), the mDNS
port (UDP 5353) and the UDP broadcast-probe responder that mobile clients
use when mDNS is filtered (
lan_discovery::UDP_DISCOVERY_PORT, spec 4.1 puts it in the same rule as 5353; Codex P2 on #4151). - overall
- The overall status from every adapter. One covered adapter is enough for “ok”: a host also has virtual adapters (VMware, WSL, Hyper-V) that are rightly uncovered, and they must not flag a machine whose real LAN works. Otherwise the most actionable problem wins: a block, then a managed policy (no helper can help), then missing rules, then an untrusted Public network.
- payload
- The
laninstances:firewallevent body. - read_
snapshot - report
- rule_
applies 🔒 - An ALLOW counts only when every field is evaluable and says yes: counting one
we cannot evaluate would claim coverage we cannot prove. A BLOCK is the
opposite case, handled in
coverage_with: skipping one we cannot evaluate would lean towardCovered, the very direction the unreadable-means-unknown rule forbids (ReAgent on #4151). - same_
path - Compare two Windows paths: case-insensitive,
/equals\, the\\?\prefix ignored. Rules that use environment variables (%SystemRoot%) do not match: coverage is only claimed when it can be shown. - should_
publish - Whether this status goes to the windows on this tick. A standing problem is
re-sent every tick so a window opened later, or a reconnected WebSocket,
learns of it: the frontend only learns from events and cannot ask (the same
lesson as
lan_mdns_health::should_publish, ReAgent P1 on #4148).OkandUnknowngo out only on change. - spawn_
watcher - Watch the OS firewall while LAN discovery is wanted and tell the windows what
it found, via
laninstances:firewall. Only Windows can read its firewall today (macOS and Linux are later PRs): elsewhere this does nothing, so the status bar simply has no firewall verdict. - state_
wire 🔒