Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Appendix C: JSON Schema Reference

This appendix documents the JSON model format used by petri-pilot for code generation. The schema defines what a valid Petri net model looks like — places, transitions, arcs, and the higher-level structures (roles, views, navigation, admin, event sourcing, simulation) that drive application generation.

The full JSON Schema is published at github.com/pflow-xyz/petri-pilot/schema/petri-model.schema.json.

Top-Level Structure

A model is a JSON object with four required fields and several optional ones:

{
  "name": "order-processing",
  "version": "1.0.0",
  "description": "Order fulfillment workflow",
  "places": [...],
  "transitions": [...],
  "arcs": [...],
  "constraints": [...],
  "roles": [...],
  "access": [...],
  "views": [...],
  "navigation": {...},
  "admin": {...},
  "eventSourcing": {...},
  "simulation": {...}
}
FieldRequiredTypeDescription
nameYesstringUnique identifier, kebab-case (^[a-z][a-z0-9-]*$)
versionNostringSemantic version (e.g., "1.0.0")
descriptionNostringHuman-readable description
placesYesarrayStates in the Petri net (min 1)
transitionsYesarrayActions/events (min 1)
arcsYesarrayConnections between places and transitions (min 1)
constraintsNoarrayInvariants that must hold
rolesNoarrayNamed roles for access control
accessNoarrayAccess control rules
viewsNoarrayUI view definitions
navigationNoobjectNavigation menu configuration
adminNoobjectAdmin dashboard configuration
eventSourcingNoobjectSnapshot and retention configuration
simulationNoobjectODE simulation configuration

Places

A place holds state — either token counts (classic Petri net) or structured data.

{
  "id": "order_received",
  "description": "Order has been received but not yet validated",
  "initial": 1,
  "kind": "token",
  "type": "string",
  "initial_value": null,
  "exported": false,
  "persisted": false
}
FieldRequiredTypeDefaultDescription
idYesstringUnique identifier, snake_case (^[a-z][a-z0-9_]*$)
descriptionNostringHuman-readable description
initialNointeger0Initial token count (min 0)
kindNo"token" or "data""token"Whether this place holds token counts or structured data
typeNostringData type for data kind places
initial_valueNoanyInitial value for data places
exportedNobooleanfalseWhether externally visible
persistedNobooleanfalseWhether persisted in event store

Data Types

For kind: "data" places, the type field specifies the data type:

TypeDescription
stringText value
int6464-bit integer
float6464-bit floating point
boolBoolean
map[string]int64Map from string keys to integer values
map[string]stringMap from string keys to string values
map[string]map[string]int64Nested map (e.g., per-user balances by asset)

Transitions

A transition is an action that fires when enabled. Each transition becomes an HTTP endpoint in the generated application.

{
  "id": "validate_order",
  "description": "Validate the order details",
  "guard": "amount > 0",
  "event_type": "OrderValidated",
  "http_method": "POST",
  "http_path": "/api/validate",
  "bindings": {
    "amount": "body.amount"
  }
}
FieldRequiredTypeDefaultDescription
idYesstringUnique identifier, snake_case
descriptionNostringUsed in OpenAPI spec
guardNostringBoolean precondition (guard DSL)
event_typeNostringPascalCase of idCustom event type name
http_methodNostring"POST"GET, POST, PUT, DELETE, PATCH
http_pathNostring/api/{id}Custom HTTP path
bindingsNoobjectParameter bindings from request

Guard Syntax

Guards are boolean expressions evaluated against the current state:

OperatorMeaningExample
==, !=Equalitystatus == 'approved'
<, >, <=, >=Comparisonamount > 0
&&, ||, !Booleana > 0 && b > 0
name[key]Map accessbalances[from]

Arcs

An arc connects a place to a transition (input) or a transition to a place (output).

{
  "from": "balances",
  "to": "transfer",
  "weight": 1,
  "keys": ["from"],
  "value": "amount"
}
FieldRequiredTypeDefaultDescription
fromYesstringSource element ID
toYesstringTarget element ID
weightNointeger1Tokens consumed/produced (min 1)
keysNoarray of stringsMap access keys for data places
valueNostring"amount"Value binding name

Constraints

An invariant that must always hold.

{
  "id": "conservation",
  "expr": "received + validated + shipped + completed == 1"
}
FieldRequiredTypeDescription
idYesstringUnique identifier
exprYesstringBoolean expression over place token counts

