confinement

package
v0.0.0-...-0879ac6 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package confinement defines the authenticated write boundary for worker and reviewer launches (FAC-190). Policy Authorize* is fail-closed path planning; production also requires a MAC issuer and an OS backend that proves shared-root and sibling write denials before a write-capable agent is started.

Index

Constants

View Source
const PolicyDigestV1 = "confinement-policy-v1-fac190-r3"

PolicyDigestV1 is the fixed policy identity for this confinement generation.

View Source
const SecretEnv = "HERD_CONFINEMENT_SECRET"

SecretEnv is the preferred production secret for confinement MACs.

View Source
const SecretEnvFallback = "HERD_CONTROL_SECRET"

SecretEnvFallback is accepted so coordinators that already mint control envelopes can reuse the same host secret without a second key material.

View Source
const SentinelRelPath = ".herd/worktree-sentinel"

SentinelRelPath is the repository-relative sentinel file installed inside every authenticated task worktree (never under the shared root).

View Source
const SessionRelRoot = ".herd/confine-sessions"

SessionRelRoot is the coordinator-owned confinement material root under the shared checkout. It is deliberately outside every task worktree so a confined agent cannot rewrite profile.sb or PATH wrappers (round-5 CRITICAL).

View Source
const SharedRootResidualArtifactRel = ".herd/residual-artifact"

SharedRootResidualArtifactRel is the documented boundary marker that must remain absent under the shared coordinator checkout. It is deliberately ticket-neutral so the production confinement contract does not depend on a historical task artifact.

Variables

View Source
var (
	ErrUnauthenticated     = errors.New("confinement: unauthenticated capability")
	ErrOutsideRoot         = errors.New("confinement: path is outside authenticated worktree")
	ErrSymlink             = errors.New("confinement: symlink path component is not allowed")
	ErrCaseAlias           = errors.New("confinement: case-alias path is not allowed")
	ErrInvalidSentinel     = errors.New("confinement: invalid or missing worktree sentinel")
	ErrHardlink            = errors.New("confinement: hardlink path is not allowed")
	ErrDifferentDevice     = errors.New("confinement: path is on a different device")
	ErrUnsupportedIdentity = errors.New("confinement: unsupported filesystem identity")
	ErrInvalidCommand      = errors.New("confinement: invalid or unbounded command")
	ErrSentinelMutation    = errors.New("confinement: sentinel mutation is not allowed")
)
View Source
var ErrMissingSecret = errors.New("confinement: HERD_CONFINEMENT_SECRET (or HERD_CONTROL_SECRET) is required")

ErrMissingSecret is returned when production issuer construction has no key.

View Source
var ErrOSProbeFailed = errors.New("confinement: OS write confinement probe failed")

ErrOSProbeFailed is returned when a hermetic denial probe still created an outside inode — the sandbox is not effective.

View Source
var ErrOSUnavailable = errors.New("confinement: OS write confinement backend unavailable")

ErrOSUnavailable is returned when production requires OS write isolation and no backend can prove denials on this host.

Functions

func CheckSharedRootResidual

func CheckSharedRootResidual(sharedRoot string) error

CheckSharedRootResidual is a read-only check that the documented residual artifact boundary is absent. It does not digest coordinator .herd state (launch-claims WAL, mail locks) — that caused false confinement_rejected under concurrent launches. The only live shared-root safety signal is residual artifact absence.

func FreezeSession

func FreezeSession(s SessionPaths) error

FreezeSession makes session *files* non-writable after install (0444 profile, 0555 wrappers, 0444 zdot rc). Directories stay 0755 so the coordinator can replace material on the next lease and so test cleanup can unlink files. The confined agent still cannot write these paths: they sit outside the worktree write grant.

Coordinator rewrite paths must thaw before WriteFile (see thawFile): profile install, InstallAgentWrappers, TabEnv, and session receipts. Without thaw, reusing a (task, lease-generation) pair fails closed forever.

func InstallSentinel

func InstallSentinel(worktreeRoot string) (string, error)

InstallSentinel writes the exact sentinel bytes under the worktree if missing or verifies an existing sentinel. It never overwrites a divergent file and never touches the shared root.

func New

