Stepyard
YAML pipelines. Python plugins. Runs on your machine - no server to set up, no cloud account needed.
pip install stepyard
stepyard init my-automations && cd my-automations
stepyard run hello
Quick example
Build a container, smoke-test it, summarise the result with an LLM, and post to Slack. One YAML file, no glue scripts.
name: deploy
description: Build the image, smoke-test it, and post an AI summary to Slack.
steps:
- id: build
uses: shell.run
with:
command: docker build -t myapp:${{ env.GIT_SHA }} .
- id: smoke_test
uses: http.request
with:
method: GET
url: https://staging.myapp.com/healthz
- id: summary
uses: llm.generate # built-in - reads OPENAI_API_KEY from the env
with:
model: gpt-4o-mini
prompt: |
Write a one-line Slack message about this deploy.
Build exit code: ${{ steps.build.output.code }}
Health check HTTP status: ${{ steps.smoke_test.output.status }}
- id: notify
uses: http.request
with:
method: POST
url: ${{ env.SLACK_WEBHOOK }}
json_body:
text: ${{ steps.summary.output.output }}
Run it:
GIT_SHA=$(git rev-parse --short HEAD) stepyard run deploy
✓ build 12.4s
✓ smoke_test 0.3s
✓ summary 0.9s
✓ notify 0.2s
Flow completed in 13.8s
Reading step outputs. Each node has a documented output shape, referenced as
${{ steps.<id>.output... }}:
Node outputshapeshell.run{ stdout, stderr, code }http.request{ status, headers, body }llm.generate{ output, usage, model, provider }(use${{ steps.<id>.output.output }}for text)Full reference:
docs/nodes/builtin.md.
Why Stepyard?
- Flows are YAML files in your repo. Steps, conditions, loops, and retries are plain keys. Version-control them alongside your code; no proprietary DSL to learn.
- Extend with plain Python. One
@nodedecorator turns any function into a reusable step. Inputs are type-validated automatically; plugin dependencies run in an isolated virtualenv, so they never clash with Stepyard's own. - Nothing leaves your machine. State is stored in a local SQLite database. Data only goes out if a step in your flow explicitly sends it.
- Every run is its own process. Each flow executes in a dedicated OS
subprocess, so a crash, timeout, or
sys.exitin one run cannot take down the scheduler or a sibling run. See Execution model. - Built-in scheduler, no hosted service. Add a
trigger:block withcron,interval, orstartup, runstepyard service start, and flows execute on schedule without a control plane.
Install
# from PyPI
pip install stepyard
# or, for development, with uv
git clone https://github.com/rorlikowski/stepyard && cd stepyard
uv pip install -e ".[dev,docs]"
uv run stepyard doctor # verify the install
Requires Python 3.10+. Works on macOS, Linux, and Windows (WSL).
The 60-second tour
stepyard init my-automations # scaffold flows/ + .gitignore + .stepyard/
cd my-automations
stepyard run hello # run a flow now (in its own subprocess)
stepyard status # see every flow and its latest run
stepyard show <run-id> # drill into the steps of one run
stepyard logs <run-id> # stream the captured logs
stepyard validate --all # type-check your YAML without running it
Schedule it instead of running by hand - add a trigger and start the daemon:
name: nightly_backup
trigger:
uses: cron
with:
schedule: "0 3 * * *" # every day at 03:00
steps:
- id: dump
uses: shell.run
with:
command: pg_dump ${{ env.DATABASE_URL }} | gzip > /tmp/backup.sql.gz
stepyard service start # run the scheduler in the background
stepyard service status
Execution model
Stepyard is deliberately process-isolated:
stepyard run <flow>spawns a fresh subprocess (python -m stepyard.engine.runner) for that single run. Its stdout/stderr are captured to.stepyard/logs/.- The scheduler daemon (
stepyard service start) runs separately. It evaluates triggers, enqueues runs in SQLite, and a worker spawns one subprocess per run (bounded bySTEPYARD_MAX_CONCURRENT_FLOWS, default4). - Inside a run, steps execute sequentially. Built-in nodes run in-process; plugin nodes installed into an isolated virtualenv run in a second short-lived subprocess that talks JSON over stdin/stdout - so a plugin's dependencies can never clash with Stepyard's own.
The practical upshot: one misbehaving flow or plugin cannot corrupt the scheduler or sibling runs.
Write your own step
# my_plugin/nodes.py
from stepyard.sdk import node, NodeResult, NodeStatus
@node(name="math.add")
def add(a: int, b: int) -> int:
return a + b
@node(name="files.archive")
def archive(path: str) -> NodeResult:
if not path:
return NodeResult(status=NodeStatus.FAILED, error="path is required")
# ... do the work ...
return NodeResult(status=NodeStatus.SUCCESS, output={"archived": path})
Register it via an entry point and it becomes available as uses: math.add in
every flow. See docs/plugins/creating.md.
Documentation
| Section | What's inside |
|---|---|
| Getting Started | Install, quickstart, two tutorials |
| Core Concepts | Flows, expressions, control flow, triggers, errors |
| How-to Guides | Scheduling, secrets, approvals, debugging |
| Built-in Nodes | Every node that ships with Stepyard |
| Plugin Development | Write, test, and publish plugins |
| CLI Reference | Every command and flag |
The full documentation is published at rorlikowski.github.io/stepyard.
Browse the docs locally with uv run mkdocs serve.
License
MIT - see LICENSE.
Metadata
Release files for stepyard 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stepyard-0.1.2.tar.gz | 1.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stepyard-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / stepyard-0.1.2.tar.gz
| Download URL | stepyard-0.1.2.tar.gz |
|---|---|
| Size | 1.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fae515974453efb4a460366047debef97fd8756e01538857d3d52c5af8888993
|
|
BLAKE2b-256 checksum How to use checksums |
fb8d007b6b6bcdaeffea3ab80553b22e7dac7dc11f972d60d6c63880539867c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 14, 2026.
Transparency logRelease files / stepyard-0.1.2-py3-none-any.whl
| Download URL | stepyard-0.1.2-py3-none-any.whl |
|---|---|
| Size | 127.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
78788e741d89187ecc19bc2f0c7c1f81a336c2c5d3440d36ccc09fd8daf38d6c
|
|
BLAKE2b-256 checksum How to use checksums |
a9098c9eed84f753e1299fe3eeba21b1e2a4bb1a1bcc8e7c317a78d98b9d7611
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 14, 2026.
Transparency log