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
94 changes: 94 additions & 0 deletions examples/pipeline_attestation/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""End-to-end PulseEngine attestation pipeline example.

Demonstrates the full sign + attest + verify_chain + show_chain flow against a
minimal Rust component. This is the minimum viable pipeline that produces a
non-empty transformation chain today — meld_fuse requires multi-component
setups and wasm_optimize (loom) has a pending WASI path-resolution issue
(TODO in //wasm/private:wasm_optimize.bzl), so they are not wired in here.
"""

load("@rules_wasm_component//rust:defs.bzl", "rust_wasm_component_bindgen")
load(
"@rules_wasm_component//wasm:defs.bzl",
"wasm_attest",
"wasm_keygen",
"wasm_show_chain",
"wasm_sign",
"wasm_verify_chain",
)
load("@rules_wasm_component//wit:defs.bzl", "wit_library")

package(default_visibility = ["//visibility:public"])

# 1. WIT interface for a trivial greeter component.
wit_library(
name = "greet_interfaces",
package_name = "example:greet",
srcs = ["wit/greet.wit"],
world = "greet",
)

# 2. Build the component from Rust.
rust_wasm_component_bindgen(
name = "greeter_component",
srcs = ["src/lib.rs"],
profiles = ["release"],
validate_wit = False,
wit = ":greet_interfaces",
)

# 3. Generate signing keys (compact format for wasm_sign).
wasm_keygen(
name = "example_keys",
public_key_name = "example.public",
secret_key_name = "example.secret",
)

# 4. Sign the component. Signing mutates the module (adds a signature section)
# but does not record a transformation attestation — that's what step 5
# exists for.
wasm_sign(
name = "greeter_signed",
component = ":greeter_component",
keys = ":example_keys",
)

# 5. Record a transformation attestation covering the signing step. This
# writes a `wsc-attestation` custom section into the output describing the
# (greeter_component -> greeter_signed) transformation.
wasm_attest(
name = "greeter_attested",
input_component = ":greeter_component",
output_component = ":greeter_signed",
tool_name = "wasmsign2",
tool_version = "0.2.6",
transformation_type = "custom",
)

# 6. Emit the attestation chain as JSON — useful as a CI artifact for
# compliance or debugging.
wasm_show_chain(
name = "greeter_chain",
as_json = True,
component = ":greeter_attested",
)

# 7. Verify the chain. Because the module now carries a transformation
# attestation, wsc verify-chain runs the full policy pipeline (empty
# policy here => default rules only) and emits a success marker.
wasm_verify_chain(
name = "greeter_chain_verified",
component = ":greeter_attested",
)

# Convenience aggregate for `bazel build //examples/pipeline_attestation:all_pipeline_outputs`.
filegroup(
name = "all_pipeline_outputs",
srcs = [
":greeter_attested",
":greeter_chain",
":greeter_chain_verified",
":greeter_component",
":greeter_signed",
],
)
53 changes: 53 additions & 0 deletions examples/pipeline_attestation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Pipeline attestation example

End-to-end demonstration of the PulseEngine attestation surface:

```
rust_wasm_component_bindgen -> wasm_sign -> wasm_attest
|
wasm_show_chain <-----+
wasm_verify_chain <----+
```

## Targets

| Target | Rule | Output |
|--------|------|--------|
| `:greeter_component` | `rust_wasm_component_bindgen` | Signed-free WASM component |
| `:example_keys` | `wasm_keygen` | Ed25519 key pair (compact format) |
| `:greeter_signed` | `wasm_sign` | Component with embedded signature |
| `:greeter_attested` | `wasm_attest` | Signed component + transformation attestation |
| `:greeter_chain` | `wasm_show_chain` | Chain as JSON (CI-friendly artifact) |
| `:greeter_chain_verified` | `wasm_verify_chain` | Success marker, fails the build on policy/chain error |
| `:all_pipeline_outputs` | `filegroup` | Convenience aggregate |

## Build it

```sh
bazel build //examples/pipeline_attestation:all_pipeline_outputs
```

Expected output includes a success message from `wasm_verify_chain`:

```
✓ Transformation chain is valid
Transformation stages: 1
Tools used: wasmsign2
```

And a JSON chain (`greeter_chain.json`) with one attestation entry recording
input hash, output hash, tool name/version, and timestamp.

## Why `wasm_attest` here rather than `meld_fuse` or `wasm_optimize`

- **`meld_fuse`** produces meaningful chains but requires two or more
components with compatible component-model interfaces — an end-to-end
fusion demo is more than this example wants to set up.
- **`wasm_optimize` (loom)** has a pending WASI path-resolution issue (see
the `TODO` in `wasm/private/wasm_optimize.bzl`) so it is not wired into
this example yet.

`wasm_attest` is the attestation escape hatch for arbitrary transformations
and is sufficient to demonstrate the full `sign -> attest -> verify -> show`
surface against a single component. Once `wasm_optimize` and `meld_fuse`
self-attest reliably end-to-end, follow-up examples can chain them.
14 changes: 14 additions & 0 deletions examples/pipeline_attestation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Tiny greeter component used to demonstrate the PulseEngine attestation
// pipeline: build -> sign -> attest -> verify_chain -> show_chain.

use greeter_component_bindings::exports::example::greet::greeter::Guest;

struct Component;

impl Guest for Component {
fn greet(name: String) -> String {
format!("Hello, {name}!")
}
}

greeter_component_bindings::export!(Component with_types_in greeter_component_bindings);
11 changes: 11 additions & 0 deletions examples/pipeline_attestation/wit/greet.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// Minimal interface for the pipeline attestation example.
package example:[email protected];

interface greeter {
/// Return a short greeting for the given name.
greet: func(name: string) -> string;
}

world greet {
export greeter;
}
8 changes: 6 additions & 2 deletions wasm/private/wasm_optimize.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ def _wasm_optimize_impl(ctx):
# Get LOOM WASM component
loom_wasm = ctx.file._loom_wasm

# Build command arguments
# Build command arguments.
# Note: loom v0.3.0 does NOT accept the `--` separator between the wasm
# module path and the subcommand when run under `wasmtime run`.
# TODO: loom reads input paths via WASI and `--dir=.` does not resolve
# the bazel-out symlinks; a small wrapper (mirroring wasmsign2_wrapper)
# is needed to compute real paths and register them as WASI mounts.
args = ctx.actions.args()
args.add("run")
args.add("--dir=.")
args.add(loom_wasm)
args.add("--")
args.add("optimize")
args.add(input_wasm)
args.add("-o", output_wasm)
Expand Down