Metadata-Version: 2.5
Name: switchboard-connector
Version: 0.1.4
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. Download it from
[switchboard-releases](https://github.com/ProvablyAI/switchboard-releases/releases/latest);
after that it updates itself from its About page. To build it yourself instead:

```bash
cd apps/desktop && pnpm install
pnpm tauri build --config '{"bundle":{"createUpdaterArtifacts":false}}'   # 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 and stored locally; the
connector worker and the harness-launched MCP both resolve that single setting,
so they cannot point at different servers. The desktop onboarding offers
`https://switchboard.provably.ai` (or `SWITCHBOARD_URL`, if the app was launched
with it) under Advanced, checks that the server's `/health` answers before
installing anything, and stores it. From the terminal, pass
`switchboard install --switchboard-url <url>`. Without a stored address the
connector uses `SWITCHBOARD_URL`, and otherwise the same production default.

### 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. Bump the version first
and tag to match.

| What | Version in | Tag | Produces |
|---|---|---|---|
| Connector | `pyproject.toml` | `connector-v0.1.1` | A wheel and sdist, published to PyPI |
| Desktop app | `apps/desktop/src-tauri/tauri.conf.json` (keep `package.json` and `Cargo.toml` in step) | `desktop-v0.1.1` | A **draft** release in `switchboard-releases` |

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

Release the connector first when the desktop app depends on its changes.

**Connector.** The tag publishes straight to PyPI, with no draft to review.
Publishing uses trusted publishing through the `pypi` GitHub environment, 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 to that environment if you want a deliberate click
before anything goes out. Running the workflow from the Actions tab publishes
too.

**Desktop app.** Each OS builds on its own runner (Tauri cannot
cross-compile): `.dmg` for Apple silicon and Intel, `.deb`, `.rpm` and
`.AppImage`, `.msi` and `.exe`. A tag run first checks the tag against
`tauri.conf.json`, then creates one draft that every platform uploads to. A run
from the Actions tab (where you can pick one platform) only builds: the bundles
are attached to the run and no release is created.

Releases go to the public
[`ProvablyAI/switchboard-releases`](https://github.com/ProvablyAI/switchboard-releases)
repo, because the app's **Check for updates** (About page) downloads them
without credentials. Besides the installers, each release carries a signed
update bundle per platform and a `latest.json`. The app reads
`releases/latest/download/latest.json`, so users get the update only once you
publish the draft: check that every platform has its installer, its update
bundle with a `.sig`, and that `latest.json` is there.

One-time setup, already done for this repo:

- `ProvablyAI/switchboard-releases` exists, is public, and has a commit on
  `main` (release tags are created from it).
- `plugins.updater.pubkey` in `tauri.conf.json` holds the updater public key.
  The workflow refuses to build while it is the placeholder.
- Repo secrets: `TAURI_SIGNING_PRIVATE_KEY` and
  `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` (the updater private key and its
  password, generated with `pnpm tauri signer generate`), and `RELEASES_TOKEN`
  (a fine-grained token with *Contents: read and write* on
  `switchboard-releases` only).

The private key signs every future update. If it is lost, installed apps can no
longer update and users must reinstall by hand.

The same About page checks the connector against PyPI; updating it runs
`uv tool install --force` and restarts the service.

The installers are not code-signed yet: macOS Gatekeeper blocks the `.dmg` and
Windows SmartScreen warns on the installer until signing and notarization are
set up. (The updater signature above is separate from that.)

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