How the OpenHuman iOS Client Connects to the Desktop Core with TunnelTransport E2E Encryption

The OpenHuman iOS client establishes end-to-end encrypted communication with the desktop core through TunnelTransport, which wraps Socket.io with XChaCha20-Poly1305 encryption when LAN connections are unavailable.

The tinyhumansai/openhuman repository implements a pluggable transport architecture that enables secure remote procedure calls between mobile clients and desktop cores. When local network discovery fails, the iOS client automatically falls back to TunnelTransport, leveraging X25519 key exchange and modern authenticated encryption to protect data in transit across untrusted networks. This ensures that sensitive RPC payloads remain confidential even when traversing public internet infrastructure.

Transport Selection and Fallback Strategy

The TransportManager (app/src/services/transport/TransportManager.ts) orchestrates connection strategy during application startup. It implements a hierarchical selection process that prioritizes direct local area network connections before escalating to encrypted tunnel traversal.

When the iOS client cannot establish a direct LAN-HTTP connection, the manager instantiates TunnelTransport at lines 122–133. The constructor receives four critical parameters: the backend tunnel URL, a unique channel identifier, the base64-encoded core public key (coreB64), and a bearer token for authentication. This fallback mechanism ensures seamless connectivity regardless of network topology.

// TransportManager – selecting TunnelTransport when LAN is unavailable
import { TunnelTransport } from './TunnelTransport';
import { CoreTransport } from './CoreTransport';

export class TransportManager {
  // …
  async getTransport(): Promise<CoreTransport> {
    // Try LAN HTTP first…
    if (await this.tryLanHttp()) {
      // return LanHttpTransport instance
    }

    // Fallback to TunnelTransport (E2E encrypted)
    const tunnel = new TunnelTransport(
      this.backendUrl,          // e.g. "https://backend.tinyhumans.ai"
      this.channelId,           // unique channel identifier
      coreB64(this.corePublicKey), // base‑64‑encoded core public key
      this.bearerToken          // auth token for the core
    );
    return tunnel;
  }
}

Establishing the Encrypted Tunnel

TunnelTransport (app/src/services/transport/TunnelTransport.ts) implements the CoreTransport interface and manages the full lifecycle of the encrypted channel. Upon instantiation, it performs a cryptographic handshake before establishing the Socket.io connection, ensuring that no plaintext data traverses the network.

Cryptographic Handshake with X25519

The constructor generates an ephemeral X25519 key pair for the client, then performs an Elliptic Curve Diffie-Hellman exchange with the desktop core's public key. The derived shared secret serves as the root material for the symmetric cipher. This ephemeral approach provides forward secrecy for each connection session.

Authenticated Encryption Using XChaCha20-Poly1305

From the shared secret, the transport derives an XChaCha20-Poly1305 cipher instance. All subsequent JSON-RPC payloads undergo AEAD encryption before transmission and authenticated decryption upon receipt. This construction provides both confidentiality and integrity verification for every message exchanged between the iOS client and the OpenHuman desktop core.

// TunnelTransport – establishing an encrypted socket.io channel
import { io, Socket } from 'socket.io-client';
import { X25519, XChaCha20Poly1305 } from '@tinyhumans/crypto';

export class TunnelTransport implements CoreTransport {
  private socket: Socket;
  private cipher: XChaCha20Poly1305;

  constructor(
    tunnelUrl: string,
    channelId: string,
    corePubKeyB64: string,
    token: string
  ) {
    // 1️⃣ Create a client key pair
    const clientKey = X25519.generateKeyPair();

    // 2️⃣ Perform a Diffie‑Hellman exchange with the core’s public key
    const corePubKey = X25519.importPublicKey(corePubKeyB64);
    const sharedSecret = X25519.deriveSharedSecret(clientKey.private, corePubKey);

    // 3️⃣ Derive an XChaCha20‑Poly1305 cipher from the shared secret
    this.cipher = XChaCha20Poly1305.fromKey(sharedSecret);

    // 4️⃣ Open a socket.io connection with the token for auth
    this.socket = io(`${tunnelUrl}/${channelId}`, {
      auth: { token },
      transports: ['websocket']
    });

    // 5️⃣ Encrypt outgoing messages and decrypt incoming ones
    this.socket.on('message', (encPayload) => {
      const plain = this.cipher.decrypt(encPayload);
      this.handleMessage(plain);
    });
  }

  async send(payload: Uint8Array): Promise<void> {
    const encrypted = this.cipher.encrypt(payload);
    this.socket.emit('message', encrypted);
  }

  // …receive, close, etc.
}

Interface Integration and Application Bootstrap

TunnelTransport conforms to the CoreTransport interface defined in app/src/services/transport/CoreTransport.ts. This abstraction allows higher-level services to interact with the transport layer through standardized methods like send, receive, and close without knowledge of the underlying encryption or network implementation.

The transport initialization sequence begins in app/src/App.tsx, where the application bootstrap process invokes TransportManager and injects the selected transport into CoreRpcClient. This architecture enables the business logic to remain agnostic of connection methodology, whether communicating via direct LAN, encrypted tunnel, or cloud HTTP transport.

Summary

  • TransportManager (app/src/services/transport/TransportManager.ts) handles automatic fallback from LAN to encrypted tunnel transport when direct connections fail.
  • TunnelTransport implements the CoreTransport interface and establishes Socket.io connections secured with XChaCha20-Poly1305 encryption.
  • The cryptographic handshake utilizes X25519 ephemeral key exchange to derive shared secrets, providing forward secrecy.
  • All RPC payloads are authenticated and encrypted end-to-end, ensuring data confidentiality across untrusted networks.
  • The transport layer abstraction allows seamless integration at application startup (app/src/App.tsx) without modifying higher-level CoreRpcClient logic.

Frequently Asked Questions

What encryption algorithm does TunnelTransport use for E2E security?

TunnelTransport employs XChaCha20-Poly1305 authenticated encryption with associated data (AEAD). This modern cipher is derived from an X25519 shared secret established during the initial handshake, ensuring both confidentiality and integrity for all messages exchanged between the iOS client and the OpenHuman desktop core.

How does the iOS client decide when to use TunnelTransport instead of LAN?

The TransportManager attempts to establish a LAN-HTTP connection first. If this attempt fails or the desktop core is not discovered on the local network, the manager automatically falls back to TunnelTransport at lines 122–133 of TransportManager.ts, passing the necessary credentials and cryptographic keys to establish the encrypted relay connection.

Where is the TunnelTransport initialization triggered in the iOS application?

According to the source code in tinyhumansai/openhuman, the transport initialization sequence begins in app/src/App.tsx during the application bootstrap phase. The TransportManager is instantiated here, and the selected transport (potentially TunnelTransport) is injected into the CoreRpcClient for use throughout the application lifecycle.

Does TunnelTransport provide forward secrecy for connections?

Yes, TunnelTransport generates an ephemeral X25519 key pair for each connection attempt. Because these keys are temporary and the shared secret is derived fresh for every session, past communications cannot be decrypted even if the core's long-term private key is compromised later, satisfying the forward secrecy requirement.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →