# Ember extensions

An Ember extension is a trusted native package installed into Ember. It is not sandboxed. An extension can run native code and access the user's computer and network. Ember shows this warning before installation.

Extensions use the `.ember-extension` ZIP format. Version 1 is a local-install format. Ember validates the archive paths and manifest, but it does not establish publisher trust. Restart Ember after installing, enabling, disabling, or replacing an extension.

```text
wireguard-proxy.ember-extension
|-- extension.json
`-- bin/
    `-- wireguard-proxy.exe
```

## Manifest

`extension.json` is required.

```json
{
  "manifestVersion": 1,
  "id": "dev.ember.wireguard",
  "name": "WireGuard proxy",
  "description": "Routes approved applet requests through a WireGuard tunnel.",
  "version": "0.1.0",
  "entry": "bin/wireguard-proxy.exe",
  "apis": [
    { "namespace": "network.wireguard", "methods": ["status", "proxyFetch"] }
  ]
}
```

`id` uses Ember's reverse-domain identifier format. `entry` must name a file inside the archive. Each declared API names the namespace Ember may make available to applets. Declaring an API does not grant it to an applet.

## Applet grants and calls

An applet requests exact namespaces and methods in its version 2 manifest:

```json
{
  "capabilities": {
    "extensions": [{
      "id": "dev.ember.wireguard",
      "namespace": "network.wireguard",
      "methods": ["status", "proxyFetch"]
    }]
  }
}
```

Ember includes approved methods beneath `window.ember.extension`. The full `extension` prefix is deliberate. It tells an applet author that this is not an Ember built-in API:

```js
const result = await ember.extension.network.wireguard.proxyFetch({ url: "https://example.com" });
```

At call time Ember checks the signed applet grant, then checks that the named extension is installed, enabled, and declares the API and method. Ember starts the extension entry as a child process, writes one JSON request to stdin, and reads one JSON response from stdout. The request has `appletId`, `namespace`, `method`, and `payload` fields. The extension must return exit code zero and print one JSON value.

Extensions may return either a direct JSON value or a result wrapper. A wrapper with `{ "ok": true, "value": ... }` resolves to its value. A wrapper with `{ "ok": false, "error": { "code": "...", "message": "..." } }` throws `EmberApiError` in the applet.

Create a package with `npm run extension:pack -- <extension-folder>`. Version 1 packages are intentionally local and unsigned. The install screen states that extensions run unsandboxed native code.
