Dashboard Container¶
Run the Pacto dashboard, a web UI for your services, their contracts,
dependencies and compliance, as a container. It is the same server as
pacto dashboard in the CLI, packaged for Docker and Kubernetes.
Image¶
The tag matches the Pacto release version, without a v prefix, and there is no
latest — swap the pinned version below for the release you want. The image is
signed keylessly; verify it with the command in
Supply chain.
Quick Start¶
Every docker run here publishes to 127.0.0.1 on purpose: the image sets
--host 0.0.0.0 and the dashboard has no authentication at all.
# Run with OCI registry sources
docker run -p 127.0.0.1:3000:3000 \
-e PACTO_DASHBOARD_REPO=ghcr.io/org/svc-a,ghcr.io/org/svc-b \
ghcr.io/trianalab/pacto/dashboard:4.0.0
# Run with registry authentication
docker run -p 127.0.0.1:3000:3000 \
-e PACTO_DASHBOARD_REPO=ghcr.io/org/svc-a \
-e PACTO_REGISTRY_TOKEN=ghp_xxx \
ghcr.io/trianalab/pacto/dashboard:4.0.0
To build the image from a checkout instead, make docker-build and
make docker-run tag it with the version derived from your git state.
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_CORS_ORIGIN |
Trusted cross-origin allowed to call the API | "" (same-origin only) |
PACTO_DASHBOARD_TRACES |
Offline OTLP/JSON trace exports to fold observed dependencies from. Space-separated list of paths | "" |
PACTO_DASHBOARD_TRACE_SOURCES |
The same input with a stable identity per file: space-separated NAME=PATH entries, where NAME is the Data Source name the API and UI show |
"" |
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 | "" |
Most PACTO_DASHBOARD_* variables map to the CLI flag of the same name. Two do
not: PACTO_DASHBOARD_REPO is the repository positional argument, and
PACTO_DASHBOARD_TRACE_SOURCES maps to --trace-source, singular.
The two trace variables let the dashboard learn which services really call
each other, from trace files. Pacto has no OpenTelemetry (OTLP) receiver, so
you export traces to files and mount them in. Each file becomes a
named observation source. Under Kubernetes the operator-managed dashboard sets
PACTO_DASHBOARD_TRACE_SOURCES for you from dashboard.observation.sources —
see offline trace sources.
PACTO_CACHE_DIR sets only where the dashboard reads cached bundles. It
writes them under XDG_CACHE_HOME. To keep the cache, mount a volume at
~/.cache/pacto/oci. The defaults line up because the image sets
HOME=/home/pacto. The Kubernetes example below mounts an emptyDir there
instead, so its cache is rebuilt on every restart.
The CLI reference lists every variable.
Data Sources¶
The dashboard auto-detects its sources at startup. The container-specific bindings are:
oci:PACTO_DASHBOARD_REPOis set, or repositories are discovered from KubernetesresolvedReffields. Supplies bundles, version history, interfaces and diffs.cache: the on-disk OCI cache, surfaced as its own source only as an offline baseline when no registry is configured.k8s: a kubeconfig is mounted, or the container runs in-cluster. Supplies runtime state from the Pacto operator.local: apacto.yamlis found in the working directory (mount via volume).
Kubernetes + OCI hybrid mode¶
When the Kubernetes source is on and the operator fills
status.contract.resolvedRef, the dashboard finds the contract repositories
by itself. You get what is running from the operator and the contracts from
the registry. If a registry is unreachable, or private and not logged in, the
dashboard quietly falls back to Kubernetes only: no version history,
interfaces, schemas or diffs.
Mounting the k8s and local sources¶
Both arrive as volumes; inside a cluster the in-cluster config is used automatically, with no kubeconfig mount:
# k8s: a kubeconfig, scoped to one namespace
docker run -p 127.0.0.1:3000:3000 \
-v ~/.kube/config:/home/pacto/.kube/config:ro \
-e PACTO_DASHBOARD_NAMESPACE=production \
ghcr.io/trianalab/pacto/dashboard:4.0.0
# local: a contract directory
docker run -p 127.0.0.1:3000:3000 \
-v /path/to/contracts:/data:ro \
ghcr.io/trianalab/pacto/dashboard:4.0.0 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.json |
OpenAPI 3.1 specification (includes a server URL matching the bind address). Also served as /openapi.yaml, and downgraded to OpenAPI 3.0.3 at /openapi-3.0.json. |
GET /docs |
Interactive API documentation. |
/openapi on its own is a prefix, not a route: it returns 404. The image also
carries a Docker HEALTHCHECK polling /health every 10 seconds.
Security¶
The dashboard has no authentication — do not expose it
It ships no login, no API key and no authorization. Anyone who can reach
the port reads every contract, dependency and compliance result, and can
make the container pull from your registries. Publish it to 127.0.0.1
(-p 127.0.0.1:3000:3000) or put an authenticating proxy in front of it.
The protections below are CSRF and resource-exhaustion defences, not access
control, and none of them stop a client that can simply reach the port.
A few endpoints mutate local state (POST /api/resolve, POST /api/versions
and POST /api/refresh pull and cache OCI artifacts). Against those:
- Same-origin only by default. No
Access-Control-Allow-Originheader is emitted and cross-origin mutating requests get a403, so a malicious page in your browser cannot drive the dashboard. The bundled UI is unaffected. - Explicit cross-origin opt-in.
--cors-origin https://your-app(orPACTO_DASHBOARD_CORS_ORIGIN) allows one trusted cross-origin client. - HTTP timeouts guard against slow-client (Slowloris) exhaustion.
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:4.0.0
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