Files
bitvid/docs/nip46-client-overview.md
T

4.1 KiB

NIP-46 Client Overview

js/nostr/nip46Client.js implements the Nostr Connect (NIP-46) protocol, allowing the application to delegate event signing to a remote signer (e.g., a "Bunker" or mobile wallet) instead of managing private keys locally.

Architecture

The communication happens over standard Nostr relays using Kind 24133 (Nostr Connect) events. These events act as an RPC (Remote Procedure Call) transport layer.

Components

  1. Nip46RpcClient: The central controller. It manages:

    • Encryption: Secures payloads using NIP-44 (XChaCha20) or NIP-04 (legacy AES).
    • Subscription: Listens for responses from the specific remote signer pubkey.
    • Queueing: Uses Nip46RequestQueue to serialize and throttle requests (preventing "rate-limited" errors).
    • Session: Persists connection details (remote pubkey, relays) to localStorage.
  2. Nip46RequestQueue: A priority queue ensuring RPC calls are processed sequentially and respect relay rate limits.

Connection Flow

  1. Parsing: The app receives a connection URI (e.g., bunker://... or nostrconnect://...).
  2. Handshake: Nip46RpcClient initiates a connect RPC call.
  3. Verification: If a secret was provided in the URI, the client verifies the signer's response matches it.
  4. Metadata: The client fetches the user's public key via get_public_key.
sequenceDiagram
    participant App
    participant Client as Nip46RpcClient
    participant Relay
    participant Signer as Remote Signer

    App->>Client: connect(bunkerURI)
    Client->>Client: parse URI & derive ephemeral keys
    Client->>Relay: Publish Kind 24133 (method: "connect")
    Relay->>Signer: Deliver Event
    Signer->>Relay: Publish Kind 24133 (result: "ack")
    Relay->>Client: Deliver Response
    Client->>Client: Verify Secret & Permissions
    Client-->>App: Connected

Signing Flow

When the app needs to publish a video or update a profile, it calls signEvent.

sequenceDiagram
    participant App
    participant Client as Nip46RpcClient
    participant Relay
    participant Signer

    App->>Client: signEvent(unsignedEvent)
    Client->>Client: JSON.stringify(unsignedEvent)
    Client->>Client: Encrypt Payload (NIP-44)
    Client->>Relay: Publish Kind 24133 (method: "sign_event")

    Note over Client, Signer: Network Latency / Queueing

    Signer->>Relay: Publish Kind 24133 (result: signedJson)
    Relay->>Client: Deliver Response
    Client->>Client: Decrypt & Parse
    Client-->>App: Signed Event

Security & Encryption

  • Ephemeral Keys: The client generates a random local key pair for each session. The user's main private key is never transmitted.
  • Encryption Negotiation: The client prefers NIP-44 v2 but falls back to NIP-04 if the signer doesn't support it.
  • Auth Challenges: If the signer requires out-of-band authentication (e.g., scanning a QR code), it returns an auth_url error, which the client bubbles up to the UI.

Key Functions

  • parseNip46ConnectionString(uri): specific parser for bunker:// and nostrconnect:// URIs, extracting secrets, relays, and remote pubkeys.
  • createNip46Cipher(...): Factory that creates an encryption interface (encrypt/decrypt) based on available libraries and negotiated algorithms.
  • decryptNip46PayloadWithKeys(...): Helper to brute-force decrypt an incoming message using all available algorithms (handling legacy vs modern formats).

Error Handling

  • Timeouts: RPC calls have a default timeout (15s).
  • Rate Limiting: If relays reply with rate-limited, the client performs exponential backoff.
  • Session Recovery: On page load, readStoredNip46Session rehydrates the session from localStorage to resume without re-connecting.

When to change

  • Protocol Updates: If NIP-46 or NIP-44 standards are updated (e.g. new encryption vectors, new RPC methods).
  • Security Audit: If a security review identifies weaknesses in the ephemeral key handling or encryption flow.
  • Relay Logic: If the strategy for relay selection or rate-limiting needs to be optimized for better connectivity.