Pacto for Developers
You own the service — and you own the contract. Pacto gives you a structured way to declare your service’s operational interface alongside your code, so platform engineers, CI systems, and other teams have an accurate, machine-readable description of what your service needs to run.
No forms. No tickets. No wiki pages that go stale. One YAML file, validated by tooling, versioned in a registry.
Table of contents
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
pacto init my-service
This scaffolds a contract with sensible defaults. Edit pacto.yaml to match your service.
2. Infer schemas from your code (optional)
If your service has a configuration file, use the schema-infer plugin to generate a JSON Schema from it. Use -o to write the output directly into your bundle:
pacto generate schema-infer my-service --option file=config.yaml -o my-service
This generates config.schema.json. Reference it in your contract:
configuration:
schema: config.schema.json
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:
configuration:
ref: oci://ghcr.io/acme/platform-config-pacto:1.0.0
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:
interfaces:
- name: api
type: http
port: 8080
visibility: public
contract: interfaces/openapi.yaml
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.
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
Ask yourself:
- Is my service long-running (
service) or does it run to completion (job)? - Does it hold local state that survives restarts (
stateful) or not (stateless)? - Does it keep optional in-memory state like caches (
hybrid)? - How critical is the data it handles?
The answers 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:
- ref: oci://ghcr.io/acme/auth-pacto@sha256:abc123
required: true
compatibility: "^2.0.0"
- 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
- ref: oci://ghcr.io/acme/utils-pacto
required: true
compatibility: "^3.0.0"
During development, you can reference local contracts:
dependencies:
- ref: file://../shared-db
required: true
compatibility: "^1.0.0"
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.
6. Adopt a policy (optional)
If your platform team publishes a policy contract, reference it in your contract:
policy:
ref: oci://ghcr.io/acme/platform-policy-pacto:1.0.0
A policy is a JSON Schema that validates the contract itself — enforcing organizational standards like requiring health endpoints or mandating specific ports. See policy 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:
service:
name: my-service
version: 1.0.0
chart:
ref: oci://ghcr.io/acme/my-chart
version: 1.0.0
During development, you can use a local chart path:
service:
chart:
ref: ./charts/my-chart
version: 1.0.0
Local chart references are rejected by pacto push. Switch to an OCI reference before publishing.
8. Validate before pushing
pacto validate my-service
Validation catches errors in three layers:
- Structural — missing fields, wrong types, invalid enum values
- Cross-field — interface references match, state invariants hold, files exist
- Semantic — strategy consistency warnings
9. Pack and push
pacto pack my-service
pacto push oci://ghcr.io/your-org/my-service-pacto -p my-service
If the artifact already exists in the registry, pacto push prints a warning and exits without pushing. Use --force to overwrite:
pacto push oci://ghcr.io/your-org/my-service-pacto -p my-service --force
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
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 configuration.values.DB_HOST=localhost
Overrides work on all commands that take a contract reference. For diff, use --old-set/--old-values and --new-set/--new-values to override each contract independently.
See the Contract Reference — Contract overrides section for full details.
Common patterns
Stateless HTTP API
runtime:
workload: service
state:
type: stateless
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
scaling:
min: 2
max: 10
Stateful service (database proxy, cache)
runtime:
workload: service
state:
type: stateful
persistence:
scope: local
durability: persistent
dataCriticality: high
lifecycle:
upgradeStrategy: ordered
gracefulShutdownSeconds: 60
health:
interface: api
path: /health
scaling:
min: 3
max: 5
API with local cache (hybrid)
runtime:
workload: service
state:
type: hybrid
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
scaling:
min: 2
max: 8
A hybrid service handles requests statelessly but keeps a local cache or session store. The platform knows it can scale horizontally, but might account for cache warm-up time.
Fixed-replica service
Use replicas instead of min/max when the service should always run an exact number of instances:
scaling:
replicas: 1
Scheduled job
runtime:
workload: scheduled
state:
type: stateless
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
# No scaling — jobs don't scale horizontally
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]
Integrate pacto diff into your CI pipeline to block merges that introduce breaking changes.
Using GitHub Actions? Check out the official Pacto CLI action.
AI-assisted workflow
If you use an AI assistant that supports MCP (Claude Code, Cursor, GitHub Copilot), you can connect it to Pacto so it can validate, inspect, and generate contracts on your behalf.
Setup
Add Pacto as an MCP server in your project. For Claude Code, create .mcp.json in your project root:
{
"mcpServers": {
"pacto": {
"command": "pacto",
"args": ["mcp"]
}
}
}
For other tools, see the full MCP Integration guide.
What you can do
Once connected, the AI assistant can use Pacto tools directly in your conversation:
- Validate — “Validate my contract in ./my-service” — catches structural, cross-field, and semantic errors without leaving your editor
- Inspect — “Show me the full contract for oci://ghcr.io/acme/auth-pacto:2.0.0” — explore contracts from your registry
- Explain — “Explain what this service does” — get a human-readable summary of interfaces, dependencies, and runtime behavior
- Generate — “Generate a contract for a stateless Go HTTP API called user-service” — scaffold new contracts from a description
- Dependencies — “What does my service depend on?” — resolve and explore the dependency graph
- Documentation — “Generate docs for this contract” — produce Markdown documentation without running CLI commands
This is particularly useful when writing a new contract from scratch — describe your service to the assistant and let it generate the initial pacto.yaml, then iterate with validation feedback in the same conversation.
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 travels with the contract as part of the OCI artifact, so it is versioned and distributed alongside the contract it describes. It has no effect on validation, diffing, or compatibility checks — changes to docs/ never produce diff entries or affect classification.
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:
my-service/
pacto.yaml
interfaces/
openapi.yaml
sbom/
sbom.spdx.json
Pacto supports SPDX 2.3 (.spdx.json) and CycloneDX 1.5 (.cdx.json) formats. The recommended tool for generating SBOMs is Syft:
# 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
Other supported generators include Trivy and cdxgen.
The SBOM travels with the contract as part of the OCI artifact. When both the old and new versions of a contract include an SBOM, pacto diff reports package-level changes (added, removed, version or license modified). These changes are informational — they never affect the overall breaking/non-breaking classification.
No contract-level field references the SBOM. Pacto discovers it automatically by scanning the sbom/ directory for recognized file extensions — the same convention-based approach used for docs/.
Tips
- Version your contract alongside your code. The
pacto.yamllives in your repository. - Pin dependency digests in production. Tags are mutable; digests are not.
- Keep interface contracts up to date. OpenAPI specs and protobuf definitions in the bundle should match what your service actually serves.
- Include documentation in the bundle. Add a
docs/directory with runbooks, architecture notes, and integration guides. It ships with the contract but doesn’t affect diffing or validation. - Include an SBOM. Add an SBOM to
sbom/using Syft, Trivy, or cdxgen.pacto diffwill report package-level changes between versions. - 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.