egg

package
v0.147.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CaptureSessionHistory added in v0.122.0

func CaptureSessionHistory(agent, cwd, eggDir, home string, startedAfter time.Time) error

CaptureSessionHistory copies the agent's native session history (e.g. Claude's JSONL) into the egg directory as chat.jsonl.gz + chat.meta. Callers may choose to treat capture failures as best-effort, but this function reports them.

func DefaultCacheDirs added in v0.22.0

func DefaultCacheDirs() []string

DefaultCacheDirs returns OS-standard cache directories that build tools need. Go, npm, pip, cargo, etc. all write to these. No secrets live here.

func DefaultDenyPaths added in v0.20.0

func DefaultDenyPaths() []string

DefaultDenyPaths returns paths that should be blocked by default in sandboxed sessions.

func FindLiveSessionFile added in v0.127.0

func FindLiveSessionFile(agent, cwd, home string) (string, error)

FindLiveSessionFile locates the live agent JSONL file for an active session. Returns the file path and agent name, or empty strings if not found.

func InferLocalPorts added in v0.144.0

func InferLocalPorts(cfg *EggConfig, agent, providerURL string) []int

InferLocalPorts returns the host loopback ports that must be forwarded into the jail's network namespace.

Retaining CLONE_NEWNET means the jail's 127.0.0.1 is not the host's, so every local model provider breaks unless its port is forwarded. Existing configs declare loopback intent with domain literals rather than ports, so the ports are inferred from the agent profile and the provider URL. Explicit network.local_ports entries are always honored.

func ParseChatMeta added in v0.122.0

func ParseChatMeta(data string) map[string]string

ParseChatMeta parses a simple key=value metadata file.

func ParseFSRules added in v0.20.0

func ParseFSRules(fs []string, home string) ([]sandbox.Mount, []string, []string)

ParseFSRules splits fs entries into mounts, deny paths, and deny-write paths. Entries are "mode:path" where mode is rw, ro, deny, or deny-write.

func RequiresSandbox added in v0.144.0

func RequiresSandbox(cfg *EggConfig, agentName string) bool

RequiresSandbox reports whether cfg asks the runtime to enforce any policy. Trusted-host compatibility is intentionally recognized only when every relevant boundary is wide open. In particular, a wildcard network policy by itself must not silently disable filesystem, syscall, and resource isolation. Keep this predicate aligned with the explicit OuterBoundary marker passed to the egg subprocess by spawnEgg.

func RestoreSessionHistory added in v0.122.0

func RestoreSessionHistory(agent, cwd, eggDir, home string) (agentSessionID string, err error)

RestoreSessionHistory decompresses chat.jsonl.gz and places it in the agent's native session directory so the agent can resume the conversation. Returns the agent session ID for use with resume flags.

Types

type AgentProfile added in v0.9.8

type AgentProfile struct {
	Domains       []string          // network domains needed (empty = no network)
	EnvVars       []string          // required env var names (merged from host)
	SetEnv        map[string]string // env vars forced to a default (host/config still wins)
	PlatformEnv   []string          // platform-specific env vars (e.g. macOS Keychain access)
	WriteDirs     []string          // relative to $HOME, need write access
	WriteRegex    []string          // dirs needing UseRegex (e.g. ".claude" covers .claude.json)
	SettingsFile  string            // agent config file relative to HOME (e.g. ".claude/settings.json")
	SessionDir    string            // agent session storage relative to $HOME (e.g. ".claude/projects")
	ResumeFlag    string            // CLI flag for resuming (e.g. "--resume")
	SessionIDFlag string            // CLI flag for controlling session ID (e.g. "--session-id")
}

AgentProfile declares what an agent needs from the host system. The sandbox merges these into the egg config automatically so users don't need to know agent internals (e.g. where Claude stores config).

func Profile added in v0.9.8

func Profile(agent string) AgentProfile

Profile returns the agent profile for the given agent name. Unknown agents get a restrictive default (no network, no extra dirs). Platform-specific env vars are injected based on runtime.GOOS.

type BaseField added in v0.23.0

