Systematic study of existing open-source Proton client projects to extract patterns, pitfalls, and reusable code for hermes-proton. Projects analyzed: - hydroxide (Go, SRP, CardDAV/IMAP/SMTP) — complete auth+session ref - rclone protondrive (Go, Drive only) — encryption patterns - roman-16/proton-cli (Go, all 5 products) — 3-tier architecture ref - bscott/pm-cli (Go, Bridge mail) — agent-friendly gold standard - StollD/proton-webdav-bridge (Go, Drive as WebDAV) — caching pattern - cdump/proton-tui (Rust, VPN) — standalone SRP auth module - rvacyber/openclaw-protonmail-skill (TS, Bridge mail) — skill pattern ref Feeds into T1 (hannibal architecture). Closes t_3c87cd08
26 KiB
Proton Third-Party Client Landscape
Landscape scan of 7 open-source Proton client implementations — patterns, pitfalls, and reusable code for hermes-proton. Compiled: 2026-06-08 Analyst: Face (kanban t_3c87cd08)
Overview
| # | Project | Lang | Stars | Auth | Scope | Key Value for Hermes-Proton |
|---|---|---|---|---|---|---|
| 1 | hydroxide (emersion) | Go | 2.1k | SRP direct | Mail, Contacts, Calendar | Complete SRP+2FA+refresh implementation; bbolt caching; NaCl secretbox session persistence |
| 2 | rclone protondrive | Go | 57k | SRP direct | Drive only | Battle-tested Drive encryption (4MB blocks, X25519, gopenpgp v2); most-used third-party client |
| 3 | proton-cli (roman-16) | Go | 17 | SRP direct | All 5 products | Most architecturally mature; clean 3-tier layering; session cache; REF ID system; error classification |
| 4 | pm-cli (bscott) | Go | 14 | Bridge pass | Mail only | Agent-friendly gold standard: --json everywhere, --help-json, idempotency keys, semantic summaries |
| 5 | proton-webdav-bridge (StollD) | Go | 28 | SRP direct | Drive as WebDAV | In-memory tree cache + 5s event poll; Drive encryption via go-proton-api fork (archived) |
| 6 | proton-tui (cdump) | Rust | 12 | SRP direct | VPN only | Standalone SRP-6a auth module (reusable Rust crate); WireGuard key conversion; VPN lifecycle |
| 7 | openclaw-protonmail-skill (rvacyber) | TS | 16 | Bridge pass | Mail only | Closest analogue to Hermes skill pattern; IMAP/SMTP via Bridge; env-var config; search sanitization |
1. Project-by-Project Analysis
1.1 hydroxide (emersion/hydroxide)
Location: github.com/emersion/hydroxide
Stars: 2.1k | License: MIT | Language: Go 1.24
Auth Approach — Full SRP Direct (No Bridge Dependency)
AuthInfo(username) → POST /auth/info → {srpSession, Modulus (PGP-signed), ServerEphemeral, Salt}
srp(password, info) → bcrypt (cost 10) + expandHash (4×SHA-512) + generateProofs (2048-bit, LE)
Auth(username, password, info) → POST /auth → verify server proof (constant-time compare)
AuthTOTP(code) → POST /auth/2fa (bitmap check: Enabled != 0)
Unlock(auth, keySalts, passphrase) → decrypt keyring with password/token
- SRP implementation location:
protonmail/srp.go— 2048-bit group, little-endian byte order matching Proton web client - Password hashing:
protonmail/password.go—hashPassword(): version 3/4 appends "proton" to salt, bcrypts (cost 10), runsexpandHash()— 4 rounds SHA-512 with suffix bytes 0-3 - Modulus trust: Hardcoded
modulusPubkeyPGP key in source; modulus response is clearsigned and verified - 2FA: TOTP only; bitmap check (
Enabled != 0notEnabled == 1); U2F/WebAuthn not implemented - Re-auth chain:
AuthRefresh()→ POST/auth/refresh; on code 10013 (invalid refresh token) → fallback to full SRP Auth (fails if 2FA enabled)
Token Persistence — NaCl Secretbox
- Storage:
~/.config/hydroxide/auth.json - Format: JSON map
map[string]string(username → base64-encrypted blob) - Encryption: NaCl secretbox (XSalsa20-Poly1305) with random 24-byte nonce
- Key: Random 32-byte bridge password (base64), shown once at login, NOT stored by hydroxide
- CachedAuth struct:
{Auth (UID, AccessToken, RefreshToken, ExpiresIn), LoginPassword, MailboxPassword, KeySalts}
Error Handling
| Pattern | Details |
|---|---|
| 401 auto-retry | Client.do() — single retry with ReAuth callback |
| APIError struct | {Code int, Message string} — wrapped from RawAPIError |
| Refresh failure | Code 10013 → full re-auth via SRP (2FA blocks this) |
| 429 rate limit | Not handled — no retry-wait logic (gap for production) |
Crypto
github.com/ProtonMail/go-crypto v1.4.1(Proton's OpenPGP fork with SHA-512 support)- Message encryption:
Message.Encrypt(to, signed)→ armored PGP viaopenpgp.SymmetricallyEncrypt - Attachment crypto:
Attachment.GenerateKey()→ AES-256 key → symmetrically encrypted - Contact crypto:
NewEncryptedContactCard,NewSignedContactCard— dual-card scheme - Key derivation:
computeKeyPassword()→ bcrypt, strip prefix (last 29 bytes)
Project Structure
protonmail/ # Core API client (18 files)
├── auth.go # Auth flows (AuthInfo, Auth, AuthTOTP, AuthRefresh, Unlock, Logout)
├── srp.go # SRP-6a protocol
├── password.go # Key derivation (bcrypt + SHA-512)
├── crypto.go # OpenPGP operations
├── messages.go # Message CRUD + Encrypt/Read
├── events.go # Event polling (GetEvent)
└── ...
auth/auth.go # Session persistence, Manager, re-auth, 2FA detection
imap/ # IMAP server backend + bbolt database
smtp/ # SMTP backend (MIME → PGP → send)
carddav/ # CardDAV handler
cmd/hydroxide/ # CLI entry (11 subcommands)
Key takeaway: Most complete SRP+session management reference. The NaCl secretbox pattern for encrypted token storage is directly adoptable.
1.2 rclone protondrive backend
Location: github.com/rclone/rclone/tree/master/backend/protondrive
Stars: 57k (rclone project) | License: MIT | Language: Go
Auth Approach — SRP Direct (via archived bridge library)
Uses henrybear327/Proton-API-Bridge (archived Feb 2026) and henrybear327/go-proton-api (archived Feb 2026). Both are single-contributor projects with no upstream development.
- SRP auth via
ProtonMail/go-srp v0.0.7 - Auth flow: Login → API client creation → keyring unlock → Drive backend init
- SaltedKeyPass caching: After first login, the salted key pass is cached so subsequent operations don't need password
- authHandler callback pattern: Transparent token refresh middleware
Encryption — gopenpgp v2 (X25519)
- Key type: X25519 (Curve25519-based OpenPGP keys)
- Block size: 4MB blocks
- Encryption: OpenPGP CFB mode per block
- Hashing: SHA-256 block hashes + armored detached signatures per block; SHA-1 content digests (Proton legacy)
- Bootstrap key: HKDF from user key → address key → share key → node key → session key
- Key library:
github.com/ProtonMail/gopenpgp/v2 v2.10.0
Token Management
- Config stores: UID + AccessToken + RefreshToken + SaltedKeyPass
configKeyconstants hidden in config file- Auto-refresh handled by bridge library (opaque to rclone backend)
Error Handling
| Pattern | Details |
|---|---|
| shouldRetry | Catches API Code=200501 (rate limit?) and 5xx server errors |
| HTTP retry | Deferred to resty (rclone's HTTP client) |
| Sentinel errors | 17 typed error constants in Proton-API-Bridge |
⚠️ Critical Risk
Both henrybear327/Proton-API-Bridge and henrybear327/go-proton-api are archived (Feb 2026). rclone vendors them as-is. Any new project relying on this code path inherits the risk of unpatched bugs and API breakage.
1.3 proton-cli (roman-16/proton-cli)
Location: github.com/roman-16/proton-cli
Stars: 17 | License: MIT | Language: Go 1.26.1 | Version: v1.4.0 (May 2026)
Architecture — 3-Tier Layered (Best in Class)
cmd/ # Cobra CLI commands (per-product packages)
internal/
api/ # HTTP client, SRP auth, error types, HV resolver
services/ # Domain services (mail, drive, calendar, contacts, pass) — per-product packages
app/ # Wiring container (App struct holding 5 services + API + Renderer)
session/ # Session persistence (~/.config/proton-cli/sessions/<profile>.json)
config/ # TOML multi-profile config
keys/ # PGP key hierarchy unlock
pgp/ # Card encryption (clear/signed/encrypted/encrypted+signed)
aead/ # AES-256-GCM for Pass items/vaults
render/ # Output formatting (text/JSON/YAML/tables/progress)
idcache/ # Short-ID → full ID cache
hv/ # Embedded webview CAPTCHA helper
The App wiring pattern is worth adopting directly:
type App struct {
Profile string
Creds Credentials
API *api.Client // Single session, shared across all 5 services
Mail *mail.Service
Drive *drive.Service
Calendar *calendar.Service
Contacts *contacts.Service
Pass *pass.Service
R *render.Renderer
IDCache *idcache.Cache
}
Auth Approach — Full SRP (Not Bridge)
POST /auth/v4/sessions(creates unauth session)POST /core/v4/auth/info→go-srpproofsPOST /core/v4/auth→ verify server proof (MITM prevention)POST /core/v4/auth/2faif bit 0 set inTwoFA.Enabled- HV (CAPTCHA) via embedded webview helper binary
- Dependencies:
go-srp v0.0.7,gopenpgp/v2 v2.10.0
Session Management
- Storage:
~/.config/proton-cli/sessions/<profile>.json(0600) - SaltedKeyPass caching: Stored in session file → subsequent invocations don't need password
- Auto-refresh:
api.Client.Do()callsPOST /auth/v4/refreshon 401, persists viasession.Save() - Thread safety:
sync.RWMutexon client for token reads/writes - Multi-profile: Each profile has independent session + config
Error Handling — Typed Hierarchy with Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | User error |
| 2 | Auth failure |
| 3 | Not found |
| 4 | Ambiguous/conflict |
| 5 | Network/server error |
| 130 | Cancelled (Ctrl+C) |
Hierarchy: APIError → HumanVerificationError (code 9001) → ExitError → WrongTableError (cross-table probing for message↔conversation confusion) → ErrNotFound / AmbiguousError
Notable: Cross-table probing on 422 errors — if a GET fails, it probes the opposite table (message↔conversation) and returns a precise diagnostic.
Key Design Decisions
- Single HTTP client shared across all 5 products — no per-service auth
- App-in-context pattern —
*app.Appstored incontext.Contextviaapp.WithApp(), avoids global state - REF ID resolution — prefix matching (8+ chars) from idcache; ambiguous → exit 4 with candidates
- Dashed ID protection — auto-inserts
--before Proton base64 IDs starting with- - Render abstraction —
render.Rendererhandles text/JSON/YAML; commands never callfmt.Println - Dry-run on mutations — global
--dry-runflag checked by all mutation commands
Notable Gaps
- No OAuth — env-var or config-file passwords only
- No structured logging —
slogto stderr only; no log file/rotation - CAPTCHA webview only — no stdin-based email/SMS verification
1.4 pm-cli (bscott/pm-cli)
Location: github.com/bscott/pm-cli
Stars: 14 | License: MIT | Language: Go | Version: v0.2.5 (April 2026)
Architecture — Bridge Protocol Only
- Does NOT call Proton API directly — connects to local Proton Bridge daemon
- IMAP:
127.0.0.1:1143(STARTTLS) — read/search mail - SMTP:
127.0.0.1:1025(STARTTLS) — send mail - Auth: Bridge-generated app password (not Proton password)
- TLS:
InsecureSkipVerify: true(Bridge self-signed cert), v0.2.5 enforces loopback-only
Agent-Friendly Features (Gold Standard for Hermes)
| Feature | Implementation | Value for Agents |
|---|---|---|
Universal --json |
Formatter struct threaded through all commands; {"success": true, "data": {...}} envelope |
Always parseable, always on stdout |
--help-json |
Full typed command tree as JSON (flags, args, types, examples) | Zero-parsing schema introspection in 1 call |
| Idempotency keys | --idempotency-key on mail send/reply/forward; 24h TTL in ~/.config/pm-cli/idempotency.json |
Safe retry without double-send |
| Semantic AI commands | mail summarize (sentiment, priority, action_required, summary); mail extract (emails, URLs, dates, phone_numbers, action_items) |
Pre-digested LLM input |
| Consistent error envelope | {"success": false, "error": "..."} on stdout |
Always parseable even on failure |
| Stable IDs | Both seq numbers (123) and UIDs (uid:456) in output |
Stable across invocations |
| Watch + env vars | mail watch --exec exposes metadata as PM_MSG_* env vars |
Safe agent automation |
Error Handling
- Two-mode: human (stderr, color) vs JSON (stdout, structured)
- Descriptive messages with actionable guidance
- Exit code 1 on any error
- Wrapped errors via
%w
Security Hardening (v0.2.5)
- SMTP Header Injection —
SanitizeHeaderValue()strips CR/LF - TLS Loopback Enforcement —
isLoopbackHost()refuses non-localhost - Shell Injection — env vars replace string interpolation in watch mode
- ANSI Escape Injection —
SanitizeForTerminal()strips escapes
Gaps
- Bridge required — doesn't work without Bridge daemon
- Mail only — no Calendar, Drive, Pass, VPN
- Polling only — 30s poll interval, no IMAP IDLE
- Single account — one config at a time
- Local-only — can't be remote/multi-user
1.5 proton-webdav-bridge (StollD)
Location: github.com/StollD/proton-webdav-bridge
Stars: 28 | License: MIT | Language: Go
Architecture
- Purpose: Exposes Proton Drive as a local WebDAV filesystem
- Daemon pattern: Registers WebDAV handler on localhost:7984, proxies Drive operations
- Auth: SRP direct via
henrybear327/go-proton-api(same archived fork as rclone)
Caching — Pure In-Memory
- Two
map[string]*Link— one for ID lookups, one for path lookups - No disk persistence — full re-fetch on restart
- Event poll: Background goroutine polls Proton share events every 5 seconds via
TriggerUpdate() - Synchronous cache refresh before every download/upload
Encryption
- Same gopenpgp/v2 stack as rclone (X25519)
- Full key hierarchy: User Key → Address Key → Share Key → Node Key → Session Key
- Reader decrypts blocks on-demand with SHA-256 hash verification
- Writer encrypts in 4MB blocks with SHA-1 content hashing (Proton API validation)
Critical Issues
- Archived dependencies — both
henrybear327/go-proton-apiandStollD/proton-driveare single-contributor, archived panic(err)on init failures — no graceful degradation- Headless auth unsupported — interactive
--loginonly - Token expiry =
os.Exit(1)— hard abort
1.6 proton-tui (cdump)
Location: github.com/cdump/proton-tui
Stars: ~12 | License: MIT | Language: Rust | Version: v0.3.0
Auth — Standalone Rust SRP-6a Implementation
The auth.rs module (~500 lines) is the single most reusable piece for Hermes-proton.
Flow:
POST /auth/info→ extract modulus from PGP signed response bodySrpClient::new(modulus)→ generate random 256-bita- Password hashing: bcrypt (cost 10,
$2y$) +pm_hash()(4×SHA-512 extend to 2048 bits) - SRP-6a math:
u = H(A,B),S = (B - k*g^x)^(a+u*x) mod N, client proofM = H(A,B,K) POST /auth→ verify, handle 2FA
Reusable functions:
pm_hash— Proton's custom 2048-bit hash (4 SHA-512 rounds with suffixes 0x00-0x03)hash_password— bcrypt + PMHash combinedverify_server_proof— constant-time comparison- PGP modulus extraction — parse clearsigned response
Dependencies: num-bigint, sha2, bcrypt, base64, reqwest — all standard crates
VPN Connection — WireGuard Only
- Key generation: Ed25519
SigningKey→ X25519 via SHA-512 + bit clamping - Certificate registration:
POST /vpn/v1/certificatewith ED25519 public PEM - Connection:
sudo wg-quick up <config>with runtime/saved config targets - Disconnection:
sudo wg-quick down <config>
Token Management
- Storage:
~/.config/proton-tui/tokens.json(0o600 on Unix) - Struct:
StoredTokens { uid, access_token, refresh_token } - Clear: Menu option (deletes file)
- Conversion:
impl From<AuthResult> for StoredTokens
1.7 openclaw-protonmail-skill (rvacyber)
Location: github.com/rvacyber/openclaw-protonmail-skill
Stars: 16 | License: MIT | Language: TypeScript | Version: v1.0.1 (2026-04)
Skill Structure — Direct Hermes Reference
SKILL.md # OpenClaw manifest (YAML frontmatter)
src/
index.ts # ProtonMailSkill class — orchestrator
imap.ts # IMAPClient — read/search operations
smtp.ts # SMTPClient — send/reply operations
tools.ts # OpenClaw tool registrations + TOOL_DEFINITIONS
bin/protonmail # CLI entry (Node.js shebang)
Bridge Connection Pattern (Directly Transferable)
// IMAP (read path) — 127.0.0.1:1143, TLS, rejectUnauthorized: false
// SMTP (write path) — 127.0.0.1:1025, STARTTLS (secure: false), rejectUnauthorized: false
// Always enforce localhost-only host validation
Operations
| Method | What It Does | Key Implementation Detail |
|---|---|---|
listInbox(limit, unreadOnly) |
List recent messages | Search ALL or UNSEEN, sort by UID desc, fetch headers |
search(query, limit) |
Search with syntax | Parse from:, subject:, body:, newer_than: filters |
readMessage(messageId) |
Full email content | UID fetch + mailparser; messageFound + fetch.end hang prevention |
send(to, subject, body, options) |
Send new email | Plain text + optional HTML/CC/BCC/attachments via nodemailer |
reply(originalMessage, body) |
Reply with threading | RFC 5322 In-Reply-To/References headers |
Search Sanitization (Security Pattern)
- Length limit: 200 chars
- Character allowlist:
[a-zA-Z0-9@._+\-\s:] - No CR/LF/control chars
- Unrecognized queries → safe subject keyword search fallback
Config & Env
# SKILL.md metadata
requires:
env: [PROTONMAIL_ACCOUNT, PROTONMAIL_BRIDGE_PASSWORD]
PROTONMAIL_ACCOUNT=user@pm.me
PROTONMAIL_BRIDGE_PASSWORD=<bridge-app-password>
Transferable config pattern: env vars with config-override fallback; same naming scheme for Hermes (HERMES_PROTON_ACCOUNT, HERMES_PROTON_BRIDGE_PASSWORD).
Security Evolution (v0.1.0 → v0.1.1)
- v0.1.0: TLS bypass without localhost enforcement
- v0.1.1: Hosts validated against
['127.0.0.1', 'localhost', '::1'] - Hermes-proton must follow the hardened pattern
2. Cross-Cutting Patterns
2.1 Auth Strategy Comparison
| Approach | Projects | Pros | Cons |
|---|---|---|---|
| SRP Direct | hydroxide, rclone, proton-cli, proton-webdav, proton-tui | No dependency; full API access; all products | Complex implementation; 2FA handling; CAPTCHA required; token refresh logic |
| Bridge Protocol | pm-cli, openclaw-skill | No crypto needed; no 2FA; no CAPTCHA; stable IMAP/SMTP | Mail only; Bridge dependency; self-signed TLS; local process only |
Recommendation for Hermes-proton: Support both paths — Bridge for mail (fastest path, proven), SRP for Calendar/Drive/Pass/VPN (via go-proton-api or proton-cli patterns).
2.2 SRP Implementation Details (Consensus Across Projects)
- Go standard:
ProtonMail/go-srp— official library, used by 4/4 Go projects - Rust alternative:
num-bigint+sha2+bcrypt— standalone impl in proton-tui/auth.rs - Key derivation: bcrypt (cost 10,
$2y$) → 4×SHA-512 (expand to 2048 bits) → modulus operations - Modulus verification: PGP clearsigned modulus response verified against Proton's hardcoded public key
- 2FA: Bitmap check (
Enabled & 1); TOTP only in all projects - CAPTCHA: Only roman-16/proton-cli has a solution (embedded webview helper)
2.3 Token/Session Persistence Patterns
| Project | Location | Encryption | Format |
|---|---|---|---|
| hydroxide | ~/.config/hydroxide/auth.json |
NaCl secretbox (XSalsa20-Poly1305) | JSON map |
| proton-cli | ~/.config/proton-cli/sessions/<profile>.json |
Filesystem perms (0600) | JSON |
| proton-webdav | $XDG_DATA_HOME/proton-webdav-bridge/tokens.json |
Filesystem perms (0600) | JSON |
| proton-tui | ~/.config/proton-tui/tokens.json |
Filesystem perms (0600) | JSON |
| pm-cli | OS keyring (libsecret/Keychain) | Platform keyring | N/A |
Recommendation: Use OS keyring for Bridge password (pm-cli pattern); encrypt cached tokens with NaCl secretbox if stored on disk (hydroxide pattern).
2.4 Error Handling Patterns
| Pattern | Found In | Details |
|---|---|---|
| 401 auto-retry with refresh | hydroxide, proton-cli, rclone | Single retry with ReAuth callback |
| Typed error hierarchy | proton-cli, pm-cli, hydroxide | APIError, ExitError, WrongTableError |
| Cross-table probing | proton-cli | 422 → probe opposite table |
| Exit code mapping | proton-cli | 0-130 typed codes |
| Structured JSON errors | pm-cli | {"success": false, "error": "..."} |
| 429 not handled | All projects | Gap for production — no project implements 429 backoff |
2.5 Crypto Stack
| Component | Standard Library | Used By |
|---|---|---|
| OpenPGP (X25519) | go-crypto / gopenpgp v2 |
hydroxide, rclone, proton-cli, proton-webdav |
| Symmetric encryption | NaCl secretbox (XSalsa20-Poly1305) | hydroxide (session storage) |
| AES-256-GCM | Standard library | proton-cli (Pass items) |
| Card encryption | OpenPGP + signing | proton-cli, hydroxide |
| Key derivation | bcrypt + SHA-512 | All SRP-based projects |
3. Critical Risks
3.1 Archived Dependencies
Two projects that use SRP direct for Drive (rclone protondrive, proton-webdav-bridge) depend on archived libraries:
henrybear327/Proton-API-Bridge— archived Feb 2026henrybear327/go-proton-api— archived Feb 2026StollD/proton-drive— single-contributor
Mitigation: Use roman-16/proton-cli's approach instead (direct go-proton-api from ProtonMail, or use the official Proton Go SDK).
3.2 No 429 Handling
None of the 7 projects implement explicit rate-limit backoff. For a production agent making many API calls, this is a gap that must be filled.
3.3 Headless CAPTCHA
Only roman-16/proton-cli handles CAPTCHA (via embedded webview helper). For headless agent environments, CAPTCHA is a blocker unless using the Bridge path (which bypasses CAPTCHA entirely).
3.4 2FA Limitations
- Token refresh failure + 2FA enabled = cannot auto-re-auth (hydroxide)
- Only TOTP supported; U2F/WebAuthn unimplemented in all projects
- Bridge path avoids 2FA entirely
4. Recommendations for Hermes-Proton
4.1 Architecture
Adopt roman-16/proton-cli's 3-tier layered architecture:
- Plugin layer (Komodo plugin) — auth lifecycle, session management, shared state
- Service layer (Hermes skills) — per-product tool collections
- Transport layer — Bridge (mail) + SRP direct (Calendar/Drive/Pass/VPN)
4.2 Auth Paths
Path A: Bridge (mail only)
- OpenClaw-skill pattern: IMAP
127.0.0.1:1143+ SMTP127.0.0.1:1025 - Env vars:
HERMES_PROTON_ACCOUNT,HERMES_PROTON_BRIDGE_PASSWORD - Localhost-enforced TLS with self-signed cert acceptance
- OS keyring for credential storage (pm-cli pattern)
Path B: SRP Direct (all products)
- roman-16/proton-cli's auth flow as reference
go-srpfor Go-based components- proton-tui's
auth.rsfor Rust-based components - NaCl secretbox encrypted token cache (hydroxide pattern)
- Cross-table error probing for ID resolution
4.3 Agent-Friendly Output (pm-cli patterns)
- Universal
--jsonflag with{"success": true, "data": {...}}envelope --help-jsonfor tool introspection- Idempotency keys for mutation operations
- Semantic subcommands (
summarize,extract) - Consistent exit codes
4.4 Must-Avoid
- Do NOT vendor archived
henrybear327libraries (use officialProtonMail/go-srp+go-proton-api) - Do NOT skip 429 handling
- Do NOT allow non-localhost TLS bypass (follow openclaw-skill's hardening)
- Do NOT hard-code single-protocol assumptions (support both Bridge and SRP)
4.5 Key File Locations to Reference
| Pattern | Source Project | File |
|---|---|---|
| SRP auth (Go) | hydroxide | protonmail/auth.go, protonmail/srp.go |
| SRP auth (Go, idomatic) | proton-cli | internal/api/auth.go |
| SRP auth (Rust) | proton-tui | src/auth.rs |
| NaCl session storage | hydroxide | auth/auth.go |
| Bridge IMAP client | openclaw-skill | src/imap.ts |
| Bridge SMTP client | openclaw-skill | src/smtp.ts |
| Agent-friendly output | pm-cli | internal/output/formatter.go |
| Error classification | proton-cli | internal/app/app.go |
| Card encryption | proton-cli | internal/pgp/cards.go |
| Drive encryption | proton-webdav | entire proton-drive library |
| Session persistence | proton-tui | src/tokens.rs |
| Multi-profile config | proton-cli | internal/config/ |
5. Dependency Matrix for Hermes-Proton
| Component | Recommended Library | Version | Source |
|---|---|---|---|
| Go SRP | github.com/ProtonMail/go-srp |
v0.0.7 | Official |
| Go API Client | github.com/ProtonMail/go-proton-api |
latest | Official |
| OpenPGP/Crypto | github.com/ProtonMail/go-crypto |
v1.4.1 | Official |
| Go OpenPGP (high-level) | github.com/ProtonMail/gopenpgp/v2 |
v2.10.0 | Official |
| Rust SRP | num-bigint + sha2 + bcrypt |
latest | proton-tui auth.rs pattern |
| Node IMAP | imap |
latest | openclaw-skill pattern |
| Node SMTP | nodemailer |
latest | openclaw-skill pattern |
| Node Mail Parse | mailparser |
latest | openclaw-skill pattern |
| OS Keyring (Go) | zalando/go-keyring |
latest | pm-cli pattern |
| Embedded DB | go.etcd.io/bbolt |
v1.4.3 | hydroxide pattern |
| Webview (Go) | webview/webview | — | proton-cli HV pattern |