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.

LayerQuestion it answersDocs
Tool scopingWhich tools can the model call?Tools
Permissions / HITLWhether a given call runsPermissions
Execution sandboxWhat an allowed, approved call can actually touchSandbox
Guarded delegationWhether a child receives the delegated input before any of its tools executeSubagents

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.

src/app/ops/index.ts
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:

dawn.config.ts
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
})
src/app/ops/index.ts
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." },
    },
  },
})