libXray

package module
v1.260909.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 7 Imported by: 2

README

libXray

简体中文

This is a wrapper around Xray-core to improve the client development experience.

Note

  1. This repository has few maintainers. If you do not report a bug or initiate a PR, your issue will be ignored.
  2. This repository does not guarantee API stability, you need to adapt it yourself.
  3. This repository is only compatible with the latest release of Xray-core.

Versioning

Releases use CalVer in the form v<YY>.<M>.<D> (e.g. v26.3.27 = 2026-03-27). Because Go modules require any module with major version >= 2 to encode the major in its import path, every CalVer release is mirrored onto a Go-friendly SemVer tag on the same commit:

CalVer tag Go-import tag
v26.3.27 v1.260327.0

Go consumers should pin against the SemVer mirror:

go get github.com/xtls/libxray@v1.260327.0

The mirror tag is created automatically by .github/workflows/release-go-mirror.yml on every CalVer push. Existing CalVer tags can be backfilled with scripts/backfill-semver-tags.sh.

Features

build

Compile script. It is recommended to always use this script to compile libXray. We will not answer questions caused by using other compilation methods.

depends on git and go.

By default, the build script does not clone Xray-core. It uses Go modules and pins Xray-core to release tag v26.9.9 through its pseudo-version. Pass the optional local argument to use an existing local checkout at ../Xray-core through a Go module replace.

Usage
# Android (min Android API level is 21)
python3 build/main.py android
python3 build/main.py android local

# Apple (gomobile or go)
python3 build/main.py apple gomobile
python3 build/main.py apple go
python3 build/main.py apple gomobile local
python3 build/main.py apple go local

# Linux
python3 build/main.py linux
python3 build/main.py linux local

# Windows
python3 build/main.py windows
python3 build/main.py windows local

Builds restore go.mod and go.sum on success or failure. Gomobile builds resolve latest by default; set LIBXRAY_GOMOBILE_VERSION to select a Go module version. Both gomobile and gobind use that resolved version.

Linux and Windows builds also produce bin/xray or bin/xray.exe. This session Core protects Go DNS lookups from the VPN route and accepts only:

xray run -dns <IP:port> -interface <name> -config <xray.json>

All three options are required. -dns must be an IP endpoint, and -config points directly to the Xray JSON configuration.

[!WARNING] Use only one Go runtime per process. Go does not support loading multiple independently built Go runtimes into one process. Every native libXray artifact embeds a Go runtime, whether it is produced through cgo or gomobile. Do not load libXray together with another independently built Go, cgo, or gomobile library in the same executable or process. Doing so can fail during build, link, or load, or crash during runtime initialization before application code runs. If one process needs Go packages from several libraries, include those packages in the same Go build or gomobile bind invocation and produce one native artifact so they share a runtime. Merely repackaging or merging independently built frameworks, archives, AARs, shared libraries, or DLLs is not sufficient. Separate OS processes may each load one Go runtime, so apply this rule independently to each process. See Go #18976, golang/go#15956, and libXray #116.

Android

use gomobile .

iOS && macOS
1. use gomobile

Need "iOS Simulator Runtime".

This is the best choice for general scenarios. The cross-platform single-runtime restriction above still applies when linking other Go-based libraries.

Supports iOS, iOSSimulator, macOS, macCatalyst.

But it is not possible to set the minimum macOS version, which will cause some warnings when compiling. And it does not support tvOS.

2. use cgo

Need "iOS Simulator Runtime" and "tvOS Simulator Runtime".

Support more compilation options, output c header files.

This works well when you use ffi for integration. For example, integration with swift, kotlin, dart.

Support iOS, iOSSimulator, macOS, tvOS.

The product LibXray.xcframework contains module.modulemap. When using Swift, import it as module LibXray.

Linux

depend on gcc and g++.

Windows

Depends on gcc and g++ in PATH.

Native amd64 and arm64 builds are supported. The release workflow builds each architecture on its matching GitHub-hosted Windows runner.

API

