# Ember applet API and manifest reference

This is the public reference for building an Ember applet. For packaging tools,
JSON schemas, TypeScript declarations, and the WASI backend template, use the
repository's `docs/applets` author kit.

## Start here

An applet is a signed `.ember-applet` ZIP containing static web files and an
`applet.json` manifest. Its HTML entry page must load Ember's SDK before its
own script:

```html
<script src="/_ember/sdk.js"></script>
<script src="app.js" defer></script>
```

The SDK supplies a read-only `window.ember` object. Applets cannot use Tauri,
the host filesystem, environment variables, raw sockets, native processes, or
remote and inline scripts. Bundle all required assets in the package.

Use manifest version 1 for an offline applet with optional JSON storage. Use
version 2 for every other capability. Version 2 requires a `capabilities`
object, even when it is empty. It does not use the version 1 `permissions`
array.

```json
{
  "manifestVersion": 2,
  "id": "com.example.my-applet",
  "name": "My applet",
  "description": "A small Ember applet.",
  "version": "1.0.0",
  "entry": "index.html",
  "capabilities": { "storage": {} }
}
```

`id` uses reverse-domain form and stays stable across updates. `version` uses
`major.minor.patch`. `entry` names a packaged HTML file. Supported icons are
`applet`, `notes`, `focus`, `weather`, and `calendar`.

## API and required capability

| API | Manifest declaration | What it does |
| --- | --- | --- |
| `ember.storage` | v1: `permissions: ["storage"]`; v2: `capabilities.storage` | Applet-scoped JSON storage with `get`, `set`, `delete`, `keys`, and `clear`. Limit: 5 MB. |
| `ember.settings.get()` | `settings` manifest field | Reads only preferences the applet declared. |
| `ember.http.request()` | `capabilities.network.requests` | Sends native HTTPS requests that bypass CORS. Declare each origin, method, and applet-controlled header. |
| Browser `fetch` | `network.browserFetch` | Allows declared browser destinations. CORS still applies. |
| `WebSocket` | `network.websockets` | Allows declared `wss://` destinations. |
| Remote images and media | `network.images`, `network.media` | Adds declared origins to the applet's content policy. |
| `ember.secrets` | `capabilities.credentials.slots` | Prompts for, checks, or deletes OS-backed secrets. Applet code never receives plaintext. |
| `ember.auth.authorize()` | `auth`, an external-navigation origin, token HTTP grant, and credential slot | Runs OAuth authorization-code flow with PKCE and stores the access token in the declared slot. |
| `ember.external.openUrl()` | `externalNavigation.origins` | Opens a declared HTTPS URL in the system browser. |
| `ember.window` | `window.fullscreen` | Reads or changes fullscreen state for the applet window. |
| `ember.terminal` | `terminal` | Opens an in-memory guest terminal with built-in commands only. No host files, process execution, or network access. |
| `ember.backend` | `backend` plus any needed capabilities | Invokes a WASI component or subscribes to its events. |
| `ember.extension` | `extensions` | Calls exactly granted methods from a trusted, installed native extension. |

All SDK calls can reject with `ember.EmberApiError`. Check `error.code` for
policy and request errors such as `permission_denied`, `origin_not_allowed`,
`method_not_allowed`, `header_not_allowed`, `invalid_request`, `timeout`,
`rate_limited`, and `backend_error`.

## Networking and credentials

Origins must be HTTPS, except WebSockets which use WSS. Wildcards replace only
the leftmost subdomain label. Paths, query strings, credentials, HTTP, and
unencrypted WebSockets are not allowed in a declaration.

Native HTTP allows `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `HEAD`. Request
bodies can be text, JSON, Base64, or form data. Ember limits an applet to 120
native requests per minute, 2 MB request bodies, 10 MB responses, 30-second
timeouts, and five validated redirects. It blocks local and private network
addresses and has no cookie jar.

Credential slots attach a protected value as a header, form field, or query
parameter to their declared HTTPS targets. Name each used slot in the request's
`credentials` array. Keep publisher-wide service secrets on a server you
control. A desktop package cannot keep one secret private.

## Backends, workers, and background activity

A backend is a WASI component with `runtime: "wasiComponent"`, a packaged
`.wasm` entry, interface version 1, a lifecycle, and 16 to 256 MiB of memory.
It has no inherited environment, filesystem, raw network, or process access.
The `background` lifecycle also requires `capabilities.background`.

Declare dedicated or blob workers under `capabilities.workers`. Remote worker
code remains forbidden.

## Signing, package limits, and updates

Create an Ed25519 key outside the applet folder, then package from the Ember
repository root:

```sh
npm run applet:key -- ../publisher-key.pem
npm run applet:pack -- path/to/my-applet ../publisher-key.pem
```

The packager creates the integrity and signature files. Do not edit the archive
after packaging. Packages and their uncompressed contents may not exceed 25 MB.
They may contain at most 512 files, with an 8 MB limit per file. Ember verifies
paths, hashes, signature, manifest, and package limits before installation.

Ember pins the first publisher key used for an applet ID. Every update must use
that same private key and receives another capability review. Install the exact
generated `.ember-applet` in Ember's Library and test it before publishing.
