Documentation
¶
Index ¶
- Constants
- Variables
- func BuildToolPermissionRequest(req PermissionRequest, opts ToolPermissionOptions) (types.ToolPermissionRequest, error)
- func EnvSliceToMap(env []string) map[string]string
- func ErrorForDecision(result DecisionResult) error
- func ErrorForPermissionResult(result types.PermissionResult, fallbackReason string) error
- func ResolveToolPermission(ctx context.Context, checker types.CanUseToolFn, req PermissionRequest, ...) (types.PermissionResult, error)
- type AccessKind
- type ApprovalRequiredError
- type ApprovalScope
- type CommandPolicy
- type Context
- type DaggerExecutor
- type DaggerExecutorConfig
- type DaggerRunOptions
- type Decision
- type DecisionResult
- type EnvironmentKind
- type Executor
- type ExecutorConfig
- type FilesystemPolicy
- type NoopExecutor
- type PathDecision
- type PermissionDecision
- type PermissionDeniedError
- type PermissionRequest
- type RunRequest
- type RunResult
- type ToolAccessPreview
- type ToolPermissionOptions
Constants ¶
const ( MetadataRequestKey = "sandbox_request" MetadataPreviewKey = "sandbox_preview" )
Variables ¶
var ErrDaggerNotImplemented = errors.New(
"dagger sandbox backend: not implemented — see GitHub issue for roadmap",
)
ErrDaggerNotImplemented is returned by all DaggerExecutor methods until the Dagger backend is fully implemented (see GitHub issue for the roadmap). This sentinel lets callers distinguish "backend not built yet" from other errors so they can fall back to NoopExecutor gracefully.
Functions ¶
func BuildToolPermissionRequest ¶
func BuildToolPermissionRequest(req PermissionRequest, opts ToolPermissionOptions) (types.ToolPermissionRequest, error)
BuildToolPermissionRequest converts a normalized sandbox request into the runtime permission request expected by the engine.
func EnvSliceToMap ¶
enviroToMap converts an os.Environ()-style slice to a map. Exported for use in tests.
func ErrorForDecision ¶
func ErrorForDecision(result DecisionResult) error
ErrorForDecision converts a normalized decision into a conventional Go error.
func ErrorForPermissionResult ¶
func ErrorForPermissionResult(result types.PermissionResult, fallbackReason string) error
ErrorForPermissionResult converts a runtime permission result into a conventional Go error.
func ResolveToolPermission ¶
func ResolveToolPermission( ctx context.Context, checker types.CanUseToolFn, req PermissionRequest, opts ToolPermissionOptions, ) (types.PermissionResult, error)
ResolveToolPermission runs the normalized request through the active permission resolver.
Types ¶
type AccessKind ¶
type AccessKind string
AccessKind describes the resource access being requested.
const ( AccessRead AccessKind = "read" AccessWrite AccessKind = "write" AccessCreate AccessKind = "create" AccessDelete AccessKind = "delete" AccessSearch AccessKind = "search" AccessExecute AccessKind = "execute" AccessNetwork AccessKind = "network" AccessEscalate AccessKind = "escalate" )
type ApprovalRequiredError ¶
type ApprovalRequiredError struct {
Reason string
}
ApprovalRequiredError is returned when an action is valid but needs approval.
func (*ApprovalRequiredError) Error ¶
func (e *ApprovalRequiredError) Error() string
type ApprovalScope ¶
type ApprovalScope string
ApprovalScope controls how long an approval grant should remain valid.
const ( ApprovalScopeToolCall ApprovalScope = "tool_call" ApprovalScopeTurn ApprovalScope = "turn" ApprovalScopeSession ApprovalScope = "session" )
type CommandPolicy ¶
type CommandPolicy struct {
// contains filtered or unexported fields
}
CommandPolicy is the single authoritative command safety classifier. It replaces the scattered PermissionValidator in the bash package.
Two-tier model (mirrors Codex):
- IsKnownSafe: explicit allowlist → bypass approval entirely
- Evaluate: deny/ask/allow based on danger fragments and command type
func NewDefaultCommandPolicy ¶
func NewDefaultCommandPolicy() *CommandPolicy
func (*CommandPolicy) Evaluate ¶
func (p *CommandPolicy) Evaluate(command string) DecisionResult
Evaluate returns the policy decision for a command.
Order of evaluation:
- Known-safe allowlist → Allow (no approval prompt needed)
- Shell wrapper → evaluate composed inner
- Deny fragment match → Deny
- Ask-command list → Ask
- Command type classification: write/vcs/unknown → Ask, read/search/state → Allow
func (*CommandPolicy) IsKnownSafe ¶
func (p *CommandPolicy) IsKnownSafe(command string) bool
IsKnownSafe returns true when every sub-command in the expression is on the explicit safe allowlist, meaning the entire command can be executed without any approval prompt.
Mirrors Codex's is_known_safe_command. If the command is a shell wrapper (bash -c "…") the inner script is split into segments and each is checked.
type Context ¶
type Context struct {
WorkingDirectory string
WorkspaceRoot string
AdditionalRoots []string
Environment EnvironmentKind
SandboxEnabled bool
}
Context carries the execution boundary relevant to sandbox decisions.
func (Context) ResolvePath ¶
ResolvePath resolves a candidate path according to the current execution context. When a WorkspaceRoot is configured, relative paths are anchored to the root and validated to stay inside it. Absolute paths bypass workspace containment so the user can approve writes to /tmp or other out-of-workspace directories — the FilesystemPolicy dangerous-prefix check still applies downstream.
type DaggerExecutor ¶
type DaggerExecutor struct {
// contains filtered or unexported fields
}
DaggerExecutor runs commands inside isolated OCI containers managed by the Dagger engine (https://dagger.io). Each agent session gets its own container environment; changes are tracked as git commits so the full history is recoverable.
┌─────────────────────────────────────────────────────────────────────┐ │ STATUS: STUB — methods return ErrDaggerNotImplemented │ │ │ │ Implementation is tracked in GitHub issue #XX. │ │ DO NOT implement pieces here without first reading the issue. │ └─────────────────────────────────────────────────────────────────────┘
Architecture overview (to be implemented):
DaggerExecutor
│
├─ dagger.Connect(ctx) → *dagger.Client [once per session]
│
├─ environments map[string]*Environment [per environment ID]
│ └─ container *dagger.Container [persistent across calls]
│ └─ gitRepo *repository.Repository [tracks file changes]
│
└─ Run(ctx, req) → RunResult
├─ resolve or create Environment
├─ container.WithExec(req.Command)
├─ capture stdout/stderr
├─ persist mutated container state
└─ if req.Dagger.ExposePorts: tunnel via dag.Host().Tunnel()
Dependency: dagger.io/dagger (NOT yet in go.mod — add when implementing) Minimum Dagger engine version: v0.18+
func (*DaggerExecutor) Healthy ¶
func (e *DaggerExecutor) Healthy(_ context.Context) error
Healthy checks that the Dagger engine is reachable. Stub: always returns ErrDaggerNotImplemented.
func (*DaggerExecutor) Kind ¶
func (e *DaggerExecutor) Kind() EnvironmentKind
func (*DaggerExecutor) Run ¶
func (e *DaggerExecutor) Run(_ context.Context, _ RunRequest) (RunResult, error)
Run executes a shell command inside the Dagger container environment. Stub: always returns ErrDaggerNotImplemented.
type DaggerExecutorConfig ¶
type DaggerExecutorConfig struct {
// BaseImage is the OCI image used for new environments.
// Default: "ubuntu:24.04"
BaseImage string
// SetupCommands are shell commands run once when building the base image.
// Example: ["apt-get update -y", "apt-get install -y python3 nodejs"]
SetupCommands []string
// WorkDir is the working directory inside the container.
// Default: "/workdir"
WorkDir string
// Env is a set of KEY=VALUE pairs always injected into the container.
Env map[string]string
// StorageDir is the host directory where environment state is persisted.
// Default: ~/.config/seshat/sandbox/
StorageDir string
// NetworkAccess controls whether the container can reach the internet.
// Default: true (containers have internet access by default).
// Set to false for fully air-gapped sandboxes.
NetworkAccess bool
}
DaggerExecutorConfig holds static configuration for the DaggerExecutor. All fields have sensible defaults and can be overridden by the operator.
func DefaultDaggerConfig ¶
func DefaultDaggerConfig() DaggerExecutorConfig
DefaultDaggerConfig returns a DaggerExecutorConfig with sensible defaults.
type DaggerRunOptions ¶
type DaggerRunOptions struct {
// ExposePorts lists container ports to tunnel back to the host.
// The RunResult.Endpoints map is populated with host:port mappings.
ExposePorts []int
// EnvironmentID selects a named, persistent environment.
// Empty means the default session environment.
EnvironmentID string
}
DaggerRunOptions carries Dagger-specific per-run parameters. These are only meaningful when the active Executor is a DaggerExecutor.
type DecisionResult ¶
DecisionResult is the normalized output of a policy decision.
type EnvironmentKind ¶
type EnvironmentKind string
EnvironmentKind describes where a tool executes. The policy layer should know this, even if the actual backend runtime (local process, docker, remote host) is implemented elsewhere.
const ( EnvironmentLocal EnvironmentKind = "local" EnvironmentWorktree EnvironmentKind = "worktree" EnvironmentDocker EnvironmentKind = "docker" EnvironmentRemote EnvironmentKind = "remote" EnvironmentUnknown EnvironmentKind = "unknown" )
type Executor ¶
type Executor interface {
// Run executes a command inside the sandbox and streams its output.
// Implementations must respect ctx cancellation and RunRequest.Timeout.
Run(ctx context.Context, req RunRequest) (RunResult, error)
// Kind identifies which backend this is.
Kind() EnvironmentKind
// Healthy returns nil when the executor is ready to accept work.
// A non-nil error means the backend is unavailable (Dagger engine down,
// Docker socket missing, etc). The engine calls this once at startup.
Healthy(ctx context.Context) error
}
Executor is the interface all sandbox backends implement. The engine routes bash/shell execution through the active Executor.
Backends:
- NoopExecutor — runs commands directly on the host OS (default, no isolation)
- DaggerExecutor — runs commands inside an isolated Dagger/OCI container
Selection at startup via ExecutorConfig.Kind. Tools that are read-only (file read, glob, grep) bypass the Executor and go directly to the host FS.
func NewExecutor ¶
func NewExecutor(cfg ExecutorConfig) (Executor, error)
NewExecutor creates the Executor selected by cfg.Kind. Returns an error when the requested backend is unavailable.
type ExecutorConfig ¶
type ExecutorConfig struct {
// Kind selects the backend. Defaults to EnvironmentLocal (noop).
Kind EnvironmentKind
// Dagger holds Dagger-specific configuration.
// Only used when Kind == EnvironmentDocker.
Dagger DaggerExecutorConfig
}
ExecutorConfig is the configuration passed to NewExecutor at startup.
type FilesystemPolicy ¶
type FilesystemPolicy struct {
// contains filtered or unexported fields
}
FilesystemPolicy centralizes common filesystem access checks.
func NewDefaultFilesystemPolicy ¶
func NewDefaultFilesystemPolicy() *FilesystemPolicy
func (*FilesystemPolicy) EvaluatePath ¶
func (p *FilesystemPolicy) EvaluatePath(ctx Context, path string, access AccessKind) (PathDecision, error)
type NoopExecutor ¶
type NoopExecutor struct{}
NoopExecutor runs commands directly on the host OS with no additional isolation. This is the default executor and mirrors the current behavior of the bash tool before sandboxing was introduced.
Isolation: none. The CommandPolicy and FilesystemPolicy still apply, but there is no OS-level boundary between the agent and the host filesystem.
func NewNoopExecutor ¶
func NewNoopExecutor() *NoopExecutor
NewNoopExecutor returns a NoopExecutor ready to use.
func (*NoopExecutor) Kind ¶
func (e *NoopExecutor) Kind() EnvironmentKind
func (*NoopExecutor) Run ¶
func (e *NoopExecutor) Run(ctx context.Context, req RunRequest) (RunResult, error)
type PathDecision ¶
type PathDecision struct {
DecisionResult
ResolvedPath string
}
PathDecision includes the resolved path for filesystem checks.
type PermissionDecision ¶
type PermissionDecision struct {
Decision Decision
Reason string
Scope ApprovalScope
ApprovedPaths []string
Metadata map[string]any
}
PermissionDecision is the normalized response returned by the sandbox/approval layer.
func (PermissionDecision) IsAllowed ¶
func (d PermissionDecision) IsAllowed() bool
IsAllowed reports whether the request was approved.
type PermissionDeniedError ¶
type PermissionDeniedError struct {
Reason string
}
PermissionDeniedError is returned when the sandbox denies an action outright.
func (*PermissionDeniedError) Error ¶
func (e *PermissionDeniedError) Error() string
type PermissionRequest ¶
type PermissionRequest struct {
ToolName string
Description string
Environment EnvironmentKind
Access AccessKind
Command string
Paths []string
NetworkTargets []string
Justification string
Scope ApprovalScope
Metadata map[string]any
}
PermissionRequest is the normalized request emitted by tools and runtimes when they need a sandbox/approval decision.
func (PermissionRequest) DescriptionText ¶
func (r PermissionRequest) DescriptionText() string
DescriptionText returns a stable approval-friendly description.
func (PermissionRequest) MetadataMap ¶
func (r PermissionRequest) MetadataMap() map[string]any
MetadataMap returns stable metadata for the shared permission pipeline.
func (PermissionRequest) Validate ¶
func (r PermissionRequest) Validate() error
Validate ensures a permission request is structurally usable by the approval pipeline.
type RunRequest ¶
type RunRequest struct {
// Command is the shell command string to execute.
// Passed as-is to the interpreter defined by Shell.
Command string
// Shell specifies the interpreter (default: /bin/sh -c).
// Ignored by DaggerExecutor which always uses sh inside the container.
Shell []string
// Env is a set of KEY=VALUE pairs injected into the command environment
// on top of the executor's base environment.
Env map[string]string
// WorkDir is the working directory for the command.
// For DaggerExecutor, this is relative to the container's /workdir.
WorkDir string
// Stdin is connected to the command's standard input (may be nil).
Stdin io.Reader
// Timeout is the maximum duration for the command.
// Zero means no timeout beyond the parent context deadline.
Timeout time.Duration
// Background requests a fire-and-forget execution (no output captured).
// Used for long-running processes (servers, watchers).
Background bool
// Dagger-specific extensions — ignored by NoopExecutor.
Dagger *DaggerRunOptions
}
RunRequest is the input to Executor.Run.
type RunResult ¶
type RunResult struct {
// Stdout and Stderr capture the command output.
// Empty when Background=true.
Stdout string
Stderr string
// ExitCode is the process exit code. Non-zero indicates failure.
ExitCode int
// Duration is the wall-clock time the command took.
Duration time.Duration
// Endpoints maps exposed port numbers to "host:port" strings.
// Populated by DaggerExecutor when DaggerRunOptions.ExposePorts is set.
Endpoints map[int]string
}
RunResult is the output of Executor.Run.
type ToolAccessPreview ¶
type ToolAccessPreview struct {
ToolName string `json:"tool_name"`
Environment string `json:"environment,omitempty"`
Access string `json:"access,omitempty"`
Command string `json:"command,omitempty"`
Paths []string `json:"paths,omitempty"`
NetworkTargets []string `json:"network_targets,omitempty"`
Justification string `json:"justification,omitempty"`
}
ToolAccessPreview is the normalized human-facing summary of what a tool is asking to do.
func BuildPreview ¶
func BuildPreview(req PermissionRequest) ToolAccessPreview
BuildPreview converts a permission request into a stable preview payload.
type ToolPermissionOptions ¶
type ToolPermissionOptions struct {
ToolInput map[string]any
ToolUseID string
SessionID types.SessionID
TurnID types.TurnID
PermissionMode types.PermissionMode
WorkingDirectory string
IsToolRunningInSandbox bool
Stage types.ToolPermissionStage
Intent types.ToolPermissionIntent
Metadata map[string]any
}
ToolPermissionOptions carries runtime-specific fields for the shared permission pipeline request.