Vix v2.9.0
Vix.cpp v2.9.0 is a release about ownership.
As Vix has grown, several parts of the system had started doing work that belonged somewhere else. The build layer reconstructed information already owned by CMake and Ninja. Dependency management needed a clearer separation between what a project requests and what was actually resolved. SDK consumers could inherit machine-level requirements for dependencies that existed only because a Vix module used them internally. An asynchronous HTTP API still had places where the underlying work was not consistently asynchronous. Mobile support risked becoming useful only if Vix tried to replace the native toolchains that already understand Android and iOS best.
v2.9.0 addresses these problems by making the boundaries clearer.
Vix should own project intent, dependency intent, dependency resolution, toolchain configuration, diagnostics, and the developer experience. Build backends should own the concrete compilation graph and incremental execution. Internal dependencies should remain internal when Vix can reasonably package them. Platform capabilities should remain platform capabilities. An asynchronous API should stay asynchronous across the entire operation.
The result is a release that removes unnecessary work from the normal development loop, makes dependency resolution more reproducible, strengthens the networking stack, makes SDKs more self-contained, and adds practical Android and iOS project support without hiding the native platforms underneath.
Build: let each layer own the work it understands
A build tool should not duplicate the work of its backend.
Earlier Vix build paths had accumulated logic for scanning sources and headers, constructing build graphs, hashing state, and preparing information before invoking CMake and Ninja. Some of that machinery is useful for features that genuinely need a Vix-level view of the project, but it does not belong in every ordinary build.
CMake and Ninja already know the concrete compilation graph. Ninja already tracks whether a source file changed, which object depends on which header, and whether a target needs to be rebuilt. Reconstructing a second version of that information in the normal Vix path added work without adding corresponding value.
v2.9.0 changes that boundary.
The normal build flow now lets Vix focus on the things only Vix is well placed to understand:
- project intent
- selected modules and dependencies
- toolchain and configuration selection
- configuration identity
- backend selection
- command-line behavior
- diagnostics and presentation
CMake and Ninja then own the mechanics of compilation, dependency tracking, and incremental execution.
This removes the need for ordinary vix build to prepare a second source and header graph before invoking the backend. Build-graph work still exists where a feature actually needs it, but normal builds, incremental builds, and no-op builds no longer pay that cost by default.
The configuration model was also tightened. Presentation and execution options such as --verbose and --cmake-verbose no longer participate in build identity. Asking Vix to show more output should not create a different configuration of the program.
That distinction matters because a build configuration should describe what is being built, not how much output the user wants to see while it is being built.
The CLI also received clearer progress rendering and compiler diagnostics. Source frames, warnings, error presentation, and terminal hierarchy are easier to read, while users who need the backend directly can still request raw CMake, Ninja, and compiler output.
The principle behind these changes is simple:
Vix should add value to the build, not add work to the build.
Dependencies: Vix can manage dependencies for CMake projects
A project should not have to replace its build system just to get a better dependency workflow.
v2.9.0 significantly expands vix install around that idea.
Vix dependencies can now be added directly from Git repositories and used by ordinary CMake projects. The project can keep its existing CMakeLists.txt and native CMake targets while Vix manages dependency intent, resolution, locking, integrity, and local materialization.
For example:
vix install https://github.com/vixcpp/async \
--tag v1.2.1 \
--target vix::asyncThe dependency becomes part of the project manifest:
[dependencies.async]
git = "https://github.com/vixcpp/async"
tag = "v1.2.1"
target = "vix::async"Vix then resolves that request to an exact revision and records the reproducible result in vix.lock, including the resolved commit and package integrity metadata.
This creates an important separation:
vix.appdescribes what the project requestsvix.lockrecords the exact dependency graph that was resolved
A project can therefore express a human-readable dependency such as a Git tag while still reproducing the exact commit selected at resolution time.
Dependencies are materialized through Vix's local package store and exposed back to the project through generated CMake integration. Vix can also understand exported CMake targets, subdirectories, header-only packages, CMake options, and transitive dependency relationships.
The important part is that CMake remains CMake.
A project can continue to define its library normally:
add_library(vix_stress STATIC ...)
target_link_libraries(
vix_stress
PRIVATE
vix::requests
vix::async
)while Vix takes responsibility for obtaining and resolving the packages that provide those targets.
Vix Stress already uses this model. It remains a standalone CMake project while declaring Vix dependencies separately:
[dependencies.async]
git = "https://github.com/vixcpp/async"
tag = "v1.2.1"
target = "vix::async"
[dependencies.requests]
git = "https://github.com/vixcpp/requests"
tag = "v1.2.1"
target = "vix::requests"
[dependencies.tests]
git = "https://github.com/vixcpp/tests"
tag = "v0.1.1"
target = "vix::tests"The lockfile records exact commits and integrity metadata for those dependencies, making the installed graph reproducible rather than merely remembering a floating repository reference.
The goal is not to make CMake projects stop being CMake projects.
The goal is to give them a simpler dependency contract:
Describe what the project needs, lock what was resolved, and let Vix materialize it.
Requests: asynchronous from beginning to end
An asynchronous HTTP API is only truly asynchronous if the entire request path remains asynchronous.
vix::requests has been strengthened around that idea.
The asynchronous path now stays asynchronous through DNS resolution, connection establishment, TLS negotiation, request writes, and response reads. This is important not only for performance, but also for predictability. An API that appears asynchronous at its surface should not unexpectedly block because one internal stage still performs synchronous work.
Connection and request lifetimes were also hardened. Networking code often fails not because the protocol itself is wrong, but because an endpoint, socket, buffer, request, or callback outlives the object that was supposed to own it, or disappears before an asynchronous operation completes. v2.9.0 strengthens those ownership boundaries so asynchronous operations keep the state they need for as long as they need it.
Compatible HTTP and HTTPS connections can now be reused through an internal origin-scoped pool. A request to the same compatible origin does not need to establish a completely new connection every time when reuse is safe.
Responses also gained a generic incremental body sink. Instead of always materializing the complete decoded response body in memory first, consumers can receive decoded bytes as they arrive.
That changes what can be built efficiently on top of vix::requests.
For example, a large download can now be written directly to disk without first allocating enough memory to hold the entire response. The CLI updater uses this path so downloaded artifacts can be streamed instead of unnecessarily buffered.
The CLI has also progressively moved GitHub requests, metadata lookups, health checks, and downloads away from external curl, wget, and PowerShell commands toward vix::requests.
This is intentionally not a rule that every appearance of curl must disappear. RunCommand still keeps its HTTP URI passthrough through curl, where that behavior is part of the command's existing semantics.
The goal is not to remove a tool for ideological reasons. The goal is to stop depending on an external process where Vix already owns the HTTP operation itself.
SDKs: users should pay for the capabilities they use
A dependency that exists only because a Vix module needs it internally should not automatically become a system administration problem for every Vix user.
This release moves the SDK further in that direction.
SDK packaging is now driven by the selected profile rather than by modules that happen to be present in the CLI build. The contents of an SDK should describe the capabilities exported to its consumers, not the accidental shape of the machine that produced it.
SDK archives also no longer contain a second vix executable. The CLI and the SDK are related parts of Vix, but an SDK archive should not silently become another CLI distribution channel.
Profile metadata now follows the targets and headers that are actually exported to consumers.
Internal dependencies are becoming Vix-owned dependencies
Vix now bundles several dependencies that exist solely to implement Vix modules:
- nlohmann-json
- fmt
- spdlog
- SQLite
- zlib
- Brotli
Their relevant headers and libraries can travel with the SDK capabilities that require them.
This reduces machine-level prerequisites and, more importantly, makes the dependency contract more accurate. A consumer of vix::json should not need to understand how Vix internally obtains its JSON implementation. A consumer of compression support should not have to reconstruct the build environment used by the Vix repository.
The installed package contract was tightened so internal build targets and host-specific library paths do not leak into external consumers.
This work exposed an important distinction between two kinds of dependencies.
Some dependencies are implementation details that Vix can reasonably own and distribute. Others are genuine platform capabilities.
OpenSSL remains in the second category for profiles that actually export OpenSSL-dependent functionality. Core server TLS is opt-in, so a user who does not select that capability should not inherit its cost.
That is the broader rule behind the SDK work:
A capability that is not used should not impose a dependency.
A more accurate public dependency graph
The SDK contract was also cleaned up in several smaller but important places.
curl and wget are no longer generic SDK prerequisites.
Reply is now a public dependency of Note instead of Note carrying a second embedded implementation of the same functionality.
The all profile includes the public tests module together with its aggregate header, so the name of the profile better matches what the SDK actually exposes.
These changes are not individually large features, but together they make the installed SDK behave more like a product with a deliberate dependency model and less like a copy of the source-tree build environment.
Mobile: integrate with the platform instead of replacing it
Supporting Android and iOS does not require pretending that Gradle and Xcode do not exist.
vix::ui now includes Android and iOS project generation and build support through a shared mobile application model.
On Android, generated projects use the native Gradle and WebView stack.
On iOS, generated projects use Xcode and WKWebView.
The CLI exposes corresponding mobile initialization, build, and run commands, while the UI module remains responsible for project generation and the mobile application model.
The important part is the boundary.
Vix provides a common entry point and a consistent project-level experience, but the platform toolchains still do the work they are designed to do. Android builds are still Android builds. iOS builds are still Xcode builds.
The generated projects are validated against those native environments, including Gradle and emulator workflows for Android and Xcode simulator builds on macOS.
This is the direction Vix wants for platform support in general: provide a simpler and more coherent interface without erasing the platform underneath it.
An abstraction is useful when it removes accidental complexity. It becomes harmful when using it requires pretending the underlying system no longer exists.
Installed SDKs are now part of the tested contract
A source tree compiling successfully is not enough to prove that an SDK is usable.
The consumer never receives the Vix repository exactly as the Vix developers see it. The consumer receives installed headers, exported CMake targets, libraries, transitive requirements, and profile metadata.
v2.9.0 expands release validation around that reality.
SDK release checks now exercise installed artifacts and external consumer projects. They verify profile-specific capabilities, bundled dependency ownership, exported targets, public headers, and the behavior of projects that consume Vix from outside the source checkout.
This work caught several classes of issue that a monorepo build can hide: dependencies accidentally satisfied by the source tree, exported targets carrying the wrong usage requirements, platform-specific linkage contracts, and installed headers depending on information that was available only during the original build.
The packaged SDK is therefore becoming a first-class contract of Vix, not merely the by-product of a successful repository build.
Smaller boundaries, stronger guarantees
Many of the changes in v2.9.0 come from the same observation: complexity grows when two layers both believe they own the same responsibility.
If Vix and Ninja both maintain the incremental compilation graph, one of them is doing unnecessary work.
If Vix resolves a project dependency but the build system still has to rediscover where it came from and what exact revision was selected, ownership is unclear.
If Vix bundles an implementation dependency but the consumer must still install and configure that same dependency independently, ownership is unclear.
If an API is called asynchronous but part of its internal path blocks, its abstraction and its behavior disagree.
If a mobile abstraction can work only by replacing the native platform, it eventually has to reimplement an entire ecosystem.
v2.9.0 reduces those overlaps.
Vix still does a lot, but the goal is not for Vix to own every part of the machine. The goal is for Vix to own the parts where it can provide a simpler, more reliable programming model, and compose correctly with the parts that already have a clear owner.
What's next: Vix 3
v2.9.0 closes an important chapter for Vix 2.
Many of the changes in this release came from looking again at decisions that were reasonable when Vix was smaller, but became harder to justify as the project grew. The duplicated build work, dependency ownership, SDK boundaries, and the relationship between Vix abstractions and native platforms are all examples of lessons that will influence the next major version.
The next step is Vix 3.
Vix 3 is not intended to be a larger collection of modules or another layer of abstractions. It is an opportunity to revisit the foundations of Vix with the implementation experience gained from Vix 2.
The goal is to reduce the number of concepts users need to understand, make the remaining concepts more composable, and move complexity into places where Vix can own it reliably.
That means questioning assumptions that have accumulated over time: how projects describe what they need, how dependencies are owned and distributed, where the boundary between Vix and the C++ toolchain should live, and which parts of common C++ development can be made simpler without hiding the language or the machine.
Some of the work in v2.9.0 already points in that direction.
The build system now has clearer ownership boundaries. vix install separates dependency intent from reproducible resolution and can integrate those dependencies into existing CMake projects. SDK dependencies are becoming capabilities rather than machine setup instructions. Installed artifacts are treated as part of the public contract. Platform integrations preserve the native systems underneath them instead of trying to replace them.
Vix 3 will continue from those principles.
Its design is still being worked out, and early ideas should not become promises before they have been properly tested. The objective is not to add features for the sake of a major version number.
It is to take what Vix has taught us so far and use it to build a smaller, clearer, and more durable foundation for what comes next.
Summary
Vix v2.9.0 improves five major areas:
- the ordinary build path does less duplicated work and leaves incremental execution to CMake and Ninja
vix installcan manage reproducible Git dependencies for existing CMake projects throughvix.appandvix.lockvix::requestshas a stronger fully asynchronous path, connection reuse, and streaming response support- SDKs are more self-contained and more faithful to the capabilities users actually select
vix::uican generate and drive practical Android and iOS projects while preserving native toolchains
The release also strengthens the installed SDK as a tested public contract rather than treating installation as the last step after a source-tree build succeeds.
More broadly, v2.9.0 is less about making Vix own more of the system and more about making it own the right parts of the system.
That direction will continue into Vix 3.