libXray exposes a single structured entrypoint:

func Invoke(requestJSON string) string

The C export is:

char* CGoInvoke(char* requestJSON);
void CGoFree(char* value);

CGoInvoke allocates its response. The caller must release every non-null response with CGoFree; do not use a platform allocator directly.

The request is a JSON object:

{
  "apiVersion": 3,
  "method": "runXray",
  "payload": {
    "xrayJson": "{\"outbounds\":[...]}"
  }
}

The response is a JSON object:

{
  "success": true,
  "data": {},
  "error": ""
}

Design notes:

  1. Invoke accepts only apiVersion: 3; the API version remains fixed at 3. Contract changes require synchronized consumers and documentation within that version. Xray configurations are passed as UTF-8 JSON text in xrayJson; libXray does not read configuration file paths.
  2. A top-level env field is ignored and has no effect. Xray-core runtime environment options belong in the root env object of the Xray config.
  3. SetTunFd has been removed. When the fd is only known at runtime, write xray.tun.fd into the Xray config root env object before calling runXray.
  4. countGeoData is not backed by an Xray config, so its datDir is passed in the method payload.
  5. The complete UTF-8 encoded Invoke request and response JSON envelopes are limited to 16 MiB. If either limit is exceeded, Invoke returns a failure response with success: false, data: null, and a size-limit error.
  6. convertShareLinksToXrayJson validates each parsed outbound with the current Xray-core config builder. Invalid outbounds are omitted, and the method fails if none remain. Validation does not create or start an Xray instance. Xray JSON input is treated as a node source: only its root outbounds are retained, and all other root fields are ignored. The response contains only fields supported by libXray share links; unsupported and generated empty fields are omitted. Opaque XHTTP extra and FinalMask mask settings JSON remain unchanged. Every successful response contains only the projected outbounds list. Its optional age.secretKey decrypts official age ASCII armor in memory before the existing parser runs. Plaintext input remains unchanged.
  7. Xray-core keeps its system dialer DNS client and outbound manager in process-wide state. pingBatch, testXray, and their exported Go entrypoints take the managed lifecycle lock and reject an active runXray instance before loading/building config. A batch holds the lock through all workers and temporary-core close. This also serializes these operations with one another. Instances created outside the managed APIs are not detected or restored; callers requiring overlap with them must still use separate processes.

Supported methods:

getFreePorts
convertShareLinksToXrayJson
convertXrayJsonToShareLinks
generateAgeKeyPair
countGeoData
pingBatch
testXray
runXray
stopXray
xrayVersion
getXrayState

controller

Socket protect

Used to solve the socket protect problem on Android.

DNS resolver

Android may expose a loopback DNS server to Go's resolver while a VPN is active. Call SetDNS before runXray to make Go use the DNS server selected by the VPN configuration and protect the DNS socket from the VPN tunnel. The server must be an IP endpoint with a port, such as 8.8.8.8:53 or [2001:4860:4860::8888]:53.

Call ResetDNS after Xray has stopped. These APIs are available only in the Android artifact and change the process-wide Go resolver.

LibXray.setDNS(controller, "8.8.8.8:53");
LibXray.invoke(runXrayRequest);

// Later, when stopping the core:
LibXray.invoke(stopXrayRequest);
LibXray.resetDNS();
Process finder (per-app routing)

ConnectivityManager.getConnectionOwnerUid() is API 30+. On older Android libXray falls back to parsing /proc/net/{tcp,udp}{,6} in pure Go.

Usage (Java/Kotlin):

ProcessFinder finder = new ProcessFinder() {
    @Override
    public long findProcessByConnection(String network, String srcIP, long srcPort,
                                         String destIP, long destPort) {
        return -1; // return UID or -1
    }
};
LibXray.registerProcessFinder(finder, Build.VERSION.SDK_INT);

geo

count

Read geo files and count the categories and rules.

main

Download geosite.dat and geoip.dat and count them.

memory

Only executed on iOS, GC is initiated once a second. This can alleviate memory pressure on iOS.

nodep

file

Write data to a file.

measure

Speed ​​test the Xray configuration.

port

Get free ports.

share

libXray stores outbound names in tag. sendThrough keeps its native Xray meaning as the local bind address.

generate_share

Convert Xray JSON to VMess AEAD / VLESS share links following Xray-core discussion #716. SS, SOCKS and Trojan share links are also supported. VMess always generates an AEAD URI, not the legacy QR-code format.

Outbounds without a supported share-link format are skipped. Conversion fails if no share links can be generated.

parse_share

Parse VMess AEAD / VLESS, SS, SOCKS and Trojan share links, plus legacy vmessQrCode links, into Xray JSON.

Xray JSON node input and Base64 / Age subscription wrappers remain supported. Clash/Mihomo configurations and hysteria2:// / hy2:// URIs are not supported. This restriction concerns share-link conversion, not Hysteria2 in native Xray JSON configurations.

Parsing result

convertShareLinksToXrayJson has one response shape. Its payload contains text and optional age. Every successful conversion returns data: {"outbounds":[...]}. There is no statistics or nested config wrapper.

Invalid individual elements are skipped without discarding other valid nodes. The list preserves source order and includes only projected, buildable outbounds. No per-node hash comparison, deduplication or failed-node counting is performed.

No usable nodes, an unrecognized format, a malformed document, an invalid container or a decryption failure returns success: false with data: null. Error text never includes rejected candidates or decrypted subscription text. Callers must not import/replace a subscription when no usable nodes remain.

age-encrypted subscriptions

convertShareLinksToXrayJson accepts an optional native age secret key. Only X25519 (AGE-SECRET-KEY-1...) and ML-KEM-768 + X25519 hybrid (AGE-SECRET-KEY-PQ-1...) identities are accepted. Recognized age armor is decrypted in memory and limited to 16 MiB of plaintext.

{
  "apiVersion": 3,
  "method": "convertShareLinksToXrayJson",
  "payload": {
    "text": "-----BEGIN AGE ENCRYPTED FILE-----\n...",
    "age": {
      "secretKey": "AGE-SECRET-KEY-1..."
    }
  }
}

Generate a new keypair with keyType set to x25519 or hybrid. An omitted keyType defaults to x25519. The hybrid option matches Mihomo age keygen-pq and produces an AGE-SECRET-KEY-PQ-1... identity with an age1pq1... recipient.

{
  "apiVersion": 3,
  "method": "generateAgeKeyPair",
  "payload": {
    "keyType": "x25519"
  }
}

The response contains both secretKey and publicKey. The integrating application must persist the pair and send only publicKey as X-Age-Public-Key. libXray does not perform the subscription HTTP request, persist keys, or add headers. Applications must never send the secret key over HTTP or write decrypted subscription text to disk.

vmess

convert VMessQRCode to Xray Json.

xray_json

Some tools used to parse shared links.

xray

pingBatch

Tests multiple outbound configurations concurrently in one temporary Xray instance. Each xrayJson string is parsed only for its outbounds; all other root fields are ignored. The target outbound is selected by outboundTag, then by the proxy tag, and finally by the first outbound.

{
  "apiVersion": 3,
  "method": "pingBatch",
  "payload": {
    "configs": [
      {
        "xrayJson": "{\"outbounds\":[...]}"
      },
      {
        "xrayJson": "{\"outbounds\":[...]}",
        "outboundTag": "media"
      }
    ],
    "timeout": 5,
    "url": "https://cp.cloudflare.com/",
    "locationUrl": "https://ip-check-perf.radar.cloudflare.com/"
  }
}

Each request accepts at most five configurations and tests all accepted configurations concurrently. Requests containing more than five configurations fail before any configuration is tested.

The top-level response succeeds when the batch itself was accepted. Each item has its own result; delay is 10000 for an error and 11000 for a timeout. delay is always present, including a successful zero-millisecond result. The result array has the same length and order as the input config array. Outbound dependencies referenced by streamSettings.sockopt.dialerProxy are included automatically. Xray-core rejects the removed proxySettings field.

