Org policy reference (dvalin.policy.json)
DvalinCode bounds the agent with an org policy — a JSON file that IT or platform teams author and that the tool enforces at a single chokepoint before any tool side effect runs. The resolved policy is what dvalincode trust prints, what the audit log hashes at run_start, and what the Approval Pack snapshots.
Schema source: src/core/policy.ts (orgPolicySchema).
Three layers, one effective policy
| Layer | Default path | Who writes it |
|---|---|---|
| Machine | ~/.dvalincode/policy.json (override: DVALINCODE_POLICY_FILE) | IT / MDM |
| Repo | <workspace>/dvalin.policy.json | Team (committed) |
| Runtime | DVALINCODE_RUNTIME_POLICY_FILE (optional) | Evaluation harness / controlled launcher |
All present layers are read and merged by narrowing (intersection) — repo and runtime policies can only make the effective machine policy stricter, never wider. The runtime layer is intended for ephemeral constraints such as a benchmark's source-only rule; it is not discovered unless the environment variable is set. Order of discovery does not matter for safety.
| Field kind | How all present layers combine |
|---|---|
Allowlists (modes, providers.allow, models.allow, commands.allow, paths.allow, mcp.allow) | Intersection — only values permitted by every layer survive |
Denylists (commands.deny, paths.deny, tools.deny) | Union — a deny in any layer blocks |
commands.defaultDeny | OR — if any layer sets it, default-deny is on |
network | Most restrictive wins — off < endpoint-only < on |
maxToolCalls | Minimum — the smallest cap across layers applies |
unattended.maxPermissionMode | Most restrictive wins — plan < auto < bypass |
unattended.maxIterations, unattended.maxWallMinutes | Minimum — the smallest cap applies |
Absent fields mean unrestricted for that dimension (equivalent to the permissive default). A missing policy file is identical to no policy at all.
Malformed policy files are not treated as allow-all: they are skipped and surfaced as IGNORED (…) in dvalincode trust so gatekeepers know a policy was intended but did not apply. While authoring, validate before commit:
dvalincode policy check # validate ./dvalin.policy.json
dvalincode policy check path/to/file.json
dvalincode policy check --json # machine-readable, CI-friendly (exit 1 on failure)Network levels
The network field controls outbound connections initiated by the agent (provider requests, MCP, and subprocess egress under restricted postures).
| Level | Provider egress | Non-model egress (MCP, curl in shell, …) | Typical use |
|---|---|---|---|
off | Blocked | Blocked | Air-gapped or fully offline review |
endpoint-only | Allowed only to the configured model origin (redirects revalidated) | Blocked | Default enterprise posture — model calls OK, no arbitrary exfil |
on | Unrestricted | Unrestricted (subject to OS subprocess sandbox defaults on macOS) | Developer machines, trusted networks |
Under endpoint-only and off, shell and run_check subprocesses run inside an OS network sandbox when available (Seatbelt on macOS, Bubblewrap on Linux). See docs/EGRESS-THREAT-MODEL.md for boundary details.
Verify the live posture anytime:
dvalincode trust # human-readable resolved policy + enforcement
dvalincode trust --json # machine-readable TrustReportComplete example (every field)
Copy-paste valid JSON. Each key is explained in the field reference table below.
{
"modes": ["chat", "cowork", "code", "dvalin"],
"providers": {
"allow": ["deepseek", "openai", "ollama"]
},
"models": {
"allow": ["deepseek-chat", "gpt-4o-mini", "qwen2.5-coder"]
},
"commands": {
"allow": ["^npm\\b", "^node\\b", "^git\\b", "^pytest\\b"],
"deny": ["^curl\\b", "^wget\\b", "^rm\\b", "^ssh\\b"],
"defaultDeny": false
},
"paths": {
"allow": ["src/**", "tests/**", "docs/**"],
"deny": ["**/.env*", "secrets/**", "**/*.pem"]
},
"tools": {
"deny": ["memory_import"]
},
"mcp": {
"allow": ["github", "jira"]
},
"network": "endpoint-only",
"maxToolCalls": 75,
"unattended": {
"maxPermissionMode": "auto",
"maxIterations": 40,
"maxWallMinutes": 30
}
}Reading the example:
modes— agent may run in Chat, Cowork, Code, or Dvalin; omitting the key allows all four. The GUI groups Chat and Cowork under Home.providers.allow— only these provider profile ids (deepseek,openai, … in LLM config); omit = any provider.models.allow— only these model id strings; omit = any model.commands.allow— when set, the full shell command line must match at least one JavaScript regex; takes precedence overdefaultDeny.commands.deny— always evaluated first; matching regex blocks regardless of allowlist.commands.defaultDeny— whentrueand noallowlist is set, every shell command is blocked unless you add an allowlist.paths.allow/paths.deny— glob patterns (**,*,?); layered on top of.dvalincodeignorefor read filtering.tools.deny— tool names from the registry (shell,write_file,read_file, …); deny wins before execution.mcp.allow— MCP serveridvalues from~/.dvalincode/config.json; omit = any configured server;[]= none permitted.network— outbound posture (see network levels).maxToolCalls— hard cap on tool invocations per agent run; omit = unlimited. Resolved and reported bytrust; enforced at run start when set.unattended— bounds non-TTYruncalls, explicitrun --unattended, and MCP task runs. Explicit flags may narrow these values but cannot exceed them.
Field reference
| Field | Type | Default when absent | Semantics |
|---|---|---|---|
modes | "chat" | "cowork" | "code" | "dvalin"[] | all four modes | Subset of agent modes permitted. Cowork = plan-then-approve writes; Code = general coding; Dvalin = security scan and remediation. |
providers.allow | string[] | any provider | Allowlist of provider profile ids (e.g. deepseek, openai, ollama). User config cannot bypass a machine-level deny. |
models.allow | string[] | any model | Allowlist of model id strings exactly as configured (e.g. deepseek-chat, gpt-4o-mini). |
commands.allow | string[] | no allowlist gate | JavaScript regexes tested against the full shell command line. When present, only matching commands run. |
commands.deny | string[] | [] | JavaScript regexes; evaluated before allowlist. Malformed patterns never match (fail-safe). |
commands.defaultDeny | boolean | false | When true and commands.allow is unset, block all shell commands. Combine with allow for allowlist-only shells. |
paths.allow | string[] | workspace + ignore rules | Glob allowlist; paths outside all patterns are blocked for governed file tools. |
paths.deny | string[] | [] | Glob denylist; checked before allowlist. |
tools.deny | string[] | [] | Registry tool names to block entirely (e.g. shell, delete_file, run_security_scan). |
mcp.allow | string[] | any configured MCP server | Allowlist of MCP server id fields from config. Empty array denies all MCP. |
network | "off" | "endpoint-only" | "on" | "on" | Outbound network posture (see network levels). |
maxToolCalls | positive integer | unlimited | Maximum tool calls per agent run across all iterations. |
unattended.maxPermissionMode | "plan" | "auto" | "bypass" | unrestricted | Highest Code permission mode allowed when no human is present. |
unattended.maxIterations | positive integer | normal run default | Maximum model/tool loop iterations for unattended runs. |
unattended.maxWallMinutes | positive number | normal run default | Maximum wall-clock duration for unattended runs. |
Command matching note: patterns are new RegExp(pattern) — anchor explicitly (e.g. ^npm test\\b) to avoid accidental substring matches.
Path matching note: globs are anchored full-path matches after normalizing \ to /.
Tool names (common): shell, read_file, write_file, edit_file, delete_file, list_files, search_text, git_status, git_diff, run_check, run_security_scan, run_security_suite, memory_search, memory_write, memory_update, memory_delete, memory_import, list_skills, read_skill, project_scripts, list_remediation_cases, prepare_remediation_worktree.
Recipes
Ready-to-copy policies validated against orgPolicySchema. Adjust provider/model ids and MCP server ids to match your install.
Locked-down finance
Chat and Cowork only — no autonomous Code mode. Shell is allowlist + default-deny, secrets paths blocked, dangerous tools and all MCP denied, network fully off.
Machine policy (~/.dvalincode/policy.json):
{
"modes": ["chat", "cowork"],
"providers": {
"allow": ["openai"]
},
"models": {
"allow": ["gpt-4o-mini"]
},
"commands": {
"allow": [
"^npm test\\b",
"^npm run lint\\b",
"^git status\\b",
"^git diff\\b"
],
"deny": ["^curl\\b", "^wget\\b", "^rm\\b", "^ssh\\b", "^scp\\b"],
"defaultDeny": true
},
"paths": {
"allow": ["src/**", "tests/**", "docs/**"],
"deny": ["**/.env*", "secrets/**", "**/*.pem", "**/*.key", "infra/credentials/**"]
},
"tools": {
"deny": ["shell", "memory_write", "memory_import", "delete_file"]
},
"mcp": {
"allow": []
},
"network": "off",
"maxToolCalls": 25
}Optional repo narrowing (dvalin.policy.json in the repo root) — team removes Cowork so only read-only Chat remains:
{
"modes": ["chat"],
"paths": {
"deny": ["payments/**", "compliance/**"]
}
}Endpoint-only default
Enterprise baseline: all modes, approved providers, dangerous shell patterns blocked, secret paths denied, model endpoint reachable but no arbitrary egress.
{
"modes": ["chat", "cowork", "code", "dvalin"],
"providers": {
"allow": ["deepseek", "openai", "groq"]
},
"commands": {
"deny": ["^curl\\b", "^wget\\b", "^nc\\b", "^ssh\\b", "^scp\\b"]
},
"paths": {
"deny": ["**/.env*", "secrets/**", "**/*.pem", "**/*.key"]
},
"network": "endpoint-only",
"maxToolCalls": 100
}Permissive dev
Light guardrails for trusted developer machines — block catastrophic shell and the most sensitive dotenv files; everything else stays open.
{
"commands": {
"deny": ["^rm -rf\\b"]
},
"paths": {
"deny": ["**/.env", "**/.env.local"]
},
"network": "on"
}Narrowing walk-through
Machine policy sets network: endpoint-only and modes: [chat, cowork]. A developer commits a repo policy with network: on and modes: [chat, cowork, code].
Effective result: network stays endpoint-only, modes stay [chat, cowork]. The repo file cannot widen IT constraints — only add denials or intersect allowlists.
Run dvalincode trust from the repo root to see both source hashes and the effective fields side by side.
unattended follows the same rule. If the machine ceiling is auto, a repo cannot widen it to bypass; it can only narrow it to plan. Numeric limits resolve to the smallest value across both layers.
Related docs
- Approvability plan — why policy exists and epic acceptance criteria
- Egress threat model — network enforcement mechanics
- Governed MCP — MCP allowlist and trust surface
- Unattended recipes — cron, CI, and external-agent loops
- Threat model — agentic risks policy defends against

