Documentation
¶
Index ¶
- Constants
- func BuildEnv(cfg SandboxConfig, proxyPort int) map[string]string
- func CheckPolicy(def CommandDef, args []string) bool
- func CommandFromArgv0(argv0 string) string
- func Prepare(spec Spec) (string, error)
- func RunGitShim(args []string) (int, error)
- func ShellQuote(value string) string
- func ShimLinks(commands []string) map[string]string
- type BasicMountEntry
- type BindMount
- type BoidExecutor
- type BoidOp
- type BoidRequest
- type Broker
- func (b *Broker) GetContext(token string) (TokenContext, bool)
- func (b *Broker) Handle(req *ExecRequest) *ExecResponse
- func (b *Broker) Register(commands map[string]CommandDef, builtinPolicies map[string]BuiltinPolicy, ...) string
- func (b *Broker) RegisterWithSecrets(commands map[string]CommandDef, builtinPolicies map[string]BuiltinPolicy, ...) string
- func (b *Broker) Start(ctx context.Context) error
- func (b *Broker) Stop()
- func (b *Broker) Unregister(token string)
- type BuiltinPolicy
- type CommandDef
- type ExecRequest
- type ExecResponse
- type FileWrite
- type GitBinding
- type GitOp
- type GitRemote
- type GitRequest
- type GitUpstream
- type HookFile
- type Mount
- type MountType
- type ProjectResolver
- type Proxy
- type Sandbox
- type SandboxConfig
- type SecretResolver
- type Spec
- type StreamChunk
- type Symlink
- type TokenContext
Constants ¶
const ( StreamTypeStdout = "stdout" StreamTypeStderr = "stderr" StreamTypeExit = "exit" StreamTypeKill = "kill" )
StreamChunk is one unit in the streaming host-command protocol (Streaming=true).
Broker → Shim: type "stdout"/"stderr" carry Data; type "exit" carries ExitCode. Shim → Broker: type "kill" requests SIGTERM on the host process group.
Variables ¶
This section is empty.
Functions ¶
func BuildEnv ¶
func BuildEnv(cfg SandboxConfig, proxyPort int) map[string]string
BuildEnv generates the environment variables for a sandbox.
func CheckPolicy ¶
func CheckPolicy(def CommandDef, args []string) bool
func CommandFromArgv0 ¶
func Prepare ¶
Prepare writes the 3 scripts (outer / setup / inner) to /tmp and returns the path to the outer script. The caller is responsible for actually running it (via exec.Cmd, syscall.Exec, or a runtime abstraction).
func RunGitShim ¶
func ShellQuote ¶
ShellQuote is the exported version of shellQuote for use outside the package.
Types ¶
type BasicMountEntry ¶
BasicMountEntry describes a bind mount for the sandbox.
func BuildMounts ¶
func BuildMounts(cfg SandboxConfig) []BasicMountEntry
BuildMounts generates the mount entries for a sandbox configuration.
type BindMount ¶
type BindMount struct {
Source string
Target string // if empty, defaults to Source
Mode string // "rw" | "" (ro default)
IsFile bool // if true, treat target as a file (touch before bind, skip type detection)
Optional bool // if true, skip mount when Source does not exist on the host
}
BindMount is the dispatcher-facing DTO for arbitrary bind-mount requests. It is used by the dispatcher boundary (via SandboxSpec.AdditionalBindings) and is converted into Mount entries at the edge. The sandbox layer itself works in terms of Mount only.
type BoidExecutor ¶
type BoidExecutor interface {
ExecuteBoidBuiltin(ctx TokenContext, req *BoidRequest) *ExecResponse
}
BoidExecutor performs typed boid builtin operations after broker validation.
type BoidOp ¶
type BoidOp string
const ( BoidOpJobDone BoidOp = "job_done" BoidOpJobList BoidOp = "job_list" BoidOpJobShow BoidOp = "job_show" BoidOpJobLog BoidOp = "job_log" BoidOpActionSend BoidOp = "action_send" BoidOpAgentStop BoidOp = "agent_stop" BoidOpTaskCreate BoidOp = "task_create" BoidOpTaskGet BoidOp = "task_get" BoidOpTaskUpdate BoidOp = "task_update" BoidOpTaskImport BoidOp = "task_import" BoidOpTaskReopen BoidOp = "task.reopen" BoidOpTaskList BoidOp = "task_list" BoidOpTaskNotify BoidOp = "task_notify" BoidOpTaskAnswer BoidOp = "task_answer" BoidOpTaskDelete BoidOp = "task_delete" )
type BoidRequest ¶
type BoidRequest struct {
Op BoidOp `json:"op"`
JobID string `json:"job_id,omitempty"`
TaskID string `json:"task_id,omitempty"`
TaskField string `json:"task_field,omitempty"`
// ProjectID is extracted by the shim for broker authorization / project
// resolver; it is also present inside CreatePatch when the YAML includes
// project_id. The executor prefers createReq.ProjectID from CreatePatch
// and falls back to this field, then to ctx.ProjectID.
ProjectID string `json:"project_id,omitempty"`
ExitCode int `json:"exit_code,omitempty"`
Output string `json:"output,omitempty"`
// CreatePatch is a JSON-serialised api.CreateTaskRequest. The shim builds
// it from the full YAML map so that every field (including previously
// dropped ones such as instructions, traits, readonly, worktree,
// branch_prefix, id) is forwarded without enumeration.
CreatePatch json.RawMessage `json:"create_patch,omitempty"`
// UpdatePatch is a JSON-serialised api.UpdateTaskRequest. The shim
// assembles it from --patch-file and/or individual flags (--title,
// --description, --payload-file).
UpdatePatch json.RawMessage `json:"update_patch,omitempty"`
// task import fields
ImportTasks []json.RawMessage `json:"import_tasks,omitempty"`
ImportProjectOverride string `json:"import_project_override,omitempty"`
// task list fields
WorkspaceID string `json:"workspace_id,omitempty"`
Status string `json:"status,omitempty"`
Limit int `json:"limit,omitempty"`
// task notify fields
Message string `json:"message,omitempty"`
Ask string `json:"ask,omitempty"`
QuestionID string `json:"question_id,omitempty"`
SessionID string `json:"session_id,omitempty"`
Progress string `json:"progress,omitempty"`
Done string `json:"done,omitempty"`
Fail string `json:"fail,omitempty"`
// task answer fields
Answer string `json:"answer,omitempty"`
// task delete fields
Force bool `json:"force,omitempty"`
// action send fields
ActionType string `json:"action_type,omitempty"`
Payload []byte `json:"payload,omitempty"`
}
type Broker ¶
type Broker struct {
SocketPath string
BoidBinary string
BoidExecutor BoidExecutor
ProjectResolver ProjectResolver
// contains filtered or unexported fields
}
func (*Broker) GetContext ¶
func (b *Broker) GetContext(token string) (TokenContext, bool)
func (*Broker) Handle ¶
func (b *Broker) Handle(req *ExecRequest) *ExecResponse
func (*Broker) Register ¶
func (b *Broker) Register(commands map[string]CommandDef, builtinPolicies map[string]BuiltinPolicy, ctx TokenContext) string
func (*Broker) RegisterWithSecrets ¶
func (b *Broker) RegisterWithSecrets(commands map[string]CommandDef, builtinPolicies map[string]BuiltinPolicy, ctx TokenContext, resolver SecretResolver) string
func (*Broker) Unregister ¶
type BuiltinPolicy ¶
type BuiltinPolicy struct {
AllowedOps map[string]struct{}
// AllowedCwdRoots lists additional cwd roots permitted for this builtin
// beyond the per-token entry root (project/worktree dir). Used so that
// e.g. gate jobs can target /tmp or the host project dir without the
// broker needing to know the role itself.
AllowedCwdRoots []string
}
BuiltinPolicy defines which operations are permitted for a named builtin command. It is stamped at token registration time by the planner and checked at dispatch time by the broker, keeping all role-based authorization logic outside the broker itself.
func (BuiltinPolicy) Allows ¶
func (p BuiltinPolicy) Allows(op string) bool
Allows reports whether op is in the allowed set.
func (BuiltinPolicy) AllowsCwd ¶
func (p BuiltinPolicy) AllowsCwd(cwd string) bool
AllowsCwd reports whether cwd is within any of the policy's additional cwd roots.
type CommandDef ¶
type CommandDef struct {
Name string
Path string
AllowedPatterns []string
DeniedPatterns []string
AllowedSubcommands []string
AllowStdin bool
Env map[string]string
}
CommandDef is the canonical sandbox-side policy for brokered host commands. Dispatcher and orchestrator mirror this shape as transport data, but the policy semantics live here.
type ExecRequest ¶
type ExecRequest struct {
Command string `json:"command"`
Args []string `json:"args"`
Cwd string `json:"cwd,omitempty"`
Stdin []byte `json:"stdin,omitempty"`
Token string `json:"token"`
Boid *BoidRequest `json:"boid,omitempty"`
Git *GitRequest `json:"git,omitempty"`
Streaming bool `json:"streaming,omitempty"`
}
type ExecResponse ¶
type ExecResponse struct {
ExitCode int `json:"exit_code"`
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
}
func RunBoidShim ¶
func RunBoidShim(args []string) (*ExecResponse, error)
func ShimExec ¶
func ShimExec(brokerSocket, argv0 string, args []string, stdin []byte) (*ExecResponse, error)
ShimExec sends a host command request to the broker using the streaming protocol so that stdout/stderr is forwarded in real-time and signals (Ctrl-C) are propagated to the host process group.
type FileWrite ¶
FileWrite describes a file to materialize inside the sandbox. Content is written verbatim (shell-quoted at render time); the parent directory is created with mkdir -p beforehand.
type GitBinding ¶
type GitRequest ¶
type GitRequest struct {
Op GitOp `json:"op"`
Remote string `json:"remote,omitempty"`
Refspecs []string `json:"refspecs,omitempty"`
DryRun bool `json:"dry_run,omitempty"`
Verbose bool `json:"verbose,omitempty"`
Quiet bool `json:"quiet,omitempty"`
Prune bool `json:"prune,omitempty"`
Tags bool `json:"tags,omitempty"`
Force bool `json:"force,omitempty"`
Porcelain bool `json:"porcelain,omitempty"`
ForceWithLease bool `json:"force_with_lease,omitempty"`
Delete bool `json:"delete,omitempty"`
}
type GitUpstream ¶
type HookFile ¶
type HookFile struct {
Source string // host-side absolute path
TargetName string // filename inside sandbox .boid/hooks/
}
HookFile describes a single hook file to bind-mount into the sandbox. Retained as a helper shape; dispatcher composes these into Spec.Mounts.
type Mount ¶
type Mount struct {
Source string // host path (empty for tmpfs)
Target string // absolute path inside sandbox
Type MountType
ReadOnly bool
Slave bool // mount --make-rslave after mounting
IsFile bool // target is a file, not a directory
DetectType bool // detect file vs dir at runtime (if/elif)
Guard string // shell test expression; if non-empty, wrap in if [ $Guard ]; then
NeedsDirs []string // subdirs to create under Target before ro remount
}
Mount describes a single filesystem mount applied inside the sandbox. Types: bind, rbind, tmpfs. Flags cover read-only remount, file vs dir handling, slave propagation, guards, and required sub-directory creation.
type ProjectResolver ¶
ProjectResolver maps a project reference (UUID, exact name, or partial name) to a concrete project UUID. The broker calls it just before the UUID-based AllowsProject authorization check so that sandbox-side callers (e.g. plan agents) can use project names while the broker continues to enforce workspace isolation in UUID space. When nil, the broker passes refs through verbatim (tests and UUID-only callers).
type Sandbox ¶
type Sandbox interface {
Setup(cfg SandboxConfig) error
Shell(windowName string, cmd string) (string, error)
Cleanup() error
}
Sandbox provides an isolated execution environment.
type SandboxConfig ¶
type SandboxConfig struct {
ProjectDir string
WorkspaceDirs map[string]string // project-id -> dir (readonly mounts)
TaskFile string
HostCommands []string
Bindings []string
Env map[string]string
BoidBinary string
BrokerSocket string
ServerSocket string
}
SandboxConfig holds configuration for sandbox setup.
type SecretResolver ¶
type Spec ¶
type Spec struct {
// ID is used to namespace the generated /tmp/boid-<ID>-{outer,setup,inner}.sh files.
ID string
// --- Filesystem primitives ---
// Mounts are applied in order to compose the sandbox root filesystem.
Mounts []Mount
// Files are materialized inside the sandbox before the entry command runs.
Files []FileWrite
// Symlinks are created inside the sandbox (e.g. /opt/boid/bin/<cmd> → boid).
Symlinks []Symlink
// --- Network ---
// ProxyPort, when > 0, engages the nft drop policy + HTTP proxy env vars.
ProxyPort int
// --- Process ---
// Argv is the program and arguments to invoke (POSIX argv).
Argv []string
// WorkDir is the cwd for the entry process.
WorkDir string
// Env is exported before the entry command runs.
Env map[string]string
// StdinBytes, when non-empty, is piped into the entry's stdin.
StdinBytes []byte
// StdoutCaptureFile, when non-empty, redirects stdout to that sandbox-internal path.
StdoutCaptureFile string
// ExitScript is wrapped in `trap '<ExitScript>' EXIT`. Empty = no trap (suits exec-replaced shells).
ExitScript string
// TTY, when true, preserves the caller's TTY through pasta so Argv can run as a full terminal app.
TTY bool
// --- Bookkeeping ---
// RootDir, if non-empty, is used as the sandbox ROOT directory so Go-side
// cleanup can remove it after exit. If empty, setup script creates one with mktemp.
RootDir string
// CleanupPaths are removed by outer.sh after pasta returns (used for staging dirs).
// Removal runs outside the sandbox mount namespace, so still-active bind
// mounts cannot cause rm to traverse onto host files.
CleanupPaths []string
}
Spec describes a sandbox invocation in primitives only. The sandbox layer knows nothing about Role / Task / Job / Broker / Gate / Hook — all of those are the caller's concern. Everything needed to build and run the sandbox must already be present as mounts, files, env, argv, etc.
type StreamChunk ¶
type Symlink ¶
type Symlink struct {
LinkPath string // absolute path inside sandbox (where the symlink is created)
LinkTarget string // what the symlink points to (resolved relative to LinkPath)
}
Symlink describes a symlink to create inside the sandbox.
type TokenContext ¶
type TokenContext struct {
JobID string
TaskID string
ProjectID string
WorkspaceID string
AllowedProjectIDs []string
Role string
// ProjectDir is the project's host-side work directory. Independent of
// spec.Visibility.ProjectDir (which drives sandbox mount layout and is
// intentionally empty for gate jobs): host-side operations the broker
// performs on behalf of the sandbox (git binding, host-command cwd) have
// their own notion of "which project are we operating on" that doesn't
// care whether the sandbox itself can see the tree.
ProjectDir string
WorktreeDir string
}
func (TokenContext) AllowsProject ¶
func (c TokenContext) AllowsProject(projectID string) bool