Skip to content

The Node stack

The node stack is the central registry that manages all your nodes and their relationships in PeppyOS. Think of it as a directed graph where:

  • Nodes are registered configurations that can have multiple running instances
  • Edges represent explicit dependencies declared via depends_on in the node manifest

When you add a node to the stack, PeppyOS validates that all its dependencies are satisfied—ensuring that any topics, services, or actions your node relies on are provided by other nodes already in the stack. This dependency validation prevents runtime errors and helps you understand how your nodes interconnect.

The stack always contains a core node and an instance of it at its root, which coordinates the lifecycle of all other nodes. You can query the stack to see which nodes depend on each other, visualize the dependency graph, and manage node instances. The node stack is started as a daemon on your system and the peppy binary communicates with it to execute user actions.

Inside your node directory, add it to the PeppyOS stack by running:

Terminal window
peppy node add .

This command registers the node with the core node by staging a snapshot of the source directory. After a successful add, the node enters the Added stage: its config is known to the daemon, but no runnable artifact exists yet — build_cmd has not been run. You’ll produce that artifact in the next section with peppy node build.

The output of this command provides some useful information:

Adding node from /private/tmp/hello_world...
Log file: ~/.peppy/logs/add/hello_world_0.1.0_20260121_224824_699.log
Added node hello_world:0.1.0 to the node stack

This gives you access to the log file for debugging in case the add command fails. When a node is added to the node stack, a copy is made in the peppy cache under ~/.peppy/built_nodes/ to “snapshot” the node based on its name and tag. No two copies of the same node with the same name + tag can exist at the same time in the node stack. Re-adding a node with the same name and tag overrides the one that was previously in the node stack, except if that node has dependencies—in which case the operation fails.

An added node has no runnable artifact until it’s built. Building runs the node’s build_cmd (for example uv sync for Python or cargo build --release for Rust) against the snapshot that peppy node add staged:

Terminal window
peppy node build hello_world:0.1.0

The format is <node_name>:<tag> for a node that is already part of the node stack. While the build is running the node sits in the Building stage; once it completes, it moves to Ready and can be run.

The output looks like:

Building node hello_world:0.1.0...
Log file: ~/.peppy/logs/build/hello_world_0.1.0_20260121_224901_312.log
Built node hello_world:0.1.0. Artifact: ~/.peppy/built_nodes/hello_world_0.1.0.tar.zst

The artifact is a .tar.zst archive. When you start an instance with peppy node run, the daemon extracts it into a per-instance directory and executes run_cmd there.

Start your node:

Terminal window
peppy node run hello_world:0.1.0

This will run the run_cmd command from the peppy.json5 process configuration. The format is <node_name>:<tag> for a node that is already part of the node stack.

The node run command outputs the path to a log file where you can inspect the node’s output. For example:

Terminal window
cat /home/ubuntu/.peppy/logs/run/elegant-solomon-5423.log
[2026-02-17T10:15:23.599] Executing run_cmd: uv run hello_world (working_dir: /home/ubuntu/.peppy/instances/elegant-solomon-5423)
[2026-02-17T10:15:23.837] [stdout] hello world count 1
[2026-02-17T10:15:26.848] [stdout] hello world count 2
[2026-02-17T10:15:29.852] [stdout] hello world count 3

Now that you’ve seen all three steps, peppy node add exposes flags that chain them for you:

  • --build (-b) runs peppy node build immediately after the add succeeds.
  • --run (-r) also spawns an instance once the build finishes, and implies --build.
  • --sync (-s) runs peppy node sync before the add, so the snapshot picks up any edits you made to peppy.json5. Only valid for local filesystem sources — remote git/HTTP sources are synced server-side on fetch.
Terminal window
peppy node add -sb . # sync, then add, then build
peppy node add -sr . # sync, then add, then build, then run an instance

View all added & running nodes:

Terminal window
peppy stack list

You should see something like:

