Choosing a runtime#
The portable crates (solana_kit_core, solana_kit_client and the program clients) have no networking code. You choose where your code runs by adding one adapter crate. That crate owns the HTTP and WebSocket transport for its host. Every HTTP adapter (NativeHttp, BrowserHttp, WasiHttp, GuestHttp) implements the same solana_kit_client::Transport trait, so your RPC code is identical everywhere and only the line that constructs the transport changes. WebSockets take a separate path: each adapter's socket implements solana_kit_client::subscriptions::TextSocket for the subscription Session, not Transport.
Native
solana_kit_native
Tokio HTTP/1, verified TLS and WebSockets, with deadlines and cancel-on-drop.
Browser
solana_kit_browser
Window Fetch and the browser WebSocket, with local (non-Send) futures.
WASI
solana_kit_wasi
Preview2 HTTP and sockets, driven by a caller-owned Reactor.
LeptosKit
solana_kit_leptoskit
An interpreted Wasm guest calls named services that a native host owns.
Compare the runtimes#
| Runtime | Target | Crate | HTTP | WebSocket | Status |
|---|---|---|---|---|---|
| Native | Host targets with Tokio | solana_kit_native | NativeHttp: HTTP/1 over Hyper, rustls TLS | NativeWebSocket: WS and WSS text sockets | HTTP and WebSocket run in CI on Ubuntu, macOS and Windows |
| Browser | wasm32-unknown-unknown in a browser Window | solana_kit_browser | BrowserHttp: Window Fetch | BrowserWebSocket: browser WebSocket | HTTP and text sockets run in CI in Chrome; workers, webviews and mobile browsers are unverified |
| WASI | wasm32-wasip2 | solana_kit_wasi | WasiHttp: wasi:http | WasiWebSocket (feature websocket): wasi:sockets plus guest TLS | HTTP runs under Wasmtime 50; the socket path is newer and still partly pending |
| LeptosKit | Interpreted wasm32-unknown-unknown guest plus a native host | solana_kit_leptoskit | GuestHttp in the guest, HttpDriver or HttpHost in the host | GuestWebSocket and SocketDriver | Guest HTTP runs headless in CI; sockets and installed-app journeys are still pending |
Preview1 (wasm32-wasip1) and plain Wasm without a browser still compile the portable client, but no networking adapter exists for them.
The common shape#
All four adapters follow the same rules:
- You own the runtime. No adapter starts an executor, spawns a detached task, pools connections or installs a global.
- You pass configuration in. Endpoints, headers and deadlines are explicit, and nothing is read from the environment. Trust roots are yours to supply only where the adapter exposes them (
NativeHttp'sTlsRoots); the browser, the WASI host and the LeptosKit host apply their own TLS trust policy. - Bounded by default. Reply bodies are capped by the client's request budget. Each transport adds its own deadlines.
- Drop cancels. Dropping an in-flight exchange releases its owned network work. Nothing is retried or redirected automatically.
- Diagnostics are redacted. Errors omit endpoints and header values. The original cause stays reachable through
std::error::Error::source.
Construct a client the same way in every runtime:
Native
use solana_kit_client::{Client, Limits};
use solana_kit_native::{HttpConfig, NativeHttp};
let transport = NativeHttp::new("https://rpc.invalid/", HttpConfig::default())?;
let client = Client::new(transport, Limits::default());Browser
use solana_kit_browser::{BrowserHttp, HttpConfig};
use solana_kit_client::{Client, Limits};
let transport = BrowserHttp::new("https://rpc.invalid/", HttpConfig::default())?;
let client = Client::new(transport, Limits::default());WASI
use solana_kit_client::{Client, Limits};
use solana_kit_wasi::{Reactor, WasiHttp};
use std::time::Duration;
let reactor = Reactor::default();
let transport = WasiHttp::new(
reactor.clone(),
"https://rpc.invalid/",
Duration::from_secs(30),
Vec::new(),
)?;
let client = Client::new(transport, Limits::default());LeptosKit
use solana_kit_client::{Client, Limits};
use solana_kit_leptoskit::GuestHttp;
// The guest names a service; the native host decides where it goes.
let transport = GuestHttp::new("balance-service")?;
let client = Client::new(transport, Limits::default());Constructing a transport sends nothing. The first request happens when you poll a call. See RPC for the calls you can make once you have a client.
How to pick#
- A CLI, server, bot or desktop backend: use Native.
- A web page: use Browser. Add its
walletfeature to talk to browser wallets. - A WebAssembly component in a WASI host such as Wasmtime: use WASI. The embedding host enforces network policy and TLS for HTTP.
- A LeptosKit desktop or mobile app: use LeptosKit. The guest uses
guest; the native shell useshost. - Wallet connections on any of these: read Wallets.
One test suite, five hosts#
The portable core is checked by one conformance suite, solana_kit_conformance. cargo xtask all runs it natively, in Chrome, in Wasmtime Preview 1 and Preview 2, and in a headless LeptosKit WasmHost. Each host can also run alone:
cargo xtask native
cargo xtask browser
cargo xtask wasi
cargo xtask leptoskitcargo xtask browser needs CHROME and CHROMEDRIVER set to the Chrome binary and its matching driver, and cargo xtask wasi needs WASMTIME set to Wasmtime 50.0.0. cargo xtask native and cargo xtask leptoskit need neither. See Verification for the full setup. This suite covers the portable core. Each transport has its own separate CI workflow.