Docs · developer guide
Start with
one file.
Everything you need to compile an app, mount a UI and run it on a host. The platform is a preview: the limits section is part of the documentation, not a footnote.
Quickstart
The platform checkout is a Rust workspace plus a small Node toolchain. You need macOS on Apple Silicon, Xcode Command Line Tools, stable Rust, Node.js, CMake 3.24+ and Git. For the Web host, add thewasm32-unknown-unknown target, wasm-pack and Playwright Chromium.
# 1 · dependencies
npm install
# 2 · compile every app/*/app.ts to MBC
npm run build:app
# 3 · build + open the native Home
npm run demo
# 4 · or run the browser host
npm run dev:webThe first native build downloads and compiles SDL 3.4.10 and LVGL 9.5.0 intotarget/native-deps. Later builds reuse the cache. The compiler never runs on the device.
Edit
apps/counter/app.ts, run npm run build:app, then npm run demo. The launcher, Counter, Settings and momo are all MBC apps loaded by the same runtime.Project map
The workspace separates the contract, the hosts and the products. Dependencies only point one way.
| Path | What lives there |
|---|---|
crates/micro-ir | MBC model, codec, CRC-32, validation and icon catalog |
crates/micro-vm | Typed stack machine, host-call interface, instruction budgets |
crates/micro-core | Events, state, reactive bindings, UI tree, render patches |
crates/micro-compiler | SWC parse, allow-list diagnostics, lowering, microc CLI |
crates/micro-host-* | SDL, ESP32 and Web host adapters |
apps/ | App sources: shell, counter, settings, momo |
sdk/index.d.ts | Editor declarations for the accepted language and host API |
products/micro-web-player | Browser product: device shell + generated Wasm |
firmware/micro-os-esp32 | ESP-IDF component build and partition layout |
native/ | CMake FetchContent and the shared C bridge |
scripts/ | App build, Web copy, macOS bundle, ESP flash |
Manifest
Every app starts with app({ … }). The manifest carries identity, the launcher icon and the capability allow-list that the compiler and runtime both enforce.
app({
id: "notes",
name: "Notes",
icon: "edit",
capabilities: ["kv", "storage"],
});
const draft = state("");
ui.mount(
ui.column([
ui.text("Notes", { size: 24, lineHeight: 32 }),
ui.input(bind(() => draft.value), {
placeholder: "write something",
onChange: (text) => { draft.value = text; },
}),
]),
);Omitting capabilities — or declaring an empty list — denies every host call. The compiler rejects a call outside the manifest at the call site (MTS002). The manifest is stored in the MBC metadata section.
Language
The accepted surface is an allow-list, not full TypeScript. It is designed to be lowered to a small typed stack machine and validated before execution.
Supported
const/let; number, string, boolean,null- Arithmetic, equality, comparisons, assignment and
++/-- - Template literals, blocks,
if/else,while - Arrow handlers and bindings
state,bind,ui.mount,ui.place, and the widget catalog
Rejected
- JSX, classes, general functions, async / await, promises
- Exceptions, runtime imports, arbitrary objects / arrays
- Spread, destructuring, computed properties
- Browser / Node globals and dynamic UI creation
- Unsupported literal glyphs (the compiler fails rather than shipping a box)
Layout is explicit
Columns and rows are containers; children get a position with ui.place. Anchors can pin an edge or stretch between two. There is no hidden layout pass whose result depends on host font metrics.
ui.place(
ui.button("Add", {
onClick: () => { count.value++; },
}),
{ left: 24, top: 120, width: 160, height: 44 },
);Host API
Host calls are the only way an app touches the outside world. Reads work in bindings and handlers; writes and async requests are handlers-only. Every category must be declared in the manifest.
| Capability | Calls | Notes |
|---|---|---|
clock | clock.now | SNTP wall clock on device |
kv | kv.get / set / remove | Per-app namespaced state |
config | config.get / set | Human-editable app config |
storage | storage.* | Sandboxed TF-card files |
net | net.http / wifi / lookupWords | Async, callback-delivered |
audio | audio.* | URL playback + speech rate |
json | json.getText / getNumber | Path queries over responses |
device | device.* | Board identity, light, GPIO |
os | os.launch / goBack / setTitle | Shell and chrome control |
net.* is asynchronous and delivers results through a callback. clock.now() is sampled per call and is not a reactive source: a binding that reads it only re-evaluates when one of its state dependencies changes.
Storage
Three separate contracts, so apps never write host-specific files themselves.
| API | Use | Web / macOS / ESP32 backing |
|---|---|---|
kv | App state: counters, review stages, caches | localStorage / ~/.micros/kv/<app>.json / NVS namespace |
config | Human-editable config read at startup | localStorage / ~/.micros/<app>/config.json / NVS micro_config |
storage | App-private files | per-app localStorage / ~/.micros/sdcard/… / TF card /sdcard/micros/apps/<app>/ |
Paths are relative and sandboxed. storage exposes no host path and no cross-app handle. On a board without a card, storage.available() is false.
Limits
These are current boundaries, not a roadmap promise.
- Static UI. No runtime widget creation, no dynamic list, no source-level debugging.
- One foreground app. No background apps, services, timers or IPC.
- Bounded handlers. 10,000 instructions per handler; all app work is on the host UI thread.
- No MMU isolation. Bytecode executes in one address space, sandboxed by the VM and host API.
- Unsigned artifacts. MBC has a CRC, not a signature; there is no install consent UI.
- No OTA. Single factory app partition; no A/B slot or rollback.
- Shell is an app. An unreadable
shell.mbcaborts boot instead of falling back to native UI.
Micro OS is an application layer for constrained screens, not a general-purpose OS and not a browser.
FAQ
Does it run JavaScript?
No. There is no JavaScript engine on any host. Apps are compiled ahead of time to MBC and executed by a Rust VM. The browser host runs that VM through WebAssembly; JavaScript only boots the page and pumps frames.
Can I use npm packages or a framework?
No. The language is an allow-list with no runtime imports. The UI is the ui catalog and state is state / bind. This is deliberate: the compiler can validate the whole program.
Is it a Linux distribution?
No. It is not a kernel. On ESP32 the kernel is ESP-IDF/FreeRTOS; on macOS the process belongs to SDL3/AppKit; in the browser the page owns the thread. Micro OS owns the app contract and host boundary.
Are apps signed?
Not yet. MBC carries a CRC-32 for corruption, not authentication. Capabilities are enforced, but the manifest is unauthenticated and there is no installer grant flow.
Can I run it on a phone?
Through the browser, yes — the Web Player is responsive and supports desktop and mobile browsers. Native iOS and Android hosts do not exist yet.
Where is the source?
The platform source lives at github.com/dereking/micros. This site is built separately with Astro and deployed on Cloudflare Pages.