Privy: white-label wallet infrastructure, the technical view
Privy runs embedded wallets at consumer-app scale. TEE + Shamir secret sharing, three wallet products, server SDKs, and a recovery model that survives anything.
Privy is the embedded-wallet infrastructure most consumer Solana apps reach for first. 120M+ accounts across 2,000+ teams, three distinct wallet products on one shared key-management foundation. This article is the technical view — the keys, the accounts, the SDKs, the recovery model.
The key model
Privy uses Trusted Execution Environments (TEEs) + Shamir Secret Sharing to hold user keys. No single party — including Privy itself — can derive a user's private key from the data they hold.
User's private key → split into N shares via SSS (typically 3-of-5)
Share A → sealed inside a TEE on Privy's infra
Share B → sealed inside a different TEE (different cloud region)
Share C → derived from the user's password (PBKDF2'd)
Share D → stored on Privy's backend, accessible only on auth
Share E → recovery share (email-able, MPC-recoverable)
To sign:
3-of-5 shares are reconstructed inside a TEE
Signature is produced
Reconstructed key is discarded; never leaves the enclaveThe user's password is one of the shares — without it, no coalition of Privy operators can sign. Combined with TEE isolation, this gives the property: even with full server compromise, Privy can't spend user funds.
Three wallet products
Same underlying key model, three different surface APIs for different use cases:
- User wallets — embedded in consumer apps. The user is the owner; they sign via the React SDK (auth + confirm). The signing UI is whatever your app renders.
- Treasury wallets — owned by the dev team, not the user. For protocol-side operations (paying gas, holding treasuries, automated jobs). Accessed via the server SDK with API key auth.
- Agent wallets — for AI agents. Same as treasury wallets but with policy controls (spending limits, program allow-lists, time-of-day windows) that gate every signature attempt.
Client-side integration (React)
import { PrivyProvider, usePrivy, useSolanaWallets } from "@privy-io/react-auth"
import { Connection, SystemProgram, PublicKey, Transaction } from "@solana/web3.js"
// 1. Wrap your app
export function App({ children }: { children: React.ReactNode }) {
return (
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}
config={{
embeddedWallets: { createOnLogin: "all-users" },
loginMethods: ["email", "google", "wallet"],
defaultChain: { name: "solana" },
}}
>
{children}
</PrivyProvider>
)
}
// 2. Use the embedded wallet
function SendSol() {
const { authenticated } = usePrivy()
const { wallets } = useSolanaWallets()
const wallet = wallets[0]
async function send() {
const conn = new Connection("https://api.mainnet-beta.solana.com")
const tx = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey(wallet.address),
toPubkey: new PublicKey("..."),
lamports: 10_000_000,
})
)
const sig = await wallet.sendTransaction(tx, conn)
console.log("https://solscan.io/tx/" + sig)
}
return <button onClick={send} disabled={!authenticated}>Send</button>
}Server-side wallet operations
For treasury and agent flows, use the server SDK with your app's secret API key:
import { PrivyClient } from "@privy-io/server-auth"
const privy = new PrivyClient(process.env.PRIVY_APP_ID!, process.env.PRIVY_APP_SECRET!)
// Create a treasury wallet
const wallet = await privy.walletApi.create({
chainType: "solana",
policyIds: [policyId], // optional policy enforcement
})
// Sign a transaction server-side (no user interaction)
const result = await privy.walletApi.solana.signTransaction({
walletId: wallet.id,
transaction: tx, // base64-encoded
})
// Or send-and-sign in one call
const result2 = await privy.walletApi.solana.signAndSendTransaction({
walletId: wallet.id,
transaction: tx,
caip2: "solana:mainnet",
})Policy-gated signing for agents
Agent wallets attach to a policy evaluated server-side before every signature:
const policy = await privy.policyApi.create({
name: "Trading bot",
chainType: "solana",
rules: [
{
// Allow up to 5 SOL/day to Jupiter program
method: "signTransaction",
action: "ALLOW",
conditions: [
{ field: "program_id", operator: "EQUALS", value: JUPITER_PROGRAM_ID },
{ field: "lamports", operator: "LESS_THAN", value: 5_000_000_000 },
{ field: "lamports_per_day", operator: "LESS_THAN", value: 5_000_000_000 },
],
},
{ method: "signTransaction", action: "DENY", conditions: [] }, // default deny
],
})
await privy.walletApi.update({ id: wallet.id, policyIds: [policy.id] })Recovery
Two recovery paths, both designed so neither Privy nor a single user mistake can destroy access:
- Password recovery. If the user forgets their password, the system uses other shares + email verification to allow a password reset.
- Self-custody export. Users can export their private key at any time — useful for migrating to an external wallet or holding the key directly. Recovery becomes the user's problem after export.
When to reach for Privy
Privy fits when you want consumer-app embedded wallets at scale — millions of users, social login, zero seed-phrase UX, and a path forward for treasury ops and agent automation under the same vendor relationship.
Compared against alternatives in the embedded wallet showdown: Privy wins on raw deployed scale, Dynamic wins on connect- plus-embedded hybrids, Magic wins on enterprise sharding flexibility, Crossmint wins on stablecoin orchestration, Lazorkit wins on Solana-only passkey UX.
References
Privy is the boring infrastructure layer most Solana consumer apps standardise on. The TEE + SSS key model is the part to understand if you're evaluating whether it meets your security bar.