Skip to content

NAP-CONNECT: User-gated direct network access - #19

Draft
dskvr wants to merge 4 commits into
masterfrom
nub-connect
Draft

NAP-CONNECT: User-gated direct network access#19
dskvr wants to merge 4 commits into
masterfrom
nub-connect

Conversation

@dskvr

@dskvr dskvr commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Status: Draft
Namespace: window.napplet.connect
Discovery: shell.supports("connect")
Class posture: NAP-CLASS-2 (class-contributing NAP)

Summary

NAP-CONNECT provides sandboxed napplets with user-gated direct network access (fetch, WebSocket, SSE, EventSource) to a pre-declared list of origins. Origins are declared at build time via ["connect", "<origin>"] tags in the NIP-5A manifest. The shell reads those tags, prompts the user for approval at first load of a given (dTag, aggregateHash), persists the decision, and emits a runtime Content Security Policy whose connect-src directive contains the approved origin list. The user's decision is expressed through the HTTP-response CSP the shell serves with the napplet's HTML — not through any postMessage traffic.

A napplet declaring any connect tag takes on the posture defined in NAP-CLASS-2.md. A napplet declaring no connect tags takes on the default posture defined in NAP-CLASS-1.md. This NAP does not redefine those class postures — see the sub-track documents for CSP shapes, consent-flow MUSTs, grant-persistence semantics, residual-meta-CSP refusal requirements, and revocation responsibilities.

NAP-CONNECT has no postMessage wire protocol. Grants are expressed entirely through the runtime CSP the shell emits in the HTTP response for the napplet's HTML, plus a shell-injected discovery meta tag (<meta name="napplet-connect-granted" content="...">) read synchronously by the napplet shim at install time. There are no connect.* envelopes.

Scope

  • Manifest tag shape["connect", "<origin>"], one tag per origin.
  • Origin format — scheme + host + optional port; no path, query, fragment, or wildcard. Default ports omitted. IDN: build MUST canonicalize UTF-8 to Punycode; shell MUST reject non-Punycode.
  • Canonical aggregateHash fold — normative pseudocode (lowercase → ASCII-sort → \n-join → UTF-8 → SHA-256 → lowercase hex) with copy-pasteable conformance fixture (cc7c1b1903fb23ecb909d2427e1dccd7d398a5c63dd65160edb0bb8b231aa742 for the 3-origin fixture). Folded into the signed NIP-5A aggregateHash via a synthetic connect:origins xTag that changes grant key on origin-list modification.
  • Runtime APIwindow.napplet.connect resolves to { granted: boolean, origins: readonly string[] }; graceful-degradation default {granted: false, origins: []}.
  • Discovery meta tag<meta name="napplet-connect-granted" content="<space-separated-origins>"> read synchronously by the shim at install time. Name is shell-authoritative; distinct from any dev-mode-only napplet-connect-requires injection.
  • Capability advertisementnap:connect, connect:scheme:http, connect:scheme:ws; shells MAY refuse cleartext by advertising connect:scheme:http === false.
  • Grant lifecycle — keyed on (dTag, aggregateHash); napplet rebuild with changed connect tags produces new aggregateHash (via connect:origins fold) and auto-invalidates prior grant.
  • Security Considerations — once granted, shell has zero visibility into post-grant traffic. Grants are a full trust vote. Residual meta-CSP refusal is a shell responsibility (see NAP-CLASS-2.md).

Non-Goals

  • No postMessage wire protocol — grants flow through HTTP-response CSP + synchronous meta tag; intentional absence.
  • No per-origin partial grants — v1 is all-or-nothing per prompt.
  • No wildcard subdomains — each subdomain is a separate tag requiring separate consent. Future amendment pending threat-model review.
  • No shell visibility into post-grant traffic — browser enforces CSP transparently to the shell.
  • No quota/rate-limiting/audit logging — browser gives no hook in the sandboxed model.
  • No private-IP block list — browser CSP matches URL literal, not resolved IP; DNS rebinding bypasses any such check.

Guidance

Authors SHOULD default to shell-mediated resource fetching for everything the mediated primitive can express (avatars, static assets, one-shot byte fetches, bech32 resolution); NAP-CONNECT exists specifically for the cases that cannot be expressed that way (POST/PUT/PATCH, WebSocket, SSE, streaming, custom headers, long-lived connections, third-party libraries calling fetch directly). The two surfaces are complementary, not competing.

Implementations

(none yet)

Changelog

  • fdf6c3e - Introduced NAP-CONNECT for user-gated direct network access.

dskvr added a commit that referenced this pull request Apr 21, 2026
@dskvr
dskvr marked this pull request as draft April 22, 2026 08:56
@dskvr dskvr changed the title NUB-CONNECT: User-gated direct network access NAP-CONNECT: User-gated direct network access Jun 3, 2026
@dskvr dskvr added the DEFERRED label Jun 17, 2026
Rebased onto current master; spec only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dskvr added 3 commits June 21, 2026 18:30
NAP-CONNECT has no postMessage wire protocol, but its injected runtime state is still normative. The state shape now uses a CDDL-style record instead of TypeScript declarations.

Constraint: Keep this branch scoped to NAP-CONNECT.
Confidence: high
Scope-risk: narrow
Directive: Do not describe NAP-CONNECT runtime state with implementation-language types.
Tested: git diff --check
Tested: rg found no TypeScript code fences or declarations in naps/NAP-CONNECT.md
The NappletConnect runtime state is now described with a Markdown field table instead of a CDDL-style block. The grant and origins semantics are unchanged.

Constraint: PR #19 owns NAP-CONNECT; keep this change scoped to that spec file.

Confidence: high

Scope-risk: narrow

Tested: rg for cddl/typescript schema fences and type assignment patterns in NAP-CONNECT

Tested: git diff --check -- naps/NAP-CONNECT.md
Open NAP proposal branches need reader-visible changelogs that track semantic spec changes without recording formatting-only migrations.

Constraint: Changelog entries are one bullet per semantic commit-change.
Rejected: Include schema-table or formatting-only commits | they do not change the spec contract or boundary.
Confidence: high
Scope-risk: narrow
Directive: Keep future changelog entries semantic; omit changelog-only and formatting-only commits.
Tested: git diff --check
Not-tested: Rendered Markdown
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant