Back to Articles

Happy: Turn Claude Code Into a Mobile-Controlled AI Agent With E2E Encryption

[ View on GitHub ]

Happy: Turn Claude Code Into a Mobile-Controlled AI Agent With E2E Encryption

Hook

What if you could approve Claude Code’s file changes from your phone while getting coffee, without your proprietary codebase ever touching a server unencrypted? That’s the premise behind Happy, a TypeScript wrapper that transforms desktop-only AI coding tools into remotely controllable agents.

Context

Claude Code and OpenAI’s Codex represent a new generation of autonomous AI coding assistants that can make multi-file changes, execute commands, and work through complex programming tasks. Unlike traditional autocomplete tools, these agents pause for human approval before destructive operations, making them powerful but desk-bound—you need to be physically present at your terminal to approve changes or handle errors.

This creates a workflow problem for long-running AI tasks. Start an agent refactoring your authentication system, step away for lunch, and return to find it’s been waiting 45 minutes for permission to modify a database migration. The original tools provide no mobile access, no push notifications, and no way to hand off control between devices. Happy emerged to solve this specific pain point: enabling mobile monitoring and control of desktop AI coding sessions while maintaining security through end-to-end encryption. Rather than forking or reimplementing these tools, Happy wraps them as a transparent proxy, routing I/O through an encrypted relay server that mobile and web clients can access.

Technical Insight

happy claude command

spawn child process

stdout/stderr

stdin

generate keypair

QR code with public key

peer public key

derive shared secret

encrypt with AES-256-GCM

encrypted message + sessionID

route by sessionID

decrypt with shared secret

User Terminal

Happy CLI Wrapper

Claude Code Process

Crypto Layer

Relay Server

Mobile App

System architecture — auto-generated

Happy’s architecture is built around transparent CLI interception and encrypted message routing. The core innovation is a TypeScript wrapper that sits between you and the original Claude Code or Codex CLI, capturing stdin/stdout while preserving the exact behavior of the underlying tool. When you invoke happy claude, it spawns the actual Claude Code process as a child, pipes communication through an encryption layer, and simultaneously broadcasts to any connected remote clients.

The encryption scheme uses asymmetric cryptography with per-session key pairs. When you initialize Happy CLI, it generates an ECDH key pair and displays a QR code containing the public key and session identifier. Your mobile app scans this QR code, generates its own key pair, and establishes a shared secret via key exchange. All subsequent messages are encrypted with AES-256-GCM before being sent to the relay server, which routes them based on session ID without ever accessing plaintext:

// Simplified encryption flow from the CLI package
import { generateKeyPair, deriveSharedSecret } from './crypto';
import { encryptMessage } from './encryption';

class HappySession {
  private keyPair: KeyPair;
  private peerPublicKey?: string;
  private sharedSecret?: Buffer;

  async initSession(sessionId: string): Promise<string> {
    this.keyPair = await generateKeyPair();
    const qrData = {
      sessionId,
      publicKey: this.keyPair.publicKey,
      serverUrl: process.env.HAPPY_SERVER_URL
    };
    return JSON.stringify(qrData);
  }

  async handlePeerHandshake(peerPublicKey: string) {
    this.peerPublicKey = peerPublicKey;
    this.sharedSecret = await deriveSharedSecret(
      this.keyPair.privateKey,
      peerPublicKey
    );
  }

  async sendToRemote(message: string): Promise<void> {
    if (!this.sharedSecret) throw new Error('Session not established');
    const encrypted = await encryptMessage(message, this.sharedSecret);
    await this.relay.send(this.sessionId, encrypted);
  }
}

The device handoff mechanism is particularly elegant. Happy maintains session state in a local SQLite database, tracking which device currently has “control” rights. When you press Cmd+Shift+M on desktop or tap the control button on mobile, Happy sends a control transfer request through the encrypted channel. The receiving device updates the session state and begins accepting input, while the previous controller switches to read-only mode. The AI agent continues running uninterrupted on the desktop CLI—only the input source changes.

The mobile client is built with Expo, allowing it to compile to both iOS and Android from a shared TypeScript codebase. It uses React Native’s WebSocket implementation for persistent connections to the relay server, with automatic reconnection logic and exponential backoff. Push notifications are handled through Expo’s notification service, triggered when the CLI detects permission prompts or errors that require human intervention:

// Mobile app approval flow
import { useSession } from './hooks/useSession';
import { sendApproval } from './api/session';

function ApprovalPrompt({ prompt }: { prompt: ClaudePrompt }) {
  const { sessionId, encryptedChannel } = useSession();
  
  const handleApprove = async () => {
    const response = { type: 'approval', allowed: true, promptId: prompt.id };
    await encryptedChannel.send(response);
  };

  return (
    <View style={styles.promptCard}>
      <Text style={styles.promptText}>
        Claude wants to modify {prompt.affectedFiles.length} files
      </Text>
      <FileList files={prompt.affectedFiles} />
      <View style={styles.buttonRow}>
        <Button title="Approve" onPress={handleApprove} />
        <Button title="Reject" onPress={() => handleReject()} />
        <Button title="View Diff" onPress={() => showDiff()} />
      </View>
    </View>
  );
}

The monorepo structure separates concerns cleanly across packages: @happy/cli wraps the AI tools, @happy/app contains the Expo mobile/web client, @happy/agent handles Claude-specific protocol parsing, and @happy/server runs the relay infrastructure. This modularity means you could theoretically run your own relay server for complete control over message routing, though the project provides a hosted option for convenience.

One sophisticated touch is how Happy handles the realtime voice feature. The CLI can stream audio from your desktop microphone to mobile clients and vice versa, using Opus codec for compression and the same encrypted channel for transport. This enables voice-based coding sessions where you dictate requirements from mobile while Claude executes on your desktop, with audio feedback confirming actions. The implementation uses Web Audio API on web clients and native audio modules on mobile, with automatic gain control and echo cancellation built in.

Gotcha

Happy’s wrapper architecture introduces meaningful latency to the development loop. Every keystroke, approval, or command travels from your input device through encryption, across the network to the relay server, back to your desktop CLI, and finally to the actual Claude Code process. In practice, this adds 100-300ms to every interaction depending on your network conditions. For rapid-fire approvals during active refactoring sessions, this lag is noticeable and occasionally frustrating—you’ll find yourself waiting for the mobile UI to catch up with your intent.

The dependency on a centralized relay server is a more fundamental concern. While end-to-end encryption means the server never sees your code, the entire remote access feature collapses if the relay goes offline. The project provides Docker configs for self-hosting, but that requires maintaining yet another service in your infrastructure. There’s also the trust model to consider: you’re trusting that the relay server binary actually matches the published source code and isn’t logging metadata like session timing, file counts, or connection patterns that could leak information about your development activity. For teams working on highly sensitive projects, this architectural dependency might be a dealbreaker regardless of the encryption guarantees.

Finally, Happy is limited by the capabilities of the tools it wraps. You can’t use Happy features that the underlying Claude Code or Codex don’t support, and any bugs or limitations in those tools propagate through to your Happy experience. The wrapper adds complexity without adding functionality to the core AI—it’s purely about access and control, not enhanced intelligence or capabilities.

Verdict

Use Happy if you’re already committed to Claude Code or Codex workflows and frequently need to monitor long-running AI coding sessions while away from your desk. The mobile approval feature is genuinely valuable for teams running multi-hour refactoring tasks where blocking on permissions wastes significant time. The end-to-end encryption makes it acceptable for proprietary codebases, assuming you trust the wrapper implementation and are comfortable with the relay server dependency. It’s particularly useful for solo developers or small teams who can quickly scan QR codes to establish sessions and benefit from seamless device handoff. Skip Happy if you primarily work at a single workstation without interruption, if you’re uncomfortable with the added latency in the development loop, or if your security model prohibits any external relay servers even with client-side encryption. Also skip it if you’re not already using Claude Code or Codex—Happy doesn’t make sense as an entry point to AI-assisted coding. And definitely skip if you want a native mobile coding experience; Happy is fundamentally about remote control, not mobile-native development, so you’re always tethered to a desktop CLI instance actually running the agent.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/ai-agents/slopus-happy.svg)](https://starlog.is/api/badge-click/ai-agents/slopus-happy)