Documentation
¶
Overview ¶
Package recordmode is the deterministic core of Wardyn's "Recording Mode": it OBSERVES what a fully-open (allow-all-egress, broad-grant) run actually used — purely from already-captured audit events — and SYNTHESIZES a tightened, least-privilege RunPolicySpec the operator can review and promote.
PURITY. Like internal/composer/risk.go's Grade, the two entry points here are pure functions of their inputs. They take SLICES (audit events, grants, the run) and return values; they touch NO database, NO network, NO clock, and NO global state. This makes them trivially unit-testable and makes the synthesis a function of the captured evidence, not of anything an in-sandbox (possibly prompt-injected) agent can influence after the fact.
DETERMINISM. Both functions are deterministic and input-order INDEPENDENT:
- every set (domains, methods, argv[0]s, file writes, connects, grant ids, anomalies) is de-duplicated and then SORTED, so the same evidence yields byte-identical output regardless of the order events were recorded in;
- egress decision COUNTS are sums (order-independent by construction);
- Synthesize iterates already-sorted Observations fields (never a Go map), so its returned spec and warnings are stable across runs.
HONESTY. Recording Mode tightens egress and credential surface from evidence, but it deliberately does NOT auto-author everything:
- it NEVER auto-wildcards a domain (exact hosts only) — wildcards widen, and a recording cannot prove a wildcard is needed;
- it FORCES allow_all_egress=false and first_use_approval=true, so the tightened policy fails toward human escalation rather than silent denial of a host the (necessarily incomplete) recording happened not to exercise;
- it does NOT synthesize WorkspaceMounts (operator-authored, admin-gated) and the policy model has no exec/connect/file allowlist, so kernel ground-truth is surfaced as warnings/Observations, never as silent policy.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CleanReplay ¶ added in v0.6.0
func CleanReplay(obs []DomainObservation, truncated bool) bool
CleanReplay is the server-side verdict for a CONFINED replay (Workstream B): true iff the capture is clean FOR WHAT WAS REPLAYED. It is pure and takes exactly the evidence the verdict is defined over — the caller (only ever meaningful for a settled `recorded` capture; the caller gates on that) is responsible for calling this only once a replay has finalized.
clean iff, across every observed domain: zero DenyCount, zero PendingCount, and zero ApprovalCount (no allow was released by a live first-use approval — approving mid-replay must not earn a green the standing policy didn't; the honest loop is approve, then replay again) — AND the capture itself was not truncated (maxCaptureAuditEvents, workspace_run.go). Hard-walled denies count as caught like any other DenyCount; CleanReplay has no way to (and does not try to) distinguish them.
Empty obs (nothing was observed) with truncated=false is clean: there is nothing that disqualifies it. "Clean" does NOT mean "nothing could go wrong" — a replay the operator ends early earns the same verdict as a full one if nothing else caught it; the caller names that caveat, this function only reports what the evidence shows.
func Synthesize ¶
func Synthesize(obs Observations, runGrants []types.CredentialGrant, run types.AgentRun) (types.RunPolicySpec, []string)
Synthesize derives a tightened, least-privilege RunPolicySpec from the Observations of an open run plus the run's grant catalog and the run itself. It is pure and deterministic, and returns the spec alongside human-readable warnings explaining every tightening decision and everything it deliberately did NOT auto-author. See the package doc for the honesty guarantees.
Types ¶
type DomainObservation ¶
type DomainObservation struct {
// Host is the lowercased, trimmed egress hostname (no port).
Host string `json:"host"`
// Methods is the de-duplicated, sorted set of HTTP methods observed (an
// upper-cased "CONNECT" appears for tunneled TLS). May be empty when the
// proxy only saw opaque CONNECTs without a method or recorded none.
Methods []string `json:"methods,omitempty"`
// AllowCount/DenyCount/PendingCount are the number of egress.allow/deny/
// pending decisions recorded for this host.
AllowCount int `json:"allow_count"`
DenyCount int `json:"deny_count"`
PendingCount int `json:"pending_count"`
// ApprovalCount is the subset of AllowCount that was RELEASED by a live
// first-use approval (rule_source "approval:<id>" — the exact prefix the
// allow path of evaluate writes in internal/egress/proxy/proxy.go) rather
// than the standing policy. A confined replay's CleanReplay verdict treats any
// of these as caught: approving mid-replay must not earn a green the
// standing policy didn't — the honest loop is approve, then replay again.
ApprovalCount int `json:"approval_count"`
}
DomainObservation is one egress host the run actually reached, with the HTTP method set observed at the proxy and the per-decision counts. Methods is de-duplicated and sorted; counts are sums over every decision for the host.
type Observations ¶
type Observations struct {
// Domains is the per-host egress aggregate (deduped, sorted by host).
Domains []DomainObservation `json:"domains,omitempty"`
// MintedGrantIDs is the deduped, sorted set of grant ids the run SUCCESSFULLY
// minted a credential for (credential.mint with outcome=success). A denied or
// failed mint is NOT included — it did not actually yield a credential.
MintedGrantIDs []uuid.UUID `json:"minted_grant_ids,omitempty"`
// ExecArgv0s is the deduped, sorted set of argv[0] (program paths) the kernel
// sensor observed the run exec.
ExecArgv0s []string `json:"exec_argv0s,omitempty"`
// FileWrites is the deduped, sorted set of sensitive file paths the kernel
// sensor observed the run write.
FileWrites []string `json:"file_writes,omitempty"`
// Connects is the deduped, sorted set of "ip:port" destinations the kernel
// sensor observed the run connect to.
Connects []string `json:"connects,omitempty"`
// Anomalies is the deduped, sorted set of human-readable signals a
// least-privilege synthesis must NOT silently bless. It captures exactly:
// an egress.deny during the open recording, an unmapped kernel connect
// (possible proxy bypass), an exec of a dynamic linker (the ld-linux/mmap
// execve-hook bypass surface), and a failed/escape kernel connect.
Anomalies []string `json:"anomalies,omitempty"`
}
Observations is the deterministic aggregate of what a run actually used, computed purely from its already-captured audit events. Every slice is de-duplicated and sorted, so equal evidence yields equal Observations.
func Capture ¶
func Capture(events []types.AuditEvent, confined bool) Observations
Capture aggregates one run's already-captured audit events into a deduped, sorted Observations. It is pure and input-order independent: it reads only the egress.*, credential.mint, and kernel.* (eBPF ground-truth) streams and silently ignores any other action, so it is robust to new audit verbs.
confined distinguishes an OPEN (learning) recording, where a deny is a real anomaly a least-privilege synthesis must not silently bless, from a CONFINED replay, where a deny is the advertised containment proof working as designed — the off-policy host it denied is exactly what confinement exists to block (W19-W19b-4). A confined deny is still captured on the per-host DomainObservation (nothing is hidden), it just does not also land in Anomalies, and its message never claims "during open recording" for a session that was never open.