func New(root, sentinel string, tuple AuthTuple, issuer Issuer) (Boundary, Capability, error)

New authenticates a fixture worktree using a separate sentinel file.

func ProfileDigest

func ProfileDigest(profilePath string) (string, error)

ProfileDigest returns the SHA-256 of the profile file bytes.

func VerifyAgentWrappers

func VerifyAgentWrappers(binDir, profilePath, profileDigest string, names []string) error

VerifyAgentWrappers fails closed when any expected wrapper is missing, not executable, or no longer embeds both the profile path and content digest.

func WrapperNames

func WrapperNames(parts ...string) []string

WrapperNames returns the distinct PATH entry names that must intercept the live agent. Production launches use herdr kind = Decision.Harness ("pi"); that name MUST be present or sandbox-exec never wraps the process. Extra names (provider, argv0) may also be installed for non-pi experiments.

Types

type AuthTuple

type AuthTuple struct {
	Repository        string
	Task              string
	LeaseID           string
	Lane              string
	Session           string
	SessionGeneration string
	HerdrTab          string
	HerdrPane         string
	ProcessIdentity   string
	ArgvIdentity      string
	PolicyDigest      string
	AllowedRoots      []string
}

AuthTuple is the identity and policy context bound to a capability.

type Binding

type Binding struct {
	Boundary     Boundary   `json:"-"`
	Capability   Capability `json:"-"`
	WorktreeRoot string     `json:"worktree_root"`
	Sentinel     string     `json:"sentinel"`
	SharedRoot   string     `json:"shared_root"`
	PolicyDigest string     `json:"policy_digest"`
	Tuple        AuthTuple  `json:"tuple"`
	ProofNonce   string     `json:"proof_nonce"`
	// ProofMACHex is the issuer MAC over the AuthTuple (hex), so receipts are
	// not forgeable from public fields alone.
	ProofMACHex string `json:"proof_mac"`
	OSBackend   string `json:"os_backend"`
	OSProved    bool   `json:"os_proved"`
	// WrapperInstalled means session-dir wrappers exist and pass integrity
	// checks. It does NOT claim herdr resolved the live agent through them
	// (that requires PATH interception by the external herdr CLI).
	WrapperInstalled bool     `json:"wrapper_installed"`
	ProfilePath      string   `json:"profile_path,omitempty"`
	ProfileDigest    string   `json:"profile_digest,omitempty"`
	WrapperBinDir    string   `json:"wrapper_bin_dir,omitempty"`
	WrapperNames     []string `json:"wrapper_names,omitempty"`
	ReceiptDigest    string   `json:"receipt_digest"`
	// ReceiptMACHex authenticates the receipt with the same HMAC issuer.
	ReceiptMACHex string    `json:"receipt_mac"`
	CreatedAt     time.Time `json:"created_at"`
}

Binding is the durable pre-launch confinement proof.

func Bind

func Bind(id LaunchIdentity, issuer Issuer) (*Binding, error)

Bind authenticates a worktree, issues a production MAC, and returns a capability that policy Authorize* can use. It does not install an OS sandbox and never writes under the shared root.

func (*Binding) AuthorizeRelativeWrite

func (b *Binding) AuthorizeRelativeWrite(path string) error

AuthorizeRelativeWrite is a convenience for production callers.

func (*Binding) CheckSharedRoot

func (b *Binding) CheckSharedRoot() error

CheckSharedRoot re-checks that the residual artifact boundary is still absent.

func (*Binding) MarshalReceipt

func (b *Binding) MarshalReceipt() ([]byte, error)

MarshalReceipt returns durable JSON for launch/control evidence.

func (*Binding) SignReceipt

func (b *Binding) SignReceipt(issuer *HMACIssuer) error

SignReceipt attaches ReceiptMACHex using the HMAC issuer secret material already bound into the capability proof (re-derived via proof fields).

func (*Binding) VerifyReceiptMAC

func (b *Binding) VerifyReceiptMAC(issuer *HMACIssuer) error

VerifyReceiptMAC checks ReceiptMACHex against the issuer.

type Boundary

type Boundary interface {
	AuthorizeWrite(Capability, string) error
	AuthorizeCommand(Capability, Command) error
}

