Skip to content

Latest commit

 

History

History
1504 lines (1070 loc) · 27.6 KB

File metadata and controls

1504 lines (1070 loc) · 27.6 KB

API Reference

This page summarizes the public API of the Vix WebSocket module.

Use it when you want a quick reference for headers, classes, callbacks, configuration, server APIs, client APIs, sessions, long-polling, metrics, persistence, OpenAPI, and attached HTTP + WebSocket runtime helpers.

Main header

Use the umbrella header for most applications:

#include <vix/websocket.hpp>

This includes the main WebSocket APIs.

Direct headers

#include <vix/websocket/config.hpp>
#include <vix/websocket/protocol.hpp>
#include <vix/websocket/client.hpp>
#include <vix/websocket/server.hpp>
#include <vix/websocket/session.hpp>
#include <vix/websocket/router.hpp>
#include <vix/websocket/MessageStore.hpp>
#include <vix/websocket/SqliteMessageStore.hpp>
#include <vix/websocket/Metrics.hpp>
#include <vix/websocket/App.hpp>
#include <vix/websocket/HttpApi.hpp>
#include <vix/websocket/LongPolling.hpp>
#include <vix/websocket/LongPollingBridge.hpp>
#include <vix/websocket/AttachedRuntime.hpp>
#include <vix/websocket/Runtime.hpp>

Namespace

The main namespace is:

namespace vix::websocket

Some combined runtime helpers live in:

namespace vix

Public API overview

API Purpose
Config WebSocket runtime configuration.
JsonMessage Typed JSON message model.
Server High-level WebSocket server.
LowLevelServer Low-level native async WebSocket engine.
Session One connected WebSocket client.
Router Dispatches open, close, error, and message events.
Client Native WebSocket client.
LongPollingSession In-memory long-polling session buffer.
LongPollingManager Thread-safe manager for polling sessions.
LongPollingBridge Bridge between WebSocket and long-polling.
WebSocketMetrics Prometheus-style metrics counters and gauges.
IMessageStore Abstract message persistence interface.
SqliteMessageStore SQLite-backed message store.
AttachedRuntime Runs WebSocket beside a vix::App.
register_ws_docs(...) Registers OpenAPI docs for WebSocket endpoints.

Config

Header:

#include <vix/websocket/config.hpp>

Type:

vix::websocket::Config

Fields:

std::size_t maxMessageSize = 64 * 1024;
std::chrono::seconds idleTimeout{60};
bool enablePerMessageDeflate = true;
bool autoPingPong = true;
std::chrono::seconds pingInterval{30};

Static functions:

static Config from_core(const vix::config::Config &core);

Example:

vix::config::Config core{".env"};

vix::websocket::Config ws =
    vix::websocket::Config::from_core(core);

Configuration keys

Common environment values:

WEBSOCKET_HOST=0.0.0.0
WEBSOCKET_PORT=9090
WEBSOCKET_MAX_MESSAGE_SIZE=65536
WEBSOCKET_IDLE_TIMEOUT=60
WEBSOCKET_ENABLE_DEFLATE=true
WEBSOCKET_PING_INTERVAL=30
WEBSOCKET_AUTO_PING_PONG=true

Server

Header:

#include <vix/websocket/server.hpp>

Type:

vix::websocket::Server

Purpose:

High-level WebSocket server with sessions, callbacks, rooms, broadcasting,
typed messages, shutdown, and optional long-polling bridge integration.

Server construction

Server(
    vix::config::Config &cfg,
    std::shared_ptr<vix::executor::RuntimeExecutor> executor);
Server(
    vix::config::Config &cfg,
    std::unique_ptr<vix::executor::RuntimeExecutor> executor);

Example:

vix::config::Config config{".env"};

auto executor =
    std::make_shared<vix::executor::RuntimeExecutor>(4);

vix::websocket::Server ws{config, executor};

Server callback types

using OpenHandler =
    std::function<void(Session &)>;

using CloseHandler =
    std::function<void(Session &)>;

using ErrorHandler =
    std::function<void(Session &, const std::string &)>;

using MessageHandler =
    std::function<void(Session &, const std::string &)>;

using TypedMessageHandler =
    std::function<void(Session &, const std::string &, const vix::json::kvs &)>;

Server callbacks

void on_open(OpenHandler fn);
void on_close(CloseHandler fn);
void on_error(ErrorHandler fn);
void on_message(MessageHandler fn);
void on_typed_message(TypedMessageHandler fn);

Example:

ws.on_message(
  [](vix::websocket::Session &session, const std::string &message)
  {
    session.send_text("echo: " + message);
  });

Typed example:

ws.on_typed_message(
  [](vix::websocket::Session &session,
     const std::string &type,
     const vix::json::kvs &payload)
  {
    (void)payload;

    if (type == "ping")
    {
      session.send_text("pong");
    }
  });

Server lifecycle

void start();
void stop_async();
void stop();
void listen_blocking();

Use:

ws.start();

For shutdown:

ws.stop_async();
ws.stop();

stop_async() requests non-blocking shutdown.

stop() performs final blocking shutdown and joins internal server threads.

Server rooms

void join_room(std::shared_ptr<Session> session, const RoomId &room);
void leave_room(std::shared_ptr<Session> session, const RoomId &room);

Example:

ws.on_open([&ws](vix::websocket::Session &session)
{
  ws.join_room(session.shared_from_this(), "chat:general");
});

Server broadcasting

void broadcast_text(const std::string &text);
void broadcast_text_to_room(const RoomId &room, const std::string &text);

Examples:

ws.broadcast_text("global message");
ws.broadcast_text_to_room("chat:general", "room message");

Server long-polling bridge

void attach_long_polling_bridge(LongPollingBridge *bridge);

Example:

vix::websocket::LongPollingBridge bridge{
    &metrics,
    std::chrono::seconds{60},
    256};

ws.attach_long_polling_bridge(&bridge);

Server information

Common server information APIs include:

int port() const;
int bound_port() const;

Use these to inspect the configured or bound WebSocket port when supported by the current build.

LowLevelServer

Header:

#include <vix/websocket/websocket.hpp>

Type:

vix::websocket::LowLevelServer

Purpose:

Low-level asynchronous WebSocket server engine.

It owns:

  • async I/O context
  • TCP listener
  • accept loop
  • I/O worker threads
  • session creation
  • server shutdown state

Most applications should use vix::websocket::Server instead.

LowLevelServer API

LowLevelServer(
    vix::config::Config &coreConfig,
    std::shared_ptr<vix::executor::RuntimeExecutor> executor,
    std::shared_ptr<Router> router);

~LowLevelServer();

void run();
void stop_async();
void join_threads();

bool is_stop_requested() const;

Session

Header:

#include <vix/websocket/session.hpp>

Type:

vix::websocket::Session

Purpose:

Represents one connected WebSocket client.

A session manages:

  • HTTP Upgrade handshake
  • frame reads
  • frame writes
  • text messages
  • binary messages
  • close handling
  • heartbeat
  • idle timeout
  • router dispatch

Session construction

Session(
    std::unique_ptr<tcp_stream> stream,
    const Config &cfg,
    std::shared_ptr<Router> router,
    std::shared_ptr<vix::executor::RuntimeExecutor> executor,
    std::shared_ptr<io_context> ioc);

Most applications do not construct sessions directly.

The low-level server creates them internally.

Session API

task<void> run();

void send_text(std::string_view text);
void send_binary(const void *data, std::size_t size);

void close(std::string reason = {});
bool is_open() const noexcept;

void emit_error(const std::string &message);
void shutdown_now() noexcept;

Example:

ws.on_message(
  [](vix::websocket::Session &session, const std::string &message)
  {
    if (session.is_open())
    {
      session.send_text("received: " + message);
    }
  });

Router

Header:

#include <vix/websocket/router.hpp>

Type:

vix::websocket::Router

Purpose:

Lightweight event dispatcher for WebSocket sessions.

Router handler types

using OpenHandler =
    std::function<void(Session &)>;

using CloseHandler =
    std::function<void(Session &)>;

using ErrorHandler =
    std::function<void(Session &, const std::string &)>;

using MessageHandler =
    std::function<void(Session &, std::string)>;

Router registration API

void on_open(OpenHandler cb);
void on_close(CloseHandler cb);
void on_error(ErrorHandler cb);
void on_message(MessageHandler cb);

Router dispatch API

void handle_open(Session &session) const;
void handle_close(Session &session) const;
void handle_error(Session &session, const std::string &error) const;
void handle_message(Session &session, std::string payload) const;

Router inspection API

