Browse docsPocket Applets

Pocket Applets

A Pocket Applet is a Pocket app that a launcher runs on behalf of the system. Pocket Shell, the launcher on the M5Stack Tab5 and the Nintendo 3DS, runs Hacker News, GitHub, X, YouTube, Discord, Notes and the other applets of pocket-nexus/applets, and a launcher is an app that pairs, installs and switches between them. An applet declares the applet section of the app contract and accepts rules a general app does not.

The rules exist for three reasons:

  • One source runs on every launcher host. An applet draws on one touch surface that admits 320×240 and lays itself out by the size it is given.
  • The system agent can read and operate it. The UI is Solid on the PocketJS components, so the agent's text capture and played input reach every control.
  • A generated applet is admitted by checks alone. A person or a model can write one, and the build proves what it may touch.

Where applets run

An applet runs in a QuickJS guest on the PocketJS runtime. Its size limits are small enough for a microcontroller: the same applet source runs on a Tab5's ESP32-P4 and on a handheld's screen.

Host Processor Surface
M5Stack Tab5 ESP32-P4 microcontroller Adaptive, 640×360 logical on a 1280×720 panel
Nintendo 3DS ARM11 320×240 touch screen
Omarchy Pocket (Anbernic RG DS Plus) Rockchip RK3568 A window on the 512×384 logical touch screen
PSP MIPS R4000 480×272, full screen, read by the d-pad

The PSP host runs no worker yet, so an applet that reaches the network does not build for it.

The ESP-IDF page describes the components the Tab5's firmware is built from.

The app contract section

"applet": {
  "version": 1,
  "summary": "Your timeline, on a small screen.",
  "services": ["x", "glyphs"],
  "store": { "maxBytes": 65536 }
}
Field Meaning
version 1. A host refuses a version it does not know.
summary One sentence of at most 120 characters, shown where the applet is offered.
services Companion services the applet's worker fetches over net.http, at most 8. A service the applet does not list answers 403.
hosts Host names outside the companion that the worker fetches, at most 16. A host that installs applets from a store (Omarchy Pocket) shows them before an install and refuses any other.
store Persistent state, required for data.fs. maxBytes is at most 4 MiB.

What an applet accepts

The rules run when a plan resolves and again when the build runs, so an applet that breaks one does not build on any target.

Rule Limit
Guest bundle 640 KiB
Pak 3 MiB
Title 24 characters
Framework solid, with no native model and no companions of its own
Capabilities An allowlist: text, buttons, left stick, the surface and touch, net.http, ui.physics, audio.pcm, media.playback, data.fs, system.agent, camera.*
Imports solid-js and the applet's list of @pocketjs/framework/* modules, with relative imports inside the project root; camera, fs and worker/channels only with camera.*, data.fs and net.http
Code No dynamic import(), no eval, no Function constructor
Launcher APIs No installing, removing or pairing: those belong to the launcher

bun tools/applet-check.ts <pocket.app.json> runs every rule outside a build. The full rules and their reasons are in docs/APPLETS.md.

Reaching the network

The UI realm has no network. An applet's HTTP runs in the worker its app contract declares (domains.worker with one http node): the worker fetches the applet's services at https://companion.pocket/<name>/… and sends each answer back over a channel, and the UI reads it with @pocketjs/framework/worker/channels, which an applet imports when it declares net.http. One request (URL, method, headers and body) is at most 1024 bytes. A channel record is 1024 bytes and a response at most 256 KiB unless the target's host contract states more (worker.channels, worker.http.responseBytes).

Drawing on the surface

@pocketjs/framework/applet gives an applet its surface:

// @title Notes
import { AppletSurface } from "@pocketjs/framework/applet";
import { mount } from "@pocketjs/framework/solid";
import App from "./app.tsx";

mount(() => (
  <AppletSurface>
    <App />
  </AppletSurface>
));
Export Value
AppletSurface Mounts the UI on the auxiliary surface when the plan has one, else on the primary display at its full size
appletViewport() The surface's logical {width, height}: 320×240 on a 3DS, the adaptive size on a Tab5, 480×272 on a PSP
appletTouch() Whether the surface reports contacts
appletOnAuxiliary() Whether the UI is on the auxiliary surface

The surface exists once the app mounts, so an applet reads the size inside mount() and never at module load. On a surface without touch every control answers a button.

Staying operable by the agent

The agent reads an applet through the text it paints and acts with taps, drags, button presses and typing into the system keyboard. An applet stays operable when each control shows a text label, or a count or name next to its icon, and when a text field opens the system keyboard.