Skip to content

Put devices named by hardware address in their rule sets - #412

Merged
drudge merged 1 commit into
mainfrom
claude/per-device-blocking-0lzpfb
Oct 8, 2026
Merged

drudge merged 1 commit into
mainfrom
claude/per-device-blocking-0lzpfb

Conversation

@drudge

@drudge drudge commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Requested by Nick · project thread

Before: a device's rule_set applied only to [[clients]] entries that name an IP or network. A device named by mac, which is how the Insights drawer names devices, kept its rule set in config but blocked with the default policy.

After: a device named by mac gets its rule set at every address Sable has tied to that hardware address, whether the tie came from the neighbor table, UniFi, or the cluster lead (so replicas in Docker work too). An address follows whichever device used it last. With Insights off, Sable doesn't tie addresses to hardware, so only configured addresses apply, as #240's Decisions say.

This is PR 2 of #240. The "matched by" view in the device drawer comes with the console PR; Handler.DeviceAddressTable() is what it will read.

How

  • A table beside the runtime. dnsserver.DeviceAddresses (address → rule set name) sits in its own atomic.Pointer on the handler, so a new DHCP lease or IPv6 privacy address swaps a small map without recompiling the block lists. Precedence: an address named in config, then a learned address, then the most specific network.
  • Rebuild worker. internal/app/device_rule_sets.go wraps the three identity recorders (neighbor sampler, UniFi, cluster heartbeat) and also wakes on config changes. It reads identities over the Insights lookback and walks them newest first, so each address goes to its latest device. While no device by hardware address has a rule set, it reads nothing.
  • Insights off empties the table. The refresh runs after the recorder settings change on each config apply.

Benchmarks

BenchmarkPolicyDecision, 100k domains, 200k iterations, median of 5 on a shared, noisy container:

ns/op allocs
NoRuleSets 128 0
OutsideRuleSets 159 0
InRuleSet 242 0
LearnedDevice 245 0

TestPolicyDecisionDoesNotAllocateWithRuleSets now includes a learned address.

Checks

go tool mage verify passes and gofmt -l . is clean. The new worker test passes under -race -count=3.

Refs #240


Generated by Claude Code

A [[clients]] entry with a mac and a rule_set now applies at every
address Sable has tied to that hardware address, from the neighbor
table, UniFi, or the cluster lead. An address follows whichever device
used it last.

The table sits beside the runtime in the handler, so a new lease or
IPv6 privacy address swaps a small map instead of recompiling the
block lists. A worker rebuilds it from the store after each batch of
identities and each configuration change, and does nothing while no
device by hardware address has a rule set. With Insights off it stays
empty, so only configured addresses apply, as #240 decided.

Refs #240
@drudge drudge self-assigned this Oct 8, 2026
@drudge
drudge marked this pull request as ready for review October 8, 2026 20:27
@drudge
drudge merged commit 2ec6da8 into main Oct 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant