Vix.cpp v2.7.2

Vix.cpp v2.7.2 completes the dependency workflow introduced with Vix App Modules.

A module can now declare the registry packages it needs in its own vix.module file, while the application continues to resolve one effective dependency graph and maintain one root vix.lock.

The release also makes C++ cell execution in Vix Note safer. Programs that do not terminate or produce excessive output are now stopped before they can block the notebook interface.

Together, these changes strengthen two parts of Vix that execute user-defined work: application modules can depend on external packages without becoming separate projects, and Note cells can fail without taking the surrounding development environment with them.

Release focus

Vix App Modules were introduced in v2.7.1 to give larger applications a clearer internal structure.

A project can divide routes, services, controllers, storage code, and runtime behavior into modules while keeping one application entry point and one build. Each module has its own vix.module file, and vix.app decides which modules participate in the current application.

The first module release covered module discovery, enablement, build generation, and runtime participation. It did not yet provide a complete answer for packages used by only one module.

A package could be added to the root application, but that lost useful ownership information. An authentication module might be the only part of the project using rix/rix, yet the dependency appeared to belong to the entire application and was linked at the application level.

Vix.cpp v2.7.2 allows the module to declare that dependency directly.

The application still owns package resolution. Modules do not create independent lockfiles, dependency directories, or package environments. Instead, dependencies from enabled modules are merged into the application’s effective dependency set and resolved through the root project.

Module-level registry dependencies

A module can now declare registry packages in its vix.module manifest:

[deps]
registry = [
  "rix/rix@^0.9.1",
]

links = [
  "rix::rix",
]

The registry list describes the packages required by the module.

The links list describes the CMake targets from those packages that must be linked to the generated module target.

Keeping these two pieces of information in the module manifest makes the dependency relationship explicit. A developer reading modules/auth/vix.module can see both the external package required by the module and the target used by its C++ implementation.

The module does not receive its own vix.lock. Its package requirement is resolved as part of the root application dependency graph.

Adding a package to a module

The vix add command now accepts a module target:

vix add rix/rix --module auth

The short form is also supported:

vix add rix/rix -m auth

Assignment-style syntax can be used in scripts:

vix add rix/rix --module=auth

The command updates:

modules/auth/vix.module

rather than adding the package to the root application manifest.

After updating the module, Vix refreshes the root lockfile so the new dependency is immediately part of the reproducible project state.

The complete workflow is:

vix modules add auth
vix add rix/rix --module auth
vix install
vix build

There is no separate module installation command and no second dependency resolver inside the module.

Explicit link targets

A package can expose one or more CMake targets. When Vix cannot determine the intended target from the package name alone, it can be supplied explicitly:

vix add rix/rix \
  --module auth \
  --link rix::rix

For common package names, Vix can infer a default target:

rix/rix -> rix::rix

The inferred or explicit target is written to the module’s links list.

This separation matters because a registry package name and a CMake target are related but not identical concepts. The package identifies what Vix resolves and installs. The target identifies what CMake links into the module.

One application dependency graph

Dependencies declared by enabled modules are merged with the root project dependencies before resolution.

Suppose an application has:

root dependency:
  fmtlib/fmt

auth module:
  rix/rix

storage module:
  another/database-package

Vix builds one effective dependency set from the application and its enabled modules. That set is resolved together and written to the root:

vix.lock

This preserves a single version decision for each package across the project.

Two modules cannot silently resolve incompatible copies of the same registry dependency in isolated lockfiles. Version conflicts are handled at the application boundary, where the complete dependency graph is visible.

This also keeps source control straightforward. The repository contains one lockfile representing the exact package state required by the active application configuration.

Enabled and disabled modules

Only dependencies from enabled modules participate in the active dependency graph.

If a module exists in the project but is disabled in vix.app, its registry requirements do not force packages into the current installation or build.

This follows the same rule used for module compilation and runtime generation. A disabled module remains part of the source tree, but it is not part of the active application.

The behavior is useful for optional features and project variants. A project can keep an integration module in the repository without requiring every developer or deployment profile to install its external dependencies.

When the module is enabled again, its requirements return to the effective dependency set and are resolved through the root lockfile.

Root lockfile generation

vix add --module now refreshes the root vix.lock after modifying the module manifest.

Earlier implementations could write the package into vix.module without creating or updating the root lockfile. The module declaration was therefore valid, but the project had no resolved package state for vix install to consume.

Vix.cpp v2.7.2 closes that gap.

The command sequence now has a consistent meaning:

vix add --module   declares the dependency and refreshes resolution
vix install        materializes the resolved dependency graph
vix build          loads package targets and compiles the modules

The root lockfile remains the authoritative record of selected versions, commits, integrity information, and transitive dependencies.

Module CMake links

The generated application CMake now records link targets for each module.

For an auth module using rix::rix, Vix can generate:

set(VIX_MODULE_auth_LINKS
  rix::rix
)

The package target is linked to the generated target for auth, not automatically to the main application executable.

This preserves dependency ownership in the build graph.

A package used only by one module does not become a direct link dependency of every other module or of unrelated application code. Its include directories, compile definitions, and transitive link interface flow through the module that actually consumes it.

This is especially useful as applications grow. Module boundaries are more meaningful when dependencies follow those boundaries instead of accumulating on one global target.

Dependency loading order

Registry package targets must exist before module targets attempt to link them.

Vix now includes:

.vix/vix_deps.cmake

before enabled modules are added to the generated application build.

The order is therefore:

1. load resolved registry package targets
2. generate enabled module targets
3. link package targets to their modules
4. generate the application runtime and executable

Previously, packages could be installed correctly but still fail during vix build because the module’s target_link_libraries() call was evaluated before targets such as rix::rix had been defined.

The resulting CMake error often appeared as:

missing: rix::rix

The package itself was present. The problem was that it entered the generated build too late.

The updated generation order makes module dependency declarations usable through the normal build flow.

Generated dependency integration

The generated application CMake includes .vix/vix_deps.cmake when at least one active dependency requires it.

This condition now considers dependencies declared by enabled modules as well as dependencies declared at the root application level.

A project containing only module-level registry packages therefore receives the same dependency initialization as a project with root dependencies.

This removes the previous assumption that the root manifest had to contain a package before generated dependency CMake would be loaded.

Module dependency validation

Vix now validates the relationship between registry packages and module link targets.

A module that declares a registry package but provides no corresponding link information may be incomplete. The package would be installed, but the module might never consume its exported target.

The opposite case is also checked. A module should not declare an external link target without a package requirement capable of providing it.

These checks do not assume that package names and target names must have identical spelling. They verify that the manifest contains a coherent dependency description and report incomplete metadata before CMake produces a less useful unresolved-target error.

Validation also covers malformed dependency entries and unsafe combinations inside the [deps] section.

Project-level compatibility

The existing root dependency workflow remains unchanged:

vix add rix/rix

Without --module, the dependency is added to the root project as before.

The two forms now express different ownership:

vix add rix/rix

means that the package belongs to the application dependency set directly.

vix add rix/rix --module auth

means that the auth module owns the declaration and link relationship.

Both dependency sources are resolved into the same root lockfile.

Application and module responsibilities

The module dependency design keeps each project file responsible for one part of the workflow.

vix.app determines which modules belong to the active application:

[module.auth]
enabled = true
path = "modules/auth"
kind = "service"

vix.module describes the module itself and what it needs:

name = "auth"
kind = "service"

[routes]
prefix = "/api/auth"

[deps]
registry = [
  "rix/rix@^0.9.1",
]

links = [
  "rix::rix",
]

[tests]
enabled = true

The root vix.lock records the exact package resolution for the application and all enabled modules.

vix install materializes that locked graph.

vix build loads the resulting package targets before generating and compiling the active modules.

This model gives modules local ownership without turning them into independent package-management islands.

Vix Note execution guards

Vix Note executes C++ cells as native programs.

That is useful because a notebook can compile and run real C++ rather than evaluating a restricted approximation of the language. It also means the code can behave like any native program, including entering an infinite loop or writing output without stopping.

Before this release, a cell containing non-terminating code could leave the execution request waiting indefinitely. A program producing a very large stream of output could also consume memory and make the browser interface unresponsive.

Vix.cpp v2.7.2 adds timeout and output-size guards around C++ cell execution.

These safeguards do not attempt to prove whether a program is correct. They put practical limits around one notebook execution so a bad cell does not prevent the user from continuing to work.

Execution timeout

C++ cell execution now has a time limit.

When a program does not terminate before the configured deadline, Vix Note stops the child process and returns a controlled execution result.

This covers accidental infinite loops, blocked programs, and code waiting indefinitely for input or another condition that the notebook environment cannot satisfy.

The timeout applies to the execution process rather than compilation. Compiler diagnostics still return normally when the source does not build.

A timed-out program is reported as an execution failure, allowing the notebook to recover and accept another cell run.

Output capture limit

Vix Note now limits how much output can be captured from a C++ cell.

Consider this program:

#include <iostream>
#include <vector>

int main()
{
  std::vector<int> v;

  for (int i = 0; i < v.size() - 1; i++)
  {
    std::cout << i << '\n';
  }

  return 0;
}

v.size() returns an unsigned value. When the vector is empty, subtracting one underflows and produces a very large value.

The loop can therefore print far more output than the author expected.

