Documentation
¶
Overview ¶
Package event implements the event model of §5.1, equation (3).
Equation (3) defines an event as a partial function
e : F ⇀ ⋃_f D_f, with e(f) ∈ D_f where defined
so an event is exactly a set of (field path, value) pairs together with the designated entity ε(e) and timestamp. The set of fields an event carries is dom(e), which §11 identifies with the mask M of the conformal missing-data literature.
Determinism (R4) ¶
This package is where Go's map-iteration randomisation would otherwise leak into scores. Two structural choices prevent it rather than relying on care at each call site:
- The value map is unexported and is never returned. The only way to enumerate an event's fields is Event.All, which yields them in sorted field-path order. Iterating in nondeterministic order is therefore not expressible.
- Float accumulation over fields (equations (5), (18), and the (9) grid) is consequently always performed in the same order, so sums are bit-identical across runs. E8 asserts this.
Timestamps are event time, never wall-clock time. No function in this package, or in any package reachable from the scoring path, may call time.Now; decay in §6.2 and §7.2 is driven by the event timestamp and the state row's own last-observed timestamp. An architecture test enforces the prohibition.
Index ¶
- Constants
- type EntityID
- type Event
- func (e *Event) All() iter.Seq2[FieldPath, Value]
- func (e *Event) Entity() EntityID
- func (e *Event) Get(f FieldPath) (Value, bool)
- func (e *Event) Has(f FieldPath) bool
- func (e *Event) ID() ID
- func (e *Event) Len() int
- func (e *Event) Mask() []FieldPath
- func (e *Event) OccurredAt() Timestamp
- func (e *Event) Offset() int64
- func (e *Event) Source() SourceID
- func (e *Event) With(extra map[FieldPath]Value) Event
- type FieldPath
- type ID
- type SourceID
- type Timestamp
- type Value
Constants ¶
const ( Microsecond Timestamp = 1 Millisecond = 1000 * Microsecond Second = 1000 * Millisecond Minute = 60 * Second Hour = 60 * Minute Day = 24 * Hour )
Microseconds, and the derived units used by the decay factors of §6.2 and §7.2.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EntityID ¶
type EntityID string
EntityID identifies the entity ε(e) that an event concerns.
Which field yields the entity is configuration, not code (§5.1: "A designated field yields the entity ε(e)"). For enterprise authentication telemetry the entity is the individual user account, so that a verdict answers whether this person acted out of character against their own persisted history. A user who habitually departs from the population norm is not thereby anomalous; §7.6 makes that argument for timing specifically, and §6 for categorical attributes. Population-scope structure lives in §8 instead, where role and peer similarity are discovered as block structure.
type Event ¶
type Event struct {
// contains filtered or unexported fields
}
Event is an immutable realisation of equation (3).
Construct one with New. The zero Event carries no fields and is not scoreable.
func New ¶
func New(source SourceID, entity EntityID, occurredAt Timestamp, fields map[FieldPath]Value, offset int64) Event
New builds an event from its fields.
The supplied map is copied, so later mutation by the caller cannot alter the event. Field paths are sorted once here, fixing the canonical order used by every subsequent traversal and every float accumulation.
func (*Event) All ¶
All yields the event's fields in sorted field-path order.
This is the only enumeration this package offers, which is what makes nondeterministic traversal inexpressible rather than merely discouraged.
func (*Event) Mask ¶
Mask returns dom(e) in sorted order: the set of fields the event carries, which §11 identifies with the mask M. The returned slice is a copy, so a caller cannot disturb the canonical order.
func (*Event) OccurredAt ¶
OccurredAt returns the event timestamp, which is the only clock the scoring path is permitted to consult.
func (*Event) Offset ¶
Offset returns the reader's corpus position. Provenance and replay only; it is not part of the content digest and must not reach a score.
func (*Event) With ¶
With returns a copy of the event carrying additional fields.
It exists for derived fields: structure inferred inside a value, such as the /24 of an address or the parent domain of a hostname, is registered as a field beside the original so that every detector scores it unchanged and none of them learns that derivation happened.
The copy carries its OWN identifier, because the identifier is a digest of content and the added fields are content. Two events differing in their fields must not claim to be the same event; a caller wanting the original's identity should keep the original.
An added path that already exists is ignored rather than overwriting: derivation may only add to an event, never restate what a source said. Returning a copy keeps the receiver immutable, so an event already handed to a detector cannot change underneath it (R4).
type FieldPath ¶
type FieldPath string
FieldPath identifies a field. §5.1: F is a countable set of field paths whose members are not known at design time. Paths are opaque to every detector; a detector that named a field would violate R2.
type ID ¶
type ID [32]byte
ID is a content-derived event identifier.
It is a digest of the event's semantic content and nothing else: not of arrival order, not of the batch the event appeared in, and not of wall-clock time. Two byte-identical events therefore carry the same ID however they are batched, which is what lets E8 assert byte-identical scores across differing batch compositions. Corpora containing exact duplicate rows (LANL's redteam.txt has 34) legitimately produce repeated IDs.
type SourceID ¶
type SourceID string
SourceID identifies the telemetry source. Field registries, calibration sets (§10.1) and co-occurrence graphs (§8.2) are all scoped per source, because neither score distributions nor block structure transfer across sources.
type Timestamp ¶
type Timestamp int64
Timestamp is event time in microseconds, on the corpus's own epoch.
It is deliberately a distinct type from any wall-clock representation so that a stray time.Now cannot be assigned to it. LANL's epoch starts at 1 with one-second resolution; the reader multiplies into microseconds so that corpora of finer resolution need no schema change.
type Value ¶
type Value struct {
// contains filtered or unexported fields
}
Value is an immutable field value.
Corpus readers emit values as text; the field registry of §5.1 infers the kind (categorical, boolean, discrete, continuous, identifier, excluded, unknown). This ordering is what discharges R2: nothing in the ingest path needs advance knowledge of a field's type, cardinality, or value set.
The text is never converted to the inferred type. A kind selects how a value is *scored*, not what it is stored as, and the registry's package documentation records why: converting would make the reading depend on the batch (R1), would merge values a source distinguishes, and would commit before the evidence is in.
A value may be present but not interpretable. LANL encodes this as a literal "?" (see DATA.md). §5.3 distinguishes that case from absence: an absent field is not in dom(e) at all, whereas a present-but-uninterpretable field is in dom(e) and yields status abstained_unusable. Collapsing the two would lose the distinction the four-valued status exists to preserve.
func UnusableValue ¶
UnusableValue returns a value that is present in dom(e) but not scoreable. The text is retained so that evidence can report what was actually observed (R5).