v0.1首个正式支持的版本化规范查看版本范围

net 非正式提议

本页不属于任何正式 Host API 版本,名字、边界和签名均未冻结。现有浏览器与 shell 无法证明 DNS 检查结果和实际 HTTPS 连接地址一致;在地址绑定的平台代理完成端到端验证前,本提议不得进入 manifest、SDK 或运行时能力表。

net 让 guest 通过平台代理发起受 manifest 限制的请求—响应。guest 不获得浏览器 fetch、socket、DNS、真实连接、代理凭据或任意重定向权。

基本示例

const response = await host.net.fetch({
  target: 'catalog-api',
  path: '/v1/catalog',
  method: 'GET',
  headers: { accept: 'application/json' },
});

if (response.status !== 200) throw new Error(`HTTP ${response.status}`);
const catalog = JSON.parse(new TextDecoder().decode(response.body));

和浏览器 Fetch 一样,HTTP 404/500 是正常响应状态,不自动变成 HostError;代理失败、策略拒绝和预算失败才抛 HostError。

适用场景

  • 调用应用在 manifest 中公开声明的 HTTPS API。
  • 获取有明确大小上限的 JSON 或二进制响应。
  • 由平台代理执行目的地校验、地址过滤和审计。

WebSocket、WebTransport、WebRTC、P2P、局域网发现、任意 TCP/UDP 和无限流不在本提议内。

L1 边界

宿主独占的是外部网络、目的地中介、SSRF 防护与审计。guest 无法只靠计算访问远端;重试、缓存、同步协议、REST 客户端和业务数据模型都能基于有界请求在 guest 中实现,不进入 L1。

manifest 与能力声明

{
  "capabilities": ["host:net.fetch"],
  "network": {
    "targets": {
      "catalog-api": {
        "origin": "https://api.example.com",
        "pathPrefix": "/v1/",
        "methods": ["GET", "POST"]
      }
    }
  }
}

target 是 manifest 键,不是 host 字符串。target 键使用 [a-z][a-z0-9-]{0,62}origin 必须是没有 userinfo、path、query 或 fragment 的 HTTPS origin,host 使用小写 ASCII DNS 名而非 IP 字面量,候选契约只允许默认 443 端口。pathPrefix 必须以 / 开头和结尾。发布器解析并规范化配置;运行时按 target 查表,不做字符串 host 匹配。

Reference

net.fetch(request, options?) -> NetResponse

NetRequest {
  target: manifest-network-target
  path: string
  method: "GET" | "HEAD" | "POST" | "PUT" | "PATCH" | "DELETE"
  headers?: HeaderMap
  body?: Bytes
  timeoutMs?: u32
}

NetResponse {
  status: u16
  headers: HeaderMap
  body: Bytes
}

path 必须以 / 开头,可含 query,不得含 scheme、authority、fragment、反斜杠、控制字符、非法 percent encoding,或 percent-decode 后为 ./.. 的 path segment。宿主以 UTF-8 percent-decode 每个 segment 一次,拒绝结果中的 /\、U+0000 和第二层 percent encoding,再以规范 percent-encoding 重建 URL。规范化后的 path component 必须以声明的 pathPrefix 为 segment 前缀;query 不参与前缀判断。

HeaderMap 是小写 ASCII 名到单个可见 ASCII 值(U+0020–U+007E)的映射;值去除首尾空格后不得为空,不允许重复名、折行或控制字符。候选契约请求头白名单仅为 acceptcontent-typeauthorizationif-matchif-none-match;响应头白名单仅为 content-typecontent-lengthetaglast-modifiedcache-control。其他头不进入 guest。

GETHEAD 禁止 body 与 content-type;其他方法的 body 可省略。timeoutMs 必须位于 [1, net.maxTimeoutMs],省略时等于 net.maxTimeoutMs。只向 guest 返回最终状态码 [200, 599];1xx 不构成响应,101 协议升级不受支持。HEAD 以及 204、205、304 响应的 body 固定为空。若上游使用内容编码,代理必须在进入 guest 前完成解码、移除 content-encoding,并让 content-length 与返回 Bytes 一致。

