Metadata-Version: 2.5
Name: switchboard-connector
Version: 0.1.0
Summary: Switchboard: the local client (connector, harness adapters, mailbox) for the verifiable A2A directory
License-File: LICENSE.md
Requires-Python: >=3.12
Requires-Dist: a2a-sdk[http-server,postgresql]==1.1.2
Requires-Dist: cryptography<51,>=45
Requires-Dist: httpx<1,>=0.27
Requires-Dist: mcp==1.29.0
Requires-Dist: platformdirs<5,>=4
Requires-Dist: psycopg[binary]<4,>=3.1
Requires-Dist: pydantic<3,>=2
Requires-Dist: python-dotenv>=1.2.3
Requires-Dist: structlog>=26.1.0
Requires-Dist: uvicorn<1,>=0.35
Requires-Dist: websockets<17,>=15
Provides-Extra: qedb
Requires-Dist: sourcerykit<2,>=1.2.0b1; extra == 'qedb'
Description-Content-Type: text/markdown

# Switchboard

<div align="center">
  <a href="https://github.com/ProvablyAI/switchboard/actions/workflows/ci.yml">
    <img src="https://github.com/ProvablyAI/switchboard/actions/workflows/ci.yml/badge.svg" alt="CI" />
  </a>
  <a href="LICENSE.md">
    <img src="https://img.shields.io/badge/license-BSL%201.1-blue" alt="License: BSL 1.1" />
  </a>
</div>

Switchboard is a directory and an encrypted task mailbox for sharing selected
local agent sessions with permitted people in your workspace. Readable prompts,
answers and transcripts never leave the harness on the Mac that ran them — the
hosted service routes opaque ciphertext between permitted devices and stores
nothing it can read.

Switchboard is in alpha. Calls between separate signed-in Macs have completed
end to end. Treat every other path as pilot material.

## How it works

```mermaid
flowchart LR
    UI[Switchboard Web App<br/>or Desktop App]
    U[Caller asks a question] --> H[Caller harness<br/>any supported harness]
    H <-->|MCP over local stdio| L[Local MCP server]
    L <-->|local loopback| C[Caller Local Connector]
    UI -.->|local service health and sharing controls| L
    UI -.->|identity, pairing and sharing controls| M[Switchboard Network Backend<br/>directory, grants, opaque mailbox]
    C -->|signed encrypted envelope over HTTPS| M
    M -->|opaque ciphertext only| W[Owner Local Connector<br/>and mailbox worker]
    W -->|decrypt + recheck grant and policy| A[Local A2A task and harness adapter]
    A --> S[Owner source session<br/>or caller-isolated continuation]
    S --> A
    A -->|encrypted result follows the reverse path| H
```

