Skip to main content

Policies API

Overview

Policies define what agents can and cannot do within a workspace. They cover three areas:

  • Command rules: allow/deny patterns for executable commands
  • Network rules: egress allowlist (which hosts the workspace can reach)
  • Secret rules: which secrets are available to which workspaces

When a workspace has a policy attached, every exec call is evaluated against the policy's command rules before execution. If the command is denied, a 403 response is returned.

Create Policy

Creates a new policy with default safety rules.

curl -X POST https://api.nanoterm.dev/api/policies \
-H 'Authorization: Bearer nt_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "coding-agent-policy",
"description": "Standard rules for coding agents",
"commandRules": [
{ "action": "deny", "pattern": "rm -rf /", "description": "Prevent root deletion" },
{ "action": "deny", "pattern": "sudo *", "description": "No privilege escalation" },
{ "action": "allow", "pattern": "npm *", "description": "Allow npm commands" },
{ "action": "allow", "pattern": "git *", "description": "Allow git commands" }
],
"networkDefault": "deny",
"networkAllowlist": [
{ "host": "registry.npmjs.org", "description": "npm packages" },
{ "host": "github.com", "description": "Git repos" }
]
}'

Request Body:

FieldTypeRequiredDefaultDescription
namestringyesPolicy name
descriptionstringnonullHuman-readable description
commandRulesCommandRule[]nodefault deny listCommand allow/deny rules
networkDefault"allow" | "deny"no"deny"Default network behavior
networkAllowlistNetworkRule[]nonpm/github/pypiAllowed egress hosts
secretRulesSecretRule[]no[]Secret access rules

CommandRule:

{ "action": "allow" | "deny", "pattern": "npm *", "description": "optional" }

NetworkRule:

{ "host": "registry.npmjs.org", "description": "optional" }

Response: 201 Created

{
"id": "pol_a1b2c3d4",
"orgId": "org_x1y2z3",
"name": "coding-agent-policy",
"description": "Standard rules for coding agents",
"commandRules": [...],
"networkDefault": "deny",
"networkAllowlist": [...],
"secretRules": [],
"createdAt": "2026-04-06T08:00:00.000Z",
"updatedAt": "2026-04-06T08:00:00.000Z"
}

Default Rules

If no commandRules are provided, these deny rules are applied automatically:

PatternDescription
rm -rf /Prevent root deletion
rm -rf /*Prevent root deletion
mkfs *Prevent filesystem format
dd *Prevent raw disk write
sudo *Prevent privilege escalation
chmod 777 *Prevent open permissions
:(){ :|:& };:Prevent fork bomb

Default networkAllowlist when networkDefault is "deny":

HostDescription
registry.npmjs.orgnpm packages
*.npmjs.orgnpm CDN
github.comGit repositories
*.github.comGitHub APIs
pypi.orgPython packages
*.pypi.orgPyPI CDN
archive.ubuntu.comUbuntu packages

List Policies

curl https://api.nanoterm.dev/api/policies \
-H 'Authorization: Bearer nt_xxx'

Response: 200 OK — Array of PolicyInfo

Get Policy

curl https://api.nanoterm.dev/api/policies/pol_a1b2c3d4 \
-H 'Authorization: Bearer nt_xxx'

Response: 200 OKPolicyInfo

Update Policy

curl -X PUT https://api.nanoterm.dev/api/policies/pol_a1b2c3d4 \
-H 'Authorization: Bearer nt_xxx' \
-H 'Content-Type: application/json' \
-d '{
"commandRules": [
{ "action": "deny", "pattern": "rm -rf *", "description": "No recursive delete" },
{ "action": "allow", "pattern": "pytest *", "description": "Allow tests" }
]
}'

Only the provided fields are updated; others are preserved.

Response: 200 OK — Updated PolicyInfo

Delete Policy

curl -X DELETE https://api.nanoterm.dev/api/policies/pol_a1b2c3d4 \
-H 'Authorization: Bearer nt_xxx'

Response: 200 OK

{ "ok": true }

Evaluate Command

Test whether a command would be allowed or denied by a policy, without actually executing it.

curl -X POST https://api.nanoterm.dev/api/policies/pol_a1b2c3d4/evaluate \
-H 'Authorization: Bearer nt_xxx' \
-H 'Content-Type: application/json' \
-d '{"command": ["rm", "-rf", "/"]}'

Response (denied):

{
"allowed": false,
"reason": "Blocked by policy \"coding-agent-policy\": Prevent root deletion",
"matchedRule": {
"action": "deny",
"pattern": "rm -rf /",
"description": "Prevent root deletion"
}
}

Response (allowed):

{
"allowed": true
}

Attaching a Policy to a Workspace

Specify policyId when creating a workspace:

curl -X POST https://api.nanoterm.dev/api/workspaces \
-H 'Authorization: Bearer nt_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "coding-agent",
"image": "ubuntu:22.04",
"policyId": "pol_a1b2c3d4"
}'

Once attached, every POST /api/workspaces/:id/exec call will evaluate the command against the policy. If denied, the API returns 403:

{
"error": "Command blocked by policy",
"reason": "Blocked by policy \"coding-agent-policy\": Prevent root deletion",
"stdout": "",
"stderr": "Blocked by policy \"coding-agent-policy\": Prevent root deletion",
"exitCode": 126
}

Rule Evaluation Order

  1. Command rules are evaluated in order (first match wins).
  2. If no rule matches, the command is allowed.
  3. Glob patterns are supported: * matches any characters, ? matches one character.
note

Place more specific rules before general ones. For example, allow npm test before deny npm * if you want to allow only npm test.