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 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"`
}
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.