Appearance
NIP-5D Conformance Policy
Status: active for current NIP-5D conformance guardrails.
Authoritative NIP-5D source: https://github.com/nostr-protocol/nips/pull/2303/
Current injected-domain clarification: https://github.com/dskvr/nips/pull/4 (merged into the NIP-5D branch behind PR #2303 on 2026-06-26)
Repo-local pointer: specs/NIP-5D.md
Authority
Only the current upstream NIP-5D PR defines the core NIP-5D contract. Kehto's repo-local spec file is intentionally a pointer so stale mirrors do not become implementation authority. RUNTIME-SPEC.md is internal runtime guidance.
Published convention authority
- NAP-INTENT: merged
napplet/napsmaster5ac0490461ca6fec2f0d2e45b4835cf9bc08de24. - NAP-INC: merged
napplet/napsmaster5ac0490461ca6fec2f0d2e45b4835cf9bc08de24. - NAP-IDENTITY / NAP-THEME / NAP-SHELL: merged
napplet/napsmaster5ac0490461ca6fec2f0d2e45b4835cf9bc08de24. - NAP-RELAY: open PR #2 at
0be8abce18beb46ca37bd4ddd042f58d30b4eedc. - Published packages: NAP-INTENT authority
5ac0490461ca6fec2f0d2e45b4835cf9bc08de24; napplet/web#199 source3037200c932488f14f7f369b8583c39c9c16510a, merged asb3f0007867eac109fa4917fac9c285d3b7cc6155; and Version Packages #198 heada79e7f4638f70f4557d4183faee9348847bb8cc7, merged as release sourcedc1d24153c759152b6ba31a6ec9bea967798f2df. The current exact line is core0.31.1, nap0.31.2, shim0.29.2, SDK0.27.2, and Vite plugin0.14.1.
Active NAP-RELAY boundary
Kehto follows NAP-RELAY PR #2 at 0be8abce18beb46ca37bd4ddd042f58d30b4eedc. For relay.publish, a napplet supplies an unsigned EventTemplate; the shell signs it, sends only the signed event to relay services, and returns relay.publish.result with ok, the full signed event, and eventId on success or error on failure. Runtime, createRelayPoolService, createCoordinatedRelay, Paja, and the playground must consume the same signed event and result contract. Failed publications are not delivered through the runtime's successful-event buffer.
The released @napplet/nap@0.31.2 SDK accepts EventTemplate, but the package's RelayPublishMessage.event declaration still names NostrEvent. That is recorded upstream drift, not authority to let a napplet bypass shell signing.
Active NAP-INC boundary
NAP-INC is governed by merged naps/NAP-INC.md on napplet/naps master 5ac0490461ca6fec2f0d2e45b4835cf9bc08de24. The document remains marked draft, but the merged path supersedes the earlier stacked PR heads as protocol authority.
The released package projection owns query-to-text-payload transposition in the shared, runtime-provided INC binding. Kehto runtime routing then uses an exact stable convention topic identity. It rejects query-bearing normalized wire or discovery identities, while routing arbitrary opaque strings (including ? and #) by their complete exact text. It must not add prefix, wildcard, or query-aware matching, service-over-INC prefix dispatch, synthetic senderless events, or runtime payload-kind inference. The runtime attaches a runtime-attested dTag to delivered events, does not accept caller sender, keeps payloads and IDs opaque, and excludes the source endpoint from topic fan-out.
Merged NAP-INC and released @napplet/nap@0.31.2 both define on(topic, callback) with one IncEvent.
INC channel authorization is open-only: ACL and target liveness are evaluated at open, with no per-message authorization. The merged spec requires equivalent handles for opener and target, target inc.channel.opened before the opener result, channel.onOpened, per-handle onClosed, retained inbound/early/terminal lifecycle data in order, bounded overflow closure, and deterministic teardown. channel.list is informational only. The downstream tracker remains kehto/web#203, including the upstream-resolution reply; the superseded opener-only view must not be restored.
NAP-INTENT uses the merged structured IntentRequest and final IntentResult contract. The host resolves a verified manifest candidate, completes target creation/readiness and convention dispatch, then returns handled, handler, windowId, and convention. There is no intent.deliver or onDelivery surface. Kehto carries the selected convention to the target through the ordinary runtime-attested inc.event path; eligible intent targets therefore declare inc.
Active NAP-IDENTITY and NAP-THEME boundary
Kehto checks NAP-IDENTITY and NAP-THEME at napplet/naps master 5ac0490461ca6fec2f0d2e45b4835cf9bc08de24. Kehto documents its projection and policy rather than extending the wire contract.
identity.getPublicKeyalways settles with one correlatedidentity.getPublicKey.result;pubkey: ""is the no-signer/failure sentinel. Other supported readonly identity reads retain their matching safe primary field. Unknown identity actions are silent.identity.changedis automatic for actual connect/sign-out transitions only, includingpubkey: ""; it is neither an INC event nor an intent delivery.theme.getalways returns a complete theme withcolors.background,colors.text, andcolors.primary. Kehto deliberately reconciles the draft error-only example by returning one fixed non-sensitive complete normaltheme.get.resultwithouterrorfor ACL-denied, firewall-denied, or unavailable reads. This is a Kehto policy/spec-gap reconciliation, not a mixedtheme+errorextension or a separate theme error message.theme.changedis an automatic change push. The injected surface istheme.get()andtheme.onChanged()only; no theme subscribe/unsubscribe wire protocol exists.- Host changes target only authenticated live
shell.readysessions whose frozen environment includes the matching domain and whose recipient capability is currently granted. The protected injected identity/theme objects are readonly and accept results or changes only fromwindow.parent. A theme update stores complete state before its single eligible-recipient push.
Phase 105 completed published Napplet package adoption. This policy records the selected released line without turning Kehto-local policy into protocol authority.
Runtime Availability Policy
Current NIP-5D runtime availability is injected window.napplet.<domain> presence before authored napplet scripts run.
- Injection must happen outside the signed napplet artifact bytes.
- Injection must be limited to
window.napplet. - Domain object presence is availability only; operation semantics, versions, errors, and diagnostics belong to the matching NAP spec.
- Optional-domain presence and mandatory NAP-SHELL are separate requirements. Every Kehto-hosted iframe receives
window.napplet.shellbefore authored code, regardless of manifestrequiresor capability toggles. - NAP-SHELL owns
ready(), localsupports(domain, protocol?), read-onlyservices,onReady(), and theshell.ready/shell.initlifecycle. The runtime prelude installs its parent-bound receiver before emitting readiness; napplet artifacts are not required to bundle their own handshake. - Published core
0.31.1and shim0.29.2omit a generic mandatory shell implementation. Kehto retains the host-owned NAP-SHELL prelude under5ac0490461ca6fec2f0d2e45b4835cf9bc08de24until a corrected upstream release is reviewed; the shim is never documented as supplying shell.
Extension Classification
| Surface | Classification | Contract |
|---|---|---|
connect | Official Kehto NAP extension | Advertise as nap:connect only when the shell enforces the connect-origin policy and response/header behavior for the hosted napplet. |
class | Official Kehto NAP extension | Advertise as nap:class only when the shell assigns the napplet class and applies class-specific policy before iframe use. |
nostrdb | Out of scope for active playground NIP-5D conformance | Do not count as a required playground NAP until a Kehto NAP contract and shell capability advertisement exist. |
relay.publishEncrypted | Official relay NAP operation | Allowed only when the shell performs encryption/signing policy. Napplets may submit cleartext intent; the shell must not sign or broadcast ciphertext supplied by a napplet. |
Raw Envelope Policy
Raw envelopes are not automatically non-conformant. They are allowed only when they are either:
- a documented NAP domain envelope whose SDK helper surface is incomplete; or
- a demo/test-only envelope listed in the milestone raw-envelope allowlist.
Phase 58 Raw-Envelope Allowlist
| Envelope | Location | Classification | Boundary |
|---|---|---|---|
common.*.result | apps/playground/napplets/common-demo/src/main.ts | Disabled source / NAP helper-surface gap | The retained common-demo source is not hosted by the playground. Until it is replaced with a real demo or deleted, raw result listeners stay confined to common-demo, parent-source-bound, and correlation-id/type narrowed. |
cvm.discover | apps/playground/napplets/cvm-relatr/src/main.ts | NAP-CVM helper-surface gap | The cvm ContextVM domain has no @napplet/shim helper at this SDK version, so the Relatr demo posts cvm.discover directly. Raw use is confined to cvm-relatr and the listener is parent-source-bound. |
cvm.request | apps/playground/napplets/cvm-relatr/src/main.ts | NAP-CVM helper-surface gap | Same cvm-relatr-only gap as cvm.discover; the shell owns all ContextVM transport, signing, and relay access. |
link.open.result | apps/playground/napplets/link-demo/src/main.ts | Disabled source / NAP helper-surface gap | The retained link-demo source is not hosted by the playground. Until it is replaced with a real demo or deleted, raw result listeners stay confined to link-demo, parent-source-bound, and correlation-id/type narrowed. |
lists.*.result | apps/playground/napplets/lists-demo/src/main.ts | Disabled source / NAP helper-surface gap | The retained lists-demo source is not hosted by the playground. Until it is replaced with a real demo or deleted, raw result listeners stay confined to lists-demo, parent-source-bound, and correlation-id/type narrowed. |
ble.*.result | apps/playground/napplets/ble-demo/src/main.ts | Disabled source / NAP helper-surface gap | The retained BLE demo source is not hosted by the playground. Until it is replaced with a real demo or deleted, raw result listeners stay confined to ble-demo, parent-source-bound, and correlation-id/type narrowed. |
serial.*.result | apps/playground/napplets/serial-demo/src/main.ts | Disabled source / NAP helper-surface gap | The retained serial-demo source is not hosted by the playground. Until it is replaced with a real demo or deleted, raw result listeners stay confined to serial-demo, parent-source-bound, and correlation-id/type narrowed. |
webrtc.*.result / webrtc.event | apps/playground/napplets/webrtc-demo/src/main.ts | Disabled source / NAP helper-surface gap | The retained WebRTC demo source is not hosted by the playground. Until it is replaced with a real demo or deleted, raw result/event listeners stay confined to webrtc-demo, parent-source-bound, and correlation-id/type narrowed. |
notify.create | apps/playground/napplets/toaster/src/main.ts | NAP helper-surface gap | Notify service supports create/list, but @napplet/nap/notify/sdk lacks create/list helpers. Raw use must stay source-bound and confined to toaster. |
notify.list | apps/playground/napplets/toaster/src/main.ts | NAP helper-surface gap | Same toaster-only helper gap as notify.create; raw replies are accepted only from window.parent. |
resource.bytesMany | apps/playground/napplets/resource-demo/src/main.ts | NAP helper-surface gap | Draft NAP-RESOURCE fa6bcc6935aa19e7b70ab2a2c721dafca77c78e1 defines the wire operation, while the installed SDK lacks the helper. Raw use is confined to resource-demo, parent-source-bound, and type-narrowed. |
theme.changed | apps/playground/src/theme.ts | NAP helper-surface gap | Theme change is an automatic shell-to-napplet envelope; the raw listener is parent-source-bound and type-narrowed. No subscribe/unsubscribe wire action exists. |
New raw window.parent.postMessage() protocol envelopes in playground napplets must fail static checks unless they are added to that allowlist with a concrete classification.
Naming Policy
Use "ready", "identity-bound", "registered", "connected", or "signer authenticated" according to the actual state being described.
Do not use AUTH, REGISTER, IDENTITY, or "authenticated" to describe NIP-5D protocol identity. Those words are allowed only when discussing historical drift, NIP-42 relay behavior, or user/signer authentication outside the napplet protocol identity path.