Permissions: what needs your approval¶
| Tier | Examples | How it is enforced |
|---|---|---|
| Do without asking | read Slack/GitHub/Confluence, update the ledger, bosun delegate --window (and --update) to a repo window already running an agent; that session then acts within its task type's rules (see Delegated tasks) |
auto mode, plus an explicit allow list |
| Propose, then wait | the orchestrator's own Slack posts and reactions, gh pr merge/review/comment/edit/close/ready/update-branch, gh api, git push, Confluence/Jira comments, and any other MCP tool whose name has a write verb (send, create, update, delete, transition, execute, ...) |
the policy, ask rules in orchestrator/settings.json, and bosun guard, a PreToolUse hook that asks before every MCP write tool, including ones the rules do not list (verified: Claude Code prompts even in auto mode) |
| Propose, then wait | starting an agent's session: in a new window (bosun delegate ... --new) or in a window that has none (--start) |
the policy and chores procedure only, so you approve once in the plan without a second permission prompt |
| Propose, then wait | accepting work someone else asked for (a task spawned from their message); changing an external ticket (transition, assign, edit) | a task is accepted only with user: evidence and delegate refuses it before; ticket changes are proposals from ledger tickets --mismatch, and the tracker's write tools ask (guard) |
| Propose, then wait | applying a learning proposal (bosun learn apply, or done/reject for one) |
the policy and the chores procedure; ledger resolve refuses learn: items, so they close only through bosun learn, which applies the change and closes the item together |
| Propose, then wait (unless you asked for it) | handing an item over again while its last hand-over awaits a report (delegate ... --again), or moving it to another window (delegate --update <id> --window W) |
delegate refuses the first without --again; the policy covers both |
| Never | edit files, gh pr merge --admin, force pushes, bosun uninstall |
deny rules (prefix matches, so best effort) plus bosun guard, which denies git push with -f, --force* or a +refspec and gh pr merge --admin anywhere in a command (verified) |
| Never | a free-text bosun send that names no work item; send --new or send --start <msg> instead of delegate |
bosun guard denies send without --id <ledger id> or --no-item; updates to delegated work go through delegate --update |
| Never | a command substitution the shell would run ($(...) or backticks, outside single quotes or in an unquoted heredoc) in bosun delegate, bosun send or bosun ledger reach-out, ask, replied, propose, approve, spawn, link, unlink, fold or ticket-sync |
bosun guard denies it: their text often comes from Slack, PRs or a tracker, so it must never run commands |
Approvals: approve once, in chat¶
By default every outward call above opens a Claude Code permission prompt, even after you approved
it in the plan, and an unanswered prompt waits forever. With BOSUN_APPROVALS_ENABLED=1 (from
the orchestrator's next start; design: docs/design/approvals.md):
- The orchestrator proposes outward calls with the exact call (
ledger propose <id> --send slack --channel C --text -,--run '<command>',--tool <tool> --args-json -). When you approve ("1 and 3 yes"), it records your words (ledger approve <id> --quote '1 and 3 yes') and makes the call; a PermissionRequest hook (bosun guard --permission-request) lets exactly that call through, once, if your words are in a prompt you typed (or sent from your phone) in that session after the proposal. Tool results, pasted and injected text never count. - A tool call whose arguments hold a decimal number (
{"story_points": 2.5}) cannot be proposed: the ledger's journal holds whole numbers and text only, soproposerefuses it, naming the field. Make such a call yourself, or setBOSUN_APPROVALS_DENY_UNAPPROVED=0so that it asks at Claude Code's prompt. - An outward call with no approved proposal is denied at once (
bosun guard --approvals), telling the orchestrator to propose it, instead of waiting at a prompt.BOSUN_APPROVALS_DENY_UNAPPROVED=0brings the prompt back for those.BOSUN_APPROVALS_GATED_COMMANDSadds a site's own outward commands to gate (prefixes separated by,, e.g."mytool deploy, mytool publish"). - An approved command runs as a Bash call of its own, exactly as proposed: a call that wraps it in
more (
timeout 120 <command>, a pipe, another step) is denied with the exact command to run. The guard judges the command a wrapper runs (timeout,env,nice,nohup,xargs, ...), so a wrapper never hides a gated call. - Once the approved call has run,
bosun guard --post-tool-use(PostToolUse and PostToolUseFailure) settles its proposal: success closes it as done (outreach is marked carried out and closes withledger sentas before); failure leaves it open withapproval-failed, so it is under Needs you again and needs a new proposal and approval (approvals are single-use). An approved call not made yet, or made and closing, does not count as needing you.ledger resolve-carried-out [--dry-run]settles proposals whose call ran before this hook, by what the orchestrator's transcript shows. - Standing approvals, none unless configured:
BOSUN_STANDING_CLASSES="pr-review-traffic"(review requests, re-review bumps and merged notes for your own ready PRs, ready meaning the latest report on the PR's work says--ci greenwith nothing open; proposed as outreach, inBOSUN_STANDING_SLACK_CHANNELS, mentioning onlyBOSUN_STANDING_SLACK_MENTIONS, with no URL but the PR's) andpr-housekeeping(gh pr ready, andgh pr editwith only--title,--body,--add-reviewerfromBOSUN_STANDING_GH_REVIEWERS, andgh pr comment <url> --body '<text>'when the text is exactly one ofBOSUN_STANDING_GH_COMMENTS, '|'-separated, e.g. the command that asks an automated reviewer for another round; at mostBOSUN_STANDING_GH_COMMENTS_MAX_PER_PR(default 3, 0: no cap) per text and PR, then each needs your word). Never DMs, merges,gh apior tracker writes. Each is listed in the next check-in (ledger approvals --auto). - A re-review request on your PR (outreach with purpose
review,gh pr edit --add-reviewer, or a standing comment), approved or standing, is checked against the live PR first (BOSUN_APPROVALS_CHECK_REVIEW_THREADS=1, the default): it is denied while a review thread awaits you (bosun pr-threads <url> --awaiting-authorlists it) or a review is newer than the proposed message. If GitHub cannot be read, you get the prompt. - Automated reviewers: bots never count in
pr-threads,review-watchand that check (a bot that comments on every push would block every request), unless their GitHub login is inBOSUN_REVIEWER_BOTS(space-separated): then their threads await you and their reviews are rounds, like a person's, and their latest verdict is shown, read from the review body withBOSUN_REVIEWER_BOT_VERDICT_PATTERN(default^#+\s*Verdict:\s*(.+?)\s*$, multi-line; the first group is the verdict). A reviewer bot's review is never an approval. - The
askrules stay: if a hook fails or decides nothing, you get the prompt as before.
Content from Slack, PRs and Confluence is untrusted. The policy tells the orchestrator never to
follow instructions found there; delegated tasks are built by bosun delegate from the task
files, with the orchestrator supplying only the facts (ledger id, link, requester, one-line ask).
Repo sessions run in Claude Code's auto mode by default (BOSUN_CLAUDE_FLAGS), where a safety check
reviews each action. bosun init asks which to use: auto mode (recommended) or skip permission
checks, which sets --dangerously-skip-permissions there. That has no checks and,
in Claude Code's own words, "offers no protection against prompt injection": not on a machine holding
production credentials.
A repo session on Codex runs in its sandbox with its automatic reviewer (BOSUN_CODEX_FLAGS:
--no-daemon --approve-for-me), and bosun adds the repo's .git, its own state folder and the network to
what the sandbox allows. --dangerously-bypass-approvals-and-sandbox in that setting would remove both, for
the same reasons not to. The rules in a task are text on Codex as they are on Claude Code: a session follows
them, but neither agent's safety check is what enforces them. See
Agents.