locationUrl is optional and must be an absolute HTTP(S) URL. When omitted, no location request is made and no location fields are returned. When supplied, each prepared item sends its latency HEAD and then its location GET using the same client forced through that item's selected outbound and dependencies. Each request has the configured timeout (so an item may take up to twice it). Location time is not included in delay, and the two results are independent: success, delay and error describe latency only; a location failure does not invalidate a successful latency result, and GET is still attempted after a latency failure.

A successful GET adds the unmodified response body as the locationJson string. The App owns JSON parsing and provider-specific field handling. The provider must return HTTP 200 and at most 64 KiB; transport or body-read failures instead add locationError. Errors do not echo the URL, credentials or response body. Invalid outbound configs retain their ordinary per-item failure and do not perform either request.

testXray

Loads and builds the complete configuration from the supplied JSON text. The payload contains only xrayJson; success returns data: {}:

{
  "apiVersion": 3,
  "method": "testXray",
  "payload": {
    "xrayJson": "{\"outbounds\":[...]}"
  }
}

The Go entrypoint TestXray uses core.LoadConfig without constructing or starting an Xray instance or runtime handlers. It validates configuration structure, including TUN/WireGuard definitions, without creating devices, listeners, log files, or background connections. The builder can still read local GeoData/certificates and apply the root env to the current process. Geodata asset declarations validate HTTPS URLs and existing local files; their downloader/cron does not run during validation.

A successful check establishes that the configuration builds. It does not prove that runtime resources are available, that an instance can start, or that the network is reachable. Callers must handle actual startup failures.

runXray

Starts the managed Xray instance from the supplied JSON text. Use stopXray to stop that instance. runXrayFromJson is no longer a separate method.

metrics

Refer to the following configuration:

{
  "metrics" : {
    "listen": "127.0.0.1:49227"
  },
  "policy" : {
    "system" : {
      "statsInboundDownlink" : true,
      "statsInboundUplink" : true,
      "statsOutboundDownlink" : true,
      "statsOutboundUplink" : true
    }
  },
  "stats" : {}
}

The metrics server exposes the Xray runtime counters through HTTP. For example, when listen is 127.0.0.1:49227, read:

http://localhost:49227/debug/vars

Metrics only needs the listen field in this wrapper. Query /debug/vars directly with an HTTP client instead of going through libXray. Counters belong to the current Xray instance; libXray does not sample or persist traffic.

validation

Verify the Xray configuration.

xray

Start and stop Xray instances.

Credits

Project X

VMessPing

FreePort

MetaCubeX age (BSD 3-Clause)

License

This repository is based on the MIT License.

Documentation

Overview

libXray is an Xray wrapper focusing on improving the experience of Xray-core mobile development.

Index

Constants

View Source
const LibXrayAPIVersion = 3

Variables

This section is empty.

Functions

func Invoke added in v1.260711.0

func Invoke(requestJSON string) string

Types

type AgeDecryptConfig added in v1.260909.0

type AgeDecryptConfig struct {
	SecretKey string `json:"secretKey,omitempty"`
}

type AgeKeyType added in v1.260909.0

type AgeKeyType string
const (
	AgeKeyTypeX25519 AgeKeyType = "x25519"
	AgeKeyTypeHybrid AgeKeyType = "hybrid"
)

type ConvertShareLinksToXrayJsonRequest added in v1.260711.0

type ConvertShareLinksToXrayJsonRequest struct {
	Text string            `json:"text,omitempty"`
	Age  *AgeDecryptConfig `json:"age,omitempty"`
}

type ConvertXrayJsonToShareLinksRequest added in v1.260711.0

type ConvertXrayJsonToShareLinksRequest struct {
	XrayJson string `json:"xrayJson,omitempty"`
}

type ConvertXrayJsonToShareLinksResponse added in v1.260711.0

type ConvertXrayJsonToShareLinksResponse struct {
	Links string `json:"links,omitempty"`
}

