Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6339b84
feat: add support for OpenRC init system alongside systemd in xbctl a…
Fearless743 Apr 24, 2026
f7bb7f6
fix: ensure credentials are exported when loaded in xbctl and install…
Fearless743 Apr 24, 2026
82965e9
feat: auto-select kernel by protocol, remove kernel.type config
Fearless743 May 22, 2026
4d08e15
Merge branch 'feat/auto-kernel-by-protocol' into dev
Fearless743 May 22, 2026
aee8747
Update README.md
Fearless743 May 22, 2026
923af33
chore: 更新默认下载源地址为新仓库
Fearless743 May 22, 2026
08fbcfb
feat(xray): add SS2022 key normalization and comprehensive integratio…
Fearless743 May 22, 2026
ad86046
fix(xray): fix e2e test SSRF failures and freePort UDP race
Fearless743 May 22, 2026
04296df
fix(service): respect kernel.type config in newService for multi-kern…
Fearless743 May 22, 2026
7327d54
fix(model): route hysteria protocol to singbox kernel
Fearless743 May 29, 2026
45c2a63
fix(ci): use current repo name for GHCR image tags
Fearless743 May 29, 2026
214f885
fix: use build-time repo URL for xbctl upgrade downloads
Fearless743 May 29, 2026
854d26a
feat(ci): auto-increment tag on binary changes
Fearless743 May 29, 2026
0dccb1d
perf: comprehensive performance and memory optimizations
Fearless743 May 29, 2026
cbdaa1d
fix(ci): recognize perf: prefix for auto-release bumps
Fearless743 May 29, 2026
5fba3ce
fix(ci): skip auto-release when tag already exists
Fearless743 May 29, 2026
14410ea
feat: add AGENTS.md, optimize installer binary reuse, add mihomo kern…
Fearless743 Jun 24, 2026
40fb317
fix(install): send reuse-binary log to stderr to avoid corrupting cap…
Fearless743 Jun 26, 2026
2d11f55
fix(install): don't reuse installed binary during upgrade
Fearless743 Jul 1, 2026
da10c3b
Delete .opencode directory
Fearless743 Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Auto Release

on:
push:
branches: [dev]

permissions:
contents: write

jobs:
bump-and-tag:
name: Bump version & tag
runs-on: ubuntu-latest
outputs:
tagged: ${{ steps.bump.outputs.tagged }}
new_tag: ${{ steps.bump.outputs.new_tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check for binary changes since last tag
id: changed
run: |
LATEST_TAG=$(git tag -l 'v*' --sort=-version:refname | head -n1)
if [ -z "$LATEST_TAG" ]; then
echo "No existing tags found — treating as first release."
echo "has_changes=true" >> "$GITHUB_OUTPUT"
echo "base=HEAD~1" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
echo "base=$LATEST_TAG" >> "$GITHUB_OUTPUT"

CHANGED=$(git diff --name-only "$LATEST_TAG"..HEAD -- \
cmd/xbctl/ cmd/xboard-node/ internal/ go.mod go.sum)
if [ -n "$CHANGED" ]; then
echo "has_changes=true" >> "$GITHUB_OUTPUT"
echo "Changed files:"
echo "$CHANGED"
else
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "No binary-related changes since $LATEST_TAG."
fi

- name: Determine version bump
id: bump
if: steps.changed.outputs.has_changes == 'true'
run: |
BASE=${{ steps.changed.outputs.base }}
COMMITS=$(git log --format='%s' "$BASE"..HEAD)

if [ -z "$COMMITS" ]; then
echo "No new commits."
echo "tagged=false" >> "$GITHUB_OUTPUT"
exit 0
fi

# Determine bump level from commit prefixes.
# Priority: breaking > feat > perf/fix > chore/other (no auto-bump)
BUMP=""
while IFS= read -r msg; do
lower=$(echo "$msg" | tr '[:upper:]' '[:lower:]')
if echo "$lower" | grep -qE '^[a-z]+(\(.+\))?!:' || echo "$lower" | grep -qi 'breaking.change'; then
BUMP="major"
break
fi
if echo "$lower" | grep -qE '^feat(\(.+\))?:'; then
[ "$BUMP" != "major" ] && BUMP="minor"
fi
if echo "$lower" | grep -qE '^(fix|perf)(\(.+\))?:'; then
[ -z "$BUMP" ] && BUMP="patch"
fi
done <<< "$COMMITS"

if [ -z "$BUMP" ]; then
echo "No feat/fix/breaking commits found — skipping release."
echo "tagged=false" >> "$GITHUB_OUTPUT"
exit 0
fi

LATEST_TAG=${{ steps.changed.outputs.latest_tag }}
if [ -z "$LATEST_TAG" ]; then
LATEST_TAG="v0.0.0"
fi

# Strip leading 'v' and parse semver
VERSION=${LATEST_TAG#v}
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
# Default to 0 if empty (e.g. v1.10 has no patch)
: "${PATCH:=0}"

case "$BUMP" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac

NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}"
echo "Bump: $BUMP | $LATEST_TAG → $NEW_TAG"
echo "new_tag=$NEW_TAG" >> "$GITHUB_OUTPUT"
echo "tagged=true" >> "$GITHUB_OUTPUT"

- name: Create and push tag
if: steps.bump.outputs.tagged == 'true'
run: |
TAG=${{ steps.bump.outputs.new_tag }}
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists — skipping."
exit 0
fi
git tag "$TAG"
git push origin "$TAG"
echo "Pushed tag $TAG — CI will build & release automatically."
16 changes: 13 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ jobs:
- name: Build
env:
VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || github.sha }}
REPO_BASE: https://github.com/${{ github.repository }}/releases
run: |
# Ensure REPO_BASE is lowercase (GitHub releases URLs are case-insensitive,
# but keep it consistent with GHCR image names).
REPO_BASE="${REPO_BASE,,}"
export REPO_BASE
if [ "${{ matrix.goarch }}" = "amd64" ]; then
make build-linux
else
Expand Down Expand Up @@ -91,16 +96,20 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Set image name (lowercase)
id: image
run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/cedar2025/xboard-node:latest
ghcr.io/cedar2025/xboard-node:${{ github.sha }}
${{ startsWith(github.ref, 'refs/tags/') && format('ghcr.io/cedar2025/xboard-node:{0}', github.ref_name) || '' }}
${{ steps.image.outputs.name }}:latest
${{ steps.image.outputs.name }}:${{ github.sha }}
${{ startsWith(github.ref, 'refs/tags/') && format('{0}:{1}', steps.image.outputs.name, github.ref_name) || '' }}

