Permissions & sandbox
Every mutating tool call passes through the permission engine before it runs. You control it with modes (how permissive the session is), rules (per-tool allow/deny/ask patterns), and optionally an OS-level sandbox underneath everything.
Where settings live (keep them lean)
| File | Role |
|---|---|
~/.hipmmcode/settings.json | Preferred home for permissions.defaultMode and long-lived allow / deny / ask rules (Claude-compatible layered settings) |
Project .hipmmcode/settings.json / .hipmmcode/settings.local.json | Project shared vs local (gitignored) rules |
~/.hipmmcode/config.json | Models, API keys, MCP, UI — not a dump of hundreds of one-shot allow rules |
From v0.14.1, a first run seeds a minimal user settings file when none exists:
{
"permissions": {
"defaultMode": "auto"
}
}Prefer this Claude-simple shape. Do not copy huge historical allow lists from other tools — use Auto mode for low-risk calls, and only add allow entries you deliberately “always allow”.
Upgrading the binary does not overwrite your settings; it also does not re-inject old one-shot grants from the package. Dirty allow lists only come from local files you (or past “don’t ask again” clicks) wrote.
Permission modes
| Mode | Behavior |
|---|---|
auto | Default for new installs (v0.14.1+). Classifier auto-allows lower-risk tool calls; blocks or asks on riskier ones. Aligns with Claude Code’s Auto default |
default | Manual: read-only tools free; mutating calls prompt (or follow your rules) |
acceptEdits | File edits auto-approved; risky shell still prompts |
plan | Read-only planning — no mutations until you approve the plan (EnterPlanMode / /plan) |
bypassPermissions | Everything allowed except explicit deny rules |
dontAsk | Restrictive mode (not in the casual Shift+Tab cycle) |
Switch anytime:
- Shift+Tab cycles modes mid-session (status bar shows the active mode). Typical cycle: default → acceptEdits → plan → (auto when available) → …
/permissions mode auto- CLI:
--permission-mode auto|plan|…, or--dangerously-skip-permissions(= bypass).
A disableAutoMode managed-settings killswitch can ban Auto org-wide.
The approval prompt
When a gated tool call needs your decision, an in-terminal dialog appears — the streaming pauses until you answer:
Bash command npm install ❯ Allow once Allow always — persist an allow rule for this command Deny (Esc) ↑/↓ move · Enter confirm · Shift+Tab switch mode
Allow always records a permanent allow rule into a settings layer you pick (user / project / local), so the same command never prompts again. Prefer short, intentional rules — not a long dump of one-off commands. The dialog header names the tool ("Bash command", "Edit file", "Fetch", …) and the body shows exactly what will run.
While a /workflows monitor panel is open, a pending tool approval preempts the panel (permission focus outranks the monitor) so you are not surprised only after Esc-closing the panel.
A policy killswitch (disableBypassPermissionsMode) can disable bypass org-wide.
Rules
Rules live in config (permissions) and standard settings files, and are also editable live:
/permissions allow Bash(git push:*)
/permissions deny Bash(rm -rf:*)
/permissions ask FileWrite(src/**)
/permissions # view current rules + modeJSON shape:
{
"permissions": {
"allow": ["Bash(git status:*)", "FileEdit(src/**)"],
"deny": ["Bash(sudo:*)", "WebFetch"],
"ask": ["Bash(git push:*)"]
}
}Rule syntax: Tool (whole tool) or Tool(specifier) — command prefix patterns for Bash (Bash(git push:*)), glob paths for file tools (FileEdit(src/**)), mcp__server or mcp__server__tool for MCP. Precedence: deny > ask > allow, and the more specific rule wins. During a session you can answer a prompt with "always allow", which records a session-scoped grant.
Built-in guardrails
Independent of your rules:
- Bash read-only classifier —
ls,git status,grep, … run without prompts; state-changing commands prompt (bashPromptForWrites, default on). - Destructive-command hard block — catastrophic patterns (
rm -rf /, disk-wiping, protected-branch force-push) are refused outright, in every mode. - Protected-path write confirmation — writes to sensitive paths (shell rc files, credentials) always confirm.
- Workspace boundary — file access outside cwd +
--add-dirroots asks first (confirmOutsideWorkspace, default on). - Trust dialog — first time you run hipmmcode in a directory, it asks whether to trust it (
trustedDirs).
Unattended runs
With no human present, ask decisions fail closed (denied). For automation, choose deliberately:
| Option | Use |
|---|---|
--permission-mode acceptEdits | CI that edits files but must not run risky shell |
--dangerously-skip-permissions | Fully trusted sandboxed environments only |
--permission-webhook <url> | Your service decides: receives each request, replies allow / deny / allow_always |
--permission-prompt-tool <mcp-tool> | Delegate decisions to an MCP tool |
The same fail-closed rule applies to the server task queue and hooks-driven runs.
Auto mode (default) & classifier config
Session mode auto (the v0.14.1 default) routes ask-bucket tool calls through a classifier: lower-risk actions run without a human prompt; high-risk or unclear actions are blocked or fall back carefully (never open a silent hole when the classifier is unavailable).
Optional autoMode config (in settings / config) tunes the classifier with natural-language intents:
{
"autoMode": {
"allow": ["push to the feature branch", "delete files under .cache/"],
"hard_deny": ["never destroy production databases"],
"classifierModel": "claude-haiku-4-5",
"classifyAllShell": false
}
}| Field | Meaning |
|---|---|
allow / soft_deny / hard_deny / environment | Natural-language policy buckets for the classifier (not shell globs) |
classifierModel | Cheap model for yes/no classification (defaults per provider when unset) |
classifyAllShell | When true, suspend positive Bash allow rules and classify every shell call |
A disableAutoMode policy killswitch exists for org-managed setups.
OS sandbox
For real OS-level isolation of shell commands (beyond permission prompts), hipmmcode ships a sandbox backend: bubblewrap on Linux, Seatbelt on macOS. Off by default.
| Config key | Env | Effect |
|---|---|---|
sandbox.enabled | HIPMMCODE_SANDBOX=1 | Master switch — Bash runs inside the sandbox |
sandbox.allowNetwork | HIPMMCODE_SANDBOX_NETWORK=1 | Allow outbound network inside the sandbox |
sandbox.writableRoots | HIPMMCODE_SANDBOX_WRITABLE=/a:/b | Extra writable roots (default: cwd, HOME, tmp) |
sandbox.hideRoots | HIPMMCODE_SANDBOX_HIDE=/secret | Paths masked entirely (multi-tenant isolation) |
sandbox.failIfUnavailable | HIPMMCODE_SANDBOX_FAIL=1 | Refuse to run commands if no sandbox backend exists |
With the sandbox on, even an allow-ed command physically cannot write outside its writable roots or reach hidden paths.
Worktree isolation
Orthogonal to permissions: run risky work in a git worktree so the main checkout stays clean — /worktree <name> for the session, Agent(isolation: "worktree") per teammate, /worktree exit keep|remove when done. worktreeBaseRef picks the base (head or fresh).