Documentation
¶
Overview ¶
Package tracker implements a per-rule sliding-window strike counter.
State is sharded across N small maps each with its own mutex, keyed by a cheap hash of the IP. The single-mutex pattern in v1 was fine but became a gratuitous contention point as soon as we started imagining concurrent rules sharing a source; sharding removes that ceiling at no cost when only one goroutine is calling Hit (one shard is just as fast as the v1 mutex).
Each Hit records a single offence by an IP. The Hit returns true when the IP has accumulated at least Threshold offences within the FindTime window — the caller is then expected to ban the IP and call Reset for that IP.
A Sweep operation drops records whose newest strike is older than FindTime so the map doesn't grow unboundedly under sustained attack.
Index ¶
- type Tracker
- func (t *Tracker) FindTime() time.Duration
- func (t *Tracker) Hit(addr netip.Addr) bool
- func (t *Tracker) HitAt(addr netip.Addr, eventTime time.Time) bool
- func (t *Tracker) Load(r io.Reader) error
- func (t *Tracker) Reset(addr netip.Addr)
- func (t *Tracker) Save(w io.Writer) error
- func (t *Tracker) Size() int
- func (t *Tracker) Snapshot() map[netip.Addr]int
- func (t *Tracker) Sweep() int
- func (t *Tracker) Threshold() int
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Tracker ¶
type Tracker struct {
// Now is the clock; tests inject a fake. Defaults to time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Tracker counts strikes per IP within a sliding time window.
func (*Tracker) Hit ¶
Hit registers a single offence at wall-clock time and returns true when the strike count within the sliding window has reached the threshold. The caller is responsible for calling Reset after a successful ban so the record is cleared.
Thin shim around HitAt for callers that don't have an external event time.
func (*Tracker) HitAt ¶
HitAt registers a single offence with an explicit event time. The strike is recorded at eventTime; pruning still uses wall-clock so backfilled events don't artificially extend the sliding window.
eventTime should be the time the log entry was generated (parsed from the line via the rule's datepattern), not the time GoBan received it. Rules that don't have a datepattern simply pass time.Now() — equivalent to Hit.
func (*Tracker) Load ¶
Load reads a previously-Saved state from r and merges it into the tracker. A version mismatch returns a non-nil error AND leaves the tracker unchanged — callers should log a warning and continue with a clean start.
Load is not concurrency-safe with Hit; callers should invoke it before the daemon starts processing lines (typically right after construction).
func (*Tracker) Save ¶
Save writes the tracker's strike state to w as a gob-encoded blob. Safe for concurrent use with Hit/Reset — each shard's snapshot is taken under its own mutex.
func (*Tracker) Snapshot ¶
Snapshot returns a copy of current strike counts per IP across all shards. Strikes outside the window are pruned in the snapshot view.