GUAC — Graph for Understanding Artifact Composition — is an open-source tool from Google and the Sigstore project that ingests software security metadata (SBOMs, vulnerability data, SLSA provenance, Sigstore attestations) into a queryable graph database. Security teams use GUAC to answer questions like "which of our deployed containers is affected by CVE-2024-XXXX?" and "what is the full transitive dependency tree of this artifact?". When GUAC goes down — GraphQL API crash, ingestion pipeline stall, graph database connectivity loss — your supply chain visibility disappears. Security queries return stale data, vulnerability scans miss newly ingested packages, and incident response loses its real-time artifact graph. Vigilmon gives you external monitoring that watches GUAC from outside the system and alerts the moment your supply chain intelligence platform is degraded.
What You'll Build
- A Vigilmon HTTP monitor for GUAC's GraphQL API endpoint
- A heartbeat monitor to verify active metadata ingestion
- A graph query probe that validates the graph database contains fresh data
- Alert channels routed to your security engineering team
Prerequisites
- A running GUAC deployment (Docker Compose or Kubernetes)
- GUAC's GraphQL API exposed over HTTPS (directly or via reverse proxy)
- At least one collector configured and running (SLSA, SBOM, or OSV)
- A free account at vigilmon.online
Why Monitoring GUAC Matters
GUAC is the aggregation layer for your software supply chain intelligence. Unlike most services, its failure modes are particularly dangerous because they degrade silently:
GraphQL API unavailability blocks all supply chain queries. Security dashboards, CI/CD pipeline checks, and incident response tooling that queries GUAC for artifact relationships all fail immediately. Depending on how those clients handle errors, they may silently return empty results instead of surfacing the outage.
Ingestion pipeline stalls are the hardest GUAC failure to detect. The GraphQL API continues to serve queries, but the underlying data grows stale. New SBOMs uploaded, new CVEs published, and new attestations from CI pipelines are never ingested. Queries appear to succeed but return data from hours or days ago. Security decisions made on stale GUAC data are the worst outcome.
Graph database connectivity loss typically manifests as successful API responses with empty result sets. If GUAC's backend (Neo4j, Ent, or in-memory) becomes unreachable, the GraphQL layer may return 200 with {"data": {"packages": []}} rather than an error — making it invisible to basic health checks.
Collector service crashes silently stop ingestion for specific data types. If the OSV collector crashes, GUAC continues ingesting SBOMs but no longer receives vulnerability data. Artifact-to-CVE queries return incomplete results.
External monitoring with Vigilmon catches these from the perspective of systems that depend on GUAC data.
Step 1: Monitor the GUAC GraphQL Endpoint
GUAC's primary interface is a GraphQL API, typically running on port 8080. Expose it over HTTPS via a reverse proxy:
# nginx snippet for GUAC GraphQL
location /query {
proxy_pass http://guac-graphql:8080/query;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Verify the endpoint responds to a health introspection query:
curl -X POST https://guac.internal.yourdomain.com/query \
-H "Content-Type: application/json" \
-d '{"query": "{ __typename }"}'
# Expected: {"data":{"__typename":"Query"}}
In the Vigilmon dashboard:
- Go to Add Monitor → HTTP
- Configure:
| Field | Value |
|---|---|
| Name | guac GraphQL API |
| URL | https://guac.internal.yourdomain.com/query |
| Method | POST |
| Request body | {"query": "{ __typename }"} |
| Request headers | Content-Type: application/json |
| Expected status | 200 |
| Response body contains | __typename |
| Check interval | 1 minute |
| Timeout | 15 seconds |
| Alert after | 2 consecutive failures |
The GraphQL introspection probe is lightweight and confirms both API availability and basic schema integrity.
Step 2: Monitor GUAC's Health Endpoint
GUAC also exposes a dedicated health endpoint. Add a second monitor:
curl https://guac.internal.yourdomain.com/healthz
# Expected: 200 OK
In Vigilmon:
| Field | Value |
|---|---|
| Name | guac /healthz |
| URL | https://guac.internal.yourdomain.com/healthz |
| Method | GET |
| Expected status | 200 |
| Check interval | 1 minute |
| Timeout | 10 seconds |
| Alert after | 2 consecutive failures |
Step 3: Heartbeat Monitor for Ingestion Pipeline Activity
The API health check doesn't validate that data is being ingested. Add a CronJob that checks when the most recent artifact was ingested into the graph:
# guac-ingestion-probe.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: guac-ingestion-probe
namespace: security-monitoring
spec:
schedule: "*/15 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: ingestion-probe
image: curlimages/curl:latest
env:
- name: GUAC_URL
value: "https://guac.internal.yourdomain.com/query"
- name: HEARTBEAT_URL
valueFrom:
secretKeyRef:
name: vigilmon-secrets
key: guac-ingestion-heartbeat-url
- name: MAX_STALE_SECONDS
value: "3600"
command:
- /bin/sh
- -c
- |
set -e
# Query for the most recently ingested package
RESULT=$(curl -sf -X POST "$GUAC_URL" \
-H "Content-Type: application/json" \
-d '{
"query": "{ packages(pkgSpec: {}) { id type namespaces { namespace names { name versions { version } } } } }"
}')
# Check the response contains actual package data
if [ -z "$RESULT" ]; then
echo "FAIL: GUAC returned empty response"
exit 1
fi
PKG_COUNT=$(echo "$RESULT" | grep -o '"id"' | wc -l || true)
if [ "$PKG_COUNT" -lt 1 ]; then
echo "FAIL: GUAC graph contains no packages"
exit 1
fi
echo "Graph contains packages ($PKG_COUNT IDs visible)"
wget -qO- "$HEARTBEAT_URL"
echo "GUAC ingestion heartbeat sent"
Create the heartbeat monitor in Vigilmon:
- Add Monitor → Heartbeat
- Name:
guac ingestion pipeline - Expected interval: 15 minutes
- Grace period: 10 minutes
Step 4: Validate Graph Query Results Are Fresh
The most dangerous GUAC failure is stale data — the API returns 200 but the graph hasn't been updated in hours. Add a probe that queries for recently ingested vulnerability data:
# guac-freshness-probe.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: guac-freshness-probe
namespace: security-monitoring
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: freshness-probe
image: curlimages/curl:latest
env:
- name: GUAC_URL
value: "https://guac.internal.yourdomain.com/query"
- name: HEARTBEAT_URL
valueFrom:
secretKeyRef:
name: vigilmon-secrets
key: guac-freshness-heartbeat-url
command:
- /bin/sh
- -c
- |
set -e
# Query for CertifyVuln entries (OSV collector ingests these)
RESULT=$(curl -sf -X POST "$GUAC_URL" \
-H "Content-Type: application/json" \
-d '{
"query": "{ CertifyVuln(certifyVulnSpec: {}) { id vulnerability { id type vulnerabilityIDs { id vulnerabilityID } } metadata { timeScanned } } }"
}')
VULN_COUNT=$(echo "$RESULT" | grep -o '"id"' | wc -l || true)
if [ "$VULN_COUNT" -lt 1 ]; then
echo "WARN: No CertifyVuln entries found — OSV collector may be stalled"
exit 1
fi
echo "Found $VULN_COUNT vulnerability attestation IDs"
wget -qO- "$HEARTBEAT_URL"
echo "GUAC freshness heartbeat sent"
Create the heartbeat monitor in Vigilmon:
- Add Monitor → Heartbeat
- Name:
guac vulnerability data freshness - Expected interval: 1 hour
- Grace period: 30 minutes
Step 5: Monitor Collector Services
Each GUAC collector runs as a separate service. Monitor the OSV collector and SLSA collector independently so you can pinpoint which data type stopped ingesting:
# guac-collector-probe.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: guac-collector-probe
namespace: security-monitoring
spec:
schedule: "*/10 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: collector-probe
image: bitnami/kubectl:latest
env:
- name: HEARTBEAT_URL
valueFrom:
secretKeyRef:
name: vigilmon-secrets
key: guac-collector-heartbeat-url
command:
- /bin/sh
- -c
- |
set -e
# Check OSV collector pod is running
OSV_RUNNING=$(kubectl get pods -n security-monitoring \
-l app=guac-osv-certifier \
-o jsonpath='{.items[*].status.phase}' 2>/dev/null | grep -c "Running" || true)
# Check SLSA/deps.dev collector pod is running
DEPS_RUNNING=$(kubectl get pods -n security-monitoring \
-l app=guac-deps-certifier \
-o jsonpath='{.items[*].status.phase}' 2>/dev/null | grep -c "Running" || true)
if [ "$OSV_RUNNING" -lt 1 ]; then
echo "FAIL: GUAC OSV certifier is not running"
exit 1
fi
if [ "$DEPS_RUNNING" -lt 1 ]; then
echo "WARN: GUAC deps.dev certifier is not running"
# Not fatal but worth noting
fi
wget -qO- "$HEARTBEAT_URL"
echo "GUAC collector heartbeat sent (OSV: $OSV_RUNNING, deps: $DEPS_RUNNING)"
Create the Kubernetes secrets:
kubectl create secret generic vigilmon-secrets \
--from-literal=guac-ingestion-heartbeat-url='https://vigilmon.online/heartbeat/YOUR_INGESTION_TOKEN' \
--from-literal=guac-freshness-heartbeat-url='https://vigilmon.online/heartbeat/YOUR_FRESHNESS_TOKEN' \
--from-literal=guac-collector-heartbeat-url='https://vigilmon.online/heartbeat/YOUR_COLLECTOR_TOKEN' \
-n security-monitoring
Step 6: Configure Alert Channels
Route GUAC alerts to your security engineering team:
- In Vigilmon: Alert Channels → Add Channel → Slack Webhook
- Add your
#security-engineeringwebhook - Assign to all GUAC monitors
For the freshness monitor (stale vulnerability data — worst outcome):
- Alert Channels → Add Channel → PagerDuty or email
- Add your security on-call rotation
- Assign only to the
guac vulnerability data freshnessheartbeat
Stale vulnerability data is a security incident, not just an ops incident — escalate to security on-call directly.
What You're Now Monitoring
| Component | Monitor Type | What It Detects |
|---|---|---|
| GraphQL API | HTTP POST /query | API process down, schema broken |
| GUAC /healthz | HTTP GET | Service crash or restart |
| Ingestion pipeline | Heartbeat (15 min) | Pipeline stalled, graph not growing |
| Vulnerability data freshness | Heartbeat (1 hr) | OSV collector stalled, stale vuln data |
| Collector services | Heartbeat (10 min) | Individual collector pod crashes |
Conclusion
GUAC is the central nervous system for software supply chain intelligence, but it has a dangerous failure mode: the GraphQL API can return 200 while serving completely stale data. By the time a security team notices (when querying for a new CVE and finding nothing), the ingestion pipeline may have been stalled for hours. External monitoring with Vigilmon catches both the availability failures and the harder-to-detect data freshness failures that in-cluster health checks miss.
Get started free at vigilmon.online. The GraphQL introspection probe and freshness heartbeat together give you the two most critical GUAC signals in under ten minutes: is the API reachable, and is it serving current data?