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
- Variables
- func CheckSharedRootResidual(sharedRoot string) error
- func FreezeSession(s SessionPaths) error
- func InstallSentinel(worktreeRoot string) (string, error)
- func New(root, sentinel string, tuple AuthTuple, issuer Issuer) (Boundary, Capability, error)
- func ProfileDigest(profilePath string) (string, error)
- func VerifyAgentWrappers(binDir, profilePath, profileDigest string, names []string) error
- func WrapperNames(parts ...string) []string
- type AuthTuple
- type Binding
- type Boundary
- type Capability
- type Command
- type DarwinSeatbelt
- func (DarwinSeatbelt) Available() bool
- func (d DarwinSeatbelt) InstallAgentWrappers(session SessionPaths, profilePath string, names []string, realAgentPath string) (string, error)
- func (DarwinSeatbelt) Name() string
- func (d DarwinSeatbelt) Prepare(worktree, sharedRoot, branch string, session SessionPaths) (string, error)
- func (d DarwinSeatbelt) ProveWriteDenials(worktree, sharedRoot, profilePath string, session SessionPaths) error
- func (d DarwinSeatbelt) Wrap(cmd *exec.Cmd, profilePath string) error
- type Enforcer
- type FakeOS
- func (f *FakeOS) Available() bool
- func (f *FakeOS) InstallAgentWrappers(session SessionPaths, profilePath string, names []string, realAgentPath string) (string, error)
- func (f *FakeOS) Name() string
- func (f *FakeOS) Prepare(worktree, sharedRoot, branch string, session SessionPaths) (string, error)
- func (f *FakeOS) ProveWriteDenials(worktree, sharedRoot, profilePath string, session SessionPaths) error
- func (f *FakeOS) Wrap(cmd *exec.Cmd, profilePath string) error
- type HMACIssuer
- type Issuer
- type IssuerProof
- type LaunchIdentity
- type OSBackend
- type PreparedOS
- type SessionPaths
Constants ¶
const PolicyDigestV1 = "confinement-policy-v1-fac190-r3"
PolicyDigestV1 is the fixed policy identity for this confinement generation.
const SecretEnv = "HERD_CONFINEMENT_SECRET"
SecretEnv is the preferred production secret for confinement MACs.
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.
const SentinelRelPath = ".herd/worktree-sentinel"
SentinelRelPath is the repository-relative sentinel file installed inside every authenticated task worktree (never under the shared root).
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).
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 ¶
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") )
var ErrMissingSecret = errors.New("confinement: HERD_CONFINEMENT_SECRET (or HERD_CONTROL_SECRET) is required")
ErrMissingSecret is returned when production issuer construction has no key.
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.
ErrOSUnavailable is returned when production requires OS write isolation and no backend can prove denials on this host.
Functions ¶
func CheckSharedRootResidual ¶
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 ¶
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 ProfileDigest ¶
ProfileDigest returns the SHA-256 of the profile file bytes.
func VerifyAgentWrappers ¶
VerifyAgentWrappers fails closed when any expected wrapper is missing, not executable, or no longer embeds both the profile path and content digest.
func WrapperNames ¶
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"`
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 ¶
AuthorizeRelativeWrite is a convenience for production callers.
func (*Binding) CheckSharedRoot ¶
CheckSharedRoot re-checks that the residual artifact boundary is still absent.
func (*Binding) MarshalReceipt ¶
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
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 ¶
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 ¶
FakeOS is a test-only backend. Production never uses it.
func (*FakeOS) InstallAgentWrappers ¶
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
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 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
// 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.
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.