bool has_open_handler() const noexcept;
bool has_close_handler() const noexcept;
bool has_error_handler() const noexcept;
bool has_message_handler() const noexcept;

Most applications should register callbacks through Server instead of using Router directly.

Client

Header:

#include <vix/websocket/client.hpp>

Type:

vix::websocket::Client

Purpose:

Native WebSocket client with async connect, text messages, typed JSON messages,
heartbeat, and optional reconnect.

The client exposes callback setters, connection control, text sending, typed JSON sending, ping, close, and connection state helpers. :contentReference[oaicite:0]{index=0}

Client construction

static std::shared_ptr<Client> create(
    std::string host,
    std::string port,
    std::string target = "/");

Example:

auto client =
    vix::websocket::Client::create("127.0.0.1", "9090", "/");

Client callback types

using OpenHandler =
    std::function<void()>;

using MessageHandler =
    std::function<void(const std::string &)>;

using CloseHandler =
    std::function<void()>;

using ErrorHandler =
    std::function<void(const std::string &)>;

Client callbacks

void on_open(OpenHandler cb);
void on_message(MessageHandler cb);
void on_close(CloseHandler cb);
void on_error(ErrorHandler cb);

Example:

client->on_open([client]()
{
  client->send_text("hello");
});

client->on_message([](const std::string &message)
{
  vix::print("received:", message);
});

Client connection API

void connect();
void close();

bool is_connected() const noexcept;

connect() starts the client runtime thread and async connection flow.

close() closes the connection, stops runtime activity, and joins client threads when safe.

Client sending API

void send_text(const std::string &text);

void send_json_message(
    const std::string &type,
    const vix::json::kvs &payload);

void send_json_message(
    const std::string &type,
    std::initializer_list<vix::json::token> payloadTokens);

void send(
    const std::string &type,
    const vix::json::kvs &payload);

void send(
    const std::string &type,
    std::initializer_list<vix::json::token> payloadTokens);

void send_ping();

Example:

client->send_json_message(
    "chat.message",
    {
      {"text", "Hello"}
    });

The send(...) overloads are convenience aliases for typed JSON sending.

Client heartbeat and reconnect

void enable_heartbeat(std::chrono::seconds interval);

void enable_auto_reconnect(
    bool enable,
    std::chrono::seconds delay = std::chrono::seconds{3});

Example:

client->enable_heartbeat(std::chrono::seconds{30});
client->enable_auto_reconnect(true, std::chrono::seconds{3});

JsonMessage

Header:

#include <vix/websocket/protocol.hpp>

Type:

vix::websocket::JsonMessage

Purpose:

Structured typed WebSocket message model.

Common fields:

std::string id;
std::string kind;
std::string room;
std::string type;
std::string ts;
vix::json::kvs payload;

Common shape:

{
  "id": "00000000000000000001",
  "kind": "event",
  "room": "general",
  "type": "chat.message",
  "ts": "2026-05-17T10:00:00Z",
  "payload": {
    "text": "Hello"
  }
}

JsonMessage API

Common API:

static std::optional<JsonMessage> parse(const std::string &text);

std::string to_json_string() const;

Example:

auto parsed = vix::websocket::JsonMessage::parse(text);

if (parsed)
{
  vix::print("type:", parsed->type);
}

Protocol detail API

Header:

#include <vix/websocket/protocol.hpp>

The protocol layer includes internal frame helpers under:

namespace vix::websocket::detail

Important internal types include:

enum class Opcode;
struct Frame;
struct FrameHeader;

Frame helpers are used by the client and session implementation.

Most applications should use Server, Session, Client, and JsonMessage instead of protocol internals.

LongPollingSession

Header:

#include <vix/websocket/LongPolling.hpp>

Type:

vix::websocket::LongPollingSession

Fields:

std::string id;
std::chrono::steady_clock::time_point lastSeen;
std::deque<JsonMessage> buffer;

API:

LongPollingSession();
explicit LongPollingSession(std::string sessionId);

void touch() noexcept;

bool is_expired(
    std::chrono::seconds ttl,
    std::chrono::steady_clock::time_point now) const noexcept;

void enqueue(const JsonMessage &msg, std::size_t maxBufferSize);

std::vector<JsonMessage> drain(std::size_t maxCount);

The session stores a bounded FIFO buffer and refreshes lastSeen when messages are enqueued or drained. :contentReference[oaicite:1]{index=1}

LongPollingManager

Header:

#include <vix/websocket/LongPolling.hpp>

Type:

vix::websocket::LongPollingManager

Purpose:

Thread-safe manager for long-polling sessions and buffers.

LongPollingManager construction

LongPollingManager(
    std::chrono::seconds sessionTtl = std::chrono::seconds{60},
    std::size_t maxBufferPerSession = 256,
    WebSocketMetrics *metrics = nullptr);

Example:

vix::websocket::LongPollingManager manager{
    std::chrono::seconds{60},
    256,
    &metrics};

LongPollingManager API

void push_to(const SessionId &sessionId, const JsonMessage &message);

std::vector<JsonMessage> poll(
    const SessionId &sessionId,
    std::size_t maxMessages = 50,
    bool createIfMissing = true);

void sweep_expired();

std::size_t session_count() const;

std::size_t buffer_size(const SessionId &sessionId) const;

push_to(...) creates the session if missing, poll(...) drains up to maxMessages, and sweep_expired() removes inactive sessions based on TTL. :contentReference[oaicite:2]{index=2}

LongPollingBridge

Header:

#include <vix/websocket/LongPollingBridge.hpp>

Type:

vix::websocket::LongPollingBridge

Purpose:

Bridge between WebSocket JsonMessage events and HTTP long-polling sessions.

LongPollingBridge aliases

using SessionId = std::string;

using Resolver =
    std::function<SessionId(const JsonMessage &)>;

using HttpToWsForward =
    std::function<void(const JsonMessage &)>;

LongPollingBridge constructors

With external manager:

LongPollingBridge(
    LongPollingManager &manager,
    Resolver resolver = {},
    HttpToWsForward httpToWs = {});

Owning manager:

LongPollingBridge(
    WebSocketMetrics *metrics,
    std::chrono::seconds sessionTtl = std::chrono::seconds{60},
    std::size_t maxBufferPerSession = 256,
    Resolver resolver = {},
    HttpToWsForward httpToWs = {});

LongPollingBridge API

void on_ws_message(const JsonMessage &msg);

std::vector<JsonMessage> poll(
    const SessionId &sessionId,
    std::size_t maxMessages = 50,
    bool createIfMissing = true);

void send_from_http(
    const SessionId &sessionId,
    const JsonMessage &msg);

LongPollingManager &manager() noexcept;
const LongPollingManager &manager() const noexcept;

std::size_t session_count() const;
std::size_t buffer_size(const SessionId &sid) const;

Default resolution:

if msg.room is not empty:
  session id = "room:" + msg.room
else:
  session id = "broadcast"

WebSocketMetrics

Header:

#include <vix/websocket/Metrics.hpp>

Type:

vix::websocket::WebSocketMetrics

Purpose:

Prometheus-style metrics for WebSocket and long-polling.

WebSocketMetrics fields

std::atomic<std::uint64_t> connections_total{0};
std::atomic<std::uint64_t> connections_active{0};

std::atomic<std::uint64_t> messages_in_total{0};
std::atomic<std::uint64_t> messages_out_total{0};

std::atomic<std::uint64_t> errors_total{0};

std::atomic<std::uint64_t> lp_sessions_total{0};
std::atomic<std::uint64_t> lp_sessions_active{0};

std::atomic<std::uint64_t> lp_polls_total{0};

std::atomic<std::uint64_t> lp_messages_buffered{0};
std::atomic<std::uint64_t> lp_messages_enqueued_total{0};
std::atomic<std::uint64_t> lp_messages_drained_total{0};

WebSocketMetrics API

std::string render_prometheus() const;

Example:

vix::websocket::WebSocketMetrics metrics;

metrics.connections_total.fetch_add(1, std::memory_order_relaxed);

std::string text = metrics.render_prometheus();

Metrics exporter

void run_metrics_http_exporter(
    WebSocketMetrics &metrics,
    const std::string &address = "0.0.0.0",
    std::uint16_t port = 9100);

Example:

vix::websocket::run_metrics_http_exporter(
    metrics,
    "0.0.0.0",
    9100);

The exporter serves Prometheus text at:

GET /metrics

The exporter reads an HTTP request head, returns metrics.render_prometheus() for /metrics, and returns 404 for other targets. :contentReference[oaicite:3]{index=3}

IMessageStore

Header:

