Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ jobs:
permissions:
contents: write
id-token: write # keyless cosign over the bundle SHA256SUMS (v1.77)
packages: write # OCI push of the flight component to ghcr.io (v1.128)
steps:
# relay's MODULE.bazel local_path_override's the four pulseengine rules to
# ../<repo>, so bazel needs relay in a subdir + the rules as siblings
Expand Down Expand Up @@ -515,3 +516,58 @@ jobs:
echo "::error::Release $TAG has NO falcon-flight-*.wasm asset — the #100/#140 regression. Failing."
exit 1
fi


# ── Publish the flight component to ghcr.io as an OCI artifact (v1.128) ──
# An ADDITIVE distribution channel: the same verified `falcon:flight`
# component, pushed as an OCI 1.1 artifact with the Component-Model media
# type via `wkg`, so it is `wkg oci pull`-able and indexable by
# wasm.directory (a meta-registry over OCI registries). Cosign-signed by
# immutable DIGEST, like the release bundle. NON-BLOCKING
# (continue-on-error): the primary artifact is the cosign-signed GitHub
# Release above; a registry hiccup must never fail a release. Tighten to
# hard-fail once proven over a few tags. Ref: ghcr.io/<owner>/falcon-flight.
- name: Publish flight component to ghcr.io (OCI, cosign-signed)
continue-on-error: true
working-directory: relay
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Bind the tag via env — never expand ${{ ... }} inside run: (the
# command-injection convention this file follows throughout).
INPUT_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
TAG="${INPUT_TAG:-${GITHUB_REF_NAME}}"
# Validate the shape before it reaches any shell command: a release
# tag is falcon-vX.Y.Z (+ optional prerelease). Reject anything else.
if ! printf '%s' "$TAG" | grep -qE '^falcon-v[0-9]+\.[0-9]+\.[0-9]+([-.][A-Za-z0-9.]+)?$'; then
echo "::error::refusing to publish OCI for a malformed tag: $TAG"; exit 1
fi
MM=$(printf '%s' "$TAG" | sed -E 's/^falcon-v([0-9]+\.[0-9]+).*/\1/')
FULL=$(printf '%s' "$TAG" | sed -E 's/^falcon-v//')
WASM="falcon-flight-v${MM}.wasm"
REPO="ghcr.io/${GITHUB_REPOSITORY_OWNER}/falcon-flight"

# Pull the exact wasm the release just attached (single source of truth).
gh release download "$TAG" -p "$WASM" --clobber

# wkg = the wasm-pkg-tools CLI. PINNED (supply-chain), bumped in
# lockstep. Pushes the wasm-component OCI media type wasm.directory
# expects — a plain `oras push` would tag it as a generic blob.
cargo install wkg --locked --version 0.15.1

echo "$GHCR_TOKEN" | docker login ghcr.io -u "${GITHUB_ACTOR}" --password-stdin
wkg oci push "${REPO}:${FULL}" "$WASM" | tee push.log
# :latest is a moving convenience pointer — pushed for discovery,
# but NEVER signed (only immutable refs get a signature).
wkg oci push "${REPO}:latest" "$WASM"

# Sign the IMMUTABLE digest, not a mutable tag. Only if we resolved one.
DIGEST=$(grep -oE 'sha256:[a-f0-9]{64}' push.log | head -1 || true)
if [ -n "$DIGEST" ]; then
cosign sign --yes "${REPO}@${DIGEST}"
echo "::notice::Published + signed ${REPO}@${DIGEST} (tags ${FULL}, latest). Pull: wkg oci pull ${REPO}:${FULL}"
else
echo "::warning::pushed ${REPO}:${FULL} but could not resolve a digest to sign — left unsigned this run"
fi
57 changes: 57 additions & 0 deletions docs/OCI-DISTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# OCI distribution + wasm.directory

The verified flight component (`falcon:flight`, the `falcon-flight-vX.wasm`
artifact) is published to **ghcr.io as an OCI 1.1 artifact** on every tagged
release, in addition to the cosign-signed GitHub Release. This makes it
`wkg oci pull`-able and indexable by [wasm.directory](https://wasm.directory) —
a meta-registry that indexes OCI registries rather than hosting packages itself.

## What the release does automatically

`.github/workflows/release.yml` (flight-component job) pushes the exact wasm the
release attached:

```
ghcr.io/pulseengine/falcon-flight:<full-version> # e.g. 1.127.0
ghcr.io/pulseengine/falcon-flight:latest
```

pushed with `wkg` (wasm-pkg-tools) so it carries the Component-Model OCI media
type wasm.directory expects — not a generic blob — and cosign-signed keyless,
like the release bundle. The step is **non-blocking** (`continue-on-error`): a
registry hiccup must never fail the primary signed GitHub Release. It will be
tightened to a hard failure once it has proven itself across a few tags.

Pull + verify:

```sh
wkg oci pull ghcr.io/pulseengine/falcon-flight:1.127.0 -o falcon-flight.wasm
cosign verify ghcr.io/pulseengine/falcon-flight:1.127.0 \
--certificate-identity-regexp \
'https://github.com/pulseengine/relay/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com'
```

## One-time manual steps (org owner)

1. **Make the ghcr package public.** ghcr packages are created *private* by
default; wasm.directory can only index a public package. After the first
tagged release populates it, in the org's package settings set
`pulseengine/falcon-flight` visibility to **public** and link it to the
`relay` repo. (One-time; subsequent pushes inherit the setting.)
2. **Register the namespace with wasm.directory.** Per its publishing guide,
add the `pulseengine` namespace → `ghcr.io` mapping to the wasm.directory
registry config (or your account's namespace list), then it indexes the
package. wasm.directory is **alpha** ("indexed data may be incomplete or
reset without notice; don't depend on it in production") — treat the listing
as a visibility channel, not a dependency. The durable, signed artifacts
remain the GitHub Release and the ghcr OCI ref.

## Scope note

`falcon:flight`'s current world (`flight-demo`) exports two runnable smoke-test
functions (`run-stabilization`, `run-position-hold`) — it is a *runnable*
verified component, not yet a reusable library exposing a typed control
interface. Publishing it is a provenance/visibility play (a formally-verified
flight component, signed, in the public component ecosystem). A richer typed
`falcon:flight` interface others could import is a separate follow-on.
Loading