type BaseField struct {
	Name    string `yaml:"name,omitempty"`
	FS      string `yaml:"fs,omitempty"`
	Network string `yaml:"network,omitempty"`
	Env     string `yaml:"env,omitempty"`
}

BaseField handles the `base` key in egg configs. It can be a scalar string (backward compat: "none", "strict", etc.) or an object with per-section masks.

func (BaseField) HasMasks added in v0.23.0

func (b BaseField) HasMasks() bool

func (BaseField) IsZero added in v0.23.0

func (b BaseField) IsZero() bool

func (BaseField) MarshalYAML added in v0.23.0

func (b BaseField) MarshalYAML() (interface{}, error)

func (*BaseField) UnmarshalYAML added in v0.23.0

func (b *BaseField) UnmarshalYAML(value *yaml.Node) error

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps the generated gRPC client for a single egg process.

func Dial

func Dial(socketPath, tokenPath string) (*Client, error)

Dial connects to an egg's Unix socket and reads its auth token.

func (*Client) AttachSession

func (c *Client) AttachSession(ctx context.Context, sessionID string) (pb.Egg_SessionClient, error)

AttachSession opens a bidirectional stream for PTY I/O.

func (*Client) Close

func (c *Client) Close() error

Close closes the gRPC connection.

func (*Client) Kill

func (c *Client) Kill(ctx context.Context, sessionID string) error

Kill terminates the session.

func (*Client) Resize

func (c *Client) Resize(ctx context.Context, sessionID string, rows, cols uint32) error

Resize changes terminal dimensions.

func (*Client) Status added in v0.19.0

func (c *Client) Status(ctx context.Context) (*pb.StatusResponse, error)

Status returns debug stats from the egg session.

type ConfigSnapshot added in v0.20.0

type ConfigSnapshot struct {
	// contains filtered or unexported fields
}

ConfigSnapshot holds copies of agent config files taken before a session.

func SnapshotAgentConfig added in v0.20.0

func SnapshotAgentConfig(agent, isolatedHome string) *ConfigSnapshot

SnapshotAgentConfig reads critical config files for the given agent and saves their contents.

func (*ConfigSnapshot) Restore added in v0.20.0

func (s *ConfigSnapshot) Restore()

Restore reverts config files to their pre-session state.

type DrilledHole added in v0.144.0

type DrilledHole struct {
	Kind   string // "domain" | "write_dir" | "env" | "local_port"
	Value  string
	Agent  string
	Reason string
}

DrilledHole records one capability the system added on the agent's behalf that the user did not ask for. The sandbox is egg.yaml plus auto-drilled agent holes; without attribution those holes are invisible and the policy cannot be reviewed by the person relying on it.

type EffectivePolicy added in v0.144.0

type EffectivePolicy struct {
	Agent       string
	Mounts      []sandbox.Mount
	Deny        []string
	DenyWrite   []string
	NetworkNeed sandbox.NetworkNeed
	Domains     []string
	LocalPorts  []int
	Mode        string
	Drilled     []DrilledHole
	Derived     []DrilledHole
	Suppressed  []DrilledHole
}

EffectivePolicy is the fully resolved sandbox policy for a session: what the user declared, plus what the agent profile required, with every addition attributable. This is what `wt egg explain` renders.

func ResolvePolicy added in v0.144.0

func ResolvePolicy(cfg *EggConfig, agent, home string) EffectivePolicy

ResolvePolicy merges an egg config with the named agent's profile and reports the result along with the provenance of every automatic addition. An empty agent name (a plain shell or command session) drills nothing.

func ResolvePolicyWithProvider added in v0.144.0

func ResolvePolicyWithProvider(cfg *EggConfig, agent, home, providerURL string) (EffectivePolicy, error)

ResolvePolicyWithProvider resolves the effective policy while treating an explicit provider URL as authoritative routing metadata. The derived exact host replaces the profile's static vendor domains and remains visible as a distinct source in explain output.

type EggConfig added in v0.8.0

