OttoChain is a metagraph on the Constellation Network that turns JSON into executable workflows. Define state machines, scripts, and multi-party coordination entirely through JSON configuration — no custom code required.
Automating real-world workflows across multiple parties is hard. Traditional approaches force you to choose between:
- Smart contracts (Solidity/EVM) — powerful but rigid, expensive to deploy, and require specialized developers
- BPMN/workflow engines — flexible but centralized, no built-in consensus or cryptographic auditability
- Custom application code — maximum flexibility but zero portability, every workflow is a new engineering project
OttoChain offers a fourth path: JSON-encoded deterministic state machines that run on a distributed ledger with consensus, cryptographic proofs, and zero-code deployment.
OttoChain has two fundamental building blocks: state machines and scripts. Both are called fibers — lightweight, independently-addressable units of computation that live on-chain.
A state machine defines a workflow as a set of states, transitions, and events. Each transition has a guard (a condition that must be true) and an effect (a transformation applied to the state data). Guards and effects are written in JSON Logic, a portable rule engine format.
┌──────────┐ submit ┌──────────┐ approve ┌──────────┐
│ draft │────────────▶│ pending │─────────────▶│ approved │
└──────────┘ └──────────┘ └──────────┘
│
│ reject
▼
┌──────────┐
│ rejected │
└──────────┘
Everything — states, transitions, guards, effects — is defined in JSON:
{
"states": {
"draft": { "id": {"value": "draft"}, "isFinal": false },
"pending": { "id": {"value": "pending"}, "isFinal": false },
"approved": { "id": {"value": "approved"}, "isFinal": true },
"rejected": { "id": {"value": "rejected"}, "isFinal": true }
},
"initialState": { "value": "draft" },
"transitions": [
{
"from": {"value": "draft"},
"to": {"value": "pending"},
"eventName": "submit",
"guard": { "!": [{"var": "state.isEmpty"}] },
"effect": { "merge": [{"var": "state"}, {"event": "payload"}] }
}
]
}Key properties:
- Deterministic — Given the same state and event, the result is always identical
- Auditable — The complete machine definition is visible on-chain as JSON
- Versionable — Update workflow rules by deploying a new definition, no code changes
- AI-friendly — LLMs can read, write, and reason about JSON Logic natively
Script oracles are stateful computation units. Where state machines orchestrate lifecycle (which state am I in?), oracles handle logic (what's the answer?). An oracle exposes named methods that other fibers can call.
Think of them as on-chain microservices:
State Machine Script
┌───────────┐ _oracleCall ┌─────────────┐
│ playing │───────────────────▶│ Game Logic │
│ │ makeMove(x,y) │ │
│ │◀───────────────────│ - board │
│ │ {winner: null} │ - moves │
└───────────┘ │ - rules │
└─────────────┘
The tic-tac-toe example demonstrates this pattern: the oracle holds the board state and enforces game rules, while the state machine manages the game lifecycle (setup → playing → finished).
The real power emerges when fibers interact:
- Cross-machine triggers — One machine's transition fires an event on another machine
- Dependencies — A transition can read state from other machines before deciding
- Parent-child spawning — Machines can dynamically create child machines
- Broadcast triggers — One event fans out to many machines simultaneously
- Oracle calls — State machines invoke oracle methods during transitions
This composability lets you model complex multi-party systems. The Riverdale Economy example uses 17 machine instances across 6 types to simulate an entire economic ecosystem with manufacturing, retail, banking, consumers, monetary policy, and governance — all in JSON.
OttoChain runs as a metagraph on Constellation's Tessellation framework. Each node in the network runs three layers:
┌─────────────────────────────────────────────┐
│ Data L1 (Port 9300) │
│ Fiber processing: state machine events, │
│ oracle invocations, validation │
├─────────────────────────────────────────────┤
│ Currency L1 (Port 9200) │
│ Token transfers and balance tracking │
├─────────────────────────────────────────────┤
│ Metagraph L0 (Port 9100) │
│ Consensus, snapshots, calculated state │
├─────────────────────────────────────────────┤
│ Global L0 (Hypergraph) │
│ Network-wide consensus and finality │
└─────────────────────────────────────────────┘
Data L1 is where the action happens. When you send an event to a state machine or invoke an oracle method, the Data L1 layer:
- Validates the input (signature proofs, fiber exists, fiber is active)
- Evaluates the fiber (checks guards, applies effects, runs scripts)
- Processes triggers (cascading events to other machines)
- Handles spawns (creating child fibers if directed)
- Commits the result to the snapshot via Metagraph L0
All of this happens atomically — either the entire transaction (including all triggered cascades) succeeds, or nothing changes.
sequenceDiagram
participant Client
participant DataL1
participant FiberEngine
participant Evaluator
participant Triggers
Client->>DataL1: Send event (fiberId, eventType, payload)
DataL1->>FiberEngine: process(fiberId, input, proofs)
FiberEngine->>Evaluator: evaluate(fiber, input)
alt State Machine
Evaluator->>Evaluator: Find matching transition
Evaluator->>Evaluator: Evaluate guard (JSON Logic)
Evaluator->>Evaluator: Apply effect (JSON Logic)
else Script
Evaluator->>Evaluator: Dispatch to method
Evaluator->>Evaluator: Execute script
end
Evaluator-->>FiberEngine: FiberResult (success/failed)
alt Has triggers
FiberEngine->>Triggers: dispatch(triggers, updatedState)
Triggers->>Evaluator: evaluate triggered fibers
Triggers-->>FiberEngine: cascaded results
end
FiberEngine-->>DataL1: TransactionResult (committed/aborted)
DataL1-->>Client: Response
Every JSON Logic evaluation consumes gas — a measure of computational cost. Gas prevents infinite loops, bounds execution time, and makes costs predictable. The gas system tracks:
- Per-operation costs for each JSON Logic operator
- Total gas across the entire transaction including cascades
- Cycle detection to prevent infinite trigger loops
- Depth limits to bound cascade chains
ottochain/
├── modules/
│ ├── models/ # Domain types: StateMachineDefinition,
│ │ # Transition, FiberKind, FiberResult, etc.
│ ├── shared-data/ # Core engine: FiberEngine, FiberEvaluator,
│ │ # TriggerDispatcher, SpawnProcessor
│ ├── shared-test/ # Test utilities and shared fixtures
│ ├── l0/ # Metagraph L0 (consensus layer)
│ ├── l1/ # Currency L1 (token layer)
│ └── data_l1/ # Data L1 (fiber processing layer)
├── e2e-test/ # End-to-end tests and example definitions
│ └── examples/ # JSON definitions for all examples
├── docs/ # Documentation
├── diagrams/ # Architecture and state diagrams
└── deploy/ # Deployment scripts for Digital Ocean
The project uses Scala 2.13 with cats-effect, fs2, and circe — following tagless-final style throughout.
OttoChain's examples demonstrate the range of what's possible:
| Example | Machines | Complexity | Key Patterns |
|---|---|---|---|
| Tic-Tac-Toe | 1 SM + 1 Oracle | ⭐ | Oracle-centric architecture, self-transitions |
| Fuel Logistics | 4 SMs | ⭐⭐ | Cross-machine triggers, GPS tracking |
| Clinical Trial | 6 SMs | ⭐⭐⭐ | Multiple guards, bi-directional transitions |
| Real Estate | 8 SMs | ⭐⭐⭐ | Self-transitions, lifecycle management |
| Riverdale Economy | 17 instances | ⭐⭐⭐⭐ | Broadcast triggers, parent-child spawning |
The learning path goes from simple (tic-tac-toe) to complex (Riverdale), with each example building on patterns from the previous ones. See the Examples Overview for detailed breakdowns.
The choice to encode everything in JSON is deliberate:
-
Zero-code deployment — Define a workflow, submit it as JSON, it's running. No compilation, no deployment pipelines, no smart contract audits.
-
AI-native — LLMs understand JSON natively. An AI agent can read a state machine definition, understand the workflow, generate events, and even create new machines. This isn't theoretical — the OttoBot project demonstrates an AI agent autonomously playing tic-tac-toe through oracle interactions.
-
Portable and auditable — A JSON definition is the same everywhere. You can diff two versions, validate schemas at startup, and inspect the complete logic of any workflow without reading source code.
-
Composable — JSON Logic expressions can be nested, combined, and extended. OttoChain adds constructs like
_oracleCall,_trigger,_spawn, and_emiton top of standard JSON Logic to enable cross-fiber coordination. -
Deterministic — JSON Logic evaluation is pure and deterministic. Given the same context, the same expression always produces the same result. This is essential for distributed consensus — every node must agree on the outcome.
- New to state machines? Start with the Tic-Tac-Toe example — familiar domain, clean architecture
- Want to design workflows? Read the State Machine Design Guide
- Ready to deploy? Follow the Deployment Guide
- Evaluating the architecture? See the EVM Comparison Analysis
- Interested in AI integration? Check the AI Agent Protocol Layer proposal
- API Reference — HTTP endpoints for interacting with fibers
- Architecture Details — Deep dive into layer architecture and data storage
- Script Reference — Deployment and management scripts
- Contributing — How to contribute to OttoChain
