Help us shape the future of Go! Take the 2026 Go Developer Survey

gate

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package gate is the pre-execution and pre-edit policy engine (RFC-001 §5.5): commands are parsed with a real shell parser (never regex), classified against the effect catalogue, and evaluated against CEL rules over the declared environment. Every decision is appended to an audit log.

Index

Constants

View Source
const (
	VerdictAllow    = "allow"
	VerdictApproval = "require_approval"
	VerdictDeny     = "deny"
)

Decision severities, weakest to strongest.

Variables

View Source
var ErrBlocked = errors.New("blocked by policy")

ErrBlocked marks decisions that must stop the caller (enforce mode); the CLI maps it to a distinct exit code for hooks and CI.

Functions

func Audit

func Audit(root, kind, input string, p *Policy, d *Decision) error

Audit appends the decision to <root>/.seamark/audit.jsonl (0600, rotated by size and age). The default entry stores the normalized command names, a SHA-256 of the input, the verdict and the policy hash — never the raw input, which frequently carries tokens, passwords and connection strings. Opting in via policy.yaml (`audit:` → `raw: true`) persists the input line with best-effort secret redaction.

func ChangedPaths added in v0.2.0

func ChangedPaths(diffText string) []string

ChangedPaths lists the repo-relative files a unified diff touches, in first-appearance order — the same header parsing EvalDiff trusts, so an advisory surface (lessons on `check`) can never disagree with the verdict about which files changed.

func FailClosed added in v0.7.0

func FailClosed(err error, enforced bool) error

FailClosed wraps err in Blocked when enforced. Under enforcement the gate's own failures must block: a security hook that fails open on a malformed payload or a broken policy is itself a bypass. Without enforcement the error is returned as it is, and the caller reports it while the command proceeds.

Types

type Blocked added in v0.7.0

type Blocked struct {
	// Cause is the reason: the matched rule messages, or the failure.
	Cause error
}

Blocked is the error of a blocking outcome: a blocking verdict, or a gate failure under enforcement. It wraps ErrBlocked, so errors.Is(err, ErrBlocked) stays the one test for "exit 2", and it keeps the cause apart from the prefix, so a hook reply can carry the cause in the client's own form.

func (*Blocked) Error added in v0.7.0

func (b *Blocked) Error() string

Error prefixes the cause the way the gate always did.

func (*Blocked) Is added in v0.7.0

func (b *Blocked) Is(target error) bool

Is makes every Blocked value match ErrBlocked.

func (*Blocked) Unwrap added in v0.7.0

func (b *Blocked) Unwrap() error

Unwrap exposes the cause to errors.Is and errors.As.

type CommandRequest added in v0.7.0

type CommandRequest struct {
	// Root is the workspace root. The policy, the effect catalogue, and
	// the audit log live under it.
	Root string
	// Command is the shell text to evaluate. An empty command is an
	// error: a gate that shrugs at nothing to evaluate is a bypass.
	Command string
	// Enforce overrides the policy mode with enforce, as --enforce does.
	Enforce bool
}

CommandRequest is one shell command to gate in a workspace.

type Decision

type Decision struct {
	Verdict string   `json:"verdict"` // strongest of the matches
	Mode    string   `json:"mode"`    // warn | enforce
	Effects []string `json:"effects"`
	Matches []Match  `json:"matches,omitempty"`
	// Commands are the normalized command names found in the input
	// (wrappers unwrapped, interpreter payloads included) — what the
	// audit log stores instead of the raw command line.
	Commands []string `json:"commands,omitempty"`
	// PolicySHA identifies the exact policy text that produced this
	// decision.
	PolicySHA string `json:"policy_sha256,omitempty"`
	// Notes carry uncertainty the verdict alone would hide — e.g.
	// changed files the index has no symbols for. A note never changes
	// the verdict; it changes how much the verdict may be trusted.
	Notes []string `json:"notes,omitempty"`
}

Decision is the gate's answer for one input.

func Decide added in v0.7.0

func Decide(req CommandRequest, warn func(error)) (*Decision, error)

Decide gates one command end to end: it loads the policy and the effect catalogue of the workspace, evaluates the command, and appends the decision to the audit log. The plain command and every client hook share this order, so a policy or catalogue failure has one meaning everywhere. The decision says whether it blocks; the caller renders it in its own form.

A failure returns a Blocked error when enforcement is active at that point. Explicit enforcement is active from the start. The policy's own mode is active once the policy loads: a warn policy lets a later failure through, and an enforce policy blocks it. Under warn the same failure is a plain error.

The audit is best effort. A failed append reaches warn, when given, and never changes the decision.

func EvalCommand

func EvalCommand(p *Policy, catalog *effects.Catalog, root, commandLine string) (*Decision, error)

EvalCommand parses a shell command line, classifies every command in it (pipelines, &&-chains, substitutions, and interpreter payloads like `bash -c "…"` included) against the catalogue, and evaluates the policy. Parse failures fail closed with an error — a gate that shrugs at unparseable input is a bypass.

func EvalDiff

func EvalDiff(p *Policy, st *store.Store, diffText string) (*Decision, error)

EvalDiff computes a unified diff's blast radius and evaluates policy over it: changed lines map to symbols, and symbols already carry the transitively-propagated effect tags — their union is what this change can ultimately reach (RFC-001 §5.5, pre-edit enforcement).

func (*Decision) Blocking

func (d *Decision) Blocking() bool

Blocking reports whether this decision should stop execution: only enforce mode blocks; warn mode always lets the command through.

type Match

type Match struct {
	RuleID  string `json:"rule"`
	Verdict string `json:"verdict"`
	Message string `json:"message"`
}

Match is one rule that fired.

type Policy

type Policy struct {
	// Mode "warn" reports verdicts without blocking; "enforce" makes deny
	// and require_approval binding. Warn is the default (§10: a policy
	// layer that cries wolf gets disabled).
	Mode        string `yaml:"mode"`
	Environment struct {
		Detect      []string `yaml:"detect"`
		ProdMarkers []string `yaml:"prod_markers"`
	} `yaml:"environment"`
	// Audit configures decision logging. Raw opts into persisting the
	// input line (secret patterns redacted best-effort); the default
	// entry carries only a SHA-256 of the input, because command lines
	// frequently embed tokens, passwords and connection strings.
	Audit struct {
		Raw bool `yaml:"raw"`
	} `yaml:"audit"`
	Deny            []Rule `yaml:"deny"`
	RequireApproval []Rule `yaml:"require_approval"`

	// Hash is the SHA-256 of the policy source that produced this rule
	// set: audit entries carry it so a decision can be correlated with
	// the exact policy text that made it.
	Hash string `yaml:"-"`
}

Policy is the loaded rule set.

func LoadPolicy

func LoadPolicy(root string) (*Policy, error)

LoadPolicy returns the workspace policy at .seamark/policy.yaml, or the embedded warn-mode default when absent. The workspace file REPLACES the default rule set (policy is a whole, not a patch — partial merges would make the effective rules unreviewable). All CEL expressions compile at load time so a broken rule fails loudly, not at decision time.

func (*Policy) DetectEnv

func (p *Policy) DetectEnv(environ []string) map[string]any

DetectEnv inspects the process environment per policy: which declared variables are set, and whether any value carries a production marker.

type Rule

type Rule struct {
	ID      string `yaml:"id"`
	When    string `yaml:"when"`
	Message string `yaml:"message"`
	// contains filtered or unexported fields
}

Rule is one policy predicate.

Jump to

Keyboard shortcuts

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