/cpp-server
SpacetimeDB C++ server module SDK reference. Use when writing tables, reducers, or module logic in C++.
$ npx -y skills add clockworklabs/spacetimedb --skill cpp-server --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/cpp-server
Context preview
The summary Claude sees to decide when to auto-load this skill.
SpacetimeDB C++ server module SDK reference. Use when writing tables, reducers, or module logic in C++.
SKILL.md
cpp-server.SKILL.mdname: cpp-server
description: SpacetimeDB C++ server module SDK reference. Use when writing tables, reducers, or module logic in C++.
license: Apache-2.0
metadata:
author: clockworklabs
version: "2.0"
role: server
language: cpp
cursor_globs: "**/*.cpp,**/*.h,**/*.hpp"
cursor_always_apply: true
SpacetimeDB C++ SDK Reference
Imports
#include <spacetimedb.h>
using namespace SpacetimeDB;
Tables
Register structs with macros, then declare as tables:
struct Entity {
uint64_t id;
Identity owner;
std::string name;
bool active;
};
SPACETIMEDB_STRUCT(Entity, id, owner, name, active)
SPACETIMEDB_TABLE(Entity, entity, Public)
FIELD_PrimaryKeyAutoInc(entity, id)
FIELD_Index(entity, name)Options:
- `SPACETIMEDB_TABLE(Type, accessor, Public|Private)`: regular table
- `SPACETIMEDB_TABLE(Type, accessor, Public|Private, true)`: event table
Field constraints:
- `FIELD_PrimaryKey(accessor, field)`: primary key
- `FIELD_PrimaryKeyAutoInc(accessor, field)`: primary key with auto-increment (use 0 on insert)
- `FIELD_Unique(accessor, field)`: unique constraint
- `FIELD_Index(accessor, field)`: btree index (enables `.filter()`)
Column Types
| C++ type | Notes | |----------|-------| | `uint8_t` / `uint16_t` / `uint32_t` / `uint64_t` | unsigned integers | | `SpacetimeDB::u128` / `SpacetimeDB::u256` | large unsigned integers | | `int8_t` / `int16_t` / `int32_t` / `int64_t` | signed integers | | `SpacetimeDB::i128` / `SpacetimeDB::i256` | large signed integers | | `float` / `double` | floats | | `bool` | boolean | | `std::string` | text | | `std::vector<T>` | list/array | | `std::optional<T>` | nullable column | | `Identity` | user identity | | `ConnectionId` | connection handle | | `Timestamp` | server timestamp (microseconds since epoch) | | `TimeDuration` | duration in microseconds | | `ScheduleAt` | for scheduled tables |
Indexes
// Single-column:
FIELD_Index(entity, name)
// Access: ctx.db[entity_name].filter("Alice")
// Multi-column:
FIELD_NamedMultiColumnIndex(score, by_player_and_level, player_id, level)Range queries (requires `#include <spacetimedb/range_queries.h>`):
ctx.db[user_age].filter(range_inclusive(uint8_t(18), uint8_t(65)));
ctx.db[user_age].filter(range_from(uint8_t(18)));
Reducers
All reducers return `ReducerResult`. Use `Ok()` or `Err(message)`:
SPACETIMEDB_REDUCER(create_entity, ReducerContext ctx, std::string name) {
if (name.empty()) {
return Err("Name cannot be empty");
}
ctx.db[entity].insert(Entity{0, ctx.sender(), name, true});
return Ok();
}DB Operations
ctx.db[entity].insert(Entity{0, owner, "Sample", true}); // Insert (0 for autoInc)
ctx.db[entity_id].find(entityId); // Find by PK → std::optional
ctx.db[entity_identity].find(ctx.sender()); // Find by unique column
ctx.db[entity_name].filter("Alice"); // Filter by index → iterable
ctx.db[entity]; // All rows → iterable (range-for)
ctx.db[entity].count(); // Count rows
// Update: find, mutate, update
if (auto e = ctx.db[entity_id].find(entityId)) {
e->name = "New Name";
ctx.db[entity_id].update(*e);
}
// Delete by primary key
ctx.db[entity_id].delete_by_key(entityId);Note: Bracket notation `ctx.db[accessor]` is used for all table access. The accessor name comes from `SPACETIMEDB_TABLE` and `FIELD_*` macros.
Lifecycle Hooks
SPACETIMEDB_INIT(init, ReducerContext ctx) {
LOG_INFO("Database initializing...");
return Ok();
}
SPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {
LOG_INFO("Connected: " + ctx.sender().to_string());
return Ok();
}
SPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {
LOG_INFO("Disconnected: " + ctx.sender().to_string());
return Ok();
}Authentication & Timestamps
// Auth: ctx.sender() is the caller's Identity
if (row.owner != ctx.sender()) {
return Err("unauthorized");
}
// Server timestamps
ctx.db[item].insert(Item{0, ctx.sender(), ctx.timestamp});
// Timestamp arithmetic
Timestamp later = ctx.timestamp + TimeDuration::from_seconds(10);Reducer Context
`ReducerContext` is the single source of sender identity, deterministic time, and deterministic randomness inside a reducer. Always go through `ctx` for these. Standard library clocks and random sources are not available in modules.
ctx.db[table] // Table access (bracket notation)
ctx.sender() // Caller's Identity
ctx.timestamp // Invocation timestamp
ctx.connection_id // std::optional<ConnectionId>
ctx.identity() // Module's own identity
ctx.rng() // Deterministic RNG
ctx.sender_auth() // AuthCtx with JWT claims
Scheduled Tables
struct Reminder {
uint64_t scheduled_id;
ScheduleAt scheduled_at;
std::string message;
};
SPACETIMEDB_STRUCT(Reminder, scheduled_id, scheduled_at, message)
SPACETIMEDB_TABLE(Reminder, reminder, Public)
FIELD_PrimaryKeyAutoInc(reminder, scheduled_id)
SPACETIMEDB_SCHEDULE(reminder, 1, send_reminder) // 1 = scheduled_at field index (0-based)
SPACETIMEDB_REDUCER(send_reminder, ReducerContext ctx, Reminder arg) {
LOG_INFO("Reminder: " + arg.message);
return Ok();
}
// One-time: fires at a specific time
ctx.db[reminder].insert(Reminder{0, ScheduleAt::time(ctx.timestamp + TimeDuration::from_seconds(10)), "msg"});
// Repeating: fires on an interval
ctx.db[reminder].insert(Reminder{0, ScheduleAt::interval(TimeDuration::from_seconds(5)), "msg"});Custom Types
// Struct (product type):
struct Point { float x; float y; };
SPACETIMEDB_STRUCT(Point, x, y)
// Enum (sum type):
SPACETIMEDB_UNIT_TYPE(Active)
SPACETIMEDB_UNIT_TYPE(Inactive)
SPACETIMEDB_ENUM(PlayerStatus,
(Active, Active),
(Inactive, InactiRead more
name: cpp-server description: SpacetimeDB C++ server module SDK reference. Use when writing tables, reducers, or module logic in C++. license: Apache-2.0 metadata: author: clockworklabs version: "2.0" role: server language: cpp cursor_globs: "**/*.cpp,**/*.h,**/*.hpp" cursor_always_apply: true
SpacetimeDB C++ SDK Reference
Imports
#include <spacetimedb.h> using namespace SpacetimeDB;
Tables
Register structs with macros, then declare as tables:
struct Entity {
uint64_t id;
Identity owner;
std::string name;
bool active;
};
SPACETIMEDB_STRUCT(Entity, id, owner, name, active)
SPACETIMEDB_TABLE(Entity, entity, Public)
FIELD_PrimaryKeyAutoInc(entity, id)
FIELD_Index(entity, name)Options:
- `SPACETIMEDB_TABLE(Type, accessor, Public|Private)`: regular table
- `SPACETIMEDB_TABLE(Type, accessor, Public|Private, true)`: event table
Field constraints:
- `FIELD_PrimaryKey(accessor, field)`: primary key
- `FIELD_PrimaryKeyAutoInc(accessor, field)`: primary key with auto-increment (use 0 on insert)
- `FIELD_Unique(accessor, field)`: unique constraint
- `FIELD_Index(accessor, field)`: btree index (enables `.filter()`)
Column Types
| C++ type | Notes | |----------|-------| | `uint8_t` / `uint16_t` / `uint32_t` / `uint64_t` | unsigned integers | | `SpacetimeDB::u128` / `SpacetimeDB::u256` | large unsigned integers | | `int8_t` / `int16_t` / `int32_t` / `int64_t` | signed integers | | `SpacetimeDB::i128` / `SpacetimeDB::i256` | large signed integers | | `float` / `double` | floats | | `bool` | boolean | | `std::string` | text | | `std::vector<T>` | list/array | | `std::optional<T>` | nullable column | | `Identity` | user identity | | `ConnectionId` | connection handle | | `Timestamp` | server timestamp (microseconds since epoch) | | `TimeDuration` | duration in microseconds | | `ScheduleAt` | for scheduled tables |
Indexes
// Single-column:
FIELD_Index(entity, name)
// Access: ctx.db[entity_name].filter("Alice")
// Multi-column:
FIELD_NamedMultiColumnIndex(score, by_player_and_level, player_id, level)Range queries (requires `#include <spacetimedb/range_queries.h>`):
ctx.db[user_age].filter(range_inclusive(uint8_t(18), uint8_t(65))); ctx.db[user_age].filter(range_from(uint8_t(18)));
Reducers
All reducers return `ReducerResult`. Use `Ok()` or `Err(message)`:
SPACETIMEDB_REDUCER(create_entity, ReducerContext ctx, std::string name) {
if (name.empty()) {
return Err("Name cannot be empty");
}
ctx.db[entity].insert(Entity{0, ctx.sender(), name, true});
return Ok();
}DB Operations
ctx.db[entity].insert(Entity{0, owner, "Sample", true}); // Insert (0 for autoInc)
ctx.db[entity_id].find(entityId); // Find by PK → std::optional
ctx.db[entity_identity].find(ctx.sender()); // Find by unique column
ctx.db[entity_name].filter("Alice"); // Filter by index → iterable
ctx.db[entity]; // All rows → iterable (range-for)
ctx.db[entity].count(); // Count rows
// Update: find, mutate, update
if (auto e = ctx.db[entity_id].find(entityId)) {
e->name = "New Name";
ctx.db[entity_id].update(*e);
}
// Delete by primary key
ctx.db[entity_id].delete_by_key(entityId);Note: Bracket notation `ctx.db[accessor]` is used for all table access. The accessor name comes from `SPACETIMEDB_TABLE` and `FIELD_*` macros.
Lifecycle Hooks
SPACETIMEDB_INIT(init, ReducerContext ctx) {
LOG_INFO("Database initializing...");
return Ok();
}
SPACETIMEDB_CLIENT_CONNECTED(on_connect, ReducerContext ctx) {
LOG_INFO("Connected: " + ctx.sender().to_string());
return Ok();
}
SPACETIMEDB_CLIENT_DISCONNECTED(on_disconnect, ReducerContext ctx) {
LOG_INFO("Disconnected: " + ctx.sender().to_string());
return Ok();
}Authentication & Timestamps
// Auth: ctx.sender() is the caller's Identity
if (row.owner != ctx.sender()) {
return Err("unauthorized");
}
// Server timestamps
ctx.db[item].insert(Item{0, ctx.sender(), ctx.timestamp});
// Timestamp arithmetic
Timestamp later = ctx.timestamp + TimeDuration::from_seconds(10);Reducer Context
`ReducerContext` is the single source of sender identity, deterministic time, and deterministic randomness inside a reducer. Always go through `ctx` for these. Standard library clocks and random sources are not available in modules.
ctx.db[table] // Table access (bracket notation) ctx.sender() // Caller's Identity ctx.timestamp // Invocation timestamp ctx.connection_id // std::optional<ConnectionId> ctx.identity() // Module's own identity ctx.rng() // Deterministic RNG ctx.sender_auth() // AuthCtx with JWT claims
Scheduled Tables
struct Reminder {
uint64_t scheduled_id;
ScheduleAt scheduled_at;
std::string message;
};
SPACETIMEDB_STRUCT(Reminder, scheduled_id, scheduled_at, message)
SPACETIMEDB_TABLE(Reminder, reminder, Public)
FIELD_PrimaryKeyAutoInc(reminder, scheduled_id)
SPACETIMEDB_SCHEDULE(reminder, 1, send_reminder) // 1 = scheduled_at field index (0-based)
SPACETIMEDB_REDUCER(send_reminder, ReducerContext ctx, Reminder arg) {
LOG_INFO("Reminder: " + arg.message);
return Ok();
}
// One-time: fires at a specific time
ctx.db[reminder].insert(Reminder{0, ScheduleAt::time(ctx.timestamp + TimeDuration::from_seconds(10)), "msg"});
// Repeating: fires on an interval
ctx.db[reminder].insert(Reminder{0, ScheduleAt::interval(TimeDuration::from_seconds(5)), "msg"});Custom Types
// Struct (product type):
struct Point { float x; float y; };
SPACETIMEDB_STRUCT(Point, x, y)
// Enum (sum type):
SPACETIMEDB_UNIT_TYPE(Active)
SPACETIMEDB_UNIT_TYPE(Inactive)
SPACETIMEDB_ENUM(PlayerStatus,
(Active, Active),
(Inactive, InactiRepo: clockworklabs/spacetimedb
Other skills on spacetimedb.
- /cli
SpacetimeDB CLI reference for initializing projects, building modules, publishing databases, querying data, and managing servers
Open skill - /concepts
Understand SpacetimeDB architecture and core concepts. Use when learning SpacetimeDB or making architectural decisions.
Open skill - /csharp-client
SpacetimeDB C#/.NET client SDK reference. Use when building C# clients that connect to SpacetimeDB (console, desktop, or any .NET app).
Open skill - /csharp-server
SpacetimeDB C# server module SDK reference. Use when writing tables, reducers, or module logic in C#.
Open skill - /mcp
Operate a running SpacetimeDB database through MCP tools rather than the CLI - list databases, read schemas, run SQL, and call reducers. Use when the client exposes spacetimedb MCP tools and the task is to inspect or change data in a live database.
Open skill - /rust-server
SpacetimeDB Rust server module SDK reference. Use when writing tables, reducers, or module logic in Rust.
Open skill