release:
name: Release
Expand All @@ -121,6 +130,7 @@ jobs:
with:
tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'dev' }}
make_latest: ${{ startsWith(github.ref, 'refs/tags/') }}
generate_release_notes: true
files: |
dist/xboard-node-linux-amd64
dist/xboard-node-linux-arm64
Expand Down
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# xboard-node — Agent Instructions

## Repo overview

Go 1.26 monorepo. Two binaries, three kernels.

- `cmd/xboard-node` — main daemon. Entry: `cmd/xboard-node/main.go`
- `cmd/xbctl` — CLI management tool. Entry: `cmd/xbctl/main.go`
- `internal/kernel/` — kernel adapters: `singbox/`, `xray/`, `mihomo/`
- `internal/machine/` — machine-mode orchestrator (single instance)
- `internal/service/` — node-mode service (spawned per node)
- `internal/config/` — config loading, normalization, hot-reload watcher
- `internal/panel/` — panel API client + WebSocket
- `internal/controlplane/` — inbound listener management
- `internal/cert/` — TLS certificate management (HTTP/DNS-01)
- `internal/limiter/` — per-user speed limiting + traffic tracking
- `internal/tracker/` — alive-IP tracking
- `internal/monitor/` — health monitoring
- `internal/model/` — data types + validation

Three kernels are embedded as Go libraries (not external processes). The `go.mod` uses `replace` directives to use patched forks of `sing-box`, `xray-core`, and `mihomo`.

## Commands

```bash
make build # build xboard-node + xbctl (current platform)
make build-linux # cross-compile linux/amd64
make build-linux-arm64 # cross-compile linux/arm64
make build-all # both linux targets
make test # go test -v -race -count=1 ./internal/...
make clean # remove build artifacts
make install # build + copy to /usr/local/bin, install example config
make docker # build Docker image
```

Build tags required for full feature set: `with_quic with_utls with_wireguard with_acme with_clash_api`

Tests: `make test` runs all `./internal/...` tests with race detector. No test framework beyond `go test`. Benchmarks are in files matching `*_bench_test.go` (gitignored).

## Gotchas

