Skip to main content
← Back

wash GitHub Actions & a Plugin System Built on Wasm Components

The July 23, 2025 wasmCloud community call is a developer-tooling deep dive built around Wasm components. Bailey Hayes walks through her exploration of GitHub Actions for the reimagined wash CLI — demoing a setup-wash-action, comparing composite, TypeScript, and Docker action styles, and digging into caching and CI supply-chain security. Brooks Townsend then shows the new wash plugin system, where every plugin is a WebAssembly component, and demonstrates how clap integration gives plugins native subcommands, flags, and environment variables. The group closes on how to trust third-party plugins by leaning on the Wasm sandbox and capability-based security.

Key Takeaways

  • setup-wash-action is a proposed canonical GitHub Action for installing wash in CI — Bailey built a composite-style action that wraps an existing cache-aware Cargo install so users get a fast, OS- and architecture-agnostic setup without writing their own cache logic
  • Composite beat TypeScript for this use case: switching from the GitHub-recommended TypeScript template to a composite action deleted roughly 47,000 lines (mostly transitive dependencies and a committed dist/ bundle), trading a small amount of startup speed for far less maintenance churn
  • CI supply-chain security was a running theme — pin actions to full commit SHAs, treat every action as third-party code, and use intermediate environment variables to defend against injection (referencing the real-world tj-actions/reviewdog supply-chain attack)
  • Bailey recommended housing the action in the wasmCloud CNCF org (not a separate "actions" org) so open governance is unambiguous, using a setup-wash-action repo git-submoduled into a central wasmcloud/actions monorepo
  • The wash plugin system treats each plugin as a WebAssembly component that exports a single plugin interface — plugins can add top-level subcommands or hook into existing commands (before/after a developer loop, push, etc.)
  • clap integration lets plugins declare their own subcommands, positional args, flags, defaults, and environment variables, so a Go-compiled (TinyGo) OAuth plugin gets the same polished help text and required-argument validation as built-in wash commands
  • Trusting plugins comes down to the Wasm sandbox: components run deny-by-default with per-plugin scoped filesystem access, you can wash inspect a component to see the capabilities (e.g. outbound HTTP) it requests before installing, and a plugin test command surfaces metadata up front
  • The community floated capability-driven, environment-level policy — letting wash configuration blanket-allow or disallow filesystem, HTTP, or environment access for plugins, plus OCI signing and a curated/audited marketplace for provenance

Chapters

Meeting Notes

Demo: A setup-wash GitHub Action for CI

Bailey opened with a demo motivated by Brooks's reimagined wash and by the need to maintain GitHub Actions across the Bytecode Alliance and wasmCloud orgs. As a CNCF and GitHub-native project, wasmCloud installs tooling constantly in CI, and a curl command or package-manager install isn't ideal for confident, reproducible runs. Her recommended approach is a composite-style action that wraps an existing cache-aware Cargo install, pointed at the latest wash. In the demo, a cold run took about eight minutes to build wash from source; a warm run pulled it from the GitHub Actions cache in roughly one second. She walked through the two flavors of GitHub caching — the per-workflow actions cache (used by the tj-actions/cache Cargo install action) versus the per-runner tool cache — concluding they were effectively equivalent in her testing. The downside of the off-the-shelf Cargo action is that it's Cargo-specific, which is confusing for the Go and TypeScript developers wasmCloud also serves; wrapping it behind a branded setup-wash-action gives the best of both worlds.

TypeScript vs Composite Actions, and CI Supply-Chain Security

Bailey gave the TypeScript route a fair shake. GitHub strongly recommends TypeScript actions because they have the lowest startup overhead (no container spin-up, no npm install at runtime), and their tool-cache SDK and REST helpers are genuinely nice — for example, querying available releases to resolve a semver request to the best matching patch. But the template pulls in a large dependency tree and requires committing a bundled dist/ folder, generating constant dependency-bump PRs. That led to a major thread on CI supply-chain security: every action is third-party code, actions should be pinned to full commit SHAs (a GitHub best practice oddly absent from their own template), and intermediate environment variables should be used to defend against injection. Bailey cited the real reviewdog/tj-actions supply-chain attack, where a transitive action exfiltrated secrets from any workflow it ran inside, as the cautionary tale. When she swapped the TypeScript action for a composite one, the diff deleted around 47,000 lines — mostly transitive dependencies rather than the ~300 lines of actual TypeScript (five lines of shell, in effect).

Repository Naming and Open Governance

Brooks asked where the action should live. Bailey had surveyed the top GitHub Marketplace actions and prior art from Chainguard (composite-of-composite actions, a central actions repo, and a separate marketplace repo per published action) and Victor Adossi's turnkey release-pipeline workflows. Her recommendation: name inputs defensively (e.g. wash-version to avoid collisions), suffix the repo with -action (mirroring Docker), and — importantly — keep it in the wasmCloud CNCF organization rather than a separate "actions" org. A separate org muddies who owns open governance if a maintainer steps away; staying inside the CNCF project (and similarly inside the Bytecode Alliance) keeps the contributor ladder and governance clear. The plan: a setup-wash-action repo (so it can be published to the Marketplace) git-submoduled into a central wasmcloud/actions monorepo for centralized, low-churn maintenance.

