diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index f2c332e3..aab209f8 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -7,7 +7,7 @@ on:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index adc7de2b..59a768ec 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,13 +1,26 @@
name: Release X-UI
on:
- push:
- tags:
- - "*"
workflow_dispatch:
+ release:
+ types: [published]
+ push:
+ branches:
+ - main
+ paths:
+ - '**.js'
+ - '**.css'
+ - '**.html'
+ - '**.sh'
+ - '**.go'
+ - 'go.mod'
+ - 'go.sum'
+ - 'x-ui.service'
jobs:
build:
+ permissions:
+ contents: write
strategy:
matrix:
platform:
@@ -15,10 +28,10 @@ jobs:
- arm64
- armv7
- armv6
- - armv5
- 386
+ - armv5
- s390x
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -27,53 +40,56 @@ jobs:
uses: actions/setup-go@v5
with:
go-version-file: go.mod
-
- - name: Install dependencies
- run: |
- sudo apt-get update
- if [ "${{ matrix.platform }}" == "arm64" ]; then
- sudo apt install gcc-aarch64-linux-gnu
- elif [ "${{ matrix.platform }}" == "armv7" ]; then
- sudo apt install gcc-arm-linux-gnueabihf
- elif [ "${{ matrix.platform }}" == "armv6" ]; then
- sudo apt install gcc-arm-linux-gnueabihf
- elif [ "${{ matrix.platform }}" == "armv5" ]; then
- sudo apt install gcc-arm-linux-gnueabi
- elif [ "${{ matrix.platform }}" == "386" ]; then
- sudo apt install gcc-i686-linux-gnu
- elif [ "${{ matrix.platform }}" == "s390x" ]; then
- sudo apt install gcc-s390x-linux-gnu
- fi
+ check-latest: true
- name: Build x-ui
run: |
export CGO_ENABLED=1
export GOOS=linux
export GOARCH=${{ matrix.platform }}
- if [ "${{ matrix.platform }}" == "arm64" ]; then
- export GOARCH=arm64
- export CC=aarch64-linux-gnu-gcc
- elif [ "${{ matrix.platform }}" == "armv7" ]; then
- export GOARCH=arm
- export GOARM=7
- export CC=arm-linux-gnueabihf-gcc
- elif [ "${{ matrix.platform }}" == "armv6" ]; then
- export GOARCH=arm
- export GOARM=6
- export CC=arm-linux-gnueabihf-gcc
- elif [ "${{ matrix.platform }}" == "armv5" ]; then
- export GOARCH=arm
- export GOARM=5
- export CC=arm-linux-gnueabi-gcc
- elif [ "${{ matrix.platform }}" == "386" ]; then
- export GOARCH=386
- export CC=i686-linux-gnu-gcc
- elif [ "${{ matrix.platform }}" == "s390x" ]; then
- export GOARCH=s390x
- export CC=s390x-linux-gnu-gcc
- fi
-
- go build -ldflags "-w -s" -o xui-release -v main.go
+ TOOLCHAIN_URL=""
+ MUSL_CC_HOST="https://github.com/musl-cc/musl.cc/releases/download/v0.0.1" #http://musl.cc
+ case "${{ matrix.platform }}" in
+ amd64)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/x86_64-linux-musl-cross.tgz"
+ ;;
+ arm64)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/aarch64-linux-musl-cross.tgz"
+ ;;
+ armv7)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/armv7l-linux-musleabihf-cross.tgz"
+ export GOARCH=arm
+ export GOARM=7
+ ;;
+ armv6)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/armv6-linux-musleabihf-cross.tgz"
+ export GOARCH=arm
+ export GOARM=6
+ ;;
+ armv5)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/arm-linux-musleabi-cross.tgz"
+ export GOARCH=arm
+ export GOARM=5
+ ;;
+ 386)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/i686-linux-musl-cross.tgz"
+ ;;
+ s390x)
+ TOOLCHAIN_URL="$MUSL_CC_HOST/s390x-linux-musl-cross.tgz"
+ ;;
+ esac
+ echo "Downloading musl toolchain for ${{ matrix.platform }}"
+ curl -LO "$TOOLCHAIN_URL"
+ tar -xf *.tgz
+ TOOLCHAIN_DIR=$(find . -maxdepth 1 -type d -name "*-cross" | head -n1)
+ TOOLCHAIN_DIR=$(realpath "$TOOLCHAIN_DIR")
+ export PATH="$TOOLCHAIN_DIR/bin:$PATH"
+ # Detect compiler
+ export CC=$(find $TOOLCHAIN_DIR/bin -name '*-gcc' | head -n1)
+ echo "Using CC=$CC"
+ go build -ldflags "-w -s -linkmode external -extldflags '-static'" -o xui-release -v main.go
+ file xui-release
+ ldd xui-release || echo "Static binary confirmed"
mkdir x-ui
cp xui-release x-ui/
@@ -84,7 +100,7 @@ jobs:
cd x-ui/bin
# Download dependencies
- Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.7.26/"
+ Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.8.3/"
if [ "${{ matrix.platform }}" == "amd64" ]; then
wget -q ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip
@@ -101,14 +117,14 @@ jobs:
wget -q ${Xray_URL}Xray-linux-arm32-v6.zip
unzip Xray-linux-arm32-v6.zip
rm -f Xray-linux-arm32-v6.zip
- elif [ "${{ matrix.platform }}" == "armv5" ]; then
- wget -q ${Xray_URL}Xray-linux-arm32-v5.zip
- unzip Xray-linux-arm32-v5.zip
- rm -f Xray-linux-arm32-v5.zip
elif [ "${{ matrix.platform }}" == "386" ]; then
wget -q ${Xray_URL}Xray-linux-32.zip
unzip Xray-linux-32.zip
rm -f Xray-linux-32.zip
+ elif [ "${{ matrix.platform }}" == "armv5" ]; then
+ wget -q ${Xray_URL}Xray-linux-arm32-v5.zip
+ unzip Xray-linux-arm32-v5.zip
+ rm -f Xray-linux-arm32-v5.zip
elif [ "${{ matrix.platform }}" == "s390x" ]; then
wget -q ${Xray_URL}Xray-linux-s390x.zip
unzip Xray-linux-s390x.zip
@@ -124,9 +140,16 @@ jobs:
- name: Package
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
-
+
+ - name: Upload files to Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: x-ui-linux-${{ matrix.platform }}
+ path: ./x-ui-linux-${{ matrix.platform }}.tar.gz
+
- name: Upload files to GH release
uses: svenstaro/upload-release-action@v2
+ if: github.event_name == 'release' && github.event.action == 'published'
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
diff --git a/DockerInitFiles.sh b/DockerInitFiles.sh
index ca522254..bc7b0f6d 100755
--- a/DockerInitFiles.sh
+++ b/DockerInitFiles.sh
@@ -23,7 +23,7 @@ case $1 in
esac
mkdir -p build/bin
cd build/bin
-wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.7.26/Xray-linux-${ARCH}.zip"
+wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.8.3/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat LICENSE README.md
mv xray "xray-linux-${FNAME}"
diff --git a/sub/subService.go b/sub/subService.go
index 9ea634aa..d9698729 100644
--- a/sub/subService.go
+++ b/sub/subService.go
@@ -429,6 +429,11 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
params["fp"] = fp
}
}
+ if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok {
+ if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 {
+ params["pqv"] = pqv
+ }
+ }
params["spx"] = "/" + random.Seq(15)
}
@@ -619,6 +624,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
params["fp"] = fp
}
}
+ if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok {
+ if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 {
+ params["pqv"] = pqv
+ }
+ }
params["spx"] = "/" + random.Seq(15)
}
}
diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js
index f570ba49..1739b385 100644
--- a/web/assets/js/model/inbound.js
+++ b/web/assets/js/model/inbound.js
@@ -1385,6 +1385,9 @@ class Inbound extends XrayCommonClass {
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
params.set("spx", this.stream.reality.settings.spiderX);
}
+ if (!ObjectUtil.isEmpty(this.stream.reality.settings.mldsa65Verify)) {
+ params.set("pqv", this.stream.reality.settings.mldsa65Verify);
+ }
if (type == 'tcp' && !ObjectUtil.isEmpty(flow)) {
params.set("flow", flow);
}
@@ -1565,6 +1568,9 @@ class Inbound extends XrayCommonClass {
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
params.set("spx", this.stream.reality.settings.spiderX);
}
+ if (!ObjectUtil.isEmpty(this.stream.reality.settings.mldsa65Verify)) {
+ params.set("pqv", this.stream.reality.settings.mldsa65Verify);
+ }
}
else {
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 279255e1..8f1f24a6 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -392,7 +392,8 @@ class RealityStreamSettings extends CommonClass {
fingerprint = '',
serverName = '',
shortId = '',
- spiderX = '/'
+ spiderX = '',
+ mldsa65Verify = ''
) {
super();
this.publicKey = publicKey;
@@ -400,6 +401,7 @@ class RealityStreamSettings extends CommonClass {
this.serverName = serverName;
this.shortId = shortId
this.spiderX = spiderX;
+ this.mldsa65Verify = mldsa65Verify;
}
static fromJson(json = {}) {
return new RealityStreamSettings(
@@ -408,6 +410,7 @@ class RealityStreamSettings extends CommonClass {
json.serverName,
json.shortId,
json.spiderX,
+ json.mldsa65Verify,
);
}
toJson() {
@@ -417,6 +420,7 @@ class RealityStreamSettings extends CommonClass {
serverName: this.serverName,
shortId: this.shortId,
spiderX: this.spiderX,
+ mldsa65Verify: this.mldsa65Verify,
};
}
};
@@ -792,7 +796,8 @@ class Outbound extends CommonClass {
let sni = url.searchParams.get('sni') ?? '';
let sid = url.searchParams.get('sid') ?? '';
let spx = url.searchParams.get('spx') ?? '';
- stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx);
+ let pqv = url.searchParams.get('pqv') ?? '';
+ stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx, pqv);
}
const regex = /([^@]+):\/\/([^@]+)@(.+):(\d+)(.*)$/;
diff --git a/web/html/xui/form/outbound.html b/web/html/xui/form/outbound.html
index 536ab697..8c679ebf 100644
--- a/web/html/xui/form/outbound.html
+++ b/web/html/xui/form/outbound.html
@@ -447,6 +447,9 @@
+
+
+