golib/net/ipcohort/README.md
AJ ONeal bea4c3de0a
feat(net/ipcohort): IP block/allow cohort filter
IPv4 host + CIDR membership testing backed by sorted /32 binary search
and a linear scan of CIDR ranges. CSV/text loader with bounded reads
(maxBytes is required so callers pick a limit appropriate to their
input source — 1 KB allowlists through 100+ MB threat feeds). Returns
errors for unparseable IPs so callers can decide fail-open vs
fail-closed; partial cohorts are returned alongside parse errors.
2026-05-02 15:41:38 -06:00

37 lines
1.2 KiB
Markdown

# [ipcohort](https://github.com/therootcompany/golib/tree/main/net/ipcohort)
A memory-efficient, fast IP cohort checker for blacklists, whitelists, and ad cohorts.
- 4 bytes per /32 host, 5 bytes per CIDR range (8 with alignment padding)
- O(log n) binary search across both hosts and CIDR ranges
- immutable cohorts — callers swap via `atomic.Pointer` for lock-free reads
- requires CIDR sources to be an anti-chain (no entry contained in another;
a /8 supersedes any /24 inside it). Most curated blocklists already
ship pre-normalized this way.
## Example
Check if an IP address belongs to a cohort (such as a blacklist):
```go
cohort, err := ipcohort.LoadFile("/srv/data/inbound.txt")
if err != nil {
log.Fatalf("load: %v", err)
}
blocked, err := cohort.Contains("92.255.85.72")
if err != nil {
log.Fatalf("parse: %v", err)
}
if blocked {
fmt.Println("BLOCKED")
os.Exit(1)
}
fmt.Println("allowed")
```
`Cohort.Contains(string)` parses the address each call and returns an error
on unparseable input — callers decide fail-open vs fail-closed. If you
already have a `netip.Addr` (e.g. from `netip.ParseAddr` on a request peer),
use `Cohort.ContainsAddr(netip.Addr)` to skip the parse and the error.