Concepts
A Node is the fundamental unit of computation in Peppy. It represents a runnable application or service that can expose interfaces (topics, services, actions) and consume interfaces from other nodes.
A node is defined by its configuration file (peppy.json5) which includes:
- Manifest: The node’s identity
- Execution: Commands to build and run the node, plus the parameters it accepts
- Interfaces: What the node exposes and consumes
Manifest
Section titled “Manifest”The manifest defines a node’s identity:
| Field | Description |
|---|---|
name | A validated identifier (ASCII letters, digits, _, -) |
tag | A short contract identifier (e.g., v1, donut). Must start with an ASCII letter; allowed chars are letters, digits, _, -. Dots are forbidden. |
labels | Optional metadata labels |
depends_on | Optional dependency declarations on other nodes (depends_on.nodes) or contracts (depends_on.contracts); see Contract implementation |
A node is uniquely identified by its name:tag combination.
Tags are not semver. Bumping a tag always means “incompatible with the previous one”: there is no notion of a backward-compatible patch or minor release. Pick a label that identifies the contract (v1, v2, donut), not a version number.
Execution
Section titled “Execution”The execution section defines how to prepare and launch the node:
| Field | Description |
|---|---|
language | The programming language of the node (rust, python) |
parameters | Optional parameter schema for runtime configuration |
build_cmd | Command to run during the node build phase |
run_cmd | Command to launch the node |
container | Optional container configuration (mutually exclusive with build_cmd/run_cmd) |
Interfaces
Section titled “Interfaces”Nodes communicate through three types of interfaces:
| Type | Description |
|---|---|
| Topic | Publish/subscribe messaging for streaming data |
| Service | Request/response communication for synchronous calls |
| Action | Long-running tasks with feedback and cancellation support |
A node can expose interfaces (make them available to others) and consume interfaces from other nodes.
Two higher-level constructs build on these: contract implementation, where the contract lives in a standalone document and any implementing producer can fill the consumer’s slot, and pairing, an exclusive 1:1 bidirectional topic exchange between two instances. See Choosing a communication pattern for how to pick between all of them.
Node Instance
Section titled “Node Instance”A Node Instance represents a single running execution of a node. Each instance has:
- Instance ID: A unique identifier for this running process
- PID: The process ID (when running locally)
- State: Its runtime lifecycle state:
startingwhile it comes up,runningonce started, then the terminalfinished(clean exit) orfailed(crash) if its process exits on its own - Health: The result of the core node’s latest liveness probe against a running instance,
healthyorunhealthy(not applicable once the instance is terminal)
A node can have multiple instances running simultaneously. For example, you might run multiple instances of a camera node, each connected to separate physical devices.
Node Stack
Section titled “Node Stack”The Node Stack is the central data structure that manages all active nodes in the system. It maintains a directed acyclic graph (DAG) where:
- Vertices are node entities
- Edges represent dependency relationships, pointing from a dependent node to its dependency. These edges are derived from interface connections between nodes (exposers and consumers).
Key responsibilities
Section titled “Key responsibilities”- Dependency Management: Tracks which nodes depend on which other nodes
- Interface Validation: Ensures nodes expose the interfaces their dependents require
- Instance Lifecycle: Manages the creation, health tracking, and removal of node instances. Running instances are continuously health-probed; a failing probe flags an instance
unhealthybut never removes it, so an instance leaves the stack only when it is explicitly stopped or removed. See Instance health and lifecycle for details. - Root Node: Always contains a root node (the core node) that cannot be removed
How dependencies work
Section titled “How dependencies work”When a node is added, the stack:
- Validates that all required dependencies exist
- Checks that dependencies expose the required interfaces
- Tracks pending requirements when dependencies are not yet available
- Resolves pending requirements when dependencies are added
Stack inspection
Section titled “Stack inspection”The stack-list API exposes the nodes and dependency edges as a serialized JSON graph for programmatic access. The peppy stack list command renders that graph as operator-friendly tables inside a separate outer panel for each live core node.
The Core Node
Section titled “The Core Node”The Core Node is a special node that serves as the root of the node stack and is always present. It is the daemon (peppy service serve) that the peppy CLI talks to. It is responsible for:
- Dependency Creation: When a node consumes an interface, the core node creates a dependency on the node that exposes that interface
- Stack Management: Managing the lifecycle of all other nodes in the system: spawning them, probing their health, and tearing them down so none is left orphaned when the daemon stops or dies
- System Coordination: Acting as the central coordinator for the local Peppy runtime
When the core node shuts down cleanly it stops every spawned node (cooperatively, then force-killing any straggler’s process group); if it dies unexpectedly, each node’s watchdog notices the missing heartbeat and shuts the node down after a grace period. See Daemon shutdown and orphan prevention for details.
Each Peppy runtime has exactly one core node that manages its local DAG of nodes. In a distributed deployment, multiple runtimes (each with their own core node) can run on separate machines. The core nodes operate independently, managing their local node stacks without a centralized controller. Nodes communicate across runtimes through the shared messaging layer, enabling a fully decentralized system where each core node is responsible only for its own set of nodes.
Each machine runs at most one daemon per Peppy data root. peppy service serve takes an exclusive lock at startup, and a second invocation refuses to boot while the first runs.
Because core nodes are addressed by name over the messaging layer, every core node reachable over the same router or federation must have a unique name. Each daemon derives a stable, machine-specific name by default, or you can pin one with core_node_name; a daemon whose name is already taken refuses to boot.
Summary
Section titled “Summary”Node Stack├── Core Node (always present)│ └── Instance (always a single instance)│└── Other Nodes ├── Node A │ ├── Configuration │ └── Instances (1 or more) │ └── Node B ├── Configuration └── Instances (1 or more)