traceattr

package
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package traceattr resolves the identity half of a sing-box connection record: which Lattice line an inbound tag belongs to, and which Lattice user a sing-box user name reverses to.

It is deliberately pure logic over an injected snapshot (SINGBOX-TRACE-DESIGN section 4.4). The caller owns every store read; this package owns the rules, so the rules can be tested without a server.

The whole point of the package is the honesty discipline. Every field it writes is either something the snapshot proved or nothing at all. There is no fuzzy tag matching and no "there is only one user on that line, so it must be them" fallback: a record that cannot be placed is marked so it gets retried after the topology refreshes, because a wrong answer written once is indistinguishable from a right one forever after.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Attributor

type Attributor struct {
	// contains filtered or unexported fields
}

Attributor applies one topology snapshot to records.

func New

func New(t Topology) *Attributor

New returns an Attributor over t. Nil maps are legal and simply resolve nothing, which is the correct behaviour for a cold server that has not built its first snapshot yet.

func (*Attributor) Attribute

func (a *Attributor) Attribute(r *model.ConnRecord)

Attribute fills LineUUID, LineHashID, UserID, and UserKind on r in place.

The output depends only on the record's raw fields (NodeID, InboundTag, UserName) and the snapshot, never on what a previous run wrote. That purity is what makes the retry path safe: re-running a record against a fresher topology cannot leave a half-updated mix of two snapshots behind.

func (*Attributor) AttributeAll

func (a *Attributor) AttributeAll(rs []model.ConnRecord) Result

AttributeAll attributes every record in place and reports the split.

func (*Attributor) NeedsRetry

func (a *Attributor) NeedsRetry(r model.ConnRecord) bool

NeedsRetry reports whether r should be attributed again after the topology refreshes.

Both failure modes are expected on a cold or just-restarted server rather than permanent: the line read model is behind a 60s cache, and the sing-box inventory is memory-only and lost on restart. A record captured inside that window must self-heal on a later pass instead of having the gap baked into it as a wrong answer.

func (*Attributor) Topology

func (a *Attributor) Topology() Topology

Topology returns the snapshot this Attributor was built with, so a caller can report how stale an attribution run was. The maps are shared, not copied; treat the result as read-only.

type LineRef

type LineRef struct {
	LineUUID   string
	LineHashID string
}

LineRef is the pair of line identities a record carries. LineUUID is the durable control-plane identity; LineHashID is the read-model handle the rest of the server indexes by.

type NodeTag

type NodeTag struct {
	NodeID string
	Tag    string
}

NodeTag is the join key from a log line to a line: sing-box logs an inbound tag, and a tag is only unique within one node.

type Result

type Result struct {
	Attributed int
	Unresolved int
}

Result counts one AttributeAll run. Attributed plus Unresolved always equals the number of records passed in: Unresolved counts exactly the records NeedsRetry would return true for, so the caller can size its retry queue from this alone.

type Topology

type Topology struct {
	// LinesByNodeTag maps (node_id, inbound_tag) to the line that tag serves.
	LinesByNodeTag map[NodeTag]LineRef
	// UserIDByName maps the on-box u_<16hex> name to a Lattice user id. It is
	// the same reversal userLineNameIndex already performs for per-user stats;
	// there is exactly one naming scheme and this consumes it.
	UserIDByName map[string]string
	// BuiltAt is when the snapshot was taken. The line read model is behind a
	// 60s cache and the sing-box inventory is memory-only, so knowing the age of
	// the answer matters when explaining why a record is still unresolved.
	BuiltAt time.Time
}

Topology is an immutable snapshot the caller builds from the line read model and the user-name index. Callers must not mutate the maps after handing them to New: an Attributor is read-only and may be shared across goroutines, and the refresh path is meant to build a new snapshot rather than edit a live one.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL