Documentation
¶
Index ¶
- Variables
- func BuildExecJobSpec(input SessionJobInput, argv []string, interactive bool) (*orchestrator.JobSpec, error)
- func BuildSandboxSpec(spec *orchestrator.JobSpec, rt SandboxRuntimeInfo) (sandbox.Spec, error)
- func BuildSessionJobSpec(input SessionJobInput) (*orchestrator.JobSpec, error)
- func CaptureUpstreamURL(dir string) (string, error)
- func CreateJob(dbtx db.DBTX, j *Job) error
- func FindDaemonShutdownAbortedTasks(conn *sql.DB) ([]string, error)
- func GenerateKey() []byte
- func GitOriginURL(dir string) (string, error)
- func LoadOrCreateKey(path string) ([]byte, error)
- func MarkStaleAwaitingTasksAborted(conn *sql.DB) (int, error)
- func MarkStaleExecutingTasksAborted(conn *sql.DB) (int, error)
- func MarkStaleJobsFailed(dbtx db.DBTX) error
- func NormalizeOriginURL(raw string) (string, error)
- func PoliciesToSandbox(in map[string]orchestrator.BuiltinPolicy) map[string]sandbox.BuiltinPolicy
- func ReadTranscript(rootDir, runtimeID string) ([]byte, error)
- func ResolveHostCommands(builtins []string, hostCommands map[string]orchestrator.CommandDef, ...) (byPath map[string]orchestrator.CommandDef, ...)
- func StatTranscript(rootDir, runtimeID string) (os.FileInfo, error)
- func UpdateJob(dbtx db.DBTX, j *Job) error
- func WorkspaceHomesDir(runtimesDir string) (string, error)
- type CommandBroker
- type Job
- type JobCompletionResult
- type JobContextSnapshot
- type JobEventSink
- type JobFilter
- type JobRepository
- func (r *JobRepository) CreateJob(job *Job) error
- func (r *JobRepository) GetJob(id string) (*Job, error)
- func (r *JobRepository) ListJobsByTask(taskID string) ([]*Job, error)
- func (r *JobRepository) ListJobsFiltered(filter JobFilter) ([]*Job, error)
- func (r *JobRepository) UpdateJob(job *Job) error
- type JobRuntime
- type JobStatus
- type LocalRuntime
- func (r *LocalRuntime) Attach(ctx context.Context, runtimeID string, req RuntimeAttachRequest) error
- func (r *LocalRuntime) CloseInputRuntime(runtimeID string) error
- func (r *LocalRuntime) Resize(_ context.Context, runtimeID string, size TerminalSize) error
- func (r *LocalRuntime) Signal(_ context.Context, runtimeID string, sig syscall.Signal) error
- func (r *LocalRuntime) Start(_ context.Context, spec RuntimeStartSpec) (*RuntimeHandle, error)
- func (r *LocalRuntime) Stop(ctx context.Context, runtimeID string) error
- func (r *LocalRuntime) SubscribeRuntime(runtimeID string) ([]byte, <-chan []byte, func(), bool)
- func (r *LocalRuntime) SupportsAttach(runtimeID string) bool
- func (r *LocalRuntime) Wait(ctx context.Context, runtimeID string) (RuntimeExit, error)
- func (r *LocalRuntime) WriteInputRuntime(runtimeID string, data []byte) error
- type OrchestratorAdapter
- type PeerAdvertise
- type PreparedSandbox
- type ProjectLookup
- type ProxyAllocator
- type Runner
- func (r *Runner) CleanupTaskWindow(taskID string)
- func (r *Runner) CloseInput(jobID string) error
- func (r *Runner) CompleteJob(jobID string, result JobCompletionResult)
- func (r *Runner) Dispatch(ctx context.Context, spec *orchestrator.JobSpec, ...) (jobID string, dispatchErr error)
- func (r *Runner) JobContext(jobID string) (JobContextSnapshot, bool)
- func (r *Runner) ResizeRuntime(jobID string, size TerminalSize) error
- func (r *Runner) SignalJobRuntime(runtimeID string, sig syscall.Signal)
- func (r *Runner) StopJobRuntime(runtimeID string)
- func (r *Runner) Subscribe(jobID string) (snapshot []byte, ch <-chan []byte, cancel func(), ok bool)
- func (r *Runner) UnregisterJob(jobID string)
- func (r *Runner) WaitForJob(jobID string) <-chan JobCompletionResult
- func (r *Runner) WaitForJobCtx(ctx context.Context, jobID string) (JobCompletionResult, error)
- func (r *Runner) WriteInput(jobID string, data []byte) error
- type RuntimeAttachRequest
- type RuntimeExit
- type RuntimeHandle
- type RuntimeInputWriter
- type RuntimeStartSpec
- type RuntimeSubscriber
- type SandboxPreparer
- type SandboxRuntimeInfo
- type SecretResolver
- type SecretStore
- type SessionJobInput
- type TerminalSize
- type WireConfig
- type WorkspaceEnvHostCommand
- type WorkspaceEnvRejectRule
- type WorkspaceEnvView
- type WorkspaceLookup
Constants ¶
This section is empty.
Variables ¶
var ErrRuntimeUnsupported = errors.New("job runtime operation is not supported")
Functions ¶
func BuildExecJobSpec ¶ added in v0.0.6
func BuildExecJobSpec(input SessionJobInput, argv []string, interactive bool) (*orchestrator.JobSpec, error)
BuildExecJobSpec is the shell-harness variant of BuildSessionJobSpec used by `boid exec` to run a user-supplied argv inside the project sandbox. It reuses BuildSessionJobSpec for project trait inheritance and overrides the result:
- Kind = JobKindExec (TUI displays an "exec" badge instead of "session")
- Argv = the user's argv (runner-inner-child hands this to the shell adapter)
- Interactive = caller's tty detection (sessions are always PTY-attached; exec may be piped from a non-TTY stdin)
- DisplayName falls back to argv[0] when the caller leaves it empty
HarnessType in input is ignored and forced to "shell"; argv must be non-empty. Propagates any error from BuildSessionJobSpec (in particular a session clone-declaration failure — see buildSessionCloneDeclaration).
func BuildSandboxSpec ¶
func BuildSandboxSpec(spec *orchestrator.JobSpec, rt SandboxRuntimeInfo) (sandbox.Spec, error)
BuildSandboxSpec turns a business-level JobSpec and dispatcher-side runtime facts into a primitive sandbox.Spec. It contains no role-aware switch: the mount set and environment are derived purely from JobSpec.Visibility, HostCommands, Instruction and Argv.
func BuildSessionJobSpec ¶ added in v0.0.6
func BuildSessionJobSpec(input SessionJobInput) (*orchestrator.JobSpec, error)
BuildSessionJobSpec converts a resolved SessionJobInput into a JobSpec (JobKindSession, adapter-bound HarnessType). The result is fed straight to dispatcher.Runner which builds the sandbox and hands the agent process to adapter.Run().
Returns an error when the sandbox-internal clone declaration cannot be built for a non-empty ProjectWorkDir — see buildSessionCloneDeclaration's doc comment. The caller (WebUI POST /sessions, `boid exec` CLI) must surface that as a user-visible error rather than silently degrading, since the cutover contract (docs/plans/git-gateway-cutover.md PR6) requires a clone-based dispatch for every project-visible job.
func CaptureUpstreamURL ¶ added in v0.0.10
CaptureUpstreamURL reads dir's `git config --get remote.origin.url` and normalizes it to an HTTPS URL suitable for a project's upstream_url. Returns an error if dir has no git repository, no origin remote is configured, or the origin URL is in an unrecognized form — the caller (project registration / reload / startup backfill) decides how to react.
func FindDaemonShutdownAbortedTasks ¶
FindDaemonShutdownAbortedTasks returns IDs of tasks currently in aborted status whose most recent aborted-transition action carries payload.code == "daemon_shutdown". Use this on daemon startup to auto-reopen tasks that were interrupted by the previous shutdown.
"Most recent" means the latest action with to_status='aborted' for that task (ordered by created_at desc). If a task was aborted by daemon_shutdown and then aborted again later for another reason, the later code wins and the task is NOT returned — matching the intuition that only freshly-shutdown tasks deserve auto-reopen.
func GitOriginURL ¶ added in v0.0.9
GitOriginURL returns the `git config --get remote.origin.url` value for dir, or an error if git is missing, dir is not a repo, or no origin is configured. It deliberately uses cmd.Dir rather than `git -C dir`: this repo's sandbox git wrapper rejects `-C`, and cmd.Dir works everywhere (production and sandboxed callers alike).
This is the production getOriginURL implementation passed to ResolveHostCommands; it is exported so callers outside this package (internal/server/api_store.go) can pass it too.
func LoadOrCreateKey ¶
LoadOrCreateKey loads the master key from the given path, or creates a new one if it doesn't exist.
func MarkStaleAwaitingTasksAborted ¶ added in v0.0.7
MarkStaleAwaitingTasksAborted does the same for tasks left in "awaiting" status from a previous crash or restart. After a restart no agent is parked in the (purely in-memory) BlockingAskRegistry, so every awaiting task is a zombie with no live agent behind it — reclaim it. It carries the same daemon_shutdown code as the executing path, so the startup auto-reopen sweep (FindDaemonShutdownAbortedTasks) restarts it and the agent re-asks if needed.
func MarkStaleExecutingTasksAborted ¶
MarkStaleExecutingTasksAborted transitions all tasks in "executing" status to "aborted" and records a daemon_shutdown abort action for each. Call this on server startup after MarkStaleJobsFailed. Returns the number of tasks transitioned.
func MarkStaleJobsFailed ¶
MarkStaleJobsFailed marks all running jobs as failed. Call this on server startup to clean up jobs left in running state from a previous crash or restart.
func NormalizeOriginURL ¶ added in v0.0.10
NormalizeOriginURL converts a git remote origin URL into the HTTPS form used as a project's upstream_url (docs/plans/git-gateway-cutover.md PR2: "project → 上流 URL の明示マッピング"). HTTPS URLs are returned unchanged ("既に HTTPS URL ならそのまま"); scp-like SSH (`git@host:owner/repo.git`) and `ssh://` URLs are rewritten to `https://host/owner/repo.git` (`http://` is likewise upgraded to `https://`, reusing the same host/path extraction). Returns an error for an empty or unrecognized URL form.
This is a pure function so it can be unit tested without a real git repository; CaptureUpstreamURL below composes it with the actual `git config` read.
func PoliciesToSandbox ¶
func PoliciesToSandbox(in map[string]orchestrator.BuiltinPolicy) map[string]sandbox.BuiltinPolicy
PoliciesToSandbox converts the orchestrator-owned neutral BuiltinPolicy representation into the sandbox-layer BuiltinPolicy the broker understands. dispatcher is the only layer allowed to bridge both sides.
func ReadTranscript ¶
ReadTranscript reads the transcript.log for the given runtimeID from rootDir. Returns os.ErrNotExist if the transcript file does not exist (e.g. runtime was gc'd).
func ResolveHostCommands ¶
func ResolveHostCommands( builtins []string, hostCommands map[string]orchestrator.CommandDef, projectDir string, lookPath func(string) (string, error), getOriginURL func(string) (string, error), ) (byPath map[string]orchestrator.CommandDef, byName map[string]orchestrator.CommandDef, err error)
ResolveHostCommands turns the orchestrator-side host command map (keyed by the user-declared name) into two views over the same resolved command data (docs/plans/phase5-shim-and-task-context.md, "5a: shim 固定ディレクトリ化" PR1):
- byPath is keyed by the absolute host path each host command was resolved to (also written back into each entry's Path). It used to drive the shim's per-host-path bind mount and PATH parent entries; as of the 5a-3 cutover (PR3) that scheme is retired — shims are now symlinks under a fixed sandbox-internal directory (dispatcher.sandboxShimBinDir), so no dispatcher / broker / shim code paths key off the absolute host path any more. It is returned here as an inert byproduct of the pass (the map is already built for the dedup filter) and can be dropped once no downstream consumer needs the "no two host commands collide on the same host binary" invariant it happens to encode. No production caller still reads it.
- byName is keyed by the short (user-declared) command name — the canonical view. Consumed by the broker's policy table (CommandBroker.RegisterCommands, BOID_HOST_COMMAND_RULES via buildHostCommandRulesEnv) and by the sandbox layout (hostCommandSymlinks, buildPATH). As of 5a-3 this is also the only shape the shim sends as ExecRequest.Command (sandbox.CommandFromArgv0 → the shim's bind-mount basename, which equals the declared short name by construction under sandboxShimBinDir); the broker's pre-5a-3 Path-scan fallback for the retired absolute-path shape was dropped in the same change.
Every entry appears in both maps under its own key with identical field values (byName[def.Name] == byPath[absPath]).
The names "boid", "git", and "fetch" are excluded, each for a different reason: "boid" has a dedicated bind mount + builtin policy elsewhere; "fetch" is a broker builtin (`FetchRequest`) without a host binary at all; "git" is neither a broker builtin nor a shim — it's a real binary reached via the base rbind of /usr, but the name is reserved here so a user `host_commands.git:` entry doesn't try to overlay a shim onto that path and break the sandbox-side git that the git gateway clone flow depends on. Neither map ever contains an entry for these three names.
`projectDir` is used to resolve relative paths declared in host_commands.<name>.path, and as the working directory for the origin URL lookup that expands `${boid:repo_slug}` in Env values (see docs/plans/host-command-contract.md item 3). `lookPath` and `getOriginURL` are parameterized for tests; production callers pass exec.LookPath and GitOriginURL. There are only two production call sites (runner.go, api_store.go), which is few enough that threading a parameter through them (matching the existing lookPath convention) is simpler than a package-level var seam.
func StatTranscript ¶
StatTranscript returns os.FileInfo for the transcript.log of the given runtimeID. Returns os.ErrNotExist if the file does not exist (e.g. runtime was gc'd).
func WorkspaceHomesDir ¶ added in v0.0.13
WorkspaceHomesDir returns ~/.local/share/boid/homes, the parent directory under which every workspace's home lives (docs/plans/home-workspace-volume.md 「レイアウト」). Unlike runtimes/, this directory is never GC'd — workspace homes are persistent (PR5 wires deletion to `workspace remove`).
Prefers deriving from runtimesDir when non-empty: RuntimesDir is wired by server/wire.go as runtimesDirFor(cfg) — filepath.Dir(cfg.DBPath) (or cfg.SocketPath's dir when DBPath is ":memory:") + "/runtimes" — the same per-installation data root skills/ already lives under (see server.New's skillsDir). Deriving homes/ from the same root means a daemon instance running against a non-default DBPath (an isolated data dir, e.g. every test in this codebase that spins up a real server) gets its own isolated homes/ next to its own DB/runtimes, instead of every such instance converging on one global ~/.local/share/boid/homes and leaking into the real developer machine's home directory during `go test`. Falls back to the $XDG_DATA_HOME / ~/.local/share/boid convention only when runtimesDir is empty (minimal test wiring that constructs a bare &Runner{}, or a daemon build that never wired RuntimesDir).
Exported (Phase 4 PR5, docs/plans/home-workspace-volume.md) as a pure free function — independent of any *Runner state — so internal/api's handlers (GET /api/workspaces/{slug} size reporting, POST /api/gc's workspace_homes listing, DELETE /api/workspaces/{slug}'s home dir deletion) can resolve the exact same homes/ directory the dispatcher itself uses, from the same runtimesDirFor(cfg) value server/wire.go already threads through those handlers, without needing a live *Runner.
Types ¶
type CommandBroker ¶
type CommandBroker interface {
// RegisterCommands takes the short-name-keyed view of resolved host
// commands (ResolveHostCommands' byName return value —
// docs/plans/phase5-shim-and-task-context.md, "5a: shim
// 固定ディレクトリ化" PR1). As of the 5a-3 cutover (PR3) this is the sole
// broker key — the shim always sends the declared short name as
// ExecRequest.Command (sandbox.CommandFromArgv0 — every shim's
// bind-mount basename equals its declared name by construction under
// sandboxShimBinDir), and the broker's pre-5a-3 absolute-path Path-scan
// fallback was dropped in the same change.
RegisterCommands(commands map[string]orchestrator.CommandDef, builtinPolicies map[string]sandbox.BuiltinPolicy, ctx sandbox.TokenContext, resolve SecretResolver) string
UnregisterCommandToken(token string)
SocketPath() string
}
CommandBroker is the dispatcher-owned behavior contract for host command brokering. The execution context is the canonical sandbox.TokenContext so adapters do not need to translate between dispatcher- and sandbox-side context shapes.
type Job ¶
type Job struct {
ID string `json:"id"`
TaskID string `json:"task_id"`
ProjectID string `json:"project_id"`
HandlerID string `json:"handler_id"`
DisplayName string `json:"display_name,omitempty"` // persisted via the jobs.display_name column (migration 0027)
Role string `json:"role"`
RuntimeID string `json:"runtime_id,omitempty"`
Interactive bool `json:"interactive"`
TTY bool `json:"tty"`
Status JobStatus `json:"status"`
ExitCode int `json:"exit_code,omitempty"`
Output string `json:"output,omitempty"`
ExecutionState string `json:"execution_state,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type JobCompletionResult ¶
JobCompletionResult is the result delivered via WaitForJobCtx/CompleteJob.
type JobContextSnapshot ¶ added in v0.0.13
type JobContextSnapshot struct {
Instructions []orchestrator.RoutedInstruction
Env WorkspaceEnvView
Payload json.RawMessage
PayloadPatchAllowedTraits []orchestrator.TraitType
}
JobContextSnapshot captures the per-job data the Phase 5b PR1 task-context RPCs (`boid task instructions` / `boid task env` / `boid task payload`, docs/plans/phase5-shim-and-task-context.md) need but which has no standalone DB representation to re-derive live from:
- Instructions: the job's own routed instruction (JobSpec.Instruction, see orchestrator.DispatchPlanner.PlanHook's selectInstruction). This is NOT the task's "active" instruction — orchestrator.Evaluator fires every agent-kind hook whose agent appears anywhere in the instruction history (extractInstructionAgents), not just the most recent entry, so a claude-code hook and a codex hook can both be dispatched from the same task even though only one of them matches the history's last entry. selectInstruction/FilterInstructions only route the *last* entry, so the other hook's job gets Instruction=nil — and `boid task instructions` correspondingly returns an empty list for that job (routedInstructionSlice below returns [] when inst==nil). Deriving "current instructions" from the task row instead of the job's own JobSpec.Instruction would hand a claude job the codex instruction (or vice versa) — a real regression codex review caught before merge; see wiring-seams.md #13. orchestrator.CurrentInstructions still exists for task-row-level callers that are NOT this RPC — see its own doc comment.
- Env: the reduced environment view (allowed_domains + resolved host commands, both dispatch-time-only runtime facts).
- Payload: the trait-filtered payload (depends on the firing hook's declared Traits.Consumes — plan-time-only data that JobSpec does not carry forward).
- PayloadPatchAllowedTraits: the firing hook's own `traits.produces` list, captured at dispatch time (JobSpec.HookTraitsProduces — event.Hook.Traits.Produces, verbatim). Phase 5b PR7's `boid task update --payload-patch` RPC (docs/plans/phase5-shim-and-task-context.md) uses this to gate its merge exactly like a completing hook's own file-based payload_patch would be gated (HandlerResult.allowedTraits in coordinator.go) — never by re-resolving the hook against current (possibly since-edited) project meta, which is a TOCTOU staleness bug (codex review, wiring-seams.md #17's Major 1). nil means unrestricted (see JobSpec.HookTraitsProduces's own doc comment for when that applies).
Runner.Dispatch populates one per job; UnregisterJob discards it, mirroring the broker token's own lifecycle so nothing outlives the job it describes.
type JobEventSink ¶
type JobEventSink interface {
JobCreated(taskID, jobID string)
}
JobEventSink lets the runner report job lifecycle events to a subscriber (typically the web SSE hub) without taking a hard dependency on it. All methods are best-effort: implementations should not block or fail the caller — they exist to push UI refresh hints.
type JobFilter ¶
type JobFilter struct {
Status string
Interactive *bool // nil = no filter
TasklessOnly bool // true = only jobs where task_id IS NULL
}
JobFilter specifies optional filters for listing jobs globally.
type JobRepository ¶
type JobRepository struct {
// contains filtered or unexported fields
}
func NewJobRepository ¶
func NewJobRepository(db db.DBTX) *JobRepository
func (*JobRepository) CreateJob ¶
func (r *JobRepository) CreateJob(job *Job) error
func (*JobRepository) ListJobsByTask ¶
func (r *JobRepository) ListJobsByTask(taskID string) ([]*Job, error)
func (*JobRepository) ListJobsFiltered ¶
func (r *JobRepository) ListJobsFiltered(filter JobFilter) ([]*Job, error)
func (*JobRepository) UpdateJob ¶
func (r *JobRepository) UpdateJob(job *Job) error
type JobRuntime ¶
type JobRuntime interface {
Start(ctx context.Context, spec RuntimeStartSpec) (*RuntimeHandle, error)
Attach(ctx context.Context, runtimeID string, req RuntimeAttachRequest) error
Resize(ctx context.Context, runtimeID string, size TerminalSize) error
Wait(ctx context.Context, runtimeID string) (RuntimeExit, error)
Stop(ctx context.Context, runtimeID string) error
// Signal sends a single signal to the runtime's process group without
// any follow-up SIGKILL. Used by NotifyTask to drive an "agent-stop"
// SIGUSR1 to run-agent.py while leaving the runner chain intact: the
// go-native runner subcommands set this signal to SIG_IGN (see
// runner.ignoreStopSignal), which is inherited across execve so pasta and
// the child runners survive while run-agent.py re-installs its own handler.
// Implementations should be no-op when the runtime has already exited.
Signal(ctx context.Context, runtimeID string, sig syscall.Signal) error
}
type LocalRuntime ¶
type LocalRuntime struct {
RootDir string
// contains filtered or unexported fields
}
func (*LocalRuntime) Attach ¶
func (r *LocalRuntime) Attach(ctx context.Context, runtimeID string, req RuntimeAttachRequest) error
func (*LocalRuntime) CloseInputRuntime ¶ added in v0.0.13
func (r *LocalRuntime) CloseInputRuntime(runtimeID string) error
CloseInputRuntime propagates end-of-input to the given runtime's child process: a no-op for an interactive (PTY) session or a non-interactive session started without RuntimeStartSpec.StdinForward (session.closeStdin itself no-ops when stdinWriter is nil — see its doc comment), otherwise it closes the StdinForward pipe's write end so a pipe-oriented command (`cat`, `wc`, ...) sees a real EOF and can exit. This is WriteInputRuntime's counterpart for the "the client's own stdin ended" signal: over the old raw-hijack attach transport this was implicit (Attach's input-forwarding goroutine called closeStdin in a defer once its Input reader hit EOF); the WS transport (Phase 3 PR3) has no such implicit signal — a WS connection has no half-close — so api.WSAttachHandler's "input_close" frame type calls this explicitly via Runner.CloseInput instead.
func (*LocalRuntime) Resize ¶
func (r *LocalRuntime) Resize(_ context.Context, runtimeID string, size TerminalSize) error
func (*LocalRuntime) Signal ¶
Signal delivers a single signal to the runtime's process group without any SIGKILL follow-up. NotifyTask uses this for SIGUSR1 (agent-stop) — the signal is delivered process-group-wide (kill(-pgid, sig)) and processes configured to ignore it via `trap ” USR1` / SIG_IGN survive unaffected. No-op when the runtime session has already exited.
func (*LocalRuntime) Start ¶
func (r *LocalRuntime) Start(_ context.Context, spec RuntimeStartSpec) (*RuntimeHandle, error)
func (*LocalRuntime) Stop ¶
func (r *LocalRuntime) Stop(ctx context.Context, runtimeID string) error
func (*LocalRuntime) SubscribeRuntime ¶
func (r *LocalRuntime) SubscribeRuntime(runtimeID string) ([]byte, <-chan []byte, func(), bool)
SubscribeRuntime subscribes to live output of the session identified by runtimeID. Returns the current transcript snapshot, a channel of subsequent chunks, a cancel function to unsubscribe, and whether live streaming is available.
func (*LocalRuntime) SupportsAttach ¶
func (r *LocalRuntime) SupportsAttach(runtimeID string) bool
func (*LocalRuntime) Wait ¶
func (r *LocalRuntime) Wait(ctx context.Context, runtimeID string) (RuntimeExit, error)
func (*LocalRuntime) WriteInputRuntime ¶
func (r *LocalRuntime) WriteInputRuntime(runtimeID string, data []byte) error
WriteInputRuntime writes data to the given runtime's input: the PTY master for an interactive session, or the StdinForward pipe for a non-interactive one (session.writeStdin dispatches on s.interactive — see its own doc comment). Returns nil if the session is not running or has already exited.
This used to call session.writeMaster directly, which silently discarded every byte for a non-interactive session (writeMaster's own `if !s.interactive { return nil }` guard) — harmless for hook jobs, which never forward real input over this path, but a real bug for `boid exec`'s non-interactive StdinForward sessions once Phase 3 PR3 (docs/plans/ cli-remote-connection.md「WebSocket attach 一本化」) made WriteInputRuntime (via Runner.WriteInput, called from api.WSAttachHandler's "input" frame handling) the ONLY input path CLI attach uses — the old raw-hijack transport's dedicated Attach(ctx, RuntimeAttachRequest{Input: ...}) goroutine (which did call writeStdin, see TestLocalRuntimeStdinForward_ DeliversPipedInput) no longer has any caller after that PR removed the hijack HTTP handler. See TestLocalRuntimeWriteInputRuntime_ NonInteractiveStdinForward for the regression this fixes.
type OrchestratorAdapter ¶
type OrchestratorAdapter struct {
// contains filtered or unexported fields
}
OrchestratorAdapter adapts dispatcher execution to orchestrator interfaces.
func NewOrchestratorAdapter ¶
func NewOrchestratorAdapter(dispatcher dispatchBackend, planner *orchestrator.DispatchPlanner) *OrchestratorAdapter
func (*OrchestratorAdapter) ExecuteHook ¶
func (a *OrchestratorAdapter) ExecuteHook(ctx context.Context, event *orchestrator.HookFireEvent) (string, error)
func (*OrchestratorAdapter) WaitForJob ¶
func (a *OrchestratorAdapter) WaitForJob(ctx context.Context, jobID string) (orchestrator.JobCompletion, error)
type PeerAdvertise ¶ added in v0.0.10
type PeerAdvertise struct {
// Name is the peer's repo name (the last segment of its upstream_url's
// host/owner/repo form), used purely for display/discoverability.
Name string
// CloneURL is the full gateway clone URL for this peer, scoped fetch-only
// to this job's gateway token (docs/plans/container-based-boid.md
// 「workspace peer プロジェクト」: peers are fetch-only; writing to a peer
// means a cross-project child task instead).
CloneURL string
// ReferencePath is the sandbox-internal RO bind-mount path of the peer's
// `.git` (sandboxClonePeerReferenceDirFmt), usable as `git clone
// --reference` when an agent does clone the peer.
ReferencePath string
// CloneDir is the suggested absolute sandbox-internal directory for this
// peer, e.g. "/workspace/bm-next-lp" (workspace 親化リファクタリング,
// nose 2026-07-13 decision). It is only a suggestion — nothing enforces
// an agent actually clones the peer here — but using the same leaf name
// projectDirName would resolve for the peer's own project (were it
// dispatching as self) keeps the directory name stable regardless of
// which project happens to be the one dispatching, and keeps it off
// $HOME/tmp (both tmpfs, RAM-backed).
CloneDir string
}
PeerAdvertise is the {name, clone URL, reference path} view of a workspace peer project (docs/plans/git-gateway-cutover.md PR6 cutover 「5. peer advertise の変更」). Built by Runner.buildPeerAdvertise from the peer's captured upstream_url + this job's gateway token; it intentionally carries no host filesystem path — clone-mode jobs have no host path visible for a peer project any more, only the sandbox-internal RO reference dir (ReferencePath) and the gateway clone URL an agent would `git clone` from if it wants to see the peer's working tree.
Currently unexposed to the agent: this used to be advertised via environment.yaml's `workspace_projects` section, removed by the environment.yaml 縮退 (docs/plans/phase5-shim-and-task-context.md 決定事項 4, Phase 5b PR5) — see SandboxRuntimeInfo.WorkspacePeerAdvertise's doc comment for the current (inert, pending a future RPC) status.
type PreparedSandbox ¶
PreparedSandbox is the concrete launch artifact returned by a provider. SpecPath is the JSON sandbox spec passed to `boid runner-outer`; StatePath is the runner-state.json diagnostic file (retained on failure). RootDir and StagingDir are populated so the runner can remove them after the sandbox runtime has exited; zero values mean "nothing to clean up here".
type ProjectLookup ¶
type ProjectLookup interface {
GetProject(id string) (*orchestrator.Project, error)
ListProjects() ([]*orchestrator.Project, error)
}
ProjectLookup lets dispatcher resolve ProjectID → WorkspaceID and enumerate workspace peers, so workspace-peer authorization and peer-visibility concerns stay inside dispatcher instead of leaking into JobSpec.
type ProxyAllocator ¶ added in v0.0.8
ProxyAllocator returns the loopback port of an HTTP(S) egress proxy bound to the given workspace, after applying allowed as its allowlist. The listener is long-lived: subsequent calls for the same workspace reuse the port and live-swap the allowlist. Satisfied by *sandbox.ProxyManager.
type Runner ¶
type Runner struct {
DB *sql.DB
Runtime JobRuntime
Broker CommandBroker
Sandbox SandboxPreparer
SecretStore *SecretStore
Projects ProjectLookup
// Hydrator optionally resolves a project's workspace-hydrated
// ProjectMeta (project.yaml `meta.name` plus workspace merge) by project
// ID. It is used only for workspace-peer name resolution in
// buildPeerAdvertise — the self project's name is already resolved at
// JobSpec-build time via Visibility.ProjectName and does not need this.
// nil (test wiring, or a daemon build that doesn't wire it) makes
// buildPeerAdvertise degrade to the pre-existing basename fallback, same
// as orchestrator.DispatchPlanner.Hydrator's nil behavior.
Hydrator orchestrator.MetaHydrator
// Workspaces resolves WorkspaceMeta at dispatch time for the workspace
// the dispatched project is linked to. When nil (test wiring, missing
// disk) the runner falls back to the global floor for proxy allowlist
// resolution. Together with ProxyAllocator it implements the
// workspace-scoped proxy egress allowlist (project-workspace-allowed-domains).
Workspaces WorkspaceLookup
ProxyAllocator ProxyAllocator
BoidBinary string
ServerSocket string
// ProxyPort is the default-workspace proxy port (back-compat fallback
// when the per-workspace allocator path isn't wired or returns an
// error). Workspaces with no overrides reuse this port via the
// allocator's GetOrCreate("default", ...) entry.
ProxyPort *int
// AllowedDomains is the daemon-wide proxy egress allowlist (the floor
// from config.yaml sandbox.allowed_domains + boid defaults). Workspace
// overrides are added on top via orchestrator.ResolveAllowedDomains.
AllowedDomains []string
RuntimesDir string
JobEvents JobEventSink // optional; nil disables job lifecycle broadcasts
// GitGateway is the git gateway's job-token registry
// (docs/plans/git-gateway-cutover.md PR4: gateway lifecycle + dispatch
// wiring). nil disables gateway token registration entirely — Dispatch
// and UnregisterJob treat that as a no-op rather than panicking (test
// wiring, or a daemon build without the gateway constructed). PR4 is
// inert: registration happens, but nothing inside the sandbox talks to
// the gateway yet (that's PR5/PR6).
GitGateway *gitgateway.Registry
// GatewayURL points at the daemon's own gateway listener address string,
// filled in by Server.Start once the gateway's TCP listener is bound —
// the same late-binding-via-pointer pattern as ProxyPort, since the
// gateway (like the default proxy listener) is only known once Start
// has run. nil disables gateway URL propagation into SandboxRuntimeInfo.
GatewayURL *string
// contains filtered or unexported fields
}
func Wire ¶
func Wire(cfg WireConfig) *Runner
func (*Runner) CleanupTaskWindow ¶
CleanupTaskWindow stops all tracked runtimes associated with a task.
func (*Runner) CloseInput ¶ added in v0.0.13
CloseInput implements RuntimeInputWriter for Runner. It resolves jobID to a runtimeID via the jobs table, then delegates to LocalRuntime.CloseInputRuntime.
func (*Runner) CompleteJob ¶
func (r *Runner) CompleteJob(jobID string, result JobCompletionResult)
CompleteJob signals the waiting dispatcher that a job has completed.
func (*Runner) Dispatch ¶
func (r *Runner) Dispatch(ctx context.Context, spec *orchestrator.JobSpec, cleanup orchestrator.CleanupFunc) (jobID string, dispatchErr error)
Dispatch launches a sandbox for the given JobSpec. The optional cleanup callback (typically provided by orchestrator's PlanHook for staging dir teardown) runs after the sandbox process has exited.
func (*Runner) JobContext ¶ added in v0.0.13
func (r *Runner) JobContext(jobID string) (JobContextSnapshot, bool)
JobContext returns the tracked JobContextSnapshot for jobID, and whether one was found. false covers both "no such job" and "job existed but its context was already unregistered" (UnregisterJob clears the entry).
func (*Runner) ResizeRuntime ¶
func (r *Runner) ResizeRuntime(jobID string, size TerminalSize) error
ResizeRuntime implements RuntimeInputWriter for Runner. It resolves jobID to a runtimeID via the jobs table, then delegates to JobRuntime.Resize.
func (*Runner) SignalJobRuntime ¶
SignalJobRuntime delivers a single signal to the runtime's process group without any SIGKILL follow-up. NotifyTask uses this for SIGUSR1 to ask the agent (run-agent.py) to stop the agent session gracefully — the go-native runner subcommands keep the signal SIG_IGN (inherited across execve), so they survive while run-agent.py acts on it and runner-inner-child still posts `boid job done` through the broker. Best-effort: errors at debug level only.
func (*Runner) StopJobRuntime ¶
StopJobRuntime stops the runtime identified by runtimeID. It is a best-effort operation: errors are logged at debug level only.
func (*Runner) Subscribe ¶
func (r *Runner) Subscribe(jobID string) (snapshot []byte, ch <-chan []byte, cancel func(), ok bool)
Subscribe implements RuntimeSubscriber for Runner. It resolves jobID to a runtimeID via the jobs table, then delegates to LocalRuntime if the runtime supports live streaming.
func (*Runner) UnregisterJob ¶
UnregisterJob removes the broker token and the git gateway job token associated with the given job.
func (*Runner) WaitForJob ¶
func (r *Runner) WaitForJob(jobID string) <-chan JobCompletionResult
WaitForJob registers a channel that will receive the job completion result.
func (*Runner) WaitForJobCtx ¶
WaitForJobCtx waits for job completion with context cancellation.
A non-zero exit is NOT reported as an error — the caller inspects result.ExitCode. Only true wait-machinery failures (ctx cancel) produce a non-nil error. This lets the orchestrator record `hook_fired` actions for failing hooks the same way as successful ones; prior behavior discarded the partial FiredEvents when any hook exited non-zero.
type RuntimeAttachRequest ¶
type RuntimeExit ¶
type RuntimeHandle ¶
type RuntimeInputWriter ¶
type RuntimeInputWriter interface {
WriteInput(jobID string, data []byte) error
ResizeRuntime(jobID string, size TerminalSize) error
// CloseInput signals that no more input is coming for jobID — the WS
// attach transport's counterpart to the old raw-hijack transport's
// implicit half-close (docs/plans/cli-remote-connection.md Phase 3 PR3;
// see LocalRuntime.CloseInputRuntime's doc comment for the full
// rationale). A no-op for jobs whose runtime has no notion of "closing"
// input (interactive PTY sessions, or non-interactive sessions with no
// StdinForward pipe).
CloseInput(jobID string) error
}
RuntimeInputWriter provides write access to a running job's PTY input.
type RuntimeStartSpec ¶
type RuntimeStartSpec struct {
JobID string
TaskID string
ProjectID string
HandlerID string
Role string
Command string
Interactive bool
TTY bool
// StdinForward requests a dedicated stdin pipe for a non-interactive
// (Interactive=false) session, so a later Attach's RuntimeAttachRequest.Input
// can feed real bytes to the child process — `boid exec` piped from a
// non-TTY stdin (e.g. `echo hi | boid exec cat`) needs this; a hook job
// never does. False (the default, every hook job) keeps stdin on the null
// device exactly as before: a hook script that probes stdin must keep
// seeing an immediate EOF, not block forever waiting for a forwarder that
// will never attach. Ignored when Interactive is true — PTY sessions
// always support input via the PTY master, forwarding or not.
StdinForward bool
// DesiredID, when non-empty, asks the runtime to use this UUID as its
// session identifier instead of generating a fresh one. The caller uses
// this to pre-allocate a runtime directory (e.g. for a per-sandbox docker
// proxy socket) before Start is called. The runtime honours the request
// on a best-effort basis: if the directory already exists or the ID is
// otherwise unusable, Start returns an error.
DesiredID string
}
type RuntimeSubscriber ¶
type RuntimeSubscriber interface {
Subscribe(jobID string) (snapshot []byte, ch <-chan []byte, cancel func(), ok bool)
}
RuntimeSubscriber subscribes to live output of a running job identified by jobID.
type SandboxPreparer ¶
type SandboxPreparer interface {
PrepareSandbox(spec sandbox.Spec) (*PreparedSandbox, error)
}
SandboxPreparer prepares concrete launch artifacts from a sandbox.Spec. The orchestrator-owned BuildSandboxSpec builds the spec; dispatcher only serializes it and tracks artifacts.
func NewSandboxPreparer ¶
func NewSandboxPreparer() SandboxPreparer
NewSandboxPreparer returns the sandbox provider adapter. It serializes the sandbox.Spec to a JSON file that the go-native runner (`boid runner-outer`) reads back; all role-aware translation lives in BuildSandboxSpec.
type SandboxRuntimeInfo ¶
type SandboxRuntimeInfo struct {
JobID string
BoidBinary string
ServerSocket string
ProxyPort int
BrokerSocket string
BrokerToken string
// WorkspacePeers maps peer project IDs (same workspace, excluding self) to
// host paths. Dispatcher resolves this from its ProjectLookup so peer
// visibility/authorization does not leak into orchestrator.JobSpec.
WorkspacePeers map[string]string
// Foreground indicates whether the job runs in the foreground (user-facing
// stdout/stderr, no trap-based completion callback). boid exec sets this
// to true; hook/gate jobs leave it false so stdout is captured and a
// `boid job done` trap posts completion back to the daemon.
Foreground bool
// ResolvedHostCommandsByName is the short-name-keyed view of the resolved
// host command defs produced by ResolveHostCommands
// (docs/plans/phase5-shim-and-task-context.md, "5a: shim 固定ディレクトリ化"
// PR1). It is the single source of truth for host command wiring:
//
// - the broker's policy table (CommandBroker.RegisterCommands) keys off
// it directly;
// - buildHostCommandRulesEnv turns the same map into
// BOID_HOST_COMMAND_RULES;
// - hostCommandSymlinks (5a-3) materializes one
// `/run/boid/bin/<name> -> boid` symlink per entry — so every
// host_command becomes a shim on PATH under its declared short name,
// even when host_commands.<name>.path aliases the source file to a
// different basename.
//
// The pre-5a-3 absolute-path-keyed sibling (ResolvedHostCommands / byPath)
// was dropped: it existed only to key the retired hostCommandMounts /
// buildHostCommandNamesEnv / per-command PATH parent, all replaced by the
// fixed-directory scheme. Empty when the job declares no host commands.
ResolvedHostCommandsByName map[string]orchestrator.CommandDef
// ProxySocketPath, when non-empty, is the host-side Unix socket path of the
// per-sandbox docker proxy. sandbox_builder bind-mounts it into the sandbox
// at the fixed sandbox path (see dockerProxySandboxSocket) and injects
// DOCKER_HOST / CONTAINER_HOST / TESTCONTAINERS_* env vars.
// Set by the runner before BuildSandboxSpec when capabilities.docker is
// declared in project.yaml.
ProxySocketPath string
// AllowedDomains is the proxy egress allowlist. It is purely informational
// inside the sandbox (the proxy itself enforces it on the host), surfaced
// to the agent via the `boid task env` broker RPC (Phase 5b PR1) so it
// knows which hosts are reachable without burning a turn on a 403.
AllowedDomains []string
// GatewayURL is the git gateway's sandbox-facing base URL
// (http://10.0.2.2:<port>), set by Runner from the daemon's own
// gateway listener (docs/plans/git-gateway-cutover.md PR4: gateway
// lifecycle + dispatch wiring). Empty when the gateway isn't wired.
//
// PR4 is inert: BuildSandboxSpec does not thread this into env or mounts
// yet — nothing inside the sandbox reads it. The env var advertise
// (e.g. GIT_HTTP_GATEWAY_URL) is explicitly deferred to the cutover PR
// (PR6); the runner clone sequence that would consume it is PR5.
GatewayURL string
// GatewayJobToken is this job's git gateway token, registered against
// the gateway's Registry at dispatch time (self project fetch/fetch+push,
// workspace peers and workspace extra_repos fetch-only) and unregistered
// when the job completes (see Runner.registerGatewayToken /
// Runner.UnregisterJob). Empty when the gateway isn't wired.
//
// Same PR4-is-inert caveat as GatewayURL: carried here for PR5/PR6 to
// consume, not yet used by BuildSandboxSpec.
GatewayJobToken string
// GatewayCloneURL is the full gateway clone URL for spec's own project
// (GatewayURL + "/j/" + GatewayJobToken + "/<host>/<owner>/<repo>.git"),
// built by Runner.buildGatewayCloneURL (docs/plans/git-gateway-cutover.md
// PR5). Empty unless spec.Visibility.Clone is non-nil (the opt-in
// sandbox-clone path) — computing it is otherwise wasted work, since
// nothing would consume it. BuildSandboxSpec only reads this when
// spec.Visibility.Clone != nil.
GatewayCloneURL string
// WorkspacePeerAdvertise is the {name, clone URL, reference path} view of
// WorkspacePeers, built by Runner.buildPeerAdvertise and keyed by peer
// project ID (docs/plans/git-gateway-cutover.md PR6 cutover 「5. peer
// advertise の変更」 — replaces the pre-cutover host path enumeration);
// nil when the gateway isn't wired or no peer has a resolvable
// upstream_url.
//
// Currently unused by BuildSandboxSpec: environment.yaml's
// `workspace_projects` section (its sole consumer) was removed by the
// environment.yaml 縮退 (docs/plans/phase5-shim-and-task-context.md 決定
// 事項 4, Phase 5b PR5) — peer advertise has no CLI replacement yet,
// tracked as a later 5b item / separate phase. Kept here — the same
// "carried but inert across a PR boundary" pattern as GatewayURL /
// GatewayJobToken above — as the ready-made input for that future
// `boid workspace peers`-style RPC.
WorkspacePeerAdvertise map[string]PeerAdvertise
// CloneWorkspaceDir is the host-side runtime dir path
// (`<RuntimesDir>/<runtime_id>/workspace`) that BuildSandboxSpec bind-
// mounts at the sandbox-internal clone target (/workspace/<name>) when
// spec.Visibility.Clone is set (docs/plans/git-gateway-cutover.md PR6
// cutover — 「一時領域の実体はホスト側 runtime dir の bind mount を既定と
// する」, 2026-07-08 decision in container-based-boid.md). Allocated and
// mkdir'd by Runner.Dispatch before BuildSandboxSpec runs, the same way
// startDockerProxy pre-creates its runtime dir. Empty when RuntimesDir is
// unset (e.g. minimal test wiring) — cloneMounts then skips the bind and
// the clone lands on the sandbox's own tmpfs root instead, a safe but
// non-default degrade (working tree + build artifacts in RAM).
CloneWorkspaceDir string
// WorkspaceHomeDir is the host-side per-workspace home directory
// resolved by Runner.resolveWorkspaceHome
// (docs/plans/home-workspace-volume.md Phase 4 PR1):
// ~/.local/share/boid/homes/<slug>, guaranteed to exist (and, if the
// workspace declares an init.sh, already initialized) by the time
// Dispatch reaches BuildSandboxSpec.
//
// PR2 (docs/plans/home-workspace-volume.md) reads this field: the
// Clone / projectVisible / default HOME branches below (via homeMounts)
// bind it read-write at HOME's sandbox-internal path instead of a plain
// tmpfs, with $HOME/.boid layered as a job-scoped tmpfs on top so
// $HOME/.boid/output/payload_patch.json stays isolated per job even
// though the rest of HOME now persists across jobs in the same
// workspace (see homeMounts' doc comment for why that overlay survived
// the Phase 5b PR6 cutover — codex review found removing it exploitable).
// env["HOME"] itself is unchanged — it still comes from hostHomeDir(),
// the *target* path inside the sandbox; only the *contents* now come
// from the workspace home instead of starting empty every job.
//
// When empty (test wiring that never resolved a workspace — most of
// sandbox_builder_test.go's minimal SandboxRuntimeInfo{} literals —
// or any other caller that has not threaded a workspace home through
// yet) the HOME branches gracefully degrade to the pre-PR2 behaviour: a
// single fresh tmpfs. The ProfileInit branch never reads this field at
// all — see its own doc comment for why bind-mounting HOME there would
// defeat its host-tool-discovery purpose.
WorkspaceHomeDir string
// WorkspaceSlug is the normalized workspace slug WorkspaceHomeDir was
// resolved for (docs/plans/home-workspace-volume.md Phase 4 PR3) —
// filepath.Base(WorkspaceHomeDir), computed once by Runner.Dispatch.
// BuildSandboxSpec threads it into env["BOID_WORKSPACE_SLUG"] so the
// claude/codex/opencode adapters' fail-fast "harness CLI not found"
// error (run.go, triggered when PR3's retired adapter bindings leave no
// CLI on PATH) can name the exact workspace whose init.sh needs the
// install step. Empty for test wiring that never resolved a workspace
// (most of sandbox_builder_test.go's minimal SandboxRuntimeInfo{}
// literals) — the env var is simply omitted in that case.
WorkspaceSlug string
}
SandboxRuntimeInfo carries the dispatcher-internal facts that are required to turn an orchestrator.JobSpec into a sandbox.Spec but that orchestrator never needs to know: job id, broker plumbing, proxy port, boid binary location, server socket path, staging dirs.
type SecretResolver ¶
SecretResolver resolves a secret key into its plaintext value.
type SecretStore ¶
type SecretStore struct {
// contains filtered or unexported fields
}
SecretStore provides encrypted secret storage backed by SQLite.
func NewSecretStore ¶
func NewSecretStore(d *sql.DB, key []byte) (*SecretStore, error)
NewSecretStore creates a store with the given database and encryption key.
func (*SecretStore) Delete ¶
func (s *SecretStore) Delete(namespace, key string) error
func (*SecretStore) Set ¶
func (s *SecretStore) Set(namespace, key, value string) error
type SessionJobInput ¶ added in v0.0.6
type SessionJobInput struct {
// ProjectID and ProjectWorkDir locate the host filesystem the sandbox
// will expose; ProjectWorkDir is the cwd seen by the agent.
ProjectID string
ProjectWorkDir string
// ProjectName is project.yaml's `meta.name` (see
// orchestrator.Visibility.ProjectName's doc comment). The caller fills
// this from the same workspace-hydrated ProjectMeta it already reads
// Env/HostCommands/AdditionalBindings from.
ProjectName string
// HarnessType selects the agent adapter the runner-inner-child will
// dispatch through. Must be one of "claude" / "codex" / "opencode" /
// "shell" (validated by the caller; BuildSessionJobSpec does not police it).
HarnessType string
// Argv is the literal program + arguments the shell adapter consumes.
// The claude / codex / opencode adapters ignore it (they build their
// argv from CLI conventions). Required when HarnessType == "shell";
// ignored otherwise.
Argv []string
// Instruction is the optional bootstrap prompt the agent should pick up
// on launch (e.g. the `--instruction` flag of `boid agent`, or the
// WebUI Session dialog's text field). When non-empty it is plumbed
// through RunContext.UserAnswer so the adapter's existing "user reply"
// path delivers it as the first turn of input. Empty leaves the adapter
// to pick its default bootstrap (no positional for session mode on claude,
// since the /boid-task skill is meaningless without a task.yaml).
Instruction string
// Readonly controls Visibility.Writable. Sessions default to writable
// (interactive use prioritises developer ergonomics over fail-safety)
// so callers must opt into a read-only session explicitly.
Readonly bool
// Model overrides the harness binary's default model selection.
Model string
// Project trait overlay (the session has no behavior to resolve from,
// so the caller fills these directly from ProjectMeta).
Env map[string]string
HostCommands map[string]orchestrator.HostCommandSpec
AdditionalBindings []orchestrator.BindMount
SecretNamespace string
DockerEnabled bool
// DisplayName is the human-readable label persisted to jobs.display_name
// (and shown in the TUI / Web UI). Empty falls back to "<harness>
// session" downstream.
DisplayName string
}
SessionJobInput carries the resolved data needed to build a Session (HarnessAdapter-backed, task-less) JobSpec. Phase 3-d (PR1) introduced it as the input shape for both the daemon API (POST /sessions) and the `boid agent` CLI.
session jobs inherit project-level traits only (env / host_commands / additional_bindings / secret_namespace). behavior-level traits are deliberately ignored — sessions are not driven by the task state machine and have no behavior context to resolve.
type TerminalSize ¶
type WireConfig ¶
type WireConfig struct {
DB *sql.DB
Runtime JobRuntime
Broker CommandBroker
Sandbox SandboxPreparer
SecretStore *SecretStore
Projects ProjectLookup
// Hydrator is optional workspace-hydrated ProjectMeta lookup, threaded
// straight to Runner.Hydrator (see its doc comment). nil disables
// workspace-peer meta.name resolution; buildPeerAdvertise falls back to
// filepath.Base(WorkDir).
Hydrator orchestrator.MetaHydrator
// BoidBinary is the host path to the boid executable that should be
// bind-mounted into sandboxes.
BoidBinary string
// ServerSocket is the host path to the daemon UNIX socket (for boid exec
// jobs that talk to boid over HTTP from inside the sandbox).
ServerSocket string
// ProxyPort points at the default-workspace proxy port. Used as the
// fallback when ProxyAllocator is not wired (or fails). Sandboxes
// linked to a workspace get a per-workspace port via ProxyAllocator.
ProxyPort *int
// AllowedDomains is the daemon-wide proxy egress allowlist floor
// (config.yaml sandbox.allowed_domains + boid built-in defaults).
// Workspaces add entries on top via workspace.yaml; they cannot remove
// floor entries (orchestrator.ResolveAllowedDomains enforces this).
AllowedDomains []string
// Workspaces is the WorkspaceLookup used at dispatch time to discover
// each workspace's AllowedDomains overrides. nil disables workspace
// hydration and the runner stays on the floor only.
Workspaces WorkspaceLookup
// ProxyAllocator is the per-workspace proxy listener registry. nil
// disables workspace-scoped proxy allocation and the runner serves
// every sandbox via the default-workspace listener.
ProxyAllocator ProxyAllocator
// RuntimesDir is the root directory where per-sandbox runtime directories
// are created. When non-empty and DockerEnabled, the runner pre-allocates a
// runtime directory here to host the per-sandbox docker proxy socket and
// resource ledger.
RuntimesDir string
// GitGateway is the git gateway's job-token registry
// (docs/plans/git-gateway-cutover.md PR4). nil disables gateway token
// registration entirely.
GitGateway *gitgateway.Registry
// GatewayURL points at the daemon's own gateway listener address string,
// filled in by Server.Start once the gateway's TCP listener is bound
// (same late-binding pattern as ProxyPort). nil disables gateway URL
// propagation into SandboxRuntimeInfo.
GatewayURL *string
}
type WorkspaceEnvHostCommand ¶ added in v0.0.13
type WorkspaceEnvHostCommand struct {
Name string `json:"name" yaml:"name"`
Allow []string `json:"allow,omitempty" yaml:"allow,omitempty"`
Deny []string `json:"deny,omitempty" yaml:"deny,omitempty"`
Reject []WorkspaceEnvRejectRule `json:"reject,omitempty" yaml:"reject,omitempty"`
}
WorkspaceEnvHostCommand mirrors orchestrator.CommandDef's agent-relevant surface (the parts an agent cannot infer by trying the command and reading the error): its allow/deny argument policy and reject rules. Renamed and exported from the former environmentHostCommand (Phase 5b PR1) so buildEnvironmentYAML's host_commands section and `boid task env`'s RPC response share one conversion (convertHostCommands) instead of two independently-maintained shapes that could silently drift apart.
type WorkspaceEnvRejectRule ¶ added in v0.0.13
type WorkspaceEnvRejectRule struct {
Match string `json:"match" yaml:"match"`
Reason string `json:"reason" yaml:"reason"`
}
WorkspaceEnvRejectRule mirrors orchestrator.RejectRule so agents can read, per host command, which arg shapes are rejected and what to do instead.
type WorkspaceEnvView ¶ added in v0.0.13
type WorkspaceEnvView struct {
AllowedDomains []string `json:"allowed_domains,omitempty" yaml:"allowed_domains,omitempty"`
HostCommands []WorkspaceEnvHostCommand `json:"host_commands,omitempty" yaml:"host_commands,omitempty"`
}
WorkspaceEnvView is the reduced "environment" a Phase 5b `boid task env` call returns (docs/plans/phase5-shim-and-task-context.md 決定事項 4). Only the two properties an in-sandbox agent cannot observe on its own survive the 縮退 from the legacy environment.yaml: network egress allowlist and host-command policy. Every other section the legacy doc had (sandbox.*/filesystem.*/worktree/tools/session.*/network.restricted/notes) is either hard-coded scenery or directly observable from inside the container, so it is dropped rather than carried over.
JSON tags define the RPC's wire schema; per the plan doc's "broker RPC の スキーマ安定性契約" open question, treat field renames/removals here as a breaking change to the `boid task env` contract skills depend on.
func BuildWorkspaceEnvView ¶ added in v0.0.13
func BuildWorkspaceEnvView(allowedDomains []string, hostCommands map[string]orchestrator.CommandDef) WorkspaceEnvView
BuildWorkspaceEnvView derives the reduced env view from the dispatcher's resolved allowedDomains and hostCommands, via the shared convertHostCommands helper — the sole source of the `boid task env` RPC response as of the Phase 5b PR6 cutover (docs/plans/phase5-shim-and-task-context.md), which retired the parallel dispatch-time environment.yaml file this function used to also feed.
type WorkspaceLookup ¶ added in v0.0.8
type WorkspaceLookup interface {
Load(slug string) (*orchestrator.WorkspaceMeta, error)
}
WorkspaceLookup reads a WorkspaceMeta for a given slug. Satisfied by *orchestrator.WorkspaceStore; kept as an interface so tests can stub it without touching disk. Load is expected to return os.ErrNotExist-wrapped errors when the workspace file is missing — Runner treats that as the "degraded window" and falls back to the global floor.
Source Files
¶
- broker.go
- gitgateway_wire.go
- home.go
- host_commands.go
- job_context.go
- model.go
- orchestrator_adapter.go
- policy_translate.go
- preparer.go
- repository.go
- runner.go
- runtime.go
- runtime_local_linux.go
- runtime_subscriber_export.go
- sandbox_builder.go
- sandbox_preparer.go
- secret_keyfile.go
- secret_store.go
- session_job.go
- store.go
- transcript.go
- upstream_url.go
- wire.go
- workspace_env_view.go
- workspace_home.go