Skip to main content

Key custody options

OptionPostureTrust
Platform keychainDefaultmacOS Keychain, Windows Credential Manager, Gnome Keyring. OS-isolated.
Hardware walletRecommended for high-valueLedger / Trezor. Requires physical confirmation per signature — not suitable for per-MB voucher signing at scale.
Safe (1-of-1 or multi-owner)Recommended for high-value + high-frequencySmart-account. Signs via ERC-1271. Holds a hot session key for voucher signing.
File-based keyDiscouragedPlaintext keys in filesystem leak to any process that reads your home directory.
Vouchers are signed per MB. A 1 GiB download at 1 MB cadence requires 1024 signatures. You do not want to tap a hardware wallet 1024 times. Safe + session keys (ERC-7579 smartsessions) give you:
  • A long-lived owner key held in high-security storage (hardware wallet, platform keychain).
  • A short-lived session key the Safe authorizes for a bounded window and bounded scope.
  • Spending caps — session key can sign vouchers totalling at most X USDC.
  • Selector scoping — session key can only sign this contract function (e.g., voucher EIP-712) for this channel.
  • Time-bounded — session key auto-expires at T.
  • Revocable — the owner can revoke the session key at any time on-chain.
Compromise of the session key costs at most the cap and expires quickly. Compromise of the owner key is the catastrophic scenario — keep it offline where possible.

Safe + session keys

# Configure the Safe
decdn client init \
    --eth-safe 0xSAFE... \
    --eth-key-owner keychain:com.example.owner

# Create a scoped session key
decdn client session create \
    --safe 0xSAFE... \
    --duration 24h \
    --spending-cap-usdc 50 \
    --policy voucher-signing-only

# Returns a session key id. That id is used for subsequent operations:
decdn client fetch --hash X... --session-key <id>
The session key:
  • Signs only voucher-format EIP-712 payloads for the scoped channel.
  • Expires at the configured time.
  • Can be revoked early: decdn client session revoke <id>.

SignatureChecker everywhere

All contract signature checks use OpenZeppelin’s SignatureChecker.isValidSignatureNow rather than ECDSA.recover. This means:
  • EOA signatures (raw ecrecover) — fully supported, ~5.6k gas.
  • ERC-1271 signatures (Safe, other smart accounts) — supported uniformly, ~12–15k gas.
  • EIP-712 domains and voucher formats — unchanged regardless of wallet type.
The verifier already knows the claimed signer (from the channel’s counterparty field, from the NodeInfo binding, etc.) — it validates the signature against that address rather than recovering it from the signature.

Key rotation

  • Session key — create a new one, revoke the old one. No protocol-level migration needed.
  • Safe owner — use Safe’s native swapOwner governance. All outstanding channels survive; the Safe address doesn’t change.
  • EOA key — create a new address, open new channels from it. Existing channels belong to the old address and settle to the old address on close.

Clients-side gas considerations

Every channel open and close is on-chain. At Arbitrum gas prices:
  • openChannel: ~80–120k gas
  • closeChannel (happy path): ~90k gas
  • disputeChannel: ~150k gas (rare)
Budget a small ETH balance on Arbitrum for these operations. For bursty usage, amortize by opening fewer, larger channels rather than many small ones.

What the CDN never sees

  • Your Ethereum private keys — all signatures happen on-device.
  • Your plaintext content for encrypted blobs — the app server delivers keys directly to you over cdn/keys/v1 (encryption).
  • Your OAuth / session tokens with the content provider — those exchange only with the provider’s app server.
See trust model for the full boundary.