A presence-first, ultra-light 2D virtual office.
Built to be left running all day and forgotten — not a meeting tool.
Existing virtual-office tools grew into "meeting-tool replacements" — video, screen share, rooms, recording, integrations — and got heavy. Hiroba goes the other way. It keeps only the core of being somewhere together:
- You're here — your avatar is in a space (a lobby or a small team room).
- You can see who's around — an always-visible org roster shows who's where and what they're doing (in this space / in a call / away / DND).
- You can move, and switch spaces with a tab.
- Talk by being near — proximity spatial voice in the lobby; a team room is effectively one small group call. Or call anyone with one click — a cross-space 1:1 "barge-in" that goes live instantly, wherever they are.
- It's light enough to forget — idle CPU ≈ 0%, memory in the tens of MB.
No video. No screen share. No recording. No SFU. Just an org floor of small spaces, points that move around, and voice that fades in as you get close.
┌──────────────────────────────┐ WebRTC P2P (Opus)
│ Rust signaling/state server │ ┌───────────────────────┐
│ axum + tokio + WebSocket │ ▼ ▼
│ • org roster / presence │ ┌──────┐ audio only ┌──────┐
│ • per-space position relay │ │client│◀────mesh─────▶│client│
│ • per-space proximity │ │Tauri │ │Tauri │
│ • WebRTC signaling relay │ └──────┘ └──────┘
│ • paging (cross-space 1:1) │ ▲ ▲
│ NEVER touches media │ │ WebSocket (control)│
└──────────────┬───────────────┘ └─────────────────────┘
└──────────────────────────────────────────────┘
The server only moves control data: the org roster, per-space positions,
proximity decisions, and the WebRTC handshake. Audio never flows through the
server — peers connect directly (P2P mesh). A team room of ≤5 (or a lobby of
~10) is comfortable on a mesh. State is split into two scopes — an org roster
sent to everyone, and per-space position/proximity/audio sent only to those
in that space. The wire format is specified in PROTOCOL.md.
- Server (
server/) — Rust, axum, tokio. Single static binary. Holds no media, so it stays tiny and idle-cheap. - Client (
client/) — Tauri (Rust shell + OS WebView) with a vanilla TypeScript + Canvas 2D frontend. Uses the OS WebView's built-in WebRTC, so the binary is far smaller and lighter than an Electron app.
Prerequisites: Rust (stable), Node 18+, and the Tauri v2 system dependencies for your OS.
# 1. Start the server (listens on 0.0.0.0:8787 by default)
cd server
cargo run # or: HIROBA_ADDR=0.0.0.0:9000 cargo run
# 2. Start the client (in another terminal)
cd client
npm install
npm run tauri devIn the client's join screen, enter a display name, pick an avatar color, and
point it at your server (ws://127.0.0.1:8787/ws for local dev). Open a second
client instance to see two avatars; walk them together in the lobby to hear
spatial voice fade in, or switch both to the same team tab for a group call.
You start muted — click the mic button to go live.
Controls: WASD / arrow keys to move; the tabs (top) switch spaces; the sidebar lists the org — hover a member and hit Call to page them.
# Server: a single optimized binary at server/target/release/hiroba-server
cd server && cargo build --release
# Client: native installers/bundles under client/src-tauri/target/release/bundle/
cd client && npm install && npm run tauri buildThe server is a single binary with no external dependencies (no database, no media server). See docs/SELF_HOSTING.md for deployment, configuration, firewall/NAT notes, and when you might need a TURN server.
Phases 0–2 are implemented (self-host / guest profile): an org floor of
multiple spaces, a roster sidebar with live status, tab-switched spaces,
proximity & group voice, cross-space paging (barge-in), DND / away, and mic
control. Verified by server/tests/smoke.mjs and client/tests/*.test.mjs.
| Phase | Focus | Status |
|---|---|---|
| 0 | Skeleton: server + client + P2P voice, proximity round-trip | ✅ |
| 1 | Org floor: multiple spaces, tabs, roster sidebar, space voice (self-host / DB-less) | ✅ |
| 2 | Paging (1:1 barge-in), DND, away, in-call status; lightness targets | ✅ |
| 3 | Hosted: OAuth/OIDC, multi-tenant, TURN credentials · invites, DB, billing / admin roles | 🟡 partial |
| 4 | Experience: text chat (FR-18), reactions (FR-19), richer profile, ambiance | ⛔ future |
Phase 3's core seams are now implemented: OAuth/OIDC token verification
(server/src/auth.rs — guest / HS256 JWT / OIDC JWKS), strict multi-tenant
isolation (server/src/registry.rs), and short-lived TURN credential issuance
(server/src/ice.rs + GET /ice, consumed by client/src/config.ts). All are
off by default, so the self-host / guest profile is unchanged. What remains for
Phase 3 is the commercial layer — invites, durable persistence (tenants are
still in-memory), billing, and admin roles.
The marketing site (landing + pricing) lives in site/ as a
dependency-free static site. Preview it locally with make site.
Apache-2.0. Includes a patent grant.
Brand assets are not covered by Apache-2.0. The "Hiroba" name, logo, app
icons (app-icon.png, app-icon-macos.png), site favicon, and brand assets
under site/ are excluded from the Apache-2.0 license grant. They may not be
used to brand a fork or a derived service without permission. Everything else
— code, docs, protocol — is Apache-2.0.