The wash Plugin System and clap Integration

Brooks then dug into the wash plugin system, where plugins are WebAssembly components installed from local disk or an OCI reference. A plugin can register top-level subcommands or hook into existing commands — for instance, standing up an observability stack before a developer loop starts, or annotating a component before it's pushed to a registry. The highlight was clap integration: by converting the plugin's WIT-described command and argument records into clap arguments and subcommands, each plugin gets first-class CLI behavior — its own flags, positional args, defaults, environment-variable binding (crucial for secrets like an OAuth client secret you don't want in shell history), and clap's polished required-argument validation. Brooks demoed a TinyGo-compiled OAuth plugin with a run subcommand, and shared a "sneaky" trick: a fake ghost subcommand named with a newline so installed plugins render under their own "plugins" heading in the help output.

Trusting Third-Party Plugins

The final discussion, prompted by audience questions, focused on trust. Because every plugin is a WebAssembly component, it runs inside the Wasm sandbox: deny-by-default, with each plugin getting its own scoped directory rather than full filesystem access. You can wash inspect a component to see exactly which capabilities it requests at runtime — if an "emoji generator" wants outbound HTTP, that's a red flag — and a new plugin test command surfaces a plugin's metadata, source, and license before installation. Bailey pushed for thorough docs on how WASI pre-opens work, provenance for environment variables, OCI signing, and a curated, audited marketplace. Community member ossfellow suggested environment-level policy: letting wash configuration blanket-disallow filesystem, HTTP, or environment access in, say, an enterprise context — which Brooks noted maps cleanly onto WASI's capability model.

WebAssembly News and Updates

This call captures WebAssembly maturing into everyday developer tooling. The reimagined wash CLI is being rebuilt around WebAssembly components as a plugin primitive, so the same component model that powers wasmCloud workloads now extends the developer toolchain itself — plugins authored in Rust, Go (via TinyGo), or TypeScript, distributed over OCI, and sandboxed by default. For the full story behind the redesign, see Introducing wash: a Wasm-powered plugin system. The supply-chain security conversation echoes a broader industry push toward pinned, audited CI dependencies — an area where Wasm's deny-by-default model is a natural fit. For ongoing updates, follow the wasmCloud blog and the Bytecode Alliance.

What is wasmCloud?

wasmCloud is a CNCF project that lets you build applications using WebAssembly components and deploy them anywhere — cloud, edge, or Kubernetes clusters. It uses the WebAssembly component model to let you write business logic in any supported language (Rust, Go, Python, TypeScript, C#) while the platform handles capabilities like HTTP, messaging, and key-value storage through a pluggable provider architecture. wasmCloud's reference host is built on Wasmtime, and the same component model now underpins its developer tooling — including the wash CLI and its host plugin system. With built-in OpenTelemetry observability and Kubernetes integration, wasmCloud bridges WebAssembly's portable, sandboxed execution model and production cloud-native infrastructure.

Topic Deep Dive: Wasm Components as a Plugin Primitive

The throughline of this meeting is using Wasm components as the foundation for extensible developer tooling. Rather than shipping plugins as native binaries or scripts — each carrying its own trust and portability problems — wash treats every plugin as a WebAssembly component that exports a standard plugin interface defined in WIT. That single decision delivers three things at once. First, portability: a component built once runs on any host architecture and OS, which is exactly why a portable wash install matters in CI. Second, polyglot authoring: the OAuth plugin in the demo was written in Go and compiled with TinyGo, but the same interface works from Rust or TypeScript — write the API once, get bindings for free. Third, and most importantly, security: because a component is sandboxed deny-by-default with capability-based access, a plugin you pull from an OCI registry can't read /etc/passwd or reach the network unless its capabilities — visible via wash inspect — grant it. As wasmCloud continues to build on the component model, the bet is that the same primitive securing production workloads also makes third-party tooling safe to adopt.

Who Should Watch This

This call is especially valuable for platform and DevOps engineers building reproducible CI pipelines who want a fast, secure way to install wash (start with Bailey's setup-wash-action demo at 4:03), security-minded teams worried about CI supply-chain risk (the SHA-pinning and injection discussion at 13:37), and tooling authors and contributors interested in extending a CLI with sandboxed WebAssembly plugins (Brooks's clap integration walkthrough at 31:11).

Up Next

Next week brings a better, more polished demo of the wash plugin system — Brooks's OAuth plugin running end-to-end — plus a TypeScript example to round out the Go and Rust authoring stories, and continued refinement of the plugin security boundaries (pre-opens, environment provenance, and policy-based capability controls) ahead of the upstream contribution into the wasmCloud organization.

Get Involved

wasmCloud is a CNCF project and contributions are welcome. Join the community:

Full Transcript

Read the complete transcript with speaker labels and timestamps:

Read the full transcript →