Skip to content
Foyre Foyre

Validation pipelines

Configure Validation Pipelines

Create repeatable checks for AI workloads: reviewers run pipelines, admins define policy, API users automate runs, and contributors add new built-in validation logic.

Difficulty
Intermediate
Time
20-30 minutes
Category
Validation
Tags
YAML, API, policy

A validation pipeline is an ordered list of checks that runs against a request's validation environment. The output becomes review evidence: pass, warning, fail, findings, and downloadable artifacts.

Fast path

Use the default pipeline first. Add policy checks in YAML when you need organization rules. Reach for custom scripts or containers only when the built-in checks are not enough.

Mental model

Reviewer

Runs a pipeline from a request and reads the evidence before approving.

Admin

Creates and edits YAML pipeline definitions, enables defaults, and controls approval gates.

Integrator

Creates pipelines and starts validation runs through the JSON API.

Contributor

Adds new built-in step types or scanner integrations to Foyre itself.

Prerequisites

  • A Foyre instance with a connected host cluster
  • A request with a ready validation environment and workload deployed into it
  • reviewer, architect, or admin role to run pipelines
  • admin role to create, edit, enable, delete, or set default pipelines
  • A bearer token if you are using the API examples

1. Run a pipeline as a reviewer

Open a submitted request that has a ready validation environment. In the Validation pipeline section, choose a pipeline and click Run validation pipeline.

Expected result

Validation Pipeline: Default AI Workload Validation
Status: Failed
Checks: 1 passed, 1 warning, 1 failed
Approval Impact: Blocked

Expand each step to read the summary, findings, recommendations, timestamps, and evidence artifacts.

2. Create a pipeline as an admin

Go to AdministrationValidation pipelinesNew pipeline. Paste this YAML, validate it, and create the pipeline.

yaml
apiVersion: foyre.ai/v1alpha1
kind: ValidationPipeline
metadata:
  name: default-ai-workload-validation
  displayName: Default AI Workload Validation
  description: Inventory, Kubernetes security, image scanning, and policy checks.
spec:
  failurePolicy: warn
  steps:
    - name: workload-inventory
      type: builtin.workload_inventory
      displayName: Workload Inventory
      required: true
      failurePolicy: warn
      timeoutSeconds: 120
      config:
        includeNamespaces: ["*"]
        excludeNamespaces: [kube-system]

    - name: kubernetes-security
      type: builtin.kubernetes_security
      displayName: Kubernetes Security Review
      required: true
      failurePolicy: block
      dependsOn: [workload-inventory]
      timeoutSeconds: 120
      config:
        denyPrivilegedContainers: true
        warnIfRunAsRoot: true
        warnIfMissingResourceLimits: true
        warnIfHostPathMounts: true
        warnIfHostNetwork: true

    - name: image-scan
      type: builtin.image_scan
      displayName: Container Image Scan
      required: true
      failurePolicy: block
      dependsOn: [workload-inventory]
      timeoutSeconds: 900
      config:
        scanner: trivy
        failOnCritical: true
        warnOnHigh: true
        ignoreUnfixed: false

    - name: org-policy
      type: builtin.policy
      displayName: Organization Policy
      required: true
      failurePolicy: block
      dependsOn: [workload-inventory]
      timeoutSeconds: 120
      config:
        checks:
          no_privileged_containers: { severity: high }
          require_resource_limits: { severity: low }
          deny_latest_tag: { severity: medium }
          allowed_registries:
            severity: high
            registries: ["registry.example.com", "ghcr.io/acme"]
          required_labels:
            severity: low
            labels: ["app.kubernetes.io/owner"]
          banned_capabilities:
            severity: high
            capabilities: ["SYS_ADMIN", "NET_ADMIN", "ALL"]
          host_path_mounts: { severity: high }

After creating it, use row actions to enable it and optionally set it as the default pipeline. Each save increments the pipeline version. Existing runs keep the definition snapshot they used.

3. Add custom checks when policy YAML is not enough

Custom steps run inside the validation environment. Inputs from upstream steps are mounted at /foyre/input. Anything written to /foyre/output becomes evidence.

Inline script

yaml
- name: egress-check
  type: custom.script
  displayName: Egress Check
  failurePolicy: warn
  dependsOn: [workload-inventory]
  timeoutSeconds: 300
  config:
    interpreter: bash
    script: |
      set -euo pipefail
      jq '.workloads[]?.images[]?' /foyre/input/workload-inventory.json > /foyre/output/images.txt
      echo '{"status":"passed","severity":"none","summary":"Image list captured.","findings":[]}' > /foyre/output/result.json

