OXYGENHUB

cosign checks need --insecure-ignore-tlog until the hub's log exists d2c1d6a7d12e

Checked with cosign 2.4.1 on Earth (2026-10-17): oxy 0.4.3's arm64
release asset verifies against its provenance and the hub's key ('Verified
OK'); a copy with one byte changed does not. The envelopes are not in
Sigstore's Rekor, so cosign needs --insecure-ignore-tlog; the commands the
spec, the API docs and the release page give now say so.

Closes #239
Author
Andy McCall @andymccall <[email protected]>
via
agent Claude Code <[email protected]> · delegate for changes, until 2026-11-13 19:00Z · key ea5f78160088
When
· 2026-10-17 11:35 UTC
Signature
✔ signed by Claude Code
Hash
d2c1d6a7d12edeaca968546dc2fde25068f3de0afaee844ff11d5afd514e43eb
Depends on
3f4b0ea6eabf 4ac01a732552 681db69eba17 93be9ce4653e 97fc47fee9cf d23464c0ac60 df940d75cd3d
Depended on by
nothing yet
In bookmarks
api-unknown-fields cancel-landed-runs diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
In tags
v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
7 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified crates/hub-api/src/lib.rs +1 −1
…
918 unchanged lines
919 919
    pub signer: Option<String>,
920 920
    /// The asset's SLSA provenance, signed by the hub (oxygenhub#239):
921 921
    /// `cosign verify-blob-attestation --key …/.well-known/oxyhub-signing.pem
922 -
    /// --type slsaprovenance1 --signature PROVENANCE ASSET`. `None` when the
922 +
    /// --type slsaprovenance1 --insecure-ignore-tlog=true --signature PROVENANCE ASSET`. `None` when the
923 923
    /// receipt was not signed.
924 924
    #[serde(default)]
925 925
    pub provenance_url: Option<String>,
…
155 unchanged lines
modified docs/spec.md +2 −2
…
424 unchanged lines
425 425
  A hand-uploaded asset, an unsigned tag or an unsigned change is a warning. A signature or receipt that does not verify, or a revoked key, is a failure.
