Self custodial architecture for modern crypto apps
Oct 6, 2023
This is part 1 in a series of posts detailing how Privy’s embedded wallets work, the motivations behind their design and the threat and security models they operate under. In this part, we break down the motivations behind the design of embedded wallets.
Wallets today are monoliths. Heavy machinery unlocking new interactions. They are powerful and sometimes unwieldy-entire products separate from the applications that make use of them.
They are both bank vaults and metrocards, helping users secure their digital assets as well as take simple actions on public systems. Like a bank vault, they should be secure, sturdy, error-proof, and immovable. Like metrocards, they should be lightweight, flexible, intuitive and easy to replace. Across UX and functionality both, metrocards and bank vaults should not be the same.
Mind you, we believe consumer wallets play an essential role in the ecosystem (this is why we spend a lot of time ensuring Privy’s wallet connectors work great with third-party wallets out of the box). But for many, self-custodial systems should be fluid rather than monolithic. As a first experience, wallets must be presented contextually, with immediate practical utility for their users. Wallets should morph to fit both the apps that use them and where individual users are in their respective journeys. They should be secure but easy to engage with, growing with their users’ use of them (back to our notion of progressive onboarding); enabling more robust defenses (as well as more friction) as the value they secure grows.
Building such a system and ensuring that it has the appropriate properties to grow with users’ use of it is no small feat. Engineering is about tradeoffs and balancing requirements given technical constraints. In this post, I’ll walk through our journey to give our map of the embedded wallet decision space and the motivations that led to Privy’s embedded wallet shape.
The Control-Utility Spectrum of Wallets – a failed attempt at reducing an n-dimensional space
Adopting an embedded wallet system mostly comes down to an evaluation of the following criteria:
Custody: who can access private keys?
Security: is it secure against attackers targeting the user/app/wallet? Is it secure against insider threats within the provider?
Performance: can the system scale as the user base grows?
UX: can the system adapt to customers’ product experiences?
Interoperability: can the system be ported across apps and ecosystems easily? Is the user able to compose assets across experiences?
Notice that there are tradeoffs across these goals. For instance,
The best UX may mean enabling a developer to trigger actions on a user’s behalf (for instance if they’re playing a game where UX interrupts are costly), but this is in tension with custody (what if the user isn’t aware of an action being taken?).
The most secure operation may mean employing a voluntarily slow cryptographic algorithm, but what about performance?
Self-custody entails a user always has a right-to-leave a given wallet provider or app, for instance by exporting their cryptographic keys. But handling cryptographic materials is not something users should be asked or expected to do. Here, custody goes against security.
This is the nature of systems engineering: tradeoffs abound. As we push the bounds of what is technologically feasible (as is happening at a blistering pace in modern cryptography), these tradeoffs improve. Over time, we will get more and give up less better security for less friction (for instance). Nonetheless, the fact remains that picking a solution means prioritizing certain properties over others. Recognizing this does not mean we seek to absolve ourselves of the consequences of technical decisions we make. We simply recognize that different builders and different users value different things and that our systems—within the bounds of their designs—must be fluid enough to accommodate them.
Accordingly, in designing a solution, we start by defining our threat model and ensure it corresponds to the reality of the threat landscape today for our customers and their products (more on this in our third post in the series). From there, we seek to ensure the solution can evolve with the products it serves and the threats it faces. As we went through the decision tree, we sought to guarantee a few core principles for Privy’s embedded wallets:
The end-user must be the only party who can access private keys, ie the system must be self-custodial.
Users must have an escape hatch – they must always be able to access their assets independent of any given provider.
The system should be as simple as possible to ensure it is auditable.
The system must not rely on untested or immature cryptographic primitives.
The system should be future-proof: easily upgradeable to incorporate cryptographic advances over time.
Beyond this, the system should accommodate rapid scaling with no impact to performance.
Those are the invariants we sought to build for when we started building embedded wallets. We will never say there is one right solution and ours is the way of the gospel. In fact, this is part of the motivation in writing this post. By laying bare our thinking and decision-making process, we want to help builders decide whether Privy is the right system for them. Conversely, to any builder dealing with silver-bullet infrastructure and solutions without tradeoffs, caveat emptor.
Having explained our worldview and motivations, it’s now time to turn our attention to the technicals under Privy’s embedded wallets. In the next post, we’ll break down the Shamir-Secret Sharing based architecture we chose and why it is the best fit for our stated goals today. From there, we’ll break down Privy’s threat model.