Umi conventions
A living reference for how we name and shape things, so the codebase reads as one system. New conventions land here as they’re decided.
Naming
Section titled “Naming”- Rust crates (backend):
umi-<area>—umi-primitives,umi-crypto,umi-silo. - JS packages (frontend/shared):
@umi/<name>—@umi/email,@umi/canvas. - Silos: a lowercase, snake_case id (
email,notes) is the silo’s stable identity. Its frontend package is@umi/<id>underpackages/silos/<id>/; its manifest issilo.tomlat the silo root. - Apps (deployable) live in top-level
apps/; shared/embedded code inpackages/.
Identifiers
Section titled “Identifiers”- Record ids are hyphenless (SurrealDB rule). Use snake_case, never kebab.
- snake_case everywhere it’s a wire/string value: capability names, purposes, event tags, gate
kinds, silo ids. The Rust enums serialize this way (
#[serde(rename_all = "snake_case")]).
The silo manifest (silo.toml)
Section titled “The silo manifest (silo.toml)”Every silo declares itself in one TOML file — the user-facing consent surface. Shape:
[silo]id = "email" # lowercase snake_case, no hyphens; the silo's stable identityname = "Email"version = "0.1.0"description = "…"
[entry]frontend = "@umi/email" # the UI package (optional if backend-only)backend = "…" # the backend entry (optional if frontend-only)
[[capabilities.required]] # must be granted to installcapability = "network" # a umi-security Capability name (snake_case)scope = "imap:993" # host-scopes the grant; "self" = the silo's own store partitionreason = "Fetch mail over IMAP." # human rationale shown at the consent prompt
[[capabilities.optional]] # granted just-in-time, when first usedcapability = "network"scope = "smtp:587"reason = "Send mail (only when you send)."Rules:
- Least privilege. Request the smallest set; scope every grant. Secondary/sensitive abilities
go in
optional(granted just-in-time via theConsentGate), notrequired. capabilityis a [umi-security] capability name — the manifest never invents permissions, it only requests ones the system already enforces.scopenarrows a grant: a network host/port, orselffor the silo’s own store partition. (Capabilities are coarse today; scope is where per-silo narrowing lives until the gate enforces it directly.)- No remote code. Everything a silo runs is bundled; nothing is fetched-and-executed.
Tools (MCP)
Section titled “Tools (MCP)”A silo’s backend tools are MCP tools, namespaced by silo id: email.search, email.send,
notes.create. Core is the MCP host; the agent is the client.
Errors & events
Section titled “Errors & events”umi-erroris one enum that grows per emitter — a variant is added when a crate genuinely returns it, not in anticipation.ProcessEvent(the reactivity bus) is snake_case-tagged and also grows per emitter.
Testing
Section titled “Testing”A test must be able to fail for a real bug — if you can’t name the bug it catches, don’t write it. No tests of the language/libraries (serde roundtrips, getters, enum counts); no near-duplicates.
- Invariants / property tests (
proptest) for laws over an input space — crypto, parsers, planners. One property test beats ten hand-picked examples. - Integration tests at boundaries with real deps (embedded SurrealDB in-mem,
tempfile) — a system’s value is in its seams. - Adversarial security tests are the highest value: capability bypass, silo-isolation breach, tamper, taint→sink, path traversal.
- The suite grows with the system, one crate’s real invariants at a time. Gate slow/live tests
(real-LLM, real-IMAP) behind a feature or
#[ignore].