External governance handoff
Apizr 0.4.4 adds an optional document export and verification step to the existing bundle/delivery journey. Apizr OSS still analyzes, generates and runs locally without Trunx, a registry or an approval service.
Apizr describes capabilities and records technical delivery observations. An
external system such as Trunx stores artifacts, indexes capabilities, assigns
organizational responsibility and approves environments. The pipeline applies
that separate decision before deploying. The runtime or gateway authorizes calls.
delivery_admitted: true is technical proof verification and observed promotion;
it is never organizational approval to deploy to production.
Existing documents, one identity graph
No CapabilityRelease schema is introduced. The exported files are byte-for-byte
copies of the existing direct REST/MCP bundle's public JSON records. They include
no Python modules, application resources, wheels, credentials or operator grants.
Docstrings, declared expressions and source metadata remain public document
content; review them before sharing.
| Need | Existing document / location |
|---|---|
| Stable logical capability ID | capability-catalog.json: capabilities[].id, python:<module>:<symbol> |
| Description, declared input/output and uncertainty | Catalog sources[].inspection.capability_ir.capabilities[]; repository-interface.json invocation contracts |
| Concrete generated REST routes or MCP tools | apizr-repository-rest.json / apizr-repository-mcp.json, openapi.json / mcp-tools.json |
| Static eligibility, constraints and limits | repository-readiness.json assessments and embedded policy; catalog inspections; capability-graph.json |
| Selected capabilities and execution contracts | exposure-policy.json, exposure-plan.json |
| Analysis policies | Catalog scan_policy, graph graph_policy, readiness policy, each with its digest |
| Source acquisition and generator | apizr-bundle-provenance.json when present; unrecorded provenance remains unrecorded |
| Bundle and file identities | Existing bundle manifest artifacts, source hashes and repository_interface_digest |
| Exact build and delivery linkage | BuildResult's delivery_plan.bundle_manifest_digest, delivery_manifest and delivery_manifest_digest |
| Immutable image, proof and delivery observations | Existing PushResult, PublishResult and AdmissionResult, retained separately |
A logical ID survives a function-body, docstring or annotation change. It is not
a version, artifact digest or globally unique organization ID. Keep the source
repository/provenance and bundle digest alongside it; two projects can legitimately
use the same python:pricing:total. Renaming a module/symbol changes the ID. Source
bytes, contracts, policies, selected exposure and generator/build inputs have
separate content identities. A changed body can keep the same input/output
contract while changing source, catalog, bundle and downstream delivery identities.
Declarations (annotations, docstrings, policy choices) are not runtime facts.
Graph/readiness results are static analysis with explicit unknowns and limits;
unknown never means safe. Generated endpoints/tools describe the generated
interface, not a running or reachable deployment. Only executed calls and delivery
observations are runtime evidence, with the scope and time of their observation.
Export and verify in CI
Start with the existing apizr ci build-rest / build-mcp or apizr expose build
commands and explicit analysis authority. Build the OCI image through the existing
OCI plugin when an image is needed. Preserve the original plugin results.
apizr plugins run returns an envelope: save its result object as build.json.
The Python API already returns the corresponding typed BuildResult.
apizr expose export --bundle-dir ./rest-bundle --interface rest \
--output-dir ./capability-documents --build-result ./build.json \
> ./bundle-manifest.json
Omit --build-result for a standalone local bundle. When supplied, every existing
plan link is checked against the documents and the observed build's lineage is
validated. Historical builds without delivery lineage still work through their
original APIs, but cannot establish this extra bundle-to-build link.
Transport capability-documents/, build.json, the original push/publication/
admission results and the selected remote proof through your governance system's
documented, separately authenticated ingestion mechanism. Apizr does not
invent a Trunx API. Keep the independently approved image reference, proof
reference and bundle digest in the pipeline's protected inputs.
After receiving documents, a consumer can validate them without business code:
apizr expose verify --evidence-dir ./received-documents --interface rest \
--bundle-sha256 "$REVIEWED_BUNDLE_SHA256" --build-result ./build.json \
> ./verified-bundle-manifest.json
Both new commands emit the existing bundle manifest JSON only on stdout,
return 0 on success and 2 with apizr expose: evidence_invalid on stderr on
invalid/inaccessible evidence. Argument errors also return 2. Export validates
the complete bundle without importing it, refuses nonempty output, and publishes
atomically. Verify reads only fixed-name regular JSON files, refuses links,
ambiguous duplicate keys and unknown schemas/fields, and caps selected evidence
at 64 MiB. Other files are ignored and cannot contribute evidence.
The Python equivalents are export_evidence and verify_evidence in
apizr.repository_interfaces.evidence; they return existing RestManifest or
MCPManifest models and raise input/integrity exceptions. The initial export
supports direct repository REST/MCP bundles used by the OCI service builder.
Governed execution bundle schemas remain supported by their existing runtime
validators, not this projection. No schema migration or weakening occurs.
Verification proves consistency with the independently selected digest. It
does not authenticate a hash supplied by an attacker, hash absent source/layer
bytes, import the application, rerun analysis from source, inspect a registry or
verify a signature. For delivery authenticity use the existing Attest fetch,
verify and admit operations with independently configured signer/trust.
A BuildResult alone is not a verified proof.
Delivery, retries and consuming a precise artifact
Use proof_requirement: required before building when proof must gate promotion.
The current apizr delivery run / resume coordinator and plugin operations
remain the only delivery interfaces. Their JSON states and exit semantics are
specified in multi-destination delivery.
| Observation | What the pipeline may conclude |
|---|---|
Push published:true, transfer_verified:true |
Image transferred and immutable remote identity checked |
Required push destination_promoted:false, delivery_admitted:false |
Destination promotion is withheld pending technical admission |
Publish state:verified |
Selected proof artifact is published and checked; no deployment approval |
Admission state:admitted |
Selected proof verified and destination observed at the expected digest |
remote_state_unconfirmed, timeout, interruption or partial batch |
Re-observe/resume exact identities; never infer rollback or success |
| External environment decision | Organizational authority, evaluated by the external system, not Apizr |
Admission re-fetches the explicitly selected proof, checks its exact image and
manifest, signature, timestamp and recomputation before promotion. A wrong or
altered proof is refused. An identical destination permits idempotent completion;
a conflicting image is refused. Tags can subsequently move; consume the recorded
repository@sha256:... reference, never infer a release from a tag alone.
A minimal final pipeline step, after the external decision has been authenticated and matched to the exact environment/image/proof by your integration, is:
# REVIEWED_IMAGE_REFERENCE comes from that exact authorized decision.
# The integration must first compare it to the admitted image_reference.
docker pull "$REVIEWED_IMAGE_REFERENCE"
# Pass the same digest reference to your deployment system.
These are integration boundaries, not a simulated approval or Trunx request.
Do not derive authorization from a client-controlled approved: true JSON field.
See admission for independent technical checks and races.
First publication and ambiguous Docker diagnostics
For the exact destination-tag preflight only, 0.4.4 handles Docker's exact
manifest unknown / manifest unknown: manifest unknown diagnostics by checking
https://<selected-registry>/v2/<selected-repository>/manifests/<selected-tag>.
The fallback uses the already snapshotted explicit Basic auth and CA, verified
TLS, no redirects/proxies, a bounded child process and a 64 KiB response limit.
Only HTTP 404 with one unambiguous OCI MANIFEST_UNKNOWN error confirms absence.
A message alone, HTML 404, auth/TLS failure, redirect, timeout, wrong error code or
multiple errors fails closed. Bearer challenges are not followed by this fallback;
Docker's previously supported exact reference-bound absence remains supported.
This is a bounded compatibility path, not a general registry authentication client.
See the OCI distribution specification.
Schema evolution and independent readers
Schema identifiers are versioned independently of package versions. Existing v1 JSON and canonical identities are unchanged; 0.4.4 emits no added fields into these strict contracts. Breaking meanings require a new schema version. An apparently additive field can break strict v1 readers and requires explicit compatibility review. Reject unknown versions/fields; retain original bytes for inspection, never silently discard fields or relabel a document as v1.
Canonical serialization is UTF-8 JSON,
lexicographically sorted keys, compact separators, explicit contract defaults/
nulls except model-defined omissions, no NaN and exactly one final LF. SHA-256
hashes those exact canonical bytes. Source digests hash original source bytes;
OCI digests hash original registry manifest/blob bytes. Do not reformat files
before checking artifact hashes. Optional proof_requirement omission retains
historical plan identities. The historical proof-binding serializer is unchanged.
JSON Schema validates structure; cross-document hashes and semantic validators
are additional required checks.
The existing catalog,
interface,
plan and
manifest schemas are supplemented
by generated schemas for the unchanged
build,
push,
proof verification,
proof publication,
admission and
observation results.
Regenerate the latter with scripts/export_delivery_schemas.py.
Deterministic A/B example and qualification boundaries
examples/governance/a/pricing.py and b/pricing.py define the same
python:pricing:total(unit_cents: int, quantity: int) -> int. A charges 100 cents
handling; B charges 150. For (100, 2) the executed results are 300 and 350.
The stable ID stays equal; source, catalog, exposure and bundle digests differ.
The contract remains compatible; its description records the changed fee.
Run from a clean installed environment, outside the checkout:
python -I /path/to/apizr/scripts/governance_evidence_proof.py \
--examples /path/to/apizr/examples/governance --output ./governance-proof
The script retains both REST/MCP JSON exports and comparison.json, executes only
this trusted example, removes generated source, then verifies the documents.
It explicitly records registry_executed:false and external_governance_executed:false.
Coordinated target qualification runs it using the retained core wheel.
The separate real disposable HTTPS registry qualification exports documents from actual REST/MCP builds, verifies them against their BuildResults after deleting source/bundles, and exercises push, proof publication, immutable retrieval, admission, tampering and resume with independently installed consumers. Its registry/TSA are test services, not Trunx. Mocked unit tests are a third, separate layer. See the 0.4.4 candidate record for executed evidence.
Trunx should ingest these existing records and hashes, retain exact immutable image/proof references, enforce its environment decisions separately and migrate its delivery tooling to the qualified four-package version together. Its custom Docker adapter can be retired only after the deployed registry's diagnostics and authentication are qualified with this generic path. Live Trunx ingestion, organizational approval and production consumption remain external qualification.