Skip to content
ƒtsforgev0.52.0
19

Permissions & policy

3 min read

Every action the agent takes (read a file, write one, run a shell command, hit the network, call an MCP tool) is checked against a policy before it runs. The policy is deny-first: the order is critical denies → your config rules → the active mode’s default. Nothing ambiguous is ever silently allowed.

A mode sets the baseline posture. Pick one with --policy-mode <mode>, or policy.mode in tsforge.config.json (the flag wins). Plan mode (--plan / /plan) overrides to plan while it’s on.

ModeWritesShellNetworkDeleteUse it for
defaultallowallowallowdenyInteractive day-to-day (the default).
plandenyread-onlyallowdenyRead-only exploration before you approve a plan.
acceptEditsallowaskdenydenyAuto-accept edits, but still confirm shell.
ciallowdenydenydenyNon-interactive pipelines; anything that would prompt is denied.
dontAskallowdenydenydenyA local “never prompt me” run (identical to ci).
bypassPermissionsallowallowallowallowThe escape hatch: allow everything (critical denies still apply).

In plan mode the run tool is allowed through but its own read-only guard restricts it to read-only commands, so exploration can’t mutate. delete is denied in every mode except bypassPermissions. Anything that would otherwise ask collapses to deny when there’s no interactive approval path (non-interactive runs).

Refine a mode with deny / allow / ask lists under policy.rules. They’re evaluated before the mode default, in that order (deny wins over allow wins over ask):

{
"policy": {
"mode": "default",
"rules": {
"deny": [{ "kind": "shell", "commandPrefix": "rm " }],
"allow": [{ "kind": "network" }],
"ask": [{ "kind": "delete_file", "pathPattern": "src/**" }]
}
}
}

A rule matches when every field it specifies matches the action (an empty rule matches everything as a deliberate catch-all). Available fields: kind, toolName, pathPattern (glob), commandPrefix, commandPattern (regex), mcpServer. Action kinds are read_file, write_file, edit_file, delete_file, shell, network, mcp_tool, plugin_tool, unknown.

These fire in every mode, including bypassPermissions. They have no safe override:

  • Destructive shell: commands like rm -rf / are blocked.
  • Private-key reads: reading an SSH/PEM private-key path is blocked.
  • Unregistered MCP servers: an mcp__* call to a server not in your config is blocked.

With --log, tsforge records every tool call, its policy decision (allow / ask / deny, with the matched rule and a risk level), the model’s reasoning, and each gate verdict as JSONL under ~/.tsforge/logs/. tsforge trace turns that ledger into a one-screen summary. Deterministically, with no model call, you can audit exactly what ran and why.

tsforge.config.json · Trace a run · Plan mode