---
slug: "bkramer-proton-cli"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/brandonkramer/proton-cli@main/README.md"
repo: "https://github.com/brandonkramer/proton-cli"
source_file: "README.md"
branch: "main"
---
# proton-cli

Unofficial unified Proton command-line client (**VPN + Authenticator + Contacts + Calendar + Drive + Settings + Mail**) with one install and shared sign-in UX.

> **Not an official Proton product.** Not affiliated with Proton AG.

## Install

Requires [Bun](https://bun.sh) ≥ 1.1 at runtime (even when installing via npm).

```bash
bun add -g @bkramer/proton-cli
# or
npm install -g @bkramer/proton-cli
```

From GitHub:

```bash
bun install -g github:brandonkramer/proton-cli
```

### From a clone

```bash
git clone https://github.com/brandonkramer/proton-cli.git
cd proton-cli
bun install
bun link
```

## Requirements

- Bun ≥ 1.1
- Proton account in [Single Password Mode](https://proton.me/support/single-password)
- TOTP if you use 2FA (FIDO2/security keys are not supported). Shared sign-in mints **all selected products** (default: all seven) — each product needs its **own fresh TOTP** (codes are single-use; one code cannot cover multiple mints)
- **VPN — WireGuard tools** (install tries this via Homebrew / winget; or `proton vpn setup`)
  - **macOS:** Homebrew → `wireguard-tools` (sudo for connect/disconnect)
  - **Windows:** WireGuard app via winget (Administrator terminal for connect/disconnect)
- **Authenticator — CAPTCHA (macOS):** native WKWebView helper, built on `postinstall` when possible (`bun run build:captcha` to retry; needs Xcode CLT)
- Optional: [Proton Pass CLI](https://protonpass.github.io/pass-cli/) (`pass-cli`) for credential injection

Close the Proton VPN desktop app before connecting so tunnels do not conflict.

On macOS, VPN connect/disconnect may ask for your **Mac login password** (sudo), not your Proton password.

CAPTCHA (if Proton requires it on sign-in): solve it in the **native WKWebView window**, not Safari/`verify.proton.me`.

## Agent plugins (Cursor / Codex / Claude / Pi)

This repo ships host plugin manifests and a thin MCP server (`proton mcp`) that shells out to the CLI in JSON/agent mode.

```bash
proton install-mcp --scope project --host all   # Cursor/Codex/Claude MCP + skill + Pi hint
proton install-mcp --scope user --host cursor
proton mcp                                      # stdio MCP (used by hosts)
```

| Host | Install |
|------|---------|
| Cursor | `proton install-mcp --host cursor` (user or project) — or load `.cursor-plugin/` |
| Codex | `proton install-mcp --host codex` — or load `.codex-plugin/` |
| Claude | `proton install-mcp --host claude` (project → `.mcp.json`; user → `claude plugin marketplace add` / install) — or load `.claude-plugin/` |
| Pi | `pi install git:github.com/brandonkramer/proton-cli` (skills via `"pi"` in package.json); wire `proton mcp` through your MCP adapter |

Sign-in / CAPTCHA stay interactive on a human TTY. Prefer curated MCP tools (mail/contacts/calendar/drive/vpn/settings reads + common writes) or `proton_cli` with `confirm=true` for non-reads. See `skills/proton-cli/SKILL.md`.

## Commands

Run `proton` with no args (TTY) for the interactive menu (VPN / Authenticator / Contacts / Calendar / Drive / Settings / Mail / sign-in).

Global options: `--json`, `-y` / `--yes`, `--sudo` (WireGuard on macOS).

### Shared

```bash
proton                            # interactive menu (TTY)
proton account pass://Vault/Item  # save default Pass login (password + TOTP)
proton account                    # show saved Pass ref / username
proton account --clear            # clear saved Pass ref
proton signin                     # uses saved Pass ref (or --pass / PROTON_PASS)
proton signin --pass "pass://Vault/Item"
proton signin --products vpn          # or auth / ctc / all
proton signin --partial-ok
proton status --json
proton signout
proton update --check
proton update
proton mcp                        # MCP stdio server for agents
proton install-mcp                # wire Cursor/Codex/Pi
```

With 2FA, `proton signin` / TUI **Sign in** need a **fresh TOTP per product** being minted. Prefer `proton account pass://Vault/Item` (or `--pass` / `PROTON_PASS`) so Pass supplies a new code for each mint. Requires `pass-cli` logged in. Sign-out keeps the saved Pass ref.

### VPN (`proton vpn …`)

```bash
proton vpn setup
proton vpn countries
proton vpn servers --country US
proton vpn connect --country US
proton vpn connect --city "New York"
proton vpn connect US#23
proton vpn connect --p2p
proton vpn status --json
proton vpn disconnect
```

| Flag | Meaning |
|------|---------|
| `--country <code>` | Exit country (e.g. `NL`) |
| `--city <name>` | City name |
| `--p2p` | P2P servers |
| `--securecore` | Secure Core |
| `--tor` | Tor over VPN |
| `--free-only` | Free-tier only |

Country / feature availability depends on your Proton plan.

### Authenticator (`proton auth …`)

E2EE TOTP/Steam seed sync and codes (Authenticator Key; client-side encrypt/decrypt).

```bash
proton auth sync
proton auth list
proton auth code github
proton auth status --output json
```

Product-only `proton vpn signin` / `proton auth signin` exist; prefer shared `proton signin`.

### Contacts (`proton contacts …`)

E2EE contact cards, groups, and pinned keys.

```bash
proton contacts list
proton contacts get alice
proton contacts create --name "Alice" --email alice@example.com
proton contacts groups list
proton contacts pin-key contact-id ./key.asc
```

Bare `proton` (TTY) opens a nested Contacts menu (list / add contact / groups / status). Prefer `proton contacts … --json` for scripting.

### Calendar (`proton calendar …`)

E2EE calendars and events.

```bash
proton calendar calendars list
proton calendar calendars create --name "Work" --color "#8080FF"
proton calendar events list --calendar primary
proton calendar events create --title "Standup" --start 2026-07-24T09:00 --duration 30m
proton calendar events respond EVENT_REF --status accept
```

Bare `proton` (TTY) opens a nested Calendar menu (list calendars / list events / add event / status). Prefer `proton calendar … --json` for scripting. Encrypted event operations need account password via `--password`, `--pass`, or `PROTON_PASSWORD`.

### Drive (`proton drive …`)

E2EE files, folders, sharing, trash, and photos.

```bash
proton drive status
proton drive items list
proton drive items upload ./file.txt /
proton drive folders create /Projects
proton drive share link /file.txt
proton drive trash list
proton drive photos list
```

Bare `proton` (TTY) opens a nested Drive menu (list items / upload file / list trash / status). Prefer `proton drive … --json` for scripting. Encrypted operations need account password via `--password`, `--pass`, or `PROTON_PASSWORD`.

### Settings (`proton settings …`)

Account and mail preference settings via Proton’s account/mail API (not Bridge IMAP/SMTP).

```bash
proton settings get
proton settings mail
proton settings set
proton settings set view-mode 1
proton settings set hide-remote-images 1 --dry-run
proton settings get --json
```

Sign in with `proton signin --products settings|set|all`. Writable keys include `view-mode`, `draft-type`, `hide-remote-images`, and others listed by bare `proton settings set`.

Bare `proton` (TTY) opens a nested Settings menu (account / mail / list keys / update). Prefer `proton settings … --json` for scripting.

### Mail (`proton mail …`)

E2EE list/read/search/send/organize via Proton Mail REST API (not Bridge IMAP/SMTP).

```bash
proton mail status
proton mail list
proton mail list --label sent --unread
proton mail sent                         # shortcut for --label sent
proton mail read MESSAGE_ID              # HTML → plain text in the terminal
proton mail read MESSAGE_ID --raw        # keep original HTML
proton mail search "invoice"
proton mail send --to alice@example.com --subject "Hi" --body "Hello"
proton mail send --to alice@example.com --subject "Hi" --body "Hello" \
  --attach ./file.pdf --attach ./notes.txt
proton mail organize read MESSAGE_ID
proton mail organize trash MESSAGE_ID
proton mail labels list
proton mail addresses list
```

Bare `proton` (TTY) opens a nested Mail menu (list inbox / list sent / search / compose / status). From a message you can reply / reply-all / forward. Compose can pick **To** from Contacts or type an address, and accept optional local attachment paths. Prefer `proton mail … --json` for scripting.

Read/send/decrypt need the account password via saved `proton account` / `PROTON_PASS` / `--pass`, or `--password` / `PROTON_PASSWORD`.

Sign in with `proton signin --products mail|all` (or full `proton signin`; Mail shares its session with Contacts/Settings).

## Proton Pass (optional)

If you use [Proton Pass CLI](https://protonpass.github.io/pass-cli/) (`pass-cli`):

```bash
pass-cli login   # once, if needed
proton account pass://Vault/Item   # recommended: persist default login + TOTP
proton signin                           # uses saved account ref
# or one-shot / env:
proton signin --pass "pass://Vault/Item"
export PROTON_PASS="pass://Vault/Item"
```

Also supported:

```bash
export PROTON_PASSWORD='pass://Vault/Item/password'
export PROTON_TOTP='pass://Vault/Item/totp'   # optional
pass-cli run -- proton signin
```

`Vault/Item` works too (`pass://` prefix optional). If several items share a title, the CLI prefers the one that has TOTP and stores a stable share/item ID. Env aliases: `PROTON_PASS`, `PROTONVPN_PASS`, `PROTONAUTH_PASS`, `PROTON_USERNAME`, `PROTON_PASSWORD`, `PROTON_TOTP`. Interactive prompts remain the default when Pass is unset. Never log resolved secrets. With 2FA, Pass (via `proton account` / `--pass`) supplies a **new** TOTP for each product mint.

## Agents / scripting

```bash
proton status --json
proton vpn status --json
proton vpn connect --json --country US
proton auth status --output json
proton auth code github --output json
```

| Flag / env | Meaning |
|---|---|
| `--json` / `PROTONVPN_JSON=1` | JSON on stdout (VPN / shared) |
| `--output json\|plain\|ink` / `PROTONAUTH_OUTPUT` | Authenticator output format |
| `-y` / `--yes` | Non-interactive confirms |
| `--sudo` | Allow interactive macOS sudo for WireGuard |
| `PROTON_AGENT=1` | Root `proton` agent-friendly (no accidental TUI) |
| `PROTONVPN_AGENT=1` | VPN agent mode (JSON-friendly; `sudo -n` only unless `--sudo`) |
| `PROTONAUTH_AGENT=1` / `CI=1` | Auth agent mode (default JSON; no CAPTCHA window / TUI) |
| `PROTONCONTACTS_JSON=1` / `PROTONCONTACTS_AGENT=1` | Contacts agent mode (JSON; no TUI) |
| `PROTONCALENDAR_JSON=1` / `PROTONCALENDAR_AGENT=1` | Calendar agent mode (JSON; no TUI) |
| `PROTON_DRIVE_JSON=1` / `PROTON_DRIVE_AGENT=1` | Drive agent mode (JSON; no TUI) |
| `PROTONSETTINGS_JSON=1` / `PROTONSETTINGS_AGENT=1` | Settings agent mode (JSON; no TUI) |
| `PROTONMAIL_JSON=1` / `PROTONMAIL_AGENT=1` | Mail agent mode (JSON; no TUI) |

VPN exit codes: `0` ok · `1` error · `2` usage · `3` not signed in · `4` privilege needed.

CAPTCHA never opens a window in agent mode (`captcha_required` — sign in interactively once, then reuse the session).

## Monorepo

| Path | Package | Owns |
|---|---|---|
| `packages/core` | `@bkramer/proton-core` | Shared config, dual-mint sessions, Pass helpers |
| `packages/vpn` | `@bkramer/proton-vpn` | WireGuard + vpn-api (`proton vpn …`) |
| `packages/authenticator` | `@bkramer/proton-authenticator` | E2EE TOTP/Steam sync (`proton auth …`) |
| `packages/contacts` | `@bkramer/proton-contacts` | E2EE contacts/groups (`proton contacts …`) |
| `packages/calendar` | `@bkramer/proton-calendar` | E2EE calendars/events (`proton calendar …`) |
| `packages/drive` | `@bkramer/proton-drive` | E2EE Drive files/folders/photos (`proton drive …`) |
| `packages/settings` | `@bkramer/proton-settings` | Account/mail API preferences (`proton settings …`) |
| `packages/mail` | `@bkramer/proton-mail` | E2EE Mail list/read/search/send (`proton mail …`) |
| `src/` | root bins | `proton` router + legacy wrappers |

Config root: `~/.config/proton-cli/` (`account.json` for the saved Pass ref; per-product sessions under `sessions/`).

## Shared session model

Most products use **different API hosts**, so they mint separate sessions. Exception: **Contacts, Settings, and Mail** all use `mail-api.proton.me` and share one session after a successful mint for any of them. `proton signin` still feels like one login: credentials (and Pass TOTP) are collected once, CAPTCHA is solved in the native window when required, then each remaining host is minted (with a fresh TOTP per mint).

## Agent skill

End-user usage skill for agents: [`skills/proton-cli/SKILL.md`](https://github.com/brandonkramer/proton-cli/blob/HEAD/skills/proton-cli/SKILL.md).

## License

GPL-3.0-or-later
