Metadata-Version: 2.4
Name: sandbroker
Version: 0.1.0
Summary: A typed-return secret broker: clients USE secrets over a unix socket, they never SEE the plaintext.
Author: Grayson Adams
License: MIT
Project-URL: Homepage, https://github.com/312-dev/sandbroker
Project-URL: Documentation, https://github.com/312-dev/sandbroker/blob/master/DEPLOY.md
Keywords: secrets,broker,credentials,claude,1password,azure-key-vault,webauthn
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: No Input/Output (Daemon)
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: push
Requires-Dist: pywebpush<3,>=2.0; extra == "push"
Requires-Dist: py-vapid<2,>=1.9; extra == "push"
Requires-Dist: cryptography>=41; extra == "push"
Provides-Extra: webauthn
Requires-Dist: cryptography>=41; extra == "webauthn"
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.12; extra == "mcp"
Provides-Extra: all
Requires-Dist: sandbroker[push,webauthn]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Dynamic: license-file

# sandbroker

A typed-return secret executor. Claude Code causes secrets to be **used**
without ever learning what they **are**.

**Full design, runbook, platform matrix, and discoveries log:**
[`~/dotfiles/docs/sandbroker-credential-broker.md`](../../dotfiles/docs/sandbroker-credential-broker.md)
(also on GitHub under `GraysonCAdams/dotfiles`). This file is the short version.

## The claim

> No plaintext secret resolved by sandbroker appears in Claude's context, in the
> Anthropic API request body, in the local session transcript, or in any prompt
> cache, because no channel exists through which it could travel.

It does **not** claim anything about secrets arriving through doors sandbroker
doesn't mediate. "100% secure" is not on offer; see Known limits in the doc.

## Why not a redaction hook

A `PostToolUse` redaction hook is a filter, and filters **fail open**: unmatched
pattern, unexpected encoding, or an unanticipated error string, and the raw value
proceeds to the wire. sandbroker is not a filter. The response schema has no field
a secret can occupy.

## How it holds

Four structural properties, all asserted in `tests/test_guarantee.py` (29 tests):

1. **The child cannot talk to us.** Targets spawn with stdout/stderr bound to
   `/dev/null` at the fd level, so no output on any path reaches the caller.
2. **Secrets never enter `argv`.** Rejected at registry load, because
   `/proc/PID/cmdline` exposes argv to the same uid.
3. **There is no shell.** Argv vectors only, so arguments can never become code.
4. **Egress is allowlisted.** Response keys outside `{ok, error, verb}` are
   dropped and unknown error tokens collapsed.

## Install

Two commands on Linux/WSL2 or macOS:

```bash
pipx install sandbroker        # puts sandbroker, sandbrokerd, and the bin/ helpers on PATH
sandbroker setup               # guided wizard: base + daemon, secret store, approver, Claude Code
```

`sandbroker setup` runs unprivileged (user mode) by default; add `--hardened` for
the dedicated-uid `/opt/sandbroker` system install. See
[`docs/integration-notes/setup-wizard.md`](docs/integration-notes/setup-wizard.md).

## Quick start from a checkout (Linux/WSL, systemd)

```bash
python3 tests/test_guarantee.py                 # expect OK
sudo ./install.sh                               # creates sandbroker uid + claude-broker
install -m 0755 bin/sandbroker ~/.local/bin/sandbroker
install -m 0755 hooks/deny_secret_reads.py ~/.claude/hooks/

# new login session for group membership, then:
sudo ./canary/canary_provision.sh
sandbroker canary.probe --ref file://canary      # expect {"ok": true}
sudo ./canary/canary_sweep.sh                   # expect clean
```

The hook still needs merging into `~/.claude/settings.json`; `jq` is absent on
WSL, so use the python snippet in the doc.

## Ref schemes

| Scheme | Backend |
| --- | --- |
| `file://canary` | local 0400 file, verification only |
| `akv://<vault>/<name>` | Azure Key Vault via `az` |
| `op://<vault>/<item>/<field>` | 1Password via `op` (macOS hosts) |

## Layout

```
sandbroker/sandbrokerd.py   the daemon: exec core, registry validator, egress filter
sandbroker/verbs.json    capability grants; installed root:root 0444
bin/sandbroker          the only interface Claude uses
hooks/                 PreToolUse deny for doors sandbroker doesn't mediate
canary/                provision + sweep: the leak test that must keep passing
tests/                 the guarantee, as executable assertions
install.sh             every sudo step (Linux/systemd)
```

## Verification note

**A sweep only means something when Claude runs the probe.** Running it yourself
proves nothing, because the canary was never near the model's context. Have
Claude exercise the verbs and attempt extraction, *then* sweep.