type EggConfig struct {
	Base                       BaseField         `yaml:"base,omitempty"`
	FS                         []string          `yaml:"fs"`
	Network                    NetworkField      `yaml:"network"`
	Env                        EnvField          `yaml:"env,omitempty"`
	Resources                  EggResources      `yaml:"resources"`
	Shell                      string            `yaml:"shell"`
	DangerouslySkipPermissions bool              `yaml:"dangerously_skip_permissions"`
	Audit                      bool              `yaml:"audit"`
	Trace                      bool              `yaml:"trace"`
	AgentSettings              map[string]string `yaml:"agent_settings,omitempty"` // agent name -> settings file path
}

EggConfig holds the sandbox and environment configuration for egg sessions.

func DefaultEggConfig added in v0.8.0

func DefaultEggConfig() *EggConfig

DefaultEggConfig returns the restrictive default config used when no egg.yaml exists. CWD is writable, home is read-only except agent-drilled holes. Sensitive dirs are denied. OS-standard cache dirs are writable so build tools (go, npm, cargo, pip) work out of the box. egg.yaml itself is deny-write so agents can read but not modify their sandbox config.

func DiscoverEggConfig added in v0.8.0

func DiscoverEggConfig(cwd string, wingDefault *EggConfig) *EggConfig

DiscoverEggConfig looks for egg.yaml in the given directory, falls back to the wing default, then to built-in defaults. Project configs are resolved through the base chain (additive inheritance) before being returned.

func LoadEggConfig added in v0.8.0

func LoadEggConfig(path string) (*EggConfig, error)

LoadEggConfig reads and parses an egg.yaml file.

func LoadEggConfigFromYAML added in v0.8.0

func LoadEggConfigFromYAML(yamlStr string) (*EggConfig, error)

LoadEggConfigFromYAML parses an egg config from a YAML string.

func MergeEggConfig added in v0.21.0

func MergeEggConfig(parent, child *EggConfig) *EggConfig

MergeEggConfig merges a child config on top of a parent config. - fs: append child to parent; child ro/rw overrides parent deny for same path - network: union (dedup); "*" in either -> ["*"] - env: union (dedup); "*" in either -> ["*"] - resources: child wins per-field (non-zero overrides parent) - shell: child wins if non-empty - dangerously_skip_permissions: OR

func ResolveEggConfig added in v0.21.0

func ResolveEggConfig(path string) (*EggConfig, error)

ResolveEggConfig loads an egg.yaml and resolves its base chain, returning a fully merged config. If base is empty, merges on top of DefaultEggConfig. If base is "none", returns the config as-is (empty slate).

func UnsandboxedEggConfig added in v0.144.0

func UnsandboxedEggConfig() *EggConfig

UnsandboxedEggConfig returns the explicit trusted-host policy used when an outer VM or container is the security boundary. It passes the full environment and network through and declares no filesystem or resource restrictions, which causes the egg runtime to keep PTY persistence while skipping the OS sandbox entirely.

func (*EggConfig) BuildEnv added in v0.8.0

func (c *EggConfig) BuildEnv(home string) []string

BuildEnv filters the host environment based on the config. SSH_AUTH_SOCK is stripped when ~/.ssh is denied — otherwise the agent can still make outbound SSH connections via the forwarded socket despite the filesystem deny, causing unexpected host-key prompts inside the egg. If home is non-empty it is used to expand ~ in FS rules; otherwise os.UserHomeDir().

func (*EggConfig) BuildEnvMap added in v0.8.0

func (c *EggConfig) BuildEnvMap(home string) map[string]string

BuildEnvMap returns the environment as a map for proto SpawnRequest. If home is non-empty it is used to expand ~ in FS rules; otherwise os.UserHomeDir().

func (*EggConfig) IsAllEnv added in v0.20.0

func (c *EggConfig) IsAllEnv() bool

IsAllEnv returns true if the env config passes all environment variables.

func (*EggConfig) NetworkSummary added in v0.20.0

func (c *EggConfig) NetworkSummary() string

NetworkSummary returns a short description of the network config for logging.

func (*EggConfig) SSHAgentSocketDenyPaths added in v0.145.0

