One Flux port carries three different routes
Flux Orbit exposes one public TLS application port for this container shape. The launcher listens on port 2053 and chooses an internal destination from the HTTP path. Requests under /xvpnws/ go to the VLESS WebSocket inbound on 20868, /sub/ goes to the subscription service on 2096, and every other path goes to the 3X-UI panel on 20530.
Go's standard reverse proxy handles WebSocket upgrades after the HTTP handshake, so the routing trick needs little code. It also sets the protocol boundary. Raw TCP has no HTTP path to inspect, while REALITY needs the original TLS ClientHello that Flux has already terminated. The README says both modes are incompatible with the main public port unless the deployment provides a separate raw container port that bypasses this proxy.
What happened when we ran it
Our sandbox installed commit f7bf5bf in 5 seconds and built it in 18 seconds using Go 1.24 on Debian. The container had 3 CPUs, 8 GB of RAM, no secrets, and no elevated privileges. The repository contained 3 files and about 147 source lines; its checkout size rounded to 0 MB. One package was installed.
The test command finished in 6 seconds with 0 passed and 0 failed because it discovered 0 tests. That is a successful command, not a tested proxy. Our scan also found 0 CI workflow files, no Dockerfile, and no tests directory. Nothing in this run exercised an HTTP route, WebSocket upgrade, 3X-UI startup, Flux edge, archive download, or panel login.
First start executes an unpinned upstream release
If /app/x-ui/x-ui is missing, the launcher downloads x-ui-linux-amd64.tar.gz from the upstream 3X-UI releases/latest URL. It extracts that archive, marks binaries executable, and starts x-ui with database and log paths under /app/data. This avoids compiling the upstream Vue interface in the Flux build environment. It also moves a material part of deployment into container startup.
The URL does not name a version. On October 2, upstream's latest release was v3.8.5, but a future empty volume or rebuild may retrieve something else without any change in this repository. The upstream release publishes a SHA-256 file beside the amd64 archive. This launcher does not fetch or compare it. Pin the release URL and expected digest in reviewed source before treating the image as reproducible.
Archive extraction needs a containment check
The extractor joins each tar header name to /app and writes regular files without first rejecting absolute paths or .. components. The current archive comes from the upstream 3X-UI release channel, so this is a supply-chain boundary rather than evidence of an active exploit. A compromised or malformed archive could make that missing containment check consequential.
Download handling also uses the default HTTP client without an explicit timeout. After starting 3X-UI, the proxy waits 3 seconds rather than polling a health endpoint. A slow startup can therefore expose proxy errors until the panel becomes ready. These are repairable problems in 147 lines, but the repository has no tests covering the downloader, extractor, child process, or routing table.
The admin panel is the default public route
Anything outside the two special prefixes is proxied to the 3X-UI panel. That makes panel authentication, password rotation, update policy, and access logging part of the public security boundary. The README tells operators to keep panel credentials, client UUIDs, and subscription links private. It does not add another authentication layer or network allowlist in front of the panel.
Flux terminates TLS before traffic reaches the container, so the configured 3X-UI inbound uses none for its own security setting. That is correct for this topology only if requests cannot bypass the trusted edge and reach the internal port directly. Verify the actual Flux port exposure, then test the public hostname, wrong paths, subscription URLs, WebSocket upgrades, and direct access attempts before creating real client credentials.
Every new inbound requires a code change
The routing table is compiled into main.go. To add another WebSocket inbound, an operator chooses a free internal port and unique path prefix, edits the slice, commits, pushes, and asks Orbit to Pull and Build. A simple redeploy is insufficient because the Go binary must be rebuilt. The same port and path then need matching entries inside 3X-UI.
Hard-coded routing keeps this personal deployment understandable, but it does not scale into a shared control plane. There is no environment-driven route table, collision validation, configuration file, or runtime reload. That may be acceptable for one self-hoster with one inbound. It is a poor fit for a team that changes users, routes, or protocols frequently.
September activity does not replace ownership of the fork
GitHub showed 223 stars, 0 open issues or pull requests, and a last push on September 10, 2026. The wrapper has no tagged release and no detected license. Upstream 3X-UI is a separate GPL-3.0 project with its own releases and issue queue; activity there does not test or maintain this launcher's routing and download behavior.
The wrapper solves one awkward platform constraint with a readable amount of Go. That is its appeal and its ceiling. Our 18-second build confirms the source compiles, while the 0-test suite leaves every operational promise unverified. If Flux's one-port rule is unavoidable, fork it, pin the upstream artifact, add checksum and extraction checks, and write route tests. Otherwise, use the upstream panel directly.