| Component | What it does | Where |
|---|---|---|
| Switchboard Web App | Authenticated browser UI: directory, sharing, inbox, settings | [switchboard-control](https://github.com/ProvablyAI/switchboard-control) behind Cloudflare Access |
| Switchboard Desktop App | Companion app: onboarding, agents, contacts, wallet and service controls | `apps/desktop` (Tauri: Rust + React) |
| Switchboard Network Backend | Hosted control plane: identity, grants, opaque mailbox routing, credit ledger | [switchboard-control](https://github.com/ProvablyAI/switchboard-control), Docker + PostgreSQL |
| Local Connector | Discovers local harnesses, enforces the answer-only policy, runs the mailbox worker and local MCP server | `src/switchboard/local`, supervised per platform (LaunchAgent, systemd user service, Scheduled Task) |

## Getting started

### Open the dashboard

Your workspace administrator runs the Switchboard Network Backend and has the
dashboard URL. Sign in with a permitted account and continue to the workspace.
Workspace membership admits you to the directory; each agent's owner still
chooses who may call it.

### Set up a machine

The desktop app is the easy path: it checks for `uv`, installs the connector,
registers your harnesses, runs the Verified A2A sign-in, pairs the machine and
starts the resident service. No source checkout is involved.

```bash
cd apps/desktop && pnpm install && pnpm tauri build   # then open the built app
```

Prefer the terminal? The same steps, on macOS and Linux alike:

```bash
uv tool install "switchboard-connector[qedb]"        # installs the `switchboard` CLI
switchboard install --switchboard-url <url>          # harnesses, Verified A2A, resident service
switchboard pair --device-name "my machine"
```

The PyPI distribution is `switchboard-connector` (the name `switchboard` belongs
to an unrelated project); the command it installs is `switchboard`. Update it
later with `uv tool upgrade switchboard-connector`.

`install` sets up a LaunchAgent on macOS, a systemd user service on Linux and a
Scheduled Task on Windows; `switchboard start`, `stop`, `status` and `uninstall`
manage it afterwards. Only the pair sign-in and the Share approval are manual, by
design.

The control-plane address is set once per machine with
`switchboard install --switchboard-url <url>` and stored locally; the connector
worker and the harness-launched MCP both resolve that single setting, so they
cannot point at different servers.

### Linux notes

`switchboard install` writes `~/.config/systemd/user/switchboard.service`; run
`loginctl enable-linger "$USER"` to keep it running while you are logged out,
and `journalctl --user -u switchboard` to read its log. The device credential
and mailbox keys live in the macOS Keychain; on Linux they are `0600` files
under `~/.local/state/ProvablySwitchboard/`, protected by your disk encryption.

### Windows

Windows runs Switchboard natively: `switchboard install` registers a per-user
Scheduled Task that starts the connector at logon and restarts it if it fails,
needing no elevation. Credentials are files under your profile directory, where
Windows' own ACL keeps other standard users out. `switchboard start`, `stop`,
`status` and `uninstall` drive the task exactly as they drive the service
elsewhere.

Two caveats. Harness CLIs installed by npm or bun are `.cmd` shims, which
Switchboard runs through `cmd.exe` — this path gets less real-world use than the
Unix one, so report anything that misbehaves. And Hermes Desktop's automatic
gateway discovery needs `ps` and `lsof`, so on Windows set `HERMES_API_URL`
instead.

### Windows (WSL2)

WSL2 is the alternative, and the better-travelled one: it is the Linux path
above, with two requirements.

- **Enable systemd**, so `switchboard install` can manage the service. Add this
  to `/etc/wsl.conf`, then run `wsl --shutdown` from Windows:

  ```ini
  [boot]
  systemd=true
  ```

  Without it, `switchboard install` says so and you run `switchboard run` in the
  foreground instead.
- **Install your harness CLIs inside the same distribution.** Switchboard
  discovers sessions from the Linux home directory, so CLIs installed on Windows
  are invisible to it.

WSL forwards localhost, so the dashboard in a Windows browser still reaches the
approval bridge on `127.0.0.1:8766`.

### Platform support

| Platform | State |
|---|---|
| macOS | Supported: desktop app or CLI, LaunchAgent service, Keychain credentials |
| Linux | Supported: desktop app or CLI, systemd user service, file-backed credentials |
| Windows | Supported: desktop app or CLI, Scheduled Task service, file-backed credentials under the user profile. Less exercised than the others; WSL2 is the safe alternative |

### Self-host the control plane

The hosted control plane (identity, directory, mailbox routing, credit
ledger) lives in the separate
[switchboard-control](https://github.com/ProvablyAI/switchboard-control)
repo — see that repo's README for `docker compose up` self-hosting.

## Security model

- The Network Backend routes and revokes deliveries; it never sees a prompt,
  an answer or a transcript. Envelopes are opaque ciphertext at rest.
- Sharing is one atomic action: the owner's fixed answer-only policy installs
  locally before a share can be created, and the mailbox worker rechecks the
  grant and policy before every invocation.
- Revocation blocks future delivery immediately. It does not rewrite either
  person's local transcript.
- Verified A2A (optional, QEDB-backed) adds route-and-record-integrity proofs,
  shown as green, orange or red marks on an answer.
- Private caller (opt-in per share) shows the owner a caller-chosen alias
  instead of an account identity. The alpha credit ledger settles each answer
  in one double-entry transaction.

## Development

Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/). Verified A2A needs
the `qedb` extra, which pulls `sourcerykit` from PyPI.

```bash
uv sync
make check                      # pre-commit, full test suite, build — the CI gate
```

Common targets: `make lint`, `make format`, `make typecheck` (strict mypy on
`src`), `make test`.

```text
src/switchboard/
├── harness/          adapters for Codex, Hermes, Claude Code, OpenCode and OpenClaw
├── local/            the resident service: CLI, connector, approval bridge, policy
│   ├── owner_app/    the owner-local A2A app (task API, agent cards, state, web UI)
│   ├── mcp/          the harness-facing MCP server and its remote calls
│   ├── policy/       owner policy authoring and enforcement
│   └── skills/       the switchboard-ask skill installed into every harness
├── mailbox/          envelope crypto, client and worker, commitments, QEDB ledger
├── qedb/             A2A task store, task proofs and the A2A profile
└── verified_sources/ narrow read-only SourceryKit-backed sources
apps/desktop/         Tauri desktop companion app
scripts/              MCP launcher and the verified-A2A smoke test
docs/                 harness registration and session publishing
tests/                mirrors src/switchboard
```

The hosted control plane (`control/`) lives in the separate
[switchboard-control](https://github.com/ProvablyAI/switchboard-control) repo
— users installing this repo never pull down that server-side code.

### Releases

The connector and the desktop app ship separately, each from its own tag, so a
fix to one never forces a version bump of the other. Both refuse to run if the
tag disagrees with the version in the manifest, and both leave a **draft**
release for you to read before anyone sees it.

| What | Tag | Produces |
|---|---|---|
| Connector wheel | `connector-v0.1.0` (matches `pyproject.toml`) | `dist/*` on the release, then PyPI |
| Desktop app | `desktop-v0.1.0` (matches `tauri.conf.json`) | `.dmg`, `.deb`, `.AppImage`, `.msi`, `.exe` |

```bash
git tag connector-v0.1.0 && git push origin connector-v0.1.0
git tag desktop-v0.1.0   && git push origin desktop-v0.1.0
```

PyPI publishing waits on the `pypi` GitHub environment and uses trusted
publishing, so there is no token in the repo: configure the publisher once on
PyPI (project → Publishing → GitHub, workflow `release-connector.yml`,
environment `pypi`), and add a required reviewer there if you want publishing to
be a deliberate click.

Both workflows also run from the Actions tab, where they build and attach the
artifacts to the run without creating a release or publishing anything — that is
how to test a build, or to get a single platform's installer. The desktop one
asks which platforms to build; Tauri cannot cross-compile, so each runs on its
own runner.

Neither build is signed yet: macOS Gatekeeper blocks the `.dmg` and Windows
SmartScreen warns on the installer until signing and notarization are set up.

## License

Business Source License 1.1 — see [LICENSE.md](LICENSE.md). You may not offer
the Software as a commercial hosted service without a commercial license from
Provably Technologies Ltd. On the change date the license converts to
GPL-3.0-or-later.
