# Trezor Safekeeping Flow This flow adds hardware-backed custody controls without replacing the current payment model. The backend never stores private keys. Trezor support starts as a single hardware signer and is designed to upgrade to multisig later. Default mode: optional. Existing release/refund flows do not require Trezor proof unless `TREZOR_SAFEKEEPING_REQUIRED=true`. ## Goals - Generate a fresh receive address per user/payment from a registered Trezor xpub. - Require a Trezor-produced signature before release/refund confirmation when safekeeping enforcement is enabled. - Keep the Request Network payment adapter and legacy provider abstractions intact while adding custody controls. - Preserve the existing `Payment` model and orchestration surface. ## Registration 1. User connects a Trezor in the frontend and exports an Ethereum account xpub, for example `m/44'/60'/0'`. 2. Backend builds a registration challenge: - `GET /api/trezor/registration-message?xpub=...®istrationAddress=...` 3. The registration address must be the first derived address from the xpub: - `m/44'/60'/0'/0/0` 4. User signs the challenge with that Trezor address. 5. Frontend submits: - `POST /api/trezor/register` - `xpub` - `registrationAddress` - `proofMessage` - `proofSignature` - optional `basePath`, `deviceLabel` 6. Backend verifies: - xpub is public, not private. - registration address matches xpub-derived index `0`. - signature recovers the registration address. 7. Backend stores only: - `userId` - xpub fingerprint - xpub - base derivation path - registration address - next address index - issued address records ## Address Generation To issue the next payment address: ```http POST /api/trezor/addresses/next { "purpose": "deposit", "paymentId": "..." } ``` The backend derives non-hardened receive addresses from the registered xpub: ```text m/44'/60'/0'/0/{index} ``` If a `paymentId` already has an address, the endpoint returns the same address instead of incrementing the index. ## Transaction Approval Before a release/refund confirmation, the admin asks the backend for the exact operation message: ```http POST /api/trezor/operation-message { "operation": "release", "paymentId": "...", "transactionHash": "0x...", "amount": 100, "currency": "USDT", "provider": "request.network" } ``` The Trezor signs that message. Release/refund confirmation then includes: ```json { "txHash": "0x...", "trezor": { "message": "Amanat escrow Trezor transaction approval\n...", "signature": "0x..." } } ``` When `TREZOR_SAFEKEEPING_REQUIRED=true`, `confirmReleaseRefundInstruction` verifies the signature before calling the payment adapter confirmation path. ## Enforcement Flag ```env TREZOR_SAFEKEEPING_REQUIRED=false ``` Default is permissive so existing Request Network release/refund flows continue to work. Set it to `true` only after registering the operating admin's Trezor account and testing the signing path. Any value other than the literal string `true` is treated as disabled. ## Safety Rules - Never store Trezor seed words, private keys, or xprv/tprv values. - Reject private extended keys at registration. - Verify every signature locally before accepting it. - Use exact transaction-intent messages; do not accept free-form signatures. - Treat generated deposit addresses as public routing metadata, not as proof of payment. - Keep ledger availability checks enabled for release/refund accounting. ## Upgrade Path To Multisig The current design stores a single `trezor-eoa` signer. The recommended production path is to replace the signer policy with: - `addressType: safe-multisig` - a Safe address per tenant/admin group - threshold policy, such as `2-of-3` - Trezor owners as Safe signers - release/refund flow creates a Safe transaction and records collected signatures before execution The payment orchestration API should stay the same: build instruction, collect hardware-backed approval, confirm release/refund, append ledger entry. See [[PRD - Decentralized Custody and Smart-Contract Escrow Roadmap]] for the staged Safe-first path before any custom escrow contract.