How OpenHuman Handles Web3 and x402 Payments: Wallet Domain Gating and Key Protection
OpenHuman gates all blockchain functionality behind a compile-time web3 feature flag, using a facade pattern that swaps real implementations for type-matching stubs when disabled, while protecting Ed25519 signing keys via SLIP-0010 derivation and in-process-only consumption.
OpenHuman's blockchain integration centers on the openhuman::web3 domain, which provides secure wallet management and machine-payable HTTP 402 protocol support. The architecture separates functionality into wallet and x402 sub-domains, both protected by compile-time feature gating to ensure core code compiles regardless of whether Web3 support is enabled. This article explores how these subsystems handle domain gating, key protection, and automated payment flows based on the source implementation in the tinyhumansai/openhuman repository.
Web3 Architecture Overview
The blockchain support lives under the openhuman::web3 namespace with two primary sub-domains:
- wallet: Handles on-boarding, balance queries, transfers, swaps, and low-level contract calls through types like
WalletChain,WalletAccount, andWalletStatus. - x402: Implements the HTTP 402 machine-payable protocol, intercepting
PAYMENT-REQUIREDresponses and creating signed Solana SPL-token payments.
Both sub-domains follow a facade pattern where the top-level module is always compiled, but concrete implementations are guarded by #[cfg(feature = "web3")]. When disabled, lightweight stubs in src/openhuman/web3/wallet/stub.rs and src/openhuman/web3/x402/stub.rs provide identical public signatures with no-op or error bodies.
Wallet Domain Gating and Key Protection
OpenHuman employs multiple layers of protection to secure wallet secrets while maintaining compile-time flexibility.
Compile-Time Feature Gating
The web3 Cargo feature acts as the primary gate for the entire wallet stack. The façade implementation guarantees that callers such as tinyplace/payment.rs continue to compile even when wallet support is omitted. The stub implementation mirrors all public types and functions so signatures remain identical across enabled and disabled builds.
Secret Material Handling
The wallet stores a 32-byte Ed25519 seed specifically for tiny.place payments via the tinyplace_signer_seed function. This seed is derived from the user's primary Solana wallet key using SLIP-0010 derivation paths and is consumed only in-process, never persisted to disk or exposed externally.
The secret_material function returns a WalletSecretMaterial struct containing the encrypted mnemonic and derivation path. When the web3 feature is disabled, the stub always returns a deterministic error (DISABLED_MSG), preventing accidental fallback to insecure defaults or secret leakage.
RPC URL Redaction
To prevent credential exposure in logs, the rpc::redact_rpc_url function scrubs RPC URLs that may contain provider tokens. The stub implementation returns a constant placeholder string when the feature is disabled, ensuring deterministic behavior while maintaining the same interface.
Controller Registration
When the web3 feature is enabled, the wallet registers its RPC controllers through all_wallet_registered_controllers and all_wallet_controller_schemas. When disabled, the stub returns empty vectors, causing RPC methods to return "unknown method" errors at runtime rather than compilation failures.
X402 Payment Protocol Implementation
The x402 subsystem implements machine-payable HTTP requests using Solana SPL-tokens (typically USDC) without requiring native SOL for gas fees.
Payment Flow
The x402 client processes payments through the following sequence:
- Intercept: The
handle_402function detects HTTP 402 responses containingPAYMENT-REQUIREDheaders. - Prepare: The client builds a
PaymentPayloadselecting the appropriate Solana SPL-token. - Sign: Using the wallet's Ed25519 private key,
X402Clientcreates aSolanaPaymentProof(orEvmPaymentProoffor EVM chains) and places it in aPAYMENT-SIGNATUREheader. - Retry: The original request is retried with the signed proof. The facilitator co-signs as fee-payer, eliminating the need for native SOL reserves.
- Ledger: Successful payments are recorded in a global in-process ledger managed by
init_ledger,PaymentRecord, andSpendingBudget.
When the web3 feature is disabled, the stub supplies empty structures, causing the x402 flow to be omitted entirely while maintaining API compatibility.
Client Initialization
The X402Client struct provides the primary interface for automated payment handling:
use openhuman::web3::x402::X402Client;
// Create client and handle payment-required responses automatically
let client = X402Client::new();
let result = client.handle_402_and_pay(request).await?;
println!("Payment proof: {:?}", result.payment_proof);
Security Infrastructure and Chain Support
The Web3 domain shares common infrastructure across supported chains while maintaining strict security boundaries.
Transport and RPC Layer
The src/openhuman/web3/wallet/transport.rs module resolves endpoint URLs, handles fail-over between providers, and applies URL redaction to remove sensitive query parameters. The src/openhuman/web3/wallet/rpc.rs module provides low-level RPC calls including eth_sendRawTransaction for EVM chains.
Multi-Chain Implementations
Per-chain logic resides under src/openhuman/web3/wallet/chains/ with subdirectories for btc, evm, solana, and tron. Each implements chain-specific signing and broadcasting while exposing a unified public surface through the wallet facade.
Cluster Configuration
The wallet exposes the SolanaCluster enum (Mainnet/Devnet) and USDC SPL-token mint addresses. The stub implementation always returns Mainnet and hard-coded mainnet mint addresses, ensuring deterministic behavior when Web3 is disabled.
Working with the Wallet API
The wallet facade provides consistent interfaces regardless of feature flag status:
// Query wallet status - works whether Web3 is enabled or not
let status = openhuman::web3::wallet::status().await?;
println!("Accounts: {:?}", status.value.accounts);
// Prepare a transfer (errors gracefully if web3 feature is disabled)
let params = openhuman::web3::wallet::PrepareTransferParams {
chain: openhuman::web3::wallet::WalletChain::Solana,
to_address: "9z...".into(),
amount_raw: "10".into(),
asset_symbol: Some("USDC".into()),
evm_network: None,
};
match openhuman::web3::wallet::prepare_transfer(params).await {
Ok(prep) => println!("Quote ID: {}", prep.value.quote_id),
Err(e) => eprintln!("Transfer unavailable: {}", e),
}
Key Implementation Files
| File | Role |
|---|---|
src/openhuman/web3/wallet/mod.rs |
Facade with compile-time gating and re-exports |
src/openhuman/web3/wallet/stub.rs |
Disabled stub mirroring the public API surface |
src/openhuman/web3/wallet/ops.rs |
Core operations: balances, transfers, signing |
src/openhuman/web3/wallet/chains/* |
Per-chain signing and broadcasting logic |
src/openhuman/web3/wallet/transport.rs |
Endpoint resolution, fail-over, and URL redaction |
src/openhuman/web3/x402/mod.rs |
Facade for the x402 payment protocol |
src/openhuman/web3/x402/stub.rs |
Disabled stub for x402 when feature is off |
src/openhuman/web3/x402/ops.rs |
402 interception, payment creation, and retry logic |
src/openhuman/web3/x402/store.rs |
Global ledger for payment tracking |
src/openhuman/web3/x402/types.rs |
Payment payloads, proofs, and response types |
Summary
- Compile-time gating via the
web3feature flag controls all blockchain functionality, with stub implementations ensuring API compatibility when disabled. - Key protection uses SLIP-0010 derivation for Ed25519 seeds, keeping secret material in-process only and returning deterministic errors via
secret_materialwhen the wallet is disabled. - X402 protocol automates HTTP 402 payments using SPL-token transfers with facilitator fee-payer support, eliminating SOL requirements for gas.
- Security measures include RPC URL redaction to prevent credential leakage and stub implementations that prevent accidental secret exposure.
- Multi-chain support spans Solana, EVM, Bitcoin, and Tron through unified facade interfaces while maintaining chain-specific implementations under
src/openhuman/web3/wallet/chains/.
Frequently Asked Questions
How does OpenHuman protect wallet private keys when Web3 features are disabled?
When the web3 feature is disabled, the stub implementation in src/openhuman/web3/wallet/stub.rs replaces all secret-handling functions with versions that return a deterministic DISABLED_MSG error. The secret_material function never returns actual key material, and the tinyplace_signer_seed is completely omitted from the build, ensuring no secrets can be accessed or accidentally logged.
What happens when an x402 payment request encounters a 402 response?
The X402Client::handle_402 method intercepts the PAYMENT-REQUIRED header, constructs a Solana SPL-token payment payload (typically USDC), and signs it with the wallet's Ed25519 key to create a SolanaPaymentProof. The client then retries the original request with a PAYMENT-SIGNATURE header containing the proof, while the facilitator acts as fee-payer to cover transaction costs without requiring native SOL in the user's wallet.
Can applications use OpenHuman's wallet interface without compiling in blockchain support?
Yes. The facade pattern in src/openhuman/web3/wallet/mod.rs ensures all public types like WalletChain, WalletAccount, and functions like prepare_transfer are available regardless of feature flags. When web3 is disabled, these calls return graceful errors or empty structures rather than causing compilation failures, allowing dependent code in modules like tinyplace/payment.rs to build successfully.
How does OpenHuman prevent RPC provider credentials from appearing in logs?
The rpc::redact_rpc_url function in the wallet transport layer scrubs sensitive query parameters from RPC URLs before logging. When the web3 feature is disabled, the stub implementation returns a constant placeholder string. This redaction applies to all endpoint resolution handled by src/openhuman/web3/wallet/transport.rs, ensuring provider tokens never appear in application logs or error traces.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →