Platform

Platform overviewArchitectureWorkflow orchestrationGitOps configurationGovernance and AAAAI and MCPKnowledge and contextRuntime and executionEvidence and monitoring

Use cases

All use casesProduction incidentRelease preparationHotfix to productionSecurity scan triage
Why NopsAIIntegrationsSecurity

Resources

All resourcesAI agent governanceMCP governanceMCP securitySelf-hosted platforms
PricingGitHub

Company

How a run worksAboutContactBook a demo

Create and run your first pipeline

Write a three-step pipeline where each step depends on the previous one and passes a value forward through runtime outputs.

TutorialNew userAutomation authorDeveloper

What you will do

  • A pipeline needs only name and steps; container_image sets 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_on is what serialises them — see Dependencies and parallelism.
  • A step publishes a value by writing a file under /nopsai/outputs whose name is exactly the output name, then declaring it under outputs. Step outputs has the full rules.
  • A consumer reads it in variables as $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 with required output file /nopsai/outputs/<NAME> was not produced.
  • POST /v1/pipelines/validate and PUT /v1/pipelines/{name} both take the YAML document as the request body; validation also accepts JSON with the definition under yaml or content.
  • A dependency path to the producer is required to consume its output, but it does not have to be a direct depends_on edge.

Before you start

Setup complete
First-install setup has finished and you can sign incurl -s localhost:8080/v1/setup/status
Runner
At least one registered, dispatch-enabled runnercurl -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

  1. 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"
    Result

    report prints both values, which proves the whole chain resolved.

  2. 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 | jq
    Replace before running
    • A JSON request works too, with the definition under yaml or content.
    Verify
    • Validation returns no errors. Read the message before changing the graph — it names the failure mode.
  3. 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.yaml
    Verify
    • The pipeline appears in Pipelines in the UI, and GET /v1/pipelines lists it.
  4. 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"}' | jq
    Replace before running
    • Replace platform/production with a scope that exists in your install.
    Expected result
    • {"run_id": "...", "trigger_event_id": ""}. Keep the run ID: the next pages use it for logs and history. Without Accept: application/json the same route answers with a plain-text confirmation instead.
  5. 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 -20
    Verify
    • The report step prints built app-1.0.<timestamp>-linux-amd64 from tag 1.0.<timestamp>.

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

Current behavior
  • A runtime output reference is never a valid depends_on value; declare the dependency by name.

Implementation evidence

  • pkg/models/runtime_outputs.go

    Reference parsing for the short and long output forms.

  • services/agent/internal/app/runtime_outputs.go

    Output file collection and the missing-file failure.

  • services/nopsai/routes.go

    Validate, save, and run routes used in this walkthrough.