buildwithnexusv0.15.1
Menu

Permissions & hooks

Permission gate

The permission gate checks every mutating tool, such as write_file, edit_file and run_command, before it runs.

ModeBehavior
ask (default)Shows a diff preview of up to 40 lines for a file edit, or the full shell command with line breaks shown as ⏎. Then it asks: yes, no, allow for this session, always allow, or deny with feedback. bwn remembers "always allow" for this project only. The answer covers the program, or the program and its subcommand, such as ls or git status. Some answers cover only that exact command: shells and interpreters like sh or python3, programs like rm, chmod and kill, and git commands that discard work, such as git reset --hard. The prompt names the scope. /permissions reset forgets these answers. Only a single plain command can skip the prompt. Anything chained, piped, redirected or substituted always asks. Web fetches and searches ask once per host and port, and "fetch *" in allowed_commands allows every host.
accept-editsApplies file edits inside the workspace, and inside folders added with --add-dir, without asking. Commands, deletions, network access, sensitive paths and any change inside .git still ask.
autoRuns without asking. Dangerous commands, sensitive paths and calls an ask rule covers still ask. In WSL, so do file changes on a Windows drive and commands that name a path under /mnt/<drive>/.
readonlyRefuses every change and never shows a prompt that could allow one. Read-only shell commands such as grep, cat and git log still run. Web fetches, searches and reads of sensitive paths still ask.

Allow, ask and deny rules under permissions in settings.json apply before the mode. A rule such as run_command(git push*) names a tool and a pattern. A deny rule refuses in every mode. An ask rule asks even under auto. An allow rule skips the mode's prompt, but readonly still refuses every change, and sensitive paths and dangerous commands still ask. Deny beats ask, and ask beats allow.

bwn checkpoints a file before write_file, edit_file, multi_edit, move_path, remove_path or create_docx changes it, and /undo reverts the last agent turn's edits. bwn doesn't checkpoint what shell commands, apply_patch or str_replace_editor change, so /undo doesn't restore those files. Checkpoints can't hold files over 2 MiB or not in UTF-8, and /undo leaves those files alone rather than clobbering them. If the turn changed one of those files, /undo changes nothing and names the files that blocked it. If you edited a file after the agent did, /undo asks before overwriting your changes. Answer n to keep your version and restore the rest. The file tools refuse writes outside the workspace and the folders added with --add-dir, in every mode. .git is not a sensitive path, so auto doesn't ask before changes inside it. accept-edits asks before any change inside .git, and with the sandbox on, shell commands can't write to it.

Sensitive paths always ask, even under auto, unless one of your own hooks allows the call: .env files, ~/.ssh, ~/.aws, .kube, .docker/config.json, .config/gh, .netrc, .npmrc, .pypirc, .git-credentials and similar. The check follows symlinks and covers shell arguments too, so cat ~/.aws/credentials asks. Search tools skip credential directories.

Model replies and tool output can't send control sequences to your terminal. bwn shows an escape character as a visible ␛.

Project settings

A repository can ship .buildwithnexus/settings.json and settings.local.json. Until you trust them, they can set these harmless keys: reasoning_effort, temperature, max_tokens, instruction_files, images, notify and idle_notify_secs. They can also tighten permission and sandbox, lower max_budget_usd, and add ask and deny rules. Anything else, such as model, base_url, a looser permission, allow rules, hooks or MCP servers, needs your approval at one prompt:

⚠ /home/you/src/app has project settings that can run commands, send your API key elsewhere, loosen approvals, or add to the system prompt:
  .buildwithnexus/settings.json:
    base_url: "https://llm.example.com/v1" — your requests and API key go to this address
    hook PreToolUse Bash: ./scripts/guard.sh
    permission: "auto" — edits and commands run without asking you
  (also trusts these project files as they are now; editing one asks again: ./scripts/guard.sh)
Trust? y everything above · e all except base_url and permission · n nothing [N]:

The answer defaults to No, which keeps your own settings in effect. When the file sets base_url or permission beside other keys, e trusts everything except those keys. bwn records trust for each file as a digest of that file and of the project files its hooks and MCP servers run. If you edit any of them, bwn asks again. Headless runs never prompt. They ignore those keys and print one warning. To trust them for one run, pass --trust-project <digest> with the digest that buildwithnexus trust --print prints. When the settings set base_url, or a permission looser than readonly, also name those keys with --trust-project-allow. A trusted project can add MCP servers but never change one you defined. Commands like /model and "always allow" save only their own key to your user settings, never the project's.

Permission modes and rules, the sensitive-path prompt, and checkpoints reduce mistakes, but they are not OS-level isolation. An approved command runs with your user's full permissions. Checkpoints can undo file edits in your working tree but not network calls, pushed commits, published packages, or other external effects. Run untrusted work in a container or VM.

Sandbox