POST 与取消示例

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5_000);

try {
  const response = await host.net.fetch({
    target: 'catalog-api',
    path: '/v1/items',
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: new TextEncoder().encode(JSON.stringify({ name: 'demo' })),
    timeoutMs: 5_000,
  }, { signal: controller.signal });
  handleResponse(response);
} finally {
  clearTimeout(timeout);
}

取消表示 guest 不再需要结果;宿主应尽快停止上游请求并以 cancelled 结束本地调用,但不能承诺远端服务器没有处理已发送的数据。

原生 WASM 示例

NetRequest request = {
  .target = "catalog-api",
  .path = "/v1/catalog",
  .method = NET_GET,
  .headers = accept_json_headers(),
};
NetResponse response = host_net_fetch(request, byte_sink(body, sizeof(body)), cancel_token);
if (response.status == 200) decode_catalog(body, response.written);

响应正文整体不得超过 net.maxResponseBytes。超过时整次调用以 response-too-large 失败;原生绑定先从应用声明的最低预算分配接收区,绝不能截断正文却保留 200 状态。

重定向与身份

  • 候选契约从不跟随重定向。3xx 作为普通 HTTP 状态返回,Location 不在响应头白名单内。
  • 候选契约不附加 ambient Cookie、平台登录凭据或其他 target secret。guest 可以显式提供自己持有的 authorization 值;宿主只把它送给本次已校验 target,且日志、错误与遥测不得记录该值。需要由宿主代持服务凭据的能力必须在后续版本单独评审。
  • Set-Cookie 被丢弃,不建立 guest 可观察或不可观察的 cookie jar。
  • URL 用户名/密码、IP 字面量、非 443 端口和解析到非公共地址的 target 在发布时或调用前拒绝;宿主在建连地址变化时重新执行公共地址检查。

安全与预算

  • 信任档位:lime;peer 为 manifest 声明的 third-party,mediation 为 brokered。
  • 候选契约预算键为 net.maxUrlBytesnet.maxHeaderBytesnet.maxRequestBytesnet.maxResponseBytesnet.maxConcurrentRequestsnet.requestsPerMinutenet.maxTimeoutMs
  • 响应头只返回白名单;移除网络拓扑、代理实现和身份凭据。
  • 日志只记录受限目的地和统计信息,不记录正文或秘密请求头。
  • 宿主必须防止 SSRF,包括解析后的地址类别检查和重定向再校验。

错误与测试

特有错误包括 destination-deniedinvalid-requesttimeoutcancelledresponse-too-largeupstream-unavailable。一致性测试至少覆盖未知 target、单/双 percent encoding、编码斜杠、dot segment 与 path-prefix 绕过、Unicode 域名、重定向不跟随、DNS 重绑定、头过滤、无 ambient 凭据、404 正常返回、取消竞态和响应上限。

Host API 版本与分层概览Host API v0.1版本非正式提议提议canvas API呈现canvas.getInfo()方法canvas.draw()方法canvas.present()方法canvas.measureText()方法canvas.frame event事件canvas.resize event事件asset API呈现asset.read()方法asset.loadFont()方法playback API呈现playback.open()方法playback.write()方法playback.stop()方法playback.ready event事件playback.ended event事件input API输入input.focus()方法input.pointer event事件input.key event事件input.wheel event事件input.focusChange event事件ime API输入ime.openSession()方法ime.updateState()方法ime.updateGeometry()方法ime.closeSession()方法ime.textUpdate event事件ime.composition event事件ime.formatUpdate event事件ime.characterBoundsRequest event事件ime.ended event事件capture API输入capture.requestCamera()方法capture.requestMic()方法capture.frame()方法capture.readAudio()方法capture.stop()方法capture.ended event事件store API数据store.get()方法store.put()方法store.delete()方法store.list()方法store.deletePrefix()方法clipboard API数据clipboard.readText()方法clipboard.writeText()方法env API平台集成env.snapshot()方法env.change event事件开始调用 Host API指南构建交互式绘图应用指南构建自绘文本编辑器指南管理媒体采集与播放指南组织本地数据与同步指南