#include <vix/websocket/MessageStore.hpp>

Type:

vix::websocket::IMessageStore

Purpose:

Abstract persistence interface for JsonMessage history and replay.

IMessageStore API

virtual ~IMessageStore() = default;

virtual void append(const JsonMessage &msg) = 0;

virtual std::vector<JsonMessage> list_by_room(
    const std::string &room,
    std::size_t limit,
    const std::optional<std::string> &before_id = std::nullopt) = 0;

virtual std::vector<JsonMessage> replay_from(
    const std::string &start_id,
    std::size_t limit) = 0;

SqliteMessageStore

Header:

#include <vix/websocket/SqliteMessageStore.hpp>

Type:

vix::websocket::SqliteMessageStore

Purpose:

SQLite-backed implementation of IMessageStore.

SqliteMessageStore construction

explicit SqliteMessageStore(const std::string &db_path);

Example:

vix::websocket::SqliteMessageStore store{"messages.db"};

SqliteMessageStore API

~SqliteMessageStore() override;

void append(const JsonMessage &msg) override;

std::vector<JsonMessage> list_by_room(
    const std::string &room,
    std::size_t limit,
    const std::optional<std::string> &before_id = std::nullopt) override;

std::vector<JsonMessage> replay_from(
    const std::string &start_id,
    std::size_t limit) override;

The SQLite store opens or creates the database, enables WAL mode, creates the messages table, and stores fields such as id, kind, room, type, ts, and payload_json.

App

Header:

#include <vix/websocket/App.hpp>

Type:

vix::websocket::App

Purpose:

High-level WebSocket application wrapper.

It manages:

  • config
  • executor
  • underlying WebSocket server
  • typed message routes
  • shutdown

App handler type

using TypedHandler =
    std::function<void(
        Session &,
        const std::string &type,
        const vix::json::kvs &payload)>;

App construction

App(
    const std::string &configPath,
    std::shared_ptr<vix::executor::RuntimeExecutor> executor);

Example:

auto executor =
    std::make_shared<vix::executor::RuntimeExecutor>(4);

vix::websocket::App app{".env", executor};

App API

App &ws(const std::string &endpoint, TypedHandler handler);

void run_blocking();
void stop() noexcept;

Server &server() noexcept;
vix::config::Config &config() noexcept;

std::shared_ptr<vix::executor::RuntimeExecutor> executor() noexcept;

AttachedRuntime

Header:

#include <vix/websocket/AttachedRuntime.hpp>

Type:

vix::websocket::AttachedRuntime

Purpose:

Attach a WebSocket server to a vix::App with shared lifecycle.

AttachedRuntime construction

AttachedRuntime(
    vix::App &app,
    vix::websocket::Server &ws,
    std::shared_ptr<vix::executor::RuntimeExecutor> exec);

Example:

vix::App app;

auto executor =
    std::make_shared<vix::executor::RuntimeExecutor>(4);

vix::websocket::Server ws{app.config(), executor};

vix::websocket::AttachedRuntime runtime{app, ws, executor};

The constructor starts the WebSocket server and registers an HTTP shutdown callback. :contentReference[oaicite:4]{index=4}

AttachedRuntime API

void request_stop() noexcept;
void finalize_shutdown() noexcept;

request_stop() requests non-blocking WebSocket shutdown.

finalize_shutdown() performs final blocking cleanup by stopping the WebSocket server and then stopping the shared executor. :contentReference[oaicite:5]{index=5}

Combined runtime helpers

Header:

#include <vix/websocket/AttachedRuntime.hpp>

Namespace:

namespace vix

register_ws_openapi_docs_once

void register_ws_openapi_docs_once();

Registers WebSocket OpenAPI docs once per process.

Default documented routes:

GET  /ws
GET  /ws/poll
POST /ws/send
GET  /metrics

run_http_and_ws

void run_http_and_ws(
    vix::App &app,
    vix::websocket::Server &ws,
    std::shared_ptr<vix::executor::RuntimeExecutor> exec,
    const vix::config::Config &cfg);

Port overload:

void run_http_and_ws(
    vix::App &app,
    vix::websocket::Server &ws,
    std::shared_ptr<vix::executor::RuntimeExecutor> exec,
    int port);

Purpose:

Run HTTP and WebSocket together with shared config and executor.

