ADR-007: Published artifact surfaces and operator-owned deployment boundary¶
- Status: accepted
Context¶
The repository has a tagged release workflow capable of publishing the
xferry Python package to PyPI through Trusted Publishing/OIDC and the
ghcr.io/kgmnotes/xferry container image with provenance/SBOM metadata.
Neither public surface exists yet. Their first publication is intentionally
deferred until one coordinated release.
That boundary creates three risks:
- operators and contributors may mistake configured automation for an artifact that is already publicly available;
- a Python-first workflow can publish an immutable PyPI version before image verification finishes;
- readers may incorrectly infer that a published package or image implies a managed or internet-safe deployment story.
The project already separates launch posture and the always-on method surface from artifact distribution in ADR-011. This ADR makes the artifact boundary explicit.
Decision¶
The intended supported artifact surfaces are:
- local development installs from a checkout (
pip install -e .); - tagged PyPI releases of the
xferrypackage after coordinated publication; - signed wheel/sdist/SBOM artifacts uploaded by the
Release Artifactsworkflow; - tagged GHCR images at
ghcr.io/kgmnotes/xferryafter coordinated publication.
Manual workflow_dispatch runs of Release Artifacts are verification lanes:
they build and attest the same wheel/sdist/SBOM outputs, but they do not
publish PyPI or GHCR artifacts.
Registry publication follows this topology:
buildcreates, verifies, and attests the Python distributions;image-verifybuilds and exercises a hardened local container image;release-gatewaits for both verification jobs and fails closed;- only for a
pushevent on av*tag,publish-pypiandpublish-ghcrbecome eligible to run independently in parallel after the shared gate; manual dispatch stops at the gate even when its selected ref is a tag.
This is coordinated publication, not an atomic cross-registry transaction. PyPI and GHCR cannot commit or roll back together. A release is promoted only after both publication jobs and post-publication verification succeed. If one surface succeeds and the other fails, maintainers stop promotion and inspect the partial state. They retry only the missing job or files from the same release tag and verified inputs instead of blindly rerunning a successful publication. An already published immutable Python version is never changed or re-uploaded during reconciliation.
Published artifacts do not change the operator-owned deployment boundary.
They are supported ways to acquire the software, not a statement that exposed
internet deployment is safe by default. Launch-preset selection, TLS, authentication,
proxy controls, resource limits, and rollback remain operator responsibilities
as described in SECURITY.md and ADR-011.
Rollback expectations are:
- Python package rollbacks pin a previous verified version and, when needed, use preserved wheel/sdist artifacts outside the GitHub Actions retention window.
- Container rollbacks pin a previous verified GHCR digest, not a floating tag.
Consequences¶
Positive¶
- README, SECURITY, CONTRIBUTING, and docs-site install guidance can align to the real release workflow and current publication state.
- Both Python and container verification must pass before either registry is modified.
- Future API, proxy, and publishing decisions start from one explicit artifact boundary instead of repeating backlog prose.
- Operators can distinguish “supported distribution surface” from “supported internet deployment posture”.
Negative¶
- Maintainers now explicitly own the health of tagged PyPI and GHCR publishing as supported distribution surfaces.
- Cross-registry publication can still be partial and requires explicit reconciliation before release promotion.
- Docs must stay careful not to equate publication with hosted-service support or safe public exposure.
Follow-up¶
- Trusted-proxy and public-client decisions should reference this ADR instead of restating distribution support assumptions.
- If release topology changes again, update this ADR or supersede it with a new artifact-policy ADR rather than letting docs drift silently.