Access Control
Dawn has four independent decision planes. They compose — none substitutes for another, and an app may use the planes relevant to each route.
| Layer | Question it answers | Docs |
|---|---|---|
| Tool scoping | Which tools can the model call? | Tools |
| Permissions / HITL | Whether a given call runs | Permissions |
| Execution sandbox | What an allowed, approved call can actually touch | Sandbox |
| Guarded delegation | Whether a child receives the delegated input before any of its tools execute | Subagents |
Tool scoping — which tools
agent({ tools: { allow, deny, approve, constrain } }) controls the surface offered to the model. deny revokes a tool so it's never wired into the generated entry; allow grants back a capability tool withheld from a subagent, such as readFile or runBash; deny wins when a name appears in both. This is enforced at composition time — a denied tool is never wired in, so the model has no way to call it, valid or not.
export default agent({ model: "gpt-5", systemPrompt: "…", tools: { deny: ["runBash"] } })Scoping controls the surface, not what a granted tool does once invoked — a granted writeFile can still write anywhere its implementation permits. See Tools.
Permissions — whether a call runs
Two gates are on by default for the built-in workspace tools: runBash commands are matched against allow/deny patterns, and filesystem paths outside workspace/ are permission-gated. An unmatched ("unknown") call pauses the run and asks a human, unless permissions.mode is set to non-interactive (fail-closed) or bypass (dev/test only).
Two more gates ride the same interrupt machinery: tools: { approve: [...] } requires human approval before a named tool call, and memory: { writes: "ask" } gates belief-contradiction memory writes. Use tools.approve for a separately authored tool such as deployProd; workspace command and path tools already use their pattern-aware gates. See Permissions for the interrupt payloads and resume flow.
Execution sandbox — what a call can touch
Even a tool that's allowed and approved still runs somewhere. By default that's the local workspace/ directory on the host. Adding a sandbox key to dawn.config.ts routes every readFile, writeFile, listDir, and runBash call for a thread into an isolated environment instead — filesystem, shell, and (optionally) network. Dawn ships a Docker reference provider and a Kubernetes provider behind the same SandboxProvider contract. See Execution Sandbox for the provider-neutral boundary.
Guarded delegation — whether a child receives input
A parent's delegation policy runs at the final dispatch boundary, before the child starts. Its allow, deny, approve, or constraint decision determines whether the child receives the delegated input at all; only after that decision allows dispatch can the child's own tool policies and permission gates run. See Subagents.
How they compose
A route that uses pattern-gated shell commands in an isolated environment, separately approves an authored deployment tool, and reviews delegated input stacks all four planes:
import { config } from "@dawn-ai/cli"
import { dockerSandbox } from "@dawn-ai/sandbox"
export default config({
permissions: {
allow: { bash: ["ls", "cat"] },
deny: { bash: ["rm -rf", "sudo"] },
},
sandbox: { provider: dockerSandbox({ image: "node:24-slim" }) }, // what runBash can touch
})import { agent } from "@dawn-ai/sdk"
import researcher from "./subagents/researcher/index.js"
export default agent({
model: "gpt-5",
systemPrompt: "…",
tools: { deny: ["writeFile"], approve: ["deployProd"] },
subagents: { researcher },
delegation: {
rules: {
researcher: { action: "approve", reason: "Review delegated customer context." },
},
},
})