Native Execution Mode (No Docker/Isolation)¶
Overview¶
Symbiont supports running agents without Docker or container isolation for development environments or trusted deployments where maximum performance and minimal dependencies are desired.
⚠️ Security Warnings¶
IMPORTANT: Native execution mode bypasses all container-based security controls:
- ❌ No process isolation
- ❌ No filesystem isolation
- ❌ No network isolation
- ❌ No resource limits enforcement
- ❌ Direct access to host system
The
native-sandboxfeature will not compile in release builds. It is guarded by acompile_error!undernot(debug_assertions), so a release binary can never ship the native runner. It is a debug-only development aid.
USE ONLY FOR: - Local development with trusted code - Controlled environments with trusted agents - Testing and debugging - Environments where Docker is not available
DO NOT USE FOR: - Production environments with untrusted code - Multi-tenant deployments - Public-facing services - Processing untrusted user input
Architecture¶
Sandbox Tier Hierarchy¶
┌─────────────────────────────────────────┐
│ SecurityTier::None (Native Execution) │ ← No isolation
├─────────────────────────────────────────┤
│ SecurityTier::Tier1 (Docker) │ ← Container isolation
├─────────────────────────────────────────┤
│ SecurityTier::Tier2 (gVisor) │ ← Enhanced isolation
├─────────────────────────────────────────┤
│ SecurityTier::Tier3 (Firecracker) │ ← Maximum isolation
└─────────────────────────────────────────┘
Native Execution Flow¶
graph LR
A[Agent Request] --> B{Security Tier?}
B -->|None| C[Native Process Runner]
B -->|Tier1+| D[Sandbox Orchestrator]
C --> E[Direct Process Execution]
E --> F[Host System]
D --> G[Docker/gVisor/Firecracker]
G --> H[Isolated Environment]
Configuration¶
Option 1: TOML Configuration¶
# symbiont.toml
[security]
# Allow native execution (default: false)
allow_native_execution = true
# Native execution is its own top-level section (not nested under [security]).
[native_execution]
enabled = true
default_executable = "python3"
working_directory = "/tmp/symbiont-native"
# Apply OS resource limits even in native mode
enforce_resource_limits = true
max_memory_mb = 2048 # Option<u64>
max_cpu_seconds = 300 # Option<u64> — CPU time, not core count
max_execution_time_seconds = 300 # wall-clock timeout
allowed_executables = ["python3", "node", "bash"]
Complete Config Example¶
A full config.toml with native execution alongside other system settings:
# config.toml
[api]
port = 8080
host = "127.0.0.1"
timeout_seconds = 30
max_body_size = 10485760
[database]
# Embedding vector dimension. LanceDB (the default embedded backend) needs no
# further config. The backend is chosen at build time via the `vector-lancedb`
# (default) or `vector-qdrant` Cargo feature — there is no `vector_backend`
# config key; use the SYMBIONT_VECTOR_BACKEND env var to switch at runtime.
vector_dimension = 384
# Used when running with the Qdrant backend (SYMBIONT_VECTOR_BACKEND=qdrant):
# qdrant_url = "http://localhost:6333"
# qdrant_collection = "symbiont"
[logging]
level = "info"
format = "Pretty"
structured = true
[security]
key_provider = { Environment = { var_name = "SYMBIONT_KEY" } }
enable_compression = true
enable_backups = true
enable_safety_checks = true
[storage]
context_path = "./data/context"
git_clone_path = "./data/git"
backup_path = "./data/backups"
max_context_size_mb = 1024
[native_execution]
enabled = true
default_executable = "python3"
working_directory = "/tmp/symbiont-native"
enforce_resource_limits = true
max_memory_mb = 2048
max_cpu_seconds = 300
max_execution_time_seconds = 300
allowed_executables = ["python3", "python", "node", "bash", "sh"]
NativeExecutionConfig Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable native execution mode |
default_executable |
string | "bash" |
Default interpreter/shell |
working_directory |
path | /tmp/symbiont-native |
Execution directory |
enforce_resource_limits |
bool | true |
Apply OS-level limits |
max_memory_mb |
Option |
Some(2048) |
Memory limit in MB |
max_cpu_seconds |
Option |
Some(300) |
CPU time limit |
max_execution_time_seconds |
u64 | 300 |
Wall-clock timeout |
allowed_executables |
Vec |
[bash, python3, etc.] |
Executable whitelist |
Option 2: Runtime safety guards (environment)¶
There are no SYMBIONT_NATIVE_* / SYMBIONT_ALLOW_NATIVE_EXECUTION /
SYMBIONT_DEFAULT_SANDBOX_TIER settings — native execution is configured through
the [native_execution] config section above. The only native-related
environment variables are the two runtime safety guards, both of which must be
set to actually run the native (zero-isolation) runner:
export SYMBI_UNSAFE_NATIVE_SANDBOX=1 # acknowledge the native runner
export SYMBIONT_ALLOW_UNISOLATED=1 # permit SandboxTier::None
Option 3: Agent-Level Configuration¶
metadata {
version = "1.0.0"
description = "Local Development Agent"
}
agent native_worker(task: String) -> String {
capabilities = ["local_filesystem", "network"]
policy dev_only {
allow: ["local_filesystem", "network"] if true
}
# tier 0 = no sandbox (host execution); requires the opt-ins above
with sandbox = "none" {
return process(task);
}
}
Usage Examples¶
Example 1: Development Mode¶
use symbi_runtime::{Config, SecurityTier, SandboxOrchestrator};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Enable native execution for development
let mut config = Config::default();
config.security.allow_native_execution = true;
let orchestrator = SandboxOrchestrator::new(config)?;
// Execute code natively
let result = orchestrator.execute_code(
SecurityTier::None,
"print('Hello from native execution!')",
HashMap::new()
).await?;
println!("Output: {}", result.stdout);
Ok(())
}
Example 2: Building and running with the native runner¶
There is no --native CLI flag. Native (host) execution requires three explicit opt-ins:
- Build with the
native-sandboxfeature — debug builds only. The feature provides zero isolation and is guarded by acompile_error!in release builds:
- Acknowledge both runtime guards:
export SYMBI_UNSAFE_NATIVE_SANDBOX=1 # acknowledge the native runner
export SYMBIONT_ALLOW_UNISOLATED=1 # permit SandboxTier::None in non-dev runs
- Select tier 0 (no sandbox) in the agent DSL:
Resource limits (memory/CPU/timeout) come from the with block / config (see above), not CLI flags.
Then run normally:
Example 3: Mixed Execution¶
// Use native execution for trusted local operations
let local_result = orchestrator.execute_code(
SecurityTier::None,
local_code,
env_vars
).await?;
// Use Docker for external/untrusted operations
let isolated_result = orchestrator.execute_code(
SecurityTier::Tier1,
untrusted_code,
env_vars
).await?;
Implementation Details¶
Native Process Runner¶
The native runner uses std::process::Command with optional resource limits:
pub struct NativeRunner {
config: NativeConfig,
}
impl NativeRunner {
pub async fn execute(&self, code: &str, env: HashMap<String, String>)
-> Result<ExecutionResult> {
// Direct process execution
let mut command = Command::new(&self.config.executable);
command.current_dir(&self.config.working_dir);
command.envs(env);
// Optional: Apply resource limits via rlimit (Unix)
#[cfg(unix)]
if self.config.enforce_limits {
self.apply_resource_limits(&mut command)?;
}
let output = command.output().await?;
Ok(ExecutionResult {
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
exit_code: output.status.code().unwrap_or(-1),
success: output.status.success(),
})
}
}
Resource Limits (Unix)¶
On Unix systems, native execution can still enforce some limits:
- Memory: Using
setrlimit(RLIMIT_AS) - CPU Time: Using
setrlimit(RLIMIT_CPU) - Process Count: Using
setrlimit(RLIMIT_NPROC) - File Size: Using
setrlimit(RLIMIT_FSIZE)
Platform Support¶
| Platform | Native Execution | Resource Limits |
|---|---|---|
| Linux | ✅ Full | ✅ rlimit |
| macOS | ✅ Full | ⚠️ Partial |
| Windows | ✅ Full | ❌ Limited |
Migration from Docker¶
Step 1: Update Configuration¶
Then select tier 0 (no sandbox) per agent in the DSL:
Step 2: Build and run (debug only)¶
# No longer required
# docker build -t symbi:latest .
# docker run ...
# The native-sandbox feature is debug-only (compile_error! in release builds):
cargo build --features native-sandbox
SYMBI_UNSAFE_NATIVE_SANDBOX=1 SYMBIONT_ALLOW_UNISOLATED=1 \
./target/debug/symbi run agent.symbi
Hybrid Approach¶
Use both execution modes strategically — native for trusted local operations, Docker for untrusted code:
// Trusted local operations
let local_result = orchestrator.execute_code(
SecurityTier::None, // Native
trusted_code,
env
).await?;
// External/untrusted operations
let isolated_result = orchestrator.execute_code(
SecurityTier::Tier1, // Docker
external_code,
env
).await?;
Step 3: Handle Environment Variables¶
Docker automatically isolated environment variables. With native execution, set them explicitly:
export AGENT_API_KEY="xxx"
export AGENT_DB_URL="postgresql://..."
export SYMBI_UNSAFE_NATIVE_SANDBOX=1
export SYMBIONT_ALLOW_UNISOLATED=1
symbi run agent.symbi # agent must declare: with sandbox = "none" { ... }
Performance Comparison¶
| Mode | Startup | Throughput | Memory | Isolation |
|---|---|---|---|---|
| Native | ~10ms | 100% | Minimal | None |
| Docker | ~500ms | ~95% | +128MB | Good |
| gVisor | ~800ms | ~70% | +256MB | Better |
| Firecracker | ~125ms | ~90% | +64MB | Best |
Troubleshooting¶
Issue: Permission Denied¶
# Solution: Ensure working directory is writable
mkdir -p /tmp/symbiont-native
chmod 755 /tmp/symbiont-native
Issue: Command Not Found¶
# Solution: Ensure executable is in PATH or use absolute path
export PATH=$PATH:/usr/local/bin
# Or configure absolute path
allowed_executables = ["/usr/bin/python3", "/usr/bin/node"]
Issue: Resource Limits Not Applied¶
Native execution on Windows has limited resource limit support. Consider: - Using Job Objects (Windows-specific) - Monitoring and terminating runaway processes - Upgrading to container-based execution
Best Practices¶
- Development Only: Use native execution primarily for development
- Gradual Migration: Start with containers, drop to native when stable
- Monitoring: Even without isolation, monitor resource usage
- Allowlists: Restrict allowed executables and paths
- Logging: Enable comprehensive audit logging
- Testing: Test with containers before deploying native
Security Checklist¶
Before enabling native execution in any environment:
- All agent code is from trusted sources
- Environment is isolated from production
- No external user input is processed
- Monitoring and logging are enabled
- Resource limits are configured
- Executable allowlist is restrictive
- Filesystem access is limited
- Team understands security implications
Related Documentation¶
- Security Model - Full security architecture
- Sandbox Architecture - Container tiers
- Configuration Guide - Setup options
- DSL Security Directives - Agent-level security
Remember: Native execution trades security for convenience. Always understand the risks and apply appropriate controls for your deployment environment.