docs: document Wasmeld architecture and workflows
- describe resident Runtime boundaries and sandbox guarantees - specify component archive and host capability contracts - document Console ownership and persistence behavior - explain versioned WIT Registry, lock files, and path replace - provide startup, packaging, publication, and dependency commands
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# Wasmeld
|
||||
|
||||
Wasmeld 是面向 WebAssembly Component 的服务运行与管理平台。内部服务编译为
|
||||
Component,在受限 Wasmtime Sandbox 中以常驻 Actor 运行,并通过稳定 WIT 契约
|
||||
暴露能力。
|
||||
|
||||
## 结构
|
||||
|
||||
```text
|
||||
crates/wasmeld-runtime Component Runtime 与 Sandbox
|
||||
crates/wasmeld-console 管理 API 与持久化
|
||||
crates/wasmeld-package wasmeld CLI、组件包与 WIT 依赖管理
|
||||
components/ 示例 Component
|
||||
console/ TanStack Start 管理面
|
||||
wit/ Wasmeld WIT package 源码
|
||||
```
|
||||
|
||||
## 启动
|
||||
|
||||
后端:
|
||||
|
||||
```bash
|
||||
cargo +stable run -p wasmeld-console
|
||||
```
|
||||
|
||||
管理面:
|
||||
|
||||
```bash
|
||||
cd console
|
||||
npm run dev
|
||||
```
|
||||
|
||||
构建组件:
|
||||
|
||||
```bash
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
pack components/echo/Cargo.toml --locked
|
||||
```
|
||||
|
||||
发布 WIT Package:
|
||||
|
||||
```bash
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit publish wit/service --registry http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
组件在 `wasmeld.toml` 中声明 Registry 和精确版本依赖,`wasmeld wit fetch` 会递归
|
||||
解析依赖、生成 `wit/deps`,并将完整解析结果记录在 `wit.lock`。本地开发可使用
|
||||
`[replace]` 临时覆盖 Registry 来源。详见
|
||||
[`docs/design/wit-package-management.md`](docs/design/wit-package-management.md)。
|
||||
@@ -0,0 +1,101 @@
|
||||
# Wasmeld Host Capabilities
|
||||
|
||||
**状态:** Implemented
|
||||
|
||||
**首个能力:** `wasmeld:clock/monotonic-clock@0.1.0`
|
||||
|
||||
## 边界
|
||||
|
||||
所有组件实现稳定的 `service-component` 导出契约。宿主能力不加入这个基础 World,
|
||||
而是定义成独立 WIT interface,由组件专属 World 按需 import。
|
||||
|
||||
```text
|
||||
wit/
|
||||
├── service/package.wit # wasmeld:service@0.1.0
|
||||
└── clock/package.wit # wasmeld:clock@0.1.0
|
||||
```
|
||||
|
||||
例如普通 Echo 不导入宿主能力:
|
||||
|
||||
```wit
|
||||
package component:echo@0.1.0;
|
||||
|
||||
world echo-component {
|
||||
include wasmeld:service/service-component@0.1.0;
|
||||
}
|
||||
```
|
||||
|
||||
Clock Probe 显式申请单调时钟:
|
||||
|
||||
```wit
|
||||
package component:clock-probe@0.1.0;
|
||||
|
||||
world clock-probe-component {
|
||||
include wasmeld:service/service-component@0.1.0;
|
||||
import wasmeld:clock/monotonic-clock@0.1.0;
|
||||
}
|
||||
```
|
||||
|
||||
组件通过自己的 World 生成 binding:
|
||||
|
||||
```rust
|
||||
wit_bindgen::generate!({
|
||||
path: "wit",
|
||||
world: "clock-probe-component",
|
||||
generate_all,
|
||||
});
|
||||
```
|
||||
|
||||
组件根目录中的 `wit/deps` 由 `wasmeld wit fetch` 根据 `wasmeld.toml` 和
|
||||
`wit.lock` 生成。这几行只是编译期 binding 入口,不是网络协议。组件最终只包含
|
||||
所选 World 的 imports 和 exports。
|
||||
|
||||
## Runtime
|
||||
|
||||
Runtime 注册 Component 时读取真实 imports,并映射为内部 `HostCapability`。未知
|
||||
import 会在注册阶段拒绝。Actor 创建 Linker 时,只为该 Component 实际导入的能力
|
||||
注册实现。
|
||||
|
||||
```text
|
||||
Component imports
|
||||
|
|
||||
v
|
||||
validate_component_imports
|
||||
|
|
||||
v
|
||||
Vec<HostCapability>
|
||||
|
|
||||
v
|
||||
Actor Linker
|
||||
```
|
||||
|
||||
当前 `monotonic-clock.now` 返回 Actor 创建后经过的纳秒数。它不提供系统时间、网络、
|
||||
文件或其他宿主访问。
|
||||
|
||||
Rust `std::time::Instant` 会引入 `wasi:clocks/monotonic-clock`,该 WASI 接口仍不在
|
||||
Sandbox 白名单内。组件必须使用平台明确提供的 `wasmeld:clock/monotonic-clock`,
|
||||
不能通过标准库绕过能力控制。
|
||||
|
||||
## 新增能力
|
||||
|
||||
新增能力时必须同时完成:
|
||||
|
||||
1. 在独立 `.wit` 文件中定义小型 interface。
|
||||
2. 只在需要它的组件 World 中 import。
|
||||
3. 在 Runtime 中实现生成的 Host trait。
|
||||
4. 将完全限定的 import 名称映射到新的 `HostCapability`。
|
||||
5. 在 Actor Linker 中注册实现。
|
||||
6. 增加允许和拒绝两类 Sandbox 测试。
|
||||
|
||||
不要把可选能力加入 `service-component`。不要仅依据包 manifest 授权,Runtime 必须
|
||||
以编译后 Component 的真实 imports 为准。
|
||||
|
||||
## 版本策略
|
||||
|
||||
- 已发布的 WIT 版本不可原地修改。
|
||||
- 破坏性变更发布新的 major version。
|
||||
- Runtime 在迁移期可以同时实现两个版本。
|
||||
- 不相关能力独立演进,不能迫使所有组件重新编译。
|
||||
|
||||
`service` 和 `clock` 已经是两个独立版本的 WIT package。源码只维护当前版本,历史
|
||||
版本由 Git tag 与不可变 Registry 制品保留;组件通过包名、版本和锁文件选择依赖。
|
||||
@@ -0,0 +1,94 @@
|
||||
# Wasmeld 组件包
|
||||
|
||||
**状态:** Implemented
|
||||
|
||||
**格式版本:** 1
|
||||
|
||||
`.wasmpkg` 是平台内部发布 WebAssembly Component 的单文件制品。文件使用 ZIP
|
||||
容器,扩展名固定为 `.wasmpkg`,并且只能包含两个根目录条目:
|
||||
|
||||
```text
|
||||
echo-0.1.0.wasmpkg
|
||||
├── package.toml
|
||||
└── component.wasm
|
||||
```
|
||||
|
||||
`package.toml` 示例:
|
||||
|
||||
```toml
|
||||
schema_version = 1
|
||||
id = "echo"
|
||||
revision = "0.1.0"
|
||||
world = "component:echo/echo-component@0.1.0"
|
||||
component = "component.wasm"
|
||||
sha256 = "..."
|
||||
```
|
||||
|
||||
包中不包含内存、Fuel、超时或 mailbox 等部署策略。这些限制由
|
||||
`wasmeld-console` 的 `registration_limits` 注入,组件发布者不能通过上传制品扩大
|
||||
Sandbox 权限。
|
||||
|
||||
## 组件配置
|
||||
|
||||
组件必须是 `cdylib`,并在 `Cargo.toml` 中声明平台服务 ID:
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.wasmeld]
|
||||
id = "echo"
|
||||
world = "component:echo/echo-component@0.1.0"
|
||||
```
|
||||
|
||||
版本直接使用 `[package].version`。`world` 必须是组件选择的完整、带版本 WIT World;
|
||||
打包工具会将它写入制品,控制台不需要手工填写。
|
||||
|
||||
组件的 WIT 依赖由同目录的 `wasmeld.toml` 和 `wit.lock` 管理。`pack` 会在 Cargo
|
||||
编译前自动生成 `wit/deps`;CI 应传入 `--locked`,禁止依赖摘要发生漂移。
|
||||
|
||||
## 打包
|
||||
|
||||
从项目根目录运行:
|
||||
|
||||
```bash
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
pack components/echo/Cargo.toml --locked
|
||||
```
|
||||
|
||||
命令默认使用 Rust `1.90.0` 构建 `wasm32-wasip2` release Component,并输出:
|
||||
|
||||
```text
|
||||
dist/echo-0.1.0.wasmpkg
|
||||
```
|
||||
|
||||
可以覆盖输出路径,或者复用已经构建的 Component:
|
||||
|
||||
```bash
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
pack components/echo/Cargo.toml \
|
||||
--output dist/echo.wasmpkg \
|
||||
--no-build
|
||||
```
|
||||
|
||||
通过 `WASMELD_COMPONENT_TOOLCHAIN` 可以覆盖组件构建工具链。
|
||||
|
||||
## 注册
|
||||
|
||||
管理 API 只接收一个 multipart 字段:
|
||||
|
||||
```bash
|
||||
curl -F package=@dist/echo-0.1.0.wasmpkg \
|
||||
http://127.0.0.1:8080/api/v1/services
|
||||
```
|
||||
|
||||
Console 在写入制品目录前执行以下校验:
|
||||
|
||||
- ZIP 恰好包含 `package.toml` 和 `component.wasm`
|
||||
- schema、服务 ID、版本和 WIT World 合法
|
||||
- 解压后的 Component 未超过平台上限
|
||||
- Component 的 SHA-256 与 manifest 一致
|
||||
- Wasmtime 能编译 Component,且 imports 符合 Sandbox 白名单
|
||||
- 同一服务 ID 和版本尚未注册
|
||||
|
||||
校验成功后,服务以 `stopped` 状态注册。启动 Actor 仍是独立的管理操作。
|
||||
@@ -0,0 +1,54 @@
|
||||
# Wasmeld Console 管理后端
|
||||
|
||||
**状态:** Implemented
|
||||
|
||||
**范围:** 本地 Wasmeld Runtime 管理控制面
|
||||
|
||||
## 边界
|
||||
|
||||
`wasmeld-console` 是独立 Rust 进程,对外提供管理 HTTP API,对内只调用
|
||||
`wasmeld-runtime`。Runtime 作为库嵌入同一进程,不另起网络服务。前端 `console`
|
||||
不直接访问 Runtime、文件系统或数据库。
|
||||
|
||||
~~~text
|
||||
TanStack Console / wasmeld CLI
|
||||
|
|
||||
| HTTP + JSON / multipart
|
||||
v
|
||||
wasmeld-console
|
||||
+-- API / CORS
|
||||
+-- component artifact registry
|
||||
+-- immutable WIT package registry
|
||||
+-- Toasty persistence
|
||||
+-- local libSQL
|
||||
|
|
||||
v
|
||||
wasmeld-runtime
|
||||
+-- Wasmtime Engine
|
||||
+-- resident Actor
|
||||
+-- Store + Component Instance
|
||||
~~~
|
||||
|
||||
## 状态
|
||||
|
||||
- Console 接收一个 `.wasmpkg`,从包内读取并校验组件身份。
|
||||
- `component.wasm` 和平台生成的运行时 `manifest.toml` 保存到本地制品目录。
|
||||
- 运行限制由 Console 策略注入,不接受组件包覆盖。
|
||||
- manifest、调用计数和最近 256 条事件通过 Toasty 保存到 libSQL。
|
||||
- Console 进程启动时自动创建 Runtime Engine,从数据库恢复服务元数据,并从制品目录注册 Component。
|
||||
- Console 可显式启动、停止和重启 Runtime;停止 Runtime 不会停止 Console HTTP 管理面。
|
||||
- Runtime 停止时释放全部 Actor、Store、Instance 和 epoch worker。
|
||||
- Actor 内存和运行状态只存在于当前进程。
|
||||
- Runtime 或 Console 重启后所有服务回到 `stopped`,调用计数与事件保留,但不会伪造 Wasm 内存恢复。
|
||||
|
||||
## 约束
|
||||
|
||||
- 默认只监听 `127.0.0.1:8080`。
|
||||
- libSQL 数据库默认位于 `var/wasmeld/console.db`。
|
||||
- CORS 默认只允许本地开发面板,其他 Origin 必须显式配置。
|
||||
- 服务 ID 和 revision 只能包含 ASCII 字母、数字、点、下划线和短横线。
|
||||
- `.wasmpkg` 上传和解压后的 Component 上限均默认为 64 MiB。
|
||||
- 二进制 WIT Package 上传上限默认为 4 MiB,同名同版本发布后不可覆盖。
|
||||
- 包内固定只有 `package.toml` 与 `component.wasm`,不会直接解压任意路径。
|
||||
- Wasm 编译、实例化和调用通过阻塞任务执行,不占用 Tokio 异步 worker。
|
||||
- Sandbox、fuel、deadline、内存和 mailbox 限制仍由 `wasmeld-runtime` 执行。
|
||||
@@ -0,0 +1,319 @@
|
||||
# Wasmeld Runtime 架构
|
||||
|
||||
**状态:** Draft
|
||||
|
||||
**日期:** 2026-07-22
|
||||
|
||||
**范围:** 仅实现 Wasmeld Runtime
|
||||
|
||||
## 1. 范围
|
||||
|
||||
本阶段只解决一件事:在一个 Rust 宿主进程中,安全地加载并常驻运行内部 Rust 编译的 WebAssembly Component。
|
||||
|
||||
需要具备:
|
||||
|
||||
- Rust Component 的构建、注册、加载、调用、停止与重载。
|
||||
- 同一实例跨多次调用保留内存。
|
||||
- 同一实例不发生并发重入。
|
||||
- Wasmtime 资源限制与最小 sandbox。
|
||||
- 统一且不含业务语义的 Component 调用契约。
|
||||
|
||||
当前不做 HTTP、客户、鉴权、数据库、网络、消息队列、业务状态持久化、分布式部署或发布治理。
|
||||
|
||||
## 2. 核心结构
|
||||
|
||||
~~~text
|
||||
Rust Component source
|
||||
|
|
||||
| cargo build --target wasm32-wasip2 --release
|
||||
v
|
||||
component.wasm + manifest.toml
|
||||
|
|
||||
v
|
||||
Component Registry
|
||||
|
|
||||
v
|
||||
Wasmeld Runtime (one Rust process)
|
||||
|
|
||||
+-- Engine
|
||||
+-- compiled Component cache
|
||||
+-- Service Manager
|
||||
|
|
||||
+-- ServiceActor
|
||||
+-- bounded mailbox
|
||||
+-- Store<HostState>
|
||||
+-- restricted Linker
|
||||
+-- Component Instance
|
||||
~~~
|
||||
|
||||
Runtime 是一个 Rust 库或单独进程,不提供网络协议。对内只暴露:
|
||||
|
||||
~~~text
|
||||
register(manifest, artifact) -> ServiceKey
|
||||
start(service_key, init_config) -> ActorHandle
|
||||
invoke(actor_handle, bytes) -> bytes or RuntimeError
|
||||
stop(actor_handle)
|
||||
reload(next_manifest, next_artifact, init_config) -> ActorHandle
|
||||
~~~
|
||||
|
||||
调用方未来可以是 HTTP Adapter、CLI 或测试程序;Runtime 本身不关心调用来源。
|
||||
|
||||
## 3. 核心对象
|
||||
|
||||
| 对象 | 责任 |
|
||||
| --- | --- |
|
||||
| Component Artifact | 一个不可变的 component.wasm 与最小 manifest。 |
|
||||
| Service Revision | Artifact 的 id + revision。 |
|
||||
| Engine | 进程级共享的 Wasmtime 编译和执行引擎。 |
|
||||
| Component Cache | 缓存编译后的 Component,不保存实例内存。 |
|
||||
| Service Manager | 管理 Service Revision 与 ActorHandle 的映射。 |
|
||||
| ServiceActor | 独占 Store、Instance、HostState 与有界邮箱。 |
|
||||
| ActorHandle | 向 Actor 发送调用,不能接触 Store 或 Instance。 |
|
||||
| HostState | Store 专有的运行时状态,不包含业务数据。 |
|
||||
|
||||
V1 采用:
|
||||
|
||||
~~~text
|
||||
one Service Revision -> one ServiceActor -> one Store + one Instance
|
||||
~~~
|
||||
|
||||
多个 Actor、分片和水平扩展属于后续能力。
|
||||
|
||||
## 4. 制品与注册
|
||||
|
||||
内部服务构建为 Component:
|
||||
|
||||
~~~bash
|
||||
cargo build --target wasm32-wasip2 --release
|
||||
~~~
|
||||
|
||||
最小制品:
|
||||
|
||||
~~~text
|
||||
component.wasm
|
||||
manifest.toml
|
||||
~~~
|
||||
|
||||
manifest 只包含:
|
||||
|
||||
~~~text
|
||||
id
|
||||
revision
|
||||
component path
|
||||
expected WIT world
|
||||
memory limit
|
||||
fuel per call
|
||||
deadline
|
||||
mailbox capacity
|
||||
input/output byte limits
|
||||
~~~
|
||||
|
||||
注册规则:
|
||||
|
||||
- ServiceKey 由 id 与 revision 构成,不允许覆盖。
|
||||
- Runtime 校验 manifest 的 WIT world 格式与资源策略;实例化时由 typed binding 校验基础导出契约。
|
||||
- V1 只接受本地受信任制品;Registry、签名、SBOM 与远程下载后置。
|
||||
- 已编译 Component 可缓存,但 Actor 的内存只能存在于 Store + Instance 中。
|
||||
|
||||
## 5. 最小 WIT 契约
|
||||
|
||||
V1 不定义任何业务协议。输入输出都是有长度上限的 opaque bytes。
|
||||
|
||||
~~~wit
|
||||
package wasmeld:service@0.1.0;
|
||||
|
||||
world service-component {
|
||||
variant service-error {
|
||||
init-failed(string),
|
||||
call-failed(string),
|
||||
}
|
||||
|
||||
export init: func(config: list<u8>) -> result<_, service-error>;
|
||||
export invoke: func(input: list<u8>) -> result<list<u8>, service-error>;
|
||||
}
|
||||
~~~
|
||||
|
||||
规则:
|
||||
|
||||
- init 仅在一个新 Instance 上调用一次。
|
||||
- invoke 是唯一的普通调用入口。
|
||||
- Component 返回 service-error 后,Actor 可继续使用。
|
||||
- trap、资源超限或中断后,整个 Store + Instance 必须丢弃,不能继续调用。
|
||||
- 基础 world 不定义宿主能力 import。组件专属 World 通过独立 interface 按需 import
|
||||
平台能力,详见 `wasmeld-capabilities.md`。普通 Rust `wasm32-wasip2` 产物会隐式导入标准 WASI P2
|
||||
接口,因此 Runtime 提供受限的 WASI 兼容层,而不是把它们暴露为业务 ABI。
|
||||
- Runtime 在注册时只允许以下兼容 import:`wasi:cli/{environment,exit,stdin,stdout,stderr}`、
|
||||
`wasi:clocks/wall-clock`、`wasi:filesystem/{preopens,types}`、`wasi:io/{error,streams}`。
|
||||
`wasi:io/poll`、WASI monotonic clock、随机数、socket、HTTP 和未知平台 import 都会被拒绝。
|
||||
- V1 提供独立的 `wasmeld:clock/monotonic-clock@0.1.0` 平台能力。只有显式导入
|
||||
该 interface 的组件会获得对应 Linker 实现。
|
||||
- health、shutdown、日志、文件、网络和标准 I/O 都不属于基础 ABI。wall clock 仅作为
|
||||
Rust/WASI 兼容层的一部分存在,不用于传递业务能力;随机数不在 V1 白名单内。
|
||||
|
||||
## 6. 常驻 Actor
|
||||
|
||||
~~~text
|
||||
Caller
|
||||
-> ActorHandle
|
||||
-> bounded Sender<Invocation>
|
||||
-> dedicated Wasm worker
|
||||
-> Store + Instance + HostState
|
||||
~~~
|
||||
|
||||
一个 Actor 的 worker 独占 Store。不能使用 Arc<Mutex<Store>> 共享 Store,也不能让同一 Instance 重入。
|
||||
|
||||
处理流程:
|
||||
|
||||
1. ActorHandle 提交 Invocation。
|
||||
2. Actor 从 FIFO mailbox 取出一条消息。
|
||||
3. Actor 在同一 Store + Instance 上调用 service.invoke。
|
||||
4. Actor 通过 oneshot response 返回结果。
|
||||
5. 下一条消息才可进入该 Instance。
|
||||
|
||||
这保证 Rust heap、Wasm linear memory 和全局变量在多次调用间常驻,同时保证状态修改串行。
|
||||
|
||||
实现约束:
|
||||
|
||||
- Wasm 调用放在专用 worker 线程或线程池,不能阻塞 Tokio 的通用 worker。
|
||||
- V1 不使用 Wasmtime async 或 Wasm threads。为兼容 Rust 的 `wasm32-wasip2` 组件,
|
||||
链接同步、受限的 WASI P2 白名单实现。
|
||||
- mailbox 满时立即返回 RuntimeOverloaded。
|
||||
- 调用 deadline 到期且尚未进入 Instance 时直接取消。
|
||||
- Component 不得在 invoke 之外启动后台循环。
|
||||
|
||||
Actor 内存是易失的。Runtime 重启、Actor fault、stop 或 reload 后,下一实例重新执行 init,不恢复旧内存。
|
||||
|
||||
## 7. 生命周期
|
||||
|
||||
~~~text
|
||||
Registered -> Starting -> Ready -> Draining -> Stopped
|
||||
|
|
||||
+-> Failed
|
||||
~~~
|
||||
|
||||
- register:保存 Artifact 与 manifest。
|
||||
- start:取得已编译 Component,创建 Store、受限 WASI Linker 与 Instance,然后调用 init。
|
||||
- ready:接收 invoke。
|
||||
- draining:拒绝新调用,等待当前调用完成。
|
||||
- stopped:释放 Store 和 Instance。
|
||||
- failed:trap、fuel 耗尽、deadline 或 Runtime 错误后立即释放 Store 和 Instance。
|
||||
|
||||
故障后:
|
||||
|
||||
~~~text
|
||||
fault
|
||||
-> mark Actor failed
|
||||
-> reject queued calls
|
||||
-> drop Store + Instance
|
||||
-> optionally create a new Actor
|
||||
~~~
|
||||
|
||||
自动重建可配置,但 V1 不应隐藏故障或声称保留了旧内存。
|
||||
|
||||
reload 的最小流程:
|
||||
|
||||
1. 注册新的 Service Revision。
|
||||
2. start 新 Actor,确保 init 成功。
|
||||
3. 调用方切换到新的 ActorHandle。
|
||||
4. 调用方显式 stop 旧 Actor 并释放。
|
||||
|
||||
V1 不做请求级流量切换或状态迁移。
|
||||
|
||||
## 8. Sandbox 与资源限制
|
||||
|
||||
V1 Linker 注册受限 WASI P2 兼容实现,并按 Component 的真实 imports 注册已批准的
|
||||
平台能力。每个 Actor 都有独立
|
||||
`WasiCtx`,并且显式禁止 TCP、UDP、DNS 和所有 socket 地址。默认配置还保证无环境变量、
|
||||
参数、工作目录或预打开目录,stdin 关闭,stdout/stderr 丢弃。因此 Component 默认没有:
|
||||
|
||||
- 文件系统、环境变量、命令行参数或标准 I/O 继承。
|
||||
- 网络、DNS、socket 或原始系统调用。
|
||||
- 数据库、密钥、配置文件、宿主目录或宿主进程能力。
|
||||
|
||||
每个 Actor 必须显式设置:
|
||||
|
||||
| 限制 | 目的 |
|
||||
| --- | --- |
|
||||
| linear memory | 阻止无限内存增长。 |
|
||||
| tables / instances / memories | 限制 Wasm 对象数量。 |
|
||||
| fuel per invocation | 限制确定性的计算工作量。 |
|
||||
| epoch deadline | 限制 wall-clock 执行时间。 |
|
||||
| input/output bytes | 限制单次数据传输。 |
|
||||
| mailbox capacity | 实现背压,避免无限排队。 |
|
||||
|
||||
fuel 和 epoch 只约束 Wasm 执行。未来一旦加入 Host I/O,必须单独定义 timeout、取消和连接资源策略。
|
||||
|
||||
Runtime 的 epoch ticker 上限为 10 ms,且服务 deadline 不能小于该 tick,以避免配置使
|
||||
wall-clock deadline 失效。ActorHandle 持有 ticker 的生命周期引用,因此 Runtime 对象释放后,
|
||||
仍被调用方持有的 ActorHandle 不会失去 epoch 中断能力。
|
||||
|
||||
Wasm Store 隔离 Component 内存和可见 import,但不是完整 OS 隔离。V1 以第一方代码为前提;进程、Pod、gVisor 或 Kata 隔离在实际需要时再加入。
|
||||
|
||||
## 9. 最小代码结构
|
||||
|
||||
~~~text
|
||||
wit/
|
||||
service/
|
||||
package.wit
|
||||
clock/
|
||||
package.wit
|
||||
|
||||
crates/
|
||||
wasmeld-runtime/
|
||||
bindings.rs
|
||||
error.rs
|
||||
manifest.rs
|
||||
runtime.rs
|
||||
lib.rs
|
||||
|
||||
components/
|
||||
echo/
|
||||
counter/
|
||||
fault/
|
||||
spin/
|
||||
clock-probe/
|
||||
|
||||
tests/
|
||||
runtime_components.rs
|
||||
~~~
|
||||
|
||||
## 10. 验收测试
|
||||
|
||||
测试 Component:
|
||||
|
||||
- echo:原样返回输入,验证基础加载与调用。
|
||||
- counter:init 创建计数状态,每次 invoke 递增,验证内存常驻。
|
||||
- fault:显式 trap,验证旧 Store 会被丢弃且 Actor 可重建。
|
||||
- spin:不可被优化掉的 CPU 循环,验证 fuel 与 epoch 中断。
|
||||
- clock-probe:显式导入平台 monotonic-clock,验证能力按需 Link。
|
||||
- wasi-clock-probe:通过 Rust 标准库导入 WASI monotonic clock,验证注册时拒绝越权。
|
||||
|
||||
必须验证:
|
||||
|
||||
1. Component 只有在导出匹配预期 WIT world、且其 import 可由受限 WASI Linker 满足时才可启动。
|
||||
2. 同一 Actor 调用 counter 两次能观察到递增状态。
|
||||
3. stop、fault 或 Runtime 重启后,counter 从 init 重新开始。
|
||||
4. 多个并发 invoke 会严格经同一 Actor 串行执行。
|
||||
5. 无限循环会被 fuel 或 epoch deadline 终止。
|
||||
6. trap 后旧 Store 被丢弃,重建 Actor 后可再次调用。
|
||||
7. mailbox 满时立即出现 overload,不无限等待。
|
||||
8. memory.grow 超过限制会导致 Actor fault。
|
||||
9. reload 能启动新 revision 的 Actor;旧 Actor 由调用方显式 stop。
|
||||
|
||||
## 11. 后续扩展触发条件
|
||||
|
||||
| 明确需求 | 再引入的能力 |
|
||||
| --- | --- |
|
||||
| 需要网络入口 | HTTP/gRPC Adapter 与 API Gateway。 |
|
||||
| 需要调用宿主资源 | 独立 Host Capability 与 WIT import。 |
|
||||
| 需要跨重启保留状态 | 外部状态、checkpoint 与恢复协议。 |
|
||||
| 需要多个实例 | Actor 池、分片和放置策略。 |
|
||||
| 需要生产发布治理 | 制品签名、Registry、灰度与回滚。 |
|
||||
| 需要执行非第一方代码 | 额外进程或 OS sandbox。 |
|
||||
|
||||
## 12. 参考资料
|
||||
|
||||
- [WebAssembly Component Model](https://component-model.bytecodealliance.org/)
|
||||
- [Rust Components with wasm32-wasip2](https://component-model.bytecodealliance.org/language-support/building-a-simple-component/rust.html)
|
||||
- [Wasmtime](https://docs.wasmtime.dev/)
|
||||
- [Wasmtime interruption with fuel and epochs](https://docs.wasmtime.dev/examples-interrupting-wasm.html)
|
||||
@@ -0,0 +1,145 @@
|
||||
# WIT Package 管理
|
||||
|
||||
**状态:** Implemented
|
||||
|
||||
**Manifest 版本:** 1
|
||||
|
||||
**Lock 版本:** 1
|
||||
|
||||
## 边界
|
||||
|
||||
`wasmeld-console` 作为版本化 WIT Registry 和管理面。组件构建侧由 `wasmeld`
|
||||
解析依赖、锁定摘要并生成本地 `wit/deps`。`wit-bindgen` 始终只读取本地文件,
|
||||
Component 运行时不访问 Registry。
|
||||
|
||||
```text
|
||||
WIT Git tag
|
||||
|
|
||||
v
|
||||
Wasmeld Registry
|
||||
|
|
||||
v
|
||||
wasmeld -> wit.lock -> wit/deps -> wit-bindgen
|
||||
```
|
||||
|
||||
WIT 源码不按版本复制目录。每个 package 只维护当前源码,发布时用
|
||||
`service/v1.0.0`、`clock/v1.1.0` 等 Git tag 标记源码版本。Registry 保存由
|
||||
Component Model 工具链编码的标准二进制 WIT Package,并从制品本身解析包名、
|
||||
版本、直接依赖和 SHA-256。相同包名和版本不可覆盖。
|
||||
|
||||
## 组件目录
|
||||
|
||||
```text
|
||||
components/clock-probe/
|
||||
├── Cargo.toml
|
||||
├── wasmeld.toml
|
||||
├── wit.lock
|
||||
├── src/lib.rs
|
||||
└── wit/
|
||||
├── world.wit
|
||||
└── deps/ # 自动生成,不提交
|
||||
```
|
||||
|
||||
`wasmeld.toml` 当前只接受精确版本:
|
||||
|
||||
```toml
|
||||
schema_version = 1
|
||||
|
||||
[registry]
|
||||
url = "http://127.0.0.1:8080"
|
||||
|
||||
[dependencies]
|
||||
"wasmeld:clock" = "0.1.0"
|
||||
"wasmeld:service" = "0.1.0"
|
||||
|
||||
[replace."wasmeld:clock"]
|
||||
path = "../../wit/clock"
|
||||
|
||||
[replace."wasmeld:service@0.1.0"]
|
||||
path = "../../wit/service"
|
||||
```
|
||||
|
||||
精确版本 replace 的优先级高于包级 replace。只有根组件的 replace 生效;依赖包
|
||||
自身不能修改整个依赖图。替换目录声明的 WIT package 名称和版本必须与依赖完全
|
||||
相同。没有 replace 的直接依赖和传递依赖从 `[registry].url` 获取。
|
||||
|
||||
## 锁文件
|
||||
|
||||
`wit.lock` 必须提交:
|
||||
|
||||
```toml
|
||||
schema_version = 1
|
||||
|
||||
[[package]]
|
||||
name = "wasmeld:clock"
|
||||
version = "0.1.0"
|
||||
source = "registry+http://127.0.0.1:8080"
|
||||
sha256 = "..."
|
||||
replaced = false
|
||||
```
|
||||
|
||||
Registry 依赖的摘要覆盖完整二进制包;path replace 的摘要覆盖包目录下排序后的
|
||||
顶层 `.wit` 文件名和内容。`--locked` 在安装 `wit/deps` 前比较完整传递依赖图、
|
||||
来源和摘要,不一致时构建失败。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 将 WIT 源码编码成标准二进制 WIT Package
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit build wit/service
|
||||
|
||||
# 构建并发布;也可用 WASMELD_REGISTRY 设置 Registry
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit publish wit/service --registry http://127.0.0.1:8080
|
||||
|
||||
# 获取依赖并更新 wit.lock
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit fetch --manifest components/clock-probe/wasmeld.toml
|
||||
|
||||
# 验证锁文件,不更新
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit fetch --manifest components/clock-probe/wasmeld.toml --locked
|
||||
|
||||
# 查看实际来源
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit graph --manifest components/clock-probe/wasmeld.toml
|
||||
|
||||
# 添加或删除本地替换
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit replace wasmeld:clock@0.1.0 \
|
||||
--path ../../wit/clock \
|
||||
--manifest components/clock-probe/wasmeld.toml
|
||||
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
wit replace wasmeld:clock@0.1.0 \
|
||||
--drop \
|
||||
--manifest components/clock-probe/wasmeld.toml
|
||||
```
|
||||
|
||||
`pack` 自动执行相同的同步步骤:
|
||||
|
||||
```bash
|
||||
cargo run -p wasmeld-package --bin wasmeld -- \
|
||||
pack components/clock-probe/Cargo.toml --locked
|
||||
```
|
||||
|
||||
## Registry API
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/v1/wit/packages` | 列出全部包版本 |
|
||||
| `POST` | `/api/v1/wit/packages` | multipart 发布 `package.wasm` |
|
||||
| `GET` | `/api/v1/wit/packages/{namespace}/{name}/{version}` | 查询元数据 |
|
||||
| `GET` | `/api/v1/wit/packages/{namespace}/{name}/{version}/content` | 下载二进制包 |
|
||||
|
||||
Console 管理面提供同一 Registry 的列表、发布和下载操作。Registry 元数据不接受
|
||||
客户端填写,服务端以二进制包解析结果为准。
|
||||
|
||||
## 当前边界
|
||||
|
||||
- 依赖版本必须是精确 SemVer,不解析版本范围。
|
||||
- Registry 使用本地文件系统持久化,默认目录为 `var/wasmeld/wit-packages`。
|
||||
- Registry 不保存 Git 仓库或源码;Git tag 与 commit 由源码仓库管理。
|
||||
- 本地 `replace` 只支持目录,不支持 Git replace。
|
||||
- 暂不包含认证、权限、废弃标记和垃圾回收。
|
||||
Reference in New Issue
Block a user