xui-rs
A modern, typed async Rust SDK for 3x-ui
[!IMPORTANT] The
2.0line targets the complete 3x-ui v3.8.5 HTTP and WebSocket API. Upgrading from 1.x requires small model changes; see the migration guide and upstream review.
Why xui-rs?
- Strong request and response types instead of unstructured JSON.
- A cheap-to-clone client designed for concurrent async workloads.
- API-token and cookie-session authentication with secrets redacted by default.
- Correct base-path handling for panels installed below a custom URL prefix.
- Contract tests derived from the upstream 3x-ui API surface.
- A reviewed rustdoc API baseline and exhaustive downstream re-export contract.
- RustSec, license, dependency-source, package, and SemVer release gates.
- A typed, forward-compatible real-time stream with explicit reconnect semantics.
- One explicit HTTP/HTTPS/SOCKS5 proxy configuration shared by HTTP, subscriptions, and WebSocket transports.
- Strict formatting, linting, documentation, MSRV, and cross-platform CI gates.
Installation
cargo add xui-rs@2
xui-rs requires Rust 1.88 or newer. Browse the complete API on
docs.rs and runnable programs in
examples/.
Authentication
API tokens are the recommended choice for services, bots, and automation. They avoid browser-session state and 3x-ui's CSRF flow.
use Client;
Cookie login remains available for panels where an API token cannot be used. The SDK obtains a pre-auth CSRF token, stores only cookie name/value pairs in a standards-compliant jar, and reuses the CSRF token for unsafe requests.
use ;
async
Credentials are never retained for automatic re-login. An expired session is
reported as Error::Unauthorized,
allowing the application to decide how and when credentials may be requested.
See the authentication design for the rationale.
Inbounds
The complete 18-route v3.8.5 inbound surface is available through
Client::inbounds, including AmneziaWG settings and subscription sort order.
use ;
async
See the inbound API guide for the operation inventory, replacement/import semantics, and intentionally open-ended Xray JSON fields.
Clients
Client::clients() covers all 47 client and group routes registered by v3.8.5,
including server-side paging, portable import/export, bulk operations, online
and IP attribution, HWID device management, and group traffic baselines.
use ;
async
Every email, subscription ID, and group name is encoded as one URL path
segment. Client credentials, subscription IDs, external links, and private
keys are redacted from Debug. See the client API guide.
Server and Xray
Client::server() covers all 39 routes registered by the v3.8.5 server
controller: typed host/Xray status and history, observatory data, lifecycle and
updates, logs, cryptographic helpers, REALITY target scanning, database
backup/restore, cluster IP synchronization, and AmneziaWG peer diagnostics.
use ;
async
Database downloads stay in memory and preserve attachment metadata. Restore,
self-update, and Xray lifecycle methods are explicit operations and never run
implicitly. Generated private material is redacted from Debug. See the
server API guide.
Panel and Xray settings
Client::settings() covers all 15 v3.8.5 panel-settings routes, including
scoped and expiring API tokens, notification tests, and credential replacement.
Client::xray_settings() covers all 26 Xray settings and integration routes,
including WARP, NordVPN, PIA, geodata inspection, outbound and routing tests,
and remote outbound subscriptions.
use ;
async
The ergonomic grouped settings model flattens to exact upstream wire names.
Nested Xray JSON is encoded and decoded automatically. Stored notification and
LDAP secrets, integration payloads, subscription URLs/outbounds, and API-token
plaintext are redacted from Debug. See the settings API guide.
Subscription hosts
Client::hosts() covers all 12 host-override routes registered by v3.8.5.
Logical groups span multiple inbounds and addresses, while create/update
results expose every physical row produced by that Cartesian expansion.
use ;
async
Nested mux, sockopt, and final-mask JSON is encoded automatically, path values are segment-safe, and the source's nullable empty list is normalized. See the Hosts API guide.
Remote nodes
Client::nodes() covers all 16 v3.8.5 node routes: registration and lifecycle,
saved/unsaved probes, remote inbound discovery, health history, bulk panel
updates, certificate pinning, node mTLS, and live mTLS-client reload.
use ;
async
Read models never expose write-only node API tokens. Credential retain/replace/ clear states are mutually exclusive, legacy nullable tags are normalized, and unknown enum/protocol values remain forward-compatible. See the Nodes API guide.
Public subscriptions and panel metadata
SubscriptionClient covers all twelve routes exposed by 3x-ui's separate public
subscription server: GET and HEAD for raw, Xray JSON, and Clash/Mihomo
formats, the Mihomo and legacy Clash aliases, and read-only HWID slot status. It intentionally carries no panel authentication because this server
can run on another origin and port.
use SubscriptionClient;
async
Custom raw/JSON/Clash paths, construction from panel settings, and the v3.8.5
HWID/device headers are supported. Successful responses expose the panel's
HWID registration, limit, and device-existence metadata.
Subscription identifiers, documents, generated links, routing rules, and
represented emails are redacted from Debug and request errors. The raw body
can be decoded through SubscriptionDocument::decode_base64 when subEncrypt
is enabled.
Client::panel() adds the two remaining panel-wide HTTP operations: retrieve
the runtime OpenAPI document and explicitly send a fresh database backup to
configured Telegram administrators. See the public subscription and
panel-wide operations guide.
Subscription balancers
Client::subscription_balancers() covers the complete v3.8.5 controller for
JSON-subscription balancers: list, create, replace, and both delete transports.
Strategies are typed, inbound IDs preserve the upstream repeated-form wire
contract, and unknown future strategy values fail closed for mutations.
use ;
async
Outbound proxies
Panel HTTP, public subscriptions, and WebSocket connections support the same
typed ProxyConfig. HTTP, HTTPS, SOCKS5 with local DNS, and socks5h with
proxy-side DNS are supported, including Basic/username-password authentication.
use ;
Proxy credentials are separate from the credential-free URL and redacted from
Debug and errors. Environment proxy variables are intentionally ignored, so
HTTP and WebSocket routing stays deterministic. Cookies remain scoped to the
target panel and continue to use the same standards-compliant jar through the
proxy. See the outbound proxy guide for DNS, TLS, timeout,
and security behavior.
Transport safety and observability
Response bodies are bounded in memory even when a server uses chunked transfer encoding or reports an incorrect length. Ordinary panel API and public subscription responses default to 64 MiB; explicit database downloads have an independent 512 MiB default. All three limits are configurable.
use ;
Oversized responses return the typed ErrorKind::ResponseTooLarge. Optional
tracing events correlate a request with its response headers using a request
ID, method, status, outcome, and latency while deliberately omitting URLs,
paths, queries, headers, cookies, tokens, and bodies. See the
transport safety and observability guide for exact
semantics and introspection.
Real-time events
Client::events() covers the authenticated /ws handshake and all ten
message names declared by the v3.8.5 source. Status, traffic, inbounds,
outbounds, nodes, notifications, Xray transitions, client counters, reserved
clients payloads, and invalidations have distinct typed variants.
use ;
async
The endpoint does not support bearer-token authentication. HTTP and WebSocket share one standards-compliant cookie jar, but cookie values are never exposed or copied manually. Control frames and the 10 MiB source limit are handled internally. Reconnect is explicit because 3x-ui does not replay events missed during a disconnect. See the real-time events guide.
Errors and retry policy
All operations return xui_rs::Result<T>. ErrorKind provides a stable,
copyable classification for metrics and policy, while status(), method(),
url(), and authentication/rate-limit/server/timeout helpers expose context
without destructuring variants.
use Error;
The SDK never retries API mutations implicitly: a timeout does not prove that the server failed to apply a request. See the error and retry guide for idempotency-aware recommendations and WebSocket recovery semantics.
API stability
The complete 2.0 Rust surface is recorded in a reproducible rustdoc snapshot. Downstream contract tests compile every concise crate-root re-export, important trait guarantee, and representative multithreaded-runtime future. Extensible enums are non-exhaustive, so matches should retain a wildcard arm.
See the API stability policy for the exact SemVer guarantees and the boundary between Rust source compatibility and upstream 3x-ui server behavior.
Compatibility
| xui-rs | 3x-ui | Status |
|---|---|---|
2.0.x |
3.8.5 |
Complete tagged HTTP and WebSocket API |
1.0.x |
3.7.0 |
Previous compatibility line |
Rust 1.88.0 is the minimum supported compiler. Development and CI use the pinned Rust 1.98.0 toolchain.
Development
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features
cargo test --doc --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
actionlint
cargo deny check
scripts/public-api.sh check
scripts/package-check.sh --allow-dirty
The opt-in real-panel gate runs the ignored integration target against an isolated, digest-pinned 3x-ui v3.8.5 container:
scripts/live-test.sh
See the live-testing guide for its isolation model, cookie/WebSocket coverage, mutation guard, and manual target configuration.
See CONTRIBUTING.md for contribution guidelines and SECURITY.md for private vulnerability reporting. Maintainers should follow the OIDC-based release procedure; releases never require a long-lived crates.io token.
License
Released into the public domain under the Unlicense.