Sandbox Viz
An interactive walkthrough of the multi-layered safety pipeline every agent tool call passes through before any code runs. Four built-in scenarios demonstrate the four distinct paths through the system: a read_file that auto-approves and runs in a process sandbox, a write_file('.env', …) that hard-blocks at the approval gate, an edit_file that waits for human review and then runs in gVisor, and a search_web whose result is sanitised post-execution by a quarantine model.
Each press of Step advances the active scenario one stage. The active card scales gently, its pill recolours by tier, and the narration line beneath updates — so the viewer can see the pipeline's structure without reading any source code.
Every tool call flows through a multi-layered safety pipeline before anything executes. Pick a scenario, then step through the pipeline.
Installation
npx shadcn@latest add https://craftbits.dev/r/sandbox-viz.jsonUsage
import { SandboxViz } from "@craft-bits/viz/sandbox-viz";
<SandboxViz />Drive it externally (controlled scenario):
<SandboxViz
scenarioId={scenarioId}
onScenarioChange={setScenarioId}
onComplete={({ finalStage, approvalOutcome }) => {
/* lift the final stage into an external chart / analytics */
}}
/>Replace the default scenarios with your own:
<SandboxViz
scenarios={[
{
id: "ssh-key",
label: "SSH key read",
toolCall: { tool: "read_file", args: { path: "~/.ssh/id_rsa" } },
riskTier: "block",
riskReason: "Read from a known credential path",
approvalOutcome: "blocked",
sandboxTier: "process",
executionResult: "",
resultText: "BLOCKED — credential path",
finalStage: "approval",
},
]}
/>Understanding the component
- Stage column. Six numbered cards stack vertically with down-arrow connectors between them: incoming call, risk classifier, approval gate, sandbox, execution, result.
- Reach state. Each card lights up only after the active step reaches it. Cards before that float at 0.25 opacity; the active card scales briefly from 0.96 to 1.01.
- Tier ladder. The classifier card renders three tier rows (
auto / review / block); the row matching the scenario'sriskTierscales up and recolours, and a 300ms shimmer sweeps across the card when it activates. - Sandbox tiers. The sandbox card renders three concentric layers (
microvm / gvisor / process); the layer matching the scenario'ssandboxTier(and every layer below) light up with a small per-row stagger. - Approval gate. The gate shows one of three states (
approved/waiting/blocked). Thewaitingstate holds for ≈1.5s before resolving toapproved, simulating the human round-trip. - Final stage. For
blockedscenarios the pipeline terminates at the approval gate. For all other scenarios it ends at the result card, and a chip strip lists every stage the run activated. - Reduced motion. Under
prefers-reduced-motion: reduce, every entrance, scale cycle, and shimmer disables, and the approval-wait timer collapses to 200ms.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
scenarios | readonly SandboxVizScenario[] | 4 built-ins | Override the scenario set. |
scenarioId | string | — | Controlled scenario id. Pair with onScenarioChange. |
defaultScenarioId | string | first scenario | Uncontrolled initial scenario id. |
onScenarioChange | (id: string) => void | — | Fires when the viewer picks a different scenario. |
onComplete | (summary) => void | — | Fires when the pipeline reaches its final stage. |
transition | Transition | SPRINGS.snap | Override card entrance / pill colour transition. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The narration paragraph lives in a polite live region (
aria-live="polite",aria-atomic) so screen-reader users hear the stage explanation update as the run advances. - The scenario selector is a labelled
role="group"; each button reports its selection viaaria-pressed. - Every interactive control meets the 36px hit-target minimum and exposes a visible
focus-visiblering. - Decorative SVGs (chevrons, icons, sandbox-tier rectangles) are marked
aria-hidden. - Colour is paired with text labels at every signal — tier name, approval outcome, and result line are all readable without colour.
- Motion respects
prefers-reduced-motion: reduce— every entrance, scale cycle, and shimmer disables, and the approval-wait timer shortens to 200ms.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/systems/SandboxViz.tsx). The source was a lesson component that bundled aWidget/ModeStripchrome, aChallengeBtn+ScoreDotspredict-and-challenge quiz, undo/redo history viauseWidgetHistory, and the lesson's narration framing. The viz extract keeps only the interactive Explore pipeline — the multiple-choice quizzes were curriculum-specific and live in the lesson source. Per-track palette tokens (--color-fail-400,--color-success-400,--color-warn-400,--color-accent-400,--color-ink-*) are remapped tovar(--cb-error)/var(--cb-success)/var(--cb-warning)/var(--cb-accent)/var(--cb-fg-*)so consumer themes repaint freely, and inlineSPRINGS.snappy/SPRINGS.gentleare re-keyed to the canonicalSPRINGS.snap/SPRINGS.smoothfrom@craft-bits/core/motion. The infinite shimmer loop on the classifier was tightened to a single 300ms sweep —craft-bits/duration-max-300msrules out the longer 600ms pulse.