RivetHub Setup
Point Hub web (and desktop) at a RivetOS node gateway. Hub is the node’s face, not a separate agent runtime.
Architecture frame: ARCHITECTURE.md.
Laptop first-install:
curl -fsSL https://get.rivethub.io/local.sh | bash(stable bits on rivethub.io). Published desktop and Android downloads are on that site. Dev/nightly builds live on the mesh update share your deployment publishes for in-app Updates. GitHub tags are source pins, not the app feed. This page is the source-build path.
What RivetHub is
Section titled “What RivetHub is”| Piece | Role |
|---|---|
apps/rivethub-web |
React (Vite) UI — chat, terminal, memory wiki, files, tasks, workflows, settings |
apps/rivethub-electron |
Electron shell over the same web dist (tray, shortcuts, notifications, mTLS pipe) |
| den-server | Serves hub dist as static root when configured; hosts gateway + harness APIs |
@rivetos/gateway-client |
Typed HTTP+WS client for harness control plane and gateway surfaces |
Primary interactive path: harness sessions on the node
(claude-code, grok-build, kimi-code, hermes, codex, opencode, pi, qwen-code)
via the gateway contract. A driver on the control plane is not a task executor.
The live RivetHub installer pin may lag this source-build list — the current pin ships Claude Code, Codex, Grok Build, Kimi Code, and Hermes.
Removed (Phase 5): Telegram / Discord / voice-discord channel plugins are gone. Hub is the product path. Leftover channels.telegram: in config is a validation warning only (no crash-loop).
Prerequisites
Section titled “Prerequisites”| Requirement | Notes |
|---|---|
| Node.js ≥ 22 | 24 used in CI/containers |
| Built monorepo | npm install at repo root builds packages via postinstall |
| Running node | rivetos agent (or den-server) with den/gateway up |
| At least one host harness | Optional for empty drawer; needed to chat with a coding agent |
| PostgreSQL | Memory / wiki / tasks (via datahub or external) |
Build Hub
Section titled “Build Hub”From the repo root:
npm install
# Production distnpx nx build @rivetos/rivethub-webOutput: apps/rivethub-web/dist/.
Desktop (optional)
Section titled “Desktop (optional)”npx nx build @rivetos/rivethub-webcd apps/rivethub-electron && npm install && npm run dist # or: npm run devDesktop starts unconfigured until a node gateway URL is set (the bundled app:// origin is not http(s)). On first launch with no saved rivethub.baseUrl, the shell probes https://localhost:5174/healthz (then http://) and, on a hit, writes that origin plus a roster row so the renderer restores both.
Serve Hub from the node (recommended)
Section titled “Serve Hub from the node (recommended)”den-server should serve hub as the static root so / is Hub.
Boot defaults are hub-first: static_dir is RivetHub web dist.
Environment (or den config equivalents):
# Static UI root — hub distexport RIVETOS_DEN_STATIC_DIR=apps/rivethub-web/dist
# Bind (loopback by default; off-loopback requires TLS + device client cert)export RIVETOS_DEN_HOST=0.0.0.0# From mesh.tls (or $RIVETOS_SHARED_DIR/rivet-ca/issued/<node_name>.{crt,key})export RIVETOS_DEN_TLS_CERT=$RIVETOS_SHARED_DIR/rivet-ca/issued/<node_name>.crtexport RIVETOS_DEN_TLS_KEY=$RIVETOS_SHARED_DIR/rivet-ca/issued/<node_name>.key# RIVETOS_DEN_TLS_CA is the CA-chain path from your mesh TLS config.# Client certs required off-loopback; set RIVETOS_DEN_TLS_REQUIRE_CLIENT=0 to allow encrypt-only (no device cert).
# Then start via normal agent boot (embedded den) or:node services/den-server/dist/index.jsOpen the node URL in a browser (default den port is commonly 5174 when run
standalone; use whatever your config/den.port advertises).
Authenticate with an enrolled device client certificate (rivet-ca.sh issue-client). Bearer tokens (den.token, RIVETOS_DEN_TOKEN, ?token=,
Authorization: Bearer) are not accepted. See GATEWAY-MTLS.md.
Dev mode (Vite)
Section titled “Dev mode (Vite)”# Terminal A: node / den-server (loopback HTTP, or TLS + device cert off-loopback)# Terminal B:npx nx dev @rivetos/rivethub-webPoint the Hub connection/settings at your gateway origin (scheme + host + port
only, no path/userinfo). isValidGatewayUrl rejects poisoned roster URLs.
Point Hub at a mesh peer
Section titled “Point Hub at a mesh peer”Hub supports seamless node switch: repoint the gateway client at another node’s origin; local dist stays put (never navigate to a peer’s served UI for code).
- Sidebar Node switcher / composer node picker. In a single-node setup, these stay out of the way once discovery confirms there are no other peers. They appear when a peer is discovered or a second node is saved; click a discovered peer to save it. Adding a node by URL in Settings still works.
- Android deep-link pattern:
http://127.0.0.1:5174/?node=<denUrl>(loopback may skip client certs;?token=is not application auth) - Mesh dens: roster entries with
capabilities: ["den"]andmetadata.denPortormetadata.denUrlappear viaGET /mesh.json
Use hostnames or documentation addresses in examples; do not commit lab private IPs.
Harness chat (product path)
Section titled “Harness chat (product path)”Chat binds per session, not per app:
| Concern | Control plane (driver-owned) | Legacy (unclaimed) |
|---|---|---|
| List | GET /api/harnesses/:id/sessions |
terminal harness-sessions scan |
| Stream | WS /api/harness-sessions/ws?session=<enc> |
all-sessions WS bridge |
| History | transcript hard-resync on every open | server-pushed deltas |
| Send | POST …/turns |
/term/inject into PTY |
On a full four-driver node every harness row is claimed; fallback remains for drivers disabled or older nodes.
Operator expectations
Section titled “Operator expectations”- Hard resync is mandatory. The live tail is at-most-once from attach time. Opening a conversation (or reconnect) replaces transcript from the store.
- Stop button only when the driver’s
interruptflag is true (den terminals enabled). - No approval cards for current PTY drivers (
approvals: false). Approvals live in the TUI. - “+ new” stays a local draft; first turn pins id through the term/roster path;
control plane adopts the session. Hub does not call
startSessionfor all harnesses (hermes/kimi refuse start). - Attachments: staging exists (
POST /api/uploads) but PTY drivers reject attachment turns; picker still uses legacy path where applicable. - Thinking:
reasoning-deltafolds into the live turn (capped in the hub store). kimi has no live thinking/assistant deltas; resync transcript for text. - Canonical SessionId rides on control-plane calls; drawer row keys may still be bare native id (den join key).
Sidebar map
Section titled “Sidebar map”| Route | Purpose |
|---|---|
/ Conversations |
Chat | Terminal per conversation |
/terminal |
Open PTY list; attach |
/memory |
Wikipedia-style wiki over datahub GET /api/wiki |
/files |
Browse node files root (den.files_root; unset → $RIVETOS_SHARED_DIR) |
/tasks |
List / create / steer / kill tasks |
/workflows |
Local workflow IR editor (no runner yet) |
| Settings | Gateway URL, wiki/datahub origin (auth is a device client cert) |
Tasks from Hub
Section titled “Tasks from Hub”Create tasks in-UI (goal + agent/harness from catalog + optional criteria).
Catalog entries for harness-session include harnessId and implemented;
grey options that are honest rejections (e.g. grok-build / hermes executors).
Implemented headless executors today: claude-code, kimi-code, opencode, pi, qwen-code.
Prefer those for automated task runs. grok-build, hermes, and codex stay
on the control plane as drivers and reject spawn-for-task. Interactive coding
remains the harness TUI + Hub chat.
Memory wiki
Section titled “Memory wiki”- Native Hub UI (no iframe) over datahub wiki API.
- Set datahub origin in Settings (
rivethub.wikiUrl); blank → mesh-discover datahub. - Requires postgres memory stack healthy (
rivetos doctor, migrate role applied).
Files browser
Section titled “Files browser”Server: den-server files routes (list|download|upload|mkdir|rename|delete).
Config: den.files_root / RIVETOS_DEN_FILES_ROOT (empty string disables).
Path + symlink fenced; large upload cap; recursive delete opt-in.
MicBridge (optional)
Section titled “MicBridge (optional)”Host microphone as node input for voice harnesses. Design: MICBRIDGE.md.
den-server opt-in RIVETOS_DEN_AUDIO=1. Hub capture client is a later phase.
Verify checklist
Section titled “Verify checklist”# 1. Packages buildnpx nx build @rivetos/rivethub-web
# 2. Node healthynpx rivetos doctornpx rivetos status
# 3. Harnesses visible on the nodecurl -sS --cert /path/to/device.crt --key /path/to/device.key \ --cacert "$RIVETOS_SHARED_DIR/rivet-ca/intermediate/chain.pem" \ "$GATEWAY/api/harnesses" | jq .
# Expect harness ids when all drivers registered:# claude-code, grok-build, kimi-code, hermes, codex, opencode, pi, qwen-code# roster commands: claude, grok, kimi, hermes, codex, opencode, pi, qwen
# 4. Open Hub, set gateway origin, present a device client cert, confirm drawer lists sessions# 5. Open a claude-code or grok-build session — Stop visible if terminals on# 6. Send a turn; live tool/thinking frames; hard-resync after refresh# 7. Dens page shows a live room when hooks fireUnit tests (web):
npx nx test @rivetos/rivethub-webGotchas
Section titled “Gotchas”- Device-cert-gated nodes: without an enrolled device client cert, harness and most APIs 401. Loopback HTTP is allowed for local node processes.
/healthzis always open. - Double-fold: bound sessions must not also fold the all-sessions socket (Hub mutex on
harnessBound). - turn_in_flight: server rejects overlapping turns; Hub client-queues with bounded backoff.
- Secrets in tool args: bridge summarizers redact patterns; do not log raw tool input in issues.
- CI secrets scan blocks real lab
10.xaddresses in committed tests; use documentation ranges or hostnames. - Android uses the same gateway contract in Kotlin; uploads UI and registry-stream drawer merge still deferred there.
Related
Section titled “Related”- ARCHITECTURE.md: harness-first node OS
- GATEWAY-MTLS.md: device client certificates (bearer tokens removed)
- DEN.md: den viewer and protocol
- GETTING-STARTED.md: install RivetOS
- DEPLOYMENT.md: Docker / Proxmox / mesh
