Replies: 2 comments
-
Suggested fix: client-side encryption for vendor tokensThe infrastructure to do this properly already exists. Session data is E2E encrypted using the client's machineKey (AES-256-GCM) Vendor API tokens should use the same path. Proposed flow (client encrypts) Concrete changes
Using machineKey directly for vendor tokens creates domain overlap with session encryption. If a nonce were ever reused across
https://github.com/slopus/happy/blob/main/packages/happy-server/sources/app/api/routes/connectRoutes.ts#L258-L267 — POST endpoint: https://github.com/slopus/happy/blob/main/packages/happy-server/sources/app/api/routes/connectRoutes.ts#L281-L292 — GET endpoint: Same change for the bulk endpoint at
This is the part that breaks if you only do steps 1-3. Currently, when the mobile app triggers a remote session, the server The fix: the daemon already runs on the user's machine with full access to ~/.happy/access.key. Change the spawn-session flow so https://github.com/slopus/happy/blob/main/packages/happy-cli/src/daemon/run.ts — in spawnSession: The mobile app's spawn-session RPC would then only send { directory, agent } — no token field needed.
machineKey is per-machine, so encrypted tokens are per-machine. If a user has Desktop A and Desktop B, each encrypts independently. Each machine registers its own encrypted copy. This is arguably better security — compromising one machine's key doesn't expose MigrationRecommended: force re-registration. Invalidate all stored tokens and prompt users to re-run happy connect . This is a one-time action, avoids any vulnerability window, and is simple to implement. A server-assisted migration (server decrypts old tokens, returns to client, client re-encrypts) technically works but creates a window where plaintext tokens transit the network — which contradicts the narrative of the fix. Why this works
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
TLDR
The server operator (or anyone who compromises the server or its HANDY_MASTER_SECRET env var) can read every stored API key (Anthropic, OpenAI, Gemini).**
I (Well opus 4.6) did a source code review of Happy and wanted to flag something that I think the community should be aware of, especially anyone using happy connect with their Anthropic/OpenAI/Gemini API keys.
The claim
Happy's README and privacy policy emphasize "zero-knowledge encryption" and E2E encryption. This is true for session content (conversation data).
However, it is not true for your API keys when using the connect feature.
What actually happens
When you run happy connect claude (or openai/gemini), your API token is sent to the Happy server and encrypted with the server's own master secret — not your personal encryption key.
The server-side encryption key is derived from an environment variable the server operator controls:
https://github.com/slopus/happy/blob/main/packages/happy-server/sources/modules/encrypt.ts#L5-L10
Your API token is encrypted with this key and stored in the database:
https://github.com/slopus/happy/blob/main/packages/happy-server/sources/app/api/routes/connectRoutes.ts#L260
And the server can decrypt and return it as plaintext at any time:
https://github.com/slopus/happy/blob/main/packages/happy-server/sources/app/api/routes/connectRoutes.ts#L290
return reply.send({ token: decryptString(['user', userId, 'vendors', request.params.vendor, 'token'], token.token) });
This means the server operator (or anyone who compromises the server or its HANDY_MASTER_SECRET env var) can read every stored API key.
Why this matters
This is standard server-side encryption at rest — fine for many use cases, but it is not E2E encryption, and the distinction
matters. Users who read "zero-knowledge encryption" may reasonably assume their API keys are protected the same way their
conversation data is. They are not.
Additional context
The daemon feature also means a compromised Happy account can spawn Claude Code sessions on your machine remotely
(https://github.com/slopus/happy/blob/main/packages/happy-cli/src/daemon/run.ts#L219). The OAuth token is passed from the relay
server directly into the spawned process (https://github.com/slopus/happy/blob/main/packages/happy-cli/src/daemon/run.ts#L289).
This is a documented feature, not a bug, but it's worth understanding the trust model.
Discussion
Perhaps I'm missing something.
I'll send a separate comment to suggest how to fix this
Thanks to @sshoing for discovering this
Beta Was this translation helpful? Give feedback.
All reactions