Priostack · Engineering Blog · 21 August 2026 · 9 min read

Why Petri Nets, Not State Machines

Deep diveQubitEngineering

Every workflow tool eventually has to pick a shape for "what happens next". Most reach for the state machine: a set of states, a set of transitions, one current state, an arrow to the next. It is easy to draw and easy to explain, which is exactly why it is everywhere. It is also the wrong primitive for the work Priostack actually does, and we would rather say so plainly than paper over it.

Under Qubit - the single engine beneath a Priostack app, the API, and the edge - the formalism is the Petri net. Not as an academic flourish, and not only for the parts that "look concurrent". A business process, a legal case, and a relay interlock on a machine out in a field are all the same kind of object to the engine, because they are all Petri nets. This piece is about why that choice was made, what it buys, and where it stops.

The short version: a state machine can only ever be in one state, so it cannot honestly describe two things happening at once, a resource being fought over, or two branches that must rejoin before anyone proceeds. A Petri net can, and it does so in a way you can actually check with a proof rather than a hope.

1. The one-state problem

A finite state machine has a single active state at any moment. That single fact is the whole limitation. As soon as your real process has two independent things in flight - an invoice being approved while stock is being reserved, a patient's lab result pending while their next appointment is being scheduled - the state machine has to encode the combination as a new named state. Two independent activities with three states each become nine combined states. Add a third and you are at twenty-seven. This is the state explosion, and it is not a tooling problem you can optimise away; it is what the formalism forces you to do to tell the truth about concurrency.

In practice teams do not draw the twenty-seven states. They fake it. They add flags, side variables, and "waiting for X" pseudo-states, and the diagram quietly stops being the specification. The moment the picture and the behaviour disagree, you have lost the one thing a formal model was supposed to give you: the ability to reason about the system by looking at it.

The tell. If a workflow diagram needs a paragraph of prose beside it explaining "of course, in reality these two can happen in parallel, and the flag on the left really means…", it has outgrown its formalism. The concurrency was always there; the notation just could not hold it.

2. A Petri net in one page

A Petri net has two kinds of node and one kind of moving part. Places hold state - draw them as circles. Transitions are the things that can happen - draw them as bars. Arcs connect places to transitions and transitions to places. State lives in tokens sitting in places, and the arrangement of all tokens across all places at a given instant is the marking - the true, whole state of the system.

A transition may fire only when every place feeding into it holds a token. When it fires, it consumes those input tokens and produces tokens in its output places. That is the entire rule. Everything else - sequence, choice, parallelism, joining, resource limits - is just how you wire places and transitions together.

   reserve stock                     both done
      (t1)                            (t2)
  (o)------[ ]------>(o)               |
 order          stock held  \          v
  |                          >--[ ]-->(o) ship
  (o)------[ ]------>(o)     /
 order          payment ok
   take payment    (parallel branch)

The key move is that the initial order can put a token into two branches that then run independently. "Reserve stock" and "take payment" are genuinely concurrent - neither waits for the other - and the ship transition is a real join: it cannot fire until both upstream places hold a token. No flags, no combined super-state. The concurrency and the synchronisation are drawn, not described.

3. What the net expresses that a chart cannot

Three things a state machine cannot state directly, and a Petri net states as structure:

This is why the mapping from real-world processes to nets feels honest rather than forced. Enterprise Integration Patterns, for instance, are full of splits, aggregators, and resequencers - patterns that are painful to model as one state at a time and natural as tokens flowing through transitions.

4. Properties you can decide, not just test

The pragmatic payoff is that Petri nets come with a body of decidable analysis. You do not have to run a thousand scenarios and hope you covered the bad one. For a large class of nets you can answer, up front, questions that matter:

Because Qubit runs the model as an actual net rather than as generated code with the semantics baked in and lost, these questions stay askable about the thing you deployed - not about a paraphrase of it.

An honest caveat on the theory. Some of these properties are expensive to decide in the general case, and reachability in the fully general model is famously hard. In practice the nets that come out of well-formed business and control models are far more constrained than the worst case, which is what makes analysis tractable. We lean on that structure; we do not pretend the general problem is cheap.

5. One formalism from strategy to a relay

Here is where the choice earns its place across the whole stack. A third-party Priostack app ships a bundle of models, not code: ArchiMate 4.0 for structure, BPMN 2.0 for what it does, CMMN 1.1 for cases, DMN 1.3 for decisions, Camel for routes out, IFML for views. Three of those - the process, the case, the decision - are behaviour, and behaviour is where a formalism has to be picked.

