tutorial

sbom_health.py

## Prerequisites - Syft installed on your pipeline host (`anchore/syft` binary or Docker image) - A web service framework available on the host (examples use...

Prerequisites

  • Syft installed on your pipeline host (anchore/syft binary or Docker image)
  • A web service framework available on the host (examples use Python/FastAPI and shell scripts)
  • A free account at vigilmon.online

Part 1: Understanding what to monitor in a Syft pipeline

Syft itself is a CLI tool — it does not expose an HTTP endpoint. Monitoring therefore happens at the wrapping layer: the service, script, or CI job that invokes Syft and handles its output.

There are three failure modes worth monitoring:

| Failure | Symptom | Detection method | |---------|---------|-----------------| | Syft binary missing or broken | Exit code non-zero, no SBOM written | Health endpoint that invokes syft version | | Registry unreachable | Syft hangs or errors on image pull | Separate HTTP monitor on the registry URL | | Output store full or unreachable | SBOM not persisted | Health endpoint that checks last write timestamp |

We will build a small HTTP health service that covers all three.


Part 2: Build a Syft health endpoint

Create a minimal FastAPI service that exposes a /health endpoint. Run this alongside your Syft pipeline on each scanner host.

Install dependencies

pip install fastapi uvicorn

Create sbom_health.py

import subprocess
import time
import os
from fastapi import FastAPI, Response

app = FastAPI()

SBOM_OUTPUT_DIR = os.environ.get("SBOM_OUTPUT_DIR", "/var/sbom")
MAX_STALENESS_SEC = int(os.environ.get("SBOM_MAX_STALENESS_SEC", "3600"))


@app.get("/health")
def health(response: Response):
    checks = {}

    try:
        result = subprocess.run(
            ["syft", "version"],
            capture_output=True,
            text=True,
            timeout=10,
        )
        checks["syft_binary"] = "ok" if result.returncode == 0 else "error"
    except Exception as e:
        checks["syft_binary"] = f"error: {e}"

    if os.path.isdir(SBOM_OUTPUT_DIR) and os.access(SBOM_OUTPUT_DIR, os.W_OK):
        checks["output_dir"] = "ok"
    else:
        checks["output_dir"] = "error: missing or not writable"

    try:
        files = [
            os.path.join(SBOM_OUTPUT_DIR, f)
            for f in os.listdir(SBOM_OUTPUT_DIR)
            if f.endswith(".json") or f.endswith(".spdx")
        ]
        if files:
            latest_mtime = max(os.path.getmtime(f) for f in files)
            age_sec = time.time() - latest_mtime
            checks["last_sbom_age_sec"] = int(age_sec)
            checks["pipeline_staleness"] = "warning" if age_sec > MAX_STALENESS_SEC else "ok"
        else:
            checks["pipeline_staleness"] = "no_sboms_found"
    except Exception as e:
        checks["pipeline_staleness"] = f"error: {e}"

    all_ok = all(v == "ok" for v in checks.values() if isinstance(v, str))
    response.status_code = 200 if all_ok else 503
    return {"status": "ok" if all_ok else "degraded", "checks": checks}

Run the health service

uvicorn sbom_health:app --host 0.0.0.0 --port 8090

Verify the endpoint

curl http://localhost:8090/health

Expected response when healthy:

{
  "status": "ok",
  "checks": {
    "syft_binary": "ok",
    "output_dir": "ok",
    "last_sbom_age_sec": 312,
    "pipeline_staleness": "ok"
  }
}

Part 3: Set up HTTP monitoring in Vigilmon

Monitor the Syft health endpoint

  1. Log in to vigilmon.online and click Add Monitor.
  2. Choose HTTP(S) monitor.
  3. Enter: http://your-scanner-host:8090/health
  4. Set interval to 5 minutes.
  5. Add a keyword check: must contain "status":"ok".
  6. Add your alert channel.
  7. Click Save.

The keyword check catches the case where the endpoint returns HTTP 200 but with "status":"degraded" — which means Syft is reachable but the pipeline has a problem.

Monitor the container registry Syft pulls from

