napplet is alpha. The specification is experimental and a moving target. There will most certainly be drift between packages and the specification. Things will most certainly change. For adventurers only.
Monorepo for the napplet SDK -- libraries for building NIP-5D Nostr Web Applets - "napplets"
A napplet is a sandboxed web app that runs inside a shell. The shell and napplet communicate over a MessagePort that the injected runtime hands to the shell in a single napplet.hello, using a JSON envelope format ({ type, ...payload }) defined by NIP-5D. The napplet never touches localStorage, relay connections, or signing keys directly -- the shell proxies everything through NAP interfaces. Read about napplets in NIP-5D possiblity
NAP proposal status and shipped implementation maturity are separate. All capabilities can change. Inspect NAP_IMPLEMENTATIONS from @napplet/core and use selectNapDomains for local host opt-in policy. See maturity and opt-in guidance.
Install the standalone CLI; Deno is not required:
# macOS or Linux
curl -fsSL https://napplet.run/install.sh | sh
# Windows PowerShell
irm https://napplet.run/install.ps1 | iexRun napplet --version (also -v or version) to identify the installed CLI. Run napplet guide for the current workflow and links to the relevant docs, or follow the same path directly:
napplet create my-napplet
cd my-napplet
napplet init
npx skills add napplet/web # agent skills via skills.sh (Claude Code, Codex, Cursor, …)
pnpm install
# Ask your agent to build the napplet, then verify and preview it.
pnpm verify
napplet deploy --dry-run
napplet deploynapplet create downloads the maintained Vite + TypeScript starter. napplet init owns deployment name, title, description, archetype roles and conventions, relays, and Blossom servers in .napplet/config.yaml. The standalone CLI provisions its own cached tooling for screenshots, conformance, and Paja, so no host Node.js, npm, npx, Deno, Git, or Kehto installation is required. Node.js 20+ is needed by the generated Vite project and the optional skills.sh CLI.
The CLI supports napplet screenshot --output preview.png. napplet deploy captures a screenshot and publishes a separate Zapstore application event by default; use --no-screenshot and/or --no-zapstore to opt out. See the CLI guide for configuration, browser setup, and dry-run examples.
Use the napplet-icon skill to design, generate, and refine an app icon, check it at small sizes, and prepare it for NIP-5D integration. Install it with npx skills add napplet/web --skill napplet-icon; see the agent skills guide for the complete workflow.
Use napplet-port-napp to port an eskema/nostrapps NAPP through bridge replacement, implementation and behavior verification. Install it with npx skills add napplet/web --skill napplet-port-napp.
Current event defaults begin at CLI 0.7.0, Vite plugin 0.15.0, conformance 0.18.0, and conformance-cli 0.3.0. Earlier release series use legacy path/aggregate manifests. The event kinds remain 5129/15129/35129; current events use the artifact SHA-256 in x, a description in content, and R/O plus z/i metadata. CLI deployments offer temporary --format legacy support; unattended deployments default to current. See the migration guide for event conversion, legacy pointers and shell storage/ACL implications.
| Package | npm | JSR | Description |
|---|---|---|---|
| @napplet/core | JSON envelope types (NappletMessage, NapDomain), NAP dispatch infrastructure (registerNap, dispatch), protocol constants and Nostr types. Imported by all other packages. |
||
| @napplet/shim | Runtime-side helper for injecting selected window.napplet.<domain> objects before napplet code runs. Sends JSON envelope messages via postMessage. |
||
| @napplet/sdk | Named TypeScript exports wrapping window.napplet for bundler consumers. Provides domain wrapper objects and NAP message type re-exports, including relay, inc, storage, cvm, outbox, upload, intent, ble, webrtc, link, count, lists, common, serial, fs, dm, and llm. |
||
| @napplet/nap | Compatibility package for active NAP domain subpaths (shell, relay, storage, inc, ifc, keys, theme, media, notify, identity, config, resource, cvm, outbox, upload, intent, ble, webrtc, link, count, lists, common, serial, fs, dm, llm) with barrel + granular (types/shim/sdk) exports. Tree-shakable (sideEffects: false). Includes ownership-aware media and resource, the ContextVM cvm bridge with registry helpers, outbox-aware outbox relay routing, shell-mediated upload, archetype intent dispatch, runtime-mediated BLE/WebRTC, link opening, event counts, list mutations, common social actions, serial device access, shell-mediated virtual filesystem access, direct messages, user-selected language model inference, and read-only identity helpers. See packages/nap/README.md for the full subpath reference. |
||
| @napplet/vite-plugin | Vite plugin for current NIP-5D manifests: hashes the final index.html artifact, emits description content and capability/intent metadata, and defaults to single-file output. | ||
| @napplet/cli | Standalone CLI for creating projects, owning deploy metadata, discovering builds, and deploying signed manifests. JSR/Deno remains an alternative install route. | ||
| @napplet/boilerplate | — | Project-only generator behind napplet create; clones the maintained Vite + TypeScript starter and derives its package name without setting deployment metadata. |
|
| @napplet/conformance | Framework-agnostic conformance engine: hand-written envelope validators for the active NAP wire domains, a signed manifest-event validator, a scriptable reference mock shell, the zero-config check catalog, and pretty/JSON/JUnit reporters. Browser-safe; reused by both the CLI and the web runtime. | ||
| @napplet/conformance-cli | — | Headless napplet-conformance runner. Drives the engine against a napplet in real Chromium (Playwright) and sets a CI exit code — wire it up as test:conformance. npm-only (Playwright dependency). |
|
| @napplet/conformance-web | — | Browser conformance runtime deployed at /conformance and bundled into napplet-conformance --ui. Runs the conformance engine live in the page with a check tree, envelope log, and manifest inspector. |
|
| @napplet/lab | — | — | Private tooling for probing napplets and the shells that host them. Ships lab napplets under packages/lab/napplets/*, starting with the Security Lab (@napplet/lab-security): a napplet that probes the sandbox, CSP, namespace, runtime and identity-revocation boundaries of whatever shell loads it, plus a headless Kehto Paja harness. Published as NIP-5D manifests, not to npm. |
Napplets can verify they conform to the NAP protocol before publishing, in two scopes that share one engine:
# Headless / CI — exits non-zero on any error-severity failure:
npx napplet-conformance ./dist# App variant — opens the live web runtime and re-runs on every change (like vitest --ui):
npx napplet-conformance --ui . --exec "vite build --watch"The same web runtime ships standalone (apps/conformance, deployed at /conformance) and runs the checks live in the browser with a visual report. v1 is zero-config protocol conformance: signed manifest-event validity, boots under sandbox="allow-scripts", receives a runtime-injected window.napplet, every emitted envelope is well-formed, graceful degradation when a domain is absent, and no forbidden globals.
- Conformance tooling — new
@napplet/conformanceengine +@napplet/conformance-clirunner + standaloneapps/conformanceweb runtime let a napplet self-verify NAP protocol conformance headlessly (CI) and live in the browser. Hand-written per-NAP envelope validators (drift-guarded against@napplet/nap), manifest checks, a reference mock shell, and atest:conformance-ready CLI. - v0.32.0 — Read-Only NAP-IDENTITY —
identity.getPublicKey()is a snapshot that resolves to a hex pubkey or""when no user is connected, andidentity.onChanged(handler)receives shell-pushedidentity.changedupdates. Identity no longer exposes decrypt, encrypt, or signing operations.
@napplet/shim ──► @napplet/nap ──► @napplet/core
@napplet/sdk ──► @napplet/core
@napplet/vite-plugin (build-time only, depends on nostr-tools)
@napplet/cli (Deno deploy and diagnostics tool)
@napplet/boilerplate (CLI generator, clones github.com/napplet/boilerplate)
@napplet/conformance-cli ──► @napplet/conformance + @napplet/conformance-web
@napplet/conformance-web ──► @napplet/conformance
Shell runtime @napplet/shim
ShellBridge window.napplet.outbox (query/subscribe/publish)
├── JSON envelope message routing window.napplet.inc (topics/channels)
├── Identity bound to the hello port window.napplet.storage (get/set/remove)
├── ACL enforcement window.napplet.resource (bytes/bytesMany/bytesAsObjectURL)
├── NAP dispatch (outbox/relay/storage) window.napplet.domain presence
└── INC routing
◄────────── window: { type: 'napplet.hello' } + one MessagePort (once) ─────►
◄────────── port: { type: 'outbox.subscribe', id, filters } ─────────►
◄────────── port: { type: 'outbox.event', subId, result } ─────────►
@napplet/vite-plugin (build time)
└── NIP-5D manifest generation + R/O capability advertisements
The iframe uses sandbox="allow-scripts", without allow-same-origin, as specified by NIP-5D Transport. Napplets cannot access the host shell's DOM, cookies, localStorage, or service workers. All persistent state goes through the shell's proxies.
NAP-INTENT accepts convention URIs through invoke(uri, options?) and open(uri, options?). The binding derives the role, action, and stable convention identity before dispatch.
const result = await window.napplet.intent?.open('napplet:profile/open', {
payload: { pubkey: 'abc123' },
behavior: { focus: true, reuse: false },
});
if (!result?.ok) throw new Error(result?.error ?? 'intent not accepted');
window.napplet.intent?.onDelivery(({ sender, convention, payload }) => {
console.log(sender, convention, payload);
});Acceptance transfers delivery responsibility to the runtime; target delivery is retained until onDelivery registration and does not depend on the source remaining alive. Candidates expose opaque catalog identifiers and manifest-derived parameter contracts. NAP-SHELL provides optional environment information without gating other domains. This non-normative orientation defers to the living NAP-INTENT and NAP-SHELL.
The napplet protocol is defined by the living NIP-5D specification; the NAP capability domains are defined on the NAPs track. Any shell can host napplets by injecting window.napplet before napplet scripts run. Napplet application code consumes injected domains directly or through @napplet/sdk.
pnpm install
pnpm build # Build all packages via turborepo
pnpm type-check # TypeScript validation
napplet create my-napplet # Scaffold a new napplet from the template repoPublishing runs from GitHub Actions. Prepare release metadata locally, then merge the release changes into main. Every push to main runs the JSR publisher, which skips versions already present in the registry and publishes missing versions regardless of the commit title. Manual dispatch remains available to retry a failed release. npm publishing runs through Changesets; the Deno CLI is distributed through JSR and standalone GitHub release binaries.
pnpm version-packages # Apply changesets, bump versionsRun pnpm dev:site to start the website and documentation together. Each launch chooses open ports through the OS and prints both URLs. The website uses its paired documentation server's actual address; concurrent launches can run side by side. Ctrl+C stops the servers started by that command. Standalone website and documentation commands also select open ports.
The informational site and package documentation live in apps/:
apps/web-- Astro ecosystem showcase with runnable napplets, shells, and developer tools;/protocol/lists merged NAPs and open proposals with detail pages and contribution guidance. The original Svelte explainer lives at/explainer/. Runpnpm generate:protocolto refresh the NAP directory and build the website; the deployment workflow also refreshes it daily. See website development and verification.apps/docs-- VitePress documentation, served under/docs.apps/conformance-- the standalone conformance web runtime, served under/conformance.
pnpm --filter @napplet/web dev # showcase and explainer
pnpm --filter @napplet/docs dev # documentation
pnpm --filter @napplet/conformance-web dev # conformance runtimeDocumentation development prebundling uses the same esnext target as production builds and source transforms. Keep optimizeDeps.esbuildOptions.target aligned with the other docs Vite targets when updating the toolchain.
scripts/assemble-site.mjs creates the same static artifact for link checks and .github/workflows/deploy-site.yml: showcase at /, original explainer at /explainer/, docs at /docs/, and conformance at /conformance/, plus Markdown copies of every page, /llms.txt and the /.well-known/agent-skills/ index for agents (Agent access). Bunny is the canonical deployment target, with Accept: text/markdown negotiation in the edge/ script; nsite is an optional mirror. Configure deploy secrets with scripts/setup-site-secrets.sh.
- NIP-5D -- the living napplet-shell protocol specification (source of truth)
- NAPs track -- where every NAP capability domain is proposed and defined
MIT
This non-normative guidance defers to NAP-GAMEPAD. When the runtime exposes window.napplet.gamepad, the SDK provides cached getGamepads(), read-only available and focused, and onChange(handler) with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their index.
import { gamepad } from '@napplet/sdk/gamepad';
if (window.napplet?.gamepad) {
const subscription = gamepad.onChange(state => {
if (!state.available || !state.focused) return;
for (const pad of state.pads) {
if (pad) console.log(pad.index, pad.axes);
}
});
// Call subscription.close() when the owning view is disposed, not right away.
}Declare gamepad as optional (O) when other controls fulfill the core task. Use a hard requirement (R) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native navigator.getGamepads() or open raw HID devices. Haptics and remapping are outside this proposal.
The packages expose the proposed NAP-LLM through window.napplet.llm, @napplet/sdk, @napplet/sdk/llm, and the layered @napplet/nap/llm subpaths. Napplets ask the user to select a model connection, submit stateless generations (optionally with tool definitions the napplet itself executes) with optional streamed deltas, and cancel them; the runtime supplies the connections, credentials, disclosure consent, and provider access. See the non-normative LLM guide.