Appearance
Lifecycle and resources
An extension is { manifest, activate(context) }. Each host instance owns its registrations and resources. Creating a second host does not share registries with the first.
Manifest and grants
The manifest has id, version, apiVersion: 1 and capabilities:
ts
const capabilities = [
'engine.commands',
'vtt.layers',
'vtt.tools',
'systems',
'ui.panels',
'ui.surfaces',
'presentation',
'events',
] as const;host.load(extension, grants?) can restrict the capabilities granted to that installation. Registrations require the corresponding requested and granted capability. Manifest IDs use lowercase namespaces such as example.beacon; versions use major.minor.patch with an optional prerelease suffix. Duplicate IDs fail. Contribution IDs must belong to the manifest's namespace, for example example.beacon/overlay.
Registrations remain invisible to host consumers until activation succeeds. Execution is available once host.load resolves; do not run a registered command from inside its own activation.
These checks constrain the supported host API. Bundled extension code is trusted JavaScript in this milestone, with the process's ambient privileges. This API does not provide CPU, network, filesystem or memory isolation. Do not load arbitrary third-party code into the authority process.
Own every resource
Registration and event subscriptions are owned automatically. Attach any other resource to context.resources.add(dispose). Its return value releases that resource early and is safe to call again.
ts
activate(context) {
const timer = setInterval(() => context.vtt.invalidate(), 1000);
context.resources.add(() => clearInterval(timer));
// The abort signal also scopes external asynchronous work.
// Pass context.signal to fetch or another signal-aware adapter.
}If activate returns a cleanup function, the host owns it too. Explicitly release adapter-created listeners, timers, subscriptions and rendering assets; registering a layer does not discover resources created outside the host.
Failure and unload
When activation fails, the host rolls back resources and registrations already created by that activation. On unload, it aborts the signal, removes registrations and cleans resources in reverse acquisition order. One cleanup failure must not prevent remaining resources from being released. Use the host's onError(error, extensionId) callback to observe failures.
Concurrent lifecycle requests for the same extension are rejected rather than silently racing activation and teardown. Await host.unload(id) before loading that extension again. host.dispose() tears down the instance; a renderer or application must also release its own host.subscribe disposer.
Abort is cooperative: an asynchronous task must observe its signal and avoid publishing stale output. The host cannot forcibly stop arbitrary trusted JavaScript. Durable server requests still need their own authority and idempotency controls.
Data and upgrades
Disable/unload releases executable resources. It is not a content deletion or backup operation. Persisted extension state, schema upgrades, staged updates and distribution are still to be implemented and accepted. Do not invent a storage API or promise that local closure state survives reload.