From 7414ef4c39194d906caaf560aa05d7544ef942b2 Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 6 Aug 2026 01:16:57 +0400 Subject: [PATCH] config: the LAN scanner joins house.go (V-410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure move, with one comment corrected on the way. NetScanConfig.Rate said "0 ⇒ 50"; netscan.DefaultRate is 100 and has been since the package was written, and deploy/mavend.json sets 100 explicitly. The three sibling defaults now name the constant they come from rather than restating a number that can drift again. Co-Authored-By: Claude Opus 5 --- internal/config/config.go | 59 ++-------------------------------- internal/config/house.go | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 56 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 92ded93..d19a52f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -24,7 +24,6 @@ import ( "github.com/kami/maven/internal/delivery/ntfysink" "github.com/kami/maven/internal/delivery/telegramsink" "github.com/kami/maven/internal/morning" - "github.com/kami/maven/internal/netscan" "github.com/kami/maven/internal/update" "github.com/kami/maven/internal/vision" "github.com/robfig/cron/v3" @@ -285,51 +284,6 @@ type Config struct { NetScan *NetScanConfig `json:"netscan,omitempty"` } -// NetScanConfig — the LAN scanner block (Vikunja #257). Dark until -// `"enabled": true`. -// -// The important field is Subnets, and it is the ONLY source of a scan target. -// Nothing an utterance, a router or a scanned host says can widen or move the -// range: internal/netscan.Scanner.Scan takes no target argument at all. Each -// subnet must be private and no larger than netscan.MaxPrefixHosts addresses -// (a /22), enforced at config load rather than at the first spoken scan. -type NetScanConfig struct { - // Subnets — CIDRs to scan, "192.168.1.0/24". - Subnets []string `json:"subnets,omitempty"` - - // Ports — TCP ports to try per host. Empty ⇒ 22, 80, 443, 8080. - Ports []int `json:"ports,omitempty"` - - // Timeout — per-connection budget. 0 ⇒ 400ms. - Timeout Duration `json:"timeout,omitempty"` - - // Rate — connections per second across the whole scan. 0 ⇒ 50. Low on - // purpose: a scan should look like background traffic, not a portscan. - Rate int `json:"rate,omitempty"` - - // MaxHosts — cap on addresses probed per scan. 0 ⇒ 256. - MaxHosts int `json:"max_hosts,omitempty"` - - // Enabled — false (the default) keeps a written block dark. - Enabled bool `json:"enabled,omitempty"` -} - -// NetScanner maps the config block onto the netscan package's own type. -// ok=false when absent or disabled, so validation and daemon wiring cannot -// drift on the mapping. -func (c *Config) NetScanner() (netscan.Config, bool) { - if c.NetScan == nil || !c.NetScan.Enabled { - return netscan.Config{}, false - } - return netscan.Config{ - Subnets: c.NetScan.Subnets, - Ports: c.NetScan.Ports, - Timeout: time.Duration(c.NetScan.Timeout), - Rate: c.NetScan.Rate, - MaxHosts: c.NetScan.MaxHosts, - }, true -} - // PraxisConfig — maven's connection to the Praxis attention service. type PraxisConfig struct { // URL — the Praxis HTTP API base URL (e.g. "http://localhost:9742"). @@ -1286,10 +1240,7 @@ func (c *Config) applyDefaults() { c.normaliseSmartHome() - // Same rule for the scanner. - if c.NetScan != nil && !c.NetScan.Enabled { - c.NetScan = nil - } + c.normaliseNetScan() // Same rule for the crawler: a block that neither answers on demand nor // watches anything has nothing to do, so it is normalised to "off". @@ -1445,12 +1396,8 @@ func (c *Config) validate() error { if err := c.validateSmartHome(); err != nil { return err } - // A scanner pointed at the public internet, or at a /8, fails here rather - // than after the packets have already left. - if nc, ok := c.NetScanner(); ok { - if err := netscan.Validate(nc); err != nil { - return err - } + if err := c.validateNetScan(); err != nil { + return err } // A media dir that cannot be created, or a vision endpoint that is a typo, // used to be logged at wiring time and the capability just stayed off. A diff --git a/internal/config/house.go b/internal/config/house.go index 97ee0a6..9f9445f 100644 --- a/internal/config/house.go +++ b/internal/config/house.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/kami/maven/internal/netscan" "github.com/kami/maven/internal/smarthome" ) @@ -107,3 +108,69 @@ func (c *Config) validateSmartHome() error { } return smarthome.Validate(hc) } + +// NetScanConfig — the LAN scanner block (Vikunja #257). Dark until +// `"enabled": true`. +// +// The important field is Subnets, and it is the ONLY source of a scan target. +// Nothing an utterance, a router or a scanned host says can widen or move the +// range: internal/netscan.Scanner.Scan takes no target argument at all. Each +// subnet must be private and no larger than netscan.MaxPrefixHosts addresses +// (a /22), enforced at config load rather than at the first spoken scan. +type NetScanConfig struct { + // Subnets — CIDRs to scan, "192.168.1.0/24". + Subnets []string `json:"subnets,omitempty"` + + // Ports — TCP ports to try per host. Empty ⇒ netscan.DefaultPorts + // (22, 80, 443, 8080). + Ports []int `json:"ports,omitempty"` + + // Timeout — per-connection budget. 0 ⇒ netscan.DefaultTimeout (400ms). + Timeout Duration `json:"timeout,omitempty"` + + // Rate — connections per second across the whole scan. 0 ⇒ + // netscan.DefaultRate (100). Low on purpose: a scan should look like + // background traffic, not a portscan. + Rate int `json:"rate,omitempty"` + + // MaxHosts — cap on addresses probed per scan. 0 ⇒ netscan.DefaultMaxHosts + // (256). + MaxHosts int `json:"max_hosts,omitempty"` + + // Enabled — false (the default) keeps a written block dark. + Enabled bool `json:"enabled,omitempty"` +} + +// NetScanner maps the config block onto the netscan package's own type. +// ok=false when absent or disabled, so validation and daemon wiring cannot +// drift on the mapping. +func (c *Config) NetScanner() (netscan.Config, bool) { + if c.NetScan == nil || !c.NetScan.Enabled { + return netscan.Config{}, false + } + return netscan.Config{ + Subnets: c.NetScan.Subnets, + Ports: c.NetScan.Ports, + Timeout: time.Duration(c.NetScan.Timeout), + Rate: c.NetScan.Rate, + MaxHosts: c.NetScan.MaxHosts, + }, true +} + +// normaliseNetScan applies the block's defaults. Same rule as the house: not +// enabled is the same as no block at all. +func (c *Config) normaliseNetScan() { + if c.NetScan != nil && !c.NetScan.Enabled { + c.NetScan = nil + } +} + +// validateNetScan fails a scanner pointed at the public internet, or at a /8, +// here rather than after the packets have already left. +func (c *Config) validateNetScan() error { + nc, ok := c.NetScanner() + if !ok { + return nil + } + return netscan.Validate(nc) +}