Store
cnerium::store::Store is the storage facade used by Cnerium’s reliability layer.
Most application code does not use Store directly. A normal application configures Cnerium storage through AppConfig, attaches Cnerium to vix::App, then lets durable routes use the store internally.
The store exists because durable routes need persistent metadata. When a request completes, Cnerium must remember the request body hash and the response returned by the handler. If the same request is retried later, Cnerium can return the stored response instead of running the handler again.
The public application model remains:
vix::App app;
auto cnerium = cnerium::attach(app);Storage belongs behind that attached Cnerium layer.
Header
#include <cnerium/cnerium.hpp>or directly:
#include <cnerium/store/Store.hpp>Most applications should include:
#include <vix.hpp>
#include <cnerium/cnerium.hpp>Namespace
namespace cnerium::storeThe type is:
cnerium::store::StorePurpose
Store is responsible for Cnerium framework metadata.
It stores data needed by durable routes, including:
request hashes
stored responses
operation keys
runtime metadata used by the reliability layerIt is not the application database.
For example, when an order route succeeds, the application should store the order in its own domain storage. Cnerium stores the response metadata needed to replay the HTTP result if the client retries the same operation.
The distinction is important:
application database
owns orders, users, payments, invoices, domain records
Cnerium store
owns retry metadata and stored responses for durable routesDo not use Cnerium’s store as the source of truth for business data.
Storage backend
Cnerium’s store is backed by the Softadastra SDK.
Application code should not normally depend on the low-level storage backend. The application configures the Cnerium data directory and runtime identity through AppConfig, then Cnerium prepares the store when cnerium.start() is called.
Example:
cnerium::app::AppConfig config = cnerium::app::AppConfig::development();
config.set_name("orders-service");
config.set_data_dir("data/cnerium");
config.set_node_id("orders-node");
auto cnerium = cnerium::attach(app, std::move(config));The durable route API then uses the store internally:
cnerium.durable_post(
"/orders",
"orders.create",
handler);The handler does not need to manually store idempotency records.
Relationship with AppConfig
AppConfig is the normal way to configure Cnerium storage.
The most important storage-related setting is the data directory:
config.set_data_dir("data/cnerium");For local development, a relative path is acceptable:
config.set_data_dir("data/cnerium");For production, use a stable writable path:
config.set_data_dir("/var/lib/orders-service/cnerium");The process must be able to create and write to this directory.
If the directory is temporary or deleted during deployment, Cnerium may lose the stored responses needed for safe replay.
Relationship with DurableRoute
DurableRoute uses Store to read and write reliability metadata.
The route uses the store to answer questions like:
Has this Idempotency-Key already been used for this operation?
Was it used with the same request body?
Is there a stored response that can be replayed?
Should the request execute, replay, conflict, or fail as invalid?The application-level API hides this logic:
cnerium.durable_post(
"/orders",
"orders.create",
handler);Internally, the durable route uses the store before deciding whether the handler should run.
Relationship with Idempotency
Idempotency uses Store to persist and read the state required for durable retry behavior.
The simplified relationship is:
DurableRoute
calls Idempotency
Idempotency
checks operation, key, and request body hash
Store
persists request hashes and stored responsesApplication code usually stays above this level.
A normal handler should focus on the operation itself:
cnerium.durable_post(
"/orders",
"orders.create",
[](cnerium::DurableRequest &request)
{
const auto body = request.json();
return cnerium::created({
{"ok", true}
});
});Cnerium handles the store operations around the handler.
Stored data
For a request like this:
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-123" \
-d '{"product_id":"p1","quantity":2}'Cnerium needs to store metadata conceptually similar to:
operation: orders.create
key: order-123
hash: stable hash of the request body
response: stored HTTP responseThe response includes:
status code
body
content typeThis stored response is what allows Cnerium to return the same result on a safe retry.
Store keys
Cnerium builds internal keys from the durable operation name and idempotency key.
Conceptually, keys may look like:
cnerium:hash:<operation>:<key>
cnerium:response:<operation>:<key>For example:
cnerium:hash:orders.create:order-123
cnerium:response:orders.create:order-123Application code should not depend on the exact key format unless it is working on Cnerium internals. Key formats are internal implementation details and may evolve.
For normal application code, use durable routes and let Cnerium manage keys.
Request hash storage
The request hash lets Cnerium distinguish a safe retry from unsafe key reuse.
Safe retry:
stored:
operation: orders.create
key: order-123
body hash: abc
incoming:
operation: orders.create
key: order-123
body hash: abcThe stored hash matches. Cnerium can return the stored response.
Unsafe reuse:
stored:
operation: orders.create
key: order-123
body hash: abc
incoming:
operation: orders.create
key: order-123
body hash: defThe body hash does not match. Cnerium returns 409 Conflict.
The store is what makes this comparison possible across requests.
Stored response storage
The stored response lets Cnerium return the original result after a retry.
For example, a first request may return:
{
"ok": true,
"order_id": "ord_order-123",
"product_id": "p1",
"quantity": 2
}If the client retries the same request with the same key and same body, Cnerium returns that stored response.
The handler is not executed again.
This prevents duplicate side effects such as creating a second order, emitting the same event twice, sending duplicate notifications, or starting the same workflow twice.
Store and safe retries
A safe retry depends on the store.
The flow is:
client retries the same durable request
Cnerium reads the stored request hash
Cnerium compares it with the incoming request body hash
Cnerium loads the stored response
Vix writes the stored response to the client
handler does not runIf the store is unavailable or the stored response is missing, Cnerium cannot replay the response correctly.
That is why storage should be treated as part of the durable route correctness model, not as a simple cache.
Store and process restarts
A durable route is most useful when stored metadata survives process restarts.
If the process restarts after a successful request, a later retry should still be able to receive the stored response, assuming the storage backend preserved the data.
For that to work, the data directory must be stable.
Development path:
config.set_data_dir("data/cnerium");Production path:
config.set_data_dir("/var/lib/orders-service/cnerium");Avoid temporary paths for production durable metadata.
Store and domain transactions
Cnerium storage does not replace application transactions.
A durable handler may do domain work such as:
create order in database
reserve stock
create payment intent
send notification
return durable responseCnerium stores the durable response after the handler returns.
For high-value operations, think carefully about the relationship between the application commit and the stored response commit.
For example, if the domain write succeeds but the process crashes before the stored response is committed, a later retry may not have a response to replay even though the domain state exists.
Cnerium improves retry behavior, but the application still needs domain-level correctness mechanisms such as:
database transactions
unique constraints
audit logs
provider-level idempotency
clear status transitionsStore and external providers
When a durable handler calls an external provider, use the provider’s idempotency mechanism when available.
For example, a payment operation can keep the same logical key across layers:
client Idempotency-Key
-> Cnerium durable route
-> application payment service
-> payment provider idempotency keyCnerium’s store protects the backend route. Provider-level idempotency protects the external operation.
The two should work together.
Direct usage
Most application code should not use Store directly.
The preferred API is:
auto cnerium = cnerium::attach(app);
cnerium.durable_post(
"/orders",
"orders.create",
handler);Direct store usage is mainly useful for:
Cnerium internals
integration tests
custom adapters
advanced diagnosticsIf application code needs to inspect durable behavior, prefer testing the route through HTTP first. Direct store access can couple application code to Cnerium internals.
Startup
Cnerium storage is prepared when the attached layer starts:
if (!cnerium.start())
{
return 1;
}Then the Vix app is started:
app.run();The normal order is:
create vix::App
configure Cnerium
attach Cnerium
register routes
start Cnerium
run Vix appIf Cnerium cannot open or prepare storage, cnerium.start() should fail.
Inspecting storage during development
After sending durable requests, you can inspect the configured data directory:
find data/cnerium -maxdepth 3 -type f 2>/dev/nullThe exact file layout is internal and may change. Do not write application logic that depends on those files.
This check is only useful to confirm that Cnerium is writing metadata.
Clearing development storage
During local development, you can reset durable route state by deleting the data directory:
rm -rf data/cneriumOnly do this in development.
Deleting Cnerium storage removes request hashes and stored responses. After deletion, a retry with an old idempotency key may be treated as a new request because Cnerium no longer has the previous metadata.
Do not delete production storage unless you understand the consequences.
Production considerations
For production, use a stable storage path:
config.set_data_dir("/var/lib/orders-service/cnerium");Make sure the service user can write to it:
sudo mkdir -p /var/lib/orders-service/cnerium
sudo chown -R orders:orders /var/lib/orders-service/cneriumThe exact user and group depend on your deployment.
Avoid:
/tmp/cnerium
source-controlled directories
build directories deleted on redeploy
paths owned by another serviceA durable route depends on storage to preserve retry behavior.
Testing store behavior
A good store behavior test checks that replay works.
First request:
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-H "Idempotency-Key: store-test-1" \
-d '{"product_id":"p1","quantity":2}'Safe retry:
curl -i -X POST http://127.0.0.1:8080/orders \
-H "Content-Type: application/json" \
-H "Idempotency-Key: store-test-1" \
-d '{"product_id":"p1","quantity":2}'Expected behavior:
same response is returned
handler does not execute againThen restart the process and retry the same request again. If storage persists across restart, the same response should still be replayed.
Common mistakes
Do not treat Cnerium storage as a cache that can be freely deleted in production.
Do not store domain data only in Cnerium stored responses.
Do not rely on internal store key formats from application code.
Do not use temporary directories for production durable metadata.
Do not assume Cnerium storage replaces database constraints, transactions, or audit logs.
Do not bypass cnerium.durable_post and manually write partial retry metadata unless you are working on Cnerium internals.
Summary
cnerium::store::Store is the storage facade behind Cnerium durable route behavior.
It stores request hashes and stored responses so Cnerium can replay safe retries and reject unsafe key reuse. Application code usually configures storage through AppConfig and uses it indirectly through cnerium.durable_post.
Treat Cnerium storage as reliability metadata for selected Vix backend operations, not as your application database and not as a generic cache.