Documentation
¶
Overview ¶
Package supervisor runs one long-lived child process for a supervised harness runner and keeps it running.
The child is always the agent's OWN executable invoked with a different subcommand (`railgrid runner run ...`), so a runner upgrades with the agent and there is no second artifact to distribute or verify.
Three properties are load-bearing and are enforced here rather than left to callers:
- The child never runs as uid 0. A root agent MUST hand over an explicit non-root uid/gid; a non-root agent runs the child as itself. There is no third case.
- The child's environment is built from scratch. Nothing is inherited, so the hub bearer token, the agent kubeconfig path and any API keys in the agent's environment cannot reach a code-execution child. This is the same reasoning as the Codex adapter's safeEnv, taken one step further: safeEnv subtracts from os.Environ(), this adds to an empty slice.
- Stop kills the whole process GROUP. The runner spawns Codex, which spawns git; signalling only the direct child would leave those behind.
Index ¶
Constants ¶
const ( // DefaultInitialBackoff is the pause after the first unexpected exit. DefaultInitialBackoff = time.Second // MaxBackoff caps the restart pause. A wedged runner must keep retrying — // the host may recover — but not faster than once a minute. MaxBackoff = 60 * time.Second // DefaultStopGrace is how long a child gets to exit after SIGTERM before // the group is killed. DefaultStopGrace = 10 * time.Second // DefaultMaxLogBytes bounds the runner log file. A runner that crash-loops // while printing must not fill the host's disk. DefaultMaxLogBytes int64 = 8 << 20 )
const DefaultPath = "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin"
DefaultPath is the search path handed to runner children. It matches the LaunchDaemon plist so a Homebrew-installed Codex is found on macOS.
const InheritUID = -1
InheritUID tells the supervisor to run the child as the agent's own account. It is only legal when the agent itself is not root.
Variables ¶
This section is empty.
Functions ¶
func ChildEnv ¶
ChildEnv builds a child environment from scratch. It deliberately takes only what a process needs to find its home and its tools — never os.Environ().
func ReclaimStaleHolder ¶
ReclaimStaleHolder terminates the process that holds lockPath when that lock belongs to a child of an agent that no longer exists.
The lock is a flock whose content is the holder's pid, written by the child that acquired it. A child outlives its agent when the agent is killed rather than stopped (macOS has no parent-death signal), and it then keeps the lock and the listening port: the new agent's own child can never start, while the orphan keeps answering the port with a stale build and configuration. This is only called before the first child of an agent process is started, so anything holding the lock at that point is such an orphan; a lock taken while the agent runs is never touched here.
It returns the pid it terminated, or 0 when the lock was free.
Types ¶
type Config ¶
type Config struct {
// Name identifies the child in log lines and errors.
Name string
// Executable is an absolute path. Callers pass os.Executable().
Executable string
// Args are the arguments after the executable name.
Args []string
// Dir is the child's working directory.
Dir string
// Env is the COMPLETE environment for the child. Nothing is added and
// nothing is inherited; build it with ChildEnv.
Env []string
// UID/GID are the account the child runs as, or InheritUID to run as the
// agent's own (non-root) account.
UID int
GID int
// LogPath receives the child's stdout and stderr, owner-only and bounded.
LogPath string
// MaxLogBytes bounds LogPath; zero means DefaultMaxLogBytes.
MaxLogBytes int64
// StopGrace is the SIGTERM-to-SIGKILL window; zero means DefaultStopGrace.
StopGrace time.Duration
// InitialBackoff is the first restart pause; zero means
// DefaultInitialBackoff. Tests shorten it.
InitialBackoff time.Duration
}
Config describes one supervised child.
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor owns at most one running child at a time.
func New ¶
func New(cfg Config) (*Supervisor, error)
New validates cfg and returns a stopped supervisor. It refuses a uid-0 target outright: a code-execution child of a root agent must drop privileges, and "the caller forgot" must fail here rather than at exec time.
func (*Supervisor) Alive ¶
func (s *Supervisor) Alive() bool
Alive reports whether a child process is currently running.
func (*Supervisor) LastError ¶
func (s *Supervisor) LastError() error
LastError is the most recent unexpected child exit or launch failure, or nil.
func (*Supervisor) Start ¶
func (s *Supervisor) Start(ctx context.Context)
Start launches the child and keeps restarting it until Stop. Calling Start on an already-running supervisor is a no-op, so a reconcile loop can call it on every pass.
func (*Supervisor) Starts ¶
func (s *Supervisor) Starts() int
Starts is the number of child launches so far. Tests use it to observe restarts; the harness plane uses it only for log lines.