Overview
Workspace isolation is the most critical safety mechanism in Symphony. Every coding agent runs in a dedicated per-issue directory, preventing unintended access to the source repository or other workspaces.Workspace Path Structure
Directory Layout
<system-temp>/symphony_workspaces
Path Construction
elixir/lib/symphony_elixir/workspace.ex:111-113
Identifier Sanitization
Safety Rules
elixir/lib/symphony_elixir/workspace.ex:115-117
Sanitization Examples
Path traversal attempts like
../ become ___/, which is then joined with workspace root to produce a safe path inside the workspace.Path Validation
Three-Layer Safety Check
Before using any workspace path, Symphony validates:Layer 1: Prefix Check
Layer 1: Prefix Check
- Normalize both paths to absolute
- Reject if workspace equals root
- Reject if workspace not under root prefix
elixir/lib/symphony_elixir/workspace.ex:213-228Layer 2: Symlink Escape Prevention
Layer 2: Symlink Escape Prevention
- Walk each path component from root to workspace
- Use
File.lstat/1(does not follow symlinks) - Reject if any component is a symlink
elixir/lib/symphony_elixir/workspace.ex:230-256Layer 3: Codex cwd Validation
Layer 3: Codex cwd Validation
- Validate before launching Codex subprocess
- Ensure cwd is inside workspace root
- Prevent running agent in source repo or system directories
elixir/lib/symphony_elixir/codex/app_server.ex:144-160Attack Resistance
Path Traversal
../../../etc sanitizes to ___________etc, still under workspace rootSymlink Escape
/tmp/workspaces/link -> /etc detected by lstat check, rejectedAbsolute Paths
/etc/passwd as identifier becomes _etc_passwd, safe relative pathNull Bytes
Regex replacement handles null bytes like any other unsafe character
Workspace Lifecycle
Creation Flow
elixir/lib/symphony_elixir/workspace.ex:32-51
Workspace Reuse
Workspaces are preserved across runs for the same issue until the issue reaches a terminal state.
- Incremental progress: Agent can build on previous work
- Faster iteration: No need to re-clone repository or re-install dependencies
- Debugging: Operators can inspect workspace state between runs
- Disk usage: Workspaces accumulate over time
- State pollution: Previous run artifacts may affect future runs
before_run hook to reset workspace state if needed.
Lifecycle Hooks
Hooks are shell scripts executed in the workspace directory with configurable timeout.Hook Configuration
Hook Execution
elixir/lib/symphony_elixir/workspace.ex:166-187
Hook Failure Semantics
after_create
Fatal: Workspace creation aborted, agent run fails
before_run
Fatal: Current run attempt aborted, worker exits with error
after_run
Logged and ignored: Run result not affected
before_remove
Logged and ignored: Workspace deletion proceeds anyway
Hook Environment
Shell Context
Shell Context
- Shell:
sh -lc <script>(login shell, loads user profile) - Working directory: Workspace path (e.g.
/tmp/symphony_workspaces/ABC-123) - Standard streams:
stderr_to_stdout: true(combined output) - Timeout:
hooks.timeout_ms(default: 60 seconds)
Available Data
Available Data
Hooks don’t receive environment variables automatically. Access issue data via:
- Workspace path:
pwd→/tmp/symphony_workspaces/ABC-123 - Issue identifier: Parse from workspace directory name
- WORKFLOW.md: Can embed issue context in hook script
Common Hook Patterns
Git Repository Clone
Dependency Management
Resource Provisioning
Cleanup Policies
Automatic Cleanup Triggers
Terminal State Transition
Issue moves to
Closed, Done, Cancelled, etc. during reconciliationStartup Terminal Cleanup
On service start, clean workspaces for issues already in terminal states
Terminal State Cleanup
During reconciliation, if an issue moves to a terminal state:elixir/lib/symphony_elixir/orchestrator.ex:303-306
Cleanup sequence:
Startup Cleanup
Before the first poll tick:elixir/lib/symphony_elixir/orchestrator.ex:776-791
Startup cleanup prevents workspace accumulation across service restarts. Workspaces for issues already marked “Done” are removed before polling begins.
Manual Cleanup
Operators can manually clean workspaces:Safety Invariants
Critical Rules
Invariant 1: Run agent only in workspace path
Invariant 1: Run agent only in workspace path
- Source repository directory
- System directories (
/,/etc,/usr, etc.) - User home directory
- Parent of workspace root
Invariant 2: Workspace path inside workspace root
Invariant 2: Workspace path inside workspace root
Invariant 3: Workspace key is sanitized
Invariant 3: Workspace key is sanitized
Enforcement Points
These invariants are enforced at:- Workspace creation:
validate_workspace_path/1beforemkdir - Codex launch:
validate_workspace_cwd/1beforePort.open/2 - Workspace removal:
validate_workspace_path/1beforerm_rf/1
Observability
Workspace State Inspection
Workspace Logs
Key log messages:Troubleshooting
Common Issues
Hook timeout
Hook timeout
Symptom:
Workspace hook timed out hook=after_create timeout_ms=60000Causes:- Slow network (git clone, npm install)
- Large repository
- Expensive build step
- Increase
hooks.timeout_msinWORKFLOW.md - Optimize hook script (use
--depth=1for git clone) - Cache dependencies outside workspace
Workspace outside root
Workspace outside root
Symptom:
{:error, {:workspace_outside_root, "/tmp/other/path", "/tmp/symphony_workspaces"}}Causes:- Incorrect
workspace.rootconfiguration - Symlink in path
- Path traversal attempt
- Check
workspace.rootinWORKFLOW.md - Use absolute paths, not relative
- Ensure no symlinks in workspace root path
Workspace creation failed
Workspace creation failed
Symptom:
Workspace creation failed issue_id=abc123 error=...Causes:- Permission denied
- Disk full
- Hook failure
- Check filesystem permissions on workspace root
- Verify disk space:
df -h /tmp - Review hook logs for specific error
Debug Mode
Enable verbose workspace logging:- Path validation steps
- Symlink checks
- Hook stdout/stderr
- Workspace creation/deletion
Security Considerations
Threat Model
Malicious Issue Identifiers
Path traversal attempts in issue identifier are sanitized before use
Symlink Escapes
Each path component checked with
lstat to detect symlink escapesArbitrary Code Execution
Hooks run in workspace context, not system-wide. Use
codex.approval_policy to control agent permissionsResource Exhaustion
No built-in disk quota enforcement. Use OS-level quotas or monitoring
Best Practices
Defense in depth: Multiple safety layers protect against workspace isolation failures.
- Use absolute paths: Configure
workspace.rootwith absolute path - Monitor disk usage: Set up alerts for workspace root disk consumption
- Limit hook complexity: Keep hooks simple, move complex logic to agent tools
- Audit workspace access: Review which processes can write to workspace root
- Use read-only mounts: If possible, mount source repo read-only in workspace
Next Steps
Configuration Reference
Complete
WORKFLOW.md schema and hook examplesComponent Reference
Implementation details for Workspace Manager
Workflow Lifecycle
How workspaces fit into the overall execution flow
Troubleshooting
Common workspace issues and solutions