Files
universe/architecture/desktop-stack.md
T
Hanzo AI b943b526fe docs(arch): desktop stack decomposition + brand-variant build flow
Documents how zoo/app shims hanzo/desktop, the BrandConfig contract,
chain IDs (zoo 200200/200201/200202, hanzo 36900/36901), the sidecar
list (Ollama dropped), and the read-order for new contributors.
2026-05-04 11:28:31 -07:00

4.9 KiB

Zoo / Hanzo Desktop Stack

Decomposition of the desktop-app architecture so Zoo AI Desktop is a declarative brand variant of Hanzo Desktop with no forked source.

Repos

Path Role Language
~/work/hanzo/desktop (hanzoai/desktop) Canonical desktop source. Tauri 2.x + React. Rust + TypeScript
~/work/zoo/app (zooai/app) Thin pnpm shim. pnpm build → BRAND=zoo upstream build. Bash + assets
~/work/hanzo/node AI agent backend: chat router, MCP server, agent harness, tool runner. Rust
~/work/zoo/node Native chain node zood (consensus + EVM + AI-mining precompiles). Go
~/work/lux/consensus Quasar consensus engine consumed by both nodes. Go (with Rust FFI)
~/work/zoo/logo Canonical brand-asset source for ~/work/zoo/app/brand/. SVG + PNG
~/work/zoo/universe This repo. Architecture + ops docs. Markdown + compose

zood and hanzo-node are not interchangeable. hanzo-node is the AI/agent router that the desktop app talks to over local HTTP. zood is the chain node (validators + EVM + precompiled AI-mining contract). The desktop ships hanzo-node as a sidecar; the chain RPC is remote.

Brand variants from one source

The Hanzo Desktop source supports both Hanzo and Zoo brands via:

  • libs/brand-config/ — single BrandConfig exported by getBrand()
  • apps/hanzo-desktop/src-tauri/tauri.conf.{hanzo,zoo}.json — overlay merged before invoking tauri build
  • apps/hanzo-desktop/scripts/brand-build.cjs — pre-merges Tauri config and patches Info.plist to fix CFBundleIdentifier propagation
  • apps/hanzo-desktop/src-tauri/src/local_hanzo_node/hanzo_node_options.rs — option_env!("BRAND") switches initial agent names at compile time
  • apps/hanzo-desktop/public/{visor,zoo-logo}.svg + {onboarding,zoo-onboarding}.png — both brands' assets coexist; React picks via getBrand().brand

BrandConfig

interface BrandConfig {
  brand: 'hanzo' | 'zoo';
  name: string;          // "Hanzo" | "Zoo"
  productName: string;   // "Hanzo Desktop" | "Zoo Desktop"
  identifier: string;    // com.hanzo.desktop | com.zoo.desktop
  logo: { light, dark, favicon };
  colors: { primary, bg, fg };
  hosts: string[];                                       // hanzo.ai | zoo.ngo, zoo.network
  storeUrl: { mac, win, ios, android };

  // Native chain identity. Mining checkpoints submit to the AI-mining
  // precompile on the brand's primary EVM/A-chain.
  network: {
    rpc: string;                            // https://rpc.hanzo.network | https://rpc.zoo.network
    chainId: number;                        // 36900 hanzo / 200200 zoo
    token: string;                          // $AI | $ZOO
    aiMiningPrecompile: `0x${string}`;     // 0x0300...0000
    blockExplorer: string;
  };

  // Cloud AI inference (chat completions + embeddings). Distinct from chain RPC.
  inferenceEndpoint: string;                // https://gateway.hanzo.ai

  // OAuth IAM provider for "Login with <Brand>".
  iam: { baseUrl, clientId, redirectUri, callbackEvent };
}

Chain IDs

Network chainId RPC
Hanzo Mainnet 36900 https://rpc.hanzo.network
Hanzo Testnet 36901 https://rpc.hanzo-test.network
Zoo Mainnet 200200 https://rpc.zoo.network
Zoo Testnet 200201 https://rpc.zoo-test.network
Zoo Devnet 200202 https://rpc.zoo-dev.network

AI-mining precompile: 0x0300000000000000000000000000000000000000 on each.

Sidecars

Binary Purpose Brand
hanzo-node AI agent router, chat, MCP, tool runner both
deno, uv Tool sandboxes for hanzo-tools-runner both

Removed: ollama. Embeddings + LLM inference now go through the cloud inference endpoint or via hanzo-node's built-in embedding (when available). This drops ~62 MB from the bundle and removes a managed sidecar process.

Build

# Hanzo brand (canonical)
cd ~/work/hanzo/desktop/apps/hanzo-desktop
npm run build:hanzo
# -> Hanzo.app, com.hanzo.desktop

# Zoo brand (declarative shim)
cd ~/work/zoo/app
pnpm build
# -> Zoo.app, com.zoo.desktop
# Internally: ln -s brand/icons -> upstream/src-tauri/icons/zoo,
# then BRAND=zoo VITE_BRAND=zoo npm run build:zoo upstream-side.

Releases

Brand Registry Releases
Hanzo ghcr.io/hanzoai/* github.com/hanzoai/desktop
Zoo ghcr.io/zooai/* github.com/zooai/desktop

Container registries do not mix per the rule in ~/.claude/CLAUDE.md.

Reading order for new contributors

  1. ~/work/hanzo/desktop/libs/brand-config/src/index.ts — the contract
  2. ~/work/hanzo/desktop/apps/hanzo-desktop/scripts/brand-build.cjs — how Tauri configs merge
  3. ~/work/zoo/app/scripts/build.sh — how the shim invokes the upstream build
  4. ~/work/hanzo/desktop/apps/hanzo-desktop/src-tauri/src/local_hanzo_node/hanzo_node_options.rs — how option_env!("BRAND") selects the initial agents