Linux / macOS (build script):
./dockerbuild.sh all # both variants (ubuntu + rocky)
./dockerbuild.sh ubuntu # ubuntu only
./dockerbuild.sh rocky # rocky only
./dockerbuild.sh --all # multi-arch (amd64 + arm64)Linux / macOS (Makefile):
make ubuntu # ubuntu only
make rocky # rocky only
make multiarch # multi-arch (amd64 + arm64)
make check # verify current version tarballs existWindows:
dockerbuild.bat all
dockerbuild.bat ubuntuWindows:
dockerbuild.bat all
dockerbuild.bat ubuntuWhen Futu releases a new FutuOpenD version, bump both repos with one command:
./scripts/bump-version.sh 10.12.7208 --commit # dry-run by default; omit --commit to previewOr step-by-step:
# Check what version would be found (brute-force loop)
./scripts/check-version.sh --discover
# Validate a specific version exists on CDN
./scripts/check-version.sh 10.12.7208
# Update Dockerfiles + checksums (futuopend only)
./scripts/check-version.sh --update 10.12.7208
# Also patch futuopend-deploy files (README, docs, XML template)
./scripts/check-version.sh --update 10.12.7208 --deploy ../futuopend-deploy
# Full pipeline with commits and pushes
./scripts/check-version.sh --update 10.12.7208 --deploy ../futuopend-deploy --commitAutomated option: the version-poller.yml workflow runs every Sunday at 00:00 UTC (or manually via GitHub Actions UI). If it finds a new version on Futu's CDN it opens a PR against main with the Dockerfile bump. After merging that PR, trigger version-bump.yml in futuopend-deploy:
gh workflow run version-bump.yml -f version=10.12.7208 --repo shing1211/futuopend-deploy| File | Purpose |
|---|---|
Dockerfile.ubuntu |
Ubuntu 26.04 LTS build (amd64/arm64) |
Dockerfile.rocky |
Rocky Linux 9 build (amd64/arm64) |
Makefile |
Build targets: make ubuntu, make rocky, make multiarch, make check |
entrypoint.sh |
Container entry with graceful shutdown |
scripts/check-version.sh |
Check/discover versions; update Dockerfiles + checksums |
scripts/bump-version.sh |
Cross-repo version bump in one shot (dry-run default) |
.github/workflows/version-poller.yml |
Weekly schedule + manual: discovers new versions, opens PR |
- FutuOpenD: 10.11.7108 (2026-09-17)
- Base: Ubuntu 26.04 LTS / Rocky Linux 9
- Entry Script:
entrypoint.sh(graceful SIGTERM/SIGINT handling)
- CRLF line endings in shell scripts are auto-fixed during build via
sed - Login prompts use two different channels. "Please enter account" is printed to stdout and read from stdin (needs a TTY); "Please enter password" and any SMS/CAPTCHA prompt are delivered to telnet clients, and telnet commands need
\r\n. Without a published telnet port (-p 127.0.0.1:22222:22222oncompose run) the first login appears to hang silently after the account is entered.entrypoint.shnow prints this on every interactive start. - A remembered credential leaves a marker at
~/.com.futunn.FutuOpenD/F3CNN/UserAccMap/, but the filename is not the bare account number. FutuOpenD names it<account><region>()— e.g.<account-id>()by default, or<account-id>(hk)when a region is set. An exact-match test on the account silently fails and makes a working deployment look unauthenticated. Match on<account>(*: the literal(stops a shorter account from matching a longer one that merely shares its leading digits. Contents are an encrypted blob, so presence is all that can be checked without a real login. FUTU_OPEND_VERis baked in as anENVat build time, so it reflects the binary actually installed. The entrypoint uses it to refuse to run remember-login on pre-10.10 builds.- The two entrypoint pre-flight checks are deliberately asymmetric. The version gate is fatal and not overridable, because it keys off a build-time
ENVand is the only guard that stops repeated password-auth retries from consuming Futu login attempts. The cached-credential check is advisory, because it reads runtime files and would otherwise block a legitimate start if FutuOpenD ever changed its on-disk naming. - The image has no curl, wget, nc, or python3 — only
bash,pgrep, andenvsubst. Usebash -c 'exec 3<>/dev/tcp/host/port'for health probes. HEALTHCHECKmust use shell form for${FUTU_API_PORT}to expand at runtime; Docker storesCMD-SHELLverbatim rather than substituting at build time.
- Dockerfile deduplication complete (
final-amd64+final-arm64→ singlefinalstage) --allis the preferred multi-arch flag (alias for--multiarch)- ARM builds use QEMU emulation; see README ARM perf section for box64 alternative
-
Dockerfile deduplication: merged
final-amd64+final-arm64→ singlefinalstage -
Added
--allalias for--multiarchin dockerbuild.sh -
README: added ARM performance note and box64 recommendation
-
Smoke test: container healthy, port 11111 listening, FutuOpenD binary running
-
Fixed critical: Rocky Dockerfile was missing
finalstage body -
Fixed dockerbuild.bat:
--target final-%A%→--target final -
Fixed dockerbuild.sh: PLATFORM env var now filters arch loop (was hardcoded)
-
Fixed entrypoint.sh: use captured PID instead of pgrep (TOCTOU race fix)
-
Updated docs/ARCHITECTURE.md: all stale
final-amd64/final-arm64refs →final -
futuopend-deploy updated to 10.6.6608: version strings bumped in README.md, FutuOpenD.xml.template, docs/configuration.md; new features section added; FUTU_IMAGE + FUTU_PUSH_PROTO added to .env.example
-
futuopend upgraded to 10.7.6708: 5 new vendored .so libraries (libcrypto.so.3, libcurl.so.4, libf3cnet.so, libprotobuf.so.32, libssl.so.3); FutuOpenD.xml byte-identical to 10.6.6608 (no schema changes); 8 Docker Hub tags pushed (ubuntu+rocky × amd64+arm64 + centos aliases)
-
futuopend upgraded to 10.8.6808: Search API, Chart Indicators, Options Analysis, Market Fundamentals API; XML schema unchanged; 8 Docker Hub tags pushed
-
futuopend upgraded to 10.9.6918: .so library set unchanged (11 libs); XML schema unchanged; fixed check-version.sh
$1unbound bug (${1:-}) -
futuopend upgraded to 10.10.7008: .so library set unchanged (11 libs);
FutuOpenD.xmlno longer shipslogin_account/login_pwd(10.10 defaults to interactive login — headless must pass credentials via config or-login_account/-login_by_remember); fixed dockerbuild.sh multi-arch centos mislabel + centos→rocky normalization;:latestnow a real multi-arch manifest list; dockerbuild.bat default version aligned -
OSS/Pages/CI hardening: GitHub Pages now deployed via Actions (
docs/index.mdlanding + just-the-docs theme); Discussions surfaced (SUPPORT.md+ issue templates); CI workflow (shellcheck, hadolint, version-check, build-smoke); Dependabot;mainbranch protection with required checks;CLAUDE.md/.claude/removed from public repo; base images digest-pinned; tarball SHA256 verified at build time -
futuopend upgraded to 10.11.7108: .so set unchanged (11 libs);
FutuOpenD.xmlbyte-identical to 10.10.7008; 8 Docker Hub tags pushed -
Ubuntu base upgraded to 26.04 LTS (digest-pinned); FutuOpenD 10.11.7108 verified running on it (process, port, healthcheck, no missing libs)
-
Entrypoint renders config via envsubst: discovered FutuOpenD does NOT expand
${VAR}; entrypoint now usesenvsubst+-cfg_file; addedgettext-base/gettextto Dockerfiles;area_codedefaults+852, opt-in WebSocket viaFUTU_WS_PORT,-no_monitor=1 -
Released v10.11.7108-r2: CHANGELOG cut, GitHub Release created manually
-
Released futuopend-deploy v1.1.0: CHANGELOG cut, mkdocs banner bumped,
release.ymlPREV_TAG link fixed -
Upgrade tooling complete:
check-version.sh(--deploy, --commit, --discover, --dry-run),bump-version.sh(one-shot cross-repo),version-poller.yml(weekly Sunday cron + workflow_dispatch; opens PR on new version),version-bump.ymlin futuopend-deploy (auto-patches docs + creates GitHub Release),image-tag-verifyCI job (verifies Docker Hub tag exists before merging)
- ARM smoke test on Raspberry Pi (verify real hardware works, not just QEMU)
- Confirm 10.10 interactive-login change doesn't break futuopend-deploy headless configs