How it works

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.

Step 5, in motion

From request to narrow authority.

01AI requestsInert. Changesnothing by itself.02BitShout verifiesSigning · lineageresource identity03Human choosesOn a surface theagent cannot reach04Narrow grantOne op, one resource,one clock05RevalidatedEvery single use.Stale fails closed.Effective grantsubject=com.anthropic.claude · session=7C4A · op=readinode=dev(1,17):8814402 · expires=+00:30 · uses=1
Filesystem

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 + write
src/
node_modules/
keys → ~/.sshsymlink
~/Documentsnot granted
~/.sshcredential store
~/.awscredential store
~/Library/Application Support/…browser profiles
/Volumes/NASnetwork mount

Resolution, not comparison

  1. requested~/Projects/app/keys/id_ed25519
  2. expanded/Users/you/Projects/app/keys/id_ed25519
  3. symlinks resolved/Users/you/.ssh/id_ed25519
  4. device / inodedev(1,17) : 8814402
  5. verdictoutside grant → deny

Prefix matching on the requested string would have allowed this. BitShout matches on the resolved object, so the link leads nowhere.

Network

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.

Default ruleDeny all connections unless explicitly allowed
CLapi.anthropic.com:443Claude · internetexplicit grant · 30 minAllow
CHapi.openai.com:443ChatGPT · internetexplicit grant · sessionAllow
CO192.168.1.40:22Codex CLI · local networkno matching allow ruleDeny
CO127.0.0.1:5432Codex CLI · loopbacklocal service not grantedDeny
CL224.0.0.251:5353Claude · multicastmDNS discovery not grantedDeny
CLlocalhostClaude · no resolved address · unknownhostname evidence only → unknown → denyDeny
Execution

The tree is the boundary.

claudesigned · Anthropicbashchild · /bin/bashpython3grandchild · py3curlgreat-grandchildSESSION 7C4AContained subjectStill containedconnect 192.168.1.40:22 — deniedNo matching allow rule for the inherited session→ deny. Lineage is part of the boundary, not a way around it.
AI agentcontainedBitShout brokertrusted servicevalidate identityvalidate capabilityvalidate resourcevalidate parametersvalidate policyCredentialbroker-ownedGitHub APIexternalcall toolresult onlyThe reusable secret never reaches the agent.
Capabilities & MCP

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 architecture
The decision

Observed 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.

Authorization required
Claude is requesting to read a fileOutside the permitted workspace · contains credential material
High risk
Action
Read file
Path
~/.ssh/id_ed25519
Process
claude → bash → cat
Signed by
Anthropic, PBC
Why
Not provided by application
DenyBlock this request
Allow onceThis one action, then gone
Allow for sessionUntil this session ends
Allow permanently…Create a narrow rule

BitShout asked. The application cannot answer this for itself, cannot dismiss it, and cannot proceed while it is open.

Health

Configured is not the same as active.

  1. ProtectedAll boundaries proven healthy
  2. DegradedA boundary can no longer be proven
  3. LockedNew authority refused. Existing grants suspended.
  4. UnprotectedNever silently. Always stated.
  5. cannot determine → allow·cannot determine → deny
#0841policyEvaluatedsha256 ✓#0842observedsha256 ✓#0843enforcedDenysha256 ✓#0844authorizedsha256 ✓#0845enforcedAllowsha256 ✓#0846notEnforcedsha256 ✓Append-only · generation 3 · localrecord alteredChain verification fails from #0843 onward — and says so.
Evidence

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 model
Components

The 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.

ComponentRoleOwns
BitShout.appShows state, collects your decisions, runs setup. Owns no authority.UI only
Trusted servicePolicy engine, sessions, grants, capability broker, health, evidence.Authority
Endpoint Security extensionFilesystem and execution authorization at the macOS boundary.OS boundary
Network Extension providerFlow filtering through NEFilterDataProvider.OS boundary
Vault Runtime HostOwns the VZVirtualMachine lifecycle for Vault Mode.Signed helper