Nexu uses a controller-first local runtime model. In desktop/local mode, a single apps/controller process owns Nexu config, compiles OpenClaw config, materializes skills/templates, and orchestrates the OpenClaw runtime.
Desktop Shell / Browser
β
Web (React + Ant Design + Vite)
β
Controller (Hono + Zod OpenAPI + lowdb-backed local store)
β
OpenClaw Runtime β Slack / Discord / Feishu API
| Layer | Technology |
|---|---|
| Local control plane | Hono + @hono/zod-openapi |
| Local persistence | lowdb + JSON config under ~/.nexu/ |
| Validation | Zod (single source of truth) |
| Local auth compatibility | Controller-managed local auth/session shims |
| Frontend | React + Ant Design + Vite |
| Frontend SDK | @hey-api/openapi-ts (auto-generated) |
| State | React Query (@tanstack/react-query) |
| Lint/Format | Biome |
| Package manager | pnpm workspaces |
Zod schema is the single source of truth. Types flow one-way, never duplicated:
Zod Schema (define once)
β API route validation (@hono/zod-openapi)
β OpenAPI spec (auto-generated)
β Frontend SDK types (@hey-api/openapi-ts)
β local store/runtime types
Never hand-write types that duplicate a schema. Use z.infer<typeof schema>.
apps/controller/β Single-user controller service. Routes insrc/routes/, local config store insrc/store/, OpenClaw runtime integration insrc/runtime/, compiler logic insrc/lib/openclaw-config-compiler.ts.apps/web/β React frontend. Pages insrc/pages/, generated SDK inlib/api/, auth client insrc/lib/auth-client.ts.apps/desktop/β Electron desktop runtime shell and sidecar orchestrator. The active local path launchescontroller + web + openclawsidecars only.packages/shared/β Shared Zod schemas insrc/schemas/. Includes bot, channel, gateway, invite, model, skill, and OpenClaw config schemas.nexu-skills/β Public skill repository. Each skill is a directory withSKILL.mdfrontmatter.skills.jsonis the built catalog index.specs/β Design docs, references, product specs, exec plans, generated artifacts.
Desktop/local config generation: Controller reads ~/.nexu/config.json β compiles OpenClaw config JSON (agents, channels, bindings, models) β writes OPENCLAW_CONFIG_PATH and managed skills/templates β OpenClaw hot-reloads.
Desktop runtime boot: Electron desktop starts the controller sidecar, waits for controller readiness/auth bootstrap, starts the web sidecar, and delegates OpenClaw process management to apps/controller.
Proxy policy: Desktop bootstrap computes one normalized proxy policy from HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY, applies it to Electron networking, propagates the normalized uppercase env into controller/web/OpenClaw child processes, and always merges loopback bypass entries (localhost, 127.0.0.1, ::1).
Channel connection flows: Frontend calls controller routes β controller validates and stores local credentials/config β controller recompiles OpenClaw config β runtime writers materialize the updated state β OpenClaw reloads.
Outbound HTTP: Controller outbound HTTP goes through a shared proxy-aware fetch layer. Local desktop/controller/OpenClaw loopback traffic remains direct; external traffic uses env-derived proxy settings when present.
Slack events: Slack messages are handled through the current controller-compiled OpenClaw runtime path rather than a separate Nexu gateway sidecar.
Feishu events: Feishu uses a long-lived runtime connection driven by the controller-compiled OpenClaw config.
Skill catalog: Skills are file-based. The controller scans nexu-skills/skills/ for SKILL.md frontmatter and serves install/uninstall/catalog flows. The local runtime watches the managed skills directory for hot-reload.
The active local/controller path persists Nexu-owned state under ~/.nexu/ via controller store modules, with config.json as the main source of truth and OpenClaw runtime files living under OPENCLAW_STATE_DIR.
apps/controller/src/lib/openclaw-config-compiler.ts β Active controller-first module that builds OpenClaw config from Nexu local state.
Critical constraints:
bindings[].agentIdmust matchagents.list[].idbindings[].match.accountIdmust matchchannels.{slack|feishu}.accountskey- Slack HTTP mode requires
signingSecret;groupPolicymust be"open" - LiteLLM models must set
compat.supportsStore: false - Only one agent should have
default: true
See specs/references/openclaw-config-schema.md for full schema and common pitfalls.
specs/designs/openclaw-multi-tenant.mdβ Full system design, data model, phased planspecs/designs/openclaw-architecture-internals.mdβ OpenClaw runtime analysisspecs/design-specs/core-beliefs.mdβ Engineering principles