Documentation
¶
Index ¶
- Constants
- func EnsureMiseShims(stellaHome, userToolsDir string) error
- func EnsurePrivateDir(path string) error
- func EnsureUserMiseHome(stellaHome, userToolsDir string) error
- func HostEnvBuildPath(stellaHome, userShimsDir string) string
- func HostEnvCopy(env map[string]string)
- func HostEnvPathAllowed(entry, stellaBin string) bool
- func IsPolicyCompatibilityError(err error) bool
- func LogSessionClosed(sessionID, backend, reason string)
- func LogSessionCreated(sessionID, backend string, policy Policy)
- func LogUnsupportedBackend(policy Policy, attempted []string, reason string)
- func MiseShimsDir(stellaHome string) string
- func MiseToolsDir(stellaHome string) string
- func MiseUserShimsDir(userToolsDir string) string
- func MiseUserToolsDir(stellaHome, principalDir, id string) string
- func NewSessionID() string
- func PerUserMiseDataDir(env map[string]string, stellaHome string) string
- func RelinkSystemMiseShims(stellaHome string) error
- func StellaHomeSandboxDirs() []string
- type CappedOutputBuffer
- type DirEntry
- type EnvRefresher
- type ExecOptions
- type ExecResult
- type Factory
- type FilesystemPolicy
- type Host
- type Mount
- type MountAccess
- type NetworkMode
- type NetworkPolicy
- type PathResolver
- func (r *PathResolver) Mounts() []Mount
- func (r *PathResolver) ResolvePath(agentPath string) (ResolvedPath, error)
- func (r *PathResolver) ResolveWritePath(agentPath string) (ResolvedPath, error)
- func (r *PathResolver) ToHostPath(sandboxPath string) (string, bool)
- func (r *PathResolver) ToSandboxPath(hostPath string) (string, bool)
- type PathResolverConfig
- type Policy
- type PolicyCompatibilityError
- type ProcessHandle
- type ProcessRequest
- type ResilientSession
- func (r *ResilientSession) Alive() bool
- func (r *ResilientSession) Close() error
- func (r *ResilientSession) Done() <-chan struct{}
- func (r *ResilientSession) Exec(ctx context.Context, command string, opts ExecOptions) (ExecResult, error)
- func (r *ResilientSession) Policy() Policy
- func (r *ResilientSession) RefreshEnv(updates map[string]string)
- func (r *ResilientSession) ResolvePath(path string) (string, error)
- func (r *ResilientSession) ResolveWritePath(path string) (string, error)
- func (r *ResilientSession) StartProcess(ctx context.Context, req ProcessRequest) (ProcessHandle, error)
- func (r *ResilientSession) WorkingDir() string
- type ResolvedPath
- type Session
- type SessionCreator
Constants ¶
const ( // MountWorkspace is the agent's per-agent workspace root and HOME/cwd. MountWorkspace = "/workspace" // MountUserData is the shared user-data root (toolchain, caches, skills, uploads). MountUserData = "/user" // MountStellaHome is the read-only system install tree (STELLA_HOME). MountStellaHome = "/opt/stella" // MountAgentSkills is the read-only mount of the admin-managed, agent-bound // (system_agent scope) skills dir. It lives outside the two roots (its host // dir is the user-independent agent definition tree, not the per-agent // workspace), so it gets its own fixed mount instead of mapping onto /workspace. MountAgentSkills = "/opt/stella/agent-skills" // MountSystemDBSkills is the read-only mount of DB-installed system-scope // skills. They live in a dedicated dir (a sibling of the shipped built-ins // under STELLA_HOME, not mixed into it), so they get their own fixed mount. MountSystemDBSkills = "/opt/stella/db-skills" )
Mount points an isolating backend exposes inside the sandbox. These are the stable, model-visible paths for the two-root layout (plus the read-only system install tree). The bwrap backend binds the host roots here; higher layers (e.g. the skills tool's skill_dir remap) address skills by these paths instead of host paths. Backends that don't isolate the filesystem (none) ignore them and use host paths directly.
const ( // MaxExecOutputBytes caps each captured stdout/stderr stream from untrusted execs. MaxExecOutputBytes = 10 << 20 // 10 MiB )
Variables ¶
This section is empty.
Functions ¶
func EnsureMiseShims ¶ added in v0.49.1
EnsureMiseShims prepares the mise shims a sandbox session resolves against, before the session's PATH is used. It relinks the shared system-tree shims (always) and, when userToolsDir is set, seeds and relinks the per-user tree via EnsureUserMiseHome. Splitting the two trees keeps each concern separate while a single call site guarantees both resolve under the active backend's remap. All backends call this, including docker: the relinked relative shims and seeded per-user tree are mounted into the container and resolve against the image's in-image system tree (#436).
func EnsurePrivateDir ¶ added in v0.38.0
EnsurePrivateDir creates path if needed and enforces owner-only permissions. MkdirAll does not chmod existing directories, so callers must use this for security-sensitive shared host directories.
func EnsureUserMiseHome ¶ added in v0.48.0
EnsureUserMiseHome creates the per-user mise tree at userToolsDir and seeds it with the shared system installs. Pointing a session's MISE_DATA_DIR at this tree gives the agent a real, writable mise home — it resolves the builtin tools (via the seeded symlinks) and can install its own tools/versions alongside, layered above the read-only system installs. Idempotent and safe to call before every session; a no-op when userToolsDir is empty.
The tree is created 0700 (like the sibling per-user temp dir) so other host users can't read one tenant's installed tools, and every structural directory is verified to be a real directory — never a symlink. The tree is mounted writable into the sandbox and persists across sessions, so a prior session's agent could replace a structural dir with a symlink; since this runs unsandboxed with server privileges, following such a link would let seeding escape the tree.
The "shims" dir is seeded empty on purpose: builtin tools resolve through the system shims (kept on PATH behind the per-user shims by HostEnvBuildPath), and the per-user shims fill in only as the agent installs its own tools. Any shims the agent did create are relinked to a relative target so a persisted tree stays usable if the same user later runs under a different backend.
func HostEnvBuildPath ¶ added in v0.33.0
HostEnvBuildPath returns a sanitized PATH suitable for host-execution sandbox backends (local, none). It prepends the per-user mise shims (so a user's own tool versions win), then the system mise shims and stella bin directories, and filters host PATH entries to a safe allowlist on Linux. An empty userShimsDir is dropped, so callers without a per-user tree pass "".
func HostEnvCopy ¶ added in v0.33.0
HostEnvCopy copies a fixed allowlist of host environment variables into env. Only locale, terminal, and proxy variables are included.
func HostEnvPathAllowed ¶ added in v0.33.0
HostEnvPathAllowed reports whether a PATH entry is in the safe allowlist.
func IsPolicyCompatibilityError ¶
IsPolicyCompatibilityError reports whether an error is a policy compatibility error.
func LogSessionClosed ¶
func LogSessionClosed(sessionID, backend, reason string)
func LogSessionCreated ¶
func LogUnsupportedBackend ¶
func MiseShimsDir ¶ added in v0.38.0
MiseShimsDir returns the mise shims directory for host-execution sandbox backends. Tools installed by the manifest/org reconcilers are exposed here as shims (not copied into bin), so it must be on PATH for them to resolve.
func MiseToolsDir ¶ added in v0.38.0
MiseToolsDir returns the root MISE_DATA_DIR for Stella-managed mise installs. This is the single source of truth for the on-disk layout: the manifest/org reconcilers install into it and the sandbox PATH is built from it, so both sides must derive their paths from here to stay in lockstep.
func MiseUserShimsDir ¶ added in v0.48.0
MiseUserShimsDir returns the per-user mise shims directory. It is prepended to PATH ahead of the system shims so a user's own tool versions win.
func MiseUserToolsDir ¶ added in v0.48.0
MiseUserToolsDir returns the per-principal writable MISE_DATA_DIR. It mirrors a real machine's per-user mise home: each principal gets one tree shared by all their agents, layered above the shared read-only system installs. principalDir is the home subtree — always "users", the only top-level isolation boundary (#442) — and id is the principal's key: a raw user ID, or a channel group's ID under a "group-" prefix so equal raw IDs across users and groups can never collide. Keying as {stellaHome}/{principalDir}/{id}/.mise-tools never depends on the agent, so all of a principal's agents share one tree. An empty or unsafe argument yields "" so callers fall back to the system tree.
func NewSessionID ¶
func NewSessionID() string
NewSessionID returns a cryptographically random session identifier. Using random IDs avoids Docker container name collisions across test runs.
func PerUserMiseDataDir ¶ added in v0.48.0
PerUserMiseDataDir returns the per-user MISE_DATA_DIR carried in a session's env, or "" when it points at the shared read-only system tree (or is unset). It lets a host backend recover the per-user mise home — to derive the shims dir for PATH — from the env it already remaps, so the FilesystemPolicy needs no mise-specific field. stellaHome is the host STELLA_HOME used to recognize the system tree; the returned path is whatever scope the env holds (host path here, the backend remaps it as needed).
Precondition: env's MISE_DATA_DIR and stellaHome must be in the same scope (both host paths, or both sandbox paths). Callers that remap MISE_* env vars must read this before remapping (see adjustPolicy), since after remap the data dir no longer matches the host system tree this compares against.
func RelinkSystemMiseShims ¶ added in v0.49.1
RelinkSystemMiseShims rewrites the shared system-tree shims to relative targets. Idempotent and safe to call before every session; see relinkShimsToLocalMise.
func StellaHomeSandboxDirs ¶ added in v0.38.1
func StellaHomeSandboxDirs() []string
StellaHomeSandboxDirs returns the subdirectory names (relative to STELLA_HOME) that sandbox backends must expose. Every backend — bwrap, Docker, none — should derive its mount list from this slice so adding a new directory is a one-line change. The returned names use filepath.Separator and are safe to join with any absolute root.
Types ¶
type CappedOutputBuffer ¶ added in v0.60.0
type CappedOutputBuffer struct {
// contains filtered or unexported fields
}
CappedOutputBuffer stores command output up to a byte ceiling and discards the rest.
func NewCappedOutputBuffer ¶ added in v0.60.0
func NewCappedOutputBuffer(limit int) *CappedOutputBuffer
NewCappedOutputBuffer returns an io.Writer that captures at most limit bytes.
func NewExecOutputBuffer ¶ added in v0.60.0
func NewExecOutputBuffer() *CappedOutputBuffer
NewExecOutputBuffer returns a capped buffer for one exec output stream.
func (*CappedOutputBuffer) Bytes ¶ added in v0.60.0
func (b *CappedOutputBuffer) Bytes() []byte
func (*CappedOutputBuffer) String ¶ added in v0.60.0
func (b *CappedOutputBuffer) String() string
func (*CappedOutputBuffer) Truncated ¶ added in v0.60.0
func (b *CappedOutputBuffer) Truncated() bool
type DirEntry ¶
DirEntry is retained for use by prompt_host.go which reads directories via os.ReadDir and needs a uniform entry type shared with sandbox callers.
type EnvRefresher ¶ added in v0.49.0
EnvRefresher is implemented by sessions whose injected environment can be updated after creation, so a caller can rotate a credential (e.g. an expiring OAuth access token) without tearing down and recreating the session. Updates are applied atomically and are visible to every subsequent Exec/StartProcess. Sessions that don't run real processes (e.g. the no-op session) may omit it.
type ExecOptions ¶
type ExecResult ¶
type Factory ¶
type Factory interface {
CreateSession(ctx context.Context, policy Policy) (Session, error)
Supported(policy Policy) error
Name() string
Available() bool
}
Factory creates sessions from policies.
type FilesystemPolicy ¶
type FilesystemPolicy struct {
// WorkspaceRoot is the host workspace root. When empty, WorkingDir is used for
// backwards compatibility and for relative-path anchoring in non-isolating code.
WorkspaceRoot string
// WorkingDir is the requested working directory. Producers may provide either
// a host path under WorkspaceRoot or the corresponding sandbox path; backends
// normalize it through Mounts before execution.
WorkingDir string
// Mounts is the complete host→sandbox mount plan for isolating backends.
Mounts []Mount
// TempDirHost is the host directory mounted as /tmp inside the sandbox.
// Empty means the backend chooses a session-local temp directory. Non-empty
// directories are guaranteed to exist with 0700 permissions before the
// backend sees the policy; they may be shared across sessions.
TempDirHost string
}
FilesystemPolicy defines filesystem constraints for a sandbox session.
type Host ¶
type Host = Session
Host is an alias for Session kept for internal use by the runner and core tools. New code should use Session directly.
type Mount ¶ added in v0.60.0
type Mount struct {
HostPath string
SandboxPath string
Access MountAccess
}
Mount maps a host path into the sandbox view.
type MountAccess ¶ added in v0.60.0
type MountAccess uint8
MountAccess is the access mode a sandbox mount grants inside the sandbox.
const ( // MountReadOnly allows reads but rejects writes through host-side tools and // asks isolating backends for a read-only bind when the platform supports it. MountReadOnly MountAccess = iota // MountReadWrite allows reads and writes. MountReadWrite )
type NetworkMode ¶
type NetworkMode string
NetworkMode defines the network access mode for a sandbox session.
const ( // NetworkDisabled blocks all network access. NetworkDisabled NetworkMode = "disabled" // NetworkAllowAll allows unrestricted network access. NetworkAllowAll NetworkMode = "allow_all" )
type NetworkPolicy ¶
type NetworkPolicy struct {
// Mode is the network access mode: disabled | allow_all.
Mode NetworkMode
// Timeout for network operations. Zero means no timeout.
Timeout time.Duration
}
NetworkPolicy defines network constraints for a sandbox session.
type PathResolver ¶ added in v0.60.0
type PathResolver struct {
// contains filtered or unexported fields
}
PathResolver is the shared host-side filesystem boundary for in-process tools. It translates agent-visible paths to host paths only when the result stays inside a declared mount, and it rejects symlink traversal below mount roots.
func NewPathResolver ¶ added in v0.60.0
func NewPathResolver(cfg PathResolverConfig) *PathResolver
NewPathResolver returns a resolver for the supplied filesystem view. The workspace mount is added as a compatibility default when Mounts omits it.
func (*PathResolver) Mounts ¶ added in v0.60.0
func (r *PathResolver) Mounts() []Mount
Mounts returns a copy of the normalized mount table.
func (*PathResolver) ResolvePath ¶ added in v0.60.0
func (r *PathResolver) ResolvePath(agentPath string) (ResolvedPath, error)
ResolvePath resolves an agent path for reading.
func (*PathResolver) ResolveWritePath ¶ added in v0.60.0
func (r *PathResolver) ResolveWritePath(agentPath string) (ResolvedPath, error)
ResolveWritePath resolves an agent path for writing and rejects read-only mounts.
func (*PathResolver) ToHostPath ¶ added in v0.60.0
func (r *PathResolver) ToHostPath(sandboxPath string) (string, bool)
ToHostPath maps a sandbox-space path to its host path using the deepest mount.
func (*PathResolver) ToSandboxPath ¶ added in v0.60.0
func (r *PathResolver) ToSandboxPath(hostPath string) (string, bool)
ToSandboxPath maps a host path to its sandbox-space path using the deepest mount.
type PathResolverConfig ¶ added in v0.60.0
type PathResolverConfig struct {
WorkspaceRoot string
WorkingDir string
Mounts []Mount
TempDirHost string
}
PathResolverConfig describes the mount view used by PathResolver.
type Policy ¶
type Policy struct {
// Filesystem policy
Filesystem FilesystemPolicy
// Network policy
Network NetworkPolicy
// Env holds environment variables injected into sandboxed processes.
Env map[string]string
// InheritEnv includes system environment variables when true.
InheritEnv bool
// Timeout for process execution. Zero means no timeout.
Timeout time.Duration
}
Policy is an immutable, backend-agnostic session policy describing requested limits for filesystem, network, and process constraints.
func (Policy) NetworkModeOrDefault ¶
func (p Policy) NetworkModeOrDefault() NetworkMode
NetworkModeOrDefault returns the network mode with default applied.
func (Policy) Validate ¶
Validate returns an error if the policy contains invalid configurations. This validates policy structure, not backend compatibility.
func (Policy) WorkspaceRootOrDefault ¶
WorkspaceRootOrDefault returns the mounted sandbox root on the host.
type PolicyCompatibilityError ¶
PolicyCompatibilityError indicates a policy is not compatible with a backend.
func (*PolicyCompatibilityError) Error ¶
func (e *PolicyCompatibilityError) Error() string
type ProcessHandle ¶
type ProcessHandle interface {
PID() int
Wait(ctx context.Context) (ExecResult, error)
Stdin() io.WriteCloser
Stdout() io.ReadCloser
Stderr() io.ReadCloser
Close() error
}
type ProcessRequest ¶
type ResilientSession ¶ added in v0.38.0
type ResilientSession struct {
// contains filtered or unexported fields
}
ResilientSession wraps a Session and transparently recreates it when the underlying session has been closed unexpectedly (e.g. container exited or sandbox process crashed). Explicit Close calls are permanent — no recreation after that.
func NewResilientSession ¶ added in v0.38.0
func NewResilientSession(session Session, create SessionCreator) *ResilientSession
NewResilientSession wraps an existing session with auto-recreation support.
func (*ResilientSession) Alive ¶ added in v0.38.0
func (r *ResilientSession) Alive() bool
func (*ResilientSession) Close ¶ added in v0.38.0
func (r *ResilientSession) Close() error
func (*ResilientSession) Done ¶ added in v0.38.0
func (r *ResilientSession) Done() <-chan struct{}
func (*ResilientSession) Exec ¶ added in v0.38.0
func (r *ResilientSession) Exec(ctx context.Context, command string, opts ExecOptions) (ExecResult, error)
func (*ResilientSession) Policy ¶ added in v0.38.0
func (r *ResilientSession) Policy() Policy
func (*ResilientSession) RefreshEnv ¶ added in v0.49.0
func (r *ResilientSession) RefreshEnv(updates map[string]string)
RefreshEnv overlays injected env entries for subsequent Exec and StartProcess calls. Keeping the overlay at this wrapper boundary makes credential rotation backend-independent. A recreated inner session is built from current state and clears the overlay in ensureAlive.
func (*ResilientSession) ResolvePath ¶ added in v0.38.0
func (r *ResilientSession) ResolvePath(path string) (string, error)
func (*ResilientSession) ResolveWritePath ¶ added in v0.38.0
func (r *ResilientSession) ResolveWritePath(path string) (string, error)
func (*ResilientSession) StartProcess ¶ added in v0.38.0
func (r *ResilientSession) StartProcess(ctx context.Context, req ProcessRequest) (ProcessHandle, error)
func (*ResilientSession) WorkingDir ¶ added in v0.38.0
func (r *ResilientSession) WorkingDir() string
type ResolvedPath ¶ added in v0.60.0
ResolvedPath is a path resolved through a mount.
type Session ¶
type Session interface {
// Lifecycle
Policy() Policy
Close() error
Alive() bool
Done() <-chan struct{}
// Host process surface
Exec(ctx context.Context, command string, opts ExecOptions) (ExecResult, error)
StartProcess(ctx context.Context, req ProcessRequest) (ProcessHandle, error)
// Path resolution — use os.* with the resolved path for file I/O.
// ResolvePath validates read access; use ResolveWritePath for write operations.
ResolvePath(path string) (string, error)
// ResolveWritePath is like ResolvePath but additionally rejects paths in
// read-only mounts.
ResolveWritePath(path string) (string, error)
WorkingDir() string
}
Session is the plugin-facing sandbox surface: lifecycle + mediated host access. It combines what was previously the Session lifecycle interface and the Host file/process interface into a single type so plugins receive one coherent value.