Files
2026-06-10 14:12:47 -07:00

139 lines
3.3 KiB
Markdown

# NIP-46 (Nostr Connect) Signer
The `Nip46Signer` implements remote signing capabilities through the Nostr Connect protocol (NIP-46). It allows applications to delegate signing operations to a remote signer (like a Nostr Bunker), providing enhanced security by keeping private keys separate from the application.
## Architecture
The implementation consists of two main classes:
- `Nip46Broker`: Handles the communication with the remote signer
- `Nip46Signer`: Implements the `ISigner` interface using the broker
## Example
```typescript
import {
makeSecret,
Nip46Broker,
Nip46Signer
} from '@welshman/signer'
import { makeEvent, NOTE } from '@welshman/util'
async function connectToRemoteSigner() {
// Initial setup
const clientSecret = makeSecret()
const relays = ['wss://relay.example.com']
const broker = new Nip46Broker({ relays, clientSecret })
const signer = new Nip46Signer(broker)
// Generate connection URL
const ncUrl = await broker.makeNostrconnectUrl({
name: "My App",
description: "Testing remote signing"
})
// Show URL to user (e.g., as QR code)
displayQRCode(ncUrl)
try {
// Wait for connection
const abortController = new AbortController()
const response = await broker.waitForNostrconnect(
ncUrl,
abortController.signal
)
// Store signer info for later
const bunkerUrl = broker.getBunkerUrl()
localStorage.setItem('bunkerUrl', bunkerUrl)
// Use the signer
const event = makeEvent(NOTE, {
content: "Signed with remote signer!",
tags: [["t", "test"]]
})
const signed = await signer.sign(event)
return signed
} catch (error) {
if (error?.error) {
console.warn(`Signer error: ${error.error}`)
}
throw error
}
}
// Reconnecting with saved bunker URL
async function reconnect() {
const bunkerUrl = localStorage.getItem('bunkerUrl')
if (!bunkerUrl) return null
const {
signerPubkey,
connectSecret,
relays
} = Nip46Broker.parseBunkerUrl(bunkerUrl)
const broker = new Nip46Broker({
relays,
clientSecret: makeSecret(),
signerPubkey,
connectSecret
})
return new Nip46Signer(broker)
}
```
## Nip46Broker API
### Constructor and Factory
```typescript
// Direct instantiation
new Nip46Broker({
relays: string[],
clientSecret: string,
connectSecret?: string,
signerPubkey?: string,
algorithm?: "nip04" | "nip44"
})
// Static factory: create a broker from a bunker:// URL
Nip46Broker.fromBunkerUrl(url: string): Nip46Broker
```
### Connection Methods
```typescript
// Generate a nostrconnect:// URL for the remote signer
broker.makeNostrconnectUrl(metadata: Record<string, string>): Promise<string>
// Wait for connection approval
broker.waitForNostrconnect(
url: string,
signal: AbortSignal
): Promise<Nip46ResponseWithResult>
// Get bunker URL for later reconnection
broker.getBunkerUrl(): string
// Parse a bunker URL
Nip46Broker.parseBunkerUrl(url: string): {
signerPubkey: string,
connectSecret: string,
relays: string[]
}
```
## Nip46Signer Usage
```typescript
const signer = new Nip46Signer(broker)
// All ISigner operations are available
const pubkey = await signer.getPubkey()
const signed = await signer.sign(event)
const encrypted = await signer.nip44.encrypt(pubkey, "message")
const decrypted = await signer.nip44.decrypt(pubkey, encrypted)
```