type CountGeoDataRequest added in v1.260601.0

type CountGeoDataRequest struct {
	Name    string `json:"name,omitempty"`
	GeoType string `json:"geoType,omitempty"`
	DatDir  string `json:"datDir,omitempty"`
}

type GenerateAgeKeyPairRequest added in v1.260909.0

type GenerateAgeKeyPairRequest struct {
	KeyType AgeKeyType `json:"keyType,omitempty"`
}

type GenerateAgeKeyPairResponse added in v1.260909.0

type GenerateAgeKeyPairResponse struct {
	SecretKey string `json:"secretKey,omitempty"`
	PublicKey string `json:"publicKey,omitempty"`
}

type GetFreePortsRequest added in v1.260711.0

type GetFreePortsRequest struct {
	Count int `json:"count,omitempty"`
}

type GetFreePortsResponse added in v1.260711.0

type GetFreePortsResponse struct {
	Ports []int `json:"ports,omitempty"`
}

type GetXrayStateResponse added in v1.260711.0

type GetXrayStateResponse struct {
	Running bool `json:"running"`
}

type LibXrayInvokeRequest added in v1.260711.0

type LibXrayInvokeRequest struct {
	APIVersion int             `json:"apiVersion,omitempty"`
	Method     LibXrayMethod   `json:"method,omitempty"`
	Payload    json.RawMessage `json:"payload,omitempty"`
}

type LibXrayMethod added in v1.260711.0

type LibXrayMethod string
const (
	LibXrayMethodGetFreePorts                LibXrayMethod = "getFreePorts"
	LibXrayMethodConvertShareLinksToXrayJson LibXrayMethod = "convertShareLinksToXrayJson"
	LibXrayMethodConvertXrayJsonToShareLinks LibXrayMethod = "convertXrayJsonToShareLinks"
	LibXrayMethodGenerateAgeKeyPair          LibXrayMethod = "generateAgeKeyPair"
	LibXrayMethodCountGeoData                LibXrayMethod = "countGeoData"
	LibXrayMethodPingBatch                   LibXrayMethod = "pingBatch"
	LibXrayMethodTestXray                    LibXrayMethod = "testXray"
	LibXrayMethodRunXray                     LibXrayMethod = "runXray"
	LibXrayMethodStopXray                    LibXrayMethod = "stopXray"
	LibXrayMethodXrayVersion                 LibXrayMethod = "xrayVersion"
	LibXrayMethodGetXrayState                LibXrayMethod = "getXrayState"
)

type PingBatchItemRequest added in v1.260728.0

type PingBatchItemRequest struct {
	XrayJson    string `json:"xrayJson,omitempty"`
	OutboundTag string `json:"outboundTag,omitempty"`
}

type PingBatchItemResponse added in v1.260728.0

type PingBatchItemResponse struct {
	Success       bool    `json:"success"`
	Delay         int64   `json:"delay"`
	Error         string  `json:"error,omitempty"`
	LocationJSON  *string `json:"locationJson,omitempty"`
	LocationError string  `json:"locationError,omitempty"`
}

type PingBatchRequest added in v1.260728.0

type PingBatchRequest struct {
	Configs     []PingBatchItemRequest `json:"configs,omitempty"`
	Timeout     int                    `json:"timeout,omitempty"`
	URL         string                 `json:"url,omitempty"`
	LocationURL string                 `json:"locationUrl,omitempty"`
}

type PingBatchResponse added in v1.260728.0

type PingBatchResponse struct {
	Results []PingBatchItemResponse `json:"results,omitempty"`
}

type RunXrayRequest added in v1.260601.0

type RunXrayRequest struct {
	XrayJson string `json:"xrayJson,omitempty"`
}

type TestXrayRequest added in v1.260909.0

type TestXrayRequest struct {
	XrayJson string `json:"xrayJson,omitempty"`
}

type XrayVersionResponse added in v1.260711.0

type XrayVersionResponse struct {
	Version string `json:"version,omitempty"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL