Soda Labs
Soda Labs Docs
User InterfaceHttp proxy

Onboard a user

Onboard a user through the Bubble HTTP API and get the AES user key, in Python or JavaScript.

A minimal copy-paste example that onboards a user through the Bubble HTTP API and returns the AES user key. Available in Python and JavaScript.

Quick start

  • Requirements: a wallet private key. Override the proxy with BUBBLE_PROXY_URL if needed.
  • Install dependencies:

Python 3.10+. Use a virtualenv — the SDK pins web3==6.11.2.

pip install soda-bubble-sdk

Example script

onboard.py

import base64, json, os, urllib.request
from soda_python_sdk.crypto import generate_rsa_keypair, recover_user_key, sign_eip191, verify_signatures
from eth_account import Account
from web3 import Web3

PRIVATE_KEY = os.environ["PRIVATE_KEY"]
PROXY_URL = os.environ.get("BUBBLE_PROXY_URL", "https://proxy.bubble.sodalabs.net")
RPC_URL = os.environ.get("RPC_URL", "https://ethereum-sepolia-rpc.publicnode.com")
# GCDecryptionVerifier for your chain — see Contract addresses
VERIFIER = os.environ.get("VERIFIER", "0x336646CF32aD1EdB82a8e31eE94DB1Be91932aea")

account = Account.from_key(PRIVATE_KEY)

# 1) Generate an RSA keypair for the onboarding request
private_key_bytes, public_key_bytes = generate_rsa_keypair()

# 2) Sign rsa_public_key + user_address
message = public_key_bytes + bytes.fromhex(account.address[2:])
signature = sign_eip191(message, bytes.fromhex(PRIVATE_KEY[2:]))

# 3) Call /onboard
body = {
    "rsa_public_key": base64.b64encode(public_key_bytes).decode(),
    "user_signature": base64.b64encode(signature).decode(),
    "address": account.address,
}
req = urllib.request.Request(f"{PROXY_URL}/onboard", data=json.dumps(body).encode(),
                             headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as res:
    data = json.load(res)

ciphertext = base64.b64decode(data["rsa_ciphertexts"])

# 4) Verify the evaluators signed these key shares, before deriving anything from them.
#    Without this a hostile proxy could return shares it chose, giving you a key it knows.
w3 = Web3(Web3.HTTPProvider(RPC_URL))
abi = [{"inputs": [], "name": "getSigners", "outputs": [{"type": "address[]"}],
        "stateMutability": "view", "type": "function"}]
signers = w3.eth.contract(address=Web3.to_checksum_address(VERIFIER), abi=abi).functions.getSigners().call()
sigs = [base64.b64decode(s) for s in data.get("mpc_signatures", [])]
if len(sigs) != len(signers) or not verify_signatures(ciphertext, sigs, signers):
    raise SystemExit("onboard response failed signature verification — refusing to derive a key")

# 5) Reconstruct the AES user key from the MPC shares
aes_key = recover_user_key(private_key_bytes, ciphertext[:256], ciphertext[256:])
print("User AES key (hex):", aes_key.hex())

Run it

PRIVATE_KEY=0xabc123... python onboard.py

The script signs the RSA public key plus your address, posts to /onboard, and prints the reconstructed AES user key you need for future encrypt and decrypt operations. The response also carries mpc_signatures, one per MPC evaluator — see OnboardUser for what they attest to.

On this page