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.
This commit is contained in:
Hanzo AI
2026-05-04 11:28:31 -07:00
parent 3e51152561
commit b943b526fe
+122
View File
@@ -0,0 +1,122 @@
# 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