CMSDK was built to power Psyclone, but nothing in its lower layers depends on it. Strip away PsyAPI, PsySpec and cranks and what remains is a complete, cross-platform C++ toolkit for building distributed client/server applications: typed binary messaging, sockets and protocol handling, a production HTTP/WebSocket server, a request/reply fabric with routing and load balancing, portable threading and synchronisation, microsecond time, and statistics. Many real-world services are built directly on these primitives, with no Psyclone process anywhere in sight.
This track documents that usage. You need none of the Psyclone concepts (modules, cranks, whiteboards, PsySpec XML) to follow it — just C++ and the library itself (Compiling and linking with the CMSDK library).
What you get as a standalone library
- cmlabs::DataMessage (Messaging) — the universal typed payload. One flat memory allocation holding named, typed entries (strings, integers, doubles, timestamps, raw binary blocks, arrays, attached sub-messages). It is copied verbatim onto a socket or into shared memory: there is no serialisation step, no schema compiler, no code generation. The same object is the request, the reply, the wire format and the storage format.
- The request fabric (Requests) — cmlabs::RequestClient, cmlabs::RequestGateway and cmlabs::RequestExecutor: a three-role request/reply system with load balancing, heartbeats, reconnection and HTTP/WebSocket ingress. This is the backbone of a standalone CMSDK application and gets its own page: Building a Client/Server/Gateway Application.
- cmlabs::NetworkManager (Networking) — connection and listener management with per-port protocol auto-detection: one TCP port can accept the CMSDK binary message protocol, HTTP(S) and WebSocket simultaneously. Includes a fully functional HTTP server and HTTP client.
- cmlabs::RESTParser / cmlabs::RESTRequest (RESTAPI.h) — declare a REST API in XML (request names, typed parameters, optional/required rules) and have incoming HTTP requests or DataMessages parsed and validated into typed cmlabs::RESTRequest objects.
- cmlabs::ThreadManager (Threading) and the utils synchronisation primitives (utils::Mutex, events, wait queues) — portable threads with per-thread CPU statistics, identical on Windows, Linux and macOS.
- Microsecond time (Functions dealing with time) — cmlabs::GetTimeNow() returns a uint64 of microseconds on an absolute timescale, monotonic and cheap, with cross-machine synchronisation support.
- Statistics — cmlabs::Stats for min/max/mean/variance accumulation and cmlabs::MovingAverage for windowed rolling averages (getAverage(ms, val, count)), ideal for live throughput/latency metrics.
- The utils library (Utility functions and objects) — strings, files, processes, dynamic libraries, compression, Base64, timers and more.
All of it is one library, one #include root, identical API on every supported platform. Platform differences (Winsock vs POSIX sockets, named file mappings vs shm_open, thread APIs) are absorbed below the API surface.
The mental model: client / server / gateway
A standalone CMSDK application is usually shaped like this:
clients gateway servers
┌───────────────┐ ┌────────────────┐ ┌──────────────────┐
│ RequestClient │──TCP──▶│ RequestGateway │◀──TCP──│ RequestExecutor │
│ RequestClient │──HTTP─▶│ (router, LB, │◀──TCP──│ RequestExecutor │
│ browser / WS │──WS───▶│ web server) │ │ (worker threads)│
└───────────────┘ └────────────────┘ └──────────────────┘
- A gateway process (cmlabs::RequestGateway) listens on one or more ports. It is the only component with a well-known address.
- Server processes (cmlabs::RequestExecutor) dial out to the gateway, announce themselves with heartbeats, and pull requests to process. (We say "server" for the process and "executor" for the CMSDK object — a cmlabs::RequestExecutor — that the process embeds.) Add more server processes — on the same machine or others — and the gateway load-balances across them automatically.
- Client processes (cmlabs::RequestClient) also dial out to the gateway, post cmlabs::DataMessage requests and receive replies. Because the gateway accepts HTTP and WebSocket on the same ports, browsers and third-party HTTP clients can be clients too, with no extra code.
Nobody dials into a client or a server; only the gateway accepts inbound connections. That single fact simplifies firewalling and NAT traversal enormously, and it means servers can come, go and crash without any client reconfiguration.
One note on numbering: the gateway/executor/client ids used here are arbitrary uint32 rendezvous keys that just have to match between addGateway(id, ...) and the RequestGateway(id) constructor — they are not ports, unlike Psyclone node system ids, which do equal the node's port. The distinction is spelled out in The two ID spaces.
When to choose this vs the Psyclone component model
Both tracks use the same underlying machinery — the choice is about the programming model you want on top.
Choose the standalone track (Track B) when:
- You are building a conventional service: clients ask, servers answer. Request/reply is your natural shape, not publish–subscribe.
- You want plain C++ executables with main(), your own process lifecycle, your own configuration — no runtime hosting your code.
- You are embedding CMSDK into an existing application (any framework, any build system) and just need messaging, networking, HTTP or threading.
- Your topology is simple and explicit: n clients, one or a few gateways, m interchangeable workers.
**Choose the Psyclone component model (Track A, starting at What is CMSDK?) when:**
- Your problem decomposes into a pipeline or graph of processing stages reacting to each other's outputs — publish–subscribe with triggers and contexts fits better than request/reply.
- You want the wiring (which component runs where, what it listens to) in a declarative PsySpec XML file you can rearrange without recompiling.
- You want the runtime services: whiteboards with temporal queries (Whiteboards & Catalogs), crash-isolated spaces, multi-node distribution (Distributed Systems), PsyProbe monitoring.
The two are not exclusive. A Psyclone system can expose a request-fabric front door, and a standalone service can later be refactored into Psyclone components — the cmlabs::DataMessage payloads carry over unchanged.
Where to next