| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Stateful, transport-independent realtime rooms for Vix.cpp.
vix::realtime provides the runtime foundations required to build collaborative applications, multiplayer services, live dashboards, chat systems, shared workspaces, presence systems, and other stateful realtime services in modern C++.
The module separates authoritative room logic from networking:
Current module version:
0.1.0
The API is under active development. Public headers outside internal/ are intended to form the stable module surface.
Include the complete public API with:
#include <vix/realtime.hpp>Advanced users may include individual module headers:
#include <vix/realtime/room.hpp>
#include <vix/realtime/room_manager.hpp>
#include <vix/realtime/session_resume.hpp>Headers under vix/realtime/internal/ are implementation details and are not part of the stable public API.
A Realtime room follows an event-driven execution model:
RoomCommand
|
v
RoomHandler
|
v
CommandResult
|
v
RoomEvent persistence
|
v
RoomState::apply()
|
v
Event dispatch
The authoritative state changes only through persisted room events.
This provides:
The module uses strongly typed identifiers:
vix::realtime::RoomId roomId{"workspace/main"};
vix::realtime::SessionId sessionId{"session-42"};
vix::realtime::NodeId nodeId{"node-1"};
vix::realtime::RoomVersion version{0};
vix::realtime::EventId eventId{0};RoomVersion identifies the logical version of room state.
EventId identifies the position of an event in one room event stream.
A RoomCommand represents one client or server intention:
vix::realtime::JsonObject payload;
payload.set_string("message", "Hello");
vix::realtime::RoomCommand command{
vix::realtime::RoomId{"chat/general"},
vix::realtime::SessionId{"session-42"},
"message.send",
std::move(payload)};
command.set_request_id("request-1");
command.set_correlation_id("conversation-9");Commands may include an expected room version:
command.set_expected_version(
vix::realtime::RoomVersion{12});The expected version can be used for optimistic concurrency validation.
A RoomEvent describes an authoritative state transition:
vix::realtime::JsonObject payload;
payload.set_string("message", "Hello");
vix::realtime::RoomEvent event{
vix::realtime::RoomId{"chat/general"},
"message.sent",
std::move(payload),
vix::realtime::EventAudience::Room};Supported audiences are:
| Audience | Delivery |
|---|---|
| Room | Every session in the room |
| Sender | Only the source session |
| Others | Every room session except the source |
| Session | One explicit target session |
| Internal | No transport delivery |
Session-targeted events require a target session:
event.set_target_session(
vix::realtime::SessionId{"session-84"});
event.set_audience(
vix::realtime::EventAudience::Session);Room handlers return a CommandResult:
return vix::realtime::CommandResult::accepted(
{std::move(event)});A command may be:
Example rejection:
return vix::realtime::CommandResult::rejected(
vix::realtime::ErrorCode::Unauthorized,
"session cannot modify this room");Application state derives from RoomState.
#include <memory>
#include <string>
#include <vix/realtime.hpp>
class ChatState final
: public vix::realtime::RoomState
{
public:
[[nodiscard]] vix::realtime::SchemaVersion
schema_version() const noexcept override
{
return 1;
}
void apply(
const vix::realtime::RoomEvent &event) override
{
if (event.type() == "message.sent")
{
++messageCount_;
}
}
[[nodiscard]] vix::realtime::JsonObject
serialize() const override
{
vix::realtime::JsonObject state;
state.set_i64(
"message_count",
messageCount_);
return state;
}
void restore(
const vix::realtime::JsonObject &state,
vix::realtime::SchemaVersion schemaVersion) override
{
if (schemaVersion != 1)
{
throw vix::realtime::Error{
vix::realtime::ErrorCode::CorruptedState,
"unsupported chat state schema"};
}
/*
* Read the application fields using the Vix JSON accessors used by the
* surrounding application.
*/
}
[[nodiscard]] std::unique_ptr<vix::realtime::RoomState>
clone() const override
{
return std::make_unique<ChatState>(*this);
}
private:
std::int64_t messageCount_{0};
};clone() is used to isolate command execution and replay work from the current authoritative state.
A handler validates commands and emits events.
class ChatHandler final
: public vix::realtime::RoomHandler
{
public:
[[nodiscard]] vix::realtime::CommandResult
handle_command(
const vix::realtime::RoomCommand &command,
const vix::realtime::RoomState &state,
const vix::realtime::RoomContext &context) override
{
static_cast<void>(state);
static_cast<void>(context);
if (command.type() != "message.send")
{
return vix::realtime::CommandResult::ignored();
}
vix::realtime::RoomEvent event{
command.room_id(),
"message.sent",
command.payload(),
vix::realtime::EventAudience::Room};
event.set_source_session(
command.session_id());
event.set_request_id(
command.request_id());
event.set_correlation_id(
command.correlation_id());
return vix::realtime::CommandResult::accepted(
{std::move(event)});
}
};Lifecycle hooks may also emit events:
vix::realtime::CommandResult on_open(
const vix::realtime::RoomState &state,
const vix::realtime::RoomContext &context) override;
vix::realtime::CommandResult on_join(
const vix::realtime::SessionId &sessionId,
const vix::realtime::RoomState &state,
const vix::realtime::RoomContext &context) override;
vix::realtime::CommandResult on_leave(
const vix::realtime::SessionId &sessionId,
const vix::realtime::RoomState &state,
const vix::realtime::RoomContext &context) override;
vix::realtime::CommandResult on_close(
const vix::realtime::RoomState &state,
const vix::realtime::RoomContext &context) override;A RoomFactory creates a state and handler for one room type.
class ChatFactory final
: public vix::realtime::RoomFactory
{
public:
[[nodiscard]] std::string_view
room_type() const noexcept override
{
return "chat";
}
[[nodiscard]] vix::realtime::RoomStatePtr
create_state(
const vix::realtime::RoomId &roomId) const override
{
static_cast<void>(roomId);
return std::make_unique<ChatState>();
}
[[nodiscard]] vix::realtime::RoomHandlerPtr
create_handler(
const vix::realtime::RoomId &roomId) const override
{
static_cast<void>(roomId);
return std::make_unique<ChatHandler>();
}
};Server is the transport-independent runtime facade.
#include <memory>
#include <vix/realtime.hpp>
int main()
{
vix::realtime::Config config;
config.maxActiveRooms = 1000;
config.maxSessions = 10000;
config.maxSessionsPerRoom = 256;
config.enableSessionResume = true;
config.enablePresence = true;
auto server =
std::make_shared<vix::realtime::Server>(
vix::realtime::NodeId{"node-1"},
config);
server->register_factory(
std::make_shared<ChatFactory>());
server->start();
auto room =
server->open_room(
vix::realtime::RoomId{"chat/general"},
"chat");
return 0;
}Server does not open sockets. A transport adapter forwards connections and protocol envelopes to the runtime.
A session is a logical client identity that may survive a temporary transport disconnection.
auto session =
server->create_session(
vix::realtime::SessionId{"session-42"},
"user-17");A session may:
auto result =
server->join_room(
vix::realtime::SessionId{"session-42"},
vix::realtime::RoomId{"chat/general"});
if (result.is_rejected())
{
// Handle the lifecycle rejection.
}server->leave_room(
vix::realtime::SessionId{"session-42"},
vix::realtime::RoomId{"chat/general"});Commands may be executed immediately:
vix::realtime::RoomCommand command{
vix::realtime::RoomId{"chat/general"},
vix::realtime::SessionId{"session-42"},
"message.send",
payload};
auto result =
server->execute(command);They may also be inserted into a room queue:
const auto queueStatus =
server->enqueue(
std::move(command));
if (queueStatus ==
vix::realtime::internal::CommandQueueStatus::Success)
{
server->process_next(
vix::realtime::RoomId{"chat/general"});
}Queued execution preserves command ordering inside one room.
EventStore is the authoritative room event persistence contract.
Provided implementations:
auto eventStore =
std::make_shared<
vix::realtime::MemoryEventStore>();The in-memory store is useful for:
It does not survive process restarts.
vix::realtime::PostgresEventStoreOptions options;
options.connectionString =
"host=127.0.0.1 port=5432 dbname=vix user=vix password=secret";
options.schema = "public";
options.table = "vix_realtime_events";
options.createTableIfMissing = true;
auto eventStore =
std::make_shared<
vix::realtime::PostgresEventStore>(
std::move(options));The PostgreSQL event store:
PostgreSQL support must be enabled when building the module.
Snapshots reduce the number of events required to restore a room.
Provided implementations:
vix::realtime::Config config;
config.snapshotEveryEvents = 100;
config.snapshotsToKeep = 3;
config.snapshotOnRoomClose = true;
config.restoreRoomsOnOpen = true;vix::realtime::PostgresSnapshotStoreOptions options;
options.connectionString =
"host=127.0.0.1 port=5432 dbname=vix user=vix password=secret";
options.schema = "public";
options.table = "vix_realtime_snapshots";
options.createTableIfMissing = true;
auto snapshotStore =
std::make_shared<
vix::realtime::PostgresSnapshotStore>(
std::move(options));Snapshots are uniquely identified by:
room_id + room_version
Replacing an existing snapshot version requires the same last_event_id.
ReplayEngine reconstructs room state from a snapshot and subsequent events.
auto replayEngine =
vix::realtime::internal::ReplayEngine::from_config(
config,
eventStore,
snapshotStore);
auto replayResult =
replayEngine.restore(
roomId,
roomState);Replay validates:
Default limits are configured through:
config.maxReplayEvents = 1000;
config.maxReplayBytes = 4U * 1024U * 1024U;
config.replayTimeout = std::chrono::milliseconds{5000};Presence represents ephemeral logical membership in rooms.
auto presenceStore =
std::make_shared<
vix::realtime::LocalPresenceStore>();A presence record contains:
Presence may be:
Presence is not authoritative room state and should not replace persisted events.
DistributedPresence defines the contract for a presence store shared by multiple runtime nodes.
A distributed implementation must provide:
The module does not impose a specific distributed backend.
Possible implementations include:
RoomDirectory tracks the runtime node responsible for one room.
auto directory =
std::make_shared<
vix::realtime::RoomDirectory>();Room ownership supports:
A generation prevents an older owner from reclaiming authority after a newer ownership claim has been created.
RoomDirectory is process-local. Distributed deployments should provide shared routing or coordination around this contract.
SessionResume issues and validates opaque session credentials.
auto resume =
std::make_shared<
vix::realtime::SessionResume>(
server->manager());
const auto token =
resume->issue(
vix::realtime::SessionId{"session-42"});After a transport disconnection:
auto result =
resume->resume(
vix::realtime::SessionId{"session-42"},
token,
replacementConnection);Successful resumption may rotate the token:
const auto nextToken =
result.resumeToken;Resume validation requires:
Default resume configuration:
config.enableSessionResume = true;
config.sessionResumeWindow =
std::chrono::seconds{120};Tokens use URL-safe, unpadded Base64 encoding with configurable entropy.
The Realtime protocol uses structured envelopes.
Envelope kinds are:
Serialization:
const std::string text =
vix::realtime::protocol::serialize(
envelope);Parsing:
const auto envelope =
vix::realtime::protocol::parse(
text);Current protocol version:
1.0
Protocol envelopes may contain:
Transport converts transport-specific activity into Realtime connections and protocol envelopes.
vix::realtime::TransportHandlers handlers;
handlers.onOpen =
[](vix::realtime::ConnectionPtr connection)
{
// Associate the connection with a logical session.
};
handlers.onEnvelope =
[](vix::realtime::ConnectionPtr connection,
const vix::realtime::protocol::Envelope &envelope)
{
// Route the envelope to application runtime logic.
};
handlers.onClose =
[](vix::realtime::ConnectionPtr connection)
{
// Detach the logical session.
};
handlers.onError =
[](vix::realtime::ConnectionPtr connection,
vix::realtime::ErrorCode code,
const std::string &message)
{
// Record or report the transport error.
};The abstraction allows Realtime to support WebSocket, TCP, local IPC, tests, or custom transports without changing room logic.
WebSocketAdapter bridges Vix WebSocket sessions with the Realtime transport contract.
vix::websocket::Server websocketServer;
auto adapter =
std::make_shared<
vix::realtime::WebSocketAdapter>(
websocketServer);
adapter->set_handlers(
std::move(handlers));
adapter->attach();The adapter:
The adapter does not start or stop the underlying WebSocket server.
Metrics provides thread-safe observational counters and gauges.
auto metrics =
std::make_shared<
vix::realtime::Metrics>();
metrics->record_room_opened();
metrics->record_session_created();
metrics->record_events_persisted(2);
const auto snapshot =
metrics->snapshot();Available metrics include:
Metrics use relaxed atomics and do not participate in authoritative runtime behavior.
HealthMonitor creates a point-in-time runtime report.
auto monitor =
std::make_shared<
vix::realtime::HealthMonitor>(
server,
metrics);
const auto report =
monitor->check();Health states are:
The report inspects:
Example:
if (!report.operational())
{
for (const auto &issue : report.issues)
{
// Report the issue.
}
}Default runtime configuration:
| Option | Default |
|---|---|
| maxActiveRooms | 1000 |
| maxSessions | 10000 |
| maxSessionsPerRoom | 256 |
| maxRoomsPerSession | 32 |
| maxPendingCommandsPerRoom | 1024 |
| maxPayloadSize | 64 KiB |
| maxReplayEvents | 1000 |
| maxReplayBytes | 4 MiB |
| maxResumeRooms | 32 |
| snapshotEveryEvents | 100 |
| snapshotsToKeep | 3 |
| roomIdleTimeout | 300 seconds |
| commandTimeout | 5000 milliseconds |
| roomOpenTimeout | 10000 milliseconds |
| sessionResumeWindow | 120 seconds |
| presenceHeartbeatInterval | 30 seconds |
| presenceTimeout | 90 seconds |
| replayTimeout | 5000 milliseconds |
| snapshotOnRoomClose | true |
| restoreRoomsOnOpen | true |
| enableSessionResume | true |
| enablePresence | true |
Validate configuration before constructing custom runtime components:
vix::realtime::Config config;
config.validate();Realtime operations use vix::realtime::Error.
try
{
server->open_room(
vix::realtime::RoomId{"chat/general"},
"chat");
}
catch (const vix::realtime::Error &error)
{
const auto code =
error.code();
const auto name =
vix::realtime::to_string(code);
}Important error categories include:
Commands processed by one room are serialized through the room command queue and room execution lock.
Produced events are persisted before they are applied to authoritative room state.
Events are dispatched only after successful persistence and state application.
A command producing multiple events commits them as one store batch where the selected event-store implementation supports atomic batches.
State restoration rejects gaps or inconsistencies in room versions and event identifiers.
Logical sessions may remain alive after a transport connection disappears.
Application state and handlers do not depend on WebSocket types.
A single-process deployment may use:
RoomManager
MemoryEventStore
MemorySnapshotStore
LocalPresenceStore
RoomDirectory
WebSocketAdapter
A durable single-node deployment may use:
RoomManager
PostgresEventStore
PostgresSnapshotStore
LocalPresenceStore
RoomDirectory
WebSocketAdapter
A multi-node deployment additionally requires shared coordination for:
DistributedPresence defines the shared-presence contract, while RoomOwner and RoomDirectory define the ownership model used by future distributed coordination implementations.
HTTP / WebSocket runtime
|
v
WebSocketAdapter
|
v
Server
|
v
RoomManager
/ | \
/ | \
Rooms Sessions Presence
| |
v v
Handlers Connections
|
v
EventStore + SnapshotStore
Application code should place business rules in:
Transport callbacks should remain focused on:
The module is designed so most application logic can be tested without sockets.
Recommended test layers:
Example memory-backed setup:
auto eventStore =
std::make_shared<
vix::realtime::MemoryEventStore>();
auto snapshotStore =
std::make_shared<
vix::realtime::MemorySnapshotStore>();
auto presenceStore =
std::make_shared<
vix::realtime::LocalPresenceStore>();
auto directory =
std::make_shared<
vix::realtime::RoomDirectory>();Vix Realtime is distributed under the MIT License.
Copyright 2026, Gaspard Kirira.
Vix.cpp https://github.com/vixcpp/vix
| Back | FazBrowse Home | New Git URL |