$ peppy stack list
Node stack
┌────────────────────────────────────────┬───────┬─────────┬───────────┬───────────────────────────────────────────────────────────┐
│ NODE │ STAGE │ VARIANT │ INSTANCES │ PATH │
├────────────────────────────────────────┼───────┼─────────┼───────────┼───────────────────────────────────────────────────────────┤
│ core-node-funny-chatterjee-6386:v0.8.0 │ Root │ default │ 1 running │ /home/ubuntu/.peppy/bin │
│ hello_world:0.1.0 │ Ready │ default │ 1 running │ /home/ubuntu/.peppy/built_nodes/hello_world_0.1.0.tar.zst │
└────────────────────────────────────────┴───────┴─────────┴───────────┴───────────────────────────────────────────────────────────┘
Dependencies
(none)

The STAGE column reports each node’s stage — its artifact-level lifecycle:

  • Added — the node is registered and a snapshot of the source has been taken, but build_cmd has not been run yet, so there is no runnable artifact.
  • Building — a build is in progress. A second concurrent node build on the same entity is rejected until this one finishes.
  • Ready — the build artifact is on disk. The node can be run; it may currently have zero, one, or several instances.
  • Root — the synthetic core-node entity. Always present, and always reports exactly one running instance (the daemon itself).

The INSTANCES column aggregates per-instance state (starting, running) for the node, independently of its stage. To see individual instance IDs, use peppy node info.

When you need more than the one-line view from stack list, peppy node info dumps the node’s stage, its currently-tracked instances with their individual states, and the paths to its add log and per-instance run logs. It takes a <node_name>:<node_tag> reference to a node that has already been added to the stack — it doesn’t touch the filesystem or any git/http source, so run peppy node add first if the node isn’t in the stack yet:

Terminal window
peppy node info hello_world:0.1.0
[INFO] Getting node info for hello_world:0.1.0...
Node Information
==================================================
Name: hello_world
Tag: 0.1.0
Language: Python
Build cmd: uv sync
Run cmd: uv run hello_world
Node Stack Status
--------------------------------------------------
Stage: Ready
Variant: default
Instances: 2 tracked
- suspicious-swanson-5880 [running]
- flamboyant-penrose-9622 [running]
Logs
--------------------------------------------------
hello_world:0.1.0
Add log: /home/ubuntu/.peppy/logs/add/hello_world_0.1.0_20260416_101124_613.log
Run logs:
- suspicious-swanson-5880: /home/ubuntu/.peppy/logs/run/suspicious-swanson-5880.log
- flamboyant-penrose-9622: /home/ubuntu/.peppy/logs/run/flamboyant-penrose-9622.log
Exposed Interfaces
--------------------------------------------------
Emitted Topics:
- message_stream (qos: SensorData)
Integrity
--------------------------------------------------
Config SHA256: 67505086f8ac099d0861cb5dfa539b1c8c3c9354ad90ed7713a5147c7cb43342

This is usually the fastest way to answer “is my node built yet?” and “where is the log for this specific instance?”. The Add log path is what peppy node add writes to; per-instance run logs are what peppy node run writes to. Build logs live under ~/.peppy/logs/build/ and are surfaced by peppy node build directly when it runs.

The Variant: line shows the variant that was selected when the node was added. It prints default both when no variant override was passed (auto-resolved default variant) and when the node declares no variants at all (the root’s own execution section is the default).

Exposed Interfaces summarizes the topics, services, and actions the node publishes or consumes per its peppy.json5. Config SHA256 is the fingerprint of the config that was fed through peppygen — it’s what peppy node add checks against to refuse a stale snapshot and tell you to run peppy node sync.

When you’re done, you can stop your node:

Terminal window
peppy node stop <instance-id>

This stops one instance of the hello_world:0.1.0 node, but the node itself still remains in the node stack.

To remove the node from the node stack, run:

Terminal window
peppy node remove hello_world:0.1.0

If you run peppy stack list again, you’ll see that only the core node remains:

$ peppy stack list
Node stack
┌────────────────────────────────────────┬───────┬─────────┬───────────┬─────────────────────────┐
│ NODE │ STAGE │ VARIANT │ INSTANCES │ PATH │
├────────────────────────────────────────┼───────┼─────────┼───────────┼─────────────────────────┤
│ core-node-funny-chatterjee-6386:v0.8.0 │ Root │ default │ 1 running │ /home/ubuntu/.peppy/bin │
└────────────────────────────────────────┴───────┴─────────┴───────────┴─────────────────────────┘
Dependencies
(none)

Now that you’ve created your first node, you can: