Reusable CUDA build dependencies

Normal task build and CI can use a checksum-pinned GitHub Release archive of the compiled official GGML 0.28.0/CUDA libraries. They compile the current app and UI against that installation. No registry login is needed to download the public archive. There is still one runtime image variant, with external model weights.

The application uses C++20 with the existing GCC 13/CUDA 12.9.1 toolchain. GGML and the other compiled dependencies retain C++17. Source builds configure dependencies before selecting C++20 for application tools and parity harnesses, so the existing compiled GGML artifact remains compatible and reusable.

Normal builds

task deps:plan
task deps:prepare
task build

The first two commands inspect the lock and optionally warm the dependency cache; task build performs those checks automatically. The cache lives under ignored build/ggml-release/<archive-sha256>/. Subsequent builds verify and reuse it.

The lock in docker/ggml-deps.json records the versioned asset URL, exact download size, archive SHA-256 and dependency input key. The previous unpatched GGML 0.28.0 dependency release is retained for rollback. The reviewed shared-mask fix is included in the pinned replacement dependency prerelease. Local Task builds and application CI verify and consume that same public archive. Application CI requires a published dependency pin before starting Docker, avoiding an accidental long source build when a replacement is being prepared. Configured archives use a specific asset, never the latest-release endpoint. The adjacent uploaded .sha256 file is useful for manual downloads; builds trust the checksum committed in the lock rather than fetching a replacement checksum from the server.

The downloader checks the compressed bytes before extraction. It rejects unsafe paths, links, special files, duplicate members and excessive payload size. It then checks the source/options key, installed-file hashes and absence of weights. Docker repeats the source, compiler/CUDA and file checks before linking the app. A missing, modified or incompatible configured artifact fails the build. It does not trigger a long source compilation as an automatic recovery step.

With GPU targeting set to auto, Task uses the released distribution targets when they cover the detected GPU. Explicit narrower targets and GPUs outside that list retain a separate matching source dependency path. Those builds can still require an initial CUDA compilation.

How CI consumes the archive

The container workflow prepares the dependency before starting Docker. The verified installation is supplied as the selected-ggml named build context, overriding the dependency-image stage. This works with a fresh Buildx builder; no locally loaded Docker image or retained native compiler cache is required. The GGML download/compile stages are outside that consumer’s build graph.

The archive contains opt/ggml/: public headers, static libraries, CMake exports, license and yue2-ggml.json provenance metadata. Its unpacked payload contains no compiler, application, build tree or model weights. Build tools and standalone static archives stay out of the final runtime image; GGML code is linked into the native executables. The final whole-image weight guard remains mandatory.

Plain docker build . uses the source dependency stages unless an artifact image or named context is explicitly supplied. Use Task for the automatic release archive path. See Docker runtime commands.

Refreshing the dependency release

Rebuild only after an intentional GGML, ABI/options, toolchain or GPU target change. The current GGML package preserves C++17, inference defaults and the existing distribution targets.

task deps:verify
task deps:build
task deps:export

deps:build deliberately compiles the dependency from source, even while a release archive is configured. The initial cold compilation is expensive. It creates yue2-ggml:<first-20-characters-of-input-key> in local Docker. deps:export verifies and exports that installation into data/results/build-deps/<key-prefix>/, producing a .tar.gz and matching .sha256. These generated binary files remain outside Git source control. Export also produces a small build-recipe archive and checksum containing the exact Dockerfile, source pin, CMake settings, metadata/checker tools and license notices. It contains no fetched GGML source or weights and reproduces the dependency input key.

Upload the dependency, recipe and their checksums to a separate GitHub Release tagged ggml-deps-<key-prefix>. Use a prerelease and leave the product’s latest-release label unset. Update the lock’s input key and archive URL, size and SHA-256 from the verified export. Prefer a tag at the matching committed recipe. For a dependency published before the application changes are committed, the attached recipe is the authoritative rebuild input; the tag only anchors the dependency release in repository history. Rebuild it with docker build --target ggml-deps . from the unpacked recipe. A ggml-deps-* tag does not match the product’s v*.*.* publication trigger.

Then run task test, task build and the GPU task test1 baseline. Qualify the actual uploaded download before considering the lock ready. A stale archive pin fails early while the replacement is being prepared; deps:build and deps:export remain available to produce it. To deliberately build the app entirely from source, clear archive, image and input_key in the lock.

Identity and alternative transport

The input key covers the checksum-pinned GGML source archive identity, the shared ABI definitions, dependency CMake wrapper, metadata/checker scripts, dependency-only Docker stages, pinned CUDA base, platform, architecture list and reviewed source patches. App/UI/version edits and release transport changes do not invalidate those compiled libraries. Keep the Dockerfile fingerprint boundary and shared ABI definitions intact.

Metadata records the GGML commit, exact compiler/CUDA versions, target list, input key and installed-file SHA-256 hashes. It is also copied into /opt/yue/licenses/ in the runtime. Checksums and metadata verify consistency; artifact hosting and reviewed locks establish which binary to trust.

Official source and local overrides

docker/ggml-source.json pins official version 0.28.0, its exact commit and source archive SHA-256. Source builds use CMake FetchContent to verify that archive before unpacking it under their build directory. No full GGML source is committed here. The single reviewed patch in cmake/ggml-patches/ adapts ServeurpersoCom/ggml PR #3: the CUDA attention-mask scan now broadcasts a shared mask across acoustic variations, as the attention kernels already do. The source lock records the patch checksum and exact before/after file hashes. CMake rejects unexpected source, verifies the result and safely skips an already applied patch on reconfiguration. Patch bytes and application logic enter the dependency fingerprint and exported build recipe. Our other overrides are the C++17/ABI settings in cmake/ggml-abi.cmake and CUDA/backend settings in cmake/ggml-deps/. No full dependency source is vendored.

tests/cuda/README.md documents a model-free compute-sanitizer reproduction. task benchmark-variations qualifies real two-minute renditions, sampled VRAM and full-song ASR. Fresh Studio/native generation defaults to two variations, with a maximum of three. Single-track compatibility routes and replays keep one.

The first source build needs network access; its downloaded source is reused by that build directory. Normal artifact consumers do not fetch the source at all. MIT license/copyright notices remain in vendor/licenses/ggml-MIT.txt and the distributed artifact/runtime. Updating the source pin requires a new compiled artifact; the old binary must not be relabeled to match new source.

Moving source out of Git does not fix vulnerabilities. Source-only scanning of tracked files cannot assess the downloaded GGML implementation. A build-based security analysis must include that fetched source; CPU analysis also does not qualify unused GPU backends. Existing CodeQL alerts need separate triage/rescanning.

Other native dependencies

docker/app-deps.json pins cpp-httplib 0.64.0, yyjson 0.13.0 and minimp3 commit ea99364f61c14656440e8d77e9c233ccf3124633. Every package has an exact upstream archive checksum and retained license notice. cmake/app-deps.cmake uses upstream CMake projects for HTTP/JSON and an interface target for the decoder header. No third-party source is kept in vendor/; that folder holds license notices. The inherited project MP3 encoder lives under src/codecs/mp3/.

The Docker app-deps stage downloads and compiles these small libraries once per lock/configuration/toolchain change. Application and UI edits reuse that layer. The application imports the installation with YUE2_APP_DEPS_PREFIX; its lock is checked before linking. CPU/native source builds fetch the same pinned archives under their build directory. Native builds need Python 3 for upstream httplib’s header splitting. HTTPS/compression extras remain disabled as before.

These small libraries do not need a separate binary release. Their updates leave the GGML dependency fingerprint and its existing published artifact unchanged. task deps:verify checks all native pins and retained notices. Dependency updates still require HTTP/API, decoder and generation testing; fetching newer sources alone is not a security audit. Build-based security scanning must include these downloaded sources too; scanning tracked files alone cannot assess them.

Roll back to the previous GGML dependency

The previous fork artifact remains published and is never overwritten or deleted: previous dependency release. docker/ggml-rollback.json retains its exact archive checksum, input key, source commit and matching application checkout. Keep the old release and its assets.

Use a separate checkout of that recorded revision for a complete compatible rollback, leaving current work untouched:

git worktree add --detach build/ggml-rollback 12cfefee2c2520ebec5059e9bacfc91f80e32204
cd build/ggml-rollback
task build IMAGE=yue2:ggml-rollback

That checkout uses the old source snapshot and old artifact identity checks. Do not paste its input key into the new lock or weaken verification to mix versions. Starting a rollback image is a separate action; finish active jobs before replacing a running server. No task automatically deletes previous dependency releases.

The OCI artifact image remains an optional transport. The manual .github/workflows/ggml-deps.yml workflow builds it and publishes to GHCR only when its publish input is selected. This is not required by normal builds. To use a registry artifact instead of the release archive, clear archive, then pin the actual published digest:

python scripts/ggml_deps.py pin --image ghcr.io/hangry-labs/yue2-ggml@sha256:<published-digest>

Local GHCR uploads require separate package-write credentials; public read access is needed for unauthenticated builds. Native Windows binaries cannot substitute for these Linux Docker-built libraries.

Caches and measurements

The source fallback also keeps installed GGML in an ordinary Docker layer, which external layer caches can retain. Cache mounts alone are not preserved by GitHub Actions caching. The release archive avoids depending on a warm layer cache or a one-time cold CI kernel compilation.

Normal application publication attaches inline cache metadata to its registry images and imports it from the published latest tags. It avoids a separate large GitHub Actions cache upload: the first optimized CI run spent 971 seconds exporting that cache after native compilation finished in about 66 seconds. Inline caching retains less intermediate build state than mode=max; the precompiled GGML release remains the primary reuse mechanism.

Local qualification on 2026-10-31 used Docker Desktop with 20 CPUs and about 31 GiB RAM. Two app/UI builds against the prebuilt distribution image took 68 and 58 seconds, including packaging, with zero CUDA objects compiled. Base images and local build caches were warm. These are local checks, not GitHub runner benchmarks. The eight-step generation baseline produced byte-identical score, tokens, latents and MP3; this is not an inference speed improvement claim.

References: Docker named contexts, cache ordering, and GitHub Actions cache mounts.