Qubit runs BPMN as Petri nets. A CMMN case, with its stages and its "this may happen when that is available" milestones, is concurrency and guarded availability - exactly what tokens and enabled transitions describe. And an interlock on a machine at the edge is, at bottom, the same object: places for sensor conditions, transitions for the moves you are allowed to make, tokens for what is currently true.

So the reach is literal. The same formalism describes a strategy-level flow a manager signs off, the case a caseworker lives in, and the relay logic on a board bolted to a tank. That is not a marketing sentence; it is the reason we did not need three engines.

  BPMN process  ─┐
  CMMN case      ├─► one Petri-net semantics ─► Qubit ─► every surface
  edge interlock ─┘        (places, transitions, tokens)

6. Safety by construction at the edge

The edge is where the difference stops being philosophical. Skopin, Priostack's Linux edge OS, runs an app's models and policies on a real device - a technology-layer Node in an ArchiMate model can be a physical board out in the field. On a control device Skopin runs a fast, repeating scan cycle, on the order of 120 ms. Sensor conditions become read-only guards: a transition that would open a valve is simply not enabled unless the pressure place says it may be.

The important word is unreachable. An unsafe interlock state - two conflicting actuators energised at once, say - is not something Skopin checks for and rejects at runtime. It is a marking the net cannot reach at all, because no firing sequence produces it. That is safety by construction, and it is a property of Petri-net semantics, not a feature we bolted on. A runtime check can have a bug or a race; a state that does not exist in the reachable set cannot be entered by any timing.

Family framing. Priostack orchestrates, Skopin executes at the edge, and Reflex - the reference board, a small Linux device with Wi-Fi, 4G, eSIM, and 802.15.4 with room for Z-Wave, XBee, or LoRa - anchors it in the physical world. The net is what travels intact from the first to the last.

7. The same net on every surface

Because the behaviour is a net and Qubit runs it natively, the same model means the same thing on every surface. The identical app runs as a native mobile app where the Android or iOS renderer draws the IFML views, as a PWA in the browser via a JavaScript renderer, and on edge devices under Skopin. What changes between them is only who draws the screen.

The split that keeps this honest: interactive view state is local to the device and works offline, while the durable process, case, and decision state - the marking that matters - is canonical on the engine. Your phone can go dark in a lift and the net does not fork; the tokens live where the truth lives. This is only clean because there is one formalism underneath. If the mobile app were generated code and the edge device ran a different hand-written state machine, "the same app" would be a claim you could not actually make.

8. Where the formalism stops being enough

A model is a boundary, and the honest thing is to name what falls outside it. A Petri net expresses control flow, concurrency, resources, and synchronisation. It does not express "take a photo", "sign this with a key", or "place a call". Anything a model cannot express, the super app provides as a permissioned native feature - camera, keys, telephony - granted with a purpose and an expiry and enforced at the moment of invocation, not waved through at install time.

That boundary is deliberate. The net stays analysable precisely because it does not try to swallow arbitrary side effects; those live outside it, behind capabilities the user can see and grant. It also connects to the autonomy model: what a step is allowed to do on its own runs from Suggest, which only proposes, up to Continuous, which runs unattended and can be stopped - and money and account-security actions are capped at Confirm no matter what an app declares. The net says what can happen next; the autonomy grant and the capability say what is allowed to happen without asking. Two different questions, kept separate on purpose.

And the analysis has costs, already noted: some decidable properties are expensive, timing and probability are not part of the plain model, and a net that is well-formed is not automatically a net that is correct - it can be provably deadlock-free and still encode the wrong process. Petri nets buy you rigour about structure. They do not buy you good judgement about what the structure should be. That part is still yours.

Conclusion

The choice is not aesthetic. A state machine forces a single active state, and that single constraint makes it lie about concurrency, contention, and synchronisation - or forces the diagram to stop being the spec. A Petri net holds those directly, as places, transitions, and tokens, and pays that back with properties you can decide rather than merely test: reachability, boundedness, liveness, deadlock-freedom.

That is why one formalism runs from a strategy-level BPMN flow to a CMMN case to a relay interlock under Skopin, and why an unsafe state at the edge can be made structurally unreachable instead of runtime-checked. Qubit did not need three engines because there was only ever one kind of object underneath. And where the net stops - side effects, native features, judgement - we would rather draw the line in the open than pretend the model covers ground it does not.

Related reading - See how the engine's context store derives position from execution state in Geometric memory, how durable state is kept canonical in QDB, and how models map to structure across the stack in ArchiMate layers.

Priostack Engineering

Technical deep-dives on process automation, workflow engines, and the systems behind Priostack.