func (c *EggConfig) SSHAgentSocketDenyPaths(home string, force bool) []string

SSHAgentSocketDenyPaths returns the live SSH agent endpoint that must be masked alongside ~/.ssh. Removing SSH_AUTH_SOCK from the child environment is not sufficient: an agent can discover common socket paths under /tmp or the user runtime directory and connect to them directly.

Listing SSH_AUTH_SOCK explicitly (rather than via env: ["*"]) is a deliberate opt-in to agent-backed SSH without exposing raw key files. Shared-host callers pass force=true because they never forward host authentication agents.

func (*EggConfig) ToSandboxConfig added in v0.8.0

func (c *EggConfig) ToSandboxConfig(home string) sandbox.Config

ToSandboxConfig converts the egg config to a sandbox.Config. If home is non-empty it is used to expand ~ in FS rules; otherwise os.UserHomeDir().

func (*EggConfig) YAML added in v0.8.0

func (c *EggConfig) YAML() (string, error)

YAML returns the config serialized as YAML.

type EggResources added in v0.8.0

type EggResources struct {
	CPU     string `yaml:"cpu"`    // duration: "300s"
	Memory  string `yaml:"memory"` // size: "2GB"
	MaxFDs  uint32 `yaml:"max_fds"`
	MaxPids uint32 `yaml:"max_pids"` // cgroup pids.max (Linux only)
}

EggResources configures resource limits for sandboxed processes.

func (*EggResources) CPUDuration added in v0.8.0

func (r *EggResources) CPUDuration() time.Duration

CPUDuration parses the CPU field as a duration.

func (*EggResources) MemBytes added in v0.8.0

func (r *EggResources) MemBytes() uint64

MemBytes parses the Memory field as bytes (supports GB, MB suffixes).

type EnvField added in v0.20.0

type EnvField []string

EnvField handles YAML unmarshaling of env: string | []string. "*" → ["*"], list → as-is.

func (*EnvField) UnmarshalYAML added in v0.20.0

func (e *EnvField) UnmarshalYAML(value *yaml.Node) error

type NetworkField added in v0.20.0

type NetworkField struct {
	Domains      []string `yaml:"domains,omitempty"`
	LocalPorts   []int    `yaml:"local_ports,omitempty"`
	Mode         string   `yaml:"mode,omitempty"`          // "" (default) | "observe" | "enforce"
	Log          *bool    `yaml:"log,omitempty"`           // legacy compatibility; policy-decision auditing is always on
	AgentDomains string   `yaml:"agent_domains,omitempty"` // ""/"merge" (default) | "none"
}

NetworkField handles YAML unmarshaling of network: string | []string | mapping. The scalar and list forms are a frozen contract: "none"/"" → no network, "*" → unrestricted, list → domain allowlist. The mapping form is additive and carries the loopback forwarding, enforcement mode, and legacy egress-logging option described in docs/sandbox-enhancement-design.md. Log is retained as a frozen config contract; policy decisions are audited independently of it.

func (NetworkField) IsZero added in v0.144.0

func (n NetworkField) IsZero() bool

IsZero lets yaml omitempty treat an unset network block as absent.

func (NetworkField) MarshalYAML added in v0.144.0

func (n NetworkField) MarshalYAML() (interface{}, error)

MarshalYAML re-emits the legacy scalar and list shapes whenever no mapping-only option is set, so rendered configs stay byte-stable for existing users.

func (*NetworkField) UnmarshalYAML added in v0.20.0

func (n *NetworkField) UnmarshalYAML(value *yaml.Node) error

type RunConfig added in v0.9.0