Roles

Named roles for access control, with inheritance.

{
  "id": "admin",
  "name": "Administrator",
  "description": "Full access to all operations",
  "inherits": ["manager"]
}
FieldRequiredTypeDescription
idYesstringUnique role identifier, snake_case
nameNostringHuman-readable name
descriptionNostringWhat this role represents
inheritsNoarray of stringsParent role IDs (inherits their permissions)

Access Rules

Maps transitions to allowed roles.

{
  "transition": "approve_order",
  "roles": ["manager", "admin"],
  "guard": "user.department == 'finance'"
}
FieldRequiredTypeDescription
transitionYesstringTransition ID or "*" for all
rolesNoarray of stringsAllowed roles (empty = any authenticated user)
guardNostringAdditional guard expression

Views

UI view definitions for forms, tables, and detail pages.

{
  "id": "order_detail",
  "name": "Order Details",
  "kind": "detail",
  "description": "Shows order information",
  "groups": [...],
  "actions": ["approve", "reject"]
}
FieldRequiredTypeDescription
idYesstringUnique view identifier
nameNostringDisplay name
kindNostring"form", "card", "table", or "detail"
descriptionNostringWhat this view shows
groupsNoarrayLogical groupings of fields
actionsNoarray of stringsTransition IDs triggerable from this view

View Groups

{
  "id": "customer_info",
  "name": "Customer Information",
  "fields": [...]
}

View Fields

{
  "binding": "customer_email",
  "label": "Email Address",
  "type": "email",
  "required": true,
  "readonly": false,
  "placeholder": "customer@example.com"
}

Field types: text, number, email, date, datetime, select, checkbox, textarea, password, hidden.

Navigation menu configuration. Generates /api/navigation endpoint.

{
  "brand": "Order Tracker",
  "items": [
    {
      "label": "Orders",
      "path": "/orders",
      "icon": "box",
      "roles": []
    },
    {
      "label": "Admin",
      "path": "/admin",
      "icon": "gear",
      "roles": ["admin"]
    }
  ]
}

Admin

Admin dashboard configuration. Generates /admin/* endpoints.

{
  "enabled": true,
  "path": "/admin",
  "roles": ["admin"],
  "features": ["list", "detail", "history", "transitions"]
}

Features: list (instance listing), detail (instance detail page), history (event history), transitions (manual transition firing).

Event Sourcing

Snapshot and retention configuration.

{
  "snapshots": {
    "enabled": true,
    "frequency": 100
  },
  "retention": {
    "events": "90d",
    "snapshots": "1y"
  }
}

Retention durations use the pattern ^\d+[dwmy]$ — number followed by d (days), w (weeks), m (months), or y (years).

Simulation

ODE simulation configuration for AI move evaluation.

{
  "objective": "win_x - win_o",
  "players": {
    "x": {
      "maximizes": true,
      "turnPlace": "turn_x",
      "transitions": ["move_x_0", "move_x_1"]
    },
    "o": {
      "maximizes": false,
      "turnPlace": "turn_o",
      "transitions": ["move_o_0", "move_o_1"]
    }
  },
  "solver": {
    "tspan": [0, 10],
    "dt": 0.01,
    "rates": {
      "move_x_0": 1.0,
      "move_o_0": 1.0
    }
  }
}

Objective Functions

The objective field uses guard DSL syntax extended with aggregate functions:

FunctionDescriptionExample
tokens('place')Token count at a placetokens('goal')
sum('place')Sum of values in a data placesum('score')
count('place')Count of entries in a map placecount('inventory')
minOf('place')Minimum valueminOf('health')
maxOf('place')Maximum valuemaxOf('score')

Solver Configuration

FieldTypeDefaultDescription
tspan[number, number][0, 10]Simulation time span
dtnumber0.01Initial time step
ratesobjectall 1.0Per-transition firing rates

Minimal Example

The smallest valid model:

{
  "name": "toggle",
  "places": [
    {"id": "off", "initial": 1},
    {"id": "on"}
  ],
  "transitions": [
    {"id": "switch"}
  ],
  "arcs": [
    {"from": "off", "to": "switch"},
    {"from": "switch", "to": "on"}
  ]
}

This defines a one-shot toggle: a token starts in off, the switch transition fires, and the token moves to on. Three fields, two places, one transition, two arcs — the simplest possible Petri net application.