nostr-app-storage
v0.1.0
Published
Nostr storage library implementing NIP-60 wallet storage, NIP-78 settings sync, and NIP-42 relay authentication.
Maintainers
Readme
nostr-app-storage
A TypeScript/JavaScript library for storing application data on Nostr relays. Provides encrypted backup, cross-device sync, and decentralized data storage.
Features
- NIP-60 Cashu Wallet Storage: Encrypted wallet proofs and transaction history
- NIP-78 Application Settings: Cross-device settings synchronization
- NIP-42 Relay Authentication: Secure access to auth-required relays
- Automatic Chunking: Large data automatically split using nostr-chunked-events
- NIP-44 Encryption: All content encrypted to owner's pubkey
- Local-First: IndexedDB caching for offline access
Installation
npm install nostr-app-storage nostr-toolsQuick Start
import { initAppStorage } from 'nostr-app-storage';
const storage = await initAppStorage({
pubkey: myPubkeyHex,
relays: ['wss://relay.damus.io', 'wss://nos.lol'],
signer: mySigner, // EventSigner for signing events
compression: true, // Enable gzip for large data
settingsTag: 'myapp:settings',
});
// Settings
const settings = await storage.settings.get();
await storage.settings.save({ theme: 'dark', language: 'en' });
await storage.settings.sync();
// Wallet
await storage.wallet.storeProofs(proofData, mintUrl, 'default-wallet');
const proofs = await storage.wallet.getProofs('default-wallet');
// Cleanup
await storage.close();API Reference
initAppStorage(options)
Factory function that creates an AppStorage instance.
interface AppStorageOptions {
pubkey: string; // Your Nostr public key (hex)
relays: string[]; // Relay URLs
signer: EventSigner; // For signing events
compression?: boolean; // Enable gzip compression
settingsTag?: string; // Custom d-tag for settings (default: 'nostr-app:settings')
settingsEncrypted?: boolean; // Encrypt settings on relay
}Settings API
// Get current settings (local-first, instant)
const settings = await storage.settings.get<MySettings>();
// Save settings (saves locally + syncs to relay)
await storage.settings.save({ theme: 'dark' });
// Sync with relay (fetch + merge)
const result = await storage.settings.sync();
// result.source: 'local' | 'remote' | 'unchanged'
// Listen for changes from other devices
storage.settings.onChange((newSettings, source) => {
console.log('Settings changed from', source);
});
// Get sync status
const status = await storage.settings.getSyncStatus();Wallet API
// Store proofs
await storage.wallet.storeProofs(proofData, mintUrl, walletDTag);
// Get proofs
const proofs = await storage.wallet.getProofs(walletDTag);
// Record transaction
await storage.wallet.recordTransaction({
direction: 'in',
txType: 'voucher_received',
amount: 1000,
counterparty: senderPubkey,
timestamp: Date.now(),
});
// Get transaction history
const transactions = await storage.wallet.getTransactions({ limit: 50 });
// Publish wallet state to relay (auto-chunks large wallets)
await storage.wallet.publishWalletState(walletState);
// Restore wallet from relay
const result = await storage.wallet.fetchWalletState();NIP-42 Authentication
import { Nip42Auth, createAuthenticatedConnection } from 'nostr-app-storage';
// Set global credentials
Nip42Auth.setCredentials({ pubkey: myPubkey, privateKey: myPrivateKey });
// Or use a signer
Nip42Auth.setSigner(mySigner);
// Create authenticated WebSocket
const conn = await createAuthenticatedConnection({
relayUrl: 'wss://relay.example.com',
pubkey: myPubkey,
signer: mySigner,
timeout: 10000,
onReady: (ws) => console.log('Authenticated!'),
});
// conn.ws - WebSocket instance
// conn.authenticated - boolean
// conn.close() - cleanupLow-Level APIs
SettingsSync
import { SettingsSync } from 'nostr-app-storage';
const settingsSync = new SettingsSync({
pubkey: myPubkey,
relays: ['wss://relay.example.com'],
dTag: 'myapp:settings',
encrypted: false,
signer: mySigner,
compression: true,
chunkThreshold: 350000,
});
await settingsSync.init();
await settingsSync.saveSettings({ theme: 'dark' });
const result = await settingsSync.sync();WalletStateSync
import { WalletStateSync } from 'nostr-app-storage';
const walletSync = new WalletStateSync({
pubkey: myPubkey,
signer: mySigner,
relays: ['wss://relay.example.com'],
crypto: myCrypto,
compression: true,
});
await walletSync.publishWalletState({ tokens: [...], history: [...] });
const result = await walletSync.fetchWalletState();Event Builders
import { Nip60EventBuilder, SettingsEventBuilder } from 'nostr-app-storage';
// Wallet events
const walletBuilder = new Nip60EventBuilder();
const walletEvent = walletBuilder.buildWalletEvent({
dTag: 'my-wallet',
name: 'Main Wallet',
unit: 'sat',
mints: ['https://mint.example.com'],
}, myPubkey);
// Settings events
const settingsBuilder = new SettingsEventBuilder();
const settingsEvent = settingsBuilder.buildSettingsEvent({
dTag: 'myapp:settings',
settings: { theme: 'dark' },
}, myPubkey);Event Kinds
| Kind | Description | |------|-------------| | 17375 | Wallet Metadata (Parameterized Replaceable) | | 7375 | Proof Storage (Regular Event) | | 7376 | Transaction History (Regular Event) | | 37375 | Wallet State (Parameterized Replaceable, Chunked) | | 30078 | Application Settings (Parameterized Replaceable) | | 22242 | AUTH Event (NIP-42) |
Conflict Resolution
Settings sync uses a last-write-wins strategy based on timestamps:
| Scenario | Resolution | |----------|------------| | Only local exists | Push to relay | | Only remote exists | Save locally | | Both exist, remote newer | Use remote | | Both exist, local newer | Use local |
Custom merge functions can be provided:
const result = await storage.settings.sync((local, remote) => {
// Custom merge logic
return {
settings: { ...local.settings, ...remote.settings },
source: 'merged'
};
});Large Data Handling
The library automatically handles large data using nostr-chunked-events:
- Wallet states > 350KB are automatically chunked
- Settings > 350KB are automatically chunked
- Compression reduces size before chunking (when enabled)
- Reassembly is automatic when fetching
TypeScript Support
Full TypeScript support with comprehensive type definitions:
import type {
AppStorage,
AppStorageOptions,
SettingsSyncResult,
WalletState,
TransactionRecord,
CashuProof,
} from 'nostr-app-storage';Dependencies
nostr-tools>= 2.0.0 (peer dependency)nostr-chunked-events^0.1.0idb^7.0.0
License
MIT
