AI Code Execution

This guide shows how to use Firecase as a secure code execution backend for AI agents. The pattern: your agent generates code, Firecase runs it in an isolated sandbox, and returns the output.

Why Firecase for AI Agents?

AI agents that generate and execute code need:

  • Isolation — Untrusted code must not access your infrastructure
  • Speed — Sub-second sandbox creation for interactive workflows
  • Persistence — Maintain state across multiple execution rounds
  • Forking — Branch from a known state for parallel exploration

Firecase provides all of these. Each sandbox is a Firecracker microVM with hardware-level isolation, ~150ms boot time, persistent disk state, and instant copy-on-write forking.

Basic Pattern: Execute and Return

The simplest integration: create a sandbox, execute the agent's code, return the result.

Python
import requests

API_KEY = "fc_live_..."
BASE = "https://api.firecase.ai"
headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

# Create a persistent sandbox for this conversation
instance = requests.post(f"{BASE}/instances", headers=headers,
    json={"name": f"agent-session-{session_id}"}).json()

# Start the VM
requests.post(f"{BASE}/instances/{instance['id']}/vm", headers=headers)

# Agent generates code → execute it
def run_code(instance_id: str, code: str, language: str = "python3") -> dict:
    if language == "python3":
        command = ["python3", "-c", code]
    elif language == "bash":
        command = ["bash", "-c", code]
    elif language == "node":
        command = ["node", "-e", code]
    else:
        raise ValueError(f"Unsupported language: {language}")

    result = requests.post(
        f"{BASE}/instances/{instance_id}/exec",
        headers=headers,
        json={"command": command, "timeout_ms": 30000}
    ).json()

    return {
        "exit_code": result["exit_code"],
        "stdout": result["stdout"],
        "stderr": result["stderr"],
    }

# Use it
output = run_code(instance["id"], "print(2 + 2)")
# {"exit_code": 0, "stdout": "4\n", "stderr": ""}

Stateful Sessions

Since instances persist disk state, the agent can install packages, write files, and build up an environment across multiple rounds:

Python
# Round 1: Install dependencies
run_code(instance_id, "pip install pandas matplotlib", language="bash")

# Round 2: Write a data analysis script
requests.post(f"{BASE}/instances/{instance_id}/files", headers=headers,
    json={
        "path": "/home/user/analyze.py",
        "content": """
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

df = pd.DataFrame({'x': range(10), 'y': [i**2 for i in range(10)]})
df.to_csv('/home/user/results.csv', index=False)
plt.plot(df['x'], df['y'])
plt.savefig('/home/user/plot.png')
print(f"Saved {len(df)} rows to results.csv")
"""
    })

# Round 3: Execute the script
result = run_code(instance_id, "python3 /home/user/analyze.py", language="bash")

# Round 4: Read the results
csv = requests.get(
    f"{BASE}/instances/{instance_id}/files",
    headers=headers,
    params={"path": "/home/user/results.csv"}
).json()

Checkpoint and Branch

Use checkpoints to create "save points" that the agent can branch from:

Python
# After setting up the environment, checkpoint
checkpoint = requests.post(
    f"{BASE}/instances/{instance_id}/checkpoints",
    headers=headers,
    json={"label": "env-ready"}
).json()

# Try approach A
result_a = run_code(instance_id, approach_a_code)

if result_a["exit_code"] != 0:
    # Approach A failed — rollback and try approach B
    requests.delete(f"{BASE}/instances/{instance_id}/vm", headers=headers)
    requests.post(f"{BASE}/instances/{instance_id}/rollback", headers=headers,
        json={"checkpoint_id": checkpoint["id"]})
    requests.post(f"{BASE}/instances/{instance_id}/vm", headers=headers)

    result_b = run_code(instance_id, approach_b_code)

Parallel Exploration with Forks

Fork the sandbox to explore multiple approaches simultaneously:

Python
import asyncio

# Checkpoint the current state
checkpoint = requests.post(
    f"{BASE}/instances/{instance_id}/checkpoints",
    headers=headers,
    json={"label": "pre-exploration"}
).json()

# Fork 5 instances for parallel exploration
async def explore(approach_code: str, fork_name: str):
    fork = requests.post(
        f"{BASE}/instances/{instance_id}/fork",
        headers=headers,
        json={"name": fork_name, "checkpoint_id": checkpoint["id"]}
    ).json()

    requests.post(f"{BASE}/instances/{fork['id']}/vm", headers=headers)
    result = run_code(fork["id"], approach_code)

    # Clean up
    requests.delete(f"{BASE}/instances/{fork['id']}/vm", headers=headers)
    requests.delete(f"{BASE}/instances/{fork['id']}", headers=headers)

    return result

results = await asyncio.gather(*[
    explore(code, f"explore-{i}")
    for i, code in enumerate(candidate_solutions)
])

# Pick the best result
best = min(results, key=lambda r: r["exit_code"])

Safety Patterns

Timeout Protection

Always set timeouts to prevent runaway processes:

Python
result = requests.post(
    f"{BASE}/instances/{instance_id}/exec",
    headers=headers,
    json={
        "command": ["python3", "-c", agent_code],
        "timeout_ms": 30000  # 30 second limit
    }
).json()

Resource Limits via Profiles

Create a restrictive profile for agent sandboxes:

Python
profile = requests.post(f"{BASE}/profiles", headers=headers, json={
    "name": "ai-agent-sandbox",
    "default_vcpus": 2,
    "default_memory_mib": 1024,
    "max_vcpus": 2,
    "max_memory_mib": 2048,
    "max_storage_bytes": 2147483648,  # 2 GiB
    "idle_timeout_seconds": 600,      # Auto-stop after 10 min idle
    "network_egress_policy": "deny",  # No internet access
    "max_checkpoints": 5,
    "auto_delete_on_stop": True       # Clean up automatically
}).json()

Network Isolation

For untrusted code, use "network_egress_policy": "deny" to completely block internet access from the sandbox.

Cleanup

Always clean up sandboxes when done:

Python
# Stop the VM
requests.delete(f"{BASE}/instances/{instance_id}/vm", headers=headers)

# Delete the instance (optional — keeps disk state if you want to resume later)
requests.delete(f"{BASE}/instances/{instance_id}", headers=headers)

Next Steps