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.
123 lines
4.9 KiB
Markdown
123 lines
4.9 KiB
Markdown
# 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
|
|
|
|
```ts
|
|
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
|
|
|
|
```sh
|
|
# 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
|