Vix Integration
This page explains how Cnerium integrates with Vix internally.
Cnerium is built for the Vix ecosystem. It does not replace vix::App, does not create a second HTTP runtime, and does not define a competing backend model. Its job is to attach to a Vix application and add durable execution semantics to selected write operations.
The public integration starts here:
vix::App app;
auto cnerium = cnerium::attach(app);That line defines the relationship.
Vix owns the backend. Cnerium attaches to it.
Integration goal
The goal of the Vix integration is to make durable behavior available inside a normal Vix backend without forcing developers to leave the Vix programming model.
A Vix developer should not feel that Cnerium is a separate framework with a different universe, different server, different router, and different lifecycle.
The intended experience is:
vix::App app;
auto cnerium = cnerium::attach(app);
app.get("/health", health_handler);
cnerium.durable_post(
"/orders",
"orders.create",
create_order);
if (!cnerium.start())
{
return 1;
}
app.run();The normal routes remain normal Vix routes. The critical write route is registered through Cnerium because it needs retry safety.
What Vix owns
Vix remains the owner of the backend application layer.
Cnerium relies on Vix for:
HTTP server
routing
middleware
request parsing
response writing
application lifecycle
runtime executor
WebSocket transport
build workflow
development workflow
production workflowCnerium should not duplicate these systems.
If a feature belongs to the backend application model, it belongs to Vix. If a feature belongs to durable retry behavior for selected routes, it belongs to Cnerium.
This boundary is the core of the integration.
What Cnerium adds
Cnerium adds reliability behavior around selected Vix routes.
It owns:
attach model
durable route registration helpers
durable request wrapper
durable response type
idempotency checks
request body hashing
replay protection
stored response replay
application-level realtime event emission
Cnerium storage integrationCnerium does not decide how the whole backend is structured. Vix already does that.
Cnerium only answers a narrower question:
Should this critical write operation execute now, replay a stored response, or be rejected?Attach model
Cnerium integrates through attachment.
vix::App app;
auto cnerium = cnerium::attach(app);The attached Cnerium object keeps a reference to the Vix app. It uses that reference to register durable routes into the existing Vix router.
The Vix app is not moved into Cnerium. It is not owned by Cnerium. It remains the application object controlled by the user.
This is important because the developer should still think:
I am building a Vix backend.
I am attaching Cnerium where reliability matters.not:
I am switching from Vix to another backend framework.Route registration
A normal Vix route is registered directly on vix::App:
app.get("/health", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.json({
{"ok", true}
});
});A durable Cnerium route is registered through the attached Cnerium layer:
cnerium.durable_post(
"/orders",
"orders.create",
[](cnerium::DurableRequest &request)
{
return cnerium::created({
{"ok", true}
});
});Internally, Cnerium still registers a route into the Vix application. The difference is that the Vix callback points to a Cnerium adapter and durable route executor.
Conceptually:
cnerium.durable_post(...)
creates DurableRoute
stores DurableRoute in AttachedApp
registers POST route on vix::App
Vix calls Cnerium adapter when the route matchesThe route is hosted by Vix. The durable decision belongs to Cnerium.
HTTP request flow
A normal Vix route follows this flow:
client
-> Vix HTTP server
-> Vix router
-> Vix handler
-> Vix response writerA Cnerium durable route follows this flow:
client
-> Vix HTTP server
-> Vix router
-> Cnerium Vix HTTP adapter
-> DurableRoute
-> DurableRequest
-> Idempotency check
-> ReplayProtection
-> Store
-> DurableHandler, only for a new safe request
-> DurableResponse
-> Vix response writerThe server does not change. The router does not change. The response is still written through Vix.
Cnerium inserts a reliability decision before the user handler runs.
VixHttp adapter
The VixHttp adapter is the bridge between Vix HTTP and Cnerium durable routes.
Its responsibility is narrow:
receive a Vix request
execute the matching Cnerium DurableRoute
convert the DurableResponse into a Vix response
preserve status code, body, and content typeIt should not implement:
an HTTP server
a router
middleware
request parsing
session handling
response ownershipVix already owns those systems.
A thin adapter keeps the integration understandable and prevents duplicated framework behavior.
DurableRequest and Vix request
cnerium::DurableRequest wraps the underlying Vix HTTP request.
It exposes request data needed by durable handlers:
method
target
path
body
headers
query parameters
route parameters
JSON body
Idempotency-Key
request body hash
native Vix requestExample:
cnerium.durable_post(
"/orders",
"orders.create",
[](cnerium::DurableRequest &request)
{
const auto body = request.json();
const std::string key = request.idempotency_key_value();
return cnerium::created({
{"ok", true},
{"idempotency_key", key}
});
});DurableRequest does not mean Cnerium owns HTTP. It means a durable handler needs a request view adapted to the durable execution model.
For normal Vix routes, use Vix request types.
For durable Cnerium routes, use DurableRequest.
DurableResponse and Vix response
A durable handler returns cnerium::DurableResponse.
return cnerium::created({
{"ok", true},
{"order_id", order_id}
});Cnerium needs a response object because the result must be storable.
A normal Vix response is written once. A durable response may need to be returned again later when the client safely retries the same request.
The integration flow is:
DurableHandler returns DurableResponse
Cnerium stores replayable response metadata
VixHttp writes the response through VixFor safe retries:
Cnerium loads StoredResponse
VixHttp writes the stored response through Vix
handler is not executed againThis is why durable handlers do not write directly to vix::Response.
Response writing
Vix remains responsible for writing the final HTTP response.
Cnerium produces either:
fresh DurableResponse from the handler
stored response from replay
bad request response for invalid key
conflict response for unsafe key reuseThe Vix adapter converts that result into the Vix response wrapper.
The adapter must preserve:
HTTP status code
response body
Content-TypeThis matters because stored response replay should behave like the original response.
Lifecycle integration
The lifecycle is intentionally explicit:
if (!cnerium.start())
{
return 1;
}
app.run();cnerium.start() starts resources owned by Cnerium:
store
Softadastra SDK-backed storage adapter
optional realtime adapter
Cnerium runtime stateapp.run() starts the Vix HTTP application.
Cnerium should not call app.run() internally because that would blur ownership. The application should remain visibly controlled by Vix.
Shutdown integration
Cnerium should stop only what it owns.
It may stop:
Cnerium runtime state
store resources
realtime adapter
Softadastra SDK-backed adapter resourcesIt should not destroy the Vix app.
The Vix application lifecycle belongs to the caller and to Vix.
A simple application should rely on ordinary C++ lifetime:
int main()
{
vix::App app;
auto cnerium = cnerium::attach(app);
if (!cnerium.start())
{
return 1;
}
app.run();
return 0;
}When main exits, objects are destroyed in reverse order.
Runtime executor
Vix owns the runtime executor used by its server and WebSocket infrastructure.
Cnerium may need access to Vix runtime facilities for adapters such as realtime event delivery. That access should remain integration-level, not ownership-level.
The rule is:
Cnerium may use Vix runtime resources through public Vix APIs.
Cnerium should not become the owner of the Vix runtime.This keeps Cnerium stable as a layer on top of Vix rather than a forked runtime model.
WebSocket integration
Vix owns the WebSocket transport.
Cnerium exposes application-level realtime events:
cnerium.emit(
"order.created",
cnerium::support::object({
{"order_id", cnerium::Json(order_id)}
}));The internal bridge is the VixWebSocket adapter.
Its responsibility is to:
start configured Vix WebSocket support
convert Cnerium events to Vix JSON payloads
broadcast events
broadcast events to rooms
stop realtime support when neededIt should not implement a second WebSocket stack.
The relationship is:
Cnerium says what happened.
Vix delivers it.Realtime event flow
When a durable handler emits an event, the flow is:
DurableHandler
-> cnerium.emit
-> Cnerium realtime layer
-> VixWebSocket adapter
-> Vix WebSocket runtime
-> connected clientsIf a request is replayed from storage, the handler does not run. Therefore, events emitted inside the handler are not emitted again.
This gives durable operations a clean event behavior:
new request
handler runs
event may be emitted
response is stored
safe retry
stored response is returned
handler does not run
event is not emitted again by the handlerVix JSON integration
Cnerium uses Vix JSON types through its support layer.
using Json = vix::json::Json;This avoids creating a separate JSON model inside Cnerium.
The benefit is practical:
Vix HTTP responses use Vix-compatible JSON.
Cnerium durable responses use the same JSON foundation.
Cnerium realtime event payloads can be converted to Vix WebSocket payloads.Cnerium should reuse Vix primitives where they are already the ecosystem standard.
Build integration
Cnerium should build as a normal C++ library that links against Vix and the Softadastra SDK.
The dependency direction should be clear:
Cnerium
-> Vix public targets
-> Softadastra SDK public targetsCnerium should not require developers to manually patch Vix internals.
A consumer should be able to use Cnerium in a Vix project with the normal Vix build workflow.
For example:
vix build
vix runor inside the Cnerium repository:
vix build --build-target all -v -- -DCNERIUM_BUILD_EXAMPLES=ONThe build integration should support the public mental model: Cnerium is an ecosystem library attached to Vix, not a separate platform.
Include integration
The intended public include pattern is:
#include <vix.hpp>
#include <cnerium/cnerium.hpp>vix.hpp gives the backend application API.
cnerium/cnerium.hpp gives the reliability API.
Cnerium headers may include specific Vix headers internally where needed:
#include <vix/http/Request.hpp>
#include <vix/http/ResponseWrapper.hpp>
#include <vix/websocket/server.hpp>
#include <vix/json/json.hpp>Those includes should reflect real adapter needs.
Cnerium should not expose unnecessary Vix internals in its public surface.
Dependency boundaries
Cnerium should depend only on public Vix APIs.
Avoid depending on:
private Vix implementation details
unstable internal headers
manual modifications to Vix source code
duplicated Vix runtime logicThe integration should remain stable across Vix releases because Cnerium is part of the ecosystem, not a patch against private internals.
If Cnerium needs a capability from Vix, the better direction is usually to expose a clean public Vix API rather than reaching into internals.
Error handling integration
Cnerium should return durable HTTP responses that Vix can write cleanly.
For example:
missing Idempotency-Key
-> 400 Bad Request
same key with different body
-> 409 Conflict
valid new request
-> handler response
safe retry
-> stored responseVix should remain responsible for the actual response writing. Cnerium should produce clear response data for the adapter.
Error messages should be useful, stable, and direct.
Normal routes and durable routes together
A real Vix backend can mix normal and durable routes:
app.get("/health", health_handler);
app.get("/orders/{id}", get_order);
app.get("/products", list_products);
cnerium.durable_post(
"/orders",
"orders.create",
create_order);
cnerium.durable_post(
"/payments",
"payments.create",
create_payment);This is the intended integration style.
Most routes remain Vix routes. Only critical write operations become Cnerium durable routes.
Why not cnerium::App
A Cnerium-owned application object is easy to misunderstand.
This shape:
cnerium::App app;suggests that Cnerium is a separate backend framework. It makes a Vix developer ask why they should use Cnerium instead of Vix.
The better public model is:
vix::App app;
auto cnerium = cnerium::attach(app);This shows that Cnerium is not competing with Vix.
It attaches to Vix and strengthens selected operations.
Migration from older shape
If older examples use a Cnerium application wrapper, they should be moved toward the attach model.
Old shape:
cnerium::App app;
app.durable_post(...);
return app.run();Preferred shape:
vix::App app;
auto cnerium = cnerium::attach(app);
cnerium.durable_post(...);
if (!cnerium.start())
{
return 1;
}
app.run();
return 0;This migration improves the mental model.
It makes Vix ownership explicit and keeps Cnerium focused on reliability.
Testing integration
A Vix integration test should prove that Cnerium routes are registered into the Vix app and that durable behavior works through real HTTP requests.
Minimum test cases:
normal Vix route responds normally
durable route accepts a new request with Idempotency-Key
safe retry returns the stored response
same key with different body returns 409 Conflict
missing Idempotency-Key returns 400 Bad Request
handler is not executed twice for safe retriesThese tests prove that the adapter, route registration, storage, and response writing are working together.
Integration mistakes to avoid
Do not create a second HTTP server inside Cnerium.
Do not duplicate the Vix router.
Do not hide vix::App behind a Cnerium-owned application model.
Do not write directly to Vix responses from durable handlers.
Do not make Cnerium WebSocket behavior independent from Vix WebSocket.
Do not depend on private Vix internals.
Do not make all routes durable by default.
Do not let the attached Cnerium object die while the Vix app is still running.
Contributor rules
When changing the Vix integration, keep these rules:
Vix owns the backend application.
Cnerium owns durable route behavior.
Adapters must remain thin.
Public APIs should make ownership obvious.
Normal Vix routes must stay normal.
Durable routes must remain selective.
Cnerium should use public Vix APIs.If a change makes Cnerium feel like a separate backend framework, it is probably moving in the wrong direction.
Summary
Cnerium integrates with Vix through attachment.
The Vix app remains the backend owner. Cnerium registers durable routes into that app, wraps matching Vix requests as durable requests, applies idempotency and replay protection, returns durable responses through the Vix response writer, and emits application-level events through the Vix WebSocket runtime.
The integration is successful when a Vix developer can keep building a normal Vix backend and add Cnerium only where critical write operations need stronger retry guarantees.