Skip to content

Authentication

Overview

Lexe has two main authentication methods:

1) Root seed: The root seed is the wallet's master secret. All child keys and secrets are derived from it. The root seed gives total, unrestricted, irrevocable control over funds in a Lexe wallet, and can only be rotated by moving all funds to a new wallet. The root seed is required to unilaterally recover one's Bitcoin if Lexe ever goes away, so it is very important to back it up to a safe place.

2) Client credentials: Lexe client credentials are scoped and revocable credentials encoded as a long opaque string. Client credentials are well-suited for giving limited access to an existing Lexe wallet to 3rd party applications.

The rest of this page covers these authentication methods in detail.

Root Seed

The root seed is the master secret for a Lexe wallet. If the root seed is lost, all wallet funds are permanently unrecoverable. Be sure to back it up to somewhere safe before depositing funds.

Authenticate with the root seed if you need total control over how secrets are generated, backed up, and persisted, or if you need the ability to create new Lexe nodes, for example, if you are a wallet developer. The root seed can be constructed from arbitrary bytes, and can thus be derived from a 'higher' master secret.

Generating a new root seed

Lexe mobile app

For most Lexe users, the root seed is generated by the Lexe mobile app and persisted in their phone's secure storage.

  • Google Drive backup: If the user has enabled Lexe's Google Drive integration, the root seed is also backed up to the user's Google Drive, encrypted by a strong password. As long as the user remembers their password, or stores it in a safe place like a password manager, they will be able to restore their wallet on a new device by simply connecting their Google Drive and entering their password. The Google Drive integration is built so that Lexe does not learn the user's email address or any other personal information about the user.
  • Seedphrase backup: If users want to opt-out of Google Drive, the root seed can also be converted to a 24 word mnemonic (commonly referred to as a "seedphrase") and backed up directly. Storing the seedphrase in a password manager like 1Password is a decent tradeoff for most users, but it is best if the seedphrase is stored offline.

Lexe SDKs and CLI

The root seed can also be generated using Lexe's SDKs or the Lexe CLI.

  • CLI users run $ lexe init which generates a new root seed and persists it to ~/.lexe/seedphrase.txt (Unix mode 0600). The user is responsible for backing up their root seed in this case.
  • SDK users can call RootSeed.generate() to sample a new root seed. They can then call RootSeed.write(_) to persist it to ~/.lexe/seedphrase.txt (mode 0600), or use one of the available methods to_mnemonic, to_hex, or to_bytes to manage and persist it themselves. See the Rust quickstart or the Python quickstart for full examples.
  • The Sidecar SDK does not support root seed generation. Create and persist the root seed using the CLI or one of the other SDKs.

Any Lexe root seed can be used with Lexe's mobile app. Simply open the app, select "Restore", then paste the seedphrase into the app.

If desired, developers can use their own RNG to sample the Lexe root seed, or derive it from a higher secret. For example, a multi-chain wallet that uses the Lexe SDK to integrate Lightning can use HKDF-SHA-256 to deterministically derive a 32-byte key from their own master secret, and pass the bytes to RootSeed.from_bytes to construct a Lexe root seed from it.

Signup, provisioning, and updates

Signup

After root seed generation, the next step is to register an account with Lexe. No personal information is required; all that Lexe receives is the user's ed25519 pubkey (the user_pk) and Lightning node pubkey.

SDK users can sign up by calling LexeWallet.signup(_). For CLI users, $ lexe init already handles signup, but $ lexe signup is available if it is required to sign up manually. The Sidecar SDK does not support signup.

Provisioning

Once signed up, it is necessary to provision the Lightning node running inside the secure enclave, by giving it a copy of the root seed. The mobile app and SDKs will connect to the secure enclave inside Lexe's cloud and verify that the Lightning node's measurement (a SHA-256 hash of the node binary) appears in its trusted measurements list.

The trusted node measurements are hard-coded into the app, SDKs, and CLI, and can be independently verified to be trustworthy by auditing the open-source code published in Lexe's GitHub and reproducibly building the user node enclave binary from it. If the enclave's remote attestation passes the check, then a copy of the root seed is sent to the enclave via a secure channel. From here on, Lexe can help keep the user's Lightning node online, but Lexe cannot read the root seed because it's protected by the enclave.

SDK users can provision by calling LexeWallet.provision(_). For CLI users, $ lexe init already handles the initial provisioning, but $ lexe provision is available for provisioning manually or updating the node. The Sidecar SDK does not currently support provisioning, but may in the future.

If you would like to learn more about this process, you can read the SECURITY.md available in Lexe's GitHub.