Bring your own container

yaml
- name: company-policy
  type: custom.kubernetes_job
  displayName: Company Policy Check
  failurePolicy: block
  dependsOn: [workload-inventory]
  timeoutSeconds: 300
  config:
    image: registry.example.com/security/company-ai-checker:latest
    command: ["/app/check"]
    args: ["--input", "/foyre/input/workload-inventory.json"]
    env:
      POLICY_MODE: strict

Chart settings

Inline scripts and uploaded output artifacts require the validation runner image and an ingest URL reachable from the validation environment. Without that wiring, custom steps can still run in log-only mode.

4. Configure approval gates

Under AdministrationValidation pipelinesApproval policy, set how validation affects approval.

Toggle
Effect
requireValidationBeforeApproval
A request cannot be approved until at least one validation run has completed.
blockApprovalOnFailedValidation
A blocked validation result prevents normal approval.
allowValidationOverride
A reviewer can override a blocked result by entering a reason.

5. Create and run pipelines through the API

Set your base URL and bearer token:

shell
export BASE=http://localhost:8080
export TOKEN=<your-bearer-token>
export REQUEST_ID=<request-id>

Validate pipeline YAML before saving it:

shell
python - <<'PY' > pipeline.json
import json
from pathlib import Path

definition = Path("pipeline.yaml").read_text()
print(json.dumps({"definition_yaml": definition, "enabled": True, "is_default": False}))
PY

curl -X POST "$BASE/api/validation/pipelines/validate" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d @pipeline.json

Create the pipeline, set it as default if needed, then start a run:

shell
curl -X POST "$BASE/api/validation/pipelines" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d @pipeline.json

curl -X POST "$BASE/api/requests/$REQUEST_ID/validation-runs" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"reason":"Pre-production validation"}'

Poll the run and download evidence artifacts:

shell
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/requests/$REQUEST_ID/validation-runs"

export RUN_ID=<run-id>

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/validation-runs/$RUN_ID"

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/validation-runs/$RUN_ID/artifacts"

export ARTIFACT_ID=<artifact-id>

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/validation-artifacts/$ARTIFACT_ID/download" \
  -o evidence.json

6. Add a new built-in step as a contributor

Most teams should use builtin.policy, custom.script, or custom.kubernetes_job. Add a native executor only when the check should ship as part of Foyre.

python
# backend/app/domain/validation_steps.py
"builtin.network_egress": StepTypeSpec(
    type="builtin.network_egress",
    display_name="Network Egress Review",
    description="Inspect workloads for external network egress.",
    builtin=True,
)
python
# backend/app/validation/executors/network_egress.py
from app.domain.enums import ValidationSeverity, ValidationStepStatus
from app.validation.types import ArtifactDraft, StepContext, StepOutcome


def run(ctx: StepContext) -> StepOutcome:
    findings = []

    return StepOutcome(
        status=ValidationStepStatus.warning if findings else ValidationStepStatus.passed,
        severity=ValidationSeverity.medium if findings else ValidationSeverity.none,
        summary=f"{len(findings)} network egress finding(s).",
        findings=findings,
        details={"checked": True},
        artifacts=[
            ArtifactDraft(
                name="network-egress.json",
                artifact_type="json",
                content=b'{"findings":[]}',
                content_type="application/json",
            )
        ],
    )
python
# backend/app/validation/executors/__init__.py
from app.validation.executors.network_egress import run as network_egress_run

_REGISTRY = {
    "builtin.network_egress": network_egress_run,
}

Troubleshooting

The run button is missing

Confirm the user has the reviewer, architect, or admin role.

The run will not start

The request needs a ready validation environment. Create the isolated cluster, deploy the workload, and then run the pipeline.

The pipeline YAML is rejected

Check apiVersion, kind, unique step names, supported step types, and dependency names.

Approval is blocked

Fix the workload and rerun validation, or use an override with a recorded reason if policy allows it.

What you should have now

  • A working model for who runs, configures, automates, and extends validation pipelines
  • A copyable pipeline YAML definition with inventory, security, image scan, and policy checks
  • API commands for validating definitions, creating pipelines, starting runs, and downloading evidence
  • Starting points for custom scripts, custom containers, and native built-in step development