426 426
- **Export:** `?format=json` downloads the report, and `GET /api/v1/repos/{owner}/{name}/releases/trust/{tag}` returns the same. The JSON carries each signed receipt as hex, so an auditor can check it offline with `oxy receipt verify`.
427 427
428 -
**Built by.** A release asset links to the build that produced it: the passed OxygenFlow job of this repository with an artifact of the same BLAKE3 (hub spec §13.2). A job with a kept signed receipt comes first, then the newest run. The release page shows the run, the job and the signed receipt with its signer, and the API gives the same as `built_by` on each asset. With a signed receipt it also links the asset's SLSA provenance (`provenance_url`, §13.2's provenance of that artifact, which states the same bytes), so a release asset is checked with `cosign verify-blob-attestation --key oxyhub-signing.pem --type slsaprovenance1 --signature PROVENANCE ASSET` (oxygenhub#239). On a run page, a writer can attach an artifact to a release; it becomes an asset named after the artifact's path, so the link follows by content. A file uploaded by hand links the same way when its bytes match.
428 +
**Built by.** A release asset links to the build that produced it: the passed OxygenFlow job of this repository with an artifact of the same BLAKE3 (hub spec §13.2). A job with a kept signed receipt comes first, then the newest run. The release page shows the run, the job and the signed receipt with its signer, and the API gives the same as `built_by` on each asset. With a signed receipt it also links the asset's SLSA provenance (`provenance_url`, §13.2's provenance of that artifact, which states the same bytes), so a release asset is checked with `cosign verify-blob-attestation --key oxyhub-signing.pem --type slsaprovenance1 --insecure-ignore-tlog=true --signature PROVENANCE ASSET` (oxygenhub#239). On a run page, a writer can attach an artifact to a release; it becomes an asset named after the artifact's path, so the link follows by content. A file uploaded by hand links the same way when its bytes match.
429 429
430 430
## 10. Notifications
431 431
…
66 unchanged lines
498 498
  Otherwise the job's result still stands, and the run page says why the receipt was not kept. The run page links each kept receipt, with its signer. The change page lists the receipts for the state whose only head is that change. `/{owner}/{name}/receipts/{attest}` downloads a signed receipt for `oxy receipt verify` or `import`.
499 499
- **Variables** (oxygenhub#102): Administration, an organisation's settings and a repository's settings each hold `NAME=value` pairs (a name is a letter or `_` then letters, digits and `_`, up to 64, never `FLOW_*`; a value up to 4096 characters; up to 100 per level) under **Variables**, each page showing what it inherits and which inherited names it overrides. A claim carries the repository's effective set as `env`, inner levels overriding outer; Flow puts them in the job's environment under `FLOW_*` and the pipeline's own `env`, so they are part of the receipt. They are not secrets and are shown in full to whoever sees the page; secrets are mounted by Flow (OxygenFlow spec §9), never put in the environment. Changes are audited (`flow.variable.set`, `flow.variable.deleted`); `flow run --var NAME=VALUE` gives a local run the same.
500 500
- **Secrets** (oxygenhub#107; OxygenFlow spec §9): the same three levels hold **Secrets**, named as a pipeline's `secret "name" { mount-path "/run/secrets/…" }` names them (1 to 64 of `A-Z a-z 0-9 . _ -`, a value up to 64 KiB, up to 100 per level), typed once and sealed under `keys/secrets.key`; pages show names, when each was set and where it comes from, never a value. A claim carries only the secrets its job declares, each with its path and value, from the innermost level that sets the name; one no level sets is noted at the top of the job's log and left out, so the job fails on the missing file rather than running with a stand-in. Flow writes each as a file (0400, the job's user's, on an in-memory mount) before the job starts and never puts it in the environment, the receipt or an artifact. Log intake replaces each value, and its base64, with `***` before a record is stored. Changes are audited (`flow.secret.set`, `flow.secret.deleted`), never with the value.
501 -
- **Provenance others verify** (oxygenhub#239). For an artifact of a job whose signed receipt the hub kept, `…/flow/{run}/artifacts/{job}/{artifact}/provenance` answers an in-toto Statement v1 with an SLSA provenance v1 predicate, in a DSSE envelope signed with the hub's Ed25519 key. Its `buildType` is `https://oxygenhub.io/oxygenflow/job/v1` on every hub, as `oxy receipt export --format slsa` writes it (Oxygen spec §14.6). The subject is the artifact by SHA-256 (and BLAKE3); the build definition names the repository, bookmark, job and pipeline hash, and depends on the state built; the run details name the runner as builder, the run as invocation with its start and finish, and the kept receipt's attestation as a byproduct. The runner signed the receipt; the hub, which checked it, states what it says in the shape cosign, policy-controller, Kyverno and slsa-verifier read. `/.well-known/oxyhub-signing.pem` is the key: `cosign verify-blob-attestation --key oxyhub-signing.pem --type slsaprovenance1 --signature tool.intoto.json tool`.
501 +
- **Provenance others verify** (oxygenhub#239). For an artifact of a job whose signed receipt the hub kept, `…/flow/{run}/artifacts/{job}/{artifact}/provenance` answers an in-toto Statement v1 with an SLSA provenance v1 predicate, in a DSSE envelope signed with the hub's Ed25519 key. Its `buildType` is `https://oxygenhub.io/oxygenflow/job/v1` on every hub, as `oxy receipt export --format slsa` writes it (Oxygen spec §14.6). The subject is the artifact by SHA-256 (and BLAKE3); the build definition names the repository, bookmark, job and pipeline hash, and depends on the state built; the run details name the runner as builder, the run as invocation with its start and finish, and the kept receipt's attestation as a byproduct. The runner signed the receipt; the hub, which checked it, states what it says in the shape cosign, policy-controller, Kyverno and slsa-verifier read. `/.well-known/oxyhub-signing.pem` is the key: `cosign verify-blob-attestation --key oxyhub-signing.pem --type slsaprovenance1 --insecure-ignore-tlog=true --signature tool.intoto.json tool`. (`--insecure-ignore-tlog`: the envelopes are not in Sigstore's Rekor; the hub's own transparency log, docs/transparency.md, takes that role. Checked with cosign 2.4.1 on 2026-10-17: a release asset verifies, a copy with one byte changed does not.)
502 502
- **Artifacts** (OxygenFlow spec §5.2): a job may declare artifacts with `output`.
503 503
  - The runner holding the job uploads each one with `POST /api/v1/flow/jobs/{id}/artifacts/{name}` (raw bytes, at most 512 MiB).
504 504
  - The hub refuses names the job does not declare. It keeps each artifact as a blob of the run's repository, records its path, size and BLAKE3 in `flow_artifacts`, and answers with the blob's ObjectRef.
…
205 unchanged lines
modified templates/releases.html +1 −1
…
17 unchanged lines
18 18
  {% if !v.r.notes.is_empty() %}<article class="doc">{{ v.notes_html|safe }}</article>{% endif %}
19 19
  <h3>Assets</h3>
20 20
  <ul class="plain">
21 -
    {% for a in v.assets %}<li><a href="{{ repo.url }}/asset/{{ a.id }}/{{ a.name }}">{{ a.name }}</a> <span class="small mute">{{ a.size|bytes }} · blake3 <code title="{{ a.hash }}">{{ a.hash|short }}</code> · {{ a.downloads }} downloads</span>{% if let Some(p) = v.built_by(a.id) %} <span class="small" data-built>· built by <a href="{{ repo.url }}/flow/{{ p.run_number }}">run #{{ p.run_number }}</a> {{ p.job }}{% if p.receipt_attest.is_empty() %} <span class="mute">(unsigned receipt)</span>{% else %} · <a href="{{ repo.url }}/receipts/{{ p.receipt_attest }}" title="Check it with oxy receipt verify">signed receipt</a> by {{ p.receipt_signer }} · <a href="{{ repo.url }}/flow/{{ p.run_number }}/artifacts/{{ p.job|urlencode }}/{{ p.name|urlencode }}/provenance" title="SLSA provenance signed by this hub: cosign verify-blob-attestation --key /.well-known/oxyhub-signing.pem --type slsaprovenance1">provenance</a>{% endif %}</span>{% endif %}
21 +
    {% for a in v.assets %}<li><a href="{{ repo.url }}/asset/{{ a.id }}/{{ a.name }}">{{ a.name }}</a> <span class="small mute">{{ a.size|bytes }} · blake3 <code title="{{ a.hash }}">{{ a.hash|short }}</code> · {{ a.downloads }} downloads</span>{% if let Some(p) = v.built_by(a.id) %} <span class="small" data-built>· built by <a href="{{ repo.url }}/flow/{{ p.run_number }}">run #{{ p.run_number }}</a> {{ p.job }}{% if p.receipt_attest.is_empty() %} <span class="mute">(unsigned receipt)</span>{% else %} · <a href="{{ repo.url }}/receipts/{{ p.receipt_attest }}" title="Check it with oxy receipt verify">signed receipt</a> by {{ p.receipt_signer }} · <a href="{{ repo.url }}/flow/{{ p.run_number }}/artifacts/{{ p.job|urlencode }}/{{ p.name|urlencode }}/provenance" title="SLSA provenance signed by this hub: cosign verify-blob-attestation --key /.well-known/oxyhub-signing.pem --type slsaprovenance1 --insecure-ignore-tlog=true">provenance</a>{% endif %}</span>{% endif %}
22 22
      {% if one && repo.can(crate::auth::Access::Write) %}<form class="tiny" method="post" action="{{ repo.url }}/releases/edit/{{ v.r.id }}"><input type="hidden" name="_csrf" value="{{ shell.csrf }}"><input type="hidden" name="asset" value="{{ a.id }}"><button name="action" value="delete_asset">delete</button></form>{% endif %}</li>{% endfor %}
23 23
    <li><a href="{{ repo.url }}/archive?ref={{ v.r.tag }}&format=tar.zst">Source ({{ v.r.tag }}.tar.zst)</a></li>
24 24
    <li><a href="{{ repo.url }}/archive?ref={{ v.r.tag }}&format=zip">Source ({{ v.r.tag }}.zip)</a></li>
…
18 unchanged lines