The combined runner registers WebSocket docs, HTTP OpenAPI routes, Swagger UI routes, creates an AttachedRuntime, then runs the HTTP app. :contentReference[oaicite:6]{index=6}

serve_http_and_ws

serve_http_and_ws(configPath, port, fn);
serve_http_and_ws(fn);

Purpose:

High-level helper that creates Config, RuntimeExecutor, App, and WebSocket Server,
then lets the caller configure HTTP and WebSocket behavior.

Example:

vix::serve_http_and_ws([](vix::App &app, vix::websocket::Server &ws)
{
  app.get("/", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    res.text("HTTP + WebSocket");
  });

  ws.on_message(
    [](vix::websocket::Session &session, const std::string &message)
    {
      session.send_text("echo: " + message);
    });
});

OpenAPI docs

Header:

#include <vix/websocket/openapi_docs.hpp>

Namespace:

namespace vix::websocket::openapi

Function:

void register_ws_docs(
    std::string ws_upgrade_path = "/ws",
    std::string lp_poll_path = "/ws/poll",
    std::string lp_send_path = "/ws/send",
    std::string metrics_path = "/metrics");

Example:

vix::websocket::openapi::register_ws_docs();

Custom paths:

vix::websocket::openapi::register_ws_docs(
    "/api/ws",
    "/api/ws/poll",
    "/api/ws/send",
    "/api/ws/metrics");

HTTP API helpers

Header:

#include <vix/websocket/HttpApi.hpp>

Namespace:

namespace vix::websocket::http

Purpose:

Helpers for long-polling HTTP endpoints and request parsing.

Common endpoints:

GET  /ws/poll
POST /ws/send

The helper code supports URL decoding, query parameter extraction, request target extraction, and integration with LongPollingBridge.

Minimal server example

#include <memory>
#include <string>

#include <vix/config/Config.hpp>
#include <vix/executor/RuntimeExecutor.hpp>
#include <vix/websocket.hpp>

int main()
{
  vix::config::Config config{".env"};

  auto executor =
      std::make_shared<vix::executor::RuntimeExecutor>(4);

  vix::websocket::Server ws{config, executor};

  ws.on_message(
    [](vix::websocket::Session &session, const std::string &message)
    {
      session.send_text("echo: " + message);
    });

  ws.start();

  return 0;
}

Minimal client example

#include <memory>
#include <string>

#include <vix/print.hpp>
#include <vix/websocket.hpp>

int main()
{
  auto client =
      vix::websocket::Client::create("127.0.0.1", "9090", "/");

  client->on_open([client]()
  {
    client->send_text("hello");
  });

  client->on_message([](const std::string &message)
  {
    vix::print("received:", message);
  });

  client->connect();

  return 0;
}

Minimal attached runtime example

#include <memory>
#include <string>

#include <vix.hpp>
#include <vix/websocket.hpp>

int main()
{
  vix::App app;

  auto executor =
      std::make_shared<vix::executor::RuntimeExecutor>(4);

  vix::websocket::Server ws{app.config(), executor};

  ws.on_message(
    [](vix::websocket::Session &session, const std::string &message)
    {
      session.send_text("echo: " + message);
    });

  vix::websocket::AttachedRuntime runtime{app, ws, executor};

  app.run(8080);

  return 0;
}

API by use case

Use case API
Start WebSocket server vix::websocket::Server
Handle connection open Server::on_open(...)
Handle raw message Server::on_message(...)
Handle typed JSON message Server::on_typed_message(...)
Reply to one client Session::send_text(...)
Send binary payload Session::send_binary(...)
Close one client Session::close(...)
Broadcast to all clients Server::broadcast_text(...)
Broadcast to room Server::broadcast_text_to_room(...)
Join room Server::join_room(...)
Leave room Server::leave_room(...)
Create C++ client Client::create(...)
Send client text Client::send_text(...)
Send client typed JSON Client::send_json_message(...)
Enable heartbeat Client::enable_heartbeat(...)
Enable reconnect Client::enable_auto_reconnect(...)
Use long-polling LongPollingManager, LongPollingBridge
Export metrics WebSocketMetrics::render_prometheus()
Run metrics exporter run_metrics_http_exporter(...)
Store messages IMessageStore
Store in SQLite SqliteMessageStore
Attach HTTP + WebSocket AttachedRuntime
Register OpenAPI docs register_ws_docs(...)

Related pages