Interface VoucherBackupPort
This port defines the interface for backing up and restoring vouchers to/from private user storage. In the Nostr implementation, this uses NIP-17 (private direct messages) with NIP-44 (versioned encryption) for secure, private backup.
Hexagonal Architecture
This is a port (interface) in hexagonal architecture terminology. The actual implementation (adapter) is provided by the infrastructure layer (e.g., NostrVoucherBackupRepository in cashu-voucher-nostr module).
Design Principles
- Infrastructure-agnostic: No dependencies on Nostr or any specific storage
- Pluggable: Can be implemented with Nostr, IPFS, encrypted cloud storage, etc.
- Privacy-first: Backups are private to the user (encrypted with user's key)
- Testable: Easy to mock for unit testing application services
Why Backup is Needed
Unlike deterministic secrets (NUT-13), vouchers are non-deterministic. If a user loses their wallet, they cannot regenerate voucher secrets from a seed phrase. Therefore, vouchers MUST be backed up to recoverable storage.
Security Considerations
- Backups are encrypted with the user's Nostr private key (or equivalent)
- Only the user can decrypt their own voucher backups
- The backup port does not handle encryption - that's the adapter's responsibility
Example Usage
// Backing up vouchers List<SignedVoucher> vouchers = wallet.getVouchers(); backupPort.backup(vouchers, userNostrPrivateKey); // Restoring vouchers after wallet loss List<SignedVoucher> restored = backupPort.restore(userNostrPrivateKey); wallet.addVouchers(restored);
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionvoidbackup(List<SignedVoucher> vouchers, String userPrivateKey) Backs up a list of vouchers to private user storage.default voiddeleteBackups(String userPrivateKey) Deletes all backups for the given user.default booleanhasBackups(String userPrivateKey) Checks if backups exist for the given user.Restores vouchers from private user storage.
-
Method Details
-
backup
Backs up a list of vouchers to private user storage.This method encrypts and stores the vouchers in a way that only the user can retrieve them using their private key. The backup is typically incremental - calling this multiple times may append to or replace previous backups depending on the implementation.
Backup format considerations:
- Vouchers should be serialized in a stable format
- Backup should include metadata (timestamp, version)
- Encryption should use strong, modern algorithms (e.g., NIP-44)
Implementation Notes
Nostr implementation (NIP-17 + NIP-44):
- Creates encrypted DM to self with voucher data
- Uses NIP-44 versioned encryption
- Tags with "cashu-voucher-backup" for easy retrieval
- Parameters:
vouchers- the list of vouchers to backup (must not be null, can be empty)userPrivateKey- the user's private key for encryption (format depends on implementation)- Throws:
IllegalArgumentException- if parameters are invalidRuntimeException- if backup fails (network, storage, encryption, etc.)
-
restore
Restores vouchers from private user storage.This method retrieves and decrypts all voucher backups associated with the user's private key. If multiple backups exist, they are merged and deduplicated (by voucher ID, with newest timestamp winning).
The returned list includes all vouchers that have ever been backed up, regardless of their current status. The caller is responsible for:
- Checking voucher status against the public ledger
- Filtering out redeemed/expired vouchers if desired
- Handling conflicts with existing wallet state
Implementation Notes
Nostr implementation:
- Queries all NIP-17 DMs tagged "cashu-voucher-backup"
- Decrypts each using NIP-44
- Merges and deduplicates by voucher ID
- Parameters:
userPrivateKey- the user's private key for decryption (format depends on implementation)- Returns:
- list of restored vouchers (never null, but may be empty if no backups found)
- Throws:
IllegalArgumentException- if userPrivateKey is invalidRuntimeException- if restore fails (network, storage, decryption, etc.)
-
hasBackups
Checks if backups exist for the given user.This is a convenience method to check if any voucher backups are available without actually downloading and decrypting them.
Default implementation calls
restore(String)and checks if the result is non-empty. Implementations may override with a more efficient check.- Parameters:
userPrivateKey- the user's private key (format depends on implementation)- Returns:
- true if at least one backup exists, false otherwise
- Throws:
IllegalArgumentException- if userPrivateKey is invalidRuntimeException- if check fails (network, storage, etc.)
-
deleteBackups
Deletes all backups for the given user.This is an optional operation. Some implementations may not support deletion (e.g., immutable storage like Nostr relays). Default implementation throws UnsupportedOperationException.
Warning: This operation is destructive and cannot be undone. Use with caution.
- Parameters:
userPrivateKey- the user's private key (format depends on implementation)- Throws:
UnsupportedOperationException- if implementation doesn't support deletionIllegalArgumentException- if userPrivateKey is invalidRuntimeException- if deletion fails (network, storage, etc.)
-