Runnable demo (Docker Compose)¶
A complete Pacto fleet running on your machine. An OCI registry holds real published contract revisions, an Evidence Server ingests a signed envelope from a "remote" environment and the dashboard shows the operational graph they add up to. It also shows runtime conformance live: a host agent watches the containers' sockets with eBPF and reports a service that is up but serves nothing.
There is no repository to clone, nothing to build and no file to download. The demo is published as an OCI artifact that Docker Compose owns and runs directly.
Run it¶
You need Docker Compose 2.34 or newer —
nothing else, not even the Pacto CLI. That release added
docker compose publish and -f oci://…. Older versions cannot run this
artifact at all, and the application says so itself in
x-pacto-demo.minimum-compose-version. The demo's registry is public, so no
login is needed; against a private registry, docker login <registry> first.
Resolve the release you want to the digest it published, then run that digest:
DEMO=$(docker manifest inspect -v ghcr.io/trianalab/pacto/demo:4.0.0 \
| sed -n 's/.*"digest": "\(sha256:[a-f0-9]*\)".*/\1/p' | head -1)
docker compose -f "oci://ghcr.io/trianalab/pacto/demo@$DEMO" \
-p pacto-demo up -d --wait -y
Each release publishes the demo under one tag; what runs is the digest that tag
resolved to, never the tag itself. -p pacto-demo names the project, which is
how you operate the stack afterwards: docker compose -p pacto-demo ps, logs,
stop, start and down -v all address it by name.
-y answers the one question Compose asks before it runs a stack fetched from a
registry: it lists the variables the artifact declares (the three ports below)
and waits for a yes.
Then open http://localhost:8080/#/fleet.
What you get¶
Four services. checkout has two published revisions — 1.1.0 drops an API path
1.0.0 exposed. orders declares checkout and is observed calling it, declares
payments and is never seen calling it. payments reaches the fleet as signed
evidence from a remote environment; checkout is observed calling it with no
contract declaring so.
The three edges are deliberately one of each: matched, declared but never
observed and observed but never declared. The observed calls come from a
recorded trace file, not from watching the containers. Follow the graph from orders to
checkout, open a revision to read its contract and compare checkout 1.0.0
with 1.1.0 to see a change analysed.
For the same moves at the command line — six user stories from "what is out there" to "an agent reading the same server" — follow the guided tour.
The live runtime check¶
inventory is the live part. Its container is running and labelled with its
contract and the port its API is bound to (dev.pacto.contract,
dev.pacto.bind.api=8080), but nothing in it listens there. The agent service
runs pacto agent with --sink evidence --docker: it
reads those labels, watches every container's TCP sockets with eBPF, and sends
what it sees, signed with its own key, to the Evidence Server. About half a
minute after start, inventory shows INTERFACE_ABSENT on api, seen by
pacto-agent+ebpf (the kernel). No platform API could report that: Docker
only knows the container is up.
The agent needs what a kernel observer needs, and the rest of the demo does not:
a privileged container in the host's PID and cgroup namespaces, running as root,
with the Docker socket and /sys/fs/cgroup mounted. Those two paths are the
only host paths the demo mounts. On Linux that means kernel 5.8 or later with BTF
and cgroup v2. On Docker Desktop (macOS and Windows) the "host" is Docker's own
Linux VM, which meets all of it. Where the kernel cannot load the program, the
agent says so in docker compose -p pacto-demo logs agent and inventory stays
without that finding; everything else works unchanged.
Ports¶
8080 (dashboard), 8686 (Evidence Server) and 5051 (the demo's registry).
Override with PACTO_DEMO_DASHBOARD_PORT, PACTO_DEMO_EVIDENCE_PORT and
PACTO_DEMO_REGISTRY_PORT:
PACTO_DEMO_DASHBOARD_PORT=8081 PACTO_DEMO_EVIDENCE_PORT=8687 \
PACTO_DEMO_REGISTRY_PORT=5052 \
docker compose -f "oci://ghcr.io/trianalab/pacto/demo@$DEMO" \
-p pacto-demo-next up -d --wait -y
Two project names, two sets of ports: both run at once, and down -v on one
leaves the other untouched.
Offline¶
The service images are pulled once and cached by Docker, so --pull never runs
the demo without reaching for them again:
docker compose -f "oci://ghcr.io/trianalab/pacto/demo@$DEMO" \
-p pacto-demo up -d --wait -y --pull never
The application itself is read from the registry by -f oci://… on every
invocation — Compose has no offline mode for it. So creating a project needs the
artifact registry reachable, once. After the project exists, operating it needs
nothing: stop, start, restart, logs and down -v address it by project
name, and the demo itself never leaves your machine. After the demo artifact and
its digest-pinned images have been pulled, the stack requires no external
network access. Its private Compose service network remains available because
the dashboard, Evidence Server and embedded registry must communicate with each
other.
Evidence is published as an OCI 1.1 referrer of the exact contract revision it
reports on, so the registry is the store and the Evidence Server keeps nothing.
The demo's registry is zot rather than CNCF distribution because the native
Referrers API is not optional here. docker compose -p pacto-demo down -v
removes the registry volume and the evidence with it.
The other demos¶
- Live dashboard demo — the same UI in your browser, with no Docker at all: the engine and a curated fleet compiled to WebAssembly.
- Dashboard container — the dashboard against your own services rather than a fixture.
Next: the Quickstart takes an empty directory to a validated contract and a caught breaking change in about five minutes.