Class VoucherBackupPayload

java.lang.Object
xyz.tcheeric.cashu.voucher.nostr.events.VoucherBackupPayload

public class VoucherBackupPayload extends Object
Handles voucher backup payload serialization using NIP-17 private messaging.

This class implements secure, private voucher backups using:

  • NIP-17: Private Direct Messages (sealed sender pattern)
  • NIP-44: Versioned encryption (XChaCha20-Poly1305)

Backup Architecture

Voucher backups are encrypted and stored as private messages to self:

  1. User creates vouchers with their Nostr identity
  2. Vouchers are encrypted with user's public key (NIP-44)
  3. Encrypted payload is stored in kind 4 event (encrypted DM to self)
  4. Event is published to relays
  5. Only user can decrypt and restore their vouchers

Privacy Guarantees

  • Content encryption: Only user can read voucher data
  • Self-addressed: Messages sent to user's own pubkey
  • NIP-44 encryption: Modern versioned encryption scheme

Payload Structure

 {
   "version": "1.0",
   "vouchers": [
     {
       "voucher": <SignedVoucher JSON>,
       "backedUpAt": <unix timestamp>
     }
   ],
   "metadata": {
     "totalCount": 5,
     "backupTimestamp": <unix timestamp>
   }
 }
 

Usage Example

 // Backup vouchers
 List<SignedVoucher> vouchers = ...;
 String userPrivkey = "...";
 String userPubkey = "...";

 GenericEvent backupEvent = VoucherBackupPayload.createBackupEvent(
     vouchers, userPrivkey, userPubkey
 );

 // Restore vouchers
 List<SignedVoucher> restored = VoucherBackupPayload.extractVouchers(
     backupEvent, userPrivkey, userPubkey
 );
 
See Also:
  • Field Details

    • PAYLOAD_VERSION

      public static final String PAYLOAD_VERSION
      Current payload version.
      See Also:
    • KIND_ENCRYPTED_DM

      public static final int KIND_ENCRYPTED_DM
      Event kind 4.

      Audit M-30 read this as "backups use deprecated NIP-04". The encryption is NIP-44 (see nostr.crypto.nip44.EncryptedPayloads below): XChaCha20-Poly1305 with a conversation key, not NIP-04's unauthenticated AES-CBC. What is NIP-04 here is only the event kind, because kind 4 is the number NIP-04 defined and nostr-java exposes it under that name.

      Kept at 4 deliberately. These are self-addressed backups, so no other client has to interpret them, and existing backups are readable only at this kind: changing it would strand every backup already published. NIP-17's kind 14 with gift wrapping would hide the metadata that kind 4 exposes (that these two keys exchanged something, and when), which is worth doing, but it is a migration with a compatibility window rather than a constant change.

      See Also:
    • TAG_BACKUP

      public static final String TAG_BACKUP
      Tag for backup identification.
      See Also:
  • Constructor Details

    • VoucherBackupPayload

      public VoucherBackupPayload()
  • Method Details

    • createBackupEvent

      public static nostr.event.impl.GenericEvent createBackupEvent(@NonNull @NonNull List<SignedVoucher> vouchers, @NonNull @NonNull String userPrivkey, @NonNull @NonNull String userPubkey)
      Creates an encrypted backup event for vouchers.

      This method:

      1. Serializes vouchers to JSON payload
      2. Encrypts payload with NIP-44
      3. Creates kind 4 event (encrypted DM to self)
      4. Signs with user's private key
      Parameters:
      vouchers - list of vouchers to backup (must not be null)
      userPrivkey - user's Nostr private key (hex, must not be null)
      userPubkey - user's Nostr public key (hex, must not be null)
      Returns:
      encrypted event ready for publishing
      Throws:
      VoucherNostrException - if encryption or serialization fails
    • extractVouchers

      public static List<SignedVoucher> extractVouchers(@NonNull @NonNull nostr.event.impl.GenericEvent event, @NonNull @NonNull String userPrivkey, @NonNull @NonNull String userPubkey)
      Extracts vouchers from an encrypted backup event.

      This method:

      1. Extracts encrypted content from event
      2. Decrypts content with NIP-44
      3. Deserializes JSON payload
      4. Extracts voucher list
      Parameters:
      event - the encrypted backup event (must not be null)
      userPrivkey - user's Nostr private key for decryption (must not be null)
      userPubkey - user's Nostr public key for decryption (must not be null)
      Returns:
      list of restored vouchers (never null, may be empty)
      Throws:
      VoucherNostrException - if decryption or deserialization fails
    • isValidBackupEvent

      public static boolean isValidBackupEvent(nostr.event.impl.GenericEvent event)
      Checks if an event is a valid voucher backup event.
      Parameters:
      event - the event to check
      Returns:
      true if valid backup event, false otherwise