relay-connect
NIP-46 / NIP-07 SDK and connect UI. MIT, composable, browser-safe.
relay-connect
Two MIT repositories, one purpose: NIP-46 remote signing without exposing keys in the browser.
| Repo | Package | Role |
|---|---|---|
github.com/bitmacro/relay-connect | @bitmacro/relay-connect (npm) | Shared logic — NIP-46 / NIP-07 helpers |
github.com/bitmacro/relay-connect-web | — | Reference UI (Next.js); consumes the SDK |
relay-connect-web
Stack: Next.js, React
Hosting: Vercel or self-hosted
Auth model: No user login. Server-side proxy only — RELAY_API_KEY + SIGNER_PROVIDER_USER_ID in env vars; browser never holds the key.
What it does
NIP-46 path
- Fetches available relays for the operator via
GET /api/signer/config - Initiates a NIP-46 session via
POST /api/signer/connect→ returnsnostrconnect://URI - Renders QR code for the Signer Device (Amber or any NIP-46 compatible app) to scan
- Watches the bridge relay for pairing progress (client + signer over
bridge_wss) - Completes pairing via
POST /api/signer/session/:id/complete(verifiespairing_secretserver-side)
NIP-07 path
- Detects
window.nostrandgetPublicKeyin the browser - Reads the user’s pubkey from the extension (no
RELAY_API_KEYin the page) - Optionally fetches kind 0 profile data via
SimplePoolusingNEXT_PUBLIC_NIP07_METADATA_RELAYS(and defaults) — see NIP-07
NIP-07 does not create a nip46_sessions row by itself; it is for same-machine extension signing and lightweight onboarding UI. Starting NIP-46 clears NIP-07 sessionStorage so flows stay isolated.
Server-side proxy pattern
Browser
│ HTTPS (no API key)
▼
relay-connect-web /api/signer/* ← Next.js server route
│ X-API-Key + X-Provider-User-Id (from env)
▼
relay-api [Web Server] /signer/*
│ SUPABASE_SERVICE_ROLE_KEY
▼
Supabase relay.nip46_sessionsThe browser only interacts with the Next.js server. The operator's RELAY_API_KEY is never exposed to clients.
@bitmacro/relay-connect (SDK)
Shared helpers for NIP-46 and NIP-07 flows. Consumed by relay-connect-web and planned for relay-panel.
- NIP-46 session lifecycle utilities
- NIP-07 extension detection + metadata relay resolution
- Bridge WebSocket subscription helpers
Extraction from relay-connect-web to the SDK is ongoing — the boundary moves as patterns stabilize.
Web Server Endpoints (relay-api /signer/*)
Also mounted at /api/signer/*.
| Method | Route | Description |
|---|---|---|
| GET | /signer/config | Relays available for provider_user_id (from relay_configs) |
| POST | /signer/connect | Create nip46_sessions row; returns session_id + nostrconnect_uri |
| GET | /signer/sessions | List sessions (use for polling) |
| POST | /signer/session/:id/complete | Verify pairing_secret → set status active |
| DELETE | /signer/session/:id | Revoke session |
Environment Variables
| Variable | Description |
|---|---|
RELAY_API_URL | Web Server base URL |
RELAY_API_KEY | Shared key (server-side only) |
SIGNER_PROVIDER_USER_ID | GitHub ID for the operator's relay_configs rows |
NEXT_PUBLIC_RELAY_BRIDGE_WSS | Force a specific NIP-46 bridge wss://; also used for NIP-07 metadata relays |
NEXT_PUBLIC_NIP07_METADATA_RELAYS | Comma/space-separated wss:// for kind 0 lookup after NIP-07 connect |
NEXT_PUBLIC_RELAY_CONFIG_ID | Pin a specific relay_configs UUID; skips relay selection step |
SIGNER_PROXY_TIMEOUT_MS | Proxy timeout in ms (default unset = Node default) |
Signer Device Compatibility
Any NIP-46 compliant remote signer works. Tested:
- Amber (Android) — recommended; native
nostrconnect://URI handling - Any NIP-46 bridge-compatible signer
The nostrconnect:// URI encodes the pairing_secret and bridge_wss in the query string. Each new Connect flow generates a fresh pairing_secret — reusing an existing active session without re-scanning is a product choice not implemented by default.