Symphony SSH Workers Architecture: How OpenAI Symphony Manages Remote Agents
Symphony SSH workers enable distributed agent execution across remote hosts via a four-layer architecture comprising configuration-driven host discovery, a dedicated SSH command wrapper, workspace-level remote execution, and intelligent orchestrator-based load balancing.
OpenAI's Symphony framework extends agent execution beyond local machines through its Symphony SSH workers architecture, which allows the orchestrator to dispatch tasks to remote hosts over SSH. This architecture decouples agent runtime from the orchestrator node, enabling scalable distributed workflows while maintaining strict control over concurrency through per-host capacity limits.
Configuration Schema for Remote Workers
Symphony discovers available remote workers through the worker section of its configuration schema, defined in elixir/lib/symphony_elixir/config/schema.ex. The system expects Config.settings!().worker.ssh_hosts to contain an array of host connection strings.
The configuration supports two key parameters:
ssh_hosts: An array of strings defining remote targets (e.g.,"ssh://[email protected]"or"localhost:2222"shorthand)max_concurrent_agents_per_host: Integer defining the upper limit of simultaneous agents allowed per host
Configure SSH workers in your settings file:
# config/config.exs or symfony.yaml equivalent
%{
worker: %{
ssh_hosts: ["[email protected]", "localhost:2222"],
max_concurrent_agents_per_host: 4
}
}
The SSH Command Wrapper
The SymphonyElixir.SSH module (elixir/lib/symphony_elixir/ssh.ex) serves as the low-level interface to the system SSH binary. It discovers the local ssh executable, parses host strings (handling both "host:port" shorthand and IPv6 bracket notation), and supports custom SSH configurations via the SYMPHONY_SSH_CONFIG environment variable.
Key functions include:
SSH.run/3: Executes commands remotely viaSystem.cmd/3and returns{:ok, {output, exit_status}}or error tuplesSSH.start_port/3: Opens a port for streaming output when continuous data transfer is requiredSSH.ssh_args/2: Builds the argument list including optional config file injectionSSH.parse_target/1: Normalizes host strings, extracting host and port componentsSSH.remote_shell_command/1: Wraps user commands for remote shell execution
Remote Workspace Execution
When agents require workspace operations on remote hosts, Workspace.run_remote_command/3 (lines 429-440 in elixir/lib/symphony_elixir/workspace.ex) coordinates the execution. This function receives the target worker_host and hook command, wraps the command as cd $WORKSPACE && $HOOK_CMD to ensure proper directory context, and invokes SSH.run/3 with timeout parameters derived from orchestrator settings.
This abstraction ensures that workspace creation, cleanup, and hook execution behave identically whether running locally or over SSH.
Orchestrator Load Balancing and Dispatch
The orchestrator (elixir/lib/symphony_elixir/orchestrator.ex) implements intelligent worker selection through select_worker_host/2 (lines 331-338). This function examines the ssh_hosts list from configuration, checks current agent counts against max_concurrent_agents_per_host, and returns a host string when capacity exists, or :no_worker_capacity when all hosts are saturated.
Upon selecting a host, spawn_issue_on_worker_host/5 (lines 693-698) initiates the remote workflow by spawning an AgentRunner with the worker_host parameter set. The runner subsequently invokes workspace functions that ultimately reach SSH.run/3.
Complete Execution Flow
The Symphony SSH workers architecture operates through four distinct phases:
- Startup Phase: The orchestrator loads
Config.settings!(). Non-emptyworker.ssh_hostsactivate remote worker mode. - Dispatch Phase:
Orchestrator.select_worker_host/2evaluates host capacity and selects a target, or returns:no_worker_capacityif all hosts are busy. - Execution Phase: The selected host passes to
AgentRunner, which callsWorkspace.run_remote_command/3, triggeringSSH.run/3to build the final command line (including optional-F $SYMPHONY_SSH_CONFIG) and execute via the system SSH binary. - Resolution Phase: Output and exit status return through the SSH wrapper to the workspace layer, which updates issue state accordingly.
Implementation Examples
Execute a command on a specific worker manually:
host = "[email protected]"
cmd = "git clone https://github.com/openai/example.git /tmp/example && make test"
case SymphonyElixir.SSH.run(host, cmd, stderr_to_stdout: true) do
{:ok, {output, 0}} ->
IO.puts("Success:\n#{output}")
{:ok, {output, code}} ->
IO.puts("Failed with exit #{code}:\n#{output}")
{:error, reason} ->
IO.puts("SSH error: #{inspect(reason)}")
end
Handle orchestrator dispatch logic:
# Within Orchestrator.dispatch_issue/4
case select_worker_host(state, preferred_worker_host) do
:no_worker_capacity ->
Logger.debug("No SSH slots available – falling back to local execution")
nil ->
# Local execution path when worker_host is nil
host ->
spawn_issue_on_worker_host(state, issue, attempt, recipient, host)
end
Summary
- Configuration-driven discovery: Symphony reads
Config.settings!().worker.ssh_hoststo identify available remote targets and enforcesmax_concurrent_agents_per_hostlimits. - Robust SSH abstraction: The
SymphonyElixir.SSHmodule handles host parsing, custom config injection viaSYMPHONY_SSH_CONFIG, and standardized command execution throughSSH.run/3. - Workspace transparency:
Workspace.run_remote_command/3enables seamless remote execution of workspace hooks with proper directory context and timeout handling. - Intelligent orchestration: The orchestrator balances load across SSH workers through
select_worker_host/2, returning:no_worker_capacitywhen hosts are saturated rather than overloading remote machines.
Frequently Asked Questions
How does Symphony handle SSH authentication for remote workers?
Symphony delegates SSH authentication to the underlying system SSH binary. You can specify identity files, keys, or other SSH options through the SYMPHONY_SSH_CONFIG environment variable, which the SymphonyElixir.SSH module injects as -F $SYMPHONY_SSH_CONFIG when building SSH commands.
What happens when all SSH workers are at capacity?
When Orchestrator.select_worker_host/2 determines that all configured hosts have reached their max_concurrent_agents_per_host limit, it returns :no_worker_capacity. The orchestrator can then fall back to local execution or queue the issue until a remote slot becomes available.
Can SSH workers use non-standard ports or IPv6 addresses?
Yes. The SSH.parse_target/1 function in elixir/lib/symphony_elixir/ssh.ex handles both "host:port" shorthand notation and IPv6 bracket notation (e.g., "[::1]:2222"), allowing flexible network configurations beyond standard port 22 SSH connections.
Where does the command execution context get set for remote hooks?
The Workspace.run_remote_command/3 function (lines 429-440 in elixir/lib/symphony_elixir/workspace.ex) automatically wraps commands with cd $WORKSPACE && $HOOK_CMD, ensuring that remote hooks execute within the correct workspace directory regardless of the SSH user's default shell location.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →