Rust SDK
staked-quic-client-sdk and staked-quic-tpu-client: the native Rust path for sending transactions over a leased SWQoS lane.
The Rust integration is two crates from the
nodexpert-labs/staked-quic-connection-provider repo. The repo is currently
private on GitHub, so both the web page and anonymous Git fetches require
repo access. They are the canonical implementation: the TypeScript and
Python SDKs wrap the same crates via native bindings.
The two crates
| Crate | Role |
|---|---|
staked-quic-client-sdk | Thin tonic gRPC client for the TEE signing service running next to the leased validator. Exposes StakedQuicSignerClient with connect, get_certificate, sign_certificate_verify, and health, and re-exports the generated protos as staked_quic_client_sdk::proto. |
staked-quic-tpu-client | Solana TPU client that sends transactions to leaders over QUIC. In CertMode::Staked the TLS 1.3 client-certificate handshake is signed by the TEE holding the validator identity key, so leaders apply SWQoS staked priority. CertMode::Unstaked is the best-effort baseline with a local ephemeral key. |
Neither crate is a REST client for the marketplace API. Browsing validators, booking, and minting JWTs stay plain HTTPS calls, see the trader integration guide and the API reference.
Install
The crates are not published on crates.io, and the repo has no release tags
yet. Depend on the Git repo over SSH, tracking master (the same form the
marketplace itself uses):
[dependencies]
staked-quic-tpu-client = { git = "ssh://git@github.com/nodexpert-labs/staked-quic-connection-provider.git", branch = "master" }
staked-quic-tpu-client re-exports StakedQuicSignerClient, so for the
send-transaction path one dependency is enough. Add
staked-quic-client-sdk = { git = "...", branch = "master" } only if you
drive the signing oracle directly.
The ssh + branch = "master" form requires GitHub SSH auth with access to
the private repo, and it floats on master. There is no alternative today: an
anonymous https://github.com/... fetch is rejected because the repo is
private, and there are no release tags to pin. Once the repo is public and
tagged, prefer an https + tag = "<release-tag>" dependency for anything
you deploy.
Prerequisites: lease JWT and CA PEM
The Rust SDK does not mint the lease JWT for you. You need three inputs
before calling connect:
- TEE endpoint URL (
https://...): returned once by the credentials reveal,POST /v1/leases/{lease_id}/credentials, astee_endpoint_url. - CA PEM: the host-proxy serves a self-signed certificate generated at
deploy time. Get it from the validator operator as the
cert.pemthatdeploy.shprints at deploy time. The credentials reveal also returns atls_ca_certfield, but in v1 it is a hardcoded placeholder string, not a usable PEM, so do not pin it (the TLS handshake would fail). - Lease JWT: a short-lived ES256 token minted per call from the marketplace API.
Mint the JWT with your trader API token (also from the one-shot credentials
reveal, or rotated via POST /v1/leases/{lease_id}/api-keys, see
authentication):
curl -s -X POST "https://api.swqos.dev/v1/leases/$LEASE_ID/token" \
-H "Authorization: Bearer $API_TOKEN"
{
"data": {
"jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLiJ9...",
"expires_at": 1781234567
},
"meta": { "request_id": "01J...", "version": "v1" }
}
Facts that matter for your refresh loop:
- Default TTL is 600 seconds (
expires_atis a Unix timestamp). Re-mint before expiry, the other SDKs refresh atexp - 60s. - Each
POSTmints a fresh token. The endpoint is rate limited at 12 requests per minute per API key. - Errors:
403 lease-not-active,403 lease-epoch-out-of-range(the current Solana epoch is outside your booked range),404 lease-not-found,429 rate-limit-exceeded. See errors. - The JWT is audience-bound to your
tee_endpoint_urland carries the leased validator's pubkey, so it only works against your lease's TEE.
The TypeScript and Python
LeaderClients mint and refresh the JWT automatically. In Rust you call the
token endpoint yourself (curl, reqwest, anything) and pass the jwt string
to connect.
Quickstart: send a transaction over the staked lane
This mirrors staked-quic-tpu-client/examples/send_one_staked.rs:
use staked_quic_tpu_client::{CertMode, StakedQuicSignerClient, TpuClient, TpuConfig};
#[tokio::main(flavor = "multi_thread")]
async fn main() -> anyhow::Result<()> {
// The deploy.sh-printed CA cert from the validator operator
// (the reveal's tls_ca_cert field is a v1 placeholder, do not use it).
let ca_pem = std::fs::read("cert.pem")?;
// Minted via POST /v1/leases/{id}/token (see above).
let lease_jwt = "<lease-jwt>";
// https:// is mandatory; the JWT rides as Bearer metadata on every RPC.
let sdk = StakedQuicSignerClient::connect(
"https://tee.example.com:443",
lease_jwt,
&ca_pem,
)
.await?;
let tpu = TpuClient::new(
"https://api.testnet.solana.com", // RPC is used for leader discovery only
CertMode::Staked(sdk),
TpuConfig::default(),
)
.await?;
// Sanity check: in Staked mode this is the leased validator's identity pubkey.
println!("cert pubkey: {}", hex::encode(tpu.cert_pubkey()));
let tx = build_my_signed_tx(); // your fully signed solana_sdk Transaction
let signature = tpu.send_transaction(&tx).await?;
println!("sent {signature}");
Ok(())
}
What each call does:
StakedQuicSignerClient::connect(endpoint, lease_jwt, ca_pem)opens a TLS-pinned gRPC channel to the host-proxy. It rejects any non-https://endpoint (a plaintext channel would leak the JWT and signing traffic), pins the given CA PEM, and installs an interceptor that attachesauthorization: Bearer <jwt>to every request, flagged sensitive so it stays out of HPACK tables and header dumps. The client isCloneover a shared pooled channel.TpuClient::new(rpc_url, cert_mode, config)builds the QUIC endpoint. InCertMode::Stakedit fetches the TEE certificate up front and wires the rustls client-cert resolver to the signing oracle. Transactions never go through the RPC node, it only serves leader discovery.tpu.cert_pubkey()returns the 32-byte Ed25519 pubkey embedded in the client cert: the validator identity inStakedmode, an ephemeral local key inUnstakedmode. Compare it against your lease's validator before sending real traffic.tpu.send_transaction(&tx)bincode-serializes the transaction and fans it out to the next 4 distinct upcoming leaders, then returns the transaction signature.
CertMode::Staked requires a multi-threaded tokio runtime (the default
#[tokio::main]). The TEE signing path bridges rustls's synchronous
Signer::sign to the async gRPC client over a channel, which would deadlock
on a current-thread runtime, so TpuClient::new fails loudly at construction
on that flavor.
TpuClient behavior
- Fan-out:
send_transactiontargets the next 4 distinct upcoming leaders (about 16 slots, roughly 6.4 seconds of coverage). The banking stage dedupes by signature, so duplicate arrivals are harmless. It errors only if every send failed. - Send-only: the client returns the signature once the QUIC streams are
written. Confirmation is your job, poll
getSignatureStatusesor use your own strategy. - Pinned target:
send_transaction_to(&tx, addr)skips leader discovery and hits one specifictpu_quicendpoint. Theleadermodule shipsupcoming_unique_leadersandresolve_validatorhelpers. - Persistent connections:
tpu.connect(addr)(orconnect_to(&leader)) returns aTpuConnection, a cheaply cloneable QUIC connection reusable across many sends without re-handshaking. This is how you keep the TEE-signed handshake cost (30-80 ms, cold connections only) off the hot path. - Wire limit:
serialize_txenforcesSOLANA_TX_WIRE_LIMIT(1232 bytes).send_raw/send_raw_toaccept pre-serialized payloads.
TpuConfig defaults:
| Field | Default | Meaning |
|---|---|---|
bind_addr | 0.0.0.0:0 | Local UDP bind for the QUIC endpoint |
keep_alive | 1500 ms | QUIC keep-alive interval |
max_idle_timeout | 10 s | Idle timeout before the connection drops |
fanout_count | 4 | Distinct upcoming leaders per send_transaction |
rpc_commitment | processed | Commitment for leader-discovery RPC calls |
Low-level: the signing oracle client
If you run your own QUIC/TLS stack, use staked-quic-client-sdk directly:
let health = sdk.health().await?;
// HealthStatus { provisioned: bool, pubkey_sha256: Vec<u8> }
let cert = sdk.get_certificate().await?;
// CertificateData { certificate: Vec<u8>, public_key: Vec<u8> }
let sig = sdk.sign_certificate_verify(&payload).await?;
// 64-byte Ed25519 signature
get_certificatereturns the 249-byte Firedancer-compatible mock X.509 certificate to present during the TLS handshake with the leader, plus the 32-byte validator pubkey.sign_certificate_verifysigns a TLS 1.3 CertificateVerify payload. The enclave's keyguard accepts only payloads of exactly 130, 146, or 162 bytes matching the CertificateVerify transcript format and rejects everything else, which is what keeps the validator key unable to sign votes or transactions on a trader's behalf.healthreports whether the enclave is provisioned and the SHA-256 of the loaded validator pubkey.
The client attaches your lease JWT to every call. The host-proxy enforces it
on SignCertificateVerify and answers expired or invalid tokens with gRPC
UNAUTHENTICATED. The JWT is fixed at connect time, so when you mint a
fresh token, reconnect with it.
Runnable examples
From a checkout of staked-quic-connection-provider:
# Staked lane, requires a reachable TEE proxy + active lease.
# --keypair is the TRADER wallet that signs and pays the TX;
# the validator identity never leaves the TEE.
cargo run --example send_one_staked -- \
--keypair /path/to/trader.json \
--tee-endpoint https://tee.example.com:443 \
--tee-token "<lease-jwt>" \
--tee-ca-pem cert.pem \
--rpc-url https://api.testnet.solana.com
# Unstaked baseline, no TEE required.
cargo run --example send_one_unstaked -- \
--keypair /path/to/trader.json \
--rpc-url https://api.testnet.solana.com
--rpc-url defaults to https://api.testnet.solana.com when omitted.