Syft needs to pull image manifests from your registry. If the registry is down, all SBOM generation silently fails. Add a separate monitor:

  1. Add a new HTTP(S) monitor.
  2. Enter your registry health endpoint:
    • Docker Hub: https://index.docker.io/v2/
    • GitHub Container Registry: https://ghcr.io/v2/
    • Private registry: https://registry.example.com/v2/
  3. Set interval to 1 minute.
  4. Add the same alert channel.

Part 4: Monitor Syft in CI/CD

If Syft runs inside GitHub Actions, add a reporting step so your status endpoint reflects the last run outcome:

# .github/workflows/sbom.yml
name: Generate SBOM

on:
  push:
    branches: [main]

jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Syft
        run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

      - name: Generate SBOM
        id: syft
        run: |
          syft "${{ env.IMAGE_REF }}" -o spdx-json > sbom.spdx.json
          echo "exit_code=$?" >> $GITHUB_OUTPUT

      - name: Report pipeline status
        if: always()
        run: |
          STATUS="ok"
          if [ "${{ steps.syft.outputs.exit_code }}" != "0" ]; then STATUS="failed"; fi
          curl -s -X POST "${{ secrets.STATUS_API_URL }}/api/sbom-status" \
            -H "Content-Type: application/json" \
            -H "Authorization: Bearer ${{ secrets.STATUS_API_KEY }}" \
            -d "{\"status\": \"$STATUS\", \"image\": \"${{ env.IMAGE_REF }}\"}"

Part 5: Webhook alerts for pipeline failures

Configure Vigilmon to call a webhook when the Syft health endpoint goes down:

from fastapi import FastAPI, Request
import logging

app = FastAPI()
logger = logging.getLogger(__name__)

@app.post("/webhook/vigilmon")
async def vigilmon_alert(request: Request):
    payload = await request.json()
    status = payload.get("status")
    monitor_name = payload.get("monitor_name")

    if status == "down":
        logger.error("SBOM pipeline DOWN", extra={"monitor": monitor_name})
        await notify_security_team(monitor_name)
    elif status == "up":
        logger.info("SBOM pipeline recovered", extra={"monitor": monitor_name})

    return {"received": True}

Vigilmon sends this payload on status transitions:

{
  "monitor_id": "mon_syft01",
  "monitor_name": "Syft SBOM Pipeline Health",
  "status": "down",
  "url": "http://scanner.internal:8090/health",
  "checked_at": "2026-07-02T09:00:00Z",
  "response_code": 503,
  "response_time_ms": 1204
}

Part 6: SSL monitoring for SBOM stores

If you store SBOMs in an HTTPS endpoint or OCI registry:

  1. In Vigilmon, click Add Monitor.
  2. Choose SSL monitor.
  3. Enter each HTTPS host your Syft pipeline writes to:
    • sbom-store.example.com
    • registry.example.com
  4. Set alert threshold to 14 days before expiry.
  5. Add your alert channel.

Part 7: Systemd service for the health endpoint

Keep the health service running across reboots:

# /etc/systemd/system/sbom-health.service
[Unit]
Description=Syft SBOM pipeline health endpoint
After=network.target

[Service]
User=sbomscanner
WorkingDirectory=/opt/sbom-health
ExecStart=/usr/local/bin/uvicorn sbom_health:app --host 0.0.0.0 --port 8090
Restart=on-failure
RestartSec=5
Environment=SBOM_OUTPUT_DIR=/var/sbom
Environment=SBOM_MAX_STALENESS_SEC=3600

[Install]
WantedBy=multi-user.target
sudo systemctl enable sbom-health
sudo systemctl start sbom-health

Summary

Your Syft SBOM pipeline now has layered monitoring:

  1. Binary health endpoint — confirms Syft is installed and the output directory is writable, polled every 5 minutes by Vigilmon.
  2. Registry monitor — catches upstream registry failures before they silently block SBOM generation.
  3. CI pipeline status — reports success/failure after each Syft run so Vigilmon alerts on consecutive failures.
  4. SSL monitors — alerts before certificate expiry breaks SBOM writes or registry pulls.

Vigilmon handles check scheduling, multi-region polling, and alert routing. You get notified within minutes of any failure in the Syft pipeline — not after your next compliance audit.


Monitor your SBOM generation pipeline free at vigilmon.online

#syft #sbom #containersecurity #devops #monitoring

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →