Pacto for Developers¶
You own the service — and you own the contract. Pacto gives you a structured way to declare your service's operational contract alongside your code, so platform engineers, CI systems and other teams have an accurate, machine-readable description of what your service needs to run.
Under the hood, Pacto composes the interfaces you already have — your OpenAPI spec, your config's JSON Schema — instead of inventing new formats for them. What it adds is the operational contract no single schema owns: ownership, dependencies, compatibility and readiness.
One validated, versioned YAML file instead of stale wiki pages and tickets.
Your workflow¶
flowchart LR
A[Write code] --> B[Infer schemas]
B --> C[Define pacto.yaml]
C --> D[pacto validate]
D --> E[pacto pack]
E --> F[pacto push]
F --> G[CI / Platform picks it up]
1. Initialize your contract¶
This scaffolds a bundle with a valid contract. Edit pacto.yaml to match your service.
2. Infer schemas from your code (optional)¶
A configuration interface in Pacto is a JSON Schema, and Pacto composes the schema you already have rather than making you redefine it. If your config already ships a JSON Schema — for example your Helm chart's values.schema.json — vendor that file into your bundle and point configurations[].schema at it. If it doesn't, the schema-infer plugin generates one from a config file. Use -o to write the output into your bundle:
This generates config.schema.json. Reference it in your contract:
When you define your own configuration schema, you are declaring what your service requires to run. This is the most common model for services that need to be portable across environments. If your platform team provides a shared schema instead, you can either vendor it into your bundle or reference it via OCI:
See Configuration Schema Ownership Models for details.
If your service exposes an HTTP API using FastAPI or Huma, use the openapi-infer plugin to extract an OpenAPI 3.1 spec from your source code:
# Auto-detect framework (generates interfaces/openapi.yaml)
pacto generate openapi-infer my-service -o my-service
# Override framework detection
pacto generate openapi-infer my-service -o my-service --option framework=fastapi
# Custom output path (format inferred from extension)
pacto generate openapi-infer my-service -o my-service --option output=interfaces/openapi.json
Then reference the generated spec in your contract:
Both plugins are installed automatically with Pacto. See the Official plugins section for details.
3. Declare your interfaces (optional)¶
List every boundary your service exposes. Services with no network interfaces (e.g. batch jobs or shared libraries) may omit this section:
interfaces:
- name: api
type: http
port: 8080
visibility: public
contract: interfaces/openapi.yaml
- name: events
type: event
visibility: internal
contract: interfaces/events.yaml
Include the actual interface files (OpenAPI specs, protobuf definitions, event schemas) in the bundle. Pacto references these definitions as-is — it composes the interface contracts you already publish rather than asking you to describe them a second time.
4. Define your runtime semantics (optional)¶
This is where you tell the platform how your service behaves — not how to deploy it, but what it is:
runtime:
workload: service
state:
type: stateless
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
Choose your workload (service vs job/scheduled), state.type (stateless/stateful/hybrid) and dataCriticality; these determine how platforms provision infrastructure for your service. See runtime.state in the Contract Reference for the full explanation.
5. Declare dependencies¶
If your service depends on other Pacto-enabled services:
dependencies:
- name: auth
ref: oci://ghcr.io/acme/auth-pacto@sha256:abc123
required: true
compatibility: "^2.0.0"
- name: cache
ref: oci://ghcr.io/acme/cache-pacto:1.0.0
required: false
compatibility: "~1.0.0"
# Tag omitted — resolves to the highest version matching ^3.0.0
- name: utils
ref: oci://ghcr.io/acme/utils-pacto
required: true
compatibility: "^3.0.0"
During development, you can reference local contracts:
Warning
Local refs are rejected by pacto push. Switch all dependencies to oci:// references before publishing.
If your service depends on a cloud-managed resource (e.g. a database or message queue), create a minimal Pacto contract representing it and reference it as a dependency. This keeps cloud dependencies explicit and version-tracked.
Use pacto graph to visualize your dependency tree. Pass --with-references to also see config/policy reference edges alongside dependencies, or --only-references to show only reference edges. A reference (a config or policy ref) points at a shared configuration or policy contract, as opposed to a dependency, which is a runtime relationship to another service.
6. Adopt a policy (optional)¶
If your platform team publishes a policy contract, reference it in your contract:
A policy is a JSON Schema that validates the contract itself — enforcing organizational standards like requiring health endpoints or mandating specific ports. See policies in the Contract Reference for details.
7. Reference your Helm chart (optional)¶
If your service is deployed via a Helm chart, reference it in the contract:
During development, you can use a local chart path:
Warning
Local chart references are rejected by pacto push. Switch to an OCI reference before publishing.
8. Validate before pushing¶
Validation catches errors in four layers:
- Structural — missing fields, wrong types, invalid enum values
- Cross-field — interface references match, state invariants hold, files exist
- Semantic — strategy consistency warnings
- Policy enforcement — referenced policies are resolved and enforced
See Validation layers for the full rules and error codes.
To also enforce the readiness gate — the readiness: block pacto init scaffolds into your contract — run pacto validate --readiness. It fails if the derived readiness score is below minScore. Plain pacto validate does not enforce it because the gate is time-dependent (check expiry is compared against the run time). See Contract Reference — readiness.
9. Pack and push¶
Use a .pactoignore file to keep build artifacts and other cruft out of the packed bundle.
If the artifact already exists in the registry, pacto push prints a warning and exits without pushing. Use --force to overwrite:
Using contract overrides¶
Pacto supports Helm-style overrides to modify contract values without editing pacto.yaml. This is useful for environment-specific values, CI pipelines or quick experimentation.
# Override a value inline
pacto validate my-service --set service.version=2.0.0
# Use a values file (-f is short for --values on most commands)
pacto validate my-service -f staging-values.yaml
# Combine both (--set takes precedence)
pacto validate my-service -f staging-values.yaml --set service.version=3.0.0
# Set configuration values
pacto validate my-service --set configurations[0].values.DB_HOST=localhost
Overrides work on every command that takes a contract reference, with two exceptions: diff overrides each side with --old-values/--old-set and --new-values/--new-set (it has no -f or plain --values), and pacto push reserves -f for --force, so spell out --values there.
For the per-command flag list see the CLI reference; for override precedence and syntax see the Contract Reference — Contract overrides.
Common runtime patterns¶
Each common shape has a ready-made worked example you can copy:
| Pattern | state.type |
Worked example |
|---|---|---|
| Stateless HTTP API | stateless |
nginx |
| Stateful service (database, cache) | stateful |
postgresql |
| API with local cache | hybrid |
hybrid-cache |
| Scheduled job | stateless (workload scheduled) |
cron-worker |
See runtime.state for the full field spec.
Use scaling.replicas instead of min/max when the service should always run an exact number of instances:
Detecting breaking changes¶
Before releasing a new version, diff against the previous one:
$ pacto diff oci://ghcr.io/acme/my-service-pacto:1.0.0 my-service
Classification: BREAKING
Changes (2):
[BREAKING] interfaces (removed): metrics [- metrics]
[NON_BREAKING] service.version (modified): service.version modified [1.0.0 -> 1.1.0]
Wire pacto diff into CI to block merges that introduce breaking changes — see the official Pacto CLI action.
AI-assisted workflow¶
If you use an AI assistant that supports MCP (Claude Code, Cursor and GitHub Copilot), connect it to Pacto so it can scaffold, edit and validate contracts inside your conversation. The server always exposes four authoring tools:
pacto_create— scaffold a new contract from a descriptionpacto_edit— modify an existing contractpacto_check— validate a local contract and return a summary plus improvement suggestionspacto_schema— return the full contract JSON Schema reference
Point the server at a bundle (pacto mcp <bundle-ref>) and it also exposes that bundle's OpenAPI operations as executable tools plus a pacto_skill tool for any bundled skills/*.md — see Agent capabilities.
Inspecting a registry contract, resolving dependency graphs and generating Markdown docs are CLI-only (pacto explain oci://..., pacto graph, pacto doc) — they are not MCP tools.
See the MCP Integration guide for the .mcp.json setup across all clients.
Including documentation¶
You can include an optional docs/ directory in your bundle to ship human-readable documentation alongside the contract:
my-service/
pacto.yaml
interfaces/
openapi.yaml
docs/
README.md
architecture.md
runbook.md
integration.md
Documentation ships inside the OCI artifact, versioned and distributed with the contract; it never affects validation or diffing. See the Contract Reference — docs/ for the full behavior.
Good candidates for docs/:
- Service overview — what the service does and its purpose
- Architecture notes — internal design and data flow
- Operational runbooks — incident response and scaling procedures
- Integration guides — how consumers should interact with the service
Including an SBOM¶
You can include an optional sbom/ directory in your bundle to ship a Software Bill of Materials alongside the contract:
Generate one with Syft (or Trivy/cdxgen):
# Generate an SPDX SBOM
syft . -o spdx-json=sbom/sbom.spdx.json
# Or generate a CycloneDX SBOM
syft . -o cyclonedx-json=sbom/bom.cdx.json
Pacto discovers the SBOM by scanning sbom/ for recognized extensions — no contract field references it. For the supported formats (SPDX 2.3, CycloneDX 1.5) and how pacto diff reports package-level changes, see the Contract Reference — sbom/.
Tips¶
- Version your contract alongside your code. The
pacto.yamllives in your repository. - Pin dependency digests in production. Tags are mutable; digests are not. Run
pacto lockto pin the full transitive closure to digests in a committedpacto.lock. - Keep interface contracts up to date. OpenAPI specs and protobuf definitions in the bundle should match what your service actually serves.
- Use
pacto explainto review. It produces a human-readable summary of your contract. - Use
pacto docfor rich documentation. It generates Markdown with architecture diagrams and interface tables. Use--serveto view it in the browser. - Leverage caching. OCI bundles are cached locally in
~/.cache/pacto/oci/and tag listings are cached in memory per command, so repeatedgraph,doc, anddiffcommands resolve instantly. Use--no-cacheto force a fresh pull. - Use
--verbosefor debugging. Pass-vto any command to see debug-level logs (OCI operations, resolution steps, cache hits/misses) on stderr. - Use metadata for organizational context. Team ownership, on-call channels, and service tiers go in
metadata. - Explore contracts visually. Run
pacto dashboardto launch the contract exploration dashboard — navigate dependency graphs, inspect interfaces, browse version history, compare diffs, and review configuration schemas. It auto-detects contracts from local directories, OCI registries, and Kubernetes.