Documentation
¶
Overview ¶
Package engine answers the agent's log queries over masked records: it validates a query, builds a byte plan from the range and trusted selectors only, admits it against the per-call budgets, masks every record of each planned granule before any predicate runs, and stops at plan-chunk boundaries at its deadline, with a sealed cursor to continue.
Admission, charges, stopping points, cursors, `scanned` and `covered_until` are functions of the plan, never of matches (design §3.3, §4.4): the work of a call depends on the bytes and their detector hits, not on the agent's predicates.
Index ¶
- Constants
- Variables
- func Template(masked string) string
- func WithClearSink(ctx context.Context, s *ClearSink) context.Context
- func WithExposureCount(ctx context.Context, f ExposureCount) context.Context
- func WithPeer(ctx context.Context, p Peer) context.Context
- func WithSourceLog(ctx context.Context, l func(line string)) context.Context
- type Approval
- type Approver
- type BudgetStatus
- type Budgets
- type ClassBudget
- type ClearMasker
- type ClearSink
- type ContextQuery
- type ContextResult
- type CostModel
- type CountQuery
- type CountResult
- type Engine
- func (e *Engine) BudgetStatus() BudgetStatus
- func (e *Engine) BudgetStatusFor(ctx context.Context) BudgetStatus
- func (e *Engine) ClearRecords(ctx context.Context, sink *ClearSink, refs []string) []string
- func (e *Engine) Context(ctx context.Context, cq ContextQuery) (ContextResult, error)
- func (e *Engine) Count(ctx context.Context, q CountQuery) (_ CountResult, retErr error)
- func (e *Engine) Handle(ctx context.Context, method string, params json.RawMessage) (any, *Error, error)
- func (e *Engine) Lookup(ctx context.Context, q LookupQuery) (LookupResult, error)
- func (e *Engine) Paced(ctx context.Context, src string) (context.Context, context.CancelFunc)
- func (e *Engine) Patterns(ctx context.Context, q PatternsQuery) (_ PatternsResult, retErr error)
- func (e *Engine) RecordDigest(raw string) string
- func (e *Engine) Ref(ref string) (source.RawRef, string, error)
- func (e *Engine) RefRecord(ref string) (key, epoch string, ok bool)
- func (e *Engine) Scan(ctx context.Context, q ScanQuery) (ScanResult, error)
- func (e *Engine) Search(ctx context.Context, q Query) (SearchResult, error)
- func (e *Engine) String() string
- func (e *Engine) Tail(ctx context.Context, tq TailQuery) (TailResult, error)
- func (e *Engine) Trace(ctx context.Context, tq TraceQuery) (SearchResult, error)
- func (e *Engine) Unmask(ctx context.Context, q UnmaskQuery) (UnmaskResult, error)
- type Error
- type Estimate
- type ExposureCount
- type Group
- type Limits
- type LookupQuery
- type LookupResult
- type Lookuper
- type MaskFunc
- type Masker
- type Masking
- type Options
- type PatternGroup
- type PatternsQuery
- type PatternsResult
- type Peer
- type Pred
- type Query
- type ScanInfo
- type ScanQuery
- type ScanResult
- type Scanned
- type Sealer
- type SearchResult
- type SourceBudget
- type TailQuery
- type TailResult
- type TraceQuery
- type UnmaskQuery
- type UnmaskResult
- type UnmaskValue
Constants ¶
const ( MaxBy = 2 MaxBuckets = 1000 // MaxGroups bounds distinct (bucket, by) groups; beyond, records // count under an "⟦other⟧" key. Distinct counts are exact up to it. MaxGroups = 100000 // MaxGroupBytes bounds the memory of one count's groups (keys are // masked values of any length): beyond, records count under // "⟦other⟧" as beyond MaxGroups. MaxGroupBytes = 8 << 20 )
Count limits.
const ( DefaultScanTTL = 30 * time.Minute DefaultScanDeadline = 240 * time.Second DefaultScanBytes = 64 << 30 DefaultScanRange = 400 * 24 * time.Hour )
Scan defaults (design §3.3).
const ( MethodSearch = "log.search" MethodCount = "log.count" MethodPatterns = "log.patterns" MethodContext = "log.context" MethodTrace = "log.trace" MethodTail = "log.tail" )
Agent methods served by the engine (IPC log.*).
const ( DefaultTop = 20 MaxTop = 100 // MaxTemplateTokens and MaxTemplateBytes bound a template: beyond, it // ends with TemplateCut. MaxTemplateTokens = 64 MaxTemplateBytes = 512 // Variable replaces a variable part of a template. Variable = "<*>" // TemplateCut ends a template cut at its bounds. TemplateCut = "…" )
Pattern limits.
const ( EBadQuery = "E_BAD_QUERY" EUnknownField = "E_UNKNOWN_FIELD" EPIIInQuery = "E_PII_IN_QUERY" EOverBudget = "E_OVER_BUDGET" ESource = "E_SOURCE" // EMask is reserved: a record masking fails on is withheld and // counted in masked_failures, never refused (see run). EMask = "E_MASK" EPolicyChanged = "E_POLICY_CHANGED" EStale = "E_STALE" // EApproval: the call needs a human approval this console cannot ask // (no approver on the call). EApproval = "E_APPROVAL" // EUnmaskNever: the profile's unmask = "never" (the production // default) refuses every unmask. EUnmaskNever = "E_UNMASK_NEVER" // EBusy: the console runs its most calls at once (limits.max_calls); // the call did no work and can be retried. EBusy = "E_BUSY" )
Refusal codes (stable, documented for agents).
const ( OpEq = "=" OpNe = "!=" OpIn = "in" OpExists = "exists" OpLt = "<" OpLe = "<=" OpGt = ">" OpGe = ">=" OpContains = "contains" OpMatches = "matches" )
Operators.
const ( MaxPattern = 256 // bytes of a regex MaxNeedle = 256 // bytes of a contains/text needle MaxPredicates = 32 MaxFields = 64 MaxInValues = 64 MaxSources = 32 DefaultLimit = 100 )
Limits of a query.
const ( ScanSearch = "search" ScanCount = "count" ScanPatterns = "patterns" ScanTrace = "trace" )
The tools an approved scan runs.
const ( MaxTailDuration = 50 * time.Second DefaultTailMax = 100 // DefaultTailPoll is the interval between polls of a tail. DefaultTailPoll = time.Second )
Tail bounds (design §3.1).
const ( MethodUnmask = "log.unmask" MethodLookup = "log.lookup" )
Methods of the approved and rate-limited value tools.
const ( UnmaskNever = "never" UnmaskApproved = "approved" )
Unmask modes (config: unmask).
const ( MaxUnmask = 8 MaxLookup = 10 MaxLookupValue = 256 )
MaxUnmask bounds the pseudonyms of one unmask, MaxLookup the values of one lookup and MaxLookupValue the bytes of one looked-up value.
const DefaultMaxCalls = 4
DefaultMaxCalls is the number of agent calls an engine runs at once when Options.MaxCalls is not set.
const DefaultNsPerByte = 5000
DefaultNsPerByte is the worst-case cost per byte and core before the first calibration (S4: the slowest corpus, encoded text, 0.2 MB/s/core).
const MaxAround = 50
MaxAround bounds before and after of a context call.
const MaxReason = 300
MaxReason bounds the reason an agent gives for an approval.
const MaxScanTTL = 4 * time.Hour
MaxScanTTL bounds the lifetime of an approved scan however long its plan (lockkit's grant: one approval for a bounded read scope, 4 h).
const MaxScans = 8
MaxScans bounds the approved scans in progress on a console.
const MethodScan = "log.scan"
MethodScan is an approved scan (locklog_scan).
const Other = mask.Open + "other" + mask.Close
Other is the key of groups beyond MaxGroups or MaxGroupBytes, or of index overflow.
Variables ¶
var ErrSealed = errors.New("engine: invalid or expired reference")
ErrSealed is returned for a ref or cursor that does not open: wrong type, forged, from another key (console reinstalled), corrupt.
Functions ¶
func Template ¶
Template is the pattern of a masked text: a pseudonym keeps its kind and loses its id (⟦email:…⟧), other placeholders stay as they are, and every part of a word holding a digit (parts are split at = : , ; ( ) [ ] { } " ' / and @) becomes Variable. Words are joined by one space. It reads masked text only: a template carries less than the text it comes from. A bare placeholder equal to Other is written ⟦other:…⟧, so that a template is never the overflow key. It runs in time linear in its input and stops reading once the template is over MaxTemplateBytes.
func WithClearSink ¶
WithClearSink attaches a call's clear-view sink to ctx.
func WithExposureCount ¶ added in v0.2.0
func WithExposureCount(ctx context.Context, f ExposureCount) context.Context
WithExposureCount attaches the console's exposure count to ctx: the unmask screen shows it.
func WithSourceLog ¶ added in v0.2.0
WithSourceLog attaches the operator's log to ctx: the engine reports the failures of sources through it, one line each, for the console's terminal only (never the agent, never the audit).
Types ¶
type Approval ¶
type Approval struct {
// Class is "read" or "read-pii".
Class string
Header string
// Note is the agent's reason (untrusted: the console escapes it).
Note string
Body []string
Warnings []string
// NeverAuto marks the console's own approval screen as one no auto
// mode takes. In hub mode the ticket's never_auto flag is the
// profile's auto rule's (auto.read, auto.unmask, auto.lookup:
// logconsole), not this field.
NeverAuto bool
// Clear is the clear view of the screen's record (secrets and
// redactions kept), for the console's own terminal only: the
// console shows it when its clear-results switch (Ctrl-O) is on.
// It never reaches the agent, nor a hub's masked screen: in hub mode
// it goes sealed to the pinned devices only (the clear screen).
Clear []string
// HubBody, when set, replaces Body on the hub's masked screen: the
// lines of Body that hold a clear value are left out of it (an
// approved lookup's values). Body stays the console's screen and the
// clear screen sealed to the devices.
HubBody []string
// Hash is the keyed plan hash the approval binds (HMAC under K_ref),
// and Inputs its keyed input digests by name: what a hub ticket is
// bound to (plans.Binding). Moving names the inputs a renewal cannot
// check without reading the record again. None of them ever reaches
// the agent.
Hash string
Inputs map[string]string
Moving []string
}
Approval is what the engine asks the human to approve (an approval screen of the console): a scan (class read), an unmask or an approved lookup (class read-pii).
type Approver ¶
Approver asks the human. It returns nil once approved; any other error is the console's answer (a denial, a timeout, a pending ticket), passed back unchanged as the engine's error.
type BudgetStatus ¶
type BudgetStatus struct {
Local *ClassBudget `json:"local,omitempty"`
Remote *ClassBudget `json:"remote,omitempty"`
// Sources is each source's own budget left.
Sources []SourceBudget `json:"sources"`
// LookupsLeft is the distinct values the console may still look up
// this hour, for every peer together (-1: no limit).
LookupsLeft int `json:"lookups_left"`
// LookupsLeftPeer is what the calling peer (its uid) may still look
// up this hour: its own limit within the console's (-1: no limit).
// Absent outside an agent's call (the console's :status).
LookupsLeftPeer *int `json:"lookups_left_peer,omitempty"`
// Scans is the approved scans in progress on the console, every
// peer's (at most MaxScans).
Scans int `json:"scans"`
}
BudgetStatus is what is left of the rolling budgets (log.status): no content, only the console's own counters.
type Budgets ¶
type Budgets struct {
// LocalBytesPerHour and RemoteBytesPerHour bound the bytes processed
// (plan-based, granule-rounded; remote: the bytes fetched) per rolling
// hour, by each source and by the console's sources of that kind.
LocalBytesPerHour, RemoteBytesPerHour int64
// RemoteRequestsPerMinute paces the requests of free calls to a remote
// backend (per source and per console).
RemoteRequestsPerMinute int
// MetricQueriesPerHour bounds the server-side metric queries of
// counts answered by a remote backend.
MetricQueriesPerHour int
// LookupsPerHour and LookupsPerHourPerUID bound the distinct values
// log.lookup resolves per rolling hour, for the console and for one
// peer uid.
LookupsPerHour, LookupsPerHourPerUID int
}
Budgets are the rolling budgets (design §3.3): what the agent's free calls may cost per hour (bytes processed, server-side metric queries) and per minute (remote backend requests), per source and for the whole console. Search, count, patterns, trace, context re-reads, tail polls and continuations all draw on them; an approved scan draws on its own plan instead.
func DefaultBudgets ¶
func DefaultBudgets() Budgets
DefaultBudgets are the design defaults (non-production).
type ClassBudget ¶
type ClassBudget struct {
BytesPerHour int64 `json:"bytes_per_hour"`
BytesLeft int64 `json:"bytes_left"`
RequestsLeft *int `json:"requests_left_this_minute,omitempty"`
MetricLeft *int `json:"metric_queries_left,omitempty"`
RequestsPerMinute int `json:"requests_per_minute,omitempty"`
}
ClassBudget is one kind of source's rolling budget for the console.
type ClearMasker ¶
type ClearMasker interface {
MaskClear(source string, ev parse.Event) (mask.Record, map[string]string)
}
ClearMasker is what unmask needs of the masker: the clear value of each pseudonym of a record (mask.Masker.MaskClear).
type ClearSink ¶
type ClearSink struct {
// contains filtered or unexported fields
}
ClearSink keeps the raw records one call returned, for its clear view.
type ContextQuery ¶
type ContextQuery struct {
Ref string `json:"ref"`
Before int `json:"before,omitempty"`
After int `json:"after,omitempty"`
Fields []string `json:"fields,omitempty"`
}
ContextQuery is a context call (locklog_context): the records around one returned record, from its stream, in stream order.
type ContextResult ¶
type ContextResult struct {
Epoch string `json:"epoch"`
Records []json.RawMessage `json:"records"`
Target int `json:"target"`
// Complete is false when the output cap dropped the records farthest
// from the target.
Complete bool `json:"complete"`
Scanned Scanned `json:"scanned"`
Masking Masking `json:"masking"`
MaskedFailures int `json:"masked_failures,omitempty"`
}
ContextResult is the answer of a context call. Target is the index in Records of the record the ref names. Records are masked as search results are, each with its own ref; a record masking failed on is withheld (masked_failure).
type CostModel ¶
type CostModel struct {
// contains filtered or unexported fields
}
CostModel is the calibrated worst-case cost.
func NewCostModel ¶
NewCostModel calibrates once; workers is the number of masking workers of a call (the per-call cost is the per-core cost divided by it).
func (*CostModel) Calibrate ¶
func (c *CostModel) Calibrate()
Calibrate measures the worst-case cost per byte on the fixed corpus, synchronously (at start; queries use Recalibrate).
func (*CostModel) NsPerByte ¶
NsPerByte returns the cost per byte, recalibrating hourly in the background (the current value serves until the new one is measured).
func (*CostModel) Recalibrate ¶
func (c *CostModel) Recalibrate()
Recalibrate starts a calibration in the background unless one is already running, and returns at once: a query never waits for one (it may take up to calibrationBudget), and concurrent queries start at most one.
type CountQuery ¶
type CountQuery struct {
Query
// Bucket is a duration ("5m"); empty: one bucket for the range.
Bucket string `json:"bucket,omitempty"`
// By groups by up to two trusted or pseudonymised line fields, and
// ByLabels by source labels (Loki stream labels, journald and docker
// attributes); at most MaxBy in all. A group's key holds the By
// values, then the ByLabels values.
By []string `json:"by,omitempty"`
ByLabels []string `json:"by_labels,omitempty"`
// ByPattern also groups by the Template of the masked message (the
// last key; null without a message). It counts as one of MaxBy and is
// never served from the index.
ByPattern bool `json:"by_pattern,omitempty"`
// FirstLast adds each group's first and last timestamp.
FirstLast bool `json:"first_last,omitempty"`
// contains filtered or unexported fields
}
CountQuery is a count (locklog_count).
type CountResult ¶
type CountResult struct {
Epoch string `json:"epoch"`
Groups []Group `json:"groups"`
Untimed int64 `json:"untimed,omitempty"`
Complete bool `json:"complete"`
CoveredUntil *time.Time `json:"covered_until,omitempty"`
Cursor string `json:"cursor,omitempty"`
Scanned Scanned `json:"scanned"`
// FromIndex is set when the local index answered (part of) the
// count; IndexedFrom is where the index starts.
FromIndex bool `json:"from_index,omitempty"`
IndexedFrom *time.Time `json:"indexed_from,omitempty"`
IndexedUntil *time.Time `json:"indexed_until,omitempty"`
MaskedFailures int `json:"masked_failures,omitempty"`
// A count whose groups do not fit one answer (Limits.MaxOutput) is
// paged once it is whole: each page holds the next groups, in order,
// from GroupsFrom of GroupsTotal, and a cursor to the rest (complete
// on the last). A partial count (its scan goes on) shows the first
// groups that fit, Truncated set, and OmittedGroups and OmittedN
// what it left out for now.
GroupsFrom int `json:"groups_from,omitempty"`
GroupsTotal int `json:"groups_total,omitempty"`
Truncated bool `json:"truncated,omitempty"`
OmittedGroups int `json:"omitted_groups,omitempty"`
OmittedN int64 `json:"omitted_n,omitempty"`
}
CountResult is the answer of a count.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine runs queries. It is safe for concurrent use.
func (*Engine) BudgetStatus ¶
func (e *Engine) BudgetStatus() BudgetStatus
BudgetStatus reports what is left of the rolling budgets.
func (*Engine) BudgetStatusFor ¶ added in v0.2.0
func (e *Engine) BudgetStatusFor(ctx context.Context) BudgetStatus
BudgetStatusFor is BudgetStatus with the lookups left to the peer of ctx, when one is attached.
func (*Engine) ClearRecords ¶
ClearRecords renders the records of refs in clear for the console's terminal, one line each, in order, from sink (the call's own records) or, for a local source, by reading the record again; everything under one deadline. A record it cannot show is named, not shown.
func (*Engine) Context ¶
func (e *Engine) Context(ctx context.Context, cq ContextQuery) (ContextResult, error)
Context re-reads the records around a ref (locklog_context). The record must still be the one the ref was made on (its keyed digest): a stream rewritten since gives E_STALE. Every record is masked before it is rendered; the work depends on the stream around the ref only, and scanned reports it in granules (charged to the rolling budget).
func (*Engine) Count ¶
func (e *Engine) Count(ctx context.Context, q CountQuery) (_ CountResult, retErr error)
Count runs a count. A trusted-field-only count is answered from the local index where it holds the range, and from a plan-based scan of the rest (continued free) that masks only the records holding a trusted value the masker would change (countTrusted); every other count scans its plan, masking every record first. A record masking fails on is reported in masked_failures; a trusted-only count still counts it by its trusted values, as the index does, while any other count leaves it out, since its masked predicates cannot be evaluated on it.
func (*Engine) Handle ¶
func (e *Engine) Handle(ctx context.Context, method string, params json.RawMessage) (any, *Error, error)
Handle decodes the params of an agent method and runs it. A refusal is the agent's error (structured, no log content); err is the engine's own failure. Unknown fields in params are refused.
A call takes one of the engine's call slots (Options.MaxCalls) for its whole run, or is refused at once with E_BUSY. Calls a human approves take none while they wait: a scan takes its slot once approved (run); an unmask or a lookup reads one record and takes none.
func (*Engine) Lookup ¶
func (e *Engine) Lookup(ctx context.Context, q LookupQuery) (LookupResult, error)
Lookup gives the pseudonyms of clear values of a detector kind, as the masker shows them in this epoch (the equality the agent then searches or counts by). It is free and rate-limited (distinct values per hour, per peer uid and per console), or a read-pii approval with lookup = "approved". Refused: a kind no value detector finds (field names, template tokens, rule kinds), a value that reads as a secret, and a value that is not wholly of the kind. Documented residual: membership probing (design §4.4 item 5).
func (*Engine) Paced ¶
Paced returns ctx with the request limiter of a free call to the remote source src and the call deadline: its requests outside a query (log.sources describing it) are paced and charged to the rolling request budget like a query's, and a request the budget leaves no room for before the deadline fails at once instead of waiting for the window. The caller calls cancel when done.
func (*Engine) Patterns ¶
func (e *Engine) Patterns(ctx context.Context, q PatternsQuery) (_ PatternsResult, retErr error)
Patterns runs a patterns query: it scans the plan of the window (and of the baseline), masking every record first, and groups the matching records' masked messages by Template. Admission, stopping points and cursors are those of a count: functions of the plan, never of matches.
func (*Engine) RecordDigest ¶
RecordDigest is the keyed digest of a raw record bound into its ref.
func (*Engine) RefRecord ¶ added in v0.2.0
RefRecord opens a sealed ref of a returned record: a key of the record (keyed: the same record at the same place, the same key; it says nothing of its text) and the epoch of its pseudonyms. ok is false for a ref this engine cannot open.
func (*Engine) Scan ¶
Scan runs an approved scan (log.scan). The first call (no cursor in the query) plans the tool's call with the scan's budgets, re-attaches to the same plan approved earlier by the same peer within its TTL, or asks the human to approve it; every later call continues the approved plan with the cursor, free. The plan hash, its inputs and the policy fingerprint never reach the agent: it gets the scan's random id.
func (*Engine) Tail ¶
Tail polls the sources until Duration ends or Max match, for the records timestamped from the call's start (to the second): new records are told by their time, which every source has; records without one are never tailed. Each record is evaluated and returned once: identical records are told apart by their position. While the set of local streams stays the same (no stream appears, goes or shrinks: which streams exist and their ends come from a plan of every stream whole, unfiltered, at each poll), complete:true means every new record was evaluated once. When it changes (rotation, truncation, a new file), the tail is complete:false: a copy made during the tail (copytruncate) is recognised by a record of the original at the same offset with the same keyed digest and the records it shares with the original are not returned again, but two streams of their own can share such records. The duration ends with a poll planned after it.
Work follows the new data: a plan of every local stream at the start (no record read) gives each stream's end in its granules, and a poll scans only granules past what was there or already scanned whole (the growing last one again, from its start); within a stream, records up to the last one evaluated are skipped. These granule ends are used for pruning only; they are compared with record positions only for byte streams (file, docker), whose granules and positions are both byte offsets: there the granule of a stream's held record is read again once the stream grows. Journal entries are atomic and never held. A stream first seen during the tail is scanned from its start (its records still need a time from the start on). A record that was already there at the start (for a byte stream, its end at the first listing, milliseconds after the call starts; a compressed stream not measured then, whole) is never new, whatever its date. Residuals: a journal entry already there and dated from the start on (a clock stepped back) is returned; a record written after the start with a time before it (its second, for a byte stream) is not.
Every record of a scanned granule is masked before evaluation, as in a search; a poll's matches are kept only when it completes (stopping points are group boundaries), its work always. Each poll's plan, once pruned, is admitted against the call's byte budgets, and the polls share the call's bytes; everything runs under the duration plus a grace.
func (*Engine) Trace ¶
func (e *Engine) Trace(ctx context.Context, tq TraceQuery) (SearchResult, error)
Trace runs a trace (locklog_trace). It is a search for the id over the range, with the same validation, plan, budgets, stopping points and cursors; each page is in time order across the sources of the call (untimed records last), and continuations follow the plan.
func (*Engine) Unmask ¶
func (e *Engine) Unmask(ctx context.Context, q UnmaskQuery) (UnmaskResult, error)
Unmask reveals the clear values of exact pseudonyms of one record to the agent, once, on a read-pii approval the human gives at the console (never auto-approved). The plan hash binds the ref, the record's keyed digest (HMAC under K_ref), the epoch and the exact (kind, ID, span offsets); the record is read again after the approval and anything that moved is refused (E_STALE). A secret span is never revealed, nor a value that held one.
type Error ¶
type Error struct {
Code string `json:"code"`
Message string `json:"message"`
// Span is the JSON path of the offending part of the query.
Span string `json:"span,omitempty"`
// Estimate is the plan estimate of an over-budget call.
Estimate *Estimate `json:"estimate,omitempty"`
// contains filtered or unexported fields
}
Error is a structured refusal. Its text never holds log content: query values are echoed only through the masker's text path by the caller.
type Estimate ¶
type Estimate struct {
Bytes int64 `json:"bytes"`
Granules int `json:"granules"`
Range time.Duration `json:"range_ns"`
}
Estimate is what a plan would cost.
type ExposureCount ¶ added in v0.2.0
type ExposureCount func(epoch string, pseudonyms []string) (counts []int, exact bool, since time.Time)
ExposureCount counts, for each pseudonym, the distinct records holding it returned to agents in epoch (the console's count, from the calls it answered and audited): whether the counts are exact (false: lower bounds) and since when it counts.
type Group ¶
type Group struct {
Bucket time.Time `json:"bucket"`
Key []*string `json:"key,omitempty"` // masked values; null when absent
N int64 `json:"n"`
First *time.Time `json:"first,omitempty"`
Last *time.Time `json:"last,omitempty"`
}
Group is one counted group.
type Limits ¶
type Limits struct {
// Range of a query with text, regex or masked-field predicates.
LocalRange, RemoteRange time.Duration
// CountRange of a trusted-field-only count, RemoteCountRange of one a
// remote source answers with its own counts (0: CountRange).
CountRange, RemoteCountRange time.Duration
// Bytes a call's plan may cost (admission).
LocalBytes, RemoteBytes int64
// Records per page (ceiling of limit) and output bytes per page.
MaxRecords int
MaxOutput int
// Deadline of one call.
Deadline time.Duration
}
Limits are the per-call budgets (design §3.3).
type LookupQuery ¶
type LookupQuery struct {
Kind string `json:"kind"`
Values []string `json:"values"`
Reason string `json:"reason,omitempty"`
}
LookupQuery asks for the pseudonyms of clear values of a detector kind (log.lookup): equality only.
type LookupResult ¶
type LookupResult struct {
Epoch string `json:"epoch"`
Kind string `json:"kind"`
Pseudonyms []string `json:"pseudonyms"`
}
LookupResult is the pseudonym of each value, in order (the values are not echoed).
func (LookupResult) AuditPseudonyms ¶
func (r LookupResult) AuditPseudonyms() []string
AuditPseudonyms names the lookup's answer by pseudonym only.
type MaskFunc ¶
MaskFunc masks one parsed event (the console passes its masker); the result is ignored by calibration.
type Masker ¶
type Masker interface {
Mask(source string, ev parse.Event) mask.Record
Text(s string) string
// TrustedValue masks a trusted value read outside a record (index
// values, values compared with trusted fields) as Mask masks a
// trusted leaf at that path.
TrustedValue(source, path, v string) string
Epoch() string
}
Masker is what the engine needs of the masker.
type Masking ¶
type Masking struct {
Kinds []string `json:"kinds"`
}
Masking reports what was masked in the returned records.
type Options ¶
type Options struct {
Sources map[string]source.Source
// Trusted reports a trusted line field (label unset) or a trusted
// label (label set) of a source. They are separate namespaces: a
// trusted label never makes a line field of the same name trusted.
Trusted func(src, name string, label bool) bool
Masker Masker
Sealer *Sealer
Cost *CostModel
// PolicyFP is the fingerprint of the approved policy in force.
PolicyFP func() string
// Detect reports a query value a detector flags.
Detect func(string) bool
// NameMasked reports a clear path the masker shows masked.
NameMasked func(src, path string) bool
Limits Limits
// Prefilter (opt-in, refused on production by the config) skips
// masking a record whose clear text cannot contain a needle.
Prefilter bool
// Workers mask in parallel (the console runs with --cpus min(2,
// nproc)); the cost model is per call, so it is calibrated with the
// same count.
Workers int
Now func() time.Time
// TailPoll is the interval between a tail's polls (DefaultTailPoll).
TailPoll time.Duration
// Budgets are the rolling budgets (zero: DefaultBudgets).
Budgets Budgets
// ScanTTL, ScanDeadline, ScanBytes and ScanRange bound an approved
// scan: its lifetime from the approval (DefaultScanTTL), the
// deadline of each of its calls (DefaultScanDeadline, and never past
// scanCallBudget from the request), the bytes and the range of its
// whole plan (DefaultScanBytes, DefaultScanRange). The deadline is a
// fixed bound, not calibrated: what a call does within it is (run
// admits each group of granules on the calibrated cost model).
ScanTTL time.Duration
ScanDeadline time.Duration
ScanBytes int64
ScanRange time.Duration
// ScanRequestsPerMinute paces an approved scan's remote requests (0:
// none beyond the source's own in-flight cap; design R6-P2).
ScanRequestsPerMinute int
// Unmask is "approved" to serve log.unmask on a read-pii approval;
// anything else ("never", the default) refuses it.
Unmask string
// LookupApproved makes every log.lookup a read-pii approval.
LookupApproved bool
// Production marks the approval screens of a production profile.
Production bool
// MaxCalls bounds the agent calls the engine runs at once
// (DefaultMaxCalls when 0); a call over it is refused with E_BUSY.
MaxCalls int
// contains filtered or unexported fields
}
Options configure an Engine.
type PatternGroup ¶
type PatternGroup struct {
Pattern string `json:"pattern"`
N int64 `json:"n"`
BaselineN *int64 `json:"baseline_n,omitempty"`
First *time.Time `json:"first,omitempty"`
Last *time.Time `json:"last,omitempty"`
}
PatternGroup is one pattern and its counts.
func (PatternGroup) String ¶
func (g PatternGroup) String() string
String describes a pattern group (tests and status screens).
type PatternsQuery ¶
type PatternsQuery struct {
Query
// Top is the number of patterns returned (DefaultTop, at most MaxTop).
Top int `json:"top,omitempty"`
// Baseline, when set, is an offset ("24h"): the same window that much
// earlier is counted too, for each pattern's baseline count and the
// new and gone lists. It is at least the window's length (the windows
// never overlap).
Baseline string `json:"baseline,omitempty"`
// contains filtered or unexported fields
}
PatternsQuery asks for the top templates of the masked message text (locklog_patterns).
type PatternsResult ¶
type PatternsResult struct {
Epoch string `json:"epoch"`
// Patterns are the top patterns of the window, most frequent first.
Patterns []PatternGroup `json:"patterns"`
// Distinct is the number of distinct patterns of the window, Records
// the records counted (with a message), NoText the matching records
// without one.
Distinct int `json:"distinct"`
Records int64 `json:"records"`
NoText int64 `json:"no_text,omitempty"`
// Untimed counts the matching untimed records of the window's plan.
Untimed int64 `json:"untimed,omitempty"`
// Truncated is set when the window or the baseline had more distinct
// patterns than MaxGroups (or MaxGroupBytes): the rest count under
// Other, in their own group (counted in Distinct). New is then left
// empty when the baseline is truncated, Gone when the window is.
Truncated bool `json:"truncated,omitempty"`
// With a baseline: New are the top patterns absent from it, Gone the
// top patterns of the baseline absent from the window.
BaselineFrom *time.Time `json:"baseline_from,omitempty"`
BaselineTo *time.Time `json:"baseline_to,omitempty"`
New []PatternGroup `json:"new,omitempty"`
Gone []PatternGroup `json:"gone,omitempty"`
Complete bool `json:"complete"`
CoveredUntil *time.Time `json:"covered_until,omitempty"`
Cursor string `json:"cursor,omitempty"`
Scanned Scanned `json:"scanned"`
MaskedFailures int `json:"masked_failures,omitempty"`
}
PatternsResult is the answer of a patterns query.
type Peer ¶
type Peer struct {
UID int
// Token is the hash of the agent's token (lea's requester token,
// plans.HashToken), "" when the agent sends none. An approved scan is bound to
// it as to the uid: another agent of the same uid, with another token,
// neither re-attaches to it nor continues it.
Token string
Approve Approver
}
Peer is the caller of a call: its kernel-verified uid, the agent's token it binds an approval to, and the console's approver.
type Pred ¶
type Pred struct {
Field string `json:"field,omitempty"`
Label string `json:"label,omitempty"`
Op string `json:"op"`
Value json.RawMessage `json:"value,omitempty"`
// contains filtered or unexported fields
}
Pred is one predicate of where. Field addresses a field by its shown path (clear, or with ⟦key:ID⟧ segments); Label a source label (Loki labels, container-frame attributes).
type Query ¶
type Query struct {
Sources []string `json:"sources,omitempty"`
From time.Time `json:"from"`
To time.Time `json:"to"`
// Last, when set, resolves the range at validation: [now-Last, now).
Last string `json:"last,omitempty"`
Where []Pred `json:"where,omitempty"`
Text string `json:"text,omitempty"`
Regex string `json:"regex,omitempty"`
Fields []string `json:"fields,omitempty"`
Limit int `json:"limit,omitempty"`
// Order is "asc" (default) or "desc": pages follow it within each
// stream; each call's matches are sorted, while streams of several
// sources follow each other rather than being merged by time.
Order string `json:"order,omitempty"`
Cursor string `json:"cursor,omitempty"`
// contains filtered or unexported fields
}
Query is a search or count query (design §3.2).
type ScanInfo ¶
type ScanInfo struct {
ID string `json:"id"`
ExpiresAt time.Time `json:"expires_at"`
Reattached bool `json:"reattached,omitempty"`
// Resumed: the re-attached scan went on after the last page it
// answered. A page whose answer the agent did not get is not sent
// again.
Resumed bool `json:"resumed,omitempty"`
// Done: the plan is exhausted and the scan ended.
Done bool `json:"done"`
}
ScanInfo names the approved scan of a result: a random id (it carries nothing about the plan), when it expires and whether this call re-attached to a scan approved earlier.
type ScanQuery ¶
type ScanQuery struct {
Tool string `json:"tool"`
Query json.RawMessage `json:"query"`
Reason string `json:"reason,omitempty"`
}
ScanQuery is an approved scan (log.scan): a search, count, patterns or trace call over the free budgets, approved once (class read) for its whole plan. Query is that tool's parameters; with its cursor it continues the approved plan, free and with no new approval.
type ScanResult ¶
type ScanResult struct {
Scan ScanInfo `json:"scan"`
Tool string `json:"tool"`
Result any `json:"result"`
}
ScanResult is the answer of a scan: the tool's own result (its cursor continues the scan) and the scan.
type Scanned ¶
type Scanned struct {
Bytes int64 `json:"bytes"`
Records int64 `json:"records"`
Granules int `json:"granules"`
}
Scanned is the plan-based work of a call.
type Sealer ¶
type Sealer struct {
// contains filtered or unexported fields
}
Sealer seals refs and cursors (AEAD) and computes the keyed digests plans are bound to (HMAC), each under its own subkey derived from K_ref with HKDF-SHA256 (info "locklog/aead/v1" and "locklog/hmac/v1"): one key is never used by two primitives. Nothing it produces can be opened, forged or guessed offline without K_ref, which never leaves the console's home volume: an agent holding a ref or a cursor learns nothing from it, and plan hashes and record digests are never returned on the agent socket at all.
func (*Sealer) Digest ¶
Digest is HMAC-SHA256(K_hmac, domain ‖ (0 ‖ len ‖ part)…), base64url (unpadded); K_hmac is derived from K_ref. It is the only hash used for plan binding and record identity (round-6 amendment: never an unkeyed hash of a record).
type SearchResult ¶
type SearchResult struct {
Epoch string `json:"epoch"`
Records []json.RawMessage `json:"records"`
Complete bool `json:"complete"`
CoveredUntil *time.Time `json:"covered_until,omitempty"`
Cursor string `json:"cursor,omitempty"`
Scanned Scanned `json:"scanned"`
Masking Masking `json:"masking"`
// MaskedFailures counts records withheld whole because masking
// failed. With the opt-in pre-filter and a needle in the query (text
// or contains), it is not counted and stays 0: only the records
// holding the needles are masked, so the count would tell which
// failing records hold them (prefilter.go, docs/usage.md).
MaskedFailures int `json:"masked_failures,omitempty"`
}
SearchResult is the answer of a search (design §3.4).
type SourceBudget ¶
SourceBudget is one source's rolling budget left.
type TailQuery ¶
type TailQuery struct {
Sources []string `json:"sources,omitempty"`
Where []Pred `json:"where,omitempty"`
Text string `json:"text,omitempty"`
Regex string `json:"regex,omitempty"`
Fields []string `json:"fields,omitempty"`
Duration string `json:"duration,omitempty"` // default and ceiling 50s
Max int `json:"max,omitempty"`
}
TailQuery is a tail (locklog_tail): the new records matching a filter for up to Duration, at most Max of them.
type TailResult ¶
type TailResult struct {
Epoch string `json:"epoch"`
Records []json.RawMessage `json:"records"`
Complete bool `json:"complete"`
Scanned Scanned `json:"scanned"`
Masking Masking `json:"masking"`
MaskedFailures int `json:"masked_failures,omitempty"`
}
TailResult is the answer of a tail: the new records in time order. Complete is false when the tail may have left records out: it stopped early (at max, a poll failed or could not cover what was written, the call's byte budget ran out), the output cap dropped records, a record was still being written at the end, or records without a time were seen (they are never tailed: search them).
type TraceQuery ¶
type TraceQuery struct {
Sources []string `json:"sources,omitempty"`
// ID is a clear id (as the agent saw it in a result) or a pseudonym
// ⟦kind:ID⟧.
ID string `json:"id"`
From time.Time `json:"from"`
To time.Time `json:"to"`
// Last, when set, resolves the range at validation (see Query).
Last string `json:"last,omitempty"`
Fields []string `json:"fields,omitempty"`
Limit int `json:"limit,omitempty"`
Cursor string `json:"cursor,omitempty"`
}
TraceQuery is a trace (locklog_trace): the records of a range that carry one id (a request or trace id shown in clear) or one pseudonym, across sources, as one timeline.
type UnmaskQuery ¶
type UnmaskQuery struct {
Ref string `json:"ref"`
Pseudonyms []string `json:"pseudonyms"`
Reason string `json:"reason"`
}
UnmaskQuery asks for the clear values of exact pseudonyms of one record (log.unmask), with the agent's reason for the human.
type UnmaskResult ¶
type UnmaskResult struct {
Epoch string `json:"epoch"`
Values []UnmaskValue `json:"values"`
}
UnmaskResult is the answer of an approved unmask: those spans' clear values, once.
func (UnmaskResult) AuditPseudonyms ¶
func (r UnmaskResult) AuditPseudonyms() []string
AuditPseudonyms names what was revealed by pseudonym (never the value).
type UnmaskValue ¶
UnmaskValue is one pseudonym's clear value.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package session opens the log engine of an approved policy for the console (logconsole.Opener): the masker of each source, the local index in the console's state directory, the sources the policy names (given only the mount roots the launcher checked), the keys (K_ref, pseudonym epochs), the cost model and the budgets, and serves the agent's methods over it.
|
Package session opens the log engine of an approved policy for the console (logconsole.Opener): the masker of each source, the local index in the console's state directory, the sources the policy names (given only the mount roots the launcher checked), the keys (K_ref, pseudonym epochs), the cost model and the budgets, and serves the agent's methods over it. |