Skip to content

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.

  • 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.