Building WebAssembly Like It's 2011 - David Justice, Microsoft & Terence Lee, Heroku/Salesforce
David Justice, Microsoft, Terence Lee, Heroku/Salesforce
KubeCon + CloudNativeCon Europe 2025 · Session
Overview
This talk, "Building WebAssembly Like It's 2011," delivered by David Justice of Microsoft and Terence Lee of Heroku/Salesforce at KubeCon EU, delves into the challenges of building and composing WebAssembly (Wasm) components and proposes a solution rooted in a decade-old, proven technology: Cloud Native Buildpacks. The speakers highlight the current complexity of the Wasm development experience, particularly when combining multiple languages and components, and demonstrate how a streamlined build process can significantly enhance developer productivity, mirroring the transformative impact Buildpacks had on polyglot application development in the early 2010s.

Key moments
- 0:00 Introduction, speakers, and talk agenda
- 1:00 Defining WebAssembly: portability, sandboxing, efficiency
- 2:10 Introducing the WebAssembly Component Model and WIT
- 4:00 The 'Layer Cake' Analogy for Wasm Components
- 5:10 The current 'complicated recipe' for Wasm components
- 6:30 Terence introduces Buildpacks as a 2011 solution
- 7:10 How Buildpacks simplify language-agnostic application builds
Building WebAssembly Like It's 2011 - David Justice, Microsoft & Terence Lee, Heroku/Salesforce
Speakers: David Justice, Microsoft; Terence Lee, Heroku/Salesforce
Conference: KubeCon EU
YouTube: https://www.youtube.com/watch?v=Du0mPGFd7Fc
Overview
This talk, "Building WebAssembly Like It's 2011," delivered by David Justice of Microsoft and Terence Lee of Heroku/Salesforce at KubeCon EU, delves into the challenges of building and composing WebAssembly (Wasm) components and proposes a solution rooted in a decade-old, proven technology: Cloud Native Buildpacks. The speakers highlight the current complexity of the Wasm development experience, particularly when combining multiple languages and components, and demonstrate how a streamlined build process can significantly enhance developer productivity, mirroring the transformative impact Buildpacks had on polyglot application development in the early 2010s.
The core problem addressed is the low-level nature of raw WebAssembly, which, despite its promises of portability, sandboxing, and performance, requires intricate manual handling of data types and interfaces. The introduction of the WebAssembly Component Model and WebAssembly Interface Types (WIT) provides a much-needed abstraction layer, but the tooling for orchestrating complex, multi-language component builds remains nascent. Justice and Lee argue that by leveraging Cloud Native Buildpacks, developers can abstract away the "gnarly recipe" of Wasm toolchains, creating a more intuitive and efficient pathway from source code to deployable WebAssembly components, packaged as lean OCI artifacts.
The talk is critically important for the WebAssembly community as it navigates the path from core Wasm modules to highly composable, language-agnostic components. It offers a practical, open-source-driven approach to standardizing Wasm builds, reducing friction for developers, and ultimately accelerating the adoption of WebAssembly in cloud-native environments. By demonstrating how established patterns like Buildpacks can be adapted to emerging technologies, the speakers provide a clear vision for a future where building sophisticated WebAssembly applications is as straightforward as deploying a traditional web service.
Background
▶ Watch: Introduction, speakers, and talk agenda (0:00)
WebAssembly (Wasm) has emerged as a powerful application binary format, offering compelling advantages in portability, sandboxing, efficiency, and performance across various environments—from browsers to edge devices and servers. Its ability to compile code from multiple source languages, including Go, JavaScript, .NET, and Java, into a single, compact binary format is a significant draw. However, Wasm's native interface is inherently low-level, primarily dealing with integers and pointers for representing high-level data types like strings or complex structures. This "low-level" nature necessitates a significant amount of boilerplate code for serialization and deserialization when components need to communicate, making direct interaction tedious, error-prone, and a barrier to developer productivity.
To address this, the WebAssembly Component Model was introduced. This model acts as an outer wrapper around core Wasm modules, providing a higher-level interface that handles the complexities of binary serialization automatically. It's conceptually similar to Protocol Buffers (Protobuf) or gRPC, where developers define an interface, and the tooling generates the necessary code to manage data exchange. The Component Model uses WebAssembly Interface Types (WIT), a language-agnostic interface description language, to define high-level structures, records, resources, functions, and critically, worlds. WIT allows developers to declaratively specify how components interact, abstracting away the underlying binary details and enabling seamless composition between components potentially written in different languages.
Despite the elegance of the Component Model and WIT, the process of building, compiling, and composing these components, especially in a polyglot environment or within a monorepo, remains complex. Developers often face a "gnarly recipe" involving a multitude of specialized tools: wackage for fetching WIT definitions from OCI registries, TinyGo for Go compilation, jco for JavaScript component tooling, and wac for fusing components based on WIT interfaces. Manually orchestrating these tools for every build is cumbersome, prone to error, and lacks standardization, leading to a fragmented and difficult developer experience.
This challenge harkens back to the early 2010s when Heroku pioneered Buildpacks to simplify polyglot application deployment. At that time, developers struggled with environment setup, dependency management, and creating deployable artifacts for diverse language runtimes like Ruby, Java, and Node.js. Buildpacks provided an opinionated, language-agnostic system that automatically detected application types, installed necessary tools, optimized performance, and produced deployable artifacts, allowing developers to focus solely on their application code. This concept evolved into Cloud Native Buildpacks (CNB), an open-source, CNCF incubation project that standardizes the build process to produce OCI-compliant container images from source code, without requiring developers to write Dockerfiles. The speakers draw a direct parallel, proposing that CNBs can offer the same transformative simplification for WebAssembly component development that they once did for traditional application deployment.
Key Findings
▶ Watch: Introducing the WebAssembly Component Model and WIT (2:10)
The central finding of this talk is the successful application of Cloud Native Buildpacks to significantly streamline the complex, multi-step process of building and composing WebAssembly Component Model applications. By adapting Buildpacks to the WebAssembly ecosystem, the speakers demonstrated a viable path to abstracting away the intricate toolchains and manual configuration currently required, thereby enhancing the developer experience and promoting broader adoption of Wasm.
Specifically, the key findings include:
- Buildpacks' Viability for WebAssembly: Despite their origins in 2011, Buildpacks prove to be an extremely valuable and viable solution for modern WebAssembly development. They effectively encapsulate the "gnarly recipe" of Wasm tools (
wackage,TinyGo,jco,wac, etc.) into standardized, composable units, making the build process much simpler and more declarative for developers. - Simplified Polyglot Component Composition: The talk demonstrates how Buildpacks can facilitate the seamless composition of WebAssembly components written in different languages (e.g., JavaScript and Go) into a single, unified Wasm component. This mirrors the microservices paradigm but within a single, highly efficient binary, eliminating network boundaries and associated complexities for internal communication.
- Optimized OCI Artifact Output: The Buildpack-driven process results in highly optimized OCI artifacts. Instead of a full OCI image containing a Wasm runtime (which can be around 50MB), the output is a lean Wasm component artifact, significantly smaller. A Go and JavaScript composed component was demonstrated at 14MB, with potential for Rust components to be as small as 2MB. This drastically reduces network bandwidth, storage requirements, and potentially improves cold start times.
- Enhanced Developer Experience: By reducing the build process to a single
pack buildcommand, the approach dramatically improves the developer experience. It automates toolchain management, dependency resolution, and component composition, allowing developers to focus on writing application logic rather than wrestling with build scripts. - Challenges with Monorepos and Metadata: While successful, the project identified existing limitations. Current Wasm tooling and Buildpacks lack sufficient metadata to declaratively build components within complex monorepos, especially those containing multiple components in different languages. Manual specification of "worlds" for
wac composeis still required, highlighting a need for more opinionated repository structures or improved inference capabilities. - Potential for Universal Library Support: The project revealed a significant opportunity to create language-agnostic Wasm component interfaces for common libraries (e.g., OpenAI API). Wrapping a component interface around a library means it could be used by any language that compiles to WebAssembly, eliminating the need to write and maintain libraries in multiple languages, thereby exponentially increasing developer productivity.
- Integration with Cloud-Native Runtimes: The approach naturally integrates with existing cloud-native infrastructure. The output OCI artifacts can be deployed to Kubernetes using projects like runwazi, which provides a
containerdruntime class for executing Wasm components directly, further optimizing resource utilization by avoiding redundant Wasm runtimes within each container image.
These findings collectively underscore the transformative potential of combining established build automation principles with emerging WebAssembly technologies, paving the way for a more accessible, efficient, and standardized Wasm development ecosystem.
Technical Deep Dive
▶ Watch: The 'Layer Cake' Analogy for Wasm Components (4:00)
The technical implementation presented in the talk revolves around two core pillars: the WebAssembly Component Model for enabling polyglot, composable Wasm, and Cloud Native Buildpacks for automating the complex build process.
At its foundation, WebAssembly (Wasm) provides a secure, portable, and efficient binary instruction format. However, its low-level nature means direct interaction requires handling raw pointers and memory offsets for high-level data types. The WebAssembly Component Model addresses this by providing a standardized way to define interfaces between Wasm modules, abstracting away the binary serialization. This is achieved through WebAssembly Interface Types (WIT), an IDL (Interface Definition Language) used to describe data structures (records, resources), functions, and worlds. A world in WIT defines the set of functions a component exports and the functions it expects to import from its environment or other components, acting as a contract for interaction. For instance, example.server might export an HTTP handler but import adder and chat functions from a backend service, along with shared domain types. Conversely, example.service might import an outgoing HTTP handler to make external API calls (e.g., to OpenAI) and export adder and chat functions. The WY CLI (Wasmtime CLI) interface is also leveraged, allowing components to be run as standalone CLI applications for debugging before composition.
The complexity of compiling and composing these components, especially from different source languages, necessitates a robust build automation system. This is where Cloud Native Buildpacks become critical. A pack build command orchestrates the entire process, taking application source code and a builder image as input. The builder image contains the necessary buildpacks and the lifecycle—a spec-compliant binary that coordinates the build phases:
- Detect: Buildpacks analyze the source code (e.g., presence of
package.json,go.mod) to determine which buildpacks are applicable and create a build plan, allowing buildpacks to communicate dependencies and requirements. - Analyze/Restore: On subsequent builds, this phase checks metadata from previous builds and restores cached layers, ensuring only changed components are rebuilt and uploaded.
- Build: This is the core phase where selected buildpacks execute. They install language runtimes (e.g., Node.js, Go), dependencies (e.g.,
yarn install), compile source code into Wasm modules, and generate WIT files. Crucially, they can then use Wasm-specific tools likejco(JavaScript component tooling) to create Wasm components andwacto fuse multiple components based on their WIT interfaces. Each logical part (e.g., Node.js modules, compiled Go binaries) is placed into distinct OCI image layers. - Export: The final phase packages all constructed layers on top of a run image into an OCI image, along with metadata for caching and subsequent builds.
To specifically support WebAssembly component development, the speakers created a suite of custom buildpacks:
Wom tools: Installs essential Wasm-specific tools likewackage,jco, andwac.WMT time engine: Provides the Wasmtime runtime, which is necessary to execute the final Wasm component.Node.jsandGo: Standard language buildpacks adapted for Wasm compilation. The Go buildpack notably uses TinyGo for its smaller binary size.Wack analyzer: Scans a monorepo to identify individual Wasm component projects and their respective languages.Wack composer: Orchestrates the compilation of individual components (e.g., JavaScript projects, Go projects), collects their Wasm components, and useswacto compose them into a single, integrated component based on their WIT definitions. The resulting composed component is then stored in a cache layer.Wom finalizer: This critical buildpack optimizes the final OCI image. It removes all source code and intermediate build artifacts, retaining only the composed WebAssembly component and the necessary Wasmtime runtime. This results in an extremely lean container image, significantly smaller than typical application containers.
The demonstrated component structure involved a JavaScript frontend exposing HTTP endpoints (e.g., /hello, /add) and a Golang backend providing functions for addition (adder) and OpenAI chat completion (chat). These two components, despite being written in different languages, communicate seamlessly within the composed Wasm component via their defined WIT interfaces, effectively mimicking a microservices architecture without the overhead of network calls. The outgoing HTTP handler in the Go component allows it to make external requests to the OpenAI API, demonstrating real-world integration capabilities.
A key advantage highlighted is the efficiency of the resulting artifacts. A composed Go and JavaScript Wasm component was 14MB. If built with Rust (which doesn't typically include a large runtime like Go), this could be reduced to approximately 2MB. When deployed with a Wasm-aware container runtime like runwazi in Kubernetes, the Wasm runtime itself doesn't even need to be bundled into the OCI artifact, further reducing image size and enhancing security by minimizing the attack surface.
Demo / Proof of Concept
▶ Watch: Terence introduces Buildpacks as a 2011 solution (6:30)
The core of the demonstration showcased the pack build wom compose command in action, illustrating how Cloud Native Buildpacks simplify the complex WebAssembly component build process within a monorepo. The speakers used a custom builder image containing their specialized Wasm buildpacks and pointed it to a monorepo containing both a JavaScript frontend and a Golang backend Wasm component.
The demo began by executing the pack build command, which initiated the Buildpack lifecycle. The output revealed the various phases, including analyze and detect, identifying the JavaScript and Go projects within the monorepo. As this was a simulated second build, the restore phase was shown to efficiently reuse cached layers from a previous build, highlighting the performance benefits of Buildpacks' layering and caching mechanisms. The build phase then proceeded, showing the execution of the Wack analyzer, the Node.js build process, and the Go build process, culminating in the composition of the individual Wasm components by the Wack composer. Finally, the Wom finalizer produced a lean OCI image containing the composed Wasm component and the Wasmtime runtime.
Once the OCI image was built, it was run locally, with the necessary OpenAI API key provided as an environment variable. The demonstration then showcased the composed application's functionality:
helloendpoint: A simple HTTP endpoint served by the JavaScript frontend, demonstrating basic web serving capabilities.addfunction: An endpoint that took two numbers, passed them from JavaScript to the Go backend component via the WIT interface, had them added by the Go component, and returned the result. This illustrated seamless polyglot component interaction.- OpenAI chat completion: The most significant part of the demo, where a user prompt (e.g., "Capital of England?") was sent to an HTTP endpoint. The JavaScript frontend passed this to the Go backend, which then used its
outgoing HTTP handlerto interact with the external OpenAI API. The response from OpenAI was translated back through Go to JavaScript, and then returned to the user, showcasing real-world integration with external services through a composed Wasm component.
The demo successfully validated the concept of using Buildpacks to create a single, deployable OCI image from multiple language-specific Wasm components, highlighting the efficiency and composability benefits. While acknowledging some initial "technical difficulties" during the development of the buildpacks and tooling (such as challenges with monorepo detection for standard buildpacks or the need for manual WIT world definitions), the overall demonstration served as a compelling proof of concept for a greatly improved WebAssembly developer experience. The codebase for the demo was made available on GitHub, inviting community contribution to further refine the tooling and address the identified challenges.
Defensive Implications
▶ Watch: How Buildpacks simplify language-agnostic application builds (7:10)
The approach of using Cloud Native Buildpacks to produce highly optimized WebAssembly Component Model artifacts offers several significant defensive implications for modern application security:
- Reduced Attack Surface:
- Minimalist Images: The
Wom finalizerBuildpack ensures that the final OCI image contains only the composed WebAssembly component and the necessary Wasm runtime (like Wasmtime), with all source code, build tools, and unnecessary dependencies stripped out. This drastically reduces the image size (e.g., 14MB for Go/JS, potentially 2MB for Rust) and, consequently, the attack surface. Fewer files mean fewer potential vulnerabilities to exploit. - No Source Code in Production: By removing source code from the final image, the risk of source code disclosure or tampering in a production environment is eliminated, preventing attackers from easily reverse-engineering proprietary logic or finding weaknesses.
- Runtime Separation with
runwazi: When deployed with a Wasm-aware runtime like runwazi in Kubernetes, the Wasm runtime itself doesn't need to be bundled into the application image. This further reduces the application image size and allows the Wasm runtime to be managed and secured centrally by the platform, rather than duplicated and potentially misconfigured across many application images.
- Enhanced Sandboxing and Isolation:
- WebAssembly's Intrinsic Security: WebAssembly is inherently sandboxed, running in a memory-safe, isolated environment. This provides a strong security boundary, preventing Wasm components from directly interacting with the host system or other components' memory without explicit permissions defined by WASI (WebAssembly System Interface).
- Component Model's Encapsulation: The Component Model further reinforces isolation by defining clear interfaces via WIT. Components only interact through these defined interfaces, limiting direct access and reducing the blast radius in case of a compromise within a single component.
- Standardized and Reproducible Builds:
- Eliminating Snowflake Builds: Buildpacks enforce a standardized, opinionated build process. This eliminates "snowflake" Dockerfiles and custom build scripts, which often introduce inconsistencies and potential security misconfigurations across different applications or teams.
- Consistent Security Baselines: By using common buildpacks, security teams can establish consistent security baselines for how applications are built, dependencies are managed, and vulnerabilities are scanned. This simplifies auditing and ensures that security patches (e.g., for language runtimes) are applied uniformly.
- Improved Supply Chain Security: Cloud Native Buildpacks are designed to produce reproducible builds, meaning that given the same source code and builder image, the output OCI image will be byte-for-byte identical. This is crucial for software supply chain security, enabling verification that the deployed artifact matches the intended build without unauthorized modifications.
- Faster Patching and Deployment:
- Layer Caching Efficiency: Buildpacks' intelligent layering and caching mechanisms ensure that only changed layers are rebuilt and uploaded. This means that patching a base image or a runtime version (as shown with the Node.js patch update in the demo) can be incredibly fast, reducing the window of exposure to newly discovered vulnerabilities.
- Rapid Response to CVEs: The ability to quickly rebuild and redeploy applications with updated base images or language runtimes allows organizations to respond more rapidly and efficiently to CVEs (Common Vulnerabilities and Exposures), minimizing potential impact.
In summary, leveraging Cloud Native Buildpacks for WebAssembly component development not only improves developer experience but also significantly enhances the security posture of cloud-native applications by reducing attack surfaces, enforcing strong isolation, standardizing build processes, and enabling faster, more reliable patching cycles.
Key Takeaways
- Buildpacks Simplify Wasm Complexity: Cloud Native Buildpacks provide a powerful, proven mechanism to abstract away the "gnarly recipe" of WebAssembly toolchains, making it dramatically simpler to build and compose Wasm components, especially in polyglot environments.
- Polyglot Component Composition is Practical: The WebAssembly Component Model with WIT enables seamless, performant composition of components written in different languages (e.g., JavaScript and Go) into a single Wasm artifact, mimicking microservices without network overhead.
- Highly Optimized OCI Artifacts: The Buildpack approach yields extremely lean OCI artifacts (e.g., 14MB for Go/JS, potentially 2MB for Rust) that contain only the Wasm component and minimal runtime, significantly reducing image size, improving cold start times, and enhancing security.
- Monorepos Need Better Wasm Tooling: Current Wasm tooling and Buildpacks require more sophisticated metadata inference or opinionated monorepo structures to declaratively manage multi-component, multi-language projects without manual configuration of WIT worlds.
- Universal Wasm Library Ecosystem Potential: There's a significant opportunity to develop language-agnostic Wasm component interfaces for common libraries (e.g., OpenAI), allowing developers to write a library once and use it across any Wasm-compatible language.
- Enhanced Security and Operational Efficiency: The combination of Wasm's sandboxing, Buildpacks' standardized and reproducible builds, and lean OCI artifacts leads to a reduced attack surface, faster patching cycles, and improved operational efficiency for cloud-native deployments.
About the Speaker(s)
David Justice is an employee at Microsoft and serves as a co-chair for the CNCF Wasm working group. His work focuses on advancing WebAssembly within the cloud-native ecosystem, leveraging his expertise to simplify complex development processes and improve developer experience.
Terence Lee is an architect at Heroku/Salesforce. He is recognized for his significant contributions to the Buildpacks ecosystem, having co-created Buildpacks twice. His extensive experience in building polyglot platforms and his deep understanding of developer tooling make him a key advocate for applying proven methodologies like Buildpacks to emerging technologies such as WebAssembly.
Reviews
Dr. Zero (Offensive Security Researcher) — MUST SEE
This talk masterfully applies the proven elegance of Cloud Native Buildpacks to the complex, nascent world of WebAssembly Component Model builds. It provides a pragmatic, open-source solution to the "gnarly recipe" currently plaguing polyglot Wasm development, drastically simplifying the build process. The resulting lean, secure OCI artifacts and the strong defensive implications make this a critical step forward for Wasm adoption in cloud-native environments, offering a clear path to standardized, efficient, and secure component composition.
Heather Calloway (CISO) — STRONG ACCEPT
This session makes a compelling case for leveraging Cloud Native Buildpacks to standardize and secure WebAssembly (Wasm) component development. It effectively addresses the current complexity of Wasm builds, offering a clear path to reduced attack surface, faster patching, and improved developer experience. The talk provides actionable insights for security leaders and platform teams grappling with software supply chain integrity and operational efficiency in cloud-native environments.