Skip to content

Dashboard Container

The Pacto dashboard is published as a container image for production and Kubernetes deployments. It runs the same pacto dashboard server in a deployable container. See the platform engineer guide for how the dashboard fits into operator, compliance and blast-radius workflows.


Image

ghcr.io/trianalab/pacto-dashboard:<version>

The image tag always matches the Pacto release version (e.g., 1.2.3). There is no latest tag. The container runs the exact pacto binary for that version.

Quick Start

# Run with OCI registry sources
docker run -p 3000:3000 \
  -e PACTO_DASHBOARD_REPO=ghcr.io/org/svc-a,ghcr.io/org/svc-b \
  ghcr.io/trianalab/pacto-dashboard:1.2.3

# Run with registry authentication
docker run -p 3000:3000 \
  -e PACTO_DASHBOARD_REPO=ghcr.io/org/svc-a \
  -e PACTO_REGISTRY_TOKEN=ghp_xxx \
  ghcr.io/trianalab/pacto-dashboard:1.2.3

Local Development

Build and run the dashboard container locally using Make:

# Build the image (tagged with current git version)
make docker-build

# Build and run (mounts ~/.kube/config and ~/.cache/pacto automatically)
make docker-run

Environment Variables

Variable Description Default
PACTO_DASHBOARD_HOST Bind address for the server 0.0.0.0 (in image), 127.0.0.1 (CLI)
PACTO_DASHBOARD_PORT HTTP server port 3000
PACTO_DASHBOARD_NAMESPACE Kubernetes namespace filter (empty = all) ""
PACTO_DASHBOARD_REPO Comma-separated OCI repositories to scan ""
PACTO_DASHBOARD_DIAGNOSTICS Enable source diagnostics panel (true) false
PACTO_DASHBOARD_CORS_ORIGIN Trusted cross-origin allowed to call the API "" (same-origin only)
PACTO_CACHE_DIR Directory the dashboard scans for cached OCI bundles (read side) /home/pacto/.cache/pacto/oci
PACTO_NO_CACHE Disable OCI bundle caching (1) 0
PACTO_NO_UPDATE_CHECK Disable update checks (1) 1 (set in image)
PACTO_REGISTRY_USERNAME Registry authentication username ""
PACTO_REGISTRY_PASSWORD Registry authentication password ""
PACTO_REGISTRY_TOKEN Registry authentication token ""

All PACTO_DASHBOARD_* variables map to the corresponding --host, --port, --namespace, --diagnostics, and --cors-origin CLI flags. OCI repositories can be passed as oci:// positional arguments on the CLI; in the container, use the comma-separated PACTO_DASHBOARD_REPO env var instead.

Note: PACTO_CACHE_DIR sets only the directory the dashboard scans for cached OCI bundles (read side); it is an environment variable only — there is no --cache-dir flag. When it is unset, the dashboard resolves this directory from the bundle store's CacheDir() (defaulting to ~/.cache/pacto/oci). The core CLI/OCI cache write location is controlled by XDG_CACHE_HOME (default ~/.cache/pacto/oci), not PACTO_CACHE_DIR — so to persist the cache, mount a volume at that path (as the Kubernetes example does) or set XDG_CACHE_HOME. The container default works because HOME=/home/pacto makes the read and write paths coincide. See the environment variables in the CLI reference for the full picture.

Data Sources

The dashboard auto-detects available data sources at startup. See the source model and resolution model in architecture.md for how sources merge and prioritize; the container-specific bindings are:

  • oci: Enabled when PACTO_DASHBOARD_REPO is set, or auto-discovered from K8s resolvedRef fields. Provides contract bundles, version history, interfaces and diffs. (On-disk cache at /home/pacto/.cache/pacto/oci/ is used internally — see architecture.)
  • cache: The on-disk OCI cache is internal to the OCI source; it surfaces as a distinct cache source only as an offline baseline when no registry is configured and the cache has entries.
  • k8s: Enabled when a valid kubeconfig is mounted or when running inside a Kubernetes cluster (in-cluster config). Provides runtime state from the Pacto operator.
  • local: Enabled when a pacto.yaml is found in the working directory (mount via volume).

Kubernetes + OCI hybrid mode

When deployed alongside the Pacto operator in Kubernetes, the dashboard automatically discovers OCI repositories from the resolvedRef fields in Pacto CRD statuses — no PACTO_DASHBOARD_REPO needed. This creates a hybrid view: runtime truth from the operator + contract truth from OCI, giving you version history, interface details, configuration schemas, and diffs for every service the operator manages.

Prerequisites. Hybrid mode only activates when all of the following hold:

  • A mounted kubeconfig or in-cluster config so the Kubernetes source is active.
  • The Pacto operator is running and has populated status.contract.resolvedRef on the Pacto resources to discover.
  • The discovered registries are reachable and (for private repositories) authenticated via PACTO_REGISTRY_* credentials.

If any prerequisite is missing — no resolvedRef, an unreachable registry, or missing credentials — the dashboard silently degrades to k8s-only: it still shows runtime state from the operator, but without the OCI-backed version history, interfaces, schemas, and diffs.

Kubernetes Source

To enable the Kubernetes data source, mount a kubeconfig:

docker run -p 3000:3000 \
  -v ~/.kube/config:/home/pacto/.kube/config:ro \
  -e PACTO_DASHBOARD_NAMESPACE=production \
  ghcr.io/trianalab/pacto-dashboard:1.2.3

When running inside a Kubernetes cluster, the in-cluster config is used automatically (no mount needed).

Local Source

To scan a local contract directory:

docker run -p 3000:3000 \
  -v /path/to/contracts:/data:ro \
  ghcr.io/trianalab/pacto-dashboard:1.2.3 \
  dashboard /data

Operational Endpoints

Endpoint Description
GET /health Returns {"status": "ok", "version": "..."}. Use for liveness and readiness probes.
GET /metrics Returns {"serviceCount": N, "sourceCount": N}.
GET /openapi OpenAPI 3.1 specification (includes server URL matching the bind address).
GET /docs Interactive API documentation.

The image includes a Docker HEALTHCHECK that polls /health every 10 seconds.

Security

The dashboard is a read-mostly observability UI, but a few endpoints mutate local state (POST /api/resolve, POST /api/versions, POST /api/refresh pull and cache OCI artifacts). The server applies these protections:

  • Same-origin only by default. No Access-Control-Allow-Origin header is emitted, and cross-origin mutating requests are rejected with 403. This prevents a malicious web page in the operator's browser from driving the dashboard (CSRF/SSRF). The bundled UI is served same-origin and is unaffected.
  • Explicit cross-origin opt-in. Pass --cors-origin https://your-app (or PACTO_DASHBOARD_CORS_ORIGIN) to allow one trusted cross-origin client.
  • HTTP timeouts (ReadHeaderTimeout, ReadTimeout, IdleTimeout) guard against slow-client (Slowloris) exhaustion, and shutdown is graceful.

The server binds to 127.0.0.1 by default. Setting --host 0.0.0.0 (as the container image does) exposes the dashboard — including the unauthenticated mutating endpoints — to the network, so run it only on a trusted network or behind an authenticating proxy.

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pacto-dashboard
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pacto-dashboard
  template:
    metadata:
      labels:
        app: pacto-dashboard
    spec:
      containers:
        - name: dashboard
          image: ghcr.io/trianalab/pacto-dashboard:1.2.3
          ports:
            - containerPort: 3000
          env:
            - name: PACTO_DASHBOARD_REPO
              value: "ghcr.io/org/svc-a,ghcr.io/org/svc-b"
            - name: PACTO_REGISTRY_TOKEN
              valueFrom:
                secretKeyRef:
                  name: pacto-registry
                  key: token
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 3
            periodSeconds: 5
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
          securityContext:
            runAsNonRoot: true
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
          volumeMounts:
            - name: cache
              mountPath: /home/pacto/.cache
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: cache
          emptyDir: {}
        - name: tmp
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: pacto-dashboard
spec:
  selector:
    app: pacto-dashboard
  ports:
    - port: 80
      targetPort: 3000

Build and Release

The dashboard image is built and published automatically when a new Pacto version is released. The docker-build and docker-merge jobs in the auto-release pipeline (.github/workflows/auto-release.yml) build per-architecture images (linux/amd64, linux/arm64) and merge them into a single multi-arch manifest pushed to ghcr.io/trianalab/pacto-dashboard with the matching version tag (without v prefix).