type RunConfig struct {
	Agent string
	Kind  string
	// Command replaces the agent entirely; AgentArgs extends the agent's own
	// invocation and keeps the session an agent session. They are alternatives.
	Command      []string
	AgentArgs    []string
	CWD          string
	Shell        string
	FS           []string // "rw:./", "deny:~/.ssh"
	Network      []string // domain list
	LocalPorts   []int    // host loopback ports forwarded into the network namespace
	NetworkMode  string   // ""/"enforce" or "observe"
	AgentDomains string   // ""/"merge" or "none"
	Env          map[string]string
	Rows         uint32
	Cols         uint32

	DangerouslySkipPermissions bool
	CPULimit                   time.Duration
	MemLimit                   uint64
	MaxFDs                     uint32
	PidLimit                   uint32
	Debug                      bool
	Audit                      bool
	Trace                      bool          // wrap sandbox command with strace (Linux only)
	VTE                        bool          // use VTerm snapshot for reconnect instead of replay buffer
	RenderedConfig             string        // effective egg config as YAML (after merge/resolve)
	UserHome                   string        // per-user home directory (relay sessions only)
	SkipHostAgentEnv           bool          // shared hosts keep provider credentials in UserHome
	IdleTimeout                time.Duration // 0 = disabled; self-terminate after this much idle
	ResumeSessionID            string        // agent session ID to resume (from chat.meta)
	ToolNames                  []string      // names of privileged tools (for shim generation)
	ToolSocketPath             string        // path to tool.sock (set by wing, empty = no tools)
	OuterBoundary              bool          // explicit trusted-host marker from the parent process
}

RunConfig holds everything needed to start a single egg session.

type Server

type Server struct {
	pb.UnimplementedEggServer
	// contains filtered or unexported fields
}

Server implements the Egg gRPC service — wraps a SINGLE process. Each egg is its own child process with its own socket/PID/token in ~/.wingthing/eggs/<session-id>/.

func NewServer

func NewServer(dir string) (*Server, error)

NewServer creates a new per-session egg server. dir is the session directory: ~/.wingthing/eggs/<session-id>/

func (*Server) Kill

func (s *Server) Kill(ctx context.Context, req *pb.KillRequest) (*pb.KillResponse, error)

Kill terminates the session.

func (*Server) Resize

func (s *Server) Resize(ctx context.Context, req *pb.ResizeRequest) (*pb.ResizeResponse, error)

Resize changes the terminal dimensions.

func (*Server) RunSession added in v0.9.0

func (s *Server) RunSession(ctx context.Context, rc RunConfig) error

RunSession is the core lifecycle: create sandbox, start agent in PTY, serve gRPC, exit when done.

func (*Server) Session

func (s *Server) Session(stream pb.Egg_SessionServer) error

Session implements the bidirectional PTY I/O stream.

func (*Server) Status added in v0.19.0

func (s *Server) Status(ctx context.Context, req *pb.StatusRequest) (*pb.StatusResponse, error)

type Session

type Session struct {
	ID             string
	PID            int
	Agent          string
	Kind           string
	Command        []string
	CWD            string
	Network        string // summary: "none", "*", or comma-separated domains
	RenderedConfig string // effective egg config as YAML (after merge/resolve)
	Cols           uint32
	Rows           uint32
	StartedAt      time.Time
	// contains filtered or unexported fields
}

Session holds a single PTY process and its state.

type ToolListEntry added in v0.130.0

type ToolListEntry struct {
	Name        string             `json:"name"`
	Description string             `json:"description,omitempty"`
	Params      []config.ToolParam `json:"params,omitempty"`
}

ToolListEntry describes one tool for the list action.

type ToolListResponse added in v0.130.0

type ToolListResponse struct {
	Tools []ToolListEntry `json:"tools"`
}

ToolListResponse is returned for the "list" action.

type ToolListener added in v0.130.0

type ToolListener struct {
	// contains filtered or unexported fields
}

ToolListener accepts connections on a Unix socket and dispatches tool execution to a shared ToolRunner. Egg sessions reach tools this way; the remote MCP server wraps the same runner over HTTP.

func NewToolListener added in v0.130.0

func NewToolListener(sockPath string, tools []*config.ToolConfig) (*ToolListener, error)

NewToolListener creates and starts a tool socket listener. sockPath is the path for the Unix socket (e.g. ~/.wingthing/eggs/<session>/tool.sock).

func (*ToolListener) Close added in v0.130.0

func (tl *ToolListener) Close() error

