Skip to content

Commit 1db8a29

Browse files
committed
Merge branch 'main' into docs/prereqs-rust
2 parents b8c77c6 + c53901b commit 1db8a29

File tree

5 files changed

+186
-25
lines changed

5 files changed

+186
-25
lines changed

component-model/src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
- [Authoring Components](./creating-and-consuming/authoring.md)
2626
- [Composing Components](./creating-and-consuming/composing.md)
2727
- [Running Components](./creating-and-consuming/running.md)
28-
- [Distributing Components](./creating-and-consuming/distributing.md)
28+
- [Distributing and Fetching Components and WIT](./creating-and-consuming/distributing.md)
2929
- [Tutorial](./tutorial.md)
3030

3131
# Runtime Support
Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,158 @@
1-
# Distributing Components
1+
# Distributing and Fetching Components and WIT
22

33
Modern applications rely extensively on third-party packages - so extensively that distributing packages is almost an industry in itself. Traditionally, these have been specific to a language. For example, JavaScript developers are used to using packages from NPM, and Rust developers use `crates.io`. Some runtimes support binary distribution and linking, enabling limited cross-language interop; for example, Maven packages can be written in any language that targets the Java runtime. Services like this are variously referred to as "package managers" or "registries."
44

5-
Publishing and distribution are not defined by the core component model, but will form an important part of the component ecosystem. For example, if you're writing JavaScript, and want to pull in a highly optimised machine learning algorithm written in C and compiled to Wasm, you should be able to invoke it from a registry, just as easily as you would add a NPM package from the NPM registry.
5+
Publishing and distribution are not defined by the core component model, but form important part of the component ecosystem. For example, if you're writing JavaScript, and want to pull in a highly optimised machine learning algorithm written in C and compiled to Wasm, you can pull it from a registry, ideally just as easily as you would add a NPM package from the NPM registry.
66

7-
Publishing and distribution is a work in progress. The proposed registry protocol is [warg](https://warg.io/), but this is still in development, and there are no public warg registries as yet. You can find more information about the development of the registry protocol [here](https://github.com/bytecodealliance/governance/blob/main/SIGs/SIG-Registries/proposal.md).
7+
You can get involved with improving the packaging and hosting of Wasm components by joining the [Bytecode Alliance Packaging Special Interest Group (SIG)](https://github.com/bytecodealliance/governance/blob/main/SIGs/sig-packaging/proposal.md).
8+
9+
## The `wkg` Registry Tool
10+
11+
The [`wasm-pkg-tools` project](https://github.com/bytecodealliance/wasm-pkg-tools) enables fetching and publishing Wasm components to OCI registries. It contains a `wkg` CLI tool that eases distributing and fetching components and WIT packages. The usual way of using `wkg` is to address packages by their package name, i.e. `example:[email protected]`. When using `wkg` this way, you don't need to know about the physical location of the package, as the `wkg` configuration handles that for you. If you need to, though, you can also use `wkg` to work with OCI artifacts directly, addressing them by OCI references when using the `wkg oci` subcommand.
12+
13+
`wkg` contains several subcommand:
14+
15+
- `wkg oci` - pushes/pulls Wasm artifacts to/from any OCI registry
16+
- `wkg publish` - publish components or WIT packages by package name
17+
- `wkg get` - pulls components or WIT packages by package name
18+
- `wkg wit` - commands for interacting with WIT files and dependencies
19+
- `wkg config` - interact with the `wkg` configuration
20+
21+
The following sections detail a subset of actions that can be performed with `wkg`.
22+
23+
## `wkg` Configuration Files
24+
25+
When you use most `wkg` commands (`wkg oci` being the exception), you don't interact with physical locations, only with package names. The `wkg` configuration file is used to map package naming to physical location. It provides the ability to configure:
26+
27+
- The default registry for packages in a given namespace. For example, the location for `wasi` packages such as `wasi:clocks` or `wasi:http`.
28+
- Registry overrides for specific packages, or packages not stored in the same place as the rest of their namespace. For example, if `wasi:key-value` were stored in a different registry from other `wasi` packages.
29+
- The default registry for all packages not listed in one of the previous sections
30+
31+
The configuration file also includes credentials for private registries, or for pushing to registries where you have permission, and other configuration options. See the [`wkg` docs for more configuration options](https://github.com/bytecodealliance/wasm-pkg-tools?tab=readme-ov-file#configuration).
32+
33+
For example, to fetch WASI packages, such as `wasi:clocks` and `wasi:http`, you can add a line under the `namespace_registries` section for the `wasi` namespace. Specifically, the example below configures `wkg` to fetch WASI packages from the [WebAssembly OCI GitHub Container Registry](https://github.com/orgs/WebAssembly/packages), where the latest interfaces are published upon WASI releases. To edit your `wkg` config file, run `wkg config --edit`.
34+
35+
> Remember, all package names consist of the a namespace field followed by the package field. The package name `wasi:clocks` has a namespace of `wasi` and package field of `clocks`. In this way, the following configuration ensures that `wkg` will know to route fetches and publishes of any `wasi:<package field>` to the configured location.
36+
37+
```toml
38+
# $XDG_CONFIG_HOME/wasm-pkg/config.toml
39+
default_registry = "ghcr.io"
40+
41+
[namespace_registries]
42+
# Tell wkg that packages with the `wasi` namespace are in an OCI registry under ghcr.io/webassembly
43+
wasi = { registry = "wasi", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "webassembly/" } } }
44+
```
45+
46+
As a more generic example, The following configuration, instructs `wkg` to use [ttl.sh](https://ttl.sh/) OCI registry for all packages with the `docs` namespace.
47+
48+
```toml
49+
# $XDG_CONFIG_HOME/wasm-pkg/config.toml
50+
default_registry = "ghcr.io"
51+
52+
[namespace_registries]
53+
# Instruct wkg to use the OCI protocol to fetch packages with the `foo` namespace from ttl.sh/wasm-components
54+
docs = { registry = "docs-registry", metadata = { preferredProtocol = "oci", "oci" = {registry = "ttl.sh", namespacePrefix = "wasm-components/" } } }
55+
```
56+
57+
> Note: the registry name can be referenced in the `package_registry_overrides` section of the `wkg` config to provide overrides for specific packages of a namespace.
58+
59+
## Distributing WIT and Components by Package Name with `wkg publish`
60+
61+
Once you've [configured `wkg`](#wkg-configuration-files) to know where to publish packages to, you can use the `wkg publish` command to publish *components* or *interfaces* to be consumed by others.
62+
63+
Imagine you have defined the following `adder` world in WIT:
64+
65+
```wit
66+
package docs:[email protected];
67+
68+
interface add {
69+
add: func(a: u32, b: u32) -> u32;
70+
}
71+
72+
world adder {
73+
export add;
74+
}
75+
```
76+
77+
You can publish this *WIT* using `wkg` by wrapping it up as a Wasm component. Yes, you heard that right! We are packaging WIT as Wasm.
78+
79+
```sh
80+
# Package the contents of add WIT directory as Wasm
81+
wkg wit build --wit-dir tutorial/wit/adder
82+
# Publish the produced component
83+
wkg publish docs:[email protected]
84+
```
85+
86+
If you had configured `wkg` as described in the [`wkg` configuration section](#wkg-configuration-files), this would publish the component to `ttl.sh/wasm-components/docs/adder:0.1.0`. This WIT can then be fetched using `wkg get`, specifying the format `wit`:
87+
88+
```sh
89+
wkg get --format wit docs:[email protected] --output adder.wit
90+
```
91+
92+
Instead of publishing the WIT interface, you could publish the built component by running:
93+
94+
```sh
95+
wkg publish adder.wasm --package docs:[email protected]
96+
```
97+
98+
You can then fetch the component by running:
99+
100+
```sh
101+
wkg get docs:[email protected] --output adder.wasm
102+
```
103+
104+
## More Generic Operations with `wkg oci`
105+
106+
The `wkg oci` subcommand enables pushing/pulling Wasm artifacts to/from any OCI registry. Unlike `wkg publish` and `wkg get`, providing the WIT package is not required.
107+
108+
To push a component to an OCI registry, use `wkg oci pull`. The example below pushes a component to a GitHub Container Registry.
109+
110+
```sh
111+
wkg oci push ghcr.io/user/component:0.1.0 component.wasm
112+
```
113+
114+
To pull a component, run:
115+
116+
```sh
117+
wkg oci pull ghcr.io/user/component:0.1.0 -o component.wasm
118+
```
119+
120+
## Fetching WIT Package Dependencies using `wkg`
121+
122+
Sometimes fetching a single package is not sufficient because it depends on other packages. For example, the following world describes a simple Wasm service which requires `wasi:http/proxy`:
123+
124+
```wit
125+
package foo:wasi-http-service;
126+
127+
world target-world {
128+
include wasi:http/[email protected];
129+
}
130+
```
131+
132+
You may be tempted to simply get the `wasi:http` package with `wkg get --format wit wasi:[email protected] -o wit/deps/http/`. However, `wasi:http` depends on other WASI packages such as `wasi:clocks` and `wasi:io`. To make sure to fetch a package and all its dependencies, use `wkg wit fetch`, which will read the package containing the world(s) you have defined in the given wit directory (`wit` by default). It will then fetch the
133+
dependencies and write them to the `deps` directory along with a lock file.
134+
135+
After placing the above file in `./wit`, run the following to fetch the dependencies:
136+
137+
```sh
138+
wkg wit fetch
139+
```
140+
141+
The `./wit` directory will be populated as follows:
142+
```sh
143+
wit
144+
├── deps
145+
│ ├── wasi-cli-0.2.3
146+
│ │ └── package.wit
147+
│ ├── wasi-clocks-0.2.3
148+
│ │ └── package.wit
149+
│ ├── wasi-http-0.2.3
150+
│ │ └── package.wit
151+
│ ├── wasi-io-0.2.3
152+
│ │ └── package.wit
153+
│ └── wasi-random-0.2.3
154+
│ └── package.wit
155+
└── world.wit
156+
```
157+
158+
Now, you can use the language toolchain of your choice to generate bindings and create your component.

component-model/src/language-support/go.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ Under the hood, TinyGo invokes `wasm-tools` to embed the WIT file to the module
286286
tinygo build -target=wasip2 -o add.wasm --wit-package docs:[email protected] --wit-world adder main.go
287287
```
288288

289+
> **WARNING:** By default, tinygo includes all debug-related information in your .wasm file. That is desirable when prototyping or testing locally to obtain useful backtraces in case of errors (for example, with `wasmtime::WasmBacktraceDetails::Enable`). To remove debug data and optimize your binary file, build with `-no-debug`. The resulting .wasm file will be considerably smaller (up to 75% reduction in size).
290+
289291
We now have an add component that satisfies our `adder` world, exporting the `add` function, which
290292

291293
We can confirm using the `wasm-tools component wit` command:

component-model/src/language-support/javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ You should see output like the following:
106106
OK Successfully written add.wasm.
107107
```
108108

109+
> **WARNING:** By using `--disable all`, your component won't get access to any WASI interfaces that might be useful for debugging or logging. For example, you can't `console.log(...)` or `console.error(...)` without `stdio`; you can't use `Math.random()` without `random`; and you can't use `Date.now()` or `new Date()` without `clocks`. Please note that calls to `Math.random()` or `Date.now()` will return seemingly valid outputs, but without actual randomness or timestamp correctness.
110+
109111
[mdn-js-module]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
110112

111113
## Running the Component in the `example-host`

component-model/src/language-support/rust.md

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,43 @@ the component's implementation language.
88
> [!NOTE]
99
> You can find more details about `cargo-component` on [crates.io](https://crates.io/crates/cargo-component).
1010
11-
## Setup
11+
## 1. Setup
1212

1313
Install [`cargo-component`][cargo-component-install]:
14-
```sh
14+
```console
1515
cargo install --locked cargo-component
1616
```
1717
Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools#installation):
18-
```sh
18+
```console
1919
cargo install --locked wasm-tools
2020
```
2121
Install [`wasmtime`](https://github.com/bytecodealliance/wasmtime#installation):
22-
```sh
22+
```console
2323
curl https://wasmtime.dev/install.sh -sSf | bash
2424
```
2525
Clone the [component-docs](https://github.com/bytecodealliance/component-docs) repo:
26-
```sh
26+
```console
2727
git clone https://github.com/bytecodealliance/component-docs
2828
```
2929

30-
## Scaffolding a Component
30+
## 2. Scaffolding a Component
3131

3232
We will create a component in Rust that implements the `add` function exported
33-
by the [`docs:adder/adder`][docs-adder] world in the
33+
by the [`adder` world][docs-adder] world in the
3434
`docs:adder`
3535
[package](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#package-names).
3636

3737
First `cd` into the `tutorial` directory found in the repo we just cloned:
38-
```sh
38+
```console
3939
cd component-docs/component-model/examples/tutorial
4040
```
4141

4242
Now create a new WebAssembly component package called `add`:
43-
```sh
43+
```console
4444
cargo component new add --lib && cd add
4545
```
4646

47-
## Adding the WIT world
47+
## 3. Adding the WIT world
4848

4949
We now need to change our generated `wit/world.wit` to match `docs:adder`:
5050
```wit
@@ -59,20 +59,20 @@ to the following:
5959
package = "docs:adder"
6060
```
6161

62-
## Generating bindings
62+
## 4. Generating bindings
6363

6464
Now that we've updated our `world.wit` and `Cargo.toml`, we can re-generate
6565
bindings with the command below:
6666

67-
```sh
67+
```console
6868
cargo component bindings
6969
```
7070

7171
`cargo-component` will generate bindings for our
7272
world and create a `Guest` trait that a component should
73-
implement
73+
implement.
7474

75-
## Implementing the `Guest` trait
75+
## 5. Implementing the `Guest` trait
7676

7777
Implement the `Guest` trait in `src/lib.rs`, using the scaffolded code. Your
7878
code should look something like the following:
@@ -81,17 +81,20 @@ code should look something like the following:
8181
{{#include ../../examples/tutorial/adder/src/lib.rs}}
8282
```
8383

84-
## Building a Component
84+
## 6. Building a Component
8585

8686
Now, let's build our component, being sure to optimize with a release build:
8787

88-
```sh
88+
```console
8989
cargo component build --release
9090
```
9191

9292
You can use `wasm-tools` to inspect the WIT package generated by `cargo-component`:
9393

94-
```sh
94+
95+
You can use `wasm-tools component wit` to output the WIT package of the component:
96+
97+
```console
9598
wasm-tools component wit target/wasm32-wasip1/release/add.wasm
9699
```
97100

@@ -110,6 +113,8 @@ package docs:[email protected] {
110113
}
111114
```
112115

116+
> **WARNING:** Building with `--release` removes all debug-related information from the resulting .wasm file. When prototyping or testing locally, you might want to avoid `--release` to obtain useful backtraces in case of errors (for example, with `wasmtime::WasmBacktraceDetails::Enable`). Note: the resulting .wasm file will be considerably larger (likely 4MB+).
117+
113118
### Running a Component
114119

115120
To verify that our component works, lets run it from a Rust application that knows how to run a
@@ -120,7 +125,7 @@ Rust bindings, bring in WASI worlds, and execute the component.
120125

121126
```sh
122127
$ cd examples/example-host
123-
$ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/add.wasm
128+
$ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/adder.wasm
124129
1 + 2 = 3
125130
```
126131

@@ -131,8 +136,8 @@ It's often preferable to export an interface rather than a function, either to
131136
comply with an existing specification or to capture several functions and types
132137
at once.
133138

134-
For example, to implement the [`docs:adder/adder`](#adding-the-wit-world)
135-
world, you would write the following Rust code:
139+
For example, to implement the [`adder` world](#adding-the-wit-world), you would
140+
write the following Rust code:
136141

137142
```rust
138143
{{#include ../../examples/tutorial/adder/src/lib.rs}}
@@ -226,7 +231,8 @@ world root {
226231
}
227232
```
228233

229-
As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. To fulfill the `add` import, so that only `calculate` is exported, you would need to [compose the `calculator.wasm` with some `exports-add.wasm` into a single, self-contained component](../creating-and-consuming/composing.md).
234+
As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. To fulfill the `add` import, so that
235+
only `calculate` is exported, you would need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../creating-and-consuming/composing.md).
230236

231237
## Creating a command component with `cargo component`
232238

0 commit comments

Comments
 (0)