Skip to content

Non-custodial Architecture ​

This page explains how PayWarden keeps your funds safe — and why our architecture means we legally cannot take your money even if we wanted to.

The core principle ​

PayWarden never holds private keys. We run software infrastructure. You hold your funds.

This distinction matters legally and technically:

  • Custodial services (hosted payment gateways, exchanges) hold your private keys → they are payment institutions and require licensing
  • PayWarden only holds an encrypted seed on your server → it's software infrastructure, like Nginx or PostgreSQL

How addresses are derived ​

Your 24-word mnemonic
        │
        ▼ BIP39 → 512-bit seed
        │
        ▼ BIP32 master key
        │
        ▼ BIP44 path: m/44'/195'/0'/0/{index}
        │                          │
        │                          └── TRON coin type = 195
        ▼
  Unique TRON address for order #{index}

For receiving payments, PayWarden uses only your extended public key (xpub). The xpub can derive all child public keys (and thus addresses) without ever exposing a private key.

Private keys are derived only during fund sweeping — when confirmed funds are moved from a payment address to your cold wallet at COLD_WALLET_ADDRESS. After the sweep transaction is signed and broadcast, the private key buffer is immediately overwritten:

typescript
// From fund-sweeper.ts
const privateKey = derivePrivateKey(mnemonic, index)
await broadcastTransaction(privateKey, ...)
privateKey.fill(0)  // wipe from memory immediately

Vault encryption ​

Your mnemonic is stored encrypted on disk in the vault/ directory (as vault/seed.enc):

vault/seed.enc = AES-256-GCM(
  plaintext: mnemonic,
  key:       VAULT_KEY (from environment variable),
  iv:        random 12 bytes (stored with ciphertext),
  tag:       16-byte authentication tag (detects tampering)
)

The VAULT_KEY never touches the database or logs. It lives only in your environment variables. If an attacker steals vault/seed.enc but not VAULT_KEY, they cannot decrypt your mnemonic.

What the server stores ​

DataStorageEncrypted?
Mnemonicvault/seed.enc file✅ AES-256-GCM
xpubPostgreSQL❌ (public key, safe to store)
Derived addressesPostgreSQL❌ (public)
Private keysRAM only, during sweep✅ wiped after use
API keysPostgreSQL✅ HMAC hashed
Order dataPostgreSQL❌

Recovery scenario ​

If your server is completely destroyed:

  1. Restore VAULT_KEY from your secure backup
  2. Start a fresh PayWarden instance
  3. Re-initialize with your 24-word mnemonic
  4. Re-run db:migrate
  5. PayWarden re-derives all the same addresses deterministically

All historical orders can be reconstructed from blockchain data using your xpub.

Cloud version ​

The non-custodial guarantees described above apply to the self-hosted version, where your vault key and mnemonic never leave your server.

The cloud version (currently in beta) uses server-side envelope encryption: your mnemonic is encrypted with an AWS KMS-managed key. This protects data at rest, but the server holds the decryption capability — it does not yet implement a merchant-held second factor. See Cloud Architecture for details.

Our goal is to reach a true non-custodial model for the cloud version (merchant-held second factor, where neither party alone can decrypt the vault). This is on the roadmap but not yet implemented.

Released under the BSL 1.1 License.