3.3 KiB
Destination State Contract
Audience: operators, integrators, and maintainers who inspect or reason about destination .distributor.json files.
Each managed destination bundle path contains .distributor.json. This file is the destination sentinel and state record used for comparison, skip, replacement, and recovery decisions.
State Schema
Current schema version: 1.
{
"schema_version": 1,
"distributor_version": "dev",
"pipeline_id": "reports",
"destination_id": "archive",
"published_at": "2026-06-04T12:00:00Z",
"source": {
"manifest": {
"schema_version": 1,
"id": "reports.example.2026-06-04",
"digest": "sha256:...",
"created": "2026-06-04T11:55:00Z",
"files": [
{"path": "report.md", "sha256": "sha256:...", "size": 1234}
]
}
},
"links": {
"primary_url": "https://reports.example.com/archive/report.html"
},
"outputs": [
{
"path": "report.html",
"kind": "generated",
"source_path": "report.md",
"transform": "markdown_to_html",
"url": "https://reports.example.com/archive/report.html",
"sha256": "sha256:...",
"size": 2345
}
]
}
Required fields:
schema_version: must be1.pipeline_id: configured pipeline id that wrote the state.destination_id: configured destination id that wrote the state.published_at: RFC3339 publication timestamp.source.manifest: embedded source bundle manifest.outputs: output records array; empty is allowed, but the field is required.
Optional fields:
distributor_version: application version string when available.links.primary_url: absolute HTTP or HTTPS URL selected by destination link policy.
Output Records
Each output record has:
path: destination-relative output path.kind:sourceorgenerated.source_path: source manifest path used for the output.transform: required forgeneratedoutputs; omitted for copied source outputs.url: optional absolute HTTP or HTTPS URL for the output.sha256: lowercasesha256:<64 hex>digest of the output bytes.size: output byte size, zero or greater.
Output paths must be unique and use clean relative slash-separated path rules.
Comparison Semantics
distributor compares the current source manifest to destination state before writing:
- No state and no content: publish new outputs.
- No state and existing content: treat the destination as unmanaged.
- Matching embedded source manifest: skip.
- Same source id with older
created: replace if policy allows. - Same source id with newer
created: skip by default. - Same source id and same
createdwith different digest: conflict. - Different source id, pipeline id, or destination id: conflict.
- Invalid state JSON or invalid state fields: conflict.
Normal replacement deletes only managed output paths recorded in outputs plus .distributor.json. Forced replacement deletes the bounded destination bundle path.
Boundaries
Destination state is internal managed state written by distributor. Operators may inspect it during recovery, but normal workflows should not edit it by hand. Source manifest.json is not copied as destination state.
Tests
Before changing this contract, inspect and run:
go test ./internal/state ./internal/publish