You can opt in to OS-level confinement for shell commands. Set "sandbox": "auto" in settings, pass --sandbox auto, or run /sandbox auto. Every run_command, !cmd, check_work, start_server and python_tool call then runs inside bwrap on Linux or sandbox-exec on macOS. The filesystem is read-only except for your workspace, the folders added with --add-dir, and the temp directories. On Linux, /tmp is a fresh directory that bwn discards when the command exits. ~/.buildwithnexus stays read-only, and so do the .git and .buildwithnexus of the workspace and of each added folder. With or without the sandbox, these commands don't inherit variables whose names end in _API_KEY or _API_TOKEN, or a provider's key variable such as HF_TOKEN. To keep one, list it in shell_env_passthrough. Sandboxed commands also lose SSH_AUTH_SOCK and the D-Bus session.

When no backend works, "require" refuses to run commands, and "auto" runs them unconfined with a one-time notice. "sandbox_network": false also cuts off network access. Sandboxed commands show [sandboxed] in their header. /sandbox status and bwn doctor report whether a backend is available. The sandbox does not limit what commands read, and it does not confine the agent's own file tools, hooks or MCP servers. The file tools already refuse writes outside the workspace and the added folders. Windows and WSL have no backend yet.

Hooks

Hooks run your own commands at the same lifecycle points as Claude Code. Configure them in ~/.buildwithnexus/settings.json for your user or in .buildwithnexus/settings.json for a project. Project hooks run only after you trust the project settings. They can deny a tool but never grant one, so a hostile repo can't use them to unlock anything. In PLAN, BRAINSTORM and readonly, a hook can deny a call but never allow a write.

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "run_command",
        "hooks": [{ "type": "command",
                    "command": "echo 'no shell on main' >&2; exit 2" }] }
    ]
  }
}

The events are SessionStart and SessionEnd (once per process), UserPromptSubmit, PrePrompt (before every model request in a build turn), PreToolUse, PermissionRequest (when bwn is about to ask you to approve a call), PostToolUse, PostResponse, OnError, Stop (after every turn in every mode), SubagentStop, PreCompact (before bwn summarizes the conversation) and Notification. A Notification hook fires when an approval or a question waits for you, when a turn ends, and when the prompt sits idle for idle_notify_secs, 60 seconds by default. It fires whether or not bwn shows a desktop notification, and headless runs fire it when each turn ends. A PreToolUse hook that exits with 2, or returns permissionDecision: "deny", blocks the tool, even under auto. So does a PreToolUse hook that times out, can't start or is killed. Any other non-zero exit shows a warning and the call goes on, unless the hook sets "on_error": "deny", which makes that exit block the call too. An "allow" from one of your own hooks skips every prompt, even for sensitive paths, dangerous commands and ask rules. Only deny rules still apply. A PermissionRequest hook can answer allow, deny or ask in your place, and only your own hooks can allow. Each hook gets 10 seconds by default. Set "timeout" on a hook to change it.

A matcher is an exact tool name, an a|b list such as "Edit|Write", or a glob such as "*_file" or "mcp__*". For PreCompact, the matcher matches the trigger, auto or manual. For Notification, it matches the notification type, one of permission_prompt, question, idle_prompt or done. Claude Code's tool names, such as Bash, Edit and Write, match the bwn tools that do the same thing. A hook reads a JSON payload on stdin, and a hook with "type": "http" gets the same JSON as a POST body. It has hook_event_name, session_id, transcript_path, permission_mode and cwd, plus the event's own fields, such as tool_name, tool_input and tool_response. The field names match Claude Code's. bwn also runs scripts placed in ~/.buildwithnexus/hooks/<Event>/ for that event, with no settings entry.

MCP servers

bwn is a Model Context Protocol client over stdio and Streamable HTTP. Servers listed under mcp_servers in user or project settings.json connect in the background when you send your first prompt. Each one logs mcp: <name> connected, N tools. bwn offers each discovered tool to the model as mcp__<server>__<tool>, with the server's own description and schema. These tools pass through the permission gate like any mutating tool. bwn honours a server's readOnlyHint only when you set "trust_read_only_hints": true on that server, because the hint is the server's own claim. If a server hangs or exits, bwn reports it once and drops its tools until you run /mcp reload.

{
  "mcp_servers": {
    "fs":     { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] },
    "remote": { "url": "https://mcp.example.com/mcp", "headers": { "Authorization": "Bearer …" } }
  }
}
/mcp                                  servers, status, tool counts
/mcp <name>                           that server's tools
/mcp add <name> <command> [args...]   stdio server
/mcp add <name> --url <url> [--header K=V]
/mcp remove <name> · /mcp reload
/mcp login|logout <name>              OAuth sign-in and sign-out, http servers
bwn mcp list|add|remove               the same, for scripts

An http server that asks for an OAuth sign-in shows as needs login. bwn mcp login <name> or /mcp login <name> signs in through the browser and saves the token in ~/.buildwithnexus/mcp-auth/<name>.json. In headless runs, servers connect before the first request. bwn waits once for all of them. The wait is the largest timeout_secs among the servers, and a server without one counts as 5 seconds. Then bwn skips any server that isn't ready. bwn doctor connects to every configured server and reports the result.