Hello,
lab.
A working twin of every instrument, every service, every facility node. In a browser tab.
Break every instrument.
Tuesday afternoon.
Train the rare cases. Demo the integration. Spin up a tenant in thirty seconds. Burn nothing.
A browser-resident twin of every instrument, service, and facility node. Clickable, breakable, integrated with L7|ESP.
What it is, what you can do with it, and who already gets value from it.
A practice lab. So the real one keeps doing real work.
For the reader who hasn't worked in a pharmaceutical or food-science lab before, here's what we mean.
Real labs are expensive places. A single instrument can run half a million dollars. The reagents are restricted, the access is gated, the schedule is full. Every hour the lab spends on training, on integration testing, or on a customer demo, is an hour it isn't doing the work it was built for.
Virtual Lab is a software stand-in for that whole environment. It runs in a web browser. Every instrument acts like the real one, with the same controls and the same readings. People can train on it. Software can be tested against it. Customers can be shown what their workflow will look like. None of it touches a real machine, a real sample, or a real schedule.
The same digital twin can also reflect a real lab back. Live readings from real instruments stream into the same panels. You can see what's happening across a site without being there, and you can rehearse the rare failures (the leaking seal, the dropped connection) on the practice version before they happen for real.
A working simulator, not a mockup.
Every instrument, every service, every facility node lives as a state machine the operator can click, drive, and break, using the same surfaces and the same wire formats they'd touch in a real plant.
Twins, not screens
65 instruments + services modeled as live Python state machines. Phase transitions, derived metrics, fault states, sensor noise. All real, not paper.
The bus is the spine
One event bus. Frontend, MQTT subscribers, and ESP pipelines all read the same stream of instrument_state, command_result, log. No second source of truth.
Multi-tenant by design
Per-org subdomains (amaryllis.digitaltwin.l7esp.com). Instances, layouts, and MQTT topics scope to the tenant; cross-org access surfaces as 404.
Fault injection built-in
Every twin declares its failure modes. Operators inject them on demand (agent offline, calibration drift, comms lockup, probe slip, transit excursion) to rehearse the rare cases.
L7|ESP integrated
Twins are reachable from real ESP pipelines, applets, and notifications. Workflows that run against real hardware run unchanged against the simulator.
Standards-aware
ISA-88 procedural framing on chains (procedure / unit procedure / operation / phase). ISA-95 enterprise model documented. NE 107 + ISA-101 stylings on faceplates.
Five views, one source of truth.
Every twin auto-publishes to every surface via shared row builders + a faceplate registry. Click a balance from any view; you see the same state machine.
① Bench
The training-bench grid: status cards with in-card controls (PH 4 CAL, MEASURE, START RUN). The "Hello world" of the platform. Fastest path to "this works."
② Map / floor
A 2-D plan view of a real site. Drop assets, draw bench-link references that fan out on hover, click any node for its faceplate. JSON-authored. No designer required.
③ Globe
A 3-D globe rolls up every layout to its physical site — crossfading to political boundaries on zoom. Live status dots, fault propagation, chip-per-layout fan-out on hover. Briefing room view.
④ Faceplate
The HMI-style operator panel that pops out of any tile: phase, setpoints, command buttons, active-fault banner. The same panel an operator would use on the real instrument.
⑤ Inventory + Admin
All assets, all faults, all WS hooks in one place. Designer for layouts. Org provisioning. Fault rotation toggle. Docs section embedded. ISA standards reference.
Live everywhere, all at once
WS bus pushes state to every open surface simultaneously. Faulting a centrifuge on the bench lights it red on the floor map and the globe in the same tick.
The moving parts — and where truth lives.
Four pieces, each with a single job. Everything the browser shows, ESP consumes, or the database remembers comes off one event stream.
Commands are requests. The bus carries facts.
Every caller — human, ESP, or customer system — goes through the twin. The twin validates, then either refuses (error returns to the caller only) or changes state — and only what actually happened is published. Nothing writes the bus directly; that's why there's exactly one truth.
wait_for gating on results.
API / MQTT clients input — REST commands under API tokens (customer systems, scripts, CI); MQTT is primarily the read side — subscribers consume the state stream like any real instrument feed.
Practice mode: nothing real gets touched.
L7|ESP runs the same workflow it would run in the cleanroom — but the commands land on simulators. Train people, demo integrations, and rehearse failures without consuming an instrument-hour.
Same glass, real feed.
The views, the history, and the audit trail don't care where events come from. Today they run against the simulated fleet; pointing them at a real site is a change of feed, not a rebuild.
Rehearse the rare cases on demand.
Every twin declares its failure modes in code. The Faults admin tab lets you inject one with a click, or run an automatic rotation that walks the whole catalog at a steady cadence. Perfect for live demos, training scenarios, and integration regression.
Declared, not guessed
Each twin's FAULTS tuple enumerates its real-world failure modes (agent_offline, calibration_drift, comms_lockup, probe_slip, seal_breach). No surprises in prod.
Faceplate banner
An active fault paints a red banner across the faceplate with the fault name + a "clear" affordance. Operators learn to recognize the visual signature before they meet it in the wild.
Avatar fault states
Hero twins (bioreactor, centrifuge, HPLC, reactor, plate robot, AGV) animate their fault state with leaks, smoke, error glyphs. Training videos write themselves.
Geo + globe propagate
A site with any faulted instrument lights its marker amber/red and flips the layout chip color. Briefing-room view turns into a live incident map.
Demo rotation
Admin → Faults → toggle "auto-rotate" picks a random twin every N seconds, injects a random fault, then clears it. Survives a long demo without operator input.
WS-disconnect surfaced
Bus disconnect drops a banner across every open page, so operators never debug a stale view thinking the data is live. Comes back automatically on reconnect.
One backend, many orgs.
Each customer (or internal team) gets their own subdomain: amaryllis.digitaltwin.l7esp.com. The leading label routes to a single shared backend; instances, layouts, and MQTT topics are scoped so tenants are opaque to each other. Falls through to a single-tenant default install when no org is configured, so existing deployments aren't affected.
Subdomain routing
Caddy fronts the container with a wildcard cert for *.digitaltwin.l7esp.com; backend reads X-Forwarded-Host and resolves the org. One cert, infinite tenants. Onboarding a new org is zero DNS work.
Scoping
Instances, layouts, MQTT topics, WS bus traffic. Cross-org access surfaces as 404. The MQTT broker enforces it too: SUBSCRIBE filters outside vlab/<own-org>/… are rejected with SUBACK 0x80.
First-class Org records
Orgs aren't just slugs. They carry display name, owner contact, branding accent, billing tier, status. Provisioning + drop is a one-click admin operation with cascade-delete confirmation.
Auth modes
Claim mode (dev): X-Org header is trusted at face value. Enforce mode (prod): VLAB_TOKEN_MAP env var flips on bearer-token auth (REST + WS + MQTT CONNECT), and the token's org must match the subdomain.
Topbar identity
Browser tabs read Amaryllis · Alzey, Germany · Parenteral fill/finish (EMEA). An org chip in the topbar lets you switch contexts; the chosen org survives across page loads and the geo/globe routes.
Demo content built-in
Three orgs ship out of the box. Amaryllis (10 GLP-1 pharma sites), FoodLab (food-science workflows), Shelley Group (25 layouts across a literary multinational). Open any subdomain; the model is immediately clickable.
Workflows hit twins like real hardware.
vlab is the simulator side of an L7|ESP pairing. ESP chains drive twin commands; twins push state and events back through the bus; the operator runs the same workflow against the simulator that they'd run in the cleanroom.
What flows from ESP to vlab
- Chains walk procedures across multiple twins: sample receipt, LIMS submission, instrument run, results capture, label print.
- Chains call DE functions (
VirtualLab.command,VirtualLab.wait_for) keyed byinstrument_id. - Twin commands take typed args and return structured results that ESP writes back into the chain's variables.
- ESP applets render the chain's
onrenderJS, so the same buttons appear in vlab as in a live ESP deployment.
What flows from vlab to ESP
- State events stream over MQTT. ESP subscribers (or the customer's own consumer) read them like any real instrument feed.
- Chains can wait_for a twin's
phase === 'done'before advancing. - Fault events propagate up so ESP notifications surface them.
- Smoke scripts walk a chain end-to-end on demand, useful for ESP integration regression.
Today's shipping examples: Amaryllis carries 4 GLP-1 chains + 10 per-site smoke scripts. FoodLab carries a carrageenan-texture chain spanning material intake, LIMS, prep, TA.XTplus run, and label print.
Four jobs vlab is already doing.
Not a hypothetical. These are the active uses today across sales, services, engineering, and customer success.
Sales · live demo
Pop a tenant subdomain in the browser, click through the bench → map → globe progression, inject a fault to show NE-107 status propagation. No hardware, no booking, no ramp-up. Works on a laptop in a coffee shop.
Services · onboarding
Spin up a customer-shaped org (literary, pseudonymous, or real) ahead of kickoff. Reference layouts let a CS team walk a new customer through "this is what your floor would look like" before the first deployment workshop.
Engineering · integration prototyping
Build the ESP chain against the twin. Iterate against the twin's fault catalog until the chain handles every declared failure. Ship to the customer site with confidence the workflow already survived the unhappy paths.
QA · regression
Smoke scripts walk every chain end-to-end. Twin state machines have a deterministic mode for tests. CI catches the "we changed the chain and forgot the bus envelope" bug before a customer does.
Operators · training
Run the carrageenan texture chain. Watch the TA.XTplus avatar animate compression. Inject probe_slip. Recognize the fault on the faceplate before you ever see it in the lab. Build muscle memory cheap.
Product · feature dogfooding
Every ESP-side feature gets exercised against vlab before the first customer call. The product team uses the same surfaces a customer would. Bugs surface inside the team, not in a deployment retrospective.
The roadmap.
Each step is independently shippable. Listed in rough priority order.
NowConnector contract spec
Standardize the interface every connector implements (health(), idempotent command(), subscribe(), replay()) as a schema + Python ABC. Every existing twin refactors to conform. The spec becomes the deliverable.
NowFault catalog harness
Promote the existing fault-injection mechanism to a regression suite. CI runs every twin × every declared fault and asserts the contract behavior. Output is a per-instrument certification badge.
NextTwin as benchmark — conformance reporting
The twin stays a simulator and acts as the yardstick: it embodies how the instrument should behave (phase timings, declared fault modes, recovery rules). Customers see their real unit + connector measured against that contract — expected vs. actual, side by side. Nothing real flows into the twin.
NextDeeper ISA adoption
NE 107 status pyramid in operator overlays. ISA-101 styling rules across faceplates. Optional ISA-88 physical hierarchy + ISA-18.2 alarm classes once a customer asks for them.
'27Twin as mirror — reverse-twin
The twin stops simulating: the same faceplates, bench cards, and globe markers get fed by the real device's telemetry instead of the internal state machine. Real data flows in; the twin reflects instead of predicts. Remote monitoring wearing a UI operators already know.
'27SLA + alerting per connector
Latency, drop rate, replay gap. Emitted by every connector via the standard contract. Dashboards built once, reused across vendors. Alerts route to support before customers notice.
Memory, refinery, brain — and the nervous system.
The adjacent platforms each own one direction of data flow. None of them can command an asset, and none can rehearse.
AVEVA PI — the plant's memory
The process-industry historian: millions of tags, decades of retention, bulletproof collectors. Records what happened. Read-only by design — it never tells an instrument to do anything. Its moat is validated interfaces and operator trust, not technology.
TetraScience — the lab's data refinery
Harmonizes raw instrument files into analytics-ready scientific data in the cloud. Refines what was produced. One-way, after the fact. Its moat is a large validated connector library.
Cognite — the brain that has read everything
Knowledge graph over historians, ERP, P&IDs, and 3D models; AI agents on top. Contextualizes and recommends. Its "twin" is descriptive — it knows about the pump. It doesn't act, and it consumes PI rather than replacing it.
L7|C2 — the nervous system
Executable twins with live state: validates and executes commands, rehearses before acting (simulation), and mirrors real fleets (reverse-twin). C2's twin is the pump — it can exist before the instrument does, and refuse a bad command after it does.
All three are natural consumers of C2's fact stream — cleaner than scraping historians. The honest gap runs the other way: their integration libraries are mature where C2's real-hardware lane is young. Complementary, not competitive.
One codebase, two products: the Engine and the Console.
The seam between them is the API — and it's already proven by four clients that aren't the UI.
C2 Engine headless
Event bus · executable twins · reservation + engagement ledger · time-series store · protocol adapters (MQTT today). Consumed over REST, WebSocket, and MQTT with token auth; OpenAPI spec included. Can sit under ESP, a customer's dashboards, or any existing operations UI.
C2 Console reference client
The views in this deck — bench and floor twins, World/US sites, the asset Network, the equipment ledger — organized as one navigation bar that descends the ISA-95 hierarchy: Enterprise → Sites → Work center → Equipment.
The seam is real today
Four non-UI clients already ride the same API the Console uses: the ESP integration plugins, the phone provisioning flow, the MQTT mirror lane for real devices, and the automated QA harness. Separating the products is a packaging decision, not an engineering project — and until a second consumer of the Engine exists, building them together is why features ship in days, not quarters.
The hierarchy exists. The story doesn't lead yet.
Three honest gaps, then three proposals to ponder. They blend — this is a menu, not a ballot.
Landing is generic
Everyone signs in to the same bench, whatever their role. The navigation bar descends ISA-95 — Enterprise → Sites → Work center → Equipment — but nothing about the first screen says "you are an executive, start at the network" or "you run Alzey, here is Alzey."
The descent doesn't pull
You can click from World to a site to a bench to an instrument. But no level gives you a reason to go down — no "this site is the one that needs you today" scent leading from altitude to detail.
Statistics have no home
Analysis lives where it happened to land: an admin System tab, sparklines on the asset table, rhythm sparks on the network view. There is no answer to "where do I go to understand utilization, trends, or capacity?" — at any level.
Statistics live in the descent.
Every level of the hierarchy gets the same three-part anatomy. Analysis is wherever you're standing, scoped to what you see.
The anatomy, at every level
The picture (the live view you have today) · the numbers strip (KPIs + sparklines rolled up from the level below: utilization, runs, faults, OEE) · the children (each child row carries its own spark; clicking descends). Org shows per-site numbers, site shows per-facility, facility per-instrument, instrument its own time series.
The story it tells
Sign in → land at your role's altitude (exec at Network/World, site lead at their site, tech at their bench). The numbers strip says which child is off-trend; that's the scent that pulls you down a level. Repeat until you reach the faceplate that explains it.
ForWhy it's attractive
"Where are the stats?" stops being a question — one recurring widget grammar (spark → trend → distribution), narrower scope each level. Builds directly on the deep rollups and role lens already in motion. No new navigation.
AgainstThe honest cost
Touches every surface — each view grows a numbers strip, each level needs its rollup queries. Incremental and independently shippable per level, but it's a season of work, not a sprint.
Children — rolled up from below · click descends
Open faults in this rollup
Faceplate · live
Pressure trend · 30d its own series — narrowest scope
Recent runs
The command deck — one analytical home.
A new org Home at sign-in: everything statistical at a glance, deep-linking into the live hierarchy to act.
What's on the deck
Site-health tiles (worst-state colored, one per site) · top-line KPIs · run-rate and OEE trends · fault Pareto · capacity vs. commitment from the reservation ledger. Every tile is a door: click a site tile → that site's map; click a fault bar → the instrument's faceplate.
The story it tells
Sign in → the deck tells you how the operation is doing in ten seconds → you jump into the live views only to act. Analysis concentrates in one place (and grows into a real /analytics view); the hierarchy views stay purely operational and calm.
ForWhy it's attractive
One obvious answer to "where do I look?" — and the most exec-ready landing page the product could have. Fastest of the three to demo credibly; most of the queries already exist behind the System and Network views.
AgainstThe honest cost
A second surface to keep truthful — dashboards drift. Partially duplicates Network and System. And it weakens the descent story: people bookmark the deck and stop living in the hierarchy.
OEE trend · 90d
Fault pareto · 30d
Supply & demand — dual-role every tile and row is a door →
Texture analyzer · TA-01
Extruder · EX-2
Live event log
Where the analysis went
Two tenses, one hierarchy: the NOW ⇄ TRENDS lens.
Statistics aren't a place — they're a lens over the descent you already have. Position chooses scope; the toggle chooses tense.
How it works
A global toggle + time-range control in the topbar, next to the view switch. Flip it and the level you're on re-skins: map markers trade status dots for trend arrows, bench cards flip from live state to 30-day utilization / fault-rate chips, the Assets table becomes a stats table, an instrument's detail becomes its trend explorer.
The story it tells
There is exactly one geography of the operation, and you read it in two tenses: what is happening and what has been happening. Navigate the same way in both; nothing moves, only the ink changes meaning.
ForWhy it's attractive
Zero new navigation and the deepest respect for the hierarchy discipline. The per-instrument trend explorer — the top item on the PI-parity list — becomes simply the TRENDS face of the Tablet level. Very Tufte: maximum reuse of the reader's spatial memory.
AgainstThe honest cost
Every surface needs a stats variant, and the mode must be unmistakable (obvious chrome shift — a mode users forget they're in is a support ticket). Likely blend: A's numbers strip as the spine, C's toggle where a surface earns it, B only if execs demand a poster page.
Same floor, same positions — the ink changed meaning
Vendor network · trends
Overlay any variable, any range — pressure · — utilization · ⚠ 380 bar
Run durations · 90d
Statistics · 90d
One network view, from two suppliers to two thousand.
Not a big-org mode and a small-org mode — one paradigm whose unit of display adapts to the population. Breadth collapses at scale; depth expands at intimacy.
Rank + a first-class tail
Supply chains are brutally Pareto — the top 20 relationships are the picture. Always show the top-K as full ribbons; collapse the rest into a tail node — "⊞ 1,847 more · 12% of volume" — with its own ribbon (ink stays honest), its own spark, and click-through to a searchable table. For a two-supplier org the tail is empty and the view is byte-for-byte today's view.
Segments — the missing altitude
Between one org and 3,000 sits the counterparty segment: API suppliers, packaging, contract labs, logistics. Past ~100 counterparties the columns show ~8 segment bands (count · volume · trend · worst-state); clicking one re-renders the same ego view scoped to the segment — the maps-on-maps recursion, applied to the business graph.
Reciprocity: depth for breadth
The view has a fixed attention budget. When breadth is small, spend it on depth automatically — the two-supplier shop sees reservation terms, recent runs, and per-asset sparklines expanded inline; Lilly's view pushes those one click down. The small org's page feels made for them; the giant's feels in control — one codebase.
The thresholds — data-driven, never a setting
≤ 12 counterparties → everything expanded (today's view) · 13–100 → top-10 + tail node · > 100 → segments + top movers + tail · > 1,000 → search becomes the front door, segments the backdrop. The 3D graph stays capped as the top-relationships showpiece.
Supplier segments · 30d click a band → same view, scoped
Top movers · 7d
FoodLab — your supplier same components — depth got the budget
Recent runs on reserved capacity
Two self-hosted twins, one verified handshake.
Each org holds its own keypair; the private half never leaves its walls. An invite is signed by the sender and encrypted to the recipient, lands in a request queue an admin accepts, and both sides derive the same shared key locally — no secret ever crosses the wire.
The hub relays ciphertext it can't read.
Once paired, the owner encrypts each asset update with the shared key and wraps it in a thin routing envelope. The hub sees only opaque metadata; the consumer verifies the signature and decrypts. The federated asset appears as a read-only twin in Partner Assets.
Assembled from mature, self-hostable parts.
Nothing exotic, nothing cost-prohibitive: a blind relay, an end-to-end layer, org keys, and a bit of app glue. We don’t invent crypto — we compose it.
crypto_box for pairwise (simple, beta); MLS for group-scale membership + key rotation done to standard.