Close stops the listener and waits for in-flight requests to finish.

func (*ToolListener) Reload added in v0.130.0

func (tl *ToolListener) Reload(tools []*config.ToolConfig)

Reload replaces the tool configs atomically.

type ToolRequest added in v0.130.0

type ToolRequest struct {
	Action string   `json:"action,omitempty"` // "list" for tool discovery
	Tool   string   `json:"tool,omitempty"`
	Args   []string `json:"args,omitempty"`
}

ToolRequest is sent by `wt tool-call` over the Unix socket.

type ToolResponse added in v0.130.0

type ToolResponse struct {
	ExitCode int    `json:"exit_code,omitempty"`
	Stdout   string `json:"stdout,omitempty"`
	Stderr   string `json:"stderr,omitempty"`
	Error    string `json:"error,omitempty"`
}

ToolResponse is returned to the client.

type ToolRunner added in v0.140.0

type ToolRunner struct {
	// contains filtered or unexported fields
}

ToolRunner owns the privileged tool registry and executes tools. It is the shared execution core behind both the egg tool socket (ToolListener) and the remote MCP server — so a tool behaves identically whether an in-egg agent or a remote MCP client invokes it, and credentials stay in one place.

func NewToolRunner added in v0.140.0

func NewToolRunner(tools []*config.ToolConfig) *ToolRunner

NewToolRunner builds a runner from tool configs.

func (*ToolRunner) Call added in v0.140.0

func (r *ToolRunner) Call(name string, args []string) ToolResponse

Call executes a tool by name with positional args, enforcing per-tool concurrency.

func (*ToolRunner) CallWithEnv added in v0.140.0

func (r *ToolRunner) CallWithEnv(name string, args []string, extraEnv map[string]string) ToolResponse

CallWithEnv executes a tool with additional per-call identity metadata. Extra environment values are appended last, so a static tool config cannot spoof caller identity.

func (*ToolRunner) Has added in v0.140.0

func (r *ToolRunner) Has(name string) bool

Has reports whether a tool is registered.

func (*ToolRunner) List added in v0.140.0

func (r *ToolRunner) List() []ToolListEntry

List returns all registered tools (name + description), unfiltered.

func (*ToolRunner) ParamsFor added in v0.140.0

func (r *ToolRunner) ParamsFor(name string) ([]config.ToolParam, bool)

ParamsFor returns a tool's optional ordered parameter metadata.

func (*ToolRunner) Reload added in v0.140.0

func (r *ToolRunner) Reload(tools []*config.ToolConfig)

Reload replaces the tool configs atomically, reusing compatible semaphores.

func (*ToolRunner) TimeoutFor added in v0.140.0

func (r *ToolRunner) TimeoutFor(name string) time.Duration

TimeoutFor returns a tool's configured timeout, or 0 if unknown.

type VTerm added in v0.58.0

type VTerm struct {
	// contains filtered or unexported fields
}

VTerm wraps charmbracelet/x/vt with scrollback capture via ScrollOut callback. All methods are thread-safe. Callbacks fire inside Write, so mu is already held.

func NewVTerm added in v0.58.0

func NewVTerm(cols, rows int) *VTerm

NewVTerm creates a VTerm with the given dimensions.

func (*VTerm) Close added in v0.58.0

func (v *VTerm) Close() error

Close releases the emulator resources.

func (*VTerm) Resize added in v0.58.0

func (v *VTerm) Resize(cols, rows int)

Resize changes the terminal dimensions.

func (*VTerm) ScrollbackLen added in v0.58.0

func (v *VTerm) ScrollbackLen() int

ScrollbackLen returns the number of scrollback lines currently stored.

func (*VTerm) Snapshot added in v0.58.0

func (v *VTerm) Snapshot() []byte

Snapshot generates a reconnect payload: scrollback + grid + cursor restore. The output is valid ANSI that any terminal emulator can consume directly.

func (*VTerm) Write added in v0.58.0

func (v *VTerm) Write(p []byte) (int, error)

Write feeds PTY output to the emulator.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL