Files
welshman/docs/signer/nip-55.md
T
2025-02-25 14:07:44 -08:00

166 lines
3.8 KiB
Markdown

# NIP-55 (Native App) Signer
The `Nip55Signer` implements the `ISigner` interface by communicating with native mobile signing applications through the Capacitor plugin system. This implementation is particularly useful for mobile applications that want to leverage native Nostr signing capabilities.
## Prerequisites
The signer requires the Capacitor plugin to be installed:
```bash
npm install nostr-signer-capacitor-plugin
```
## Getting Started
```typescript
import { Nip55Signer, getNip55 } from '@welshman/signer'
// Check for available signing apps
const apps = await getNip55()
if (apps.length > 0) {
const signer = new Nip55Signer(apps[0].packageName)
}
```
## API Reference
### Detecting Available Signers
```typescript
// Returns information about installed signing apps
getNip55(): Promise<AppInfo[]>
interface AppInfo {
name: string
packageName: string
// Other app-specific information
}
```
### Constructor
```typescript
constructor(packageName: string)
```
Creates a new signer instance that will communicate with the specified native app.
- `packageName`: The package identifier of the native signing app
### ISigner implementation
The `Nip55Signer` class implements the [`ISigner`](/signer/) interface
```typescript
class Nip55Signer implements ISigner {
// Constructor
constructor(private secret: string)
// ISigner implementation
sign: (event: StampedEvent) => Promise<SignedEvent>
getPubkey: () => Promise<string>
nip04: { encrypt, decrypt }
nip44: { encrypt, decrypt }
}
```
## Complete Example
```typescript
import { Nip55Signer, getNip55 } from '@welshman/signer'
import { createEvent, NOTE } from '@welshman/util'
async function example() {
try {
// Get available signing apps
const apps = await getNip55()
if (apps.length === 0) {
throw new Error('No native signing apps available')
}
// Create signer with first available app
const signer = new Nip55Signer(apps[0].packageName)
// Get public key
const pubkey = await signer.getPubkey()
console.log('Public key:', pubkey)
// Sign an event
const event = createEvent(NOTE, {
content: "Hello from native app!",
tags: [["t", "test"]]
})
const signedEvent = await signer.sign(event)
console.log('Signed event:', signedEvent)
// Encrypt a message
const encrypted = await signer.nip44.encrypt(
recipientPubkey,
"Secret message"
)
console.log('Encrypted:', encrypted)
} catch (error) {
console.error('Native signer error:', error)
}
}
```
## Implementation Details
### Request Serialization
The signer implements a lock mechanism to prevent concurrent requests:
```typescript
class Nip55Signer implements ISigner {
#lock = Promise.resolve()
#plugin = NostrSignerPlugin
#packageName: string
#packageNameSet = false
#then = async <T>(f: (signer: typeof NostrSignerPlugin) => Promise<T>) => {
const promise = this.#lock.then(async () => {
if (!this.#packageNameSet) {
await this.#initialize()
}
return f(this.#plugin)
})
this.#lock = promise.then(() => Promise.resolve())
return promise
}
}
```
### Public Key Caching
The signer caches the public key to minimize native app interactions:
```typescript
class Nip55Signer {
#npub?: string
#publicKey?: string
getPubkey = async (): Promise<string> => {
return this.#then(async signer => {
if (!this.#publicKey || !this.#npub) {
const {npub} = await signer.getPublicKey()
this.#npub = npub
const {data} = decode(npub)
this.#publicKey = data as string
}
return this.#publicKey
})
}
}
```
## Platform Support
- iOS: Requires compatible signing app
- Android: Requires compatible signing app
- Operations availability depends on native app implementation
- Some features might be platform-specific