Documentation
¶
Overview ¶
Package activity estimates external API network activity with keyed, epoch-scoped pseudonyms instead of retaining raw request identifiers. The colocated HMAC key is the privacy boundary: anyone who compromises both the database and key can enumerate the IPv4 input space and recover the IPv4 addresses represented by retained buckets. Raw addresses and user agents otherwise exist only for request classification and bucket derivation.
Index ¶
- Constants
- Variables
- func ExternalRequestAddress(r *http.Request) (netip.Addr, bool)
- type Bucket
- type Config
- type Counts
- type DailyRaw
- type DailyWindow
- type DayCount
- type DayPoint
- type MaintenanceStore
- type Metrics
- type Store
- type Telemetry
- type Tracker
- func (t *Tracker) Available() bool
- func (t *Tracker) Close(ctx context.Context) error
- func (t *Tracker) ConfigurationInvalid() bool
- func (t *Tracker) MarkOwner(ctx context.Context, r *http.Request, now time.Time) error
- func (t *Tracker) Metrics(ctx context.Context, now time.Time) (Metrics, error)
- func (t *Tracker) Telemetry() Telemetry
- func (t *Tracker) Wrap(next http.Handler) http.Handler
Constants ¶
const ( KindDay = "day" KindMonth = "month" // DailyWindowDays is the length of the daily chart. It is deliberately // shorter than the 35 retained daily epochs so the newest end of the // chart can never be truncated by a prune that runs mid-render. DailyWindowDays = 31 )
Variables ¶
var ( ErrInvalidKey = errors.New("activity hash key invalid") // ErrNoNetworkIdentity means the trust boundary refused to derive an // identity for this request — the fail-closed outcome, not a fault. ErrNoNetworkIdentity = errors.New("activity network identity not derivable") )
Functions ¶
func ExternalRequestAddress ¶ added in v0.1.15
ExternalRequestAddress exposes the same fail-closed proxy boundary to private operational features that need to identify an internal machine. Callers must not log or publish the returned address.
Types ¶
type Bucket ¶
Bucket is the complete persistence boundary. It intentionally has no raw address, header, route, or stable identifier field.
type Config ¶
type Config struct {
HashKeyHex string
QueueSize int
BatchSize int
SeenLimit int
FlushEvery time.Duration
MaintenanceEvery time.Duration
DBTimeout time.Duration
Now func() time.Time
// contains filtered or unexported fields
}
Config exposes small test seams while production uses bounded defaults.
type DailyRaw ¶
type DailyRaw struct {
Days []DayCount
// OldestEpoch is the oldest retained daily epoch, "" when none exists.
OldestEpoch string
}
DailyRaw is the unshaped store result behind the daily chart.
type DailyWindow ¶
DailyWindow is exactly DailyWindowDays points, oldest first.
func BuildDailyWindow ¶
func BuildDailyWindow(now time.Time, raw DailyRaw) DailyWindow
BuildDailyWindow shapes stored epochs into a fixed-length chart that never invents a zero. Days older than the oldest retained epoch are marked BeforeCollection rather than counted as zero activity, and days at or after it that stored nothing are marked as gaps.
type DayCount ¶
DayCount is one stored daily epoch. Count excludes owner buckets; Rows includes them, so a day the operator alone touched is still provably collected rather than being reported as a gap.
type DayPoint ¶
DayPoint is one column of the daily chart. BeforeCollection marks a day older than anything retained — the chart cannot claim zero there, only silence. Gap marks a day inside the collected range that stored nothing, which is a real reporting gap and is shown as one.
type MaintenanceStore ¶
type MaintenanceStore interface {
MarkActivityHealthy(ctx context.Context, now time.Time) error
PruneActivity(ctx context.Context, now time.Time) error
}
MaintenanceStore is deliberately narrower than Store. Server startup and periodic retention remain available even if collection or dashboard method wiring changes independently.
type Metrics ¶
type Metrics struct {
Counts Counts
Daily DailyWindow
Telemetry Telemetry
}
type Store ¶
type Store interface {
RecordActivity(ctx context.Context, buckets []Bucket) error
ActivityCounts(ctx context.Context, dayEpoch, monthEpoch string) (Counts, error)
// ActivityDaily reports the retained daily epochs inside the inclusive
// [fromEpoch, toEpoch] range, plus the oldest daily epoch still retained
// anywhere in the table. The oldest epoch may predate fromEpoch, which is
// exactly how a full chart is distinguished from one whose left edge is
// simply older than collection itself.
ActivityDaily(ctx context.Context, fromEpoch, toEpoch string) (DailyRaw, error)
}
Store is optional and deliberately separate from serverstore.Store.
type Telemetry ¶
Telemetry is the collector's own health. Flushes counts batches persisted successfully; StoreFailures counts persistence errors; Dropped counts observations that never reached storage, whether the queue was saturated or a flush failed. Every one of them makes the reported estimate a floor.
type Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
func New ¶
New validates a dedicated 256-bit key. Store maintenance starts whenever a store is available, independently of whether collection is configured, so retention and daily health markers never depend on possession of the key.
func NewWithMaintenance ¶
func NewWithMaintenance(ctx context.Context, store Store, maintenanceStore MaintenanceStore, cfg Config) *Tracker
NewWithMaintenance allows server wiring to retain maintenance even when the broader optional collection Store contract is unavailable.
func (*Tracker) Close ¶
Close first closes the admission gate, then waits for the worker to drain both its staged batch and every observation that crossed the gate. A caller may retry Close with a fresh context after a timeout; Pending continues to include every item the worker has not yet persisted or recorded as dropped.
func (*Tracker) ConfigurationInvalid ¶
func (*Tracker) MarkOwner ¶
MarkOwner synchronously upserts the authenticated admin request's current buckets. A later queued non-owner observation cannot undo owner=true.