Skip to content

Conversation

@bra1nDump
Copy link
Contributor

Add Clawdbot gateway integration allowing users to connect to their local or remote Clawdbot gateway and chat with AI sessions.

Features:

  • Device identity with Ed25519 signing using @noble/ed25519
  • WebSocket connection with protocol v3 (req/res/event frames)
  • Device pairing flow with approval via gateway dashboard
  • Gateway config persistence with auto-reconnect on app load
  • Sessions list with proper field mapping (GatewaySessionRow types)
  • New chat creates isolated sessions (not using main session)
  • Chat screen with streaming message support
  • Input styling matches main app AgentInput component

New files:

  • sources/clawdbot/ - Core gateway integration module
  • sources/app/(app)/clawdbot/ - Clawdbot screens (index, connect, new, chat)
  • sources/components/ClawdbotViewWrapper.tsx - Tab wrapper component

Also adds Clawdbot tab to main tab bar and all translations.

Generated with Claude Code via Happy

Add Clawdbot gateway integration allowing users to connect to their local or
remote Clawdbot gateway and chat with AI sessions.

Features:
- Device identity with Ed25519 signing using @noble/ed25519
- WebSocket connection with protocol v3 (req/res/event frames)
- Device pairing flow with approval via gateway dashboard
- Gateway config persistence with auto-reconnect on app load
- Sessions list with proper field mapping (GatewaySessionRow types)
- New chat creates isolated sessions (not using main session)
- Chat screen with streaming message support
- Input styling matches main app AgentInput component

New files:
- sources/clawdbot/ - Core gateway integration module
- sources/app/(app)/clawdbot/ - Clawdbot screens (index, connect, new, chat)
- sources/components/ClawdbotViewWrapper.tsx - Tab wrapper component

Also adds Clawdbot tab to main tab bar and all translations.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@GrocerPublishAgent
Copy link
Contributor

If you rebase on main, I've fixed the libsodium-wrappers ESM imports by upgrading them to 0.8.2. No more patches I think.

@leeroybrun
Copy link
Collaborator

Awesome @bra1nDump ! Can't wait to try that :)

Some small ideas/discussions from a first look:

I noticed sources/clawdbot/deviceIdentity.ts uses @noble/ed25519 plus crypto.subtle.digest(...) for the device fingerprint. Given the app is already libsodium-first, would it make sense to implement the device identity + signing via libsodium as well? That would keep primitives consistent and avoid any WebCrypto/RN portability surprises.

One possible direction (if it matches the gateway expectations):

  • Generate a seed (32 bytes) with our existing getRandomBytes
  • Use sodium.crypto_sign_seed_keypair(seed) to get Ed25519 keys
  • deviceId = hex(crypto_hash_sha256(publicKey))
  • signature = base64url(crypto_sign_detached(payloadBytes, secretKey))
  • Persist seed (or secretKey) + publicKey as base64url

Sketch:

import sodium from '@/encryption/libsodium.lib';
import { getRandomBytes } from '@/platform/cryptoRandom';
import { encodeBase64, decodeBase64 } from '@/encryption/base64';

const b64url = (b: Uint8Array) => encodeBase64(b, 'base64url');
const fromB64url = (s: string) => decodeBase64(s, 'base64url');

async function generateIdentity() {
  await sodium.ready;
  const seed = getRandomBytes(32);
  const kp = sodium.crypto_sign_seed_keypair(seed);
  const deviceId = bytesToHex(sodium.crypto_hash_sha256(kp.publicKey));
  return { deviceId, publicKey: b64url(kp.publicKey), privateKey: b64url(seed) };
}

async function signPayload(seedB64url: string, payload: string) {
  await sodium.ready;
  const seed = fromB64url(seedB64url);
  const kp = sodium.crypto_sign_seed_keypair(seed);
  const sig = sodium.crypto_sign_detached(new TextEncoder().encode(payload), kp.privateKey);
  return b64url(sig);
}

(If you’d rather store the 64-byte secretKey directly instead of the seed, that works too.)

Token/password persistence (threat model?)

clawdbotStorage.ts stores token/password in MMKV JSON. Is that acceptable for this feature’s threat model, or should we treat those like other secrets and put them in SecureStore (native) / a safer web mechanism? We have encrypt-at-rest patterns elsewhere that we could reuse if we want consistency (you can have a look at the SecretStore implementation in leeroy-wip where I added support for storing encrypted secrets).

Streaming robustness

In clawdbot/chat/[sessionKey].tsx, the UI processes only the latest event; if multiple deltas land between renders, intermediate chunks could get skipped. Might be worth processing all unhandled events since last index.

Connect screen completeness

connect.tsx keeps password state and persists it, but I didn’t see a password input rendered — is that intentionally deferred, or should it be added/removed?

Small hygiene / DX

  • sessions.list logs the full response payload — maybe gate behind DEV?
  • expo-app/CLAUDE.md includes a hard-coded /Users/... reference path — probably better as repo-relative or a public link.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants