No new framework. Just your Python, reflected into a UI.
This page is for the “how does this actually work” reader. If you want the two-minute pitch instead, see the product overview.
A note on versions — the IvoryOS behind the Nature Communications paper ran as one Flask app, UI and hardware control bundled together. Everything below describes v2: a from-scratch rearchitecture that splits that into Hub (the web app) and Core (the local service running next to your instruments).
Introspection, not integration.
Most orchestration tools need a plugin written against their SDK before they can control an instrument. IvoryOS instead inspects the Python you’ve already written — method names, type hints, docstrings, default values — and builds a matching form automatically. There is no schema to author by hand and no plugin API to keep in sync. Rename a parameter in your driver, and the generated interface updates with it.
def set_flow_rate(
self, rate: float
):
"""mL/min, 0-50"""
...This is why onboarding a new instrument is writing normal Python, not learning IvoryOS — and it’s the same mechanism that lets a driver contributed by one lab work, unmodified, in another.
Two products, deliberately separate.
Hub is where you discover and assemble — a directory of open-source drivers, contributed and shared. Core is what actually talks to your hardware. They’re split on purpose: nothing that controls real lab equipment should depend on a remote service being up. A third piece, Cloud, is further out — coordinating multiple Core instances from one dashboard.
Hub
A directory of instrument drivers, optimizer plugins, and workflow templates, contributed by the community. Pick your stack, and Hub generates the exact Python and setup files for your environment.
Core
A lightweight FastAPI service that runs on the machine next to your instruments and reflects your drivers into the browser — the Designer, execution queue, and data history all live here, served over your own local network.
Why it’s split this way: the browser UI that designs a workflow is the same one that runs it — but it only ever talks to Core on your own machine or network. Nothing needs to send hardware commands through a third-party cloud, and nothing about controlling your lab depends on our servers staying online.
Cloud
Long-term · early prototypeCore already coordinates every instrument on its own network — but as one linear workflow, one queue, at a time. Cloud’s job isn’t adding more instruments; it’s letting a workflow span multiple Core instances at once — parallel stations, multiple rooms or sites, non-linear handoffs between them. Every device driver still runs inside its own local Core, in-process; Cloud only ever talks to a Core’s API, never to a driver directly, so hardware control stays exactly as local as it is today. This is the least mature piece of IvoryOS v2 — the dashboard exists, the cross-Core orchestration protocol doesn’t yet.
Built for what wet-lab automation actually needs.
This is the Optimize page in Core — every field on it maps directly to one of the capabilities on the right, generated from your own driver’s parameters.
Prep / Main / Cleanup phases
Setup and teardown steps run once per workflow; the main sequence is what repeats, gets batched, or gets optimized.
Batch-aware execution
Mark a step as “per-sample” or “batch.” A batch step runs once per group of N samples — for equipment that processes several samples at once — instead of once per row.
Closed-loop optimization
Ax, BayBE, and NIMO are wired in behind one suggest/observe interface, with parallel-trial batches, live convergence plots, and per-objective early-stop criteria.
Human-in-the-loop pauses
A workflow can pause and ask a person for a value — load a sample, confirm a reading — then resume exactly where it left off, from any device on the network.
Full run history
Every run, step, phase, and parameter is recorded to SQLite or Postgres — queryable later, not just streamed to a CSV and forgotten.
Typed, validated parameters
Every generated form inherits your driver's real type hints, so a string typed into a float field is caught before it ever reaches your hardware.
Design your lab before you install a thing.
This is the reason Core was split from the browser frontend in the first place: the Designer only ever needs an instrument’s schema, not a live instrument. Once that schema lives in the Hub alongside every contributed driver, picking a stack and opening a fully working Designer become the same click.
A driver is contributed
Someone shares a Python driver to the Hub — today, that registers install metadata: the package, its constructor arguments, how to launch it.
The schema is captured too
The same introspection Core runs against real hardware runs once against the driver, and the resulting schema is stored alongside it in the Hub.
The Designer opens immediately
Pick a stack in the Hub and the full Designer opens in your browser right away, forms and all — it only ever needed the schema, not a live connection.