- **Config file**: `config.yml` is gitignored. Copy from `config.yml.example` or run `make install`.
- **Sing-box fork**: The `go.mod` replaces `github.com/sagernet/sing-box` with `github.com/cedar2025/sing-box` — do not assume upstream sing-box APIs are stable.
- **Xray fork**: Similarly replaced with `github.com/cedar2025/Xray-core`.
- **Mihomo fork**: Replaced with `github.com/Fearless743/mihomo`.
- **Multi-instance**: A single config can define multiple panel bindings via `instances:`. The daemon spawns one service per node or one machine orchestrator per instance.
- **Hot reload**: Config changes trigger a full restart of all instances (SIGINT → graceful shutdown → re-parse → restart).
- **Health endpoint**: `/healthz` on configurable port (default 65530). Port 0 disables it.
- **Environment variables**: Panel config can be supplied entirely via env vars (`API_HOST`, `API_KEY`, `NODE_ID`, `KERNEL`, `DOMAIN`, `CERT_FILE`, `KEY_FILE`, `LOG_LEVEL`) — no config file needed.
- **Release flow**: Tag pushes trigger CI release. `dev` branch auto-releases on binary changes (detected by `cmd/`, `internal/`, `go.mod`, `go.sum`). Version bump is conventional-commits-based (`feat`→minor, `fix`/`perf`→patch, `breaking!`→major).
- **Docker**: Uses `--network=host` by default. Alpine-based multi-stage build.
- **Installer**: `install.sh` supports `systemd` and `openrc`. Requires root. Has rollback on failure.

## Extension docs

- Custom routes: `docs-custom-routes.md`
- Custom outbounds: `docs-custom-outbounds.md`
- DNS providers (ACME): `docs-dns-providers.md`
- Mihomo kernel specifics: `docs-mihomo-kernel.md`
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
BUILD_TIME ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -s -w -X main.version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
REPO_BASE ?= https://github.com/Fearless743/xboard-node/releases
LDFLAGS := -s -w -X main.version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.commit=$(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) -X 'main.downloadBase=$(REPO_BASE)'

.PHONY: build clean test docker install build-linux build-linux-arm64 build-all

Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# xboard-node

Node backend for [Xboard](https://github.com/cedar2025/Xboard). Supports `sing-box` / `xray-core` dual kernels.
Node backend for [Xboard](https://github.com/cedar2025/Xboard). Supports `xray-core` / `sing-box` / `mihomo` triple kernels.

> **Disclaimer**: This project is for educational and learning purposes only.

## Features

- Protocols: V2Ray family, Trojan, Shadowsocks, Hysteria2, TUIC, AnyTLS
- Kernels: xray-core, sing-box, mihomo (triple kernel support)
- Sync: WebSocket push + REST polling dual channel
- User controls: speed limit, device limit, alive-IP tracking, hot update
- Deploy modes: node mode, machine mode, standalone mode
Expand All @@ -19,13 +20,13 @@ Node backend for [Xboard](https://github.com/cedar2025/Xboard). Supports `sing-b
```bash
docker run -d --restart=always --network=host \
-e apiHost=https://panel.com -e apiKey=TOKEN -e nodeID=1 \
ghcr.io/cedar2025/xboard-node:latest
ghcr.io/fearless743/xboard-node:latest
```

### Docker Compose

```bash
git clone -b compose --depth 1 https://github.com/cedar2025/xboard-node.git
git clone -b compose --depth 1 https://github.com/fearless743/xboard-node.git
cd xboard-node
vim config/config.yml # set panel.url / token / node_id
docker compose up -d
Expand All @@ -35,11 +36,11 @@ docker compose up -d

```bash
# Node mode
curl -fsSL https://raw.githubusercontent.com/cedar2025/xboard-node/dev/install.sh | \
curl -fsSL https://raw.githubusercontent.com/fearless743/xboard-node/dev/install.sh | \
sudo bash -s -- --mode node --panel https://panel.example.com --token TOKEN --node-id 1

# Machine mode
curl -fsSL https://raw.githubusercontent.com/cedar2025/xboard-node/dev/install.sh | \
curl -fsSL https://raw.githubusercontent.com/fearless743/xboard-node/dev/install.sh | \
sudo bash -s -- --mode machine --panel https://panel.example.com --token TOKEN --machine-id 1

## xbctl
Expand All @@ -64,6 +65,7 @@ Legacy single-panel config is fully compatible. Appending bindings auto-migrates
- Custom routes: [docs-custom-routes.md](docs-custom-routes.md)
- Custom outbounds: [docs-custom-outbounds.md](docs-custom-outbounds.md)
- DNS providers (ACME DNS-01): [docs-dns-providers.md](docs-dns-providers.md)
- Mihomo kernel: [docs-mihomo-kernel.md](docs-mihomo-kernel.md)

## License

Expand Down
Loading