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

Host API v0.1

状态:正式支持(Supported)。生产宿主通过封闭版本握手、两类绑定边界校验、完整兼容性测试和发布门禁后,公布 0.1: supported

阅读方式

本页只负责版本级约定和模块索引。每个 Host API 模块都有独立文档,方法、事件、应用场景和用法示例以模块文档为准。

示例统一使用逻辑 JS 绑定对象 host

const host = await appist.host({ apiVersion: '0.1' });

该入口由 @appist/host-api 提供,并在密封 guest 内自动连接宿主 transport;应用不能自行伪造或替换 transport。原生 WASM 示例使用 C 风格伪代码表达同一逻辑契约;ptrlen 和线协议编码只属于具体绑定,不属于 Host API 语义。

设计不变量

v0.1 以“最小且闭合的能力基线”为目标,而不是追求模块或便利方法最多。所有成员共同遵守:

  1. 版本先于成员:应用与宿主先协商精确版本;一个版本是封闭整体,不存在未命名的“部分支持”。
  2. L1 只承载宿主独占能力:只有真实资源、外部权限、系统集成或受控平台服务进入 Host API;可由 guest 基于既有能力等价实现的领域逻辑留在 L2。
  3. 没有证据就不冻结:缺少具体应用、端到端原型或边界结论的内容只进入非正式提议,不预占正式名字。
  4. 一种输入只有一种可观察语义:结构封闭,默认值、顺序、并发、部分结果、幂等性和终止条件必须可实现为一致性测试。
  5. 授权沿资源来源闭合:直接方法由 manifest 声明;后续操作只能从类型化句柄的资源、权限、来源与生命周期派生,不能靠无类型 token 旁路。
  6. 容量、时间与失败都有边界:数据、队列、活动资源和调用速率都受版本预算约束;长等待显式可取消,背压和溢出不得静默。
  7. 事实与策略分离:channel、来源、对端、宿主中介、物理效果和授权事实如实记录,信任档位由同一纯规则计算,不为期望结果反填事实。
  8. 逻辑契约跨绑定一致:JS 与原生 WASM 使用同一方法、事件、错误和生命周期;指针、长度与编码只属于 ABI,不产生另一套 API。

任何成员若不能满足这些不变量,应从 v0.1 移回非正式提议,而不是靠含糊措辞保留。

正式模块

v0.1 收录 9 个 L1 模块。这里的“收录”表示接口边界明确、两类绑定均已实现,并受发布端到端门禁保护:

模块 典型场景 参考文档
呈现 canvas 2D 绘制、软件像素帧、文本度量 canvas
呈现 asset 读取包内资源、注册包内字体 asset
呈现 playback 播放应用生成的 PCM 音频 playback
输入 input 指针、键盘、滚轮和焦点 input
输入 ime 系统输入法和合成文本 ime
输入 capture 摄像头帧和麦克风采样 capture
数据 store 应用作用域持久化 KV store
数据 clipboard 系统剪贴板读写 clipboard
平台集成 env 粗化环境快照和变化通知 env

八族只是分析工具,不要求首版覆盖每一族。不在上表中的现有方法不构成 v0.1 承诺;候选能力见非正式提议

版本声明与协商

应用 manifest 必须声明精确版本、直接授权的方法、需要接收的事件,以及应用成立所需的最低预算:

{
  "apiVersion": "0.1",
  "canvases": { "main": { "mode": "draw" } },
  "capabilities": ["host:canvas.draw"],
  "events": ["canvas.frame", "canvas.resize"],
  "apiLimits": {
    "canvas.maxBatchOperations": 4096
  }
}

capabilities 只列 authorization: declared 的方法。由句柄权限派生的方法(例如 capture.stop)不得再次声明;拥有有效句柄就必须能使用该句柄允许的操作。events 是兼容性和事件路由声明,不是另一份资源授权。使用 canvasinputime 时,canvases 必须把每个绘图面固定为 drawpixels;使用 asset 时,assets 必须把稳定的 assetId 映射到规范化包内路径。包内路径由 / 分隔,每段只能含 ASCII 字母、数字、._~-,且不能是 ...;不接受百分号编码或由 URL 解析器再次规范化的别名。两张表都由发布器校验并作为整棵结构不可变的启动请求交给宿主,guest 不能在运行时扩充或改写。

宿主在 /_keel/capabilities.json 公布以下封闭结构;数值都是宿主对单个应用提供的上限:

HostCapabilities {
  schemaVersion: 1
  runtime: "web" | "native"
  hostApi: {
    versions: {
      "0.1"?: {
        status: "supported"
        limits: { [K in V01LimitName]: u32[1..4294967295] }
      }
    }
  }
}

V01LimitName 恰好是机器可读 catalog.json 顶层 limits 与各模块 limits 的并集;每个键必须出现一次,不允许未知键。每个值是宿主对单个应用保证可用的预算;应用 apiLimits 写成立所需的最低保证,因此要求值不高于宿主值。公布 0.1: supported 表示实现本版本全部 27 个方法和 15 个事件,不能用成员列表把它降格成“部分 v0.1”。暂未完成兼容性测试的宿主直接省略 0.1draftrelease-candidate 是文档/实现流程状态,不进入生产 advertisement。

发布器和启动器使用同一套校验,逐项确认:

  1. 精确的 apiVersion 处于 supported
  2. 所有声明方法和事件都属于 v0.1 catalog;
  3. 每个 apiLimits 要求不高于宿主公布值;
  4. 方法与事件声明符合来源依赖和句柄授权规则;
  5. 任一条件不成立时,在 guest 执行前以 version-mismatch 失败。

apiVersion 与应用版本、contractVersion、二进制线协议版本和运行时构建哈希相互独立。

兼容性

  • 只有明确标为 extensible 的结构可以增加可忽略字段;v0.1 当前定义的参数、返回值和事件载荷都是封闭结构。
  • 新增模块、方法、必填参数或新的失败前提进入后续版本。
  • 删除方法、改变参数含义、收紧既有合法输入或改变可观察副作用属于不兼容变化。
  • 安全修复可以拒绝原本就违反预算、schema 或权限要求的调用。
  • 0.x 不承诺 1.0 级长期稳定,但承诺版本内语义稳定。

共享类型

Bytes       有明确上限的小块字节
ByteSource  大块输入;JS 绑定 Uint8Array,原生 guest 绑定已校验内存区域
ByteSink    大块输出;方法返回实际 written 与该方法定义的完整性信息
Event       经始终存在的类型化事件传输送达
Handle<Resource, Rights, Provenance, Lifetime>
CallOptions { signal?: CancellationSignal }

方法在同步进入绑定时取得 ByteSource 的不可变快照;调用返回 Promise 后 guest 修改原缓冲不得改变本次输入。实现只有在能证明调用期间 guest 不可能并发修改时才能用借用代替复制。ByteSink 从同步进入绑定到调用结束由该调用独占;这段时间 guest 不得读写或交给第二个调用。宿主只修改成员页定义的 written 区间,失败或未写区域保持调用前字节不变。JS 绑定与原生绑定都必须执行同一所有权规则。

句柄是不透明值。每次使用都必须验证资源类型、权限、来源和有效期;宿主主动失效句柄时,若应用声明了相应终止事件,就按成员页保证发送。v0.1 不允许把一个模块产生的无类型 token 交给另一模块绕过权限判断。

字符串都是合法 Unicode 标量序列;标注为 UTF-8 字节长度的预算在编码后计算。整数不允许溢出,浮点数必须有限。数组、映射和嵌套结构都受宿主公布预算限制。

方法授权与事件来源

方法只有两种授权方式:

授权 manifest 调用条件
declared 必须列出完整 capability 发布和启动均通过声明检查
handle-derived 禁止再次列 capability 传入同一应用持有、类型/权限/来源/生命周期均匹配的有效句柄

事件必须列在 manifest 的 events 中,并按来源分两类。成员页所写的送达、次数和排序保证都以该事件已经声明为前提;省略声明只会抑制该事件的交付,不改变底层资源状态,也不授予宿主发送未声明事件的权力:

  • passive:只在应用可见、相关绘图面属于本应用且事件声明存在时发送;适用于 canvas.*input.*env.change
  • handle:只有资源创建方法成功返回句柄后才发送,载荷必须带该句柄;关闭、撤销或终止后只允许再发送一次终止事件。

handle 事件继承 sourceMethods 可达方法中的最高档位;passive 事件继承本模块直接授权方法的最高档位。应用最终档位同时计入它声明的 passive 事件;在本版中这些事件均为 green。事件不能借“没有独立 capability”绕过定档。

事件传输在 guest 启动前由宿主建立,不要求 guest 预先同步注册回调。队列有界;只有成员页明确允许合并的事件可以合并。不得把事件声明当成获得摄像头、音频输出或 IME 会话的授权。

同一应用的统一事件传输按入队顺序 FIFO 交付;不同系统源同时发生时只保证各成员页明示的先后关系。runtime.maxQueuedEvents 是每个应用尚未交付事件的总上限。队列满时,宿主先按成员页规则合并可合并事件;仍无法容纳一个不可合并事件时必须以 event-overflow 终止当前 guest 会话,不能静默丢弃、覆盖旧事件或无限增长内存。该终止是运行时故障,不伪装成某次 Host API 调用的 HostError

错误、取消与预算

失败统一使用:

HostError {
  method: string
  code: string
  retryable: boolean
}

method 使用不带 host: 的规范方法名,例如 capture.frame;句柄派生方法也有稳定 method 名,但这不把它变成 manifest capability。相同 code 在不同方法中可以有不同恢复动作,guest 必须同时按 method 与 code 解释。

所有 27 个方法共有以下绑定级错误:invalid-argument 表示 guest 请求的结构、类型、编码或内存区域不符合契约;platform-unavailable 表示完成调用所需的平台基础设施暂时不可用;internal 表示宿主内部不变量失败且不得泄漏实现细节;protocol-error 表示绑定拒绝了宿主返回的成功值或事件,因为它不符合封闭 schema;transport-error 表示绑定无法可靠地完成与宿主的传输。版本选择失败单独使用 version-mismatch,不冒充某个方法的业务失败。各成员页的“特有错误”与这些公共错误取并集,构成该方法的完整封闭 code 集合;未列出的 code 不得穿过绑定。event-overflow 是会话终止原因,不是 HostError.code

  • 用户关闭系统选择或确认界面返回成员页定义的空值,不抛错;取消结果不得与成功混用。
  • 权限拒绝使用 denied,版本冲突使用 version-mismatch,预算超限使用 limit-exceeded;底层临时不可用若已有成员特有码(例如 device-unavailable)必须使用特有码,不能一律抹平成公共 platform-unavailable
  • 只有成员页标为“可取消”的调用接受末尾 CallOptions。触发取消后以 cancelled 结束,不得返回部分句柄;已经交给外部系统的副作用不承诺回滚。
  • 所有其他调用必须在其单次预算内完成,不得隐式转成长任务。
  • 预算同时覆盖单次容量、应用活跃总量和时间窗速率;预算键和单位属于版本契约,具体数值由宿主公布。

应用声明的最低预算超过宿主上限时必须在 guest 启动前失败,不能运行后再靠随机的 limit-exceeded 暴露不兼容。

JS 绑定在启动握手后以 host.limits 暴露宿主公布的完整预算表。原生 WASM v0.1 绑定不复制第二份运行时预算表:原生应用必须把成立所需的最低值全部写入 manifest,并只按这些最低值分配缓冲和安排队列;宿主仍在 guest 启动前逐项校验。原生绑定不能据此假定宿主还提供了 manifest 未声明的更高余量。

CancellationSignal 是当前 guest 内的不可序列化取消状态。调用开始前已取消则不得产生任何副作用;调用中取消只影响绑定它的调用,不自动关闭已有句柄。JS 绑定接受 AbortSignal,原生绑定接受运行时 cancel token,两者映射到同一逻辑语义。

预算值在 JSON 中都是 [1, 4294967295] 范围内的 u32,避免逻辑类型与 JS/原生绑定出现不同上限。名称以 Bytes 结尾时单位是 8-bit byte;以 Ms 结尾时单位是毫秒;以 Frames 结尾时使用对应模块定义的媒体帧;PerSecond/PerMinute 表示任意滚动 1 秒/60 秒窗口内开始且获准执行的次数;maxStreamsmaxSessions 等表示同一应用同时活动的资源数;runtime.maxQueuedEvents 表示统一事件队列中尚未交付的事件数。除成员页明示的最低持续保证(当前只有 store.listSnapshotIdleMs)外,预算都是不得超过的容量/次数上限;数值越大都表示宿主提供的能力不更弱。方法在校验失败前不得消耗速率计数,开始执行后无论成功、取消或外部失败都计一次。

固定信任档位

v0.1 的方法档位由随规范发布的 catalog.json 策略事实计算,结果只能是 greenlimeyellow。成员页必须写固定结果,禁止“满足某些条件时为 green”之类的条件档位:那些条件本身就是符合 v0.1 的强制要求;做不到的实现不得宣称支持该方法。

应用的最终档位取所有直接声明方法、它们可产生句柄所能到达方法,以及已声明 passive 事件中的最高档。handle 事件继承其来源方法的最高档,不另行改变结果。

L0 运行时原语

L0 不参与 capability 声明与信任定档:

原语 语义
log(level, ByteSource) 有长度、速率和控制字符过滤的诊断日志
host_call 发起一次类型化 Host API 调用
send_batch 批量提交运行时消息
typed event transport 将 L1 事件送入 guest;不承载资源授权

nowrandom 尚未完成 v0.1 所需的实现和验证,不属于本版本。

明确不提供

v0.1 故意不提供原始 DOM、Canvas/WebGL/WebGPU context、任意 URL 资源加载、外部网络、平台身份、打印、像素读回、屏幕捕获、任意文件路径、原始 socket、WebRTC、P2P、bearer token、全局用户 ID、email、动态代码加载或任意 guest 着色器。

scene、数据库、codec、聊天会话等可在 guest 实现的领域模型属于 L2。缺席是 v0.1 安全契约的一部分,不能作为“遗漏”在补丁版本中加入。

与当前实现的关系

  • 当前各模块普遍使用的 version: "1" 是契约描述版本,不是 Host API v0.1。
  • 当前 mediaaudiotext 等旧名仍需迁移到 captureplaybackime
  • netidentityprint 的候选契约已移回非正式提议:当前生产宿主分别缺少地址绑定的网络代理、appId 作用域的 nonce 身份流程和隔离 PDF 解析器。
  • 当前 identity.currentbilling.*ai.chatanalytics.track 不在 v0.1。
  • 当前 asset.decodeBytes({fileToken}) 组合路径必须先完成安全整改。
  • 当前两类 guest 的事件通路必须收敛到统一的类型化事件传输。

宿主只有在名称、逻辑签名、版本握手、策略和行为全部符合本版本文档时,才可以公布 apiVersion: "0.1"

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指南构建交互式绘图应用指南构建自绘文本编辑器指南管理媒体采集与播放指南组织本地数据与同步指南