This Capacitor plugin allows your application to interact with Nostr signer apps on the Android platform using intents, following the NIP-55 specification.
npm install nostr-signer-capacitor-plugin
npx cap sync- Android: minSdkVersion 23 or higher.
- Java 21 (JDK 21) for building Android with Gradle.
- Capacitor 7.x.
Current version: 0.0.5
- Bumped Android
minSdkVersionto 23 to align with Capacitor Android requirements. - Added Android unit tests (Robolectric) covering Content Resolver flows and rejected-provider fallback.
- Added npm scripts:
test:ts– runs TypeScript/Jest teststest:android– runs Android unit tests (testDebugUnitTest)test:all(defaultnpm test) – runs both suites
- Added GitHub Actions workflow at
.github/workflows/ci.ymlto run JS and Android unit tests in CI.
Run TypeScript tests:
npm run test:tsRun Android unit tests:
cd android
./gradlew clean testDebugUnitTest --no-daemon --stacktrace --info --console=plainRun all tests:
npm testimport NostrSignerPlugin from 'nostr-signer-capacitor-plugin';Before using the plugin on Android, you need to set the package name of the external signer app.
await NostrSignerPlugin.setPackageName({ packageName: 'com.example.signer' });const { installed } = await NostrSignerPlugin.isExternalSignerInstalled();
if (!installed) {
console.log('External signer app is not installed.');
}try {
const result = await NostrSignerPlugin.getInstalledSignerApps();
signerApps = result.apps;
console.log('Installed Signer Apps:', signerApps);
} catch (error) {
console.error('Error getting installed signer apps:', error);
}The AppInfo object has the following fields"
export interface AppInfo {
name: string; // The name of the app as it appears in the System launcher
packageName: string; // The package name of the app - pass this to setPackageName
iconData: string; // the base 64 encoded string of the app's icon
iconUrl: string; // the url to app's icon
}try {
const { npub } = await NostrSignerPlugin.getPublicKey();
console.log('Public Key:', npub);
} catch (error) {
console.error('Error getting public key:', error);
}const event = {
kind: 1,
content: 'Hello, Nostr!',
tags: [],
created_at: Math.floor(Date.now() / 1000),
};
try {
const { event: signedEventJson } = await NostrSignerPlugin.signEvent({
eventJson: JSON.stringify(event),
});
const signedEvent = JSON.parse(signedEventJson);
console.log('Signed Event:', signedEvent);
} catch (error) {
console.error('Error signing event:', error);
}try {
const { result: encryptedText } = await NostrSignerPlugin.nip04Encrypt({
pubKey: 'recipient_public_key',
plainText: 'Secret message',
});
console.log('Encrypted Text:', encryptedText);
} catch (error) {
console.error('Error encrypting message:', error);
}try {
const { result: decryptedText } = await NostrSignerPlugin.nip04Decrypt({
pubKey: 'sender_public_key',
encryptedText: 'encrypted_text',
});
console.log('Decrypted Text:', decryptedText);
} catch (error) {
console.error('Error decrypting message:', error);
}try {
const { result: encryptedText } = await NostrSignerPlugin.nip44Encrypt({
pubKey: 'recipient_public_key',
plainText: 'Secret message',
});
console.log('Encrypted Text (NIP-44):', encryptedText);
} catch (error) {
console.error('Error encrypting message (NIP-44):', error);
}try {
const { result: decryptedText } = await NostrSignerPlugin.nip44Decrypt({
pubKey: 'sender_public_key',
encryptedText: 'encrypted_text',
});
console.log('Decrypted Text (NIP-44):', decryptedText);
} catch (error) {
console.error('Error decrypting message (NIP-44):', error);
}try {
const { result: decryptedEventJson } = await NostrSignerPlugin.decryptZapEvent({
eventJson: JSON.stringify(encryptedEvent),
});
const decryptedEvent = JSON.parse(decryptedEventJson);
console.log('Decrypted Zap Event:', decryptedEvent);
} catch (error) {
console.error('Error decrypting zap event:', error);
}getInstalledSignerApps(...)setPackageName(...)isExternalSignerInstalled()getPublicKey()signEvent(...)nip04Encrypt(...)nip04Decrypt(...)nip44Encrypt(...)nip44Decrypt(...)decryptZapEvent(...)
getInstalledSignerApps() => Promise<{ apps: AppInfo[] }>Returns a list of AppInfo objects which contain information about which Signer apps are installed.
Returns: Promise<{ apps: AppInfop[] }>
setPackageName(options: { packageName: string; }) => Promise<void>Sets the package name of the external Nostr signer app. This is required on Android to specify which app to interact with.
| Param | Type | Description |
|---|---|---|
options |
{ packageName: string; } |
An object containing the package name string. |
isExternalSignerInstalled() => Promise<{ installed: boolean; }>Checks if the external Nostr signer app is installed on the device.
Returns: Promise<{ installed: boolean; }>
An object indicating whether the signer app is installed.
getPublicKey() => Promise<{ npub: string; }>Requests the public key from the Nostr signer app or extension.
Returns: Promise<{ npub: string; }>
An object containing the public key in npub format.
signEvent(options: { eventJson: string; }) => Promise<{ event: string; }>Requests the signer app to sign a Nostr event.
| Param | Type | Description |
|---|---|---|
options |
{ eventJson: string; } |
An object containing the event in JSON string format. |
Returns: Promise<{ event: string; }>
An object containing the signed event in JSON string format.
nip04Encrypt(options: { plainText: string; pubKey: string; }) => Promise<{ result: string; }>Encrypts a message using NIP-04 encryption.
| Param | Type | Description |
|---|---|---|
options |
{ plainText: string; pubKey: string; } |
An object containing the plaintext and the recipient's public key. |
Returns: Promise<{ result: string; }>
An object containing the encrypted text.
nip04Decrypt(options: { encryptedText: string; pubKey: string; }) => Promise<{ result: string; }>Decrypts a message using NIP-04 decryption.
| Param | Type | Description |
|---|---|---|
options |
{ encryptedText: string; pubKey: string; } |
An object containing the encrypted text and the sender's public key. |
Returns: Promise<{ result: string; }>
An object containing the decrypted plaintext.
nip44Encrypt(options: { plainText: string; pubKey: string; }) => Promise<{ result: string; }>Encrypts a message using NIP-44 encryption.
| Param | Type | Description |
|---|---|---|
options |
{ plainText: string; pubKey: string; } |
An object containing the plaintext and the recipient's public key. |
Returns: Promise<{ result: string; }>
An object containing the encrypted text.
nip44Decrypt(options: { encryptedText: string; pubKey: string; }) => Promise<{ result: string; }>Decrypts a message using NIP-44 decryption.
| Param | Type | Description |
|---|---|---|
options |
{ encryptedText: string; pubKey: string; } |
An object containing the encrypted text and the sender's public key. |
Returns: Promise<{ result: string; }>
An object containing the decrypted plaintext.
decryptZapEvent(options: { eventJson: string; }) => Promise<{ result: string; }>Decrypts a zap event.
| Param | Type | Description |
|---|---|---|
options |
{ eventJson: string; } |
An object containing the encrypted zap event in JSON string format. |
Returns: Promise<{ result: string; }>
An object containing the decrypted zap event in JSON string format.
- On Android, the plugin communicates with external signer apps using intents and the Content Resolver as per NIP-55.
- Ensure that the external signer app or browser extension supports the required NIP specifications.