Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Long term solution to client initialization #165

Open
neekolas opened this issue Jan 27, 2024 · 0 comments
Open

Long term solution to client initialization #165

neekolas opened this issue Jan 27, 2024 · 0 comments

Comments

@neekolas
Copy link
Contributor

neekolas commented Jan 27, 2024

With libxmtp and MLS, maybe it's time to take first principles look at how client initialization works at XMTP. Given how early we are in adoption on mobile, we should prioritize the needs of the next 100 apps over the migration path for the current handful.

Previous versions of the XMTP SDKs have been stateless. With libxmtp we now have a secure database to store state and key material. That gives us new options.

Because previous versions were stateless, most client applications look something like this simplified pseudocode at initialization:

const existingKeys = await getKeysFromMyAppDatastore(walletAddress, env);
if (existingKeys) {
  return Client.create(null, { privateKeyOverride: existingKeys, env });
}
// If there are no existing keys, the app needs to do a bunch of things to get a signer. Maybe ask the user which wallet they use, possibly do a WalletConnect flow, jump to another app to finish the connection.

const signer = await getSigner();
// In reality, most implementations return here and will retry the entire flow once the keys are saved.

// We call this getKeys in some SDKs or loadOrCreateKeys in others. They all work the same way,
// which is that we prompt for 1 or 2 signatures and at the end of the process the keys are both stored
// on the network and returned to the caller.
const newKeys = await Client.loadOrCreateKeys(signer, { env });
await saveKeysToAppDatastore(newKeys);
return Client.create(null, { privateKeyOverride: newKeys, env });

There's quite a bit of room in the design space for other ideas now that we have a database to play with.

This is important, since the rules are slightly different for libxmtp. Even if a developer provides a privateKeyOverride of V2 keys, there may be a signature required the first time they initialize the SDK in an app.

One alternative model is for the SDK to tell the developer what information is needed. For example:

// This block would only be needed if the application has pre-existing keys stored in their DB that must be migrated. Brand new apps could skip this step altogether.
if (Client.needsV2Keys(walletAddress, env) && await getKeysFromMyAppDatastore(walletAddress, env)) {
    // This would only need to happen one time ever,
    Client.saveV2Keys(await getKeysFromMyAppDatastore(walletAddress, env), env)
}

if (Client.needsSignature(walletAddress, env)) {
    // This may still require a whole bunch of hoops for the user to jump through
    const signer = await getSigner()
    // saveSignaturesOptions would include things like the preCreateIdentityCallback
    Client.saveSignatures(signer, env, saveSignaturesOptions)
}

return Client.create(walletAddress, env)

There are a bunch of variations of this idea that might be more ergonomic.

The special case of server-side apps

Server-side apps that act on behalf of a user, such as Chainjet, require special consideration. Today, they typically work by running Client.getKeys(...) in a browser and then uploading those keys to a server so that the application can act on behalf of the user.

With MLS, we have an opportunity to make that more secure since those keys can be revocable. But it's either difficult or impossible for the server-side application to request signatures interactively.

Instead, a more practical model would be to have a new version of Client.getKeys(...) that creates a brand new installation locally, and exports a bundle of V2 and installation keys to the server. Once V2 is phased out completely we can drop the V2 keys from the bundle and just upload installation keys.

The server SDK would then just need a method to do a one-time import of those keys into its local database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

No branches or pull requests

1 participant