Document artifact origin and installer receipts
This commit is contained in:
@@ -389,7 +389,7 @@ that observation to the assessment and signed catalog:
|
|||||||
--output /var/tmp/govoplan-fit-review.json
|
--output /var/tmp/govoplan-fit-review.json
|
||||||
```
|
```
|
||||||
|
|
||||||
The collector follows the strict version `0.3.0`
|
The collector follows the strict version `0.4.0`
|
||||||
[`installed-composition-evidence.schema.json`](installed-composition-evidence.schema.json)
|
[`installed-composition-evidence.schema.json`](installed-composition-evidence.schema.json)
|
||||||
contract. It enumerates all installed distributions whose normalized name starts
|
contract. It enumerates all installed distributions whose normalized name starts
|
||||||
with `govoplan-`, compares the enabled assessed package and module-manifest
|
with `govoplan-`, compares the enabled assessed package and module-manifest
|
||||||
@@ -419,6 +419,9 @@ For each distribution, the collector also:
|
|||||||
package-index/unknown origins and malformed metadata;
|
package-index/unknown origins and malformed metadata;
|
||||||
- compares a non-editable VCS commit with the assessment commit and, when one is
|
- compares a non-editable VCS commit with the assessment commit and, when one is
|
||||||
present, the signed catalog `selected_units` commit;
|
present, the signed catalog `selected_units` commit;
|
||||||
|
- derives two content identities from bytes it actually verifies: an
|
||||||
|
install-stable identity for immutable wheel-declared files and a full
|
||||||
|
installed-RECORD identity, while retaining only SHA-256 digests and counts;
|
||||||
- records only package/module identifiers, versions, bounded counters, hashes
|
- records only package/module identifiers, versions, bounded counters, hashes
|
||||||
and stable error codes. It never writes direct URLs, paths, hostnames,
|
and stable error codes. It never writes direct URLs, paths, hostnames,
|
||||||
usernames, exception text or file contents.
|
usernames, exception text or file contents.
|
||||||
@@ -436,25 +439,31 @@ remain mutable even when their small editable-install `RECORD` is valid. Archive
|
|||||||
or index artifacts without a hash anchored by the assessed release remain
|
or index artifacts without a hash anchored by the assessed release remain
|
||||||
unanchored. A matching declaration is reported under
|
unanchored. A matching declaration is reported under
|
||||||
`installed_source_provenance` as local consistency only, with
|
`installed_source_provenance` as local consistency only, with
|
||||||
`release_origin_bound: false`. The separate `installed_release_origin` scope
|
`release_origin_bound: false` until the separate origin proof succeeds. A signed
|
||||||
remains unchecked until an independently anchored artifact digest or signed
|
catalog can contain `release.artifacts` identities computed directly from built,
|
||||||
installer receipt binds each installed payload to the signed catalog. The tool
|
bounded wheel files. Wheels with installer-transformed scripts or headers are
|
||||||
does not accept a digest merely asserted inside the installed evidence document.
|
marked `requires_installer_receipt`; catalog metadata alone cannot attest those
|
||||||
|
post-install bytes. A role-scoped installer receipt instead binds the exact
|
||||||
|
installed-evidence digest, full installed payload identities, catalog archive
|
||||||
|
digests and canonical signed-catalog digest. The tool never accepts an installed
|
||||||
|
hash merely because the installed evidence asserted it.
|
||||||
|
|
||||||
Only evidence produced in-process by `--collect-installed-evidence` is a local
|
Only evidence produced in-process by `--collect-installed-evidence` is a local
|
||||||
observation, and it must be no more than five minutes old (with at most 30
|
observation, and it must be no more than five minutes old (with at most 30
|
||||||
seconds of future clock skew) at the selected verification time. Use
|
seconds of future clock skew) at the selected verification time. Use
|
||||||
`--installed-evidence PATH` to compare evidence collected in a separate
|
`--installed-evidence PATH` to compare evidence collected in a separate
|
||||||
environment. Imported JSON is unsigned and therefore always carries a blocker:
|
environment. Imported JSON without a valid independent installer receipt
|
||||||
it can identify differences but cannot establish checked or valid installed
|
carries a blocker: it can identify differences but cannot establish checked or
|
||||||
proof. A future signed collector-attestation format is required to cross that
|
valid installed proof. A receipt signed by a separately provisioned installer
|
||||||
|
authority authenticates that exact imported evidence across the process
|
||||||
boundary. The proof scope records observation mode, collection/evaluation time,
|
boundary. The proof scope records observation mode, collection/evaluation time,
|
||||||
freshness, and whether evaluation used current time or an explicit historical
|
receipt authentication, freshness, and whether evaluation used current time or
|
||||||
override. Evidence input and output are bounded to 4 MiB. Collection loads
|
an explicit historical override. Evidence input and output are bounded to 4
|
||||||
the `govoplan.modules` entry-point factories in order to read module IDs and
|
MiB. Collection loads the `govoplan.modules` entry-point factories in order to
|
||||||
manifest versions; that executes installed GovOPlaN manifest code. Run it only
|
read module IDs and manifest versions; that executes installed GovOPlaN manifest
|
||||||
inside the installation being assessed and with the same isolation expected for
|
code. Run it only inside the installation being assessed and with the same
|
||||||
other installed-artifact acceptance checks. This collector does not observe
|
isolation expected for other installed-artifact acceptance checks. This
|
||||||
|
collector does not observe
|
||||||
which modules a running service activated, migrations, configuration, health,
|
which modules a running service activated, migrations, configuration, health,
|
||||||
or reference-journey behavior. Runtime activation therefore remains an explicit
|
or reference-journey behavior. Runtime activation therefore remains an explicit
|
||||||
unchecked boundary.
|
unchecked boundary.
|
||||||
@@ -495,14 +504,50 @@ the tool's deterministic canonicalization.
|
|||||||
```
|
```
|
||||||
|
|
||||||
With `--installed-evidence`, this command performs comparison and proof-binding
|
With `--installed-evidence`, this command performs comparison and proof-binding
|
||||||
diagnostics only: the imported installed document is unsigned, so neither it nor
|
diagnostics. Without an installer receipt, the imported document remains
|
||||||
the boundary claim becomes accepted proof. Direct in-process collection removes
|
unsigned, so neither it nor the boundary claim becomes accepted proof. Direct
|
||||||
that unsigned-import gap, but still establishes only local metadata consistency.
|
in-process collection can match catalog-anchored artifact identities for wheels
|
||||||
No boundary claim is admitted by the current format because it has no separately
|
without installer-transformed files. The installer-receipt flow below
|
||||||
anchored artifact digest or signed installer receipt. A future evidence contract
|
authenticates a durable cross-process observation and is required for transformed
|
||||||
must bind such an independently trusted release-artifact receipt to the installed
|
script/header payloads.
|
||||||
evidence digest; a signed collector-attestation format should also make that
|
|
||||||
exchange durable across processes.
|
Issue a receipt only in the installation process performing the live collection.
|
||||||
|
The command refuses evidence supplied from a file and first validates the
|
||||||
|
assessment, independently trusted signed catalog, composition and RECORD bytes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./.venv/bin/python tools/assessments/installer-receipt.py \
|
||||||
|
--assessment /srv/govoplan/assessment.json \
|
||||||
|
--catalog /srv/govoplan/catalogs/stable.json \
|
||||||
|
--keyring /srv/govoplan/catalogs/keyring.json \
|
||||||
|
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||||
|
--receipt-id install:production:20260722 \
|
||||||
|
--signing-key installer-production=/run/secrets/installer-ed25519.pem \
|
||||||
|
--python-artifact govoplan-core=/srv/govoplan/staged/govoplan_core-0.1.13-py3-none-any.whl \
|
||||||
|
--python-artifact govoplan-campaign=/srv/govoplan/staged/govoplan_campaign-0.1.10-py3-none-any.whl \
|
||||||
|
--evidence-output /srv/govoplan/evidence/installed.json \
|
||||||
|
--receipt-output /srv/govoplan/evidence/installer-receipt.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Repeat `--python-artifact PACKAGE=/exact/consumed.whl` for every enabled
|
||||||
|
GovOPlaN distribution. The issuer reopens each exact wheel, verifies its archive
|
||||||
|
and payload identities against the signed catalog, and refuses a different
|
||||||
|
build with the same name and version. Receipt issuance must follow collection
|
||||||
|
within five minutes, and live verification must still fall inside that bounded
|
||||||
|
window; retain the pair for a reproducible historical review at its explicit
|
||||||
|
verification time.
|
||||||
|
|
||||||
|
Verify that durable pair with its separately managed trust root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./.venv/bin/python tools/assessments/capability-fit.py \
|
||||||
|
--catalog /srv/govoplan/catalogs/stable.json \
|
||||||
|
--keyring /srv/govoplan/catalogs/keyring.json \
|
||||||
|
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||||
|
--installed-evidence /srv/govoplan/evidence/installed.json \
|
||||||
|
--installer-receipt /srv/govoplan/evidence/installer-receipt.json \
|
||||||
|
--installer-authority-keyring /srv/govoplan/trust/installer-authorities.json
|
||||||
|
```
|
||||||
|
|
||||||
Target-environment and production-approval claims must use the assessment's
|
Target-environment and production-approval claims must use the assessment's
|
||||||
`deployment_profile.id` as `subject_id`. External-provider claims require the
|
`deployment_profile.id` as `subject_id`. External-provider claims require the
|
||||||
@@ -510,11 +555,16 @@ operator to supply a bounded opaque expected subject with
|
|||||||
`--expected-external-provider-subject`; without it, such a claim remains
|
`--expected-external-provider-subject`; without it, such a claim remains
|
||||||
unchecked and blocks. Expected and observed IDs are retained in proof scope.
|
unchecked and blocks. Expected and observed IDs are retained in proof scope.
|
||||||
|
|
||||||
The authority keyring is a governance trust root. Do not download or generate it
|
Both authority keyrings are governance trust roots. Installer receipt keys use
|
||||||
from the proof bundle being checked. The checker rejects proof-authority public
|
the strict
|
||||||
keys reused by either the published or independently trusted catalog keyring,
|
[`installer-receipt-authority-keyring.schema.json`](installer-receipt-authority-keyring.schema.json)
|
||||||
and rejects the same proof public-key material assigned to multiple authority
|
contract and may attest only `installed_release_origin`; their public material
|
||||||
IDs. A malformed key, an invalid or empty validity interval, or ambiguous key
|
must not be reused by catalog or boundary-proof authorities. Do not download or
|
||||||
|
generate them from the proof bundle being checked. The checker rejects
|
||||||
|
proof-authority public keys reused by either the published or independently
|
||||||
|
trusted catalog keyring, and rejects the same proof public-key material assigned
|
||||||
|
to multiple authority IDs. A malformed key, an invalid or empty validity
|
||||||
|
interval, or ambiguous key
|
||||||
identity invalidates the supplied authority set. Authority `not_after` is
|
identity invalidates the supplied authority set. Authority `not_after` is
|
||||||
exclusive. A target operator or approver must validate the referenced
|
exclusive. A target operator or approver must validate the referenced
|
||||||
drill/result artifacts before signing. The rerun verifies the
|
drill/result artifacts before signing. The rerun verifies the
|
||||||
|
|||||||
@@ -212,7 +212,9 @@ an alternative condition without `required_scopes` or `any_scopes`.
|
|||||||
|
|
||||||
The catalog workflow panel can also operate on the same selected rows:
|
The catalog workflow panel can also operate on the same selected rows:
|
||||||
|
|
||||||
- `Generate` creates a signed candidate below `runtime/release-candidates/`.
|
- `Generate` creates a signed candidate in the operator's private XDG state
|
||||||
|
directory and records only an opaque candidate ID plus the canonical catalog
|
||||||
|
SHA-256 in durable run state.
|
||||||
- `Preview` validates a candidate and shows what would be copied into the
|
- `Preview` validates a candidate and shows what would be copied into the
|
||||||
website repository.
|
website repository.
|
||||||
- `Apply + Website Tag` requires `APPLY` in the confirmation field.
|
- `Apply + Website Tag` requires `APPLY` in the confirmation field.
|
||||||
@@ -241,6 +243,30 @@ cannot be applied or published while any referenced source tag is absent or
|
|||||||
inconsistent; the preview and API response list each repository and missing or
|
inconsistent; the preview and API response list each repository and missing or
|
||||||
invalid ref so the operator can repair the exact source releases first.
|
invalid ref so the operator can repair the exact source releases first.
|
||||||
|
|
||||||
|
Every selected Python release must also supply its exact built wheel. Candidate
|
||||||
|
generation computes the archive SHA-256 and an install-stable payload identity
|
||||||
|
from one bounded, regular-file descriptor and signs those values into
|
||||||
|
`release.artifacts`. Updating a Python version without a matching wheel removes
|
||||||
|
the stale identity. A source-only preview can still explain the gap, but apply,
|
||||||
|
commit, tag, and push fail closed until every selected Python unit has a matching
|
||||||
|
built-artifact identity. Repositories selected only for a meta/source tag do not
|
||||||
|
need a Python artifact.
|
||||||
|
|
||||||
|
Candidate directories and files are operator-owned `0700`/`0600` state. Before
|
||||||
|
any later release step consumes one, the executor re-resolves the opaque handle
|
||||||
|
below the configured root and re-hashes the signed catalog against its persisted
|
||||||
|
receipt. Shared roots, symlinks, channel path fragments, altered candidates, and
|
||||||
|
unowned files are rejected.
|
||||||
|
|
||||||
|
The server-owned wheel builds remain under the private candidate's `artifacts/`
|
||||||
|
directory. This slice signs their identities but does **not** upload the wheel or
|
||||||
|
add a download URL to the public catalog; the existing Git `python_ref` is source
|
||||||
|
provenance and rebuilding it is not an equivalent artifact. Keep the private
|
||||||
|
candidate until the exact wheels have been transferred through an approved
|
||||||
|
deployment channel, verified against `archive_sha256`, consumed by the installer,
|
||||||
|
and covered by its signed receipt. Public artifact transport and receipt-aware
|
||||||
|
candidate cleanup remain separate release-lifecycle work.
|
||||||
|
|
||||||
Read-only provenance checks derive a same-host HTTPS Git URL from conventional
|
Read-only provenance checks derive a same-host HTTPS Git URL from conventional
|
||||||
SSH remotes when possible, with a non-interactive check of the configured remote
|
SSH remotes when possible, with a non-interactive check of the configured remote
|
||||||
as fallback. Source tag creation and atomic publication always use the explicit
|
as fallback. Source tag creation and atomic publication always use the explicit
|
||||||
@@ -269,18 +295,28 @@ Build a signed selective catalog candidate:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||||
|
ARTIFACT_DIR="$(mktemp -d)"
|
||||||
|
|
||||||
|
../govoplan-files/.venv/bin/python -m pip wheel \
|
||||||
|
--no-deps \
|
||||||
|
--no-build-isolation \
|
||||||
|
--wheel-dir "$ARTIFACT_DIR" \
|
||||||
|
../govoplan-files
|
||||||
|
|
||||||
./.venv/bin/python tools/release/release-catalog.py selective \
|
./.venv/bin/python tools/release/release-catalog.py selective \
|
||||||
--repo-version govoplan-files=0.1.9 \
|
--repo-version govoplan-files=0.1.9 \
|
||||||
|
--python-artifact \
|
||||||
|
"govoplan-files=$ARTIFACT_DIR/govoplan_files-0.1.9-py3-none-any.whl" \
|
||||||
--channel stable \
|
--channel stable \
|
||||||
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem"
|
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem"
|
||||||
```
|
```
|
||||||
|
|
||||||
This writes candidate catalog/keyring files below `runtime/release-candidates/`,
|
This writes candidate catalog/keyring files below
|
||||||
generates the browsable module directory below `modules/`, validates the signed
|
`$XDG_STATE_HOME/govoplan/release-candidates/` (or
|
||||||
candidate with the module installer validator, and reports whether the candidate
|
`~/.local/state/govoplan/release-candidates/`), generates the browsable module
|
||||||
catalog/keyring match the currently published channel. It does not publish to
|
directory below `modules/`, validates the signed candidate with the module
|
||||||
the website repository.
|
installer validator, and reports whether the candidate catalog/keyring match the
|
||||||
|
currently published channel. It does not publish to the website repository.
|
||||||
|
|
||||||
Regenerate the browsable module directory from an existing catalog/keyring:
|
Regenerate the browsable module directory from an existing catalog/keyring:
|
||||||
|
|
||||||
@@ -295,8 +331,10 @@ Regenerate the browsable module directory from an existing catalog/keyring:
|
|||||||
Preview publication of a reviewed candidate:
|
Preview publication of a reviewed candidate:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
CANDIDATE_ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/govoplan/release-candidates"
|
||||||
|
|
||||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||||
--candidate-dir runtime/release-candidates/stable-YYYYMMDD-HHMMSS \
|
--candidate-dir "$CANDIDATE_ROOT/stable-YYYYMMDD-HHMMSS" \
|
||||||
--channel stable
|
--channel stable
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -304,7 +342,7 @@ Apply the reviewed candidate into the website repository without pushing:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||||
--candidate-dir runtime/release-candidates/stable-YYYYMMDD-HHMMSS \
|
--candidate-dir "$CANDIDATE_ROOT/stable-YYYYMMDD-HHMMSS" \
|
||||||
--channel stable \
|
--channel stable \
|
||||||
--apply \
|
--apply \
|
||||||
--commit \
|
--commit \
|
||||||
@@ -315,7 +353,7 @@ Push is a separate explicit flag:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||||
--candidate-dir runtime/release-candidates/stable-YYYYMMDD-HHMMSS \
|
--candidate-dir "$CANDIDATE_ROOT/stable-YYYYMMDD-HHMMSS" \
|
||||||
--channel stable \
|
--channel stable \
|
||||||
--apply \
|
--apply \
|
||||||
--commit \
|
--commit \
|
||||||
@@ -323,6 +361,13 @@ Push is a separate explicit flag:
|
|||||||
--push
|
--push
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Publication validates the same in-memory catalog object that it writes; it does
|
||||||
|
not copy a path that can change after validation. On commit, the console reads
|
||||||
|
the catalog, keyring, and complete module directory back from the immutable Git
|
||||||
|
tree and requires byte-for-byte equality with those validated objects. Tags and
|
||||||
|
remote branch updates then reference that exact commit SHA rather than the
|
||||||
|
mutable worktree `HEAD`.
|
||||||
|
|
||||||
Published channels are expected below the public catalog base URL:
|
Published channels are expected below the public catalog base URL:
|
||||||
|
|
||||||
- `https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json`
|
- `https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json`
|
||||||
|
|||||||
Reference in New Issue
Block a user