What the daemon exposes beyond chat relay #895
seth-with-zest
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Short version: The Happy daemon registers RPC handlers that give the relay server shell execution, file I/O, and filesystem browsing on your machine. Chat uses a separate channel and works without them. Users who only use the chat interface are carrying attack surface they don't need.
What I found
I use Happy as a chat interface to Claude Code from my phone. While checking whether my install was up to date, I had Claude Code audit the
happy-coderpackage. The finding: the daemon registers 7 RPC handlers that the relay server (api.cluster-fluster.com) can invoke on any connected machine.bashexec()readFilewriteFilelistDirectorygetDirectoryTreeripgrepdifftasticThese handlers are registered on the daemon side regardless of whether the mobile app's current UI exposes all of them. The app source in the monorepo has call sites for
bash(git status, CLI detection),readFile(file viewing), andripgrep(file search autocomplete). Three handlers (writeFile,listDirectory,getDirectoryTree) are exported but never called.difftasticis never called as an RPC.The commands are relayed through the Happy server — the app sends an RPC request, the server forwards it, the daemon executes it locally. Payloads are encrypted (NaCl/AES-256-GCM), but the server facilitates the key exchange.
The chat relay (
sendClaudeSessionMessage) is a completely separate code path. Disabling all 7 handlers has no effect on sending or receiving messages.Trust model
Running the Happy daemon means trusting the relay server with command execution on your machine. That's inherent to how the remote features work. The
bashhandler validatescwdagainst the working directory, but the command itself is unrestricted — any shell command is accepted.I may be missing context for why the architecture works this way — there could be good reasons I don't appreciate. But the surface area seems worth understanding regardless.
Disabling the handlers
Working with Claude Code, I put together a patch script that injects rejection stubs at the end of
registerCommonHandlers(), overwriting the original handlers viaMap.set()semantics. All 7 handlers return{ success: false, error: "Handler disabled" }.After auditing the app source (
packages/happy-app/sources/):The patch must be reapplied after
npm updatesince it modifies build artifacts. I run it as part of a nightly update job. Happy to share the script if useful.Other findings
~/.happy/access.keyis created with 644 permissions. Contains the JWT auth token and NaCl keys. Should be 600.~/.happy/logs/stores decrypted conversation content in plaintext with no rotation. Accumulated ~1GB over 5 weeks.Suggestions
~/.happy/access.keywith 600 permissions.~/.happy/logs/, or document that conversations are stored locally in plaintext.git status,git diff,command -v, env var echo). An allowlist would preserve those features while closing arbitrary execution.Beta Was this translation helpful? Give feedback.
All reactions