Plugin Development¶
A plugin turns a contract into files: Helm charts, Terraform, Kubernetes manifests, a README. It is a separate program, written in any language. pacto generate <name> sends it the contract as JSON on stdin, and the plugin answers with a list of files on stdout. The Bash example below is a complete, working plugin.
Official plugins¶
| Plugin | Description |
|---|---|
| pacto-plugin-schema-infer | Infers a JSON Schema from sample configuration files (JSON, YAML, TOML) |
| pacto-plugin-openapi-infer | Extracts OpenAPI 3.1 specs from source code (FastAPI, Huma) |
Both work in the other direction: they read files you already have and write contract inputs, so you do not hand-write a schema. No deployment-artifact plugin ships with Pacto. pacto generate helm is a plugin you write or install, not one Pacto provides. Without a pacto-plugin-helm on your PATH it exits 1 with plugin "helm" not found (looked for pacto-plugin-helm in $PATH and ~/.config/pacto/plugins/).
See Installation for how to install them. Refer to each plugin's README for usage:
How it works¶
Pacto loads the contract, finds the plugin program (pacto-plugin-<name>), writes a request to its stdin, reads the response from its stdout and writes the files. A plugin gets a copy of the contract and cannot change it. Pacto stops a plugin after 60 seconds or 64 MB of output.
Plugin discovery¶
Pacto searches for pacto-plugin-<name> in $PATH, then ~/.config/pacto/plugins/. Not found is an error: pacto generate helm without pacto-plugin-helm exits 1.
Protocol (v1)¶
Request (stdin)¶
Pacto writes a JSON object to the plugin's stdin:
{
"protocolVersion": "1",
"contract": {
"pactoVersion": "2.0",
"service": {
"name": "my-service",
"version": "1.0.0"
},
"interfaces": [...],
"workload": "service",
"state": {...},
...
},
"bundleDir": "my-service",
"outputDir": "helm-output",
"options": {
"namespace": "production"
}
}
| Field | Type | Description |
|---|---|---|
protocolVersion |
string | Always "1" |
contract |
object | The full parsed contract |
bundleDir |
string | Path to the bundle (may be relative) |
outputDir |
string | Path for output files (may be relative) |
options |
object | Optional. User-provided key-value options from --option key=value |
Response (stdout)¶
The plugin writes a JSON object to stdout:
{
"files": [
{
"path": "deployment.yaml",
"content": "apiVersion: apps/v1\nkind: Deployment\n..."
},
{
"path": "service.yaml",
"content": "apiVersion: v1\nkind: Service\n..."
}
],
"message": "Generated Kubernetes manifests for my-service"
}
| Field | Type | Description |
|---|---|---|
files |
array | List of generated files |
files[].path |
string | Relative path within output directory |
files[].content |
string | File content |
message |
string | Optional message |
files[].path must be relative. Absolute paths and .. are rejected. On error, write to stderr and exit non-zero.
Example: Minimal plugin in Bash¶
#!/usr/bin/env bash
# pacto-plugin-readme — Generates a README from a Pacto contract
set -euo pipefail
# Read the full JSON request from stdin
REQUEST=$(cat)
# Extract fields using jq
NAME=$(echo "$REQUEST" | jq -r '.contract.service.name')
VERSION=$(echo "$REQUEST" | jq -r '.contract.service.version')
WORKLOAD=$(echo "$REQUEST" | jq -r '.contract.workload // "n/a"')
STATE=$(echo "$REQUEST" | jq -r '.contract.state.type // "n/a"')
# Generate a README
CONTENT="# ${NAME}
**Version:** ${VERSION}
**Workload:** ${WORKLOAD}
**State:** ${STATE}
This file was auto-generated by pacto-plugin-readme.
"
# Write the response JSON to stdout
jq -n \
--arg path "README.md" \
--arg content "$CONTENT" \
--arg msg "Generated README for ${NAME}" \
'{files: [{path: $path, content: $content}], message: $msg}'
Make it executable and place it in your $PATH:
chmod +x pacto-plugin-readme
mv pacto-plugin-readme /usr/local/bin/
pacto generate readme my-service
Guidelines¶
- Read only from
bundleDir. Do not access files outside the bundle. - Write only to stdout. Do not write files directly; return them in the response. Pacto handles file creation.
- Follow the protocol. Return clean relative paths, write errors to stderr and exit non-zero on failure.
- Be deterministic. Given the same input, produce the same output.
- Handle missing optional fields. Only
pactoVersionandserviceare required — not all contracts haveworkload,state,configurations,dependenciesorcapabilities.