Flue: define a sandboxed headless agent and deploy it anywhere
Author a Flue agent manifest that runs each agent in a sandbox instead of a dedicated container, keeping infra costs flat as task volume grows, and validate the config before you deploy.
Run this workflow
CI-verified, 2/2 fixtures passing.
Build this with your agent
One copy-paste hands Claude Code, Codex, or Cursor the full recipe, steps included, nothing to fetch.
Intended Use
Anyone running headless agents at a volume where per-container cost adds up. CI validates the Flue agent manifest: a name is set, at least one tool is listed, sandbox config exists with a valid mode, and a deploy target is specified. The actual npm install, sandbox execution, and deploy are fenced.
Not for
- Low-volume one-off agent runs where a container is perfectly affordable and simpler
- Production load-bearing workloads before pinning a stable version, the API is actively evolving
The Stack
Tested Against
withastro/flue (2026-06)ruby@3.x (YAML stdlib)Side effects & data flow
- Network
- none, local only
- Writes
- ./flue-agent.yaml
- Credentials
- none required
Prerequisites
- Node.js 18+
- npm install @flue/runtime @flue/cli (to actually run)
Steps
- 1
Author the agent manifest and validate it
Write flue-agent.yaml: a name, a list of tools the agent can call, a sandbox block with a mode (virtual, local, or remote), and a deploy target. CI checks the required fields; the npm install and agent run are fenced.
cat > flue-agent.yaml <<'YAML' name: code-reviewer tools: - read_file - run_tests - post_comment sandbox: mode: virtual deploy: target: cloudflare-workers YAML ruby -ryaml -e ' c = YAML.safe_load(File.read("flue-agent.yaml")) || {} abort "BAD: name is required" if c["name"].to_s.empty? tools = c["tools"] abort "BAD: tools must be a non-empty list" unless tools.is_a?(Array) && !tools.empty? sandbox = c["sandbox"] || {} allowed = ["virtual", "local", "remote"] abort "BAD: sandbox.mode must be one of: " + allowed.join(", ") unless allowed.include?(sandbox["mode"].to_s) abort "BAD: deploy.target is required" if (c["deploy"] || {})["target"].to_s.empty? puts "manifest OK: agent " + c["name"].inspect + ", " + tools.length.to_s + " tool(s), sandbox mode " + sandbox["mode"].inspect + ", deploy -> " + c["deploy"]["target"].inspect ' - 2
Install, run, and deploy (fenced)
Install the Flue packages (npm install @flue/runtime @flue/cli), implement the tools listed in the manifest, then deploy with the Flue CLI. Pin the version in package.json before building anything load-bearing. The install, sandbox execution, and deploy are fenced.
Eval, 2 fixtures
Last passed: verified todaymanifest-okcontainstimeout 30s · max $0Expected:
manifest OK: agentclean-exitexit_codetimeout 30s · max $0Expected:
0
Results
A full container per agent is the default but expensive pattern at scale. Flue (from the Astro team, ~6.6k stars) lets each agent run in a configurable sandbox (virtual, local, or remote container) on Node.js, Cloudflare Workers, GitHub Actions, GitLab CI, Daytona, or Render. The infra saving is real only when volume justifies it; for a handful of runs, a container is fine. The API is actively developed with no explicit stability guarantee, so pin your version.
Did this work for you?
Our CI checks the setup runs. You tell us if the whole thing worked. Tell us straight.
Related workflows
- Build the Fugu pattern in the open: fan out, assign roles, verify
- Sakana Fugu: A/B it on your own task before you migrate
- Run GLM-5.2 fully local on a Mac Studio and drive it with Hermes
- Eve: make evals the deploy gate, not a vibe check
- Eve: gate the dangerous tool behind a human, in one field
- OrcaRouter: only fan out when it is worth it
Liked this workflow?
Get new verified workflows in WebAfterAI, three issues a week (Tue, Thu, Sat).