Boundary is a fail-closed policy-planning seam. It does not launch processes, install a sandbox, or make an already-authorized write atomic.

type Capability

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

Capability is intentionally opaque to callers. A root string or sentinel path alone cannot be used as authorization.

type Command

type Command struct {
	Name            string
	ProcessIdentity string
	ArgvIdentity    string
	Paths           []string
	Children        []Command
}

Command describes one process launch and the filesystem paths it may touch. Children are checked recursively, which gives future process supervisors a single propagation seam without claiming enforcement today.

type DarwinSeatbelt

type DarwinSeatbelt struct{}

DarwinSeatbelt uses macOS sandbox-exec with a deny-default file-write profile that only re-allows the authenticated worktree (first-match safe).

func (DarwinSeatbelt) Available

func (DarwinSeatbelt) Available() bool

func (DarwinSeatbelt) InstallAgentWrappers

func (d DarwinSeatbelt) InstallAgentWrappers(session SessionPaths, profilePath string, names []string, realAgentPath string) (string, error)

func (DarwinSeatbelt) Name

func (DarwinSeatbelt) Name() string

func (DarwinSeatbelt) Prepare

func (d DarwinSeatbelt) Prepare(worktree, sharedRoot, branch string, session SessionPaths) (string, error)

func (DarwinSeatbelt) ProveWriteDenials

func (d DarwinSeatbelt) ProveWriteDenials(worktree, sharedRoot, profilePath string, session SessionPaths) error

func (DarwinSeatbelt) Wrap

func (d DarwinSeatbelt) Wrap(cmd *exec.Cmd, profilePath string) error

type Enforcer

type Enforcer struct {
	Issuer *HMACIssuer
	OS     OSBackend
	// ReceiptDir, when set, persists binding receipts as JSON files.
	// Prefer a path outside the agent write domain when possible; worktree
	// receipts remain HMAC-authenticated even if agent-writable.
	ReceiptDir string
}

Enforcer is the production gate used by dispatch before a write-capable agent process starts. Tests inject FakeOS; production uses RequireOS.

func ProductionEnforcer

func ProductionEnforcer() (*Enforcer, error)

ProductionEnforcer builds the fail-closed production enforcer from env.

func (*Enforcer) BindAndProve

func (e *Enforcer) BindAndProve(id LaunchIdentity, prep *PreparedOS) (*Binding, error)

BindAndProve authenticates the worktree and attaches PreparedOS proof.

func (*Enforcer) PrepareOS

func (e *Enforcer) PrepareOS(worktree, sharedRoot, taskRef string, leaseGeneration int64, branch, harness, realAgent string, extraNames ...string) (*PreparedOS, error)

PrepareOS installs profile+wrappers into a coordinator-owned session directory outside the worktree, proves denials (including rewrite of the session profile), and freezes session modes.

harness is the herdr agent-start kind (production: "pi") — a PATH wrapper with that name MUST be installed or sandbox-exec never wraps the agent. realAgent is LookPath'd when not absolute; extraNames may add provider/argv0 aliases for non-production experiments.

type FakeOS

type FakeOS struct {
	Proved  bool
	Wrapped int
	BinDir  string
}

FakeOS is a test-only backend. Production never uses it.

func (*FakeOS) Available

func (f *FakeOS) Available() bool

func (*FakeOS) InstallAgentWrappers

func (f *FakeOS) InstallAgentWrappers(session SessionPaths, profilePath string, names []string, realAgentPath string) (string, error)

func (*FakeOS) Name

func (f *FakeOS) Name() string

func (*FakeOS) Prepare

func (f *FakeOS) Prepare(worktree, sharedRoot, branch string, session SessionPaths) (string, error)

func (*FakeOS) ProveWriteDenials

func (f *FakeOS) ProveWriteDenials(worktree, sharedRoot, profilePath string, session SessionPaths) error

func (*FakeOS) Wrap

func (f *FakeOS) Wrap(cmd *exec.Cmd, profilePath string) error

type HMACIssuer

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

HMACIssuer is the production MAC authority. It signs the complete AuthTuple plus canonical root/sentinel so a capability cannot be forged from strings.

func IssuerFromEnv

func IssuerFromEnv() (*HMACIssuer, error)

