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
18 changes: 11 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,14 @@ crates/function/ the runtime crate: a library (everything below) + th
referrers in tests
src/location.rs go-containerregistry-compatible reference normalization: pinned refs
(the runtime), any refs with tag/digest (guestfn), http locations
src/cosign.rs cosign 3 Sigstore bundle verification, key-based (bundles found among
the manifest's referrers over the runtime's own registry client, DSSE
+ in-toto parsed here, sigstore-rs crypto); legacy .sig not read,
keyless not yet (#116); testutil signs cosign-3-shaped bundles
src/cosign.rs cosign 3 Sigstore bundle verification (bundles found among the
manifest's referrers over the runtime's own registry client): key-based
with sigstore-crypto keys (DSSE + in-toto parsed here, the keys the
trust root), keyless through sigstore-verify against
--cosign-trusted-root (signing time from RFC 3161 + Rekor SET,
inclusion proof, chain), the signer then held to the operator policy's
trustSigner rules via the SignerPolicy trait (#116); legacy .sig not
read; testutil signs cosign-3-shaped bundles
src/cache.rs the module cache: memory (idle TTL, LRU bound) over the compiled-
artifact store, single-flight loads with compile slots; cache events
src/store.rs the content-addressed disk stores (modules, compiled/<version>,
Expand Down Expand Up @@ -484,7 +488,7 @@ Readiness is answered twice — gRPC health on the function port and plain-HTTP

### Signatures

`--cosign-key` loads PEM public keys into `cosign::Verifier` (sigstore-rs crypto, key-based only); verification runs **before** the caches, once per manifest digest per process, over the runtime's own registry client (same auth path as the pull); non-OCI sources are refused when a signature is required. The format is cosign 3's: a Sigstore bundle (v0.3) in an OCI 1.1 referrer of the module's manifest, discovered through the referrers API or, on a 404 (GHCR, `registry:3`), the `sha256-<hex>` referrers tag index; bundles are recognised by the layer media type, never by `artifactType` (cosign 3.0 sets none). A referrer counts only if its `subject` is the pinned digest; the bundle is fetched bounded and verified against its digest; a DSSE envelope signature over the PAE must verify with a configured key, and only then is the in-toto statement read: `Statement/v1`, `predicateType` exactly `https://sigstore.dev/cosign/sign/v1` (SLSA provenance and other attestations never count, whoever signed them) and a subject with the manifest digest. The keys are the trust root - the bundle's verification material (key hint, certificate, tlog entries) is ignored. Refusal reasons are sorted and deduplicated so the wording is deterministic whatever order a registry lists referrers in. cosign 2's legacy `.sig` signatures are not read (no lookup, no hint). Keyless (Fulcio/Rekor) is not implemented yet (#116, waiting on sigstore-rs): a keyless bundle matches no key and never fails the check for a key bundle beside it. `cosign::testutil` (feature `testutil`) signs cosign-3-shaped bundles and attaches them to `oci::testregistry`.
`--cosign-key` loads PEM public keys into `cosign::Verifier` (sigstore-crypto's `VerificationKey`) and `--cosign-trusted-root` a Sigstore trusted root for keyless bundles (sigstore-verify); verification runs **before** the caches, once per manifest digest per process, over the runtime's own registry client (same auth path as the pull); non-OCI sources are refused when a signature is required. The format is cosign 3's: a Sigstore bundle (v0.3) in an OCI 1.1 referrer of the module's manifest, discovered through the referrers API or, on a 404 (GHCR, `registry:3`), the `sha256-<hex>` referrers tag index; bundles are recognised by the layer media type, never by `artifactType` (cosign 3.0 sets none). A referrer counts only if its `subject` is the pinned digest; the bundle is fetched bounded and verified against its digest; a DSSE envelope signature over the PAE must verify with a configured key, and only then is the in-toto statement read: `Statement/v1`, `predicateType` exactly `https://sigstore.dev/cosign/sign/v1` (SLSA provenance and other attestations never count, whoever signed them) and a subject with the manifest digest. For a key-based bundle the keys are the trust root - its verification material (key hint, tlog entries) is ignored. Refusal reasons are sorted and deduplicated so the wording is deterministic whatever order a registry lists referrers in. cosign 2's legacy `.sig` signatures are not read (no lookup, no hint). A bundle carrying a certificate is keyless (#116): `sigstore_verify::Verifier::verify` (by the manifest digest, `VerificationPolicy::any_identity()`) establishes the signing time from its RFC 3161 timestamps and its Rekor entry's SET (a present-but-failing source is fatal, no fallback to the clock), verifies the inclusion proof and checkpoint, chains the certificate to the root's Fulcio authorities at that time and checks the signature; `cosign.rs` then requires the cosign predicate and asks the operator policy's `trustSigner` rules (`OperatorPolicy::permits_signer`: `context.issuer` and `context.identity` over the Repository hierarchy, default-deny, reached through the `cosign::SignerPolicy` trait) whether the signer is trusted for the module's repository. Without `--cosign-trusted-root` a keyless bundle is refused with a message naming the flag and never fails the check for a key bundle beside it. Key-based bundles stay on the key path on purpose: sigstore-verify's public-key policy refuses a bundle with neither a tlog entry nor a timestamp, which is what `cosign sign --key --tlog-upload=false` writes. The trusted root is a file read at startup (`cosign initialize` fetches it); fetching and refreshing it over TUF is #183. `cosign::testutil` (feature `testutil`) signs cosign-3-shaped key-based bundles and attaches them to `oci::testregistry`; `testsigstore::Instance` signs keyless ones against its own trusted root. A required module met by a runtime with neither flag is refused naming both: `the operator policy requires a cosign signature, but the runtime has neither --cosign-key nor --cosign-trusted-root to verify it`.

### One-pagers

Expand Down Expand Up @@ -532,7 +536,7 @@ CI runs lint and the toolchain-free workspace tests on every push and PR (`ci.ym

### Test Patterns

Unit tests live in `#[cfg(test)] mod tests` blocks beside the code; integration suites under `tests/`. Guest modules for tests are WAT fixtures assembled with the `wat` crate: components implementing the world with a sync-lifted `run` (the engine's tests carry fixtures that misbehave in one way each; the sandbox is tested through WASI 0.2 interfaces lowered by hand - `wasi:cli/environment`, `wasi:filesystem/preopens`, `wasi:cli/exit`, `wasi:clocks/monotonic-clock` - against a `$libc` core instance whose memory the main module imports; the one core-module fixture proves the ABI v1 refusal). Registry-backed tests use `oci::testregistry` (feature `testutil`): an in-memory distribution registry that serves and accepts artifacts, optionally behind the Bearer token flow, with OCI 1.1 referrers through the referrers API (`referrers_api: true`) or the tag schema (`push_referrer` maintains the `sha256-<hex>` index); `cosign::testutil` signs cosign-3-shaped Sigstore bundles into it. Expected `RunFunctionResponse`s are constructed whole and compared with `assert_eq!` on the prost types, including fatal cases, whose per-guest message wording is blanked before the comparison.
Unit tests live in `#[cfg(test)] mod tests` blocks beside the code; integration suites under `tests/`. Guest modules for tests are WAT fixtures assembled with the `wat` crate: components implementing the world with a sync-lifted `run` (the engine's tests carry fixtures that misbehave in one way each; the sandbox is tested through WASI 0.2 interfaces lowered by hand - `wasi:cli/environment`, `wasi:filesystem/preopens`, `wasi:cli/exit`, `wasi:clocks/monotonic-clock` - against a `$libc` core instance whose memory the main module imports; the one core-module fixture proves the ABI v1 refusal). Registry-backed tests use `oci::testregistry` (feature `testutil`): an in-memory distribution registry that serves and accepts artifacts, optionally behind the Bearer token flow, with OCI 1.1 referrers through the referrers API (`referrers_api: true`) or the tag schema (`push_referrer` maintains the `sha256-<hex>` index); `cosign::testutil` signs cosign-3-shaped key-based Sigstore bundles into it, and `testsigstore::Instance` (feature `testutil`; rcgen, x509-cert and time are its dependencies) is an in-memory Sigstore instance - a Fulcio CA, a CT log and a Rekor log from fixed seeds - that issues keyless bundles (ten-minute certificate with an embedded SCT, Rekor dsse 0.0.1 entry with SET, inclusion proof and checkpoint) at a fixed past time and publishes the trusted root that verifies them, so the keyless path is covered offline and deterministically (`validate_resolve_keyless_cosign_matches_the_goldens`). Expected `RunFunctionResponse`s are constructed whole and compared with `assert_eq!` on the prost types, including fatal cases, whose per-guest message wording is blanked before the comparison.

### Linting

Expand Down Expand Up @@ -602,7 +606,7 @@ By hand: `cargo run -p function-wasm -- --insecure --debug --module-dir=examples
- `wit-component` / `wit-parser` / `wasi-preview1-component-adapter-provider`: `guestfn build`'s componentizing (`engine::componentize`): the wrap of a core module carrying wit-bindgen's component-type section, the embed of that section from a Go guest's `wit/` (`embed_world`), and the wasip1 reactor adapter it links in. Pinned in `crates/engine` beside wasmtime: the adapter is published at wasmtime's version and must be the one the engine's wasmtime-wasi was released with, and wit-component and wit-parser (with wasmparser) are the versions wasmtime's own bindgen already pulls, so no second copy is compiled; Renovate moves the adapter with the wasmtime group and ignores wit-component, wasmparser and wit-parser, which are bumped by hand in that PR to what `cargo tree -i wit-component` shows wasmtime pulling.
- `function-sdk-rust` — the gRPC/protobuf types (prost), `request`/`response`/`resource` helpers and the CLI `Args`; the generated FunctionRunnerService *client* types serve tests, while serving goes through the raw codec.
- `cedar-policy` — both policy layers.
- `sigstore` (features `cosign`, `rustls-tls`, no default features): cosign key verification crypto only; fetching stays on the runtime's own registry client and the bundle, DSSE and in-toto shapes are parsed by `cosign.rs`'s own serde structs (no `bundle`/`verify` features).
- `sigstore-verify`, `sigstore-trust-root`, `sigstore-types` (sigstore/sigstore-rust 0.15, `default-features = false` plus `aws-lc-rs`, which rustls already brings; no `tuf` feature, so no network): keyless bundle verification and `TrustedRoot::from_json`, with `sigstore_verify::crypto::VerificationKey` for the key path. Fetching stays on the runtime's own registry client, and `cosign.rs` keeps its own serde structs for the key path and the key-or-certificate check. Pre-1.0: minor bumps are breaking, so a Renovate PR for them is a code change. The `sigstore` crate (sigstore-rs) is no longer used.
- `reqwest` (blocking, rustls) — the egress client and the registry client.
- `prometheus-client` — the metrics registry and OpenMetrics encoder (the official OpenMetrics-native client; the classic text format is derived from its output in `metrics.rs`).
- `clap` — both CLIs.
Expand Down
Loading
Loading