State Machine
Project-scoped state machine designs with a two-role workflow: developers define the vocabulary, product managers wire it into diagrams. Available in the web UI at embedhub.com/tools/StateMachine.
Useful for firmware control flows, onboarding flows, protocol handshakes — anything where a designer and an implementer need to agree on what states exist and how the system moves between them.
The two-role model
A state machine in EmbedHub is split into two files in your project:
| File | Owner | Contains |
|---|---|---|
state-machines/palette/palette.yaml | Developer | The list of legal states and events |
state-machines/diagrams/<slug>.yaml | Product manager | A diagram wiring palette states with transitions |
There is exactly one palette per project, and as many diagrams as you
want — for example happy-path.yaml, error-recovery.yaml, setup.yaml
— all sharing the same vocabulary.
A diagram can only use states and events that the palette declares. Saving a diagram that references something the palette doesn't define is rejected with a field-level validation error.
The palette
A small YAML file listing the states and events your firmware (or system) supports. The developer owns it because each id corresponds to something they've actually implemented. The palette is authored and updated via the EmbedHub CLI — the web UI only displays it.
version: "2.0"
states:
- id: init
description: "Boot-time initialization"
- id: calibration
description: "Sensor calibration routine"
- id: idle
description: "Ready, waiting for activity"
- id: sleep
description: "Low-power state"
events:
- id: init_success
description: "Initialization completed without errors"
- id: calibration_success
description: "Calibration finished within tolerance"
- id: calibration_fail
description: "Calibration outside tolerance; retry from init"
- id: idle_timeout
description: "No activity for the configured idle window"
Field rules:
idis lowercase, starts with a letter or digit, then letters, digits, or_. Must be unique withinstates(and withinevents). Ids are also what diagrams reference and what shows up on the canvas — keep them short and readable.descriptionis optional, shown next to the id when picking events in the diagram editor.
A diagram
Each diagram is a separate YAML file. The slug in the filename becomes the diagram's identifier in the UI and in URLs.
version: "2.0"
name: "Boot and idle"
description: "Power-on through calibration into the idle/sleep loop"
initial_state: init
transitions:
- from: init
to: calibration
event: init_success
- from: calibration
to: idle
event: calibration_success
- from: calibration
to: sleep
event: calibration_fail
- from: idle
to: sleep
event: idle_timeout
layout:
nodes:
- id: init
x: 120
y: 80
- id: calibration
x: 320
y: 80
- id: idle
x: 520
y: 80
- id: sleep
x: 720
y: 80
What gets validated when you save:
- Every
from,to, andeventmust exist in the palette. initial_statemust be one of the palette's states.nameis required.
layout is editor-managed positions for the drag-and-drop canvas and
doesn't affect the semantics — feel free to omit it when writing by hand.
A paired <slug>.svg is generated and saved alongside the YAML so the
diagram can be embedded in other project docs (see In your
docs below).
Permissions
Access is controlled by the project role permission system, scoped by folder prefix. Roles you'd typically configure on a project:
| Role | Permission |
|---|---|
| Developer | write on state-machines/palette/* and state-machines/diagrams/* |
| Product Manager | write on state-machines/diagrams/* only |
| Viewer | read (default for project members) |
The organization owner always has full access regardless of role.
Splitting the two folders is what lets you give a PM the right to edit diagrams without also giving them the keys to the vocabulary.
In the web UI
The State Machine page has two tabs that follow the two-role model:
- Palette — a read-only listing of the states and events available on the
project. Editing happens through the EmbedHub CLI; the tab shows the exact
commands to pull, edit, and push
palette.yaml. - Diagrams — a drag-and-drop canvas. Pick states from the sidebar to add them to the canvas, drag between handles to connect, and the editor prompts for the event from the palette. The Mark initial button sets the selected state as the entry point. Auto-layout runs a left-to-right graph layout. Read-only if you lack write permission on the diagrams folder.
The project page shows a State Machines section listing every diagram with a link into the editor, and inlines the most recent diagram as an image.
In your docs
Project documentation can embed a diagram by relative path — useful for
design docs that should always show the current state machine. The file
must use the .mdx extension; plain .md is handed to the browser
as-is and the relative path won't resolve.
The onboarding flow is captured here:

When the markdown viewer renders the page, the image resolves to the diagram's SVG from the same project. Update the diagram, refresh the doc, and the embedded image updates with it. See Markdown & MDX for the full set of MDX features.
From the CLI
Because the palette and diagrams are just files in your project, the standard file commands work:
# Pull the current palette and edit it locally
embedhub pull myorg/myproject state-machines/palette/palette.yaml
$EDITOR palette.yaml
embedhub push myorg/myproject state-machines/palette/palette.yaml
# List existing diagrams
embedhub list myorg/myproject --prefix state-machines/diagrams/
# Create or update a diagram from a local file
embedhub push myorg/myproject state-machines/diagrams/onboarding.yaml
See the CLI documentation for the full command reference.
For AI agents
An AI agent with the EmbedHub CLI installed can author both files the same way it would any other project file. The recommended flow:
# 1. Read the existing vocabulary so the agent knows what's available.
embedhub pull myorg/myproject state-machines/palette/palette.yaml
# 2. Generate or update a diagram referencing only ids that exist in the palette.
embedhub push myorg/myproject state-machines/diagrams/proposed-flow.yaml
The same prefix-based permissions described above apply: an agent
operating with a project role that has write on
state-machines/diagrams/* but not on state-machines/palette/* can
freely add diagrams but cannot extend the vocabulary on its own — exactly
the same boundary a human PM would hit.
When a diagram references states or events that aren't in the palette,
the push is rejected with 422 and a list of the offending paths, so the
agent gets immediate, structured feedback to fix the file.
The accompanying <slug>.svg rendered by the web editor is not required
for the YAML to be valid — agents can push a diagram YAML without one and
re-render later by opening the diagram in the web UI.