Module Manifest
Each application module contains a vix.module file. This file describes the module itself: its name, its kind, its exported include directory, its route prefix when the module is routed, module-owned external dependencies, and whether module tests are enabled.
The module manifest belongs to the module directory. It is different from the root vix.app file. The root manifest describes the application and decides which modules are active. The module manifest describes one module from inside its own folder, including registry and Git dependencies that belong to that module rather than to the application shell as a whole.
modules/auth/
CMakeLists.txt
vix.module
include/auth/
src/
tests/This separation keeps the project readable. vix.app answers the application-level question: which modules belong to this application? vix.module answers the module-level question: what kind of module is this, and what does it expose?
Basic shape
A simple module manifest looks like this:
name = "auth"
kind = "module"
[exports]
include = "include"
[tests]
enabled = trueThis describes a module named auth. It is a normal internal module, it exports headers from its include directory, and it has tests enabled.
A matching module layout may look like this:
modules/auth/
include/auth/
api.hpp
src/
auth.cpp
tests/
test_auth.cpp
CMakeLists.txt
vix.moduleThe public include path comes from the include directory.
#include <auth/api.hpp>The source files remain private to the module target.
Routed module shape
Backend and service modules are routed modules. They expose a module entry point and usually register routes on a vix::App.
A backend module manifest looks like this:
name = "auth"
kind = "backend"
[routes]
prefix = "/api/auth"
[deps]
registry = [
]
links = [
]
[tests]
enabled = trueThe routes.prefix value gives the module a clear HTTP namespace. For a module named auth, the default route prefix is usually:
/api/authA generated backend module uses that prefix in its starter controller.
app.get("/api/auth", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
"ok", true,
"module", "auth",
"message", "Auth module is available"
});
});The prefix does not replace route registration. Routes are still registered by module code. The prefix gives the module a declared route ownership area and lets the module checker detect conflicts between modules.
WebSocket module shape
A generated WebSocket module stores its workflow in vix.module.
name = "live_chat"
kind = "websocket.attached"
runtime = true
[websocket]
workflow = "attached"
[tests]
enabled = trueThe kind value identifies the module as a WebSocket module and records the selected workflow. The supported workflow suffixes are:
websocket.attached
websocket.standalone
websocket.bridge
websocket.clientattached, standalone, and bridge are runtime workflows. They generate a module entry point that can take ownership of the application runtime. client is support code only and does not generate a runtime run(...) entry point.
The generated layout follows the module name.
modules/live_chat/
include/live_chat/LiveChatModule.hpp
src/LiveChatModule.cpp
tests/test_live_chat.cpp
CMakeLists.txt
vix.moduleCreate one with the CLI.
vix modules add live_chat --websocket --workflow attached
vix modules add --websocket --name notifications --workflow bridgename
The name field is the stable module name.
name = "auth"The name should match the module directory and the declaration in vix.app.
modules/auth/[module.auth]
enabled = true
path = "modules/auth"
kind = "backend"
depends = []Keep this name stable. It is used in generated files, route defaults, test names, CMake target names, and module checks.
kind
The kind field describes the role of the module.
kind = "backend"A simple internal module usually uses:
kind = "module"A backend feature module uses:
kind = "backend"A routed module in a non-backend application may use:
kind = "service"A generated WebSocket module uses:
kind = "websocket.attached"The exact suffix matches the WebSocket workflow. Use websocket.client only for non-runtime client/helper modules.
The value should describe what the module does in the application. A module that owns HTTP backend routes should use backend. A module that only exposes reusable internal C++ code can use module.
[exports]
The [exports] section describes what the module exposes.
[exports]
include = "include"For simple modules, this tells the project that public headers are under the module’s include directory.
modules/auth/include/The public headers should still live under a module-named folder.
modules/auth/include/auth/api.hppThat gives users of the module a stable include path.
#include <auth/api.hpp>The export section should describe public surface, not private implementation. Files under src/ should not be exported as public API.
[routes]
The [routes] section is used by routed modules.
[routes]
prefix = "/api/auth"The prefix should be unique across routed modules in the same application. If two modules claim the same prefix, the project becomes harder to reason about because route ownership is no longer clear.
Good route prefixes usually follow the module name.
auth -> /api/auth
projects -> /api/projects
builds -> /api/builds
packages -> /api/packagesThe prefix is also checked by vix modules check.
vix modules checkThis helps catch duplicate route prefixes before the application is built or run.
[deps]
The [deps] section records registry packages that belong to this module. Use it when a package is part of the module implementation rather than part of the application shell. For example, an auth module may use a JWT package while the rest of the backend only depends on the public auth API.
[deps]
registry = [
"gk/jwt@^1.0.0",
]
links = [
"gk::jwt",
]The registry list contains Vix Registry package specs. The links list contains the CMake targets that should be linked to the generated module target. These lists are intentionally kept together: one tells Vix what package must be resolved, and the other tells the build what target the module uses.
The easiest way to update this section is through vix add --module.
vix add gk/jwt@^1.0.0 --module authWhen the module is enabled in vix.app, Vix includes its registry dependencies in the application dependency resolution and writes the exact resolved graph to the root vix.lock. During the generated vix.app build, Vix also passes the declared link targets to the module CMake target. A disabled module can keep its dependency metadata on disk, but those dependencies are not part of the active application graph until the module is enabled.
[dependencies.<name>]
Structured Git dependencies that belong to one module are declared under [dependencies.<name>].
[dependencies.spdlog]
git = "https://github.com/gabime/spdlog"
tag = "v1.15.3"
target = "spdlog::spdlog"This keeps the dependency close to the module that uses it while preserving a single dependency state for the application.
The easiest way to add one is:
vix install https://github.com/gabime/spdlog \
--tag v1.15.3 \
--target spdlog::spdlog \
--module authThe short form is:
vix install https://github.com/gabime/spdlog \
--tag v1.15.3 \
--target spdlog::spdlog \
-m authVix writes the Git declaration to modules/auth/vix.module. The exact resolved commit still belongs to the root vix.lock, and the dependency uses the same shared Git cache as root application dependencies.
modules/auth/vix.module
|
+-- desired Git dependency
|
v
root vix.lock
|
+-- exact resolved dependency stateA module does not receive its own lockfile or cache directory.
Revision selection
A module Git dependency can select a tag, branch, or exact revision.
Tag:
[dependencies.spdlog]
git = "https://github.com/gabime/spdlog"
tag = "v1.15.3"
target = "spdlog::spdlog"Branch:
[dependencies.parser]
git = "https://github.com/company/parser.git"
branch = "dev"
target = "company::parser"Revision:
[dependencies.parser]
git = "https://github.com/company/parser.git"
rev = "a1b2c3d4e5f6"
target = "company::parser"Use only one revision selector for the same dependency. Tags and branches are resolved to exact commits before the dependency state is written to vix.lock.
Monorepo dependencies
If the dependency's CMake project is inside a repository subdirectory, use subdirectory.
[dependencies.parser]
git = "https://github.com/company/monorepo.git"
tag = "v2.0.0"
subdirectory = "libs/parser"
target = "company::parser"The target field identifies the CMake target used by the owning module.
Dependency CMake options
CMake options for one Git dependency are stored in a nested .cmake table.
[dependencies.spdlog]
git = "https://github.com/gabime/spdlog"
tag = "v1.15.3"
target = "spdlog::spdlog"
[dependencies.spdlog.cmake]
SPDLOG_BUILD_TESTS = false
SPDLOG_BUILD_EXAMPLE = false
SPDLOG_BUILD_BENCH = falseVix applies these options before the dependency is added to the generated CMake graph.
Ownership and conflict behavior
A dependency declared in one module belongs to that module's generated CMake target. It is not automatically attached to sibling modules or to the application target.
For example:
auth
+-- spdlog::spdlog
billing
+-- no spdlog dependencyIf two enabled modules declare the same compatible dependency, Vix can reuse one exact resolved dependency state while preserving both module owners.
If active owners require incompatible revisions or incompatible CMake configuration for the same effective Git dependency, Vix rejects the conflict instead of choosing whichever declaration was processed last.
The same conflict rules apply between a root application dependency in vix.app and a module dependency in vix.module.
Disabled modules keep their declarations on disk, but their external dependencies do not constrain the active application dependency graph until those modules are enabled.
[tests]
The [tests] section records whether the module has tests enabled.
[tests]
enabled = trueGenerated modules include a starter test file.
modules/auth/tests/test_auth.cppFor a simple module, the generated test usually checks the public API. For a backend module, it usually checks that the module exposes its name and that the module header is correctly wired.
The test section does not mean test files are part of the main application target. Module tests remain separate from the application executable.
What does not belong in vix.module
The module manifest should stay focused on module metadata. It should not become a second application manifest, even though it may declare registry and Git dependencies used by the module itself.
Application-level activation belongs in vix.app.
[module.auth]
enabled = true
path = "modules/auth"
kind = "backend"
depends = []Internal module-to-module dependencies still belong in the application manifest and in the module CMake target relationship.
[module.projects]
enabled = true
path = "modules/projects"
kind = "backend"
depends = [
"auth",
]target_link_libraries(api_projects
PUBLIC
api::auth
)The vix.module file should describe the module itself. It should not duplicate the full application graph. External dependencies are different: registry and Git dependencies can live in vix.module when they belong to the module implementation, because that keeps dependency ownership close to the code that uses it.
Example: simple module
A simple module can use this manifest:
name = "logger"
kind = "module"
[exports]
include = "include"
[tests]
enabled = trueA matching layout may look like this:
modules/logger/
include/logger/
api.hpp
src/
logger.cpp
tests/
test_logger.cpp
CMakeLists.txt
vix.moduleThis is a good shape for internal reusable code that does not register HTTP routes.
Example: backend module
A backend module can use this manifest:
name = "projects"
kind = "backend"
[routes]
prefix = "/api/projects"
[tests]
enabled = trueA matching layout may look like this:
modules/projects/
include/projects/
ProjectsModule.hpp
controllers/
ProjectsController.hpp
src/
ProjectsModule.cpp
controllers/
ProjectsController.cpp
migrations/
tests/
test_projects.cpp
CMakeLists.txt
vix.moduleThe module entry point registers the module routes.
void ProjectsModule::register_routes(vix::App &app)
{
controllers::ProjectsController::register_routes(app);
}The generated application bridge can call that entry point when the module is enabled in vix.app.
Generated manifests
When you create a module with the CLI, Vix writes the initial vix.module file for you.
vix modules add authFor a normal module, the generated manifest contains the export section.
name = "auth"
kind = "module"
[exports]
include = "include"
[deps]
registry = [
]
links = [
]
[tests]
enabled = trueFor a routed backend module, the generated manifest contains the route prefix and the same empty dependency section.
name = "auth"
kind = "backend"
[routes]
prefix = "/api/auth"
[deps]
registry = [
]
links = [
]
[tests]
enabled = trueYou can edit the generated manifest when the module needs a different route prefix, when the module kind changes, or when the module needs a structured Git dependency. For package installation, prefer the CLI commands because they also update the dependency state safely.
Registry dependency:
vix add gk/jwt@^1.0.0 --module authGit dependency:
vix install https://github.com/gabime/spdlog \
--tag v1.15.3 \
--target spdlog::spdlog \
--module authAfter editing module metadata manually, run the module checks.
vix modules checkCommon mistakes
The most common mistake is confusing vix.module with vix.app. The module manifest describes one module. It does not decide whether the module is enabled in the application. Enabled and disabled state belongs to the root vix.app file.
Another mistake is declaring a route prefix that another module already owns. This can happen when modules are copied and renamed without updating vix.module. Run vix modules check after creating or moving routed modules.
A third mistake is using [exports] to expose private implementation folders. Public headers should live under include/<module>/. Implementation files under src/ should remain private to the module target.
Another mistake is creating a lockfile or dependency cache inside a module directory. Module ownership is logical and build-level ownership. The application still uses the root vix.lock and the shared Vix dependency cache.
When multiple active modules use the same Git source, keep their revision and CMake requirements compatible. Vix rejects incompatible active requirements instead of silently selecting one.
Recommended rule
Keep vix.module small. It should tell the reader what the module is, what it exposes, which route prefix it owns when it is routed, which external dependencies belong to the module, and whether it has tests. The application graph and module activation belong in vix.app; module metadata and module-owned dependency declarations belong in vix.module. Exact resolved dependency state belongs in the root vix.lock.
Next step
Continue with dependency checks to see how module dependencies, the active module graph, and validation work together.