Web App & Payments Architecture
A detailed breakdown of Satolink's client-side runtime, cryptographic sessions, and non-custodial payment channels.
1. Non-Custodial Core Integration
The fundamental architecture of the Satolink client application (both Web App and Mobile Client) is built directly around the Breez Liquid SDK (@breeztech/breez-sdk-liquid). By utilizing this runtime locally on the user's browser or device, Satolink enforces a strict zero-custody model.
Private Key Sovereignty
Recovery seeds (12 words) and private keys are generated, encrypted, and kept exclusively in local memory. They are never sent to, read by, or stored on Satolink servers.
Local Signing
All Bitcoin Lightning and Liquid transactions are signed directly in the browser's sandbox environment before broadcasting, ensuring no external party can manipulate your funds.
2. Cryptographic Authentication: Challenge-Response
Traditional database passwords are vulnerable to leaks, breaches, and phishing. Satolink replaces central password databases with an asymmetric **Challenge-Response** protocol. To authenticate, the client signs a cryptographic challenge using the private key derived from the Breez Liquid SDK.
Authentication Flow Breakdown
- Challenge Acquisition: The client presents their
walletID(wallet fingerprint). The server replies with a secure, 32-byte cryptographically-random challenge buffer that remains valid for exactly 5 minutes. - Client Signature: The client feeds the challenge into
sdk.signMessage({ message: challenge })within the Breez Liquid SDK. The SDK prefixes the buffer with standard Lightning signature metadata ("Lightning Signed Message:"), hashes it using SHA-256, and signs it via ECDSA. The signature is outputted in z-base32 encoding. - Entropy Protection: To prove ownership of the password locally, the client derives:$$\text{AuthHash} = \text{SHA-256}(\text{password} + \text{walletID})$$Only this hash is sent to the server. The password is never transmitted or exposed.
- Server-Side Verification: The server checks the signature locally using curve secp256k1 and checks if the challenge was not reused or expired. If valid, the session is approved.
3. Session Management & RTR (Refresh Token Rotation)
To maintain a seamless and highly secure web session, Satolink uses a dual-token strategy combining short-lived JWT credentials with active Refresh Token Rotation (RTR).
Access Token (JWT)
- Stored in: JS Memory (Volatile)
- Lifespan: 15 Minutes
- Usage: Sent in the HTTP
Authorization: Bearerheader
Refresh Token
- Stored in: HttpOnly, Secure, SameSite Cookie
- Lifespan: 7 Days
- Usage: Used to request new access tokens implicitly
RTR Fraud Prevention Mechanism
When a client triggers a refresh request (POST /api/v1/user/refresh), the server issues a new Access Token and a fresh Refresh Token, while immediately invalidating the previous Refresh Token.
If a malicious actor intercepting traffic attempts to reuse a revoked Refresh Token:
System Action: The server recognizes a duplicate refresh attempt, triggers a security freeze, and instantly invalidates all active session sessions for that specific user ID. The true owner is safely prompted to re-authenticate.
4. Address Pool: Receiving Payments Offline
A persistent problem with non-custodial systems is that wallets cannot receive transactions if they are offline, since generating a deposit invoice or signature requires a private key. Satolink solves this with the **Offline Address Pool** mechanism.
Local Pre-generation
While the user is online, the app pre-generates 5 Liquid BTC and 5 Liquid USDT addresses from the local seed.
Encrypted Upload
Addresses are uploaded to Satolink's API linked to the user's public username handle (user@satolink.com).
Offline Resolution
When someone pays you, the server allocates an unused address from your pool to route the payment directly to your custody.
5. Real-Time WebSockets & Sockets Security
To supply dynamic UI updates (payment notifications, instant chats, and pool depletion warnings), Satolink opens a persistent, encrypted WebSocket connection using **Socket.io**.
| Event Name | Source | Purpose |
|---|---|---|
register | Client ➔ Server | Sends JWT token to authenticate the WebSocket channel. |
auth_error | Server ➔ Client | Triggers if the socket JWT expires, prompting the client to refresh its tokens. |
check_address_pool_updated | Client ➔ Server | Queries the database state of the pre-generated addresses. |
address_pool_status | Server ➔ Client | Pushes real-time warnings when the pre-generated addresses fall below safety levels (e.g., < 2 addresses). |
notification | Server ➔ Client | Delivers real-time notifications about incoming transaction statuses and chat messages. |
6. Technical Glossary
Breez Liquid SDK
A mobile and web software development kit built in Rust and WebAssembly, enabling client devices to maintain a local, self-custodial node on the Liquid Network.
z-base32
A variant of Base32 encoding optimized for human readability and writing. It excludes confusing characters (like O, 0, l, 1) and places easier-to-read letters in higher-probability indices.
secp256k1
The standard elliptic curve parameters used by Bitcoin and Liquid to generate cryptographic key pairs. It permits high-speed signing and verification operations.
Refresh Token Rotation (RTR)
A security method where the server issues a brand new refresh token alongside a new access token at every request, immediately revoking the old refresh token to prevent replay hijacks.