What you will do
- A pipeline needs only
nameandsteps;container_imagesets the image every step runs in unless a step overrides it. Pipeline anatomy covers every top-level directive. - Steps run in dependency order, not list order.
depends_onis what serialises them — see Dependencies and parallelism. - A step publishes a value by writing a file under
/nopsai/outputswhose name is exactly the output name, then declaring it underoutputs. Step outputs has the full rules. - A consumer reads it in
variablesas$steps.<step>.outputs.<NAME>, which must be the entire value of that variable. - Output names must match
^[A-Za-z_][A-Za-z0-9_]*$, and a missing file fails the step withrequired output file /nopsai/outputs/<NAME> was not produced. POST /v1/pipelines/validateandPUT /v1/pipelines/{name}both take the YAML document as the request body; validation also accepts JSON with the definition underyamlorcontent.- A dependency path to the producer is required to consume its output, but it does not have to be a direct
depends_onedge.
Before you start
- Setup complete
- First-install setup has finished and you can sign in
curl -s localhost:8080/v1/setup/status - Runner
- At least one registered, dispatch-enabled runner
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" localhost:8080/v1/system/dispatcher | jq - Scope
- A runtime scope to run in, such as the scope created during setup
Steps
- 01
Write the pipeline
Three steps: one produces a tag, one consumes it and produces an artifact name, one reports both. Each step declares what it publishes and what it consumes.
first-pipeline.yamlyaml name: first-pipeline container_image: alpine:3.20 steps: - name: prepare script: | echo "1.0.$(date +%s)" > /nopsai/outputs/BUILD_TAG echo "prepared $(cat /nopsai/outputs/BUILD_TAG)" outputs: - name: BUILD_TAG - name: build depends_on: [prepare] variables: BUILD_TAG: $steps.prepare.outputs.BUILD_TAG script: | echo "building $BUILD_TAG" echo "app-$BUILD_TAG-linux-amd64" > /nopsai/outputs/ARTIFACT outputs: - name: ARTIFACT - name: report depends_on: [build] variables: BUILD_TAG: $steps.prepare.outputs.BUILD_TAG ARTIFACT: $steps.build.outputs.ARTIFACT script: | echo "built $ARTIFACT from tag $BUILD_TAG" - 02
Validate before running it
Validation catches an undefined dependency, an output that is consumed without a dependency path, and a reference embedded in a larger string, each with a distinct message.
Validate the definitionbash curl -sX POST http://localhost:8080/v1/pipelines/validate \ -H "Authorization: Bearer $NOPSAI_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @first-pipeline.yaml | jqVerify- Validation returns no errors. Read the message before changing the graph — it names the failure mode.
- 03
Save the pipeline
Store the definition under the name you want to run it by. The UI editor writes the same definition through the same route.
Create or update the pipelinebash curl -sX PUT http://localhost:8080/v1/pipelines/first-pipeline \ -H "Authorization: Bearer $NOPSAI_TOKEN" \ -H "Content-Type: application/yaml" \ --data-binary @first-pipeline.yamlVerify- The pipeline appears in Pipelines in the UI, and
GET /v1/pipelineslists it.
- The pipeline appears in Pipelines in the UI, and
- 04
Run it
Start a run in the scope you want it resolved in. The scope decides which variables and secrets the run can see.
Start a runbash curl -sX POST http://localhost:8080/v1/run/first-pipeline \ -H "Authorization: Bearer $NOPSAI_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"scope":"platform/production"}' | jqExpected result{"run_id": "...", "trigger_event_id": ""}. Keep the run ID: the next pages use it for logs and history. WithoutAccept: application/jsonthe same route answers with a plain-text confirmation instead.
- 05
Confirm the value travelled
Read the last step output rather than trusting a green status: the point of this pipeline is that a value crossed two step boundaries.
Read the run logsbash curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "http://localhost:8080/v1/runs/$RUN_ID/logs" | jq -r '.[].line' | tail -20Verify- The
reportstep printsbuilt app-1.0.<timestamp>-linux-amd64 from tag 1.0.<timestamp>.
- The
How it works
Steps that do not depend on each other run concurrently up to runner capacity. This pipeline is deliberately a straight line so the output chain is visible; removing depends_on from build would make it start immediately and fail to resolve BUILD_TAG.
The long form of an output reference is $steps.<step>.<task>.outputs.<NAME>. A single-task step publishes under its own name, so the short $steps.<step>.outputs.<NAME> used here resolves to the same value.
Mark an output sensitive: true when it carries a credential; the value stays available downstream but is masked wherever logs are rendered. Ordinary outputs stay readable on purpose, so release evidence such as versions and image references remains reviewable.
RUNTIME_OUTPUT_MAX_BYTES caps a single output value and defaults to 65536 bytes.
Limits
- A runtime output reference is never a valid
depends_onvalue; declare the dependency by name.
Implementation evidence
pkg/models/runtime_outputs.goReference parsing for the short and long output forms.
services/agent/internal/app/runtime_outputs.goOutput file collection and the missing-file failure.
services/nopsai/routes.goValidate, save, and run routes used in this walkthrough.