IssuerFromEnv loads the production issuer from the process environment.

func NewHMACIssuer

func NewHMACIssuer(secret []byte) (*HMACIssuer, error)

NewHMACIssuer builds an issuer from raw secret bytes. Empty secrets fail closed.

func (*HMACIssuer) Issue

func (i *HMACIssuer) Issue(root, sentinel string, tuple AuthTuple) (IssuerProof, error)

Issue returns a MAC over the canonical confinement binding and a fresh nonce.

func (*HMACIssuer) Verify

func (i *HMACIssuer) Verify(root, sentinel string, tuple AuthTuple, proof IssuerProof) error

Verify checks that proof was issued for the exact root/sentinel/tuple.

type Issuer

type Issuer interface {
	Issue(root, sentinel string, tuple AuthTuple) (IssuerProof, error)
}

Issuer is the missing production authority seam. A real implementation must verify a MAC/signature and nonce issued for the complete AuthTuple.

type IssuerProof

type IssuerProof struct {
	MAC   []byte
	Nonce string
}

type LaunchIdentity

type LaunchIdentity struct {
	Repository        string
	Task              string
	LeaseGeneration   int64
	Lane              string
	Session           string
	SessionGeneration int64
	HerdrTab          string
	HerdrPane         string
	ProcessIdentity   string
	Argv              []string
	WorktreeRoot      string
	SharedRoot        string
	// AgentKind is the herdr kind (codex/grok/…) used with argv[0] for wrappers.
	AgentKind string
}

LaunchIdentity is the production binding input from launch/dispatch. Every field is required for write-capable workers and reviewers.

type OSBackend

type OSBackend interface {
	Name() string
	Available() bool
	// Prepare writes the seatbelt profile to session.Profile (outside worktree).
	Prepare(worktree, sharedRoot, branch string, session SessionPaths) (profilePath string, err error)
	// ProveWriteDenials runs children under profilePath. Also proves the
	// confined process cannot rewrite session.Profile (integrity store).
	ProveWriteDenials(worktree, sharedRoot, profilePath string, session SessionPaths) error
	// Wrap rewrites cmd to run under sandbox-exec with the prepared profile.
	Wrap(cmd *exec.Cmd, profilePath string) error
	// InstallAgentWrappers installs PATH-first wrappers into session.BinDir.
	InstallAgentWrappers(session SessionPaths, profilePath string, names []string, realAgentPath string) (binDir string, err error)
}

OSBackend isolates helper children that exercise write paths and installs the durable agent wrapper that production launches must place first on PATH.

Session material (profile + wrappers) MUST be installed outside the worktree write grant — see SessionPaths / NewSessionPaths.

func ActiveOS

func ActiveOS() OSBackend

ActiveOS returns a live backend or nil when none is usable.

func RequireOS

func RequireOS() (OSBackend, error)

RequireOS fails closed when OS write confinement cannot be proven.

type PreparedOS

type PreparedOS struct {
	Backend       string
	ProfilePath   string
	ProfileDigest string
	BinDir        string
	Names         []string
	Session       SessionPaths
}

PreparedOS is the durable OS material installed before TabCreate. Profile and wrappers live in Session (outside the worktree write grant).

func (*PreparedOS) TabEnv

func (p *PreparedOS) TabEnv(worktree, existingPATH string) ([]string, error)

TabEnv returns environment pairs for Herdr tab create. ZDOTDIR lives in the session directory (outside worktree), not under the agent write grant.

func (*PreparedOS) WrapperResolves

func (p *PreparedOS) WrapperResolves(name string) bool

WrapperResolves reports whether name would resolve under BinDir.

type SessionPaths

type SessionPaths struct {
	Root    string // absolute session directory
	Profile string
	BinDir  string
	ZdotDir string
}

SessionPaths is the durable, outside-worktree layout for one launch.

func NewSessionPaths

func NewSessionPaths(sharedRoot, taskRef string, leaseGeneration int64) (SessionPaths, error)

NewSessionPaths builds <shared>/.herd/confine-sessions/<task>/g<lease>/. The directory is created by the coordinator (unsandboxed) before AgentStart.

Jump to

Keyboard shortcuts

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