Identity first. Everything else follows.
BitShout answers five questions in a fixed order, and refuses to skip one. Who is asking. What are they asking for. Is that allowed by policy. Did a human grant an exception. Did the operating system actually enforce the answer. Nothing is inferred, and nothing is assumed on the way through.
Identify the subject, not the process name
A PID is not an identity and a filename is not a signature. BitShout resolves the code-signing identity, Team ID, bundle identifier, executable identity and audit token, then binds the process to a containment session. If the application is updated or replaced under it, the old identity stops matching rather than quietly carrying on.
Follow the session down the process tree
Children, grandchildren, shells, interpreters and exec replacements all inherit the session. There is no hop count at which containment lapses.
Resolve what is actually being touched
A path is expanded, standardised, symlink-resolved and reduced to a device and inode. A network destination is classified from its bytes, not from a hostname string. An execution target is resolved to a canonical executable with its signing identity and hash. Only then does policy see it.
Evaluate deterministic policy
The policy engine is ordinary code with a version number. No model is consulted, no heuristic decides, and the same inputs produce the same verdict every time. For a contained subject, no matching allow rule means deny — including when the operation itself is unrecognised.
Ask you, on a surface the agent cannot reach
A grantable denial becomes a request. The request has no authority of its own. You choose deny, once, for this session, for a duration, or a permanent narrow rule — and the resulting grant is bound to a subject, a session, one operation, one resource and a clock.
Enforce, then record what kind of enforcement it was
The verdict goes back to whichever boundary asked. Then the whole decision is written to the local hash-chained evidence store, labelled with whether the operating system denied it or BitShout only evaluated it. Those two are never merged into one word.
From request to narrow authority.
A path is not an identity.
Prefix matching on a requested string is the failure mode that makes most sandboxes leaky: put a symlink inside the granted folder and the string still starts with the right characters. BitShout resolves first and compares second, on the object the kernel would actually open.
Credential stores, browser profiles, unrelated repositories, mounted NAS shares and removable volumes are denied by default — not because of their names, but because nothing granted them.
Filesystem model~/Projects/appgranted workspace · read + writesrc/node_modules/keys → ~/.sshsymlink~/Documentsnot granted~/.sshcredential store~/.awscredential store~/Library/Application Support/…browser profiles/Volumes/NASnetwork mountResolution, not comparison
- requested
~/Projects/app/keys/id_ed25519 - expanded
/Users/you/Projects/app/keys/id_ed25519 - symlinks resolved
/Users/you/.ssh/id_ed25519 - device / inode
dev(1,17) : 8814402 - verdict
outside grant → deny
Prefix matching on the requested string would have allowed this. BitShout matches on the resolved object, so the link leads nowhere.
Destinations are classified from their bytes.
Loopback, local network, multicast, internet — each decided from the address itself. A hostname with no resolved or observed address stays category unknown, and unknown is denied. Reverse DNS never creates identity.
The tree is the boundary.
The credential stays behind.
MCP servers and tools are registered by exact identity. Discovery is knowledge, not authority: a tool the server advertises is not a tool the agent may call, and a tool description is not a permission. Parameters are normalised before policy sees them, results are sanitised after, and an oversized or malformed response fails closed.
Broker architectureObserved facts, and one field that is only a claim.
Everything on the approval sheet is something BitShout established for itself. The application's stated reason is the exception, and it is shown greyed and italic for exactly that reason: it is what the agent said, not what BitShout saw, and it carries no weight.
The request stays open until you resolve it. The agent cannot approve it, dismiss it, time it out, or proceed around it — and Endpoint Security and Network Extension callbacks never block waiting on a human, so your Mac does not stall while the sheet is up.
- Action
- Read file
- Path
- ~/.ssh/id_ed25519
- Process
- claude → bash → cat
- Signed by
- Anthropic, PBC
- Why
- Not provided by application
BitShout asked. The application cannot answer this for itself, cannot dismiss it, and cannot proceed while it is open.
Configured is not the same as active.
- ProtectedAll boundaries proven healthy
- DegradedA boundary can no longer be proven
- LockedNew authority refused. Existing grants suspended.
- UnprotectedNever silently. Always stated.
- cannot determine → allow·cannot determine → deny
Append-only, hash-chained, local.
Clearing history and applying retention do not rewrite the past; they open a new generation and write an explicit marker. A record that was altered fails verification along with everything chained after it, and BitShout says so rather than quietly re-anchoring.
Evidence modelThe window is not the boundary.
If BitShout's interface crashes or you quit it, containment does not disappear with it. Authority lives in a trusted service and in the operating system components it drives — never in the process drawing the buttons.