NIP-5D · workshop path
Build your first napplet.
Start with the maintained template. Run it inside a real host shell. Leave with a verified, single-purpose Nostr app.
Understand the split
Your napplet draws the UI. The shell holds authority.
A napplet is a small web app in a sandboxed iframe. It asks its host for identity, relays, signing, storage, and other capabilities through typed messages.
Untrusted and sandboxed
Trusted by the user
Paja runs your app inside the Kehto runtime and shows interfaces, permissions, signer state, and every protocol envelope.
Prepare your machine
Install the app toolchain and the local host.
Package manager.
Check the basics
corepack enable
corepack prepare pnpm@10.8.0 --activate
node --version # v20 or newer
pnpm --version # v10
git --version
Install Deno and the Kehto host
# macOS or Linux
curl -fsSL https://deno.land/install.sh | sh
# Windows PowerShell
irm https://deno.land/install.ps1 | iex
Open a new terminal, then install and check Kehto:
deno install --global --allow-all --name kehto npm:@kehto/cli
deno --version
kehto --help
Install the napplet CLI
# macOS or Linux
curl -fsSL https://napplet.run/install.sh | sh
# then check it
napplet guide
# Windows PowerShell
irm https://napplet.run/install.ps1 | iex
napplet guide
The installer downloads a standalone binary and verifies its checksum. Deno is not required.
Create the project
Use the maintained starter. Let the CLI own metadata.
napplet create my-napplet
cd my-napplet
napplet init
napplet skills install --to codex
pnpm install
During napplet init, choose a short name, a clear title, the closest
archetype, and the workshop relay and Blossom server supplied by your facilitator.
Edit the app surfaces in src/. Keep the Vite, single-file,
conformance, and .napplet/config.json wiring intact.
Build one small thing
Napplets should be focused. Pick one job.
There are four valid ways in. Today we are taking the agentic path with the installed skills.
Prompt your coding agent
Use the installed napplet skills to build a one-screen note composer. Autosave the draft through shell storage, show the connected pubkey, and publish through the outbox. Keep the existing scaffold and verify the result.
Prefer @napplet/sdk. Normal Nostr reads and writes go through
outbox. Persistent state goes through storage. The shell—not
your app—signs and talks to relays.
The boundary in four lines
const draft = await window.napplet.storage.getItem('draft');
await window.napplet.storage.setItem('draft', text);
const pubkey = await window.napplet.identity.getPublicKey();
await window.napplet.outbox.publish(eventTemplate);
Run inside Paja
A raw Vite tab is not a napplet host.
kehto paja --target-url http://127.0.0.1:5173 \
-- pnpm vite --host 127.0.0.1
Open the printed Paja runtime URL, not the Vite target URL. Paja keeps hot reload while hosting the app in a real sandboxed Kehto iframe.
kehto cannot be spawned
Open a new terminal and run kehto --help. If it is still missing, add
~/.deno/bin to PATH, then retry the Paja command.
- Use a NIP-07 signer when you have one available.
- Otherwise select the Dev signer for disposable signing.
- Use Interfaces to test a missing capability.
- Use ACL to grant or deny an operation.
- Watch Messages to see each request and response.
Its ephemeral identity has no follows, relay history, or web of trust. Napplets that depend on that context may look empty or remain unavailable. Paja prompts before every sign or publish operation, whichever signer you choose.
Verify the artifact
Build, test the sandbox contract, then inspect the plan.
pnpm verify
pnpm test:conformance
napplet debug
napplet deploy --dry-run
You are done when all four pass and Paja can exercise the app without errors.
Workshop finish line
A single-purpose napplet, hosted in Paja, responsive at small sizes, and clean under conformance. Publishing is a separate step.
Keep the boundary clean
If the shell owns it, ask the shell.
- Never place an
nsecin source, config, prompts, or shell history. - No direct
WebSocket, relay pool, orwindow.nostr. - No browser
localStorage; use the storage interface. - No direct external assets or fetch by default; use the resource interface.
- Declare only capabilities the napplet cannot work without.
- Handle absent optional interfaces without crashing.
Read the source material
Short path first. Protocol details when needed.
- Napplet getting started Current CLI-first workflow.
- Paja getting started Host controls, signer, ACL, and message log.
- NIP-5D proposal Living napplet ↔ shell protocol.
- NAP specifications Capability contracts.
- NIP-5A Static site manifests and content-addressed files.