Updates

Whenever a new user node version is released, it has a different measurement, and needs to be re-provisioned. Because the trusted node measurements are hard coded into the clients (the mobile app, the CLI, or the SDKs), updating the client is required to update the user node.

To update the user node to its latest version, simply update the app, CLI, or SDK, then call LexeWallet.provision(_). The Lightning node will run the updated version the next time it starts. You can call LexeWallet.node_info() and check the version field to verify that the enclave is running the version you expect.

Using the root seed

The root seed can be used with Lexe's SDKs and the CLI.

  • CLI and Sidecar: Pass via --root-seed or --root-seed-path, or set via env (LEXE_ROOT_SEED or LEXE_ROOT_SEED_PATH), or store in .env in the current or any parent directory.
  • SDK: Construct the RootSeed and pass it into LexeWallet. See the Rust quickstart or the Python quickstart for an example.

Lexe Client Credentials

Another way to authenticate a Lexe wallet is using client credentials. The defining feature of Lexe client credentials is that they are scoped, revocable, and may have budgets attached.

Client credentials are designed for giving access to 3rd party apps that need a limited amount of control over the wallet, and which don't need (or want) the responsibility of generating the root seed or managing backup and restore.

A client credential is a long, opaque string that looks like this: eyJ1c2VyX3BrIjoiNmZkNzY0MTU2OTMwNTA5ZmFkNTM2MWQzYjIyYjYxZjc1YWE5MWVkNjQwMjE1YjJjNDFjMmZmODZiMmJmYzQ3MiIsImxleGVfYXV0aF90b2tlbiI6IjlkVENVdkM4eTdxY055VWJxeW56M253SVFRSGJRcVBWS2VNaFhVajFBZnItdmdqOUUyMTdfMnRDUzFJUU03T...

Creating client credentials

Client credentials can be created via the Lexe mobile app. In the left sidebar, select "Client credentials":

Lexe app sidebar with the client credentials entry highlighted

From there, you can create a client credential which has access to your wallet, and add a label to remind you what it was for.

Revoking client credentials

To revoke a credential, simply navigate to the same menu and press the delete button. Unlike credit cards, there is no need to ask any 3rd party service you shared those credentials with to delete your credentials from their database. Any further attempts to use your credentials will be blocked by your user node.

Client credentials page with the delete button highlighted

Credential scopes

NOTE: Credential scopes are currently in progress. Currently, all client credentials give "full" access to the wallet, and should be shared with caution.

Client credentials will be created with one of the following scopes:

  • "read": Read-only access to the wallet. Can view wallet balance, list payments, and listen for incoming and outgoing payments.
  • "receive": Has read access, plus the ability to create invoices and offers, but not pay them.
  • "full": Full access to the wallet, except for the root seed. Has the ability to spend all funds, create new client credentials, and provision new node versions.

Credential budgets

NOTE: Credential budgets are not yet implemented. This is a highly requested feature, currently in progress. Stay tuned!

Client credentials will also be able to have budgets attached, which will limit how much the credential can spend over a given time period. For example, you will be able to create a client credential that can spend at most 20 USD per month. These credentials can be used to pay for subscriptions, and give users the confidence that services cannot charge any more than their node allows.

Planned denominations:

  • BTC
  • USD

Planned time periods:

  • Daily
  • Weekly
  • Monthly
  • Yearly
  • All time

Using client credentials

Client credentials can be used with Lexe's SDKs, the CLI, and the sidecar.

  • CLI: Pass via --client-credentials or --client-credentials-path, or set via env (LEXE_CLIENT_CREDENTIALS or LEXE_CLIENT_CREDENTIALS_PATH), or store in .env in the current or any parent directory.
  • Sidecar: Set default credentials via CLI (--client-credentials, --client-credentials-path) or env (LEXE_CLIENT_CREDENTIALS, LEXE_CLIENT_CREDENTIALS_PATH), or store in .env in the current or any parent directory, or pass per-request via the Authorization: Bearer <credentials> header. Per-request credentials take precedence over default credentials if provided, and are useful for controlling multiple wallets from a single sidecar process.
  • SDK: Construct the ClientCredentials and pass it into LexeWallet. See the Rust quickstart or the Python quickstart for an example.

When using someone else's client credential, remember that you are acting from the wallet owner's perspective. If you instantiate a LexeWallet with a user's client credential, calling LexeWallet.pay(_) will spend the user's funds, not your own. If you need to charge the user's wallet for a good or service, you can do so by using the user's wallet to pay your own.