One API surface; transports are adapters
Umi exposes one API — a capability-gated service layer in the engine — and every consumer is a
client of it. This is the OS reading: one syscall interface, many clients. There is not a
“frontend API” and a separate “remote API” and a separate “silo API”; there is one contract, and
the consent gate (umi-security) lives at it, so every caller is equally constrained by
construction.
The consumers:
- The desktop frontend talks to core through a fast in-process adapter (Tauri IPC) — no network hop on the chatty local hot path (canvas, token streams).
- Remote / LAN devices (a phone away from home) talk to core over axum HTTP/WS.
- Silos — which run out-of-process — talk to core over the same axum boundary, holding capability tokens.
So axum is the unified external door: phone and silos come through it, authenticated and capability-scoped, while the desktop UI takes the in-process fast path. Both are thin adapters over one service contract — not two API designs to maintain. Define the API once; the gate is in the service layer, never in a transport.
Why this is decided now and not “later”: the boundary is upstream of the first silo. The moment Email (the first silo) becomes real, it needs a door to speak through — and that door is this one. The contract shapes the engine and the silo boundary, so it belongs with the foundational decisions, not at the end of a build queue.
Open for when we build it
Section titled “Open for when we build it”- Event bus over the wire:
ProcessEvent→ SSE (one-way, simple) for live UI updates, plain requests for commands; WebSocket only if a client genuinely needs to stream upward. DeviceAuth: device pairing (code/QR) + per-device tokens — the remote-access security surface.- LAN TLS: self-signed + pinning, with the rustls ring provider named explicitly.