Vix Note now stops the execution when captured output reaches the allowed limit. The oversized result is not sent in full to the browser.

This protects the server process, the HTTP response, and the notebook frontend from one unexpectedly noisy cell.

Recoverable cell failures

Timeout and output-limit failures are treated as failures of the current cell execution, not failures of the complete Note session.

The user can inspect the result, correct the source, and run the cell again.

This distinction is important in an interactive notebook. User code is expected to be incomplete or incorrect during development. The surrounding tool should contain those failures and remain usable.

The guards do not make arbitrary native code completely isolated. They address the two immediate cases that previously caused visible blocking: non-terminating execution and unbounded captured output.

Warning cleanup

Vix.cpp v2.7.2 also removes remaining compiler warnings from the affected code paths.

Vix Note runtime and web route code had sign-conversion warnings where signed and unsigned values crossed API boundaries.

The Note web server also contained socket-length conversions that could produce warnings on supported compilers and platforms.

These conversions are now handled explicitly.

Additional warnings across the Vix build were corrected so normal project compilation and CI logs remain focused on actionable problems.

A warning-free build is not only cosmetic. It makes new warnings visible when they are introduced and reduces the chance that unsafe conversions become accepted as ordinary build noise.

Fixed workflow gaps

This release fixes several failures that came from the incomplete connection between module manifests and the root dependency system.

vix add --module now updates the root lockfile instead of leaving the project without a resolved dependency state.

vix install now works when every registry dependency originates from modules and the root application contains no direct package declaration.

Generated builds load installed package targets before module CMake is evaluated.

Module link targets from vix.module are applied to the generated module that owns them.

Disabled modules no longer introduce inactive package requirements.

Together, these changes complete the path from declaration to compilation:

vix.module
    ↓
root dependency resolution
    ↓
vix.lock
    ↓
vix install
    ↓
.vix/vix_deps.cmake
    ↓
module target links
    ↓
vix build

Validation

Regression coverage was added for module dependency declaration, root lockfile generation, installation, CMake generation, and target linking.

The tests cover:

  • adding a package with --module;
  • short -m syntax;
  • assignment-style --module=<name>;
  • explicit --link;
  • default target inference;
  • updates to the correct vix.module;
  • root lockfile creation and refresh;
  • dependencies from several enabled modules;
  • exclusion of disabled-module dependencies;
  • generation of module-specific link variables;
  • loading .vix/vix_deps.cmake before module targets;
  • successful resolution of package targets such as rix::rix;
  • invalid package and link declarations;
  • project-level vix add compatibility.

Vix Note validation covers:

  • non-terminating C++ cells;
  • processes exceeding the execution timeout;
  • programs producing excessive output;
  • output truncation before browser delivery;
  • recovery of the notebook after a guarded execution;
  • normal C++ cells completing inside the configured limits.

The full project was also rebuilt after warning cleanup to verify that the updated code paths compile without the previous conversion warnings.

Compatibility

Existing projects with root registry dependencies continue to use the same vix add, vix install, and vix build workflow.

Existing modules without a [deps] section remain valid.

Module dependencies are additive. A project can keep shared dependencies at the root while moving feature-specific packages into the modules that use them.

The application continues to maintain one root vix.lock. This release does not introduce nested module lockfiles or separate dependency installations under each module directory.

Projects that disable a module may see its exclusive packages removed from the effective dependency graph the next time the root lockfile is refreshed. Re-enabling the module restores those requirements.

Vix Note continues to compile and run ordinary C++ cells normally. Timeout and output limits affect only executions that exceed the configured safety boundaries.

Known limitations

Module-level dependencies currently use the application’s registry dependency system. They do not make a module independently publishable or installable as a separate package.

The links list remains explicit metadata. Vix can infer common target names, but packages exposing several possible public targets may still require --link.

Dependencies are resolved globally, so incompatible version requirements from two enabled modules must be reconciled at the application level. Modules do not receive isolated package versions.

Vix Note execution guards limit runtime duration and captured output, but they are not a complete operating-system sandbox. Native code can still access resources permitted to the Vix Note process.

More advanced isolation, resource accounting, and interactive process control require additional runtime work beyond this release.

Release summary

Vix.cpp v2.7.2 gives App Modules ownership of their external package requirements without fragmenting the project dependency model.

A module can declare registry packages and CMake links in vix.module. Enabled module requirements are merged into the application graph, resolved through one root vix.lock, installed once, and linked only to the module that needs them.

The release also makes Vix Note more resilient when native C++ cells do not behave as expected. Execution timeouts and output limits keep an infinite loop or excessive output from blocking the notebook.

With the remaining compiler warnings removed, v2.7.2 provides a cleaner and more reliable foundation for the module and notebook workflows introduced in the v2.7 release line.