Split the lx go vet step into two passes so every lx-owned package keeps
the full analyzer set; only daemon/ and experimental/libbox/ (upstream
TriggerDebugCrash/TriggerGoPanic) drop the unsafeptr check.
snapshot.debian.org intermittently 503s during the musl sysroot build and
blocks releases (v1.14.0-lx.2-rc.1 failed twice on it). actions/cache also
misses across tag builds (ref-scoping). Add a producer workflow that uploads
the built toolchain to a musl-toolchain-cache release, and a restore step in
lx-release.yml that pulls it on cache-miss before falling back to
snapshot.debian.org. Both workflows are lx-owned; zero upstream diff.
The 1.14 merge (6b63cee4) brought daemon/managed_service.go and
experimental/libbox/debug.go into vet scope; both crash Go ON PURPOSE via
*(*int)(unsafe.Pointer(uintptr(0)))=0 (TriggerDebugCrash/TriggerGoPanic),
which vet's unsafeptr analyzer flags. They are upstream files we don't edit,
and no lx-owned file uses unsafe at all, so disable just that analyzer.
Fixes the red lint job on every push since the merge.
rc.22 linux-musl jobs failed on 'get-clang.sh: 503 No healthy backends' from
snapshot.debian.org (transient mirror outage). get-clang.sh retries internally
but back-to-back, so a whole outage window fails all attempts. Wrap the two
Debian-fetching steps with an external retry + growing backoff:
- Download Chromium musl toolchain: 5 attempts, 30→60→120→240s
- Regenerate Debian keyring: 4 attempts, 20→40→80s
Conservative: publish still needs all builds (a real musl breakage still blocks
the release, only transient mirror flakes are ridden out). No code change.
Requested in #6 (Atheros AR9344). Chromium/cronet has no big-endian MIPS
toolchain, so the musl+naive path is impossible for this target; add it to
the plain cross-compile matrix instead as a pure-Go CGO_ENABLED=0 build —
statically linked (runs on musl/OpenWrt as-is), with with_naive_outbound
and with_purego dropped (purego has no mips port either). Everything else
matches the desktop tag set.
Verified locally: GOOS=linux GOARCH=mips GOMIPS=softfloat build with the
reduced tag set compiles clean; `file` reports ELF 32-bit MSB MIPS32,
statically linked.
The base-version step (c4fd73cd) adds an `upstream` remote (SagerNet/sing-box)
so git-describe can see the v1.14.0-alpha.* tags. But `gh release create` without
--repo resolves the target repo from the remotes and picked `upstream` →
HTTP 403 "Resource not accessible by integration" against
api.github.com/repos/SagerNet/sing-box/releases (the token has no rights there).
This is why rc.19's builds all succeeded but publish failed, while rc.18 (before
the upstream remote existed) published fine.
Pin --repo "${{ github.repository }}" so publish always targets this fork
regardless of what remotes the earlier steps added.
The base-version derivation used `git describe --match v1.14.0-alpha.*` as the
primary source, but actions/checkout only fetches THIS repo's tags — the alpha
tags are SagerNet/sing-box (upstream) tags, absent in the CI clone. So `git
describe` found nothing and silently fell to the subject-grep fallback, which
resolves alpha.36 (alpha.37 was merged in a commit whose subject omits the
number). That's why rc.17/rc.18 notes shipped "base alpha.36" while a local
clone with upstream tags gets 37.
Fetch just the upstream v1.14.0-alpha.* tags before git describe so the primary
graph-based path works in CI. Both hand-fixed on the published releases; this
makes the next tag correct automatically.
docs/ is an upstream-owned tree (it arrives wholesale from SagerNet on
every rebase). Our three downstream docs lived inside it — lx-config.md,
lx-changelog.md, lx-release-runbook.md — mixing fork files into the
upstream surface against CONSTITUTION principle #1 (thin layer / minimal
diff). Move them to a dedicated root-level docs-lx/ so the boundary
between our docs and upstream's is explicit.
- git mv preserves history.
- Updated every reference (docs/lx-* -> docs-lx/lx-*): README.md/.ru.md,
SPECS/{003,004,005,009,020,README}, transport/wireguard/endpoint.go
comments, lx-ci.yml, and lx-release.yml (the release-notes extractor +
fallback URL now read docs-lx/lx-changelog.md).
- Fixed the now-relative links inside the moved files that pointed at
upstream docs/ siblings: lx-config.md -> ../docs/configuration/outbound/
urltest.md; lx-changelog.md -> ../docs/changelog.md (x2).
Verified: all relative + external links resolve, both workflows are valid
YAML, the release-notes awk path is docs-lx/, go vet clean on the touched
package. No release feature — folds into the next tag naturally.
The subject-grep base-detection missed alpha.37: it was merged in a commit
titled "Merge upstream/testing (bump version, fix linux ping)" with no
"alpha.37" in the subject (upstream tagged it after we merged), so the grep
found only alpha.36 and rc.17 notes shipped a stale base.
Make `git describe --match v1.14.0-alpha.*` the primary source — it reads
HEAD's ancestry in the commit graph, independent of merge-message wording —
and keep the subject-grep as the fallback for a fork checkout without
upstream tags. Verified locally: now resolves v1.14.0-alpha.37.
SPEC 014 dropped with_clash_api because LxBox (Android) drives the core
over the native libbox CommandClient, making the Clash REST server dead
weight in the AAR. But the drop landed in the shared Makefile.lx LX_TAGS,
which also feeds every desktop/CLI release build (mac/windows/linux-musl
via `make -s lx-print-tags`). A CLI binary has no CommandClient channel —
it is managed by external dashboards (yacd/MetaCubeXD) over the Clash REST
API — so every desktop release since rc.1 shipped with no way to manage
the core; a config with experimental.clash_api failed fast. CI stayed
green (lx-ci BASE_TAGS kept the tag), so it was invisible in CI.
Restore with_clash_api to the desktop LX_TAGS; leave build_libbox (AAR)
unchanged. The two tag sets now diverge by design: desktop = with Clash
API, AAR = without.
Verified: desktop binary builds with with_clash_api in Tags; `check`
accepts an experimental.clash_api config; the Clash REST server comes up
live (endpoints answer 401 security-middleware, not the stub's fail-fast).
Docs: Makefile.lx comment, SPEC 014 (§2/§3.1 scoped to AAR + new §3.4),
lx-release.yml tag comment + notes line, changelog rc.17.
The release-notes template hardcoded "base v1.14.0-alpha.35"; it went stale and
had to be hand-edited on rc.14, rc.15 and rc.16 (each was actually on alpha.36).
Resolve the base dynamically in the "Resolve tag" step: take the highest alpha.NN
named in any "Merge upstream" commit subject (robust on a fork without upstream
tags fetched), falling back to git describe against upstream alpha tags, then a
generic v1.14.x label. The notes line now interpolates steps.ver.outputs.base.
The release-notes heredoc hardcoded 'base v1.13.13' and only AWG+XHTTP — stale
since the 1.14 migration, identical for every rc, and never reflecting what a tag
actually shipped (SPEC 014/015/017/018 were invisible). Now the 'What's new'
section is extracted from docs/lx-changelog.md for the current version (awk between
'#### vX' and the next '#### '), spliced via 'sed r' so changelog backticks/$()
stay inert (no command injection from doc prose). Base line fixed to alpha.35;
standing-features list updated with the CommandClient extensions.
Restore over the native libbox CommandClient what upstream only exposed through
the dropped Clash API: per-node delay testing and a route+DNS rule-table snapshot.
Both RPCs are a pure bridge (CONSTITUTION §3.6) gated by the with_lx_command tag.
- daemon/started_service.proto: URLTestOutbound + GetRules RPCs and messages under
the // lx:begin/end lx_command marker; regenerated .pb.go/_grpc.pb.go.
- daemon/started_service_command_lx.go (+ _stub.go): handlers behind with_lx_command,
stub twin returns codes.Unimplemented. URLTestOutbound resolves an outbound OR an
endpoint (no OutboundGroup assert), honours link+timeout, error-in-payload Variant B
(delay==0 && error=="" is success 0ms), history Store/Delete via group.RealTag.
GetRules returns route + DNS rules split by isDNS.
- adapter/dns.go + dns/router.go: new adapter.DNSRouter.Rules() getter (route Router
already had one), read under rulesAccess; both under // lx: markers.
- experimental/libbox/command_client_command_lx.go: CommandClient.URLTestOutbound
(*URLTestOutboundResult, error) and GetRules (RuleIterator, error) — gomobile-bindable
shapes (the SPEC's bare (uint16,string,error) does not bind); Variant B preserved.
- cmd/internal/build_libbox/main.go: with_lx_command into sharedTags (AAR).
- Makefile.lx: with_lx_command in LX_TAGS; pinned lx-proto/lx-proto-install targets
(protoc-gen-go v1.36.11, protoc-gen-go-grpc v1.5.1) for reproducible regeneration.
- lx-ci.yml: vet+gofmt cover the lx files; build-check proves both builds toggle the
stub marker.
- Collateral one-time pin normalisation of managed_service/v2rayapi/v2raygrpc .pb.go
(audited in SPEC §3.5).
docs(lx-changelog): v1.14.0-lx.1-rc.2. SPEC 014 → accepted.
LxBox is moving to manage the core over the native libbox CommandClient
(group/url-test/select/connections streams), so the Clash REST API is dead
weight on the client. Drop with_clash_api from both the Android AAR
(build_libbox sharedTags) and the desktop LX_TAGS. A config referencing
experimental.clash_api now fails fast (no silent fallback); lx configs won't.
lx-release.yml: tags with an -rc.N / -alpha.N / -beta.N suffix now publish as
GitHub pre-releases (--prerelease), so an unverified build never displaces the
stable lx release as Latest.
First build on the upstream 1.14 base. The WG-endpoint GRO fix (010) lands at
the AmneziaWG v0.0.3 submodule source (no downstream guard), but the Android
download-stall path is NOT yet re-verified on hardware -- hence the -rc.1 tag.
Full 1.14 migration, step 1 of 2 (sing-box repo layer). Three conflicts
resolved, all as predicted by the feasibility analysis:
- route/rule/rule_item_package_name_regex.go (add/add): took upstream's
canonical version (slices.ContainsFunc) — our lx.15 backport collapses
back into upstream, so the file no longer diverges going forward.
- route/rule_conds.go: kept our package_name_regex in isProcess{,DNS}Rule
and took upstream's new isNeighbor{,DNS}Rule additions.
- cmd/internal/build_libbox/main.go: kept lx with_xhttp/with_awg append and
the no-tailscale block; deliberately dropped upstream's new with_usbip
(server-side USB/IP, contradicts client-trim).
go.mod auto-merged: wireguard-go require bumped to v0.0.3, lx replace block
(=> ./submodules/wireguard-go) preserved. Submodule pointer unchanged here —
the AmneziaWG graft rebase onto v0.0.3 is step 2 (next commit). This commit
does NOT build yet (submodule still on the old wireguard-go base).
Manual workflow_dispatch builder for any branch/tag: target ∈
{android-aar, apple-xcframework, binary, linux-musl, all}, branch = any ref.
Mirrors lx-release build jobs but uploads artifacts instead of releasing.
Lives on lx (default branch) so `gh workflow run` can find it; each job
checks out the requested branch, so lx source is never required to build it.
gh workflow run lx-build.yml -f target=android-aar -f branch=<branch>
No project code touched — CI file only.
lx-release.yml: new build_linux_musl job (amd64/arm64/armv7/mipsle) that
clones cronet-go, fetches the Chromium musl toolchain via cmd/build-naive,
and builds CGO_ENABLED=1 with with_musl (swapping with_purego) so libcronet
is linked statically — no libdl.so.2, runs on musl routers, naive kept.
Linux moves out of the desktop build job. Artifact names mirror upstream
arch suffixes (armv7, mipsle-softfloat) without the -musl suffix since
Linux ships a single (musl) variant.
lx-ci.yml: dispatch-only linux_musl smoke job runs the same pipeline
(build + verify statically-linked / no libdl) without publishing.
awg2_ranged.json (fake keys) exercises ranged H1-H4 through sing-box
check; wire it into the positive and negative CI checks alongside
awg2_basic.json.
Mirrors upstream build.yml: the windows/386 leg uses a Win7-patched Go
(.github/setup_go_for_windows7.sh — MetaCubeX/go reverts of the Win7
removals) so the binary runs on Windows 7. Drops with_naive_outbound for
this leg (cronet-go has no windows/386 build); the rest of LX_TAGS
compiles for 386. Archive: sing-box-<ver>-windows-386-legacy-windows-7.zip,
matching the launcher's singbox-launcher-win7-32 (also 386).
- docs/lx-config.md: config reference for XHTTP transport and AmneziaWG 2.0
endpoint (field tables + examples with placeholder keys)
- .github/workflows/lx-ci.yml: matrix over the two features
(baseline / with_xhttp / with_awg / full) + negative check that feature-off
rejects its config; vet job; cross-platform matrix {linux,darwin,windows}x
{amd64,arm64} building the full lx set with the merged AWG fork (submodules)
- link the config doc from SPECS/README
Add fpm-based Alpine APK packaging alongside existing DEB/RPM/Pacman
packages. Alpine APKs use `linux` in the filename to distinguish from
OpenWrt APKs which use the `openwrt` prefix.
Move hardcoded build tags and ldflags from Makefile, Dockerfile, CI
workflows, and local build scripts into canonical files under release/:
- release/DEFAULT_BUILD_TAGS (Linux common archs, Darwin, Android)
- release/DEFAULT_BUILD_TAGS_WINDOWS (includes with_purego)
- release/DEFAULT_BUILD_TAGS_OTHERS (no with_naive_outbound)
- release/LDFLAGS (shared linker flags)