The Pacto model¶
How Pacto decides whether a running service matches its contract. Three words carry the whole page:
- A contract is what the service's authors say it is (
pacto.yaml). - Evidence is what a tool saw in the running system, such as "a pod listens on port 8080".
- A collector is the tool that gathers that evidence.
The engine compares the two and reports findings: one line per problem, each
with a code such as INTERFACE_ABSENT. One rule governs it: a confirmed
contradiction is an error, and a check Pacto could not make is Unknown.
Most of what follows keeps those two apart.
Declaration versus observation¶
The contract records intent: what a service is, whatever platform runs it.
What it looks like at runtime is an observation, and it lives outside the
contract. A contract carries only intent. Ports, scaling, images and other
runtime settings belong to delivery and the integrations own them. Runtime facts travel separately, as an EvidenceSet produced by
a collector, and the engine reasons over the two together.
flowchart TB
A["Author intent<br/>pacto.yaml"] --> C["Contract"]
R["Running environment"] --> COL["Collector"]
COL --> E["EvidenceSet"]
C --> EV["Evaluate"]
E --> EV
EV --> OUT["Findings + Coverage"]
The engine: Evaluate(contract, evidence)¶
The heart of the system is a pure function, Evaluate:
It keeps no state. Each observation carries an Outcome the collector set
(Observed, Failed, Stale and so on), and the engine applies no timing,
network or Kubernetes logic of its own. An assertion is one thing the contract
claims, such as "the interface public-api is served". For each required
assertion (an interface's availability, a capability, a dependency, a
configuration, the workload or persistence), the engine looks for a matching
observation and produces exactly one of three results:
| Result | When | Finding |
|---|---|---|
| Confirmed violation | A matching observation has Outcome=Observed and its payload contradicts the contract. |
error, category RuntimeDrift, for example CONFIGURATION_ABSENT, CONFIGURATION_MISMATCH, INTERFACE_ABSENT, DEPENDENCY_UNREACHABLE. |
| Uncertainty | No usable observation exists: missing, Unsupported, Failed, Stale or Insufficient. |
unknown, category Inconclusive, for example EVIDENCE_MISSING, COLLECTION_FAILED. |
| Satisfied | A matching Observed observation is consistent with the contract. |
None. |
A required assertion Pacto cannot observe never counts as a pass.
Coverage is the count of what the engine could check: it reports how many required assertions were evaluated versus declared.
Coverage leaves the aggregate compliance state unchanged, because a check
Pacto could not make is uncertainty.
Compliance model¶
The compliance model consumers derive from these findings has four substantive states, Compliant, NonCompliant, Unknown and Invalid, plus three informational ones:
| State | Meaning |
|---|---|
| Warning | A non-blocking finding. |
| Reference | The contract declares no workload, so there is nothing to run and nothing to observe. |
| NotEvaluated | The contract declares a workload but was never runtime-evaluated at all. This is what an offline OCI, cache or local source looks like, and what pacto doc, pacto fleet and the dashboard report for a bundle read off disk or out of a registry. |
A workload that is being evaluated but has no usable evidence yet resolves to
Unknown. The two answer different questions: Unknown means "we looked and
could not tell" and NotEvaluated means "nothing has looked". The Kubernetes
operator reports only Unknown. NotEvaluated is in the CRD enum for parity with the engine (see Kubernetes
limitations).
Collectors and the evidence boundary¶
A collector is any component that observes a real system and produces a valid
EvidenceSet. That EvidenceSet is the stable extension boundary. A collector
and a plugin extend Pacto in opposite directions: a plugin consumes a contract to
generate an artifact, and a collector observes a system to produce evidence.
You integrate a collector by producing an EvidenceSet.
A collector is never installed as a plugin.
Pacto ships one general runtime collector and plugs it into each platform. The
watcher observes TCP listeners and connection outcomes with eBPF and names the
workload behind each socket: a pod, a container, a systemd unit, a pacto run
session or a plain process. Its facts are platform neutral, and so are its
health, metrics and protocol probes. It runs on Linux hosts that meet its
requirements. Three front ends put it to
work (see runtime conformance):
pacto runwraps one command and evaluates it locally.pacto agentwatches a host, binds workloads to contracts from Docker labels or a bindings file and evaluates them locally or sends signed evidence.- The Kubernetes operator (
integrations/kubernetes) can deploypacto agenton every node (opt-in:nodeAgent.enabled) and merges what it publishes with its own Kubernetes collector, which reads the API server, into oneEvidenceSet.
Every path ends in the same Evaluate. The "custom collector" box below is a
design extension point.
flowchart LR
C["Pacto contract<br/>declared intent"]
subgraph Collectors["Collectors: environment-specific"]
W["Runtime watcher: eBPF + probes<br/><i>first-party, Linux</i>"]
K["Kubernetes collector<br/><i>first-party, operator</i>"]
X["Custom collector<br/><i>external integration</i>"]
end
W -- "pacto run / pacto agent" --> E["EvidenceSet<br/>observed facts"]
W -- "node agent, merged by the operator" --> K
K --> E
X -.-> E
C --> V["Evaluate"]
E --> V
V --> F["Findings + Coverage"]
In-process, a custom Go collector builds an EvidenceSet (pkg/evidence) and
calls Evaluate (pkg/validation) directly. This is the compiled, run
ExampleEvaluate in pkg/validation/collector_example_test.go:
// 1. The contract declares intent (loaded from a bundle in real use).
c := contract.Contract{
Service: contract.Service{Name: "orders", Version: "1.0.0"},
Interfaces: []contract.Interface{{Name: "public-api", Type: "openapi", Ref: "interfaces/openapi.yaml"}},
}
// 2. A collector observes the environment and produces an EvidenceSet.
prov := evidence.Provenance{Collector: "example", DetectedAt: time.Unix(0, 0)}
ev := evidence.EvidenceSet{
Subject: evidence.SubjectRef{Kind: "service", Name: "orders"},
ContractRef: "oci://example/orders:1.0.0",
Source: "example",
ObservedAt: time.Unix(0, 0),
Observations: []evidence.Observation{
evidence.NewInterfaceObserved(evidence.SubjectRef{Kind: "interface", Name: "public-api"}, "openapi", true, prov),
},
}
// 3. The pure engine evaluates Contract x Evidence.
findings, coverage := validation.Evaluate(c, ev)
Across a trust boundary a remote environment reports a signed EvidenceSet over
the evidence protocol instead.
Distinctions the model keeps¶
The model keeps each of these pairs apart. Merging a pair turns an uncertain answer into a wrong one.
- A requested reference and a resolved identity.
payments-api:latestis a question;payments-api@sha256:…is an answer. A tag is mutable, so resolve the reference before you treat it as exact content. - The same name in two domains. Identity is domain-qualified, so two
organizations that both run a
payments-apistay two services. A repository basename or a path leaf is a label. - An exact revision match and retrievable content. A target can pin a digest that names a revision unambiguously while that content sits in a registry Pacto cannot read. A feature that needs the content reports it as missing; a feature that needs only the identity works as usual.
- Declared ownership and a canonical owner identity. An owner identity carries both its kind and its value, so a team and a person who share a string stay separate. An email address is a contact for an owner.
- Readiness and compliance. Readiness is a team's own scored self-assessment of a revision. Compliance comes from evidence. A revision that passes every readiness check can run on a target that is non-compliant.
- No evidence and evidence of absence. Pacto records each observation as observed, unsupported, failed, stale or insufficient. "We could not look" and "we looked and it was missing" are different records.
Scope¶
Pacto records what a service revision guarantees, so that people and tools can compute against it. These limits hold for the contract engine and the operator. The opt-in delivery component changes two of them, as noted.
- Pacto complements your developer portal and service catalog. A portal decides what people see and a catalog lists services to browse. Pacto has no onboarding, scaffolding templates or golden paths.
- Pacto publishes to the OCI registries you already run.
- Pacto models one service's operational intent. Your databases, queues and networks stay in the tools that manage them.
- Permissions stay with your runtime controls. Pacto validates a contract against policy schemas. OPA, Kyverno, admission and IAM decide whether an action is permitted. Delivery adds its own admission policies, which decide who may move or approve a change to a Stage.
- The engine reasons; collectors observe; actors act. The engine reads only
ContractandEvidenceSet. Delivery is one such actor: it picks the version a Stage runs, rolls back and freezes, and Flux or Argo CD applies it. - Reading a contract grants no permission. Finding and reading a contract changes nothing and performs no action.
The operational control loop¶
These roles compose into a loop a platform or an agent can drive. Steps 1, 2, 5
and 6 are implemented here: the CLI, the dashboard, the collector and
Evaluate. Steps 3 and 4 belong to systems you already run.
- Declare. A contract states the service's identity, interfaces, capabilities, configuration, dependencies and policies.
- Read. A platform, a controller or an agent inspects the contract (
pacto explain, the dashboard API or generated tools over MCP). - Constrain. (External.) Policies, permissions, admission and IAM decide which actions are allowed.
- Act. (External.) Controllers, deploy systems, plugins or agents act through existing infrastructure.
- Observe. Collectors obtain runtime evidence from the real system and produce an
EvidenceSet. - Evaluate.
Evaluate(contract, evidence)reports whether observed reality is consistent with the declared contract.
What's next¶
- The Pacto Operational Graph: the read model over many such evaluations, and the vocabulary for how much of the world an answer saw
- Validation layers: whether a contract is valid, a separate question from whether it matches reality
- Evidence protocol: reporting a signed
EvidenceSetacross a trust boundary - Impact analysis: the confidence model over a change