From a802e33062a63c433dd673d067e29ed93b355ef5 Mon Sep 17 00:00:00 2001 From: rain Date: Fri, 10 Jul 2026 08:10:50 -0400 Subject: [PATCH 01/35] docs: update delete-data flag examples --- .../00100-intro/00100-getting-started/00500-faq.md | 8 ++++---- .../00300-unity-tutorial/00300-part-2.md | 6 +++--- .../00300-unity-tutorial/00400-part-3.md | 8 ++++---- .../00300-unity-tutorial/00500-part-4.md | 4 ++-- .../00400-unreal-tutorial/00300-part-2.md | 6 +++--- .../00400-unreal-tutorial/00400-part-3.md | 10 +++++----- .../00400-unreal-tutorial/00500-part-4.md | 4 ++-- .../00500-godot-tutorial/00300-part-2.md | 6 +++--- .../00500-godot-tutorial/00400-part-3.md | 8 ++++---- .../00500-godot-tutorial/00500-part-4.md | 4 ++-- .../00100-databases/00300-spacetime-publish.md | 2 +- .../00500-migrations/00200-automatic-migrations.md | 4 ++-- .../00100-how-to/00100-deploy/00100-maincloud.md | 2 +- .../00100-cli-reference/00300-spacetime-json.md | 2 +- 14 files changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/docs/00100-intro/00100-getting-started/00500-faq.md b/docs/docs/00100-intro/00100-getting-started/00500-faq.md index abbbc177437..b96cd4f2f1f 100644 --- a/docs/docs/00100-intro/00100-getting-started/00500-faq.md +++ b/docs/docs/00100-intro/00100-getting-started/00500-faq.md @@ -221,7 +221,7 @@ conn.subscriptionBuilder().subscribe( ### How do I handle schema migrations? -When you publish an updated module, SpacetimeDB compares the new schema with the existing one and performs automatic migrations for compatible changes (adding tables, adding columns with defaults, etc.). For breaking changes, you may need to publish with `--delete-data` during development. See [Automatic Migrations](../../00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md) for details on what changes are supported. +When you publish an updated module, SpacetimeDB compares the new schema with the existing one and performs automatic migrations for compatible changes (adding tables, adding columns with defaults, etc.). For breaking changes, you may need to publish with `--delete-data=always` during development. See [Automatic Migrations](../../00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md) for details on what changes are supported. ### How do I add a column to an existing table? @@ -261,10 +261,10 @@ Yes. When you `spacetime publish` an update, SpacetimeDB hot-swaps the module co ### How do I clear my database? -Use the `-c` (or `--delete-data`) flag when publishing: +Use the `-c always` (or `--delete-data=always`) option when publishing: ```bash -spacetime publish my-app -c +spacetime publish my-app -c always ``` This deletes all data and re-runs the `init` reducer. @@ -291,7 +291,7 @@ If your module's `init` reducer panics (for example, due to a unique constraint ### How do I reset my database during development? -Use `spacetime publish my-app --delete-data` to clear all data and republish. Or use `spacetime dev`, which handles this automatically with the `--delete-data=on-conflict` flag. +Use `spacetime publish my-app --delete-data=always` to clear all data and republish. Or use `spacetime dev`, which handles this automatically with the `--delete-data=on-conflict` flag. ### I got a weird error when compiling my module! diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md index 033bd7ed2ea..6cf56341ba5 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md @@ -587,7 +587,7 @@ The `ReducerKind.ClientConnected` argument to the `SpacetimeDB.Reducer` attribut > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `ReducerKind.ClientConnected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `Sender` value of the `ReducerContext`. > - `ReducerKind.ClientDisconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -607,7 +607,7 @@ The `client_connected` argument to the `spacetimedb::reducer` macro indicates to > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `client_connected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `client_disconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -626,7 +626,7 @@ The `SPACETIMEDB_CLIENT_CONNECTED` macro indicates to SpacetimeDB that this is a > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `SPACETIMEDB_CLIENT_CONNECTED` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `SPACETIMEDB_CLIENT_DISCONNECTED` - Called when a user disconnects from the SpacetimeDB database. diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index fc5b6ae572c..570c5a50d0b 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -16,7 +16,7 @@ This progressive tutorial is continued from [part 2](./00300-part-2.md). -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `Connect` reducer. @@ -85,7 +85,7 @@ We also added two helper functions so we can get a random range as either a `int -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -161,7 +161,7 @@ In this reducer, we are using the `world_size` we configured along with the `Red -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -898,7 +898,7 @@ SPACETIMEDB_CLIENT_DISCONNECTED(disconnect, ReducerContext ctx) { Finally, publish the new module to SpacetimeDB with this command: ```sh -spacetime publish --server local blackholio --delete-data +spacetime publish --server local blackholio --delete-data=always ``` Deleting the data is optional in this case, but in case you've been messing around with the module we can just start fresh. diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md index bfc58fc0ded..bb7fb24b245 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md @@ -522,7 +522,7 @@ ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ Republish your module with: ```sh -spacetime publish --server local blackholio --delete-data +spacetime publish --server local blackholio --delete-data=always ``` Regenerate your server bindings with: @@ -861,7 +861,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca ## Connecting to Maincloud -- Publish to Maincloud `spacetime publish --server maincloud --delete-data` +- Publish to Maincloud `spacetime publish --server maincloud --delete-data=always` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). You will have to update the database name in `blackholio-server/spacetime.local.json` to match. - Update the URL in the Unity project to: `https://maincloud.spacetimedb.com` - Update the module name in the Unity project to ``. diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md index a592e63bfda..3d43ad8c38b 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md @@ -580,7 +580,7 @@ The `ReducerKind.ClientConnected` argument to the `SpacetimeDB.Reducer` attribut > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `ReducerKind.ClientConnected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `Sender` value of the `ReducerContext`. > - `ReducerKind.ClientDisconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -600,7 +600,7 @@ The `client_connected` argument to the `spacetimedb::reducer` macro indicates to > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `client_connected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `client_disconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -619,7 +619,7 @@ The `SPACETIMEDB_CLIENT_CONNECTED` macro indicates to SpacetimeDB that this is a > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `SPACETIMEDB_CLIENT_CONNECTED` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `SPACETIMEDB_CLIENT_DISCONNECTED` - Called when a user disconnects from the SpacetimeDB database. diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md index 1b388814d96..74d7bba4ced 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md @@ -16,7 +16,7 @@ This progressive tutorial is continued from [part 2](./00300-part-2.md). -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `Connect` reducer. @@ -85,7 +85,7 @@ We also added two helper functions so we can get a random range as either a `int -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -161,7 +161,7 @@ In this reducer, we are using the `world_size` we configured along with the `Red -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -894,13 +894,13 @@ SPACETIMEDB_CLIENT_DISCONNECTED(disconnect, ReducerContext ctx) { Finally, publish the new module to SpacetimeDB with this command: ```sh -spacetime publish --server local blackholio --delete-data +spacetime publish --server local blackholio --delete-data=always ``` Deleting the data is optional in this case, but in case you've been messing around with the module we can just start fresh. :::note -When using `--delete-data`, SpacetimeDB will prompt you to confirm the deletion. Enter **y** and press **Enter** to proceed. +When using `--delete-data=always`, SpacetimeDB will prompt you to confirm the deletion. Enter **y** and press **Enter** to proceed. ::: ### Creating the Arena diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md index 4cb494183c2..a6a26a08526 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md @@ -520,7 +520,7 @@ ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ Republish your module with: ```sh -spacetime publish --server local blackholio --delete-data +spacetime publish --server local blackholio --delete-data=always ``` Regenerate your server bindings with: @@ -917,7 +917,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca ## Connecting to Maincloud -- Publish to Maincloud `spacetime publish --server maincloud --delete-data` +- Publish to Maincloud `spacetime publish --server maincloud --delete-data=always` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). - Update the URL in the Unreal project to: `https://maincloud.spacetimedb.com` - Update the module name in the Unreal project to ``. diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md index 845219c5e26..e061919b0c6 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md @@ -586,7 +586,7 @@ The `ReducerKind.ClientConnected` argument to the `SpacetimeDB.Reducer` attribut > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `ReducerKind.ClientConnected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `Sender` value of the `ReducerContext`. > - `ReducerKind.ClientDisconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -606,7 +606,7 @@ The `client_connected` argument to the `spacetimedb::reducer` macro indicates to > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `client_connected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `client_disconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -625,7 +625,7 @@ The `SPACETIMEDB_CLIENT_CONNECTED` macro indicates to SpacetimeDB that this is a > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. +> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. > - `SPACETIMEDB_CLIENT_CONNECTED` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `SPACETIMEDB_CLIENT_DISCONNECTED` - Called when a user disconnects from the SpacetimeDB database. diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md index 0a4586a5216..2425f9b8d83 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md @@ -16,7 +16,7 @@ This progressive tutorial is continued from [part 2](./00300-part-2.md). -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `Connect` reducer. @@ -85,7 +85,7 @@ We also added two helper functions so we can get a random range as either a `int -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -161,7 +161,7 @@ In this reducer, we are using the `world_size` we configured along with the `Red -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -898,7 +898,7 @@ SPACETIMEDB_CLIENT_DISCONNECTED(disconnect, ReducerContext ctx) { Finally, publish the new module to SpacetimeDB with this command: ```sh -spacetime publish --server local blackholio --delete-data +spacetime publish --server local blackholio --delete-data=always ``` Deleting the data is optional in this case, but in case you've been messing around with the module we can just start fresh. diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md index 71eff102a9c..e8b55a1bdc7 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md @@ -522,7 +522,7 @@ ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ Republish your module with: ```sh -spacetime publish --server local blackholio --delete-data +spacetime publish --server local blackholio --delete-data=always ``` Regenerate your server bindings with: @@ -844,7 +844,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca ## Connecting to Maincloud -- Publish to Maincloud `spacetime publish --server maincloud --delete-data` +- Publish to Maincloud `spacetime publish --server maincloud --delete-data=always` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). You will have to update the database name in `blackholio-server/spacetime.local.json` to match. - Update the URL in the Main node to: `https://maincloud.spacetimedb.com` - Update the database name in the Main node to ``. diff --git a/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md b/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md index f8aaf847553..5e2f40b025c 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md +++ b/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md @@ -95,7 +95,7 @@ If this publish is a major upgrade from 1.x to 2.0, read [1.x to 2.0 Upgrade Not To completely reset your database and delete all data: ```bash -spacetime publish --delete-data +spacetime publish --delete-data=always ``` ⚠️ **Warning:** This permanently deletes all data in your database! diff --git a/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md b/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md index 4a29ff7465f..59306940097 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md +++ b/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md @@ -59,13 +59,13 @@ The following changes cannot be performed with automatic migration and will caus If you need to make changes that aren't supported by automatic migration, see [Incremental Migrations](./00300-incremental-migrations.md) for a production-ready pattern that allows complex schema changes without downtime or data loss. -For development and testing, you can use `spacetime publish --delete-data` to completely reset your database, but this should **not** be used in production as it permanently deletes all data. +For development and testing, you can use `spacetime publish --delete-data=always` to completely reset your database, but this should **not** be used in production as it permanently deletes all data. ## Best Practices ### During Development -- Use `--delete-data` freely during early development when data loss is acceptable +- Use `--delete-data=always` freely during early development when data loss is acceptable - Test migrations with sample data before applying to production databases - Consider creating separate databases for development, staging, and production diff --git a/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md b/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md index 53d2503338c..1a2dedfddef 100644 --- a/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md +++ b/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md @@ -41,7 +41,7 @@ To update an existing module, run the same command. SpacetimeDB hot-swaps the mo To clear all data and start fresh: ```bash -spacetime publish my-database --server maincloud --delete-data +spacetime publish my-database --server maincloud --delete-data=always ``` ## Connecting Clients to Maincloud diff --git a/docs/docs/00300-resources/00200-reference/00100-cli-reference/00300-spacetime-json.md b/docs/docs/00300-resources/00200-reference/00100-cli-reference/00300-spacetime-json.md index f14340c3337..0e90d315c80 100644 --- a/docs/docs/00300-resources/00200-reference/00100-cli-reference/00300-spacetime-json.md +++ b/docs/docs/00300-resources/00200-reference/00100-cli-reference/00300-spacetime-json.md @@ -211,7 +211,7 @@ These apply to all selected databases: - `--server`: target server - `--break-clients`: allow breaking changes -- `--delete-data`: clear database data +- `--delete-data=`: clear database data (`always`, `on-conflict`, or `never`) - `--yes` / `--force`: skip confirmation prompts ### Per-database overrides From c849ef6b7143d6e6a958632bcf22c578206972cc Mon Sep 17 00:00:00 2001 From: rain Date: Fri, 10 Jul 2026 10:42:03 -0400 Subject: [PATCH 02/35] docs: keep delete-data examples concise --- .../00100-intro/00100-getting-started/00500-faq.md | 8 ++++---- .../00300-unity-tutorial/00300-part-2.md | 6 +++--- .../00300-unity-tutorial/00400-part-3.md | 8 ++++---- .../00300-unity-tutorial/00500-part-4.md | 4 ++-- .../00400-unreal-tutorial/00300-part-2.md | 6 +++--- .../00400-unreal-tutorial/00400-part-3.md | 10 +++++----- .../00400-unreal-tutorial/00500-part-4.md | 4 ++-- .../00500-godot-tutorial/00300-part-2.md | 6 +++--- .../00500-godot-tutorial/00400-part-3.md | 8 ++++---- .../00500-godot-tutorial/00500-part-4.md | 4 ++-- .../00100-databases/00300-spacetime-publish.md | 6 +++++- .../00500-migrations/00200-automatic-migrations.md | 4 ++-- .../00100-how-to/00100-deploy/00100-maincloud.md | 2 +- skills/cli/SKILL.md | 4 ++++ 14 files changed, 44 insertions(+), 36 deletions(-) diff --git a/docs/docs/00100-intro/00100-getting-started/00500-faq.md b/docs/docs/00100-intro/00100-getting-started/00500-faq.md index b96cd4f2f1f..abbbc177437 100644 --- a/docs/docs/00100-intro/00100-getting-started/00500-faq.md +++ b/docs/docs/00100-intro/00100-getting-started/00500-faq.md @@ -221,7 +221,7 @@ conn.subscriptionBuilder().subscribe( ### How do I handle schema migrations? -When you publish an updated module, SpacetimeDB compares the new schema with the existing one and performs automatic migrations for compatible changes (adding tables, adding columns with defaults, etc.). For breaking changes, you may need to publish with `--delete-data=always` during development. See [Automatic Migrations](../../00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md) for details on what changes are supported. +When you publish an updated module, SpacetimeDB compares the new schema with the existing one and performs automatic migrations for compatible changes (adding tables, adding columns with defaults, etc.). For breaking changes, you may need to publish with `--delete-data` during development. See [Automatic Migrations](../../00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md) for details on what changes are supported. ### How do I add a column to an existing table? @@ -261,10 +261,10 @@ Yes. When you `spacetime publish` an update, SpacetimeDB hot-swaps the module co ### How do I clear my database? -Use the `-c always` (or `--delete-data=always`) option when publishing: +Use the `-c` (or `--delete-data`) flag when publishing: ```bash -spacetime publish my-app -c always +spacetime publish my-app -c ``` This deletes all data and re-runs the `init` reducer. @@ -291,7 +291,7 @@ If your module's `init` reducer panics (for example, due to a unique constraint ### How do I reset my database during development? -Use `spacetime publish my-app --delete-data=always` to clear all data and republish. Or use `spacetime dev`, which handles this automatically with the `--delete-data=on-conflict` flag. +Use `spacetime publish my-app --delete-data` to clear all data and republish. Or use `spacetime dev`, which handles this automatically with the `--delete-data=on-conflict` flag. ### I got a weird error when compiling my module! diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md index 6cf56341ba5..033bd7ed2ea 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md @@ -587,7 +587,7 @@ The `ReducerKind.ClientConnected` argument to the `SpacetimeDB.Reducer` attribut > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `ReducerKind.ClientConnected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `Sender` value of the `ReducerContext`. > - `ReducerKind.ClientDisconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -607,7 +607,7 @@ The `client_connected` argument to the `spacetimedb::reducer` macro indicates to > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `client_connected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `client_disconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -626,7 +626,7 @@ The `SPACETIMEDB_CLIENT_CONNECTED` macro indicates to SpacetimeDB that this is a > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `SPACETIMEDB_CLIENT_CONNECTED` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `SPACETIMEDB_CLIENT_DISCONNECTED` - Called when a user disconnects from the SpacetimeDB database. diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index 570c5a50d0b..fc5b6ae572c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -16,7 +16,7 @@ This progressive tutorial is continued from [part 2](./00300-part-2.md). -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `Connect` reducer. @@ -85,7 +85,7 @@ We also added two helper functions so we can get a random range as either a `int -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -161,7 +161,7 @@ In this reducer, we are using the `world_size` we configured along with the `Red -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -898,7 +898,7 @@ SPACETIMEDB_CLIENT_DISCONNECTED(disconnect, ReducerContext ctx) { Finally, publish the new module to SpacetimeDB with this command: ```sh -spacetime publish --server local blackholio --delete-data=always +spacetime publish --server local blackholio --delete-data ``` Deleting the data is optional in this case, but in case you've been messing around with the module we can just start fresh. diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md index bb7fb24b245..bfc58fc0ded 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md @@ -522,7 +522,7 @@ ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ Republish your module with: ```sh -spacetime publish --server local blackholio --delete-data=always +spacetime publish --server local blackholio --delete-data ``` Regenerate your server bindings with: @@ -861,7 +861,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca ## Connecting to Maincloud -- Publish to Maincloud `spacetime publish --server maincloud --delete-data=always` +- Publish to Maincloud `spacetime publish --server maincloud --delete-data` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). You will have to update the database name in `blackholio-server/spacetime.local.json` to match. - Update the URL in the Unity project to: `https://maincloud.spacetimedb.com` - Update the module name in the Unity project to ``. diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md index 3d43ad8c38b..a592e63bfda 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md @@ -580,7 +580,7 @@ The `ReducerKind.ClientConnected` argument to the `SpacetimeDB.Reducer` attribut > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `ReducerKind.ClientConnected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `Sender` value of the `ReducerContext`. > - `ReducerKind.ClientDisconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -600,7 +600,7 @@ The `client_connected` argument to the `spacetimedb::reducer` macro indicates to > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `client_connected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `client_disconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -619,7 +619,7 @@ The `SPACETIMEDB_CLIENT_CONNECTED` macro indicates to SpacetimeDB that this is a > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `SPACETIMEDB_CLIENT_CONNECTED` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `SPACETIMEDB_CLIENT_DISCONNECTED` - Called when a user disconnects from the SpacetimeDB database. diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md index 74d7bba4ced..1b388814d96 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md @@ -16,7 +16,7 @@ This progressive tutorial is continued from [part 2](./00300-part-2.md). -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `Connect` reducer. @@ -85,7 +85,7 @@ We also added two helper functions so we can get a random range as either a `int -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -161,7 +161,7 @@ In this reducer, we are using the `world_size` we configured along with the `Red -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -894,13 +894,13 @@ SPACETIMEDB_CLIENT_DISCONNECTED(disconnect, ReducerContext ctx) { Finally, publish the new module to SpacetimeDB with this command: ```sh -spacetime publish --server local blackholio --delete-data=always +spacetime publish --server local blackholio --delete-data ``` Deleting the data is optional in this case, but in case you've been messing around with the module we can just start fresh. :::note -When using `--delete-data=always`, SpacetimeDB will prompt you to confirm the deletion. Enter **y** and press **Enter** to proceed. +When using `--delete-data`, SpacetimeDB will prompt you to confirm the deletion. Enter **y** and press **Enter** to proceed. ::: ### Creating the Arena diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md index a6a26a08526..4cb494183c2 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md @@ -520,7 +520,7 @@ ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ Republish your module with: ```sh -spacetime publish --server local blackholio --delete-data=always +spacetime publish --server local blackholio --delete-data ``` Regenerate your server bindings with: @@ -917,7 +917,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca ## Connecting to Maincloud -- Publish to Maincloud `spacetime publish --server maincloud --delete-data=always` +- Publish to Maincloud `spacetime publish --server maincloud --delete-data` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). - Update the URL in the Unreal project to: `https://maincloud.spacetimedb.com` - Update the module name in the Unreal project to ``. diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md index e061919b0c6..845219c5e26 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00300-part-2.md @@ -586,7 +586,7 @@ The `ReducerKind.ClientConnected` argument to the `SpacetimeDB.Reducer` attribut > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `ReducerKind.Init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `ReducerKind.ClientConnected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `Sender` value of the `ReducerContext`. > - `ReducerKind.ClientDisconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -606,7 +606,7 @@ The `client_connected` argument to the `spacetimedb::reducer` macro indicates to > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `init` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `client_connected` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `client_disconnected` - Called when a user disconnects from the SpacetimeDB database. @@ -625,7 +625,7 @@ The `SPACETIMEDB_CLIENT_CONNECTED` macro indicates to SpacetimeDB that this is a > SpacetimeDB gives you the ability to define custom reducers that automatically trigger when certain events occur. > -> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data=always`. +> - `SPACETIMEDB_INIT` - Called the first time you publish your module and anytime you clear the database with `spacetime publish --server local --delete-data`. > - `SPACETIMEDB_CLIENT_CONNECTED` - Called when a user connects to the SpacetimeDB database. Their identity can be found in the `sender` value of the `ReducerContext`. > - `SPACETIMEDB_CLIENT_DISCONNECTED` - Called when a user disconnects from the SpacetimeDB database. diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md index 2425f9b8d83..0a4586a5216 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md @@ -16,7 +16,7 @@ This progressive tutorial is continued from [part 2](./00300-part-2.md). -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `Init` reducer. SpacetimeDB calls the `Init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `Connect` reducer. @@ -85,7 +85,7 @@ We also added two helper functions so we can get a random range as either a `int -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `init` reducer. SpacetimeDB calls the `init` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -161,7 +161,7 @@ In this reducer, we are using the `world_size` we configured along with the `Red -Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data=always`. It gives you an opportunity to initialize the state of your database before any clients connect. +Let's start by spawning food into the map. The first thing we need to do is create a new, special reducer called the `SPACETIMEDB_INIT` reducer. SpacetimeDB calls the `SPACETIMEDB_INIT` reducer automatically when you first publish your module, and also after any time you run with `publish --delete-data`. It gives you an opportunity to initialize the state of your database before any clients connect. Add this new reducer above our `connect` reducer. @@ -898,7 +898,7 @@ SPACETIMEDB_CLIENT_DISCONNECTED(disconnect, ReducerContext ctx) { Finally, publish the new module to SpacetimeDB with this command: ```sh -spacetime publish --server local blackholio --delete-data=always +spacetime publish --server local blackholio --delete-data ``` Deleting the data is optional in this case, but in case you've been messing around with the module we can just start fresh. diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md index e8b55a1bdc7..71eff102a9c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md @@ -522,7 +522,7 @@ ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ Republish your module with: ```sh -spacetime publish --server local blackholio --delete-data=always +spacetime publish --server local blackholio --delete-data ``` Regenerate your server bindings with: @@ -844,7 +844,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca ## Connecting to Maincloud -- Publish to Maincloud `spacetime publish --server maincloud --delete-data=always` +- Publish to Maincloud `spacetime publish --server maincloud --delete-data` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). You will have to update the database name in `blackholio-server/spacetime.local.json` to match. - Update the URL in the Main node to: `https://maincloud.spacetimedb.com` - Update the database name in the Main node to ``. diff --git a/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md b/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md index 5e2f40b025c..dd0402479a4 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md +++ b/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md @@ -94,8 +94,12 @@ If this publish is a major upgrade from 1.x to 2.0, read [1.x to 2.0 Upgrade Not To completely reset your database and delete all data: + + ```bash -spacetime publish --delete-data=always +spacetime publish --delete-data ``` ⚠️ **Warning:** This permanently deletes all data in your database! diff --git a/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md b/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md index 59306940097..4a29ff7465f 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md +++ b/docs/docs/00200-core-concepts/00100-databases/00500-migrations/00200-automatic-migrations.md @@ -59,13 +59,13 @@ The following changes cannot be performed with automatic migration and will caus If you need to make changes that aren't supported by automatic migration, see [Incremental Migrations](./00300-incremental-migrations.md) for a production-ready pattern that allows complex schema changes without downtime or data loss. -For development and testing, you can use `spacetime publish --delete-data=always` to completely reset your database, but this should **not** be used in production as it permanently deletes all data. +For development and testing, you can use `spacetime publish --delete-data` to completely reset your database, but this should **not** be used in production as it permanently deletes all data. ## Best Practices ### During Development -- Use `--delete-data=always` freely during early development when data loss is acceptable +- Use `--delete-data` freely during early development when data loss is acceptable - Test migrations with sample data before applying to production databases - Consider creating separate databases for development, staging, and production diff --git a/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md b/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md index 1a2dedfddef..53d2503338c 100644 --- a/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md +++ b/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md @@ -41,7 +41,7 @@ To update an existing module, run the same command. SpacetimeDB hot-swaps the mo To clear all data and start fresh: ```bash -spacetime publish my-database --server maincloud --delete-data=always +spacetime publish my-database --server maincloud --delete-data ``` ## Connecting Clients to Maincloud diff --git a/skills/cli/SKILL.md b/skills/cli/SKILL.md index 534ad7ee4ec..f5cd3479128 100644 --- a/skills/cli/SKILL.md +++ b/skills/cli/SKILL.md @@ -63,6 +63,10 @@ spacetime publish my-database --server local --yes spacetime publish my-database --delete-data=always --yes ``` +Bare `--delete-data` defaults to `always`. Keep simple interactive docs examples +bare, and use `--delete-data=always` for scripted/non-interactive examples that +also pass `--yes`. + ### Database Interaction ```bash From 4794bd4472b330fcfb92c5d229e567e5a8ef9bd9 Mon Sep 17 00:00:00 2001 From: rain Date: Sat, 11 Jul 2026 08:10:16 -0400 Subject: [PATCH 03/35] docs: clarify C# reducer result callbacks --- .../00600-clients/00600-csharp-reference.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md index 0db7e730c91..264ce97c797 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md @@ -306,14 +306,14 @@ interface IRemoteDbContext ``` `Reducers` will have methods to invoke each reducer defined by the module, -plus methods for adding and removing callbacks on each of those reducers. +plus events for observing the result of reducer calls made by this connection. ##### Example ```csharp var conn = ConnectToDB(); -// Register a callback to be run every time the SendMessage reducer is invoked +// Register a callback to observe the result of SendMessage calls made by this connection. conn.Reducers.OnSendMessage += Reducer_OnSendMessageEvent; ``` @@ -711,9 +711,9 @@ record Event } ``` -Event when we are notified that a reducer ran in the remote database. The [`ReducerEvent`](#record-reducerevent) contains metadata about the reducer run, including its arguments and termination [`Status`](#record-status). +Event when we are notified of the result of a reducer call made by this connection. The [`ReducerEvent`](#record-reducerevent) contains metadata about the reducer run, including its arguments and termination [`Status`](#record-status). -This event is passed to row callbacks resulting from modifications by the reducer. +For changes caused by other clients' reducer calls, use table row callbacks or event tables rather than reducer callbacks. The server does not broadcast reducer arguments globally. #### Variant `SubscribeApplied` @@ -1084,13 +1084,14 @@ int CountPlayersAtLevel(RemoteTables tables, uint level) => tables.Player.Level. ## Observe and invoke reducers -All [`IDbContext`](#interface-idbcontext) implementors, including [`DbConnection`](#type-dbconnection) and [`EventContext`](#type-eventcontext), have a `.Reducers` property, which in turn has methods for invoking reducers defined by the module and registering callbacks on it. +All [`IDbContext`](#interface-idbcontext) implementors, including [`DbConnection`](#type-dbconnection) and [`EventContext`](#type-eventcontext), have a `.Reducers` property. Generated module bindings expose one invoke method and one result event for each reducer. -Each reducer defined by the module has three methods on the `.Reducers`: +For a reducer named `send_message`, generated C# bindings use PascalCase names: -- An invoke method, whose name is the reducer's name converted to snake case, like `set_name`. This requests that the module run the reducer. -- A callback registation method, whose name is prefixed with `on_`, like `on_set_name`. This registers a callback to run whenever we are notified that the reducer ran, including successfully committed runs and runs we requested which failed. This method returns a callback id, which can be passed to the callback remove method. -- A callback remove method, whose name is prefixed with `remove_on_`, like `remove_on_set_name`. This cancels a callback previously registered via the callback registration method. +- An invoke method, like `SendMessage(...)`. This requests that the module run the reducer. +- A result event, like `OnSendMessage`. This event fires on the calling connection when SpacetimeDB reports that reducer call's result, including committed, failed, and out-of-energy statuses. + +Reducer result events are not global notifications. They are for reducer calls made by this connection. To notify other clients that something happened, write to a public table or event table and subscribe to it. ## Identify a client From 7a9dc228504a884518810ae8a9aaaf69a6074a6e Mon Sep 17 00:00:00 2001 From: rain Date: Sun, 12 Jul 2026 08:10:29 -0400 Subject: [PATCH 04/35] docs: align TypeScript skill table naming guidance --- skills/typescript-server/SKILL.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/skills/typescript-server/SKILL.md b/skills/typescript-server/SKILL.md index e71e17a1290..83c51365d3b 100644 --- a/skills/typescript-server/SKILL.md +++ b/skills/typescript-server/SKILL.md @@ -23,7 +23,8 @@ import { ScheduleAt } from 'spacetimedb'; // for scheduled tables only ## Tables -`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field MUST be snake_case: +`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field is optional; +when present, it overrides the canonical SQL name and should be snake_case: ```typescript const entity = table( @@ -36,9 +37,11 @@ const entity = table( ); ``` -Options: `name` (snake_case, recommended), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` +Options: `name` (optional canonical SQL name override), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` -`ctx.db` accessors are the camelCase form of the table's `name` field. +`ctx.db` accessors come from the key passed to `schema({ ... })`, not from the +`name` override. If `name` is omitted, the canonical SQL name is derived from the +schema key. ## Column Types From 47b823cb8005a54ab82039043b6a26023241ea41 Mon Sep 17 00:00:00 2001 From: rain Date: Wed, 15 Jul 2026 08:10:37 -0400 Subject: [PATCH 05/35] docs: align TypeScript table accessor examples --- .../00200-functions/00300-reducers/00300-reducers.md | 8 ++++---- .../00300-reducers/00400-reducer-context.md | 6 +++--- .../00300-tables/00210-file-storage.md | 8 ++++---- .../00300-tables/00550-event-tables.md | 7 ++++--- .../00100-how-to/00600-migrating-to-2.0.md | 8 ++++---- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md index 4180d28ba6d..6238cf8eab4 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md @@ -546,7 +546,7 @@ import { ScheduleAt } from 'spacetimedb'; import { schema, t, table } from 'spacetimedb/server'; // Define a schedule table for the procedure -const fetchSchedule = table( +const fetch_schedule = table( { name: 'fetch_schedule', scheduled: (): any => fetch_external_data }, { scheduled_id: t.u64().primaryKey().autoInc(), @@ -555,12 +555,12 @@ const fetchSchedule = table( } ); -const spacetimedb = schema({ fetchSchedule }); +const spacetimedb = schema({ fetch_schedule }); export default spacetimedb; // The procedure to be scheduled export const fetch_external_data = spacetimedb.procedure( - { arg: fetchSchedule.rowType }, + { arg: fetch_schedule.rowType }, t.unit(), (ctx, { arg }) => { const response = ctx.http.fetch(arg.url); @@ -571,7 +571,7 @@ export const fetch_external_data = spacetimedb.procedure( // From a reducer, schedule the procedure by inserting into the schedule table export const queueFetch = spacetimedb.reducer({ url: t.string() }, (ctx, { url }) => { - ctx.db.fetchSchedule.insert({ + ctx.db.fetch_schedule.insert({ scheduled_id: 0n, scheduled_at: ScheduleAt.interval(0n), // Run immediately url, diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index 06e30fb7b2a..e45f9174561 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -324,7 +324,7 @@ Scheduled reducers and procedures are private by default in SpacetimeDB 2.x, so ```typescript import { schema, table, t } from 'spacetimedb/server'; -const scheduledTask = table( +const scheduled_task = table( { name: 'scheduled_task', scheduled: (): any => send_reminder }, { taskId: t.u64().primaryKey().autoInc(), @@ -333,10 +333,10 @@ const scheduledTask = table( } ); -const spacetimedb = schema({ scheduledTask }); +const spacetimedb = schema({ scheduled_task }); export default spacetimedb; -export const send_reminder = spacetimedb.reducer({ arg: scheduledTask.rowType }, (_ctx, { arg }) => { +export const send_reminder = spacetimedb.reducer({ arg: scheduled_task.rowType }, (_ctx, { arg }) => { console.log(`Reminder: ${arg.message}`); }); ``` diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index 86c1589ea46..9b88b2de7d8 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -20,7 +20,7 @@ Store binary data using `Vec` (Rust), `List` (C#), `std::vector { // Delete existing avatar if present - ctx.db.userAvatar.userId.delete(userId); + ctx.db.user_avatar.userId.delete(userId); // Insert new avatar - ctx.db.userAvatar.insert({ + ctx.db.user_avatar.insert({ userId, mimeType, data, diff --git a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md index 3f2936ca7ea..9bcdc1e5b52 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md @@ -21,7 +21,8 @@ To declare a table as an event table, add the `event` attribute to the table def ```typescript -const damageEvent = table({ +const damage_event = table({ + name: 'damage_event', public: true, event: true, }, { @@ -31,7 +32,7 @@ const damageEvent = table({ }); const spacetimedb = schema({ - damageEvent, + damage_event, }); export default spacetimedb; ``` @@ -97,7 +98,7 @@ export const attack = spacetimedb.reducer( // Game logic... // Publish the event - ctx.db.damageEvent.insert({ + ctx.db.damage_event.insert({ entity_id: target_id, damage, source: "melee_attack", diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index 8f70388d274..aef29ff65f9 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -190,15 +190,15 @@ spacetimedb.reducer('deal_damage', { target: t.identity(), amount: t.u32() }, (c **Server (module) -- after:** ```typescript // 2.0 server -- explicitly publish events via an event table -const damageEvent = table({ event: true }, { +const damage_event = table({ name: 'damage_event', event: true }, { target: t.identity(), amount: t.u32(), }) -// schema() takes an object: schema({ damageEvent }), never schema(damageEvent) -const spacetimedb = schema({ damageEvent }); +// schema() takes an object: schema({ damage_event }), never schema(damage_event) +const spacetimedb = schema({ damage_event }); export const dealDamage = spacetimedb.reducer({ target: t.identity(), amount: t.u32() }, (ctx, { target, amount }) => { - ctx.db.damageEvent.insert({ target, amount }); + ctx.db.damage_event.insert({ target, amount }); }); ``` From 9dc58c7c2ff06fd85b84c14fe155f43a26938b9e Mon Sep 17 00:00:00 2001 From: rain Date: Fri, 17 Jul 2026 08:10:43 -0400 Subject: [PATCH 06/35] docs: align scheduled function examples --- .../00300-reducers/00400-reducer-context.md | 12 +++++----- .../00100-how-to/00600-migrating-to-2.0.md | 22 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index e45f9174561..0db4503a469 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -327,8 +327,8 @@ import { schema, table, t } from 'spacetimedb/server'; const scheduled_task = table( { name: 'scheduled_task', scheduled: (): any => send_reminder }, { - taskId: t.u64().primaryKey().autoInc(), - scheduledAt: t.scheduleAt(), + task_id: t.u64().primaryKey().autoInc(), + scheduled_at: t.scheduleAt(), message: t.string(), } ); @@ -349,14 +349,14 @@ using SpacetimeDB; public static partial class Module { - [SpacetimeDB.Table(Accessor = "ScheduledTask", Scheduled = nameof(SendReminder))] + [SpacetimeDB.Table(Accessor = "ScheduledTask", Scheduled = nameof(SendReminder), ScheduledAt = nameof(ScheduledAt))] public partial struct ScheduledTask { [SpacetimeDB.PrimaryKey] [SpacetimeDB.AutoInc] - public ulong taskId; - public ScheduleAt scheduledAt; - public string message; + public ulong TaskId; + public ScheduleAt ScheduledAt; + public string Message; } [SpacetimeDB.Reducer] diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index aef29ff65f9..bb91562f2b4 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -1366,14 +1366,14 @@ spacetimedb.reducer('runMyTimer', myTimer.rowType, (ctx, timer) => { ``` ```typescript -const myTimer = table({ scheduled: () => runMyTimer }, { - scheduledId: t.u64().primaryKey().autoInc(), - scheduledAt: t.scheduleAt(), +const my_timer = table({ name: "my_timer", scheduled: (): any => runMyTimer }, { + scheduled_id: t.u64().primaryKey().autoInc(), + scheduled_at: t.scheduleAt(), }); -const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) // 2.0 -- Can only be called by the database -export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { // Do stuff }) ``` @@ -1479,17 +1479,17 @@ In the rare event that you have a reducer or procedure which is intended to be i ```typescript -const myTimer = table({ scheduled: () => runMyTimerPrivate }, { - scheduledId: t.u64().primaryKey().autoInc(), - scheduledAt: t.scheduleAt(), +const my_timer = table({ name: "my_timer", scheduled: (): any => runMyTimerPrivate }, { + scheduled_id: t.u64().primaryKey().autoInc(), + scheduled_at: t.scheduleAt(), }); -const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) -export const runMyTimerPrivate = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { +export const runMyTimerPrivate = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { // Do stuff... }); -export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { // Same logic as runMyTimerPrivate — extract to a helper if needed }); ``` From 2caabd69fa1f9fa6acb1e44ee970d2da04dace9d Mon Sep 17 00:00:00 2001 From: rain Date: Sat, 18 Jul 2026 08:10:15 -0400 Subject: [PATCH 07/35] docs: update procedure language support overview --- .../00400-key-architecture.md | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md index 487b962f4ad..702e893bfea 100644 --- a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md +++ b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md @@ -345,9 +345,21 @@ ctx.procedures.makeRequest().then( -C# modules currently cannot define procedures. Support for defining procedures in C# modules will be released shortly. +C# modules can define procedures, but procedure support is currently unstable. +To use it, add `#pragma warning disable STDB_UNSTABLE` at the top of your file. -A C# [client](#client) can call a procedure defined by a Rust or TypeScript module: +```csharp +#pragma warning disable STDB_UNSTABLE + +[SpacetimeDB.Procedure] +public static string MakeRequest(ProcedureContext ctx) +{ + // ... + return "result"; +} +``` + +A C# [client](#client) can call a procedure defined by a module: ```csharp void Main() @@ -384,7 +396,7 @@ Because procedures are unstable, Rust modules that define them must opt in to th ```toml [dependencies] -spacetimedb = { version = "1.x", features = ["unstable"] } +spacetimedb = { version = "2.*", features = ["unstable"] } ``` Then, that module can define a procedure: @@ -436,7 +448,7 @@ Use the other tabs (TypeScript/C#/Rust/Unreal C++/Blueprint) for client call exa -An Unreal C++ [client](#client) can call a procedure defined by a Rust or TypeScript module: +An Unreal C++ [client](#client) can call a procedure defined by a module: ```cpp { @@ -468,7 +480,7 @@ void AGameManager::OnMakeRequestComplete(const FProcedureEventContext& Context, -An Unreal [client](#client) can call a procedure defined by a Rust or TypeScript module: +An Unreal [client](#client) can call a procedure defined by a module: ![MakeRequest without callback](/images/unreal/intro/ue-blueprint-makerequest-nocallback.png) From a129e9cd5813068fc99442be801fc864a26bfc69 Mon Sep 17 00:00:00 2001 From: rain Date: Sun, 19 Jul 2026 08:09:37 -0400 Subject: [PATCH 08/35] docs: clarify Unreal uproject-dir path --- .../00300-tutorials/00400-unreal-tutorial/00300-part-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md index a592e63bfda..c3113c50bf4 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md @@ -655,7 +655,7 @@ spacetime generate --lang unrealcpp --uproject-dir .. --unreal-module-name black This will generate a set of files in the `blackholio/Source/blackholio/Private/ModuleBindings` and `blackholio/Source/blackholio/Public/ModuleBindings` directories which contain the code generated types and reducer functions that are defined in your module, but usable on the client. :::note -`--uproject-dir` is straightforward as the path to the .uproject file. `--unreal-module-name` is the name of the Unreal module which in most projects is the name of the project, in this case `blackholio`. +`--uproject-dir` is the path to the Unreal project directory that contains the `.uproject` file. `--unreal-module-name` is the name of the Unreal module, which in most projects is the name of the project, in this case `blackholio`. ::: :::warning From fab18a53cdb8bc089765c4b6f1e857a2598884e3 Mon Sep 17 00:00:00 2001 From: rain Date: Mon, 20 Jul 2026 08:10:51 -0400 Subject: [PATCH 09/35] docs: update Rust and C++ sender examples --- .../00300-tutorials/00300-unity-tutorial/00400-part-3.md | 6 +++--- .../00300-tutorials/00500-godot-tutorial/00400-part-3.md | 6 +++--- .../docs/00200-core-concepts/00200-functions/00500-views.md | 3 +-- .../00200-core-concepts/00300-tables/00210-file-storage.md | 6 +++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index fc5b6ae572c..01a99bcfc91 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -721,7 +721,7 @@ const START_PLAYER_MASS: i32 = 15; #[spacetimedb::reducer] pub fn enter_game(ctx: &ReducerContext, name: String) -> Result<(), String> { log::info!("Creating player with name {}", name); - let mut player: Player = ctx.db.player().identity().find(ctx.sender).ok_or("")?; + let mut player: Player = ctx.db.player().identity().find(ctx.sender()).ok_or("")?; let player_id = player.player_id; player.name = name; ctx.db.player().identity().update(player); @@ -786,11 +786,11 @@ pub fn disconnect(ctx: &ReducerContext) -> Result<(), String> { .db .player() .identity() - .find(&ctx.sender) + .find(&ctx.sender()) .ok_or("Player not found")?; let player_id = player.player_id; ctx.db.logged_out_player().insert(player); - ctx.db.player().identity().delete(&ctx.sender); + ctx.db.player().identity().delete(&ctx.sender()); // Remove any circles from the arena for circle in ctx.db.circle().player_id().filter(&player_id) { diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md index 0a4586a5216..b5e3e4451fa 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md @@ -721,7 +721,7 @@ const START_PLAYER_MASS: i32 = 15; #[spacetimedb::reducer] pub fn enter_game(ctx: &ReducerContext, name: String) -> Result<(), String> { log::info!("Creating player with name {}", name); - let mut player: Player = ctx.db.player().identity().find(ctx.sender).ok_or("")?; + let mut player: Player = ctx.db.player().identity().find(ctx.sender()).ok_or("")?; let player_id = player.player_id; player.name = name; ctx.db.player().identity().update(player); @@ -786,11 +786,11 @@ pub fn disconnect(ctx: &ReducerContext) -> Result<(), String> { .db .player() .identity() - .find(&ctx.sender) + .find(&ctx.sender()) .ok_or("Player not found")?; let player_id = player.player_id; ctx.db.logged_out_player().insert(player); - ctx.db.player().identity().delete(&ctx.sender); + ctx.db.player().identity().delete(&ctx.sender()); // Remove any circles from the arena for circle in ctx.db.circle().player_id().filter(&player_id) { diff --git a/docs/docs/00200-core-concepts/00200-functions/00500-views.md b/docs/docs/00200-core-concepts/00200-functions/00500-views.md index 89c2e1c2931..c6c1ade084f 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00500-views.md +++ b/docs/docs/00200-core-concepts/00200-functions/00500-views.md @@ -167,8 +167,7 @@ Views must be static methods and can return either a single row (`T?`) or multip Use the `#[spacetimedb::view]` macro on a function: ```rust -use spacetimedb::{view, ViewContext, AnonymousViewContext, table, SpacetimeType}; -use spacetimedb_lib::Identity; +use spacetimedb::{view, ViewContext, AnonymousViewContext, table, SpacetimeType, Identity}; #[spacetimedb::table(accessor = player)] pub struct Player { diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index 9b88b2de7d8..bdab79ed169 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -332,7 +332,7 @@ SPACETIMEDB_REDUCER(register_document, ReducerContext ctx, std::string filename, std::string mime_type, uint64_t size_bytes, std::string storage_url) { ctx.db[document].insert(Document{ .id = 0, // auto-increment - .owner_id = ctx.sender, + .owner_id = ctx.sender(), .filename = filename, .mime_type = mime_type, .size_bytes = size_bytes, @@ -579,7 +579,7 @@ pub fn upload_to_s3( ctx.with_tx(|tx_ctx| { tx_ctx.db.document().insert(Document { id: 0, - owner_id: tx_ctx.sender, + owner_id: tx_ctx.sender(), filename: filename_clone.clone(), s3_key: s3_key_clone.clone(), uploaded_at: tx_ctx.timestamp, @@ -719,7 +719,7 @@ pub fn get_upload_url( pub fn confirm_upload(ctx: &ReducerContext, filename: String, s3_key: String) { ctx.db.document().insert(Document { id: 0, - owner_id: ctx.sender, + owner_id: ctx.sender(), filename, s3_key, uploaded_at: ctx.timestamp, From 03cc8c5a1782ce594b157b0776acd24596bae1c7 Mon Sep 17 00:00:00 2001 From: rain Date: Tue, 21 Jul 2026 08:10:14 -0400 Subject: [PATCH 10/35] Fix Rust codegen bindings path in docs --- docs/docs/00200-core-concepts/00600-clients/00200-codegen.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md b/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md index f380a574a68..acc6e7ef221 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md +++ b/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md @@ -57,10 +57,10 @@ Replace **PATH-TO-MODULE-DIRECTORY** with the path to your module's directory, w ```bash mkdir -p src/module_bindings -spacetime generate --lang rust --out-dir client/src/module_bindings --module-path PATH-TO-MODULE-DIRECTORY +spacetime generate --lang rust --out-dir src/module_bindings --module-path PATH-TO-MODULE-DIRECTORY ``` -This generates Rust files in `client/src/module_bindings/`. Import them in your client with: +This generates Rust files in `src/module_bindings/`. Import them in your client with: ```rust mod module_bindings; From f73fb9a7eba7f1a9fa75effc2822c46af7db5377 Mon Sep 17 00:00:00 2001 From: rain Date: Tue, 21 Jul 2026 14:21:42 -0400 Subject: [PATCH 11/35] docs: address audit PR review comments --- .../00400-key-architecture.md | 5 +--- .../00300-spacetime-publish.md | 4 --- .../00300-reducers/00300-reducers.md | 8 ++--- .../00300-reducers/00400-reducer-context.md | 10 +++---- .../00300-tables/00210-file-storage.md | 8 ++--- .../00100-how-to/00600-migrating-to-2.0.md | 30 +++++++++---------- 6 files changed, 29 insertions(+), 36 deletions(-) diff --git a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md index 702e893bfea..10ba131faef 100644 --- a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md +++ b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md @@ -345,12 +345,9 @@ ctx.procedures.makeRequest().then( -C# modules can define procedures, but procedure support is currently unstable. -To use it, add `#pragma warning disable STDB_UNSTABLE` at the top of your file. +C# modules can define procedures: ```csharp -#pragma warning disable STDB_UNSTABLE - [SpacetimeDB.Procedure] public static string MakeRequest(ProcedureContext ctx) { diff --git a/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md b/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md index dd0402479a4..f8aaf847553 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md +++ b/docs/docs/00200-core-concepts/00100-databases/00300-spacetime-publish.md @@ -94,10 +94,6 @@ If this publish is a major upgrade from 1.x to 2.0, read [1.x to 2.0 Upgrade Not To completely reset your database and delete all data: - - ```bash spacetime publish --delete-data ``` diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md index 6238cf8eab4..4180d28ba6d 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md @@ -546,7 +546,7 @@ import { ScheduleAt } from 'spacetimedb'; import { schema, t, table } from 'spacetimedb/server'; // Define a schedule table for the procedure -const fetch_schedule = table( +const fetchSchedule = table( { name: 'fetch_schedule', scheduled: (): any => fetch_external_data }, { scheduled_id: t.u64().primaryKey().autoInc(), @@ -555,12 +555,12 @@ const fetch_schedule = table( } ); -const spacetimedb = schema({ fetch_schedule }); +const spacetimedb = schema({ fetchSchedule }); export default spacetimedb; // The procedure to be scheduled export const fetch_external_data = spacetimedb.procedure( - { arg: fetch_schedule.rowType }, + { arg: fetchSchedule.rowType }, t.unit(), (ctx, { arg }) => { const response = ctx.http.fetch(arg.url); @@ -571,7 +571,7 @@ export const fetch_external_data = spacetimedb.procedure( // From a reducer, schedule the procedure by inserting into the schedule table export const queueFetch = spacetimedb.reducer({ url: t.string() }, (ctx, { url }) => { - ctx.db.fetch_schedule.insert({ + ctx.db.fetchSchedule.insert({ scheduled_id: 0n, scheduled_at: ScheduleAt.interval(0n), // Run immediately url, diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index 0db4503a469..4a3e68d358e 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -324,19 +324,19 @@ Scheduled reducers and procedures are private by default in SpacetimeDB 2.x, so ```typescript import { schema, table, t } from 'spacetimedb/server'; -const scheduled_task = table( +const scheduledTask = table( { name: 'scheduled_task', scheduled: (): any => send_reminder }, { - task_id: t.u64().primaryKey().autoInc(), - scheduled_at: t.scheduleAt(), + taskId: t.u64().primaryKey().autoInc(), + scheduledAt: t.scheduleAt(), message: t.string(), } ); -const spacetimedb = schema({ scheduled_task }); +const spacetimedb = schema({ scheduledTask }); export default spacetimedb; -export const send_reminder = spacetimedb.reducer({ arg: scheduled_task.rowType }, (_ctx, { arg }) => { +export const send_reminder = spacetimedb.reducer({ arg: scheduledTask.rowType }, (_ctx, { arg }) => { console.log(`Reminder: ${arg.message}`); }); ``` diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index bdab79ed169..ba5f8b5f2fe 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -20,7 +20,7 @@ Store binary data using `Vec` (Rust), `List` (C#), `std::vector { // Delete existing avatar if present - ctx.db.user_avatar.userId.delete(userId); + ctx.db.userAvatar.userId.delete(userId); // Insert new avatar - ctx.db.user_avatar.insert({ + ctx.db.userAvatar.insert({ userId, mimeType, data, diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index bb91562f2b4..e094d2f30c4 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -190,15 +190,15 @@ spacetimedb.reducer('deal_damage', { target: t.identity(), amount: t.u32() }, (c **Server (module) -- after:** ```typescript // 2.0 server -- explicitly publish events via an event table -const damage_event = table({ name: 'damage_event', event: true }, { +const damageEvent = table({ name: 'damage_event', event: true }, { target: t.identity(), amount: t.u32(), }) -// schema() takes an object: schema({ damage_event }), never schema(damage_event) -const spacetimedb = schema({ damage_event }); +// schema() takes an object: schema({ damageEvent }), never schema(damageEvent) +const spacetimedb = schema({ damageEvent }); export const dealDamage = spacetimedb.reducer({ target: t.identity(), amount: t.u32() }, (ctx, { target, amount }) => { - ctx.db.damage_event.insert({ target, amount }); + ctx.db.damageEvent.insert({ target, amount }); }); ``` @@ -1366,14 +1366,14 @@ spacetimedb.reducer('runMyTimer', myTimer.rowType, (ctx, timer) => { ``` ```typescript -const my_timer = table({ name: "my_timer", scheduled: (): any => runMyTimer }, { - scheduled_id: t.u64().primaryKey().autoInc(), - scheduled_at: t.scheduleAt(), +const myTimer = table({ name: 'my_timer', scheduled: (): any => runMyTimer }, { + scheduledId: t.u64().primaryKey().autoInc(), + scheduledAt: t.scheduleAt(), }); -const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) // 2.0 -- Can only be called by the database -export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { // Do stuff }) ``` @@ -1479,17 +1479,17 @@ In the rare event that you have a reducer or procedure which is intended to be i ```typescript -const my_timer = table({ name: "my_timer", scheduled: (): any => runMyTimerPrivate }, { - scheduled_id: t.u64().primaryKey().autoInc(), - scheduled_at: t.scheduleAt(), +const myTimer = table({ name: 'my_timer', scheduled: (): any => runMyTimerPrivate }, { + scheduledId: t.u64().primaryKey().autoInc(), + scheduledAt: t.scheduleAt(), }); -const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) -export const runMyTimerPrivate = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { +export const runMyTimerPrivate = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { // Do stuff... }); -export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { // Same logic as runMyTimerPrivate — extract to a helper if needed }); ``` From 077a8c6bac4f15c359ac0b7a8815ab20d94913f3 Mon Sep 17 00:00:00 2001 From: rain Date: Wed, 22 Jul 2026 08:10:19 -0400 Subject: [PATCH 12/35] docs: align reducer context docs with SDK --- .../00300-reducers/00400-reducer-context.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index 4a3e68d358e..c79d779959b 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -131,7 +131,7 @@ Every reducer invocation has an associated caller identity. ```typescript -import { schema, table, t, type Identity } from 'spacetimedb/server'; +import { schema, table, t } from 'spacetimedb/server'; const player = table( { name: 'player', public: true }, @@ -257,7 +257,7 @@ SPACETIMEDB_REDUCER(update_score, ReducerContext ctx, uint32_t new_score) { The connection ID identifies the specific client connection that invoked the reducer. This is useful for tracking sessions or implementing per-connection state. :::note -The connection ID may be `None`/`null`/`undefined` for reducers invoked by the system (such as scheduled reducers or lifecycle reducers) or when called via the CLI without specifying a connection. +The connection ID may be absent for reducers invoked by the system (such as scheduled reducers or lifecycle reducers) or when called via the CLI without specifying a connection. In TypeScript modules, `ctx.connectionId` is `ConnectionId | null`. ::: ### Timestamp @@ -362,7 +362,7 @@ public static partial class Module [SpacetimeDB.Reducer] public static void SendReminder(ReducerContext _ctx, ScheduledTask task) { - Log.Info($"Reminder: {task.message}"); + Log.Info($"Reminder: {task.Message}"); } } ``` @@ -426,7 +426,7 @@ SPACETIMEDB_REDUCER(send_reminder, ReducerContext _ctx, ScheduledTask task) { | `db` | `DbView` | Access to the module's database tables | | `sender` | `Identity` | Identity of the caller | | `senderAuth` | `AuthCtx` | Authorization context for the caller (includes JWT claims and internal call detection) | -| `connectionId` | `ConnectionId \| undefined`| Connection ID of the caller, if available | +| `connectionId` | `ConnectionId \| null` | Connection ID of the caller, if available | | `timestamp` | `Timestamp` | Time when the reducer was invoked | | `random` | `Random` | Random number generator (deterministic, seeded by SpacetimeDB) | From e52c15ffe747a9a4a101e9f2fe0d598c81984252 Mon Sep 17 00:00:00 2001 From: rain Date: Thu, 23 Jul 2026 08:10:34 -0400 Subject: [PATCH 13/35] docs: update C# reducer failure pattern --- .../00300-resources/00100-how-to/00600-migrating-to-2.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index e094d2f30c4..e70bad128de 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -107,9 +107,9 @@ conn.Reducers.OnDealDamage += (ctx, _, _) => { Console.WriteLine("Reducer succeeded"); } - else if (ctx.Event.Status is Status.Failed failed) + else if (ctx.Event.Status is Status.Failed(var reason)) { - Console.WriteLine($"Reducer failed: {failed}"); + Console.WriteLine($"Reducer failed: {reason}"); } else if (ctx.Event.Status is Status.OutOfEnergy) { From 879a02804664557bd79ab07c5159ea8d572f3ade Mon Sep 17 00:00:00 2001 From: rain Date: Sat, 25 Jul 2026 08:09:32 -0400 Subject: [PATCH 14/35] docs: fix HTTP handler route placeholder --- .../00200-functions/00600-HTTP-handlers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00600-HTTP-handlers.md b/docs/docs/00200-core-concepts/00200-functions/00600-HTTP-handlers.md index c8bc481d338..9696462542d 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00600-HTTP-handlers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00600-HTTP-handlers.md @@ -7,7 +7,7 @@ import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' HTTP handlers allow a SpacetimeDB database to expose an HTTP API. -External clients can make HTTP requests to routes nested under [`/v1/database/:name_or_address/route`](../../00300-resources/00200-reference/00200-http-api/00300-database.md#any-v1databasename_or_identityroutepath); these requests are resolved to routes defined by the database and then passed to the corresponding HTTP handler. +External clients can make HTTP requests to routes nested under [`/v1/database/:name_or_identity/route`](../../00300-resources/00200-reference/00200-http-api/00300-database.md#any-v1databasename_or_identityroutepath); these requests are resolved to routes defined by the database and then passed to the corresponding HTTP handler. :::warning ***HTTP handlers are currently in beta, and their API may change in upcoming SpacetimeDB releases.*** @@ -225,4 +225,4 @@ SpacetimeDB uses strict routing, meaning that a request must match a path exactl ## Sending Requests -Routes defined by a SpacetimeDB database are exposed under the prefix `/v1/database/:name/route`. To access the `say-hello` route above, send a request to `$SPACETIMEDB_URI/v1/database/$DATABASE/route/say-hello`, where `$SPACETIMEDB_URI` is the SpacetimeDB host (usually `https://maincloud.spacetimedb.com`), and `$DATABASE` is the name of the database. +Routes defined by a SpacetimeDB database are exposed under the prefix `/v1/database/:name_or_identity/route`. To access the `say-hello` route above, send a request to `$SPACETIMEDB_URI/v1/database/$DATABASE/route/say-hello`, where `$SPACETIMEDB_URI` is the SpacetimeDB host (usually `https://maincloud.spacetimedb.com`), and `$DATABASE` is the name or identity of the database. From e03a492df84a9252457c4c8fd2d22c9ffcf64655 Mon Sep 17 00:00:00 2001 From: rain Date: Sun, 26 Jul 2026 08:09:54 -0400 Subject: [PATCH 15/35] docs: avoid ambient clock in file storage examples --- .../00200-core-concepts/00300-tables/00210-file-storage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index ba5f8b5f2fe..5a9192d1cc0 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -405,7 +405,7 @@ export const upload_to_s3 = spacetimedb.procedure( t.string(), // Returns the S3 key (ctx, { filename, contentType, data, s3Bucket, s3Region }) => { // Generate a unique S3 key - const s3Key = `uploads/${Date.now()}-${filename}`; + const s3Key = `uploads/${ctx.timestamp.microsSinceUnixEpoch}-${filename}`; const url = `https://${s3Bucket}.s3.${s3Region}.amazonaws.com/${s3Key}`; // Upload to S3 (simplified - add AWS4 signature in production) @@ -610,7 +610,7 @@ export const get_upload_url = spacetimedb.procedure( { filename: t.string(), contentType: t.string() }, t.object('UploadInfo', { uploadUrl: t.string(), s3Key: t.string() }), (ctx, { filename, contentType }) => { - const s3Key = `uploads/${Date.now()}-${filename}`; + const s3Key = `uploads/${ctx.timestamp.microsSinceUnixEpoch}-${filename}`; // Generate pre-signed URL (requires AWS credentials and signing logic) const uploadUrl = generatePresignedUrl(s3Key, contentType); From 4e4902824aed5e87d534b768106755427f6a1604 Mon Sep 17 00:00:00 2001 From: rain Date: Mon, 27 Jul 2026 08:10:17 -0400 Subject: [PATCH 16/35] docs: correct TypeScript connectionId nullability --- .../00200-core-concepts/00100-databases/00500-cheat-sheet.md | 2 +- .../00200-functions/00300-reducers/00500-lifecycle.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md b/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md index 55b95444cff..5ecd5421564 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md +++ b/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md @@ -775,7 +775,7 @@ SPACETIMEDB_VIEW(std::optional, player_count, Public, AnonymousView ```typescript ctx.db // Database access ctx.sender // Identity of caller -ctx.connectionId // ConnectionId | undefined +ctx.connectionId // ConnectionId | null ctx.timestamp // Timestamp ctx.databaseIdentity // Module's identity ``` diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md index b679d90ef82..a7a13cc3e5d 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md @@ -385,5 +385,5 @@ Reducers can be triggered at specific times using schedule tables. See [Schedule :::info Scheduled Reducer Context Scheduled reducer calls originate from SpacetimeDB itself, not from a client. Therefore: - `ctx.sender()` will be the module's own identity -- `ctx.connection_id()` will be `None`/`null`/`undefined` +- The connection ID will be absent (`null` in TypeScript, `null` in C#, `None` in Rust, and `std::nullopt` in C++) ::: From bb430701d7261aeee6867c4ff96ddb68dae9ef58 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 28 Jul 2026 08:10:19 -0400 Subject: [PATCH 17/35] docs: clarify TypeScript provider reconnect behavior --- .../00200-core-concepts/00600-clients/00300-connection.md | 6 +++--- .../00600-clients/00700-typescript-reference.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md index f1ad4c73d42..e5378e4eddc 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md +++ b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md @@ -397,11 +397,11 @@ Conn->Disconnect(); ### Reconnection Behavior -:::note[Current Limitation] +:::note[Reconnection behavior] -Automatic reconnection behavior is inconsistently implemented across SDKs. If your connection is interrupted, you may need to create a new `DbConnection` to re-establish connectivity. +Lower-level `DbConnection` objects do not reconnect themselves. If you create a `DbConnection` directly and the connection is interrupted, create a new `DbConnection` to re-establish connectivity. -We recommend implementing reconnection logic in your application if reliable connectivity is critical. +The TypeScript React, Solid, and Svelte providers manage their connections through the SDK's shared connection manager. While a provider is mounted, that manager automatically rebuilds unexpectedly closed connections with exponential backoff and re-checks connection liveness when the page becomes visible, regains focus, returns online, or is restored from the back-forward cache. ::: diff --git a/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md b/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md index 6f1b6eeecad..ef9a5ab9798 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md @@ -1034,7 +1034,7 @@ The SpacetimeDB TypeScript SDK includes React bindings under the `spacetimedb/re The React integration is fully compatible with React StrictMode and correctly handles the double-mount behavior (only one WebSocket connection is created). -While a `SpacetimeDBProvider` is mounted, the React connection manager also replaces the managed `DbConnection` if the underlying WebSocket closes or reports a connection error. Reconnect attempts use exponential backoff, starting at 1 second and doubling after each consecutive failure up to a 30 second maximum; the backoff resets after a successful connection. Hooks such as `useTable` observe the provider state, receive the fresh connection, and establish their subscriptions again; while the replacement connection is being established, `useTable` reports `isReady` as `false` until its subscription is applied on the new connection. This provider-level recovery does not change the lower-level `DbConnection` contract: applications that create a `DbConnection` directly are still responsible for creating a new connection if they need reconnection behavior. +While a `SpacetimeDBProvider` is mounted, the shared connection manager also replaces the managed `DbConnection` if the underlying WebSocket closes or reports a connection error. Reconnect attempts use exponential backoff, starting at 1 second and doubling after each consecutive failure up to a 30 second maximum; the backoff resets after a successful connection. In browser environments, the manager also re-checks connection liveness when the page becomes visible, regains focus, returns online, or is restored from the back-forward cache, so a stalled reconnect or silently closed socket can be rebuilt promptly after a suspended tab resumes. Hooks such as `useTable` observe the provider state, receive the fresh connection, and establish their subscriptions again; while the replacement connection is being established, `useTable` reports `isReady` as `false` until its subscription is applied on the new connection. This provider-level recovery does not change the lower-level `DbConnection` contract: applications that create a `DbConnection` directly are still responsible for creating a new connection if they need reconnection behavior. | Name | Description | | ----------------------------------------------------------- | --------------------------------------------------------- | From e6f1e64871943c347466274df70b1c595701724d Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Thu, 30 Jul 2026 08:10:17 -0400 Subject: [PATCH 18/35] docs: clarify event table subscribe-all behavior --- .../docs/00200-core-concepts/00300-tables/00550-event-tables.md | 2 +- .../00200-core-concepts/00600-clients/00600-csharp-reference.md | 2 +- .../00600-clients/00700-typescript-reference.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md index 9bcdc1e5b52..306fa92272c 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md @@ -175,7 +175,7 @@ This behavior follows naturally from the fact that event table rows are never me ## Subscribing to Events -On the client side, event tables are subscribed to in the same way as regular tables. The important difference is that event table rows are never stored in the client cache. Calling `count()` on an event table always returns 0, and `iter()` always yields no rows. Instead, you observe events through `on_insert` callbacks, which fire for each row that was inserted during the transaction. +On the client side, event tables are subscribed to with explicit queries, just like regular tables. Subscribe-all helpers such as `subscribeToAllTables`, `SubscribeToAllTables`, and `subscribe_to_all_tables` do not include event tables. Once subscribed, event table rows are never stored in the client cache. Calling `count()` on an event table always returns 0, and `iter()` always yields no rows. Instead, you observe events through `on_insert` callbacks, which fire for each row that was inserted during the transaction. Because event table rows are ephemeral, only `on_insert` callbacks are available. There are no `on_delete`, `on_update`, or `on_before_delete` callbacks, since rows are never present in the client state to be deleted or updated. diff --git a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md index 264ce97c797..e5cd26b9363 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md @@ -425,7 +425,7 @@ class SubscriptionBuilder } ``` -Subscribe to all rows from all public tables. This method is provided as a convenience for simple clients. The subscription initiated by `SubscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. +Subscribe to all rows from all public non-event tables. Event tables are excluded and must be subscribed to with explicit queries. This method is provided as a convenience for simple clients. The subscription initiated by `SubscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. #### Type `TypedSubscriptionBuilder` diff --git a/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md b/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md index ef9a5ab9798..657ff8397b3 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md @@ -384,7 +384,7 @@ class SubscriptionBuilder { } ``` -Subscribe to all rows from all public tables. This method is provided as a convenience for simple clients. The subscription initiated by `subscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. +Subscribe to all rows from all public non-event tables. Event tables are excluded and must be subscribed to with explicit queries. This method is provided as a convenience for simple clients. The subscription initiated by `subscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. ## Query Builder API From 3b711778717a09bc3c3a4eb501bc112188cc0c07 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Fri, 31 Jul 2026 08:10:31 -0400 Subject: [PATCH 19/35] docs: update C# reducer callback tutorial wording --- .../00100-intro/00300-tutorials/00100-chat-app.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md index 96ed9018dfd..954fb903712 100644 --- a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md +++ b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md @@ -2042,7 +2042,10 @@ void PrintMessage(RemoteTables tables, Message message) #### Warn if our name was rejected -We can also register callbacks to run each time a reducer is invoked. We register these callbacks using the `OnReducerEvent` method of the `Reducer` namespace, which is automatically implemented for each reducer by `spacetime generate`. +We can also register callbacks for reducer results. We register these callbacks +using generated events on `conn.Reducers`, such as `conn.Reducers.OnSetName` +and `conn.Reducers.OnSendMessage`, which are automatically implemented for +each reducer by `spacetime generate`. Each reducer callback takes one fixed argument: @@ -2061,7 +2064,11 @@ These callbacks will be invoked in one of two cases: Note that a status of `Failed` or `OutOfEnergy` implies that the caller identity is our own identity. -We already handle successful `SetName` invocations using our `User.OnUpdate` callback, but if the module rejects a user's chosen name, we'd like that user's client to let them know. We define a function `Reducer_OnSetNameEvent` as a `Reducer.OnSetNameEvent` callback which checks if the reducer failed, and if it did, prints an error message including the rejected name. +We already handle successful `SetName` invocations using our `User.OnUpdate` +callback, but if the module rejects a user's chosen name, we'd like that user's +client to let them know. We define a function `Reducer_OnSetNameEvent` and +register it with `conn.Reducers.OnSetName`; the callback checks if the reducer +failed, and if it did, prints an error message including the rejected name. We'll test both that our identity matches the sender and that the status is `Failed`, even though the latter implies the former, for demonstration purposes. From fc626dc3abe4b4f66726bca4d1f39ae9097b63c3 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sat, 1 Aug 2026 08:12:04 -0400 Subject: [PATCH 20/35] docs: align TypeScript table accessor examples --- .../00300-reducers/00300-reducers.md | 8 ++--- .../00300-reducers/00400-reducer-context.md | 6 ++-- .../00200-functions/00400-procedures.md | 14 ++++----- .../00200-functions/00500-views.md | 24 +++++++-------- .../00300-tables/00210-file-storage.md | 8 ++--- .../00300-tables/00400-access-permissions.md | 6 ++-- .../00100-how-to/00600-migrating-to-2.0.md | 30 +++++++++---------- 7 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md index 4180d28ba6d..6238cf8eab4 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md @@ -546,7 +546,7 @@ import { ScheduleAt } from 'spacetimedb'; import { schema, t, table } from 'spacetimedb/server'; // Define a schedule table for the procedure -const fetchSchedule = table( +const fetch_schedule = table( { name: 'fetch_schedule', scheduled: (): any => fetch_external_data }, { scheduled_id: t.u64().primaryKey().autoInc(), @@ -555,12 +555,12 @@ const fetchSchedule = table( } ); -const spacetimedb = schema({ fetchSchedule }); +const spacetimedb = schema({ fetch_schedule }); export default spacetimedb; // The procedure to be scheduled export const fetch_external_data = spacetimedb.procedure( - { arg: fetchSchedule.rowType }, + { arg: fetch_schedule.rowType }, t.unit(), (ctx, { arg }) => { const response = ctx.http.fetch(arg.url); @@ -571,7 +571,7 @@ export const fetch_external_data = spacetimedb.procedure( // From a reducer, schedule the procedure by inserting into the schedule table export const queueFetch = spacetimedb.reducer({ url: t.string() }, (ctx, { url }) => { - ctx.db.fetchSchedule.insert({ + ctx.db.fetch_schedule.insert({ scheduled_id: 0n, scheduled_at: ScheduleAt.interval(0n), // Run immediately url, diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index c79d779959b..8a0e40d64ae 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -324,7 +324,7 @@ Scheduled reducers and procedures are private by default in SpacetimeDB 2.x, so ```typescript import { schema, table, t } from 'spacetimedb/server'; -const scheduledTask = table( +const scheduled_task = table( { name: 'scheduled_task', scheduled: (): any => send_reminder }, { taskId: t.u64().primaryKey().autoInc(), @@ -333,10 +333,10 @@ const scheduledTask = table( } ); -const spacetimedb = schema({ scheduledTask }); +const spacetimedb = schema({ scheduled_task }); export default spacetimedb; -export const send_reminder = spacetimedb.reducer({ arg: scheduledTask.rowType }, (_ctx, { arg }) => { +export const send_reminder = spacetimedb.reducer({ arg: scheduled_task.rowType }, (_ctx, { arg }) => { console.log(`Reminder: ${arg.message}`); }); ``` diff --git a/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md b/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md index 32589a22a88..f96b8e238ec 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md +++ b/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md @@ -128,7 +128,7 @@ This means there's no `ctx.db` field to access the database. Instead, procedure code must manage transactions explicitly with `ProcedureCtx.withTx`. ```typescript -const myTable = table( +const my_table = table( { name: "my_table" }, { a: t.u32(), @@ -136,12 +136,12 @@ const myTable = table( }, ) -const spacetimedb = schema({ myTable }); +const spacetimedb = schema({ my_table }); export default spacetimedb; export const insert_a_value = spacetimedb.procedure({ a: t.u32(), b: t.u32() }, t.unit(), (ctx, { a, b }) => { ctx.withTx(ctx => { - ctx.db.myTable.insert({ a, b }); + ctx.db.my_table.insert({ a, b }); }); return {}; }) @@ -328,7 +328,7 @@ export const maybe_insert_a_value = spacetimedb.procedure({ a: t.u32(), b: t.str if (a < 10) { throw new SenderError("a is less than 10!"); } - ctx.db.myTable.insert({ a, b }); + ctx.db.my_table.insert({ a, b }); }); }) ``` @@ -1190,7 +1190,7 @@ A common use case for procedures is integrating with external APIs like OpenAI's import { schema, t, table, SenderError } from 'spacetimedb/server'; import { TimeDuration } from 'spacetimedb'; -const aiMessage = table( +const ai_message = table( { name: 'ai_message', public: true }, { user: t.identity(), @@ -1200,7 +1200,7 @@ const aiMessage = table( } ); -const spacetimedb = schema({ aiMessage }); +const spacetimedb = schema({ ai_message }); export default spacetimedb; export const ask_ai = spacetimedb.procedure( @@ -1235,7 +1235,7 @@ export const ask_ai = spacetimedb.procedure( // Store the conversation in the database ctx.withTx(txCtx => { - txCtx.db.aiMessage.insert({ + txCtx.db.ai_message.insert({ user: txCtx.sender, prompt, response: aiResponse, diff --git a/docs/docs/00200-core-concepts/00200-functions/00500-views.md b/docs/docs/00200-core-concepts/00200-functions/00500-views.md index c6c1ade084f..d86af13564c 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00500-views.md +++ b/docs/docs/00200-core-concepts/00200-functions/00500-views.md @@ -40,7 +40,7 @@ const players = table( } ); -const playerLevels = table( +const player_levels = table( { name: 'player_levels', public: true }, { player_id: t.u64().unique(), @@ -48,7 +48,7 @@ const playerLevels = table( } ); -const spacetimedb = schema({ players, playerLevels }); +const spacetimedb = schema({ players, player_levels }); export default spacetimedb; // At-most-one row: return Option via t.option(...) @@ -75,7 +75,7 @@ export const players_for_level = spacetimedb.anonymousView( t.array(playerAndLevelRow), (ctx) => { const out: Array<{ id: bigint; name: string; level: bigint }> = []; - for (const playerLevel of ctx.db.playerLevels.level.filter(2n)) { + for (const playerLevel of ctx.db.player_levels.level.filter(2n)) { const p = ctx.db.players.id.find(playerLevel.player_id); if (p) out.push({ id: p.id, name: p.name, level: playerLevel.level }); } @@ -493,7 +493,7 @@ const entity = table( ); // Track which chunks each player is subscribed to -const playerChunk = table( +const player_chunk = table( { name: 'player_chunk', public: true }, { playerId: t.u64().primaryKey(), @@ -521,7 +521,7 @@ export const entities_in_my_chunk = spacetimedb.view( const player = ctx.db.players.identity.find(ctx.sender); if (!player) return []; - const chunk = ctx.db.playerChunk.playerId.find(player.id); + const chunk = ctx.db.player_chunk.playerId.find(player.id); if (!chunk) return []; return Array.from(ctx.db.entity.chunkX.filter(chunk.chunkX)) @@ -1067,7 +1067,7 @@ const players = table( } ); -const playerLevels = table( +const player_levels = table( { name: 'player_levels', public: true }, { player_id: t.u64().unique(), @@ -1075,7 +1075,7 @@ const playerLevels = table( } ); -const spacetimedb = schema({ players, playerLevels }); +const spacetimedb = schema({ players, player_levels }); export default spacetimedb; export const all_players = spacetimedb.anonymousView( @@ -1086,8 +1086,8 @@ export const all_players = spacetimedb.anonymousView( export const all_player_levels = spacetimedb.anonymousView( { name: 'all_player_levels', public: true }, - t.array(playerLevels.rowType), - (ctx) => ctx.from.playerLevels + t.array(player_levels.rowType), + (ctx) => ctx.from.player_levels ); ``` @@ -1342,17 +1342,17 @@ export const players_with_levels = spacetimedb.anonymousView( t.array(players.rowType), (ctx) => { return ctx.from.players - .leftSemijoin(ctx.from.playerLevels, (p, pl) => p.id.eq(pl.player_id)); + .leftSemijoin(ctx.from.player_levels, (p, pl) => p.id.eq(pl.player_id)); } ); export const levels_for_high_scorers = spacetimedb.anonymousView( { name: 'levels_for_high_scorers', public: true }, - t.array(playerLevels.rowType), + t.array(player_levels.rowType), (ctx) => { return ctx.from.players .where(p => p.score.gte(1000n)) - .rightSemijoin(ctx.from.playerLevels, (p, pl) => p.id.eq(pl.player_id)) + .rightSemijoin(ctx.from.player_levels, (p, pl) => p.id.eq(pl.player_id)) .where(pl => pl.level.gte(10n)); } ); diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index 5a9192d1cc0..68087bcdda9 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -20,7 +20,7 @@ Store binary data using `Vec` (Rust), `List` (C#), `std::vector { // Delete existing avatar if present - ctx.db.userAvatar.userId.delete(userId); + ctx.db.user_avatar.userId.delete(userId); // Insert new avatar - ctx.db.userAvatar.insert({ + ctx.db.user_avatar.insert({ userId, mimeType, data, diff --git a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md index 36005fa6c3f..666b68b5960 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md +++ b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md @@ -568,7 +568,7 @@ Use views to return a custom type that omits sensitive columns. The view reads f import {schema, t, table} from 'spacetimedb/server'; // Private table with sensitive data -const userAccount = table( +const user_account = table( { name: 'user_account' }, // Private by default { id: t.u64().primaryKey().autoInc(), @@ -581,7 +581,7 @@ const userAccount = table( } ); -const spacetimedb = schema({ userAccount }); +const spacetimedb = schema({ user_account }); export default spacetimedb; // Public type without sensitive columns @@ -597,7 +597,7 @@ export const my_profile = spacetimedb.view( t.option(publicUserProfile), (ctx) => { // Look up the caller's account by their identity (unique index) - const user = ctx.db.userAccount.identity.find(ctx.sender); + const user = ctx.db.user_account.identity.find(ctx.sender); if (!user) return null; return { id: user.id, diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index e70bad128de..9069559e07d 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -190,15 +190,15 @@ spacetimedb.reducer('deal_damage', { target: t.identity(), amount: t.u32() }, (c **Server (module) -- after:** ```typescript // 2.0 server -- explicitly publish events via an event table -const damageEvent = table({ name: 'damage_event', event: true }, { +const damage_event = table({ name: 'damage_event', event: true }, { target: t.identity(), amount: t.u32(), }) -// schema() takes an object: schema({ damageEvent }), never schema(damageEvent) -const spacetimedb = schema({ damageEvent }); +// schema() takes an object: schema({ damage_event }), never schema(damage_event) +const spacetimedb = schema({ damage_event }); export const dealDamage = spacetimedb.reducer({ target: t.identity(), amount: t.u32() }, (ctx, { target, amount }) => { - ctx.db.damageEvent.insert({ target, amount }); + ctx.db.damage_event.insert({ target, amount }); }); ``` @@ -1084,14 +1084,14 @@ SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx) { In 2.0 modules, only columns with a `.primaryKey()` constraint expose an `update` method, whereas previously, `.unique()` constraints also provided that method. The previous behavior led to confusion, as only updates which preserved the value in the primary key column resulted in `onUpdate` callbacks being invoked on the client. ```typescript -const myTable = table({ name: 'my_table' }, { +const my_table = table({ name: 'my_table' }, { id: t.u32().unique(), name: t.string(), }) // 1.0 -- REMOVED in 2.0 spacetimedb.reducer('my_reducer', ctx => { - ctx.db.myTable.id.update({ + ctx.db.my_table.id.update({ id: 1, name: "Foobar", }); @@ -1100,8 +1100,8 @@ spacetimedb.reducer('my_reducer', ctx => { // 2.0 -- Perform a delete followed by an insert // OR change the `.unique()` constraint into `.primaryKey()` constraint spacetimedb.reducer(ctx => { - ctx.db.myTable.id.delete(1); - ctx.db.myTable.insert({ + ctx.db.my_table.id.delete(1); + ctx.db.my_table.insert({ id: 1, name: "Foobar" }); @@ -1366,14 +1366,14 @@ spacetimedb.reducer('runMyTimer', myTimer.rowType, (ctx, timer) => { ``` ```typescript -const myTimer = table({ name: 'my_timer', scheduled: (): any => runMyTimer }, { +const my_timer = table({ name: 'my_timer', scheduled: (): any => runMyTimer }, { scheduledId: t.u64().primaryKey().autoInc(), scheduledAt: t.scheduleAt(), }); -const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) // 2.0 -- Can only be called by the database -export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { // Do stuff }) ``` @@ -1479,17 +1479,17 @@ In the rare event that you have a reducer or procedure which is intended to be i ```typescript -const myTimer = table({ name: 'my_timer', scheduled: (): any => runMyTimerPrivate }, { +const my_timer = table({ name: 'my_timer', scheduled: (): any => runMyTimerPrivate }, { scheduledId: t.u64().primaryKey().autoInc(), scheduledAt: t.scheduleAt(), }); -const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) -export const runMyTimerPrivate = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { +export const runMyTimerPrivate = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { // Do stuff... }); -export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { // Same logic as runMyTimerPrivate — extract to a helper if needed }); ``` From d88851c69de10fc5dc3a3ebd66d001666958cb7b Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 2 Aug 2026 08:10:10 -0400 Subject: [PATCH 21/35] docs: use database name in client connection docs --- .../00300-tutorials/00300-unity-tutorial/00300-part-2.md | 8 ++++---- .../00300-tutorials/00300-unity-tutorial/00400-part-3.md | 2 +- .../00300-tutorials/00300-unity-tutorial/00500-part-4.md | 4 ++-- .../00300-tutorials/00400-unreal-tutorial/00300-part-2.md | 4 ++-- .../00300-tutorials/00400-unreal-tutorial/00500-part-4.md | 2 +- .../00300-tutorials/00500-godot-tutorial/00400-part-3.md | 2 +- .../00500-authentication/00400-BetterAuth.md | 4 ++-- .../00100-how-to/00100-deploy/00100-maincloud.md | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md index 033bd7ed2ea..3d9f4347e5e 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00300-part-2.md @@ -701,7 +701,7 @@ Replace the implementation of the `GameManager` class with the following. public class GameManager : MonoBehaviour { const string SERVER_URL = "http://127.0.0.1:3000"; - const string MODULE_NAME = "blackholio"; + const string DATABASE_NAME = "blackholio"; public static event Action OnConnected; public static event Action OnSubscriptionApplied; @@ -719,13 +719,13 @@ public class GameManager : MonoBehaviour Application.targetFrameRate = 60; // In order to build a connection to SpacetimeDB we need to register - // our callbacks and specify a SpacetimeDB server URI and module name. + // our callbacks and specify a SpacetimeDB server URI and database name. var builder = DbConnection.Builder() .OnConnect(HandleConnect) .OnConnectError(HandleConnectError) .OnDisconnect(HandleDisconnect) .WithUri(SERVER_URL) - .WithDatabaseName(MODULE_NAME); + .WithDatabaseName(DATABASE_NAME); // If the user has a SpacetimeDB auth token stored in the Unity PlayerPrefs, // we can use it to authenticate the connection. @@ -803,7 +803,7 @@ public class GameManager : MonoBehaviour > Unity WebGL needs one extra precaution here. Browser WebSocket APIs cannot set an `Authorization` header, so reconnecting with a saved server-issued token may yield a short-lived WebSocket token in `HandleConnect`. The `#if UNITY_WEBGL` guard keeps the original saved token instead of overwriting it during reconnect. -Here we configure the connection to the database, by passing it some callbacks in addition to providing the `SERVER_URI` and `MODULE_NAME` to the connection. When the client connects, the SpacetimeDB SDK will call the `HandleConnect` method, allowing us to start up the game. +Here we configure the connection to the database, by passing it some callbacks in addition to providing the `SERVER_URL` and `DATABASE_NAME` to the connection. When the client connects, the SpacetimeDB SDK will call the `HandleConnect` method, allowing us to start up the game. In our `HandleConnect` callback we build a subscription and call `Subscribe`, subscribing to all data in the database. This causes SpacetimeDB to synchronize the state of all your tables with your Unity client's SDK client cache. diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index 01a99bcfc91..612042afcb3 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -1554,7 +1554,7 @@ At this point, after publishing our module we can press the play button to see t - If you get an error when running the generate command, make sure you have an empty subfolder in your Unity project Assets folder called `module_bindings` -- If you get an error in your Unity console when starting the game, double check that you have published your module and you have the correct module name specified in your `GameManager`. +- If you get an error in your Unity console when starting the game, double check that you have published your module and you have the correct database name specified in your `GameManager`. ### Next Steps diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md index bfc58fc0ded..40aee6fd84c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md @@ -864,13 +864,13 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca - Publish to Maincloud `spacetime publish --server maincloud --delete-data` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). You will have to update the database name in `blackholio-server/spacetime.local.json` to match. - Update the URL in the Unity project to: `https://maincloud.spacetimedb.com` -- Update the module name in the Unity project to ``. +- Update the database name in the Unity project to ``. - Clear the PlayerPrefs in Start() within `GameManager.cs` - Your `GameManager.cs` should look something like this: ```csharp const string SERVER_URL = "https://maincloud.spacetimedb.com"; -const string MODULE_NAME = ""; +const string DATABASE_NAME = ""; ... diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md index c3113c50bf4..2c16629631c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00300-part-2.md @@ -873,7 +873,7 @@ void AGameManager::HandleSubscriptionApplied(FSubscriptionEventContext& Context) } ``` -Here we configure the connection to the database, by passing it some callbacks in addition to providing the `SERVER_URI` and `MODULE_NAME` to the connection. When the client connects, the SpacetimeDB SDK will call the `HandleConnect` method, allowing us to start up the game. +Here we configure the connection to the database, by passing it some callbacks in addition to providing the `ServerUri` and `DatabaseName` to the connection. When the client connects, the SpacetimeDB SDK will call the `HandleConnect` method, allowing us to start up the game. In our `HandleConnect` callback we build a subscription and are calling `Subscribe` and subscribing to all data in the database. This will cause SpacetimeDB to synchronize the state of all your tables with your Unreal client's SpacetimeDB SDK's "client cache". You can also subscribe to specific tables using SQL syntax, e.g. `SELECT * FROM my_table`. Our [SQL documentation](../../../00300-resources/00200-reference/00400-sql-reference.md) enumerates the operations that are accepted in our SQL syntax. @@ -955,7 +955,7 @@ Update the **Event EndPlay** and **Event Tick** to the following: Update the **OnConnect_Event**: ![Update OnConnect](/images/unreal/part-2-08-blueprint-onconnect.png) -Here we configure the connection to the database, by passing it some callbacks in addition to providing the `SERVER_URI` and `MODULE_NAME` to the connection. When the client connects, the SpacetimeDB SDK will call the `OnConnect_Event` method, allowing us to start up the game. +Here we configure the connection to the database, by passing it some callbacks in addition to providing the `ServerUri` and `DatabaseName` to the connection. When the client connects, the SpacetimeDB SDK will call the `OnConnect_Event` method, allowing us to start up the game. In our `OnConnect_Event` callback we build a subscription and are calling `Subscribe` and subscribing to all data in the database. This will cause SpacetimeDB to synchronize the state of all your tables with your Unreal client's SpacetimeDB SDK's "client cache". You can also subscribe to specific tables using SQL syntax, e.g. `SELECT * FROM my_table`. Our [SQL documentation](../../../00300-resources/00200-reference/00400-sql-reference.md) enumerates the operations that are accepted in our SQL syntax. diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md index 4cb494183c2..11233e3bcbc 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md @@ -920,7 +920,7 @@ Notice that the food automatically respawns as you vaccuum them up. This is beca - Publish to Maincloud `spacetime publish --server maincloud --delete-data` - `` This name should be unique and cannot contain any special characters other than internal hyphens (`-`). - Update the URL in the Unreal project to: `https://maincloud.spacetimedb.com` -- Update the module name in the Unreal project to ``. +- Update the database name in the Unreal project to ``. - Your `BP_GameManager` should look something like this: ![Maincloud Setup](/images/unreal/part-4-01-maincloud.png) diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md index b5e3e4451fa..46291fe7202 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md @@ -1639,7 +1639,7 @@ After publishing our module we can press the play button to see the fruits of ou - If you get an error when running the generate command, make sure you have an empty subfolder in your Godot project Assets folder called `module_bindings` -- If you get an error in your Godot console when starting the game, double check that you have published your module and you have the correct module name specified in your `GameManager`. +- If you get an error in your Godot console when starting the game, double check that you have published your module and you have the correct database name specified in your `GameManager`. ### Next Steps diff --git a/docs/docs/00200-core-concepts/00500-authentication/00400-BetterAuth.md b/docs/docs/00200-core-concepts/00500-authentication/00400-BetterAuth.md index e9ac54ec7c0..b27e9df79fc 100644 --- a/docs/docs/00200-core-concepts/00500-authentication/00400-BetterAuth.md +++ b/docs/docs/00200-core-concepts/00500-authentication/00400-BetterAuth.md @@ -58,7 +58,7 @@ The examples below use placeholder URLs: Better Auth issuer: https://app.example.com/api/auth OAuth client ID: SpacetimeDB URL: -Module name: +Database name: ``` Use the exact same issuer value everywhere. The issuer must match the token's @@ -272,7 +272,7 @@ const token = await getBetterAuthOidcToken(); const conn = DbConnection.builder() .withUri('') - .withDatabaseName('') + .withDatabaseName('') .withToken(token) .onConnect((_conn, identity) => { console.log( diff --git a/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md b/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md index 53d2503338c..0f7238dd3cc 100644 --- a/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md +++ b/docs/docs/00300-resources/00100-how-to/00100-deploy/00100-maincloud.md @@ -46,7 +46,7 @@ spacetime publish my-database --server maincloud --delete-data ## Connecting Clients to Maincloud -To connect your client application to a module running on Maincloud, use `https://maincloud.spacetimedb.com` as the host URL and your database name as the module name: +To connect your client application to a module running on Maincloud, use `https://maincloud.spacetimedb.com` as the host URL and pass the database name to `withDatabaseName` / `WithDatabaseName`: From 4f82e07de3c2fb8af923ad59caa8d2decf6f9c18 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Mon, 3 Aug 2026 08:10:26 -0400 Subject: [PATCH 22/35] docs: fix TypeScript submodule examples --- .../00200-core-concepts/00100-databases/00600-submodules.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/00200-core-concepts/00100-databases/00600-submodules.md b/docs/docs/00200-core-concepts/00100-databases/00600-submodules.md index 242888f333d..c7b39909143 100644 --- a/docs/docs/00200-core-concepts/00100-databases/00600-submodules.md +++ b/docs/docs/00200-core-concepts/00100-databases/00600-submodules.md @@ -53,7 +53,7 @@ export const sessionCount = spacetimedb.procedure( ); export const activeSessions = spacetimedb.anonymousView( - { name: 'activeSessions', public: true }, + { name: 'active_sessions', public: true }, t.array(sessions.rowType), (ctx) => [...ctx.db.sessions.iter()] ); @@ -114,7 +114,7 @@ Submodule tables appear under a namespace field on `ctx.db`. The field matches t ```typescript -spacetimedb.reducer('example', {}, (ctx) => { +export const example = spacetimedb.reducer((ctx) => { // Consumer's own tables (no namespace) for (const player of ctx.db.players.iter()) { /* ... */ } @@ -145,7 +145,7 @@ export function sessionCountHelper(ctx: ReducerContext): num } // my-database: call submodule reducer and helper from a consumer reducer -spacetimedb.reducer('onLogin', { token: t.string() }, (ctx, { token }) => { +export const onLogin = spacetimedb.reducer({ token: t.string() }, (ctx, { token }) => { // call a submodule reducer authLib.verifyToken(ctx.as.myauth, { token }); From f60eddaa97a892744e4d5c1cefc30f1e06ecdf1b Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 4 Aug 2026 08:10:50 -0400 Subject: [PATCH 23/35] docs: align scheduled task field names --- .../00200-functions/00300-reducers/00400-reducer-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index 8a0e40d64ae..f1850fcf51f 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -327,8 +327,8 @@ import { schema, table, t } from 'spacetimedb/server'; const scheduled_task = table( { name: 'scheduled_task', scheduled: (): any => send_reminder }, { - taskId: t.u64().primaryKey().autoInc(), - scheduledAt: t.scheduleAt(), + task_id: t.u64().primaryKey().autoInc(), + scheduled_at: t.scheduleAt(), message: t.string(), } ); From d4ddc12cc477d671b25ceb38bfd02b287c338994 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Wed, 5 Aug 2026 08:09:46 -0400 Subject: [PATCH 24/35] Document Rust string default values --- .../00200-core-concepts/00300-tables/00250-default-values.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md b/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md index 0a45eed057c..c7591ab17c8 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md +++ b/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md @@ -77,6 +77,8 @@ pub struct Player { score: u32, #[default(true)] is_active: bool, + #[default("")] + bio: String, } ``` From 1d60e6446de68128c9c4b1a2cfbc50fa66fa7c01 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Thu, 6 Aug 2026 08:10:10 -0400 Subject: [PATCH 25/35] docs: update Unreal client callback examples --- .../00200-core-concepts/00600-clients/00200-codegen.md | 4 +--- .../00600-clients/00300-connection.md | 10 +++++----- .../00600-clients/00800-unreal-reference.md | 9 +++++++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md b/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md index acc6e7ef221..a1c9950a5e9 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md +++ b/docs/docs/00200-core-concepts/00600-clients/00200-codegen.md @@ -234,9 +234,7 @@ conn.reducers().on_create_user(|ctx, name, email| { Context.Reducers->CreateUser(TEXT("Alice"), TEXT("alice@example.com")); // Register a callback to observe reducer invocations -FOnCreateUserDelegate Callback; -BIND_DELEGATE_SAFE(Callback, this, AMyActor, OnCreateUser); -Context.Reducers->OnCreateUser(Callback); +Context.Reducers->OnCreateUser.AddDynamic(this, &AMyActor::OnCreateUser); // Callback function (must be UFUNCTION) UFUNCTION() diff --git a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md index e5378e4eddc..92e310094fe 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md +++ b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md @@ -319,13 +319,13 @@ let conn = DbConnection::builder() ```cpp // Create delegates FOnConnectDelegate ConnectDelegate; -ConnectDelegate.BindDynamic(this, &AMyActor::OnConnected); +BIND_DELEGATE_SAFE(ConnectDelegate, this, AMyActor, OnConnected); FOnConnectErrorDelegate ErrorDelegate; -ErrorDelegate.BindDynamic(this, &AMyActor::OnConnectError); +BIND_DELEGATE_SAFE(ErrorDelegate, this, AMyActor, OnConnectError); FOnDisconnectDelegate DisconnectDelegate; -DisconnectDelegate.BindDynamic(this, &AMyActor::OnDisconnected); +BIND_DELEGATE_SAFE(DisconnectDelegate, this, AMyActor, OnDisconnected); // Build connection with callbacks UDbConnection* Conn = UDbConnection::Builder() @@ -351,9 +351,9 @@ void OnConnectError(const FString& Error) } UFUNCTION() -void OnDisconnected() +void OnDisconnected(UDbConnection* Connection, const FString& Error) { - UE_LOG(LogTemp, Warning, TEXT("Disconnected from SpacetimeDB")); + UE_LOG(LogTemp, Warning, TEXT("Disconnected from SpacetimeDB: %s"), *Error); } ``` diff --git a/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md b/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md index 5888d3bf08f..9bc2ff53530 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00800-unreal-reference.md @@ -871,10 +871,10 @@ void AMyActor::BeginPlay() // Setup connection callbacks FOnConnectDelegate ConnectDelegate; - ConnectDelegate.BindDynamic(this, &AMyActor::OnConnected); + BIND_DELEGATE_SAFE(ConnectDelegate, this, AMyActor, OnConnected); FOnDisconnectDelegate DisconnectDelegate; - DisconnectDelegate.BindDynamic(this, &AMyActor::OnDisconnected); + BIND_DELEGATE_SAFE(DisconnectDelegate, this, AMyActor, OnDisconnected); // Build and connect Conn = UDbConnection::Builder() @@ -933,6 +933,11 @@ void AMyActor::SendMessage(const FString& Text) Conn->Reducers->SendMessage(Text); } } + +void AMyActor::OnDisconnected(UDbConnection* Connection, const FString& Error) +{ + UE_LOG(LogTemp, Warning, TEXT("Disconnected from SpacetimeDB: %s"), *Error); +} ``` For small modules or quick debugging sessions, you can still subscribe to every public table: From b12d92a352dcf2755142bb59514916edcbc7abc6 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Fri, 7 Aug 2026 08:09:34 -0400 Subject: [PATCH 26/35] docs: sync Codex plugin skills --- codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md | 4 ++++ .../spacetimedb/skills/typescript-server/SKILL.md | 11 ++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md b/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md index 534ad7ee4ec..f5cd3479128 100644 --- a/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md +++ b/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md @@ -63,6 +63,10 @@ spacetime publish my-database --server local --yes spacetime publish my-database --delete-data=always --yes ``` +Bare `--delete-data` defaults to `always`. Keep simple interactive docs examples +bare, and use `--delete-data=always` for scripted/non-interactive examples that +also pass `--yes`. + ### Database Interaction ```bash diff --git a/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md b/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md index 2e9326b3150..a870f680cbb 100644 --- a/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md +++ b/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md @@ -52,7 +52,8 @@ import { ScheduleAt } from 'spacetimedb'; // for scheduled tables only ## Tables -`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field MUST be snake_case: +`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field is optional; +when present, it overrides the canonical SQL name and should be snake_case: ```typescript const entity = table( @@ -65,9 +66,13 @@ const entity = table( ); ``` -Options: `name` (snake_case, recommended), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` +Options: `name` (optional canonical SQL name override), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` -`ctx.db` accessors are the keys passed to `schema({...})`, verbatim: `schema({ score_record })` → `ctx.db.score_record`. Use snake_case keys matching the table `name`. Client codegen converts case; server `ctx.db` does not. +`ctx.db` accessors are the keys passed to `schema({ ... })`, verbatim: +`schema({ score_record })` -> `ctx.db.score_record`. The optional `name` +field overrides the canonical SQL name; it does not change the server +`ctx.db` accessor. Use snake_case keys matching the table `name`. Client +codegen converts case; server `ctx.db` does not. ## Column Types From df43dab958f77d3793d7eb3c99af2ea380a2f219 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sat, 8 Aug 2026 08:10:21 -0400 Subject: [PATCH 27/35] docs: align C++ ScheduleAt examples --- .../00300-tutorials/00300-unity-tutorial/00400-part-3.md | 4 ++-- .../00300-tutorials/00300-unity-tutorial/00500-part-4.md | 2 +- .../00300-tutorials/00400-unreal-tutorial/00400-part-3.md | 4 ++-- .../00300-tutorials/00400-unreal-tutorial/00500-part-4.md | 2 +- .../00300-tutorials/00500-godot-tutorial/00400-part-3.md | 4 ++-- .../00300-tutorials/00500-godot-tutorial/00500-part-4.md | 2 +- .../00200-functions/00300-reducers/00300-reducers.md | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index 612042afcb3..fb96a00797b 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -394,14 +394,14 @@ SPACETIMEDB_INIT(init, ReducerContext ctx) { }); ctx.db[spawn_food_timer].insert(SpawnFoodTimer{ 0, - ScheduleAt(TimeDuration::from_millis(500)), + ScheduleAt::interval(TimeDuration::from_millis(500)), }); return Ok(); } ``` :::note -You can use `ScheduleAt(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. +You can use `ScheduleAt::interval(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt::time(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. ::: diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md index 40aee6fd84c..08e8408142a 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md @@ -512,7 +512,7 @@ Add the following to your `init` reducer to schedule the `move_all_players` redu ```cpp ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ 0, - ScheduleAt(TimeDuration::from_millis(50)), + ScheduleAt::interval(TimeDuration::from_millis(50)), }); ``` diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md index 1b388814d96..2e653728464 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md @@ -387,14 +387,14 @@ SPACETIMEDB_INIT(init, ReducerContext ctx) { }); ctx.db[spawn_food_timer].insert(SpawnFoodTimer{ 0, - ScheduleAt(TimeDuration::from_millis(500)), + ScheduleAt::interval(TimeDuration::from_millis(500)), }); return Ok(); } ``` :::note -You can use `ScheduleAt(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. +You can use `ScheduleAt::interval(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt::time(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. ::: diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md index 11233e3bcbc..ee4d8c14ab4 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md @@ -510,7 +510,7 @@ Add the following to your `init` reducer to schedule the `move_all_players` redu ```cpp ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ 0, - ScheduleAt(TimeDuration::from_millis(50)), + ScheduleAt::interval(TimeDuration::from_millis(50)), }); ``` diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md index 46291fe7202..11dfd9592f3 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md @@ -394,14 +394,14 @@ SPACETIMEDB_INIT(init, ReducerContext ctx) { }); ctx.db[spawn_food_timer].insert(SpawnFoodTimer{ 0, - ScheduleAt(TimeDuration::from_millis(500)), + ScheduleAt::interval(TimeDuration::from_millis(500)), }); return Ok(); } ``` :::note -You can use `ScheduleAt(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. +You can use `ScheduleAt::interval(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt::time(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. ::: diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md index 71eff102a9c..77477f88d14 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md @@ -512,7 +512,7 @@ Add the following to your `init` reducer to schedule the `move_all_players` redu ```cpp ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ 0, - ScheduleAt(TimeDuration::from_millis(50)), + ScheduleAt::interval(TimeDuration::from_millis(50)), }); ``` diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md index 6238cf8eab4..0745dae876c 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md @@ -686,7 +686,7 @@ SPACETIMEDB_PROCEDURE(uint32_t, fetch_external_data, ProcedureContext ctx, Fetch // From a reducer, schedule the procedure by inserting into the schedule table SPACETIMEDB_REDUCER(queue_fetch, ReducerContext ctx, std::string url) { - auto scheduled_at = ScheduleAt(TimeDuration::from_seconds(0)); // Run immediately + auto scheduled_at = ScheduleAt::interval(TimeDuration::from_seconds(0)); // Run immediately FetchSchedule fetch_task{ 0, // scheduled_id - auto-increment will assign scheduled_at, // When to execute From 35ed2c246aae1e2bbd761dd601cc0d84c732eec9 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 08:10:01 -0400 Subject: [PATCH 28/35] docs: align reducer overview examples --- .../00300-reducers/00300-reducers.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md index 0745dae876c..fd745b69f7e 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md @@ -22,6 +22,18 @@ Use the `spacetimedb.reducer` function: ```typescript import { schema, table, t } from 'spacetimedb/server'; +const user = table( + { name: 'user', public: true }, + { + id: t.u64().primaryKey().autoInc(), + name: t.string().index('btree'), + email: t.string().unique(), + } +); + +const spacetimedb = schema({ user }); +export default spacetimedb; + export const create_user = spacetimedb.reducer({ name: t.string(), email: t.string() }, (ctx, { name, email }) => { // Validate input if (name === '') { @@ -69,7 +81,7 @@ public static partial class Module } ``` -Reducers must be static methods with `ReducerContext` as the first parameter. Additional parameters must be types marked with `[SpacetimeDB.Type]`. Reducers should return `void`. +Reducers must be static methods with `ReducerContext` as the first parameter. Additional parameters may be built-in SpacetimeDB types, primitive types, or custom types marked with `[SpacetimeDB.Type]`. Reducers should return `void`. @@ -470,7 +482,7 @@ console.log(`Total users: ${total}`); ```csharp -var total = ctx.Db.User.Count(); +var total = ctx.Db.User.Count; Log.Info($"Total users: {total}"); ``` From 0cf717b0406db282e2cdf0b429a1ec7532dbfcf7 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Mon, 10 Aug 2026 08:10:41 -0400 Subject: [PATCH 29/35] docs: clarify Unity connection ticking --- .../plugins/spacetimedb/skills/unity/SKILL.md | 13 +++++-------- .../00300-unity-tutorial/00200-part-1.md | 2 ++ .../00600-clients/00600-csharp-reference.md | 4 ++++ .../00100-how-to/00050-troubleshooting.md | 3 ++- skills/unity/SKILL.md | 13 +++++-------- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md b/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md index 61d523f1e97..aa604746793 100644 --- a/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md +++ b/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md @@ -82,11 +82,6 @@ public class SpacetimeManager : MonoBehaviour .Build(); } - void Update() - { - Connection?.FrameTick(); - } - void OnDestroy() { Connection?.Disconnect(); @@ -114,9 +109,9 @@ public class SpacetimeManager : MonoBehaviour --- -## FrameTick (Critical) +## FrameTick / Network Manager (Critical) -**`FrameTick()` must be called every frame in `Update()`.** The SDK queues all network messages and only processes them when you call `FrameTick()`. Without it, no callbacks fire and the client appears frozen. See the `Update()` method in the SpacetimeManager above. +The Unity package includes `SpacetimeDBNetworkManager`, a MonoBehaviour that advances active connections from Unity's `Update` loop. Add exactly one `SpacetimeDBNetworkManager` component to a scene GameObject, or call `Connection.FrameTick()` yourself every frame. Without one of these, no callbacks fire and the client appears frozen. **Thread safety**: `FrameTick()` processes messages on the calling thread (the main thread in Unity). Do NOT call it from a background thread. Do NOT access `conn.Db` from background threads. @@ -212,6 +207,9 @@ All SpacetimeDB SDK calls (`FrameTick`, `conn.Db` access, reducer calls) must ha ### Scene Loading Use `DontDestroyOnLoad(gameObject)` on the SpacetimeManager to prevent the connection from being destroyed during scene transitions. Without it, the connection drops every time you load a new scene. +### Domain Reloading +The SDK resets its own internal static Unity state when entering Play Mode, including projects that disable Domain Reloading in Unity's Enter Play Mode Options. Reset any static fields and singleton references in your own game code separately. + ### IL2CPP / AOT The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP builds: - Ensure generated bindings are up to date @@ -219,4 +217,3 @@ The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP bu ### Token Persistence Token save/load via `PlayerPrefs` is demonstrated in the SpacetimeManager singleton above. Persisting the server-issued token and passing it back on reconnect keeps the same identity; without a saved token the server issues a new identity in the `OnConnect` callback. This token does not expire and a lost one can't be recovered, so self-issued identities are for development. For production, authenticate with an OIDC provider such as SpacetimeAuth, which handles token lifecycle. - diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md index c0dfa43d476..ff383b8acd9 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md @@ -74,6 +74,8 @@ The `SpacetimeDBNetworkManager` is a simple script which hooks into the Unity `U When you build a new connection to SpacetimeDB, that connection will be added to and managed by the `SpacetimeDBNetworkManager` automatically. +The SDK also resets its own internal static Unity state when entering Play Mode, including projects that disable Domain Reloading in Unity's Enter Play Mode Options. Your own static fields and singleton references are still your responsibility. + Click on the `GameManager` object in the scene and click **Add Component**. Search for and select the `SpacetimeDBNetworkManager` to add it to your `GameManager` object. Our Unity project is all set up! If you press play, it will show a blank screen, but it should start the game without any errors. Now we're ready to get started on our SpacetimeDB server module, so we have something to connect to! diff --git a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md index e5cd26b9363..b8c8be459ad 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md @@ -67,6 +67,8 @@ https://github.com/clockworklabs/com.clockworklabs.spacetimedbsdk.git (See also the [Unity Tutorial](../../00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md)) +The Unity package includes a `SpacetimeDBNetworkManager` component. Add one instance to a scene GameObject if you want the SDK to advance active connections from Unity's `Update` loop automatically. If you do not use the manager, call [`FrameTick`](#method-frametick) yourself every frame. + ## Generate module bindings Each SpacetimeDB client depends on some bindings specific to your module. Create a `module_bindings` directory in your project's directory and generate the C# interface files using the Spacetime CLI. From your project directory, run: @@ -220,6 +222,8 @@ class DbConnection { `FrameTick` will advance the connection until no work remains or until it is disconnected, then return rather than blocking. Games might arrange for this message to be called every frame. +In Unity projects, a `SpacetimeDBNetworkManager` component can call `FrameTick` for active connections automatically. Use either the manager or your own update loop; without one of them, callbacks will not be invoked. + It is not advised to run `FrameTick` on a background thread, since it modifies [`dbConnection.Db`](#property-db). If main thread code is also accessing the `Db`, it may observe data races when `FrameTick` runs on another thread. (Note that the SDK already does most of the work for parsing messages on a background thread. `FrameTick()` does the minimal amount of work needed to apply updates to the `Db`.) diff --git a/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md b/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md index 35b03022b61..9502eff90fb 100644 --- a/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md +++ b/docs/docs/00300-resources/00100-how-to/00050-troubleshooting.md @@ -66,7 +66,8 @@ You may need to advance your connection by calling one of the following methods: | Rust (browser only) | `conn.run_background_task()` | Spawn a task to continuously advance the connection. | | Rust | `conn.run_async()` | A `Future` which you can `await` or poll to advance the connection. | | Rust | `conn.frame_tick()` | In single-threaded games, call this every frame to advance the connection. | -| C# | `Conn.FrameTick()` | Call this from your game or application update loop. If you use a separate loop, keep `Conn.Db` access on that same thread or synchronize access. | +| C# | `Conn.FrameTick()` | In native C# clients, call this from your game or application update loop. If you use a separate loop, keep `Conn.Db` access on that same thread or synchronize access. | +| Unity | `SpacetimeDBNetworkManager` or `Conn.FrameTick()` | The Unity package can advance connections automatically when a single `SpacetimeDBNetworkManager` component is present in the scene. If you manage the loop yourself, call `FrameTick()` every frame. | | Unreal | `Conn->FrameTick()` or `Conn->SetAutoTicking(true)` | Call `FrameTick()` every frame, or enable auto-ticking once after building the connection. | | TypeScript | N/a | The TypeScript client SDK advances connections automatically. | diff --git a/skills/unity/SKILL.md b/skills/unity/SKILL.md index 61d523f1e97..aa604746793 100644 --- a/skills/unity/SKILL.md +++ b/skills/unity/SKILL.md @@ -82,11 +82,6 @@ public class SpacetimeManager : MonoBehaviour .Build(); } - void Update() - { - Connection?.FrameTick(); - } - void OnDestroy() { Connection?.Disconnect(); @@ -114,9 +109,9 @@ public class SpacetimeManager : MonoBehaviour --- -## FrameTick (Critical) +## FrameTick / Network Manager (Critical) -**`FrameTick()` must be called every frame in `Update()`.** The SDK queues all network messages and only processes them when you call `FrameTick()`. Without it, no callbacks fire and the client appears frozen. See the `Update()` method in the SpacetimeManager above. +The Unity package includes `SpacetimeDBNetworkManager`, a MonoBehaviour that advances active connections from Unity's `Update` loop. Add exactly one `SpacetimeDBNetworkManager` component to a scene GameObject, or call `Connection.FrameTick()` yourself every frame. Without one of these, no callbacks fire and the client appears frozen. **Thread safety**: `FrameTick()` processes messages on the calling thread (the main thread in Unity). Do NOT call it from a background thread. Do NOT access `conn.Db` from background threads. @@ -212,6 +207,9 @@ All SpacetimeDB SDK calls (`FrameTick`, `conn.Db` access, reducer calls) must ha ### Scene Loading Use `DontDestroyOnLoad(gameObject)` on the SpacetimeManager to prevent the connection from being destroyed during scene transitions. Without it, the connection drops every time you load a new scene. +### Domain Reloading +The SDK resets its own internal static Unity state when entering Play Mode, including projects that disable Domain Reloading in Unity's Enter Play Mode Options. Reset any static fields and singleton references in your own game code separately. + ### IL2CPP / AOT The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP builds: - Ensure generated bindings are up to date @@ -219,4 +217,3 @@ The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP bu ### Token Persistence Token save/load via `PlayerPrefs` is demonstrated in the SpacetimeManager singleton above. Persisting the server-issued token and passing it back on reconnect keeps the same identity; without a saved token the server issues a new identity in the `OnConnect` callback. This token does not expire and a lost one can't be recovered, so self-issued identities are for development. For production, authenticate with an OIDC provider such as SpacetimeAuth, which handles token lifecycle. - From 72f7085d3c7dacce42519ee61323d103cf97f7b4 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Mon, 10 Aug 2026 20:00:32 -0400 Subject: [PATCH 30/35] docs: fix React template project layout --- .../docs/00100-intro/00200-quickstarts/00100-react.md | 11 ++++++----- templates/react-ts/README.md | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/docs/00100-intro/00200-quickstarts/00100-react.md b/docs/docs/00100-intro/00200-quickstarts/00100-react.md index fb16ce69ff8..6ae206e872f 100644 --- a/docs/docs/00100-intro/00200-quickstarts/00100-react.md +++ b/docs/docs/00100-intro/00200-quickstarts/00100-react.md @@ -47,7 +47,7 @@ spacetime dev --template react-ts Your project contains both server and client code. - Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `client/src/App.tsx` to build your UI. + Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `src/App.tsx` to build your UI. ``` @@ -55,10 +55,11 @@ my-spacetime-app/ ├── spacetimedb/ # Your SpacetimeDB module │ └── src/ │ └── index.ts # Server-side logic -├── client/ # React frontend -│ └── src/ -│ ├── App.tsx -│ └── module_bindings/ # Auto-generated types +├── src/ # React frontend +│ ├── App.tsx +│ └── module_bindings/ # Auto-generated types +├── spacetime.json # Shared SpacetimeDB dev config +├── spacetime.local.json # Local database name └── package.json ``` diff --git a/templates/react-ts/README.md b/templates/react-ts/README.md index 8db4516126e..8a4ee015999 100644 --- a/templates/react-ts/README.md +++ b/templates/react-ts/README.md @@ -33,17 +33,18 @@ The template includes a basic React app connected to SpacetimeDB. Your project contains both server and client code. -Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `client/src/App.tsx` to build your UI. +Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `src/App.tsx` to build your UI. ``` my-spacetime-app/ ├── spacetimedb/ # Your SpacetimeDB module │ └── src/ │ └── index.ts # Server-side logic -├── client/ # React frontend -│ └── src/ -│ ├── App.tsx -│ └── module_bindings/ # Auto-generated types +├── src/ # React frontend +│ ├── App.tsx +│ └── module_bindings/ # Auto-generated types +├── spacetime.json # Shared SpacetimeDB dev config +├── spacetime.local.json # Local database name └── package.json ``` From d7c3e4770ac13aed2e1d0e5259d381c6b5a9ad62 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Mon, 10 Aug 2026 20:38:11 -0400 Subject: [PATCH 31/35] docs: address audit review comments --- .../00300-unity-tutorial/00400-part-3.md | 4 +- .../00300-unity-tutorial/00500-part-4.md | 2 +- .../00400-unreal-tutorial/00400-part-3.md | 4 +- .../00400-unreal-tutorial/00500-part-4.md | 2 +- .../00500-godot-tutorial/00400-part-3.md | 4 +- .../00500-godot-tutorial/00500-part-4.md | 2 +- .../00300-reducers/00300-reducers.md | 22 +++---- .../00300-reducers/00400-reducer-context.md | 12 ++-- .../00200-functions/00400-procedures.md | 20 +++--- .../00200-functions/00500-views.md | 66 +++++++++---------- .../00300-tables/00210-file-storage.md | 10 +-- .../00300-tables/00400-access-permissions.md | 8 +-- .../00300-tables/00500-schedule-tables.md | 38 +++++------ .../00300-tables/00550-event-tables.md | 14 ++-- .../00100-how-to/00600-migrating-to-2.0.md | 30 ++++----- 15 files changed, 119 insertions(+), 119 deletions(-) diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md index fb96a00797b..612042afcb3 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md @@ -394,14 +394,14 @@ SPACETIMEDB_INIT(init, ReducerContext ctx) { }); ctx.db[spawn_food_timer].insert(SpawnFoodTimer{ 0, - ScheduleAt::interval(TimeDuration::from_millis(500)), + ScheduleAt(TimeDuration::from_millis(500)), }); return Ok(); } ``` :::note -You can use `ScheduleAt::interval(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt::time(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. +You can use `ScheduleAt(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. ::: diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md index 08e8408142a..40aee6fd84c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00500-part-4.md @@ -512,7 +512,7 @@ Add the following to your `init` reducer to schedule the `move_all_players` redu ```cpp ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ 0, - ScheduleAt::interval(TimeDuration::from_millis(50)), + ScheduleAt(TimeDuration::from_millis(50)), }); ``` diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md index 2e653728464..1b388814d96 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md @@ -387,14 +387,14 @@ SPACETIMEDB_INIT(init, ReducerContext ctx) { }); ctx.db[spawn_food_timer].insert(SpawnFoodTimer{ 0, - ScheduleAt::interval(TimeDuration::from_millis(500)), + ScheduleAt(TimeDuration::from_millis(500)), }); return Ok(); } ``` :::note -You can use `ScheduleAt::interval(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt::time(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. +You can use `ScheduleAt(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. ::: diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md index ee4d8c14ab4..11233e3bcbc 100644 --- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00500-part-4.md @@ -510,7 +510,7 @@ Add the following to your `init` reducer to schedule the `move_all_players` redu ```cpp ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ 0, - ScheduleAt::interval(TimeDuration::from_millis(50)), + ScheduleAt(TimeDuration::from_millis(50)), }); ``` diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md index 11dfd9592f3..46291fe7202 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00400-part-3.md @@ -394,14 +394,14 @@ SPACETIMEDB_INIT(init, ReducerContext ctx) { }); ctx.db[spawn_food_timer].insert(SpawnFoodTimer{ 0, - ScheduleAt::interval(TimeDuration::from_millis(500)), + ScheduleAt(TimeDuration::from_millis(500)), }); return Ok(); } ``` :::note -You can use `ScheduleAt::interval(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt::time(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. +You can use `ScheduleAt(TimeDuration::from_millis(...))` to schedule a reducer call at an interval like we're doing here. SpacetimeDB will continue to call the reducer at this interval until you remove the row. You can also use `ScheduleAt(Timestamp::from_millis_since_epoch(...))` to specify a specific time at which to call a reducer once. SpacetimeDB will remove that row automatically after the reducer has been called. ::: diff --git a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md index 77477f88d14..71eff102a9c 100644 --- a/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md +++ b/docs/docs/00100-intro/00300-tutorials/00500-godot-tutorial/00500-part-4.md @@ -512,7 +512,7 @@ Add the following to your `init` reducer to schedule the `move_all_players` redu ```cpp ctx.db[move_all_players_timer].insert(MoveAllPlayersTimer{ 0, - ScheduleAt::interval(TimeDuration::from_millis(50)), + ScheduleAt(TimeDuration::from_millis(50)), }); ``` diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md index fd745b69f7e..9f8f0fec51c 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md @@ -558,21 +558,21 @@ import { ScheduleAt } from 'spacetimedb'; import { schema, t, table } from 'spacetimedb/server'; // Define a schedule table for the procedure -const fetch_schedule = table( - { name: 'fetch_schedule', scheduled: (): any => fetch_external_data }, +const fetchSchedule = table( + { name: 'fetch_schedule', scheduled: (): any => fetchExternalData }, { - scheduled_id: t.u64().primaryKey().autoInc(), - scheduled_at: t.scheduleAt(), + scheduledId: t.u64().primaryKey().autoInc(), + scheduledAt: t.scheduleAt(), url: t.string(), } ); -const spacetimedb = schema({ fetch_schedule }); +const spacetimedb = schema({ fetchSchedule }); export default spacetimedb; // The procedure to be scheduled -export const fetch_external_data = spacetimedb.procedure( - { arg: fetch_schedule.rowType }, +export const fetchExternalData = spacetimedb.procedure( + { arg: fetchSchedule.rowType }, t.unit(), (ctx, { arg }) => { const response = ctx.http.fetch(arg.url); @@ -583,9 +583,9 @@ export const fetch_external_data = spacetimedb.procedure( // From a reducer, schedule the procedure by inserting into the schedule table export const queueFetch = spacetimedb.reducer({ url: t.string() }, (ctx, { url }) => { - ctx.db.fetch_schedule.insert({ - scheduled_id: 0n, - scheduled_at: ScheduleAt.interval(0n), // Run immediately + ctx.db.fetchSchedule.insert({ + scheduledId: 0n, + scheduledAt: ScheduleAt.interval(0n), // Run immediately url, }); }); @@ -698,7 +698,7 @@ SPACETIMEDB_PROCEDURE(uint32_t, fetch_external_data, ProcedureContext ctx, Fetch // From a reducer, schedule the procedure by inserting into the schedule table SPACETIMEDB_REDUCER(queue_fetch, ReducerContext ctx, std::string url) { - auto scheduled_at = ScheduleAt::interval(TimeDuration::from_seconds(0)); // Run immediately + auto scheduled_at = ScheduleAt(TimeDuration::from_seconds(0)); // Run immediately FetchSchedule fetch_task{ 0, // scheduled_id - auto-increment will assign scheduled_at, // When to execute diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md index f1850fcf51f..3274f75fc57 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md +++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md @@ -324,19 +324,19 @@ Scheduled reducers and procedures are private by default in SpacetimeDB 2.x, so ```typescript import { schema, table, t } from 'spacetimedb/server'; -const scheduled_task = table( - { name: 'scheduled_task', scheduled: (): any => send_reminder }, +const scheduledTask = table( + { name: 'scheduled_task', scheduled: (): any => sendReminder }, { - task_id: t.u64().primaryKey().autoInc(), - scheduled_at: t.scheduleAt(), + taskId: t.u64().primaryKey().autoInc(), + scheduledAt: t.scheduleAt(), message: t.string(), } ); -const spacetimedb = schema({ scheduled_task }); +const spacetimedb = schema({ scheduledTask }); export default spacetimedb; -export const send_reminder = spacetimedb.reducer({ arg: scheduled_task.rowType }, (_ctx, { arg }) => { +export const sendReminder = spacetimedb.reducer({ arg: scheduledTask.rowType }, (_ctx, { arg }) => { console.log(`Reminder: ${arg.message}`); }); ``` diff --git a/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md b/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md index f96b8e238ec..615db33c756 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md +++ b/docs/docs/00200-core-concepts/00200-functions/00400-procedures.md @@ -128,7 +128,7 @@ This means there's no `ctx.db` field to access the database. Instead, procedure code must manage transactions explicitly with `ProcedureCtx.withTx`. ```typescript -const my_table = table( +const myTable = table( { name: "my_table" }, { a: t.u32(), @@ -136,12 +136,12 @@ const my_table = table( }, ) -const spacetimedb = schema({ my_table }); +const spacetimedb = schema({ myTable }); export default spacetimedb; -export const insert_a_value = spacetimedb.procedure({ a: t.u32(), b: t.u32() }, t.unit(), (ctx, { a, b }) => { +export const insertAValue = spacetimedb.procedure({ a: t.u32(), b: t.u32() }, t.unit(), (ctx, { a, b }) => { ctx.withTx(ctx => { - ctx.db.my_table.insert({ a, b }); + ctx.db.myTable.insert({ a, b }); }); return {}; }) @@ -323,12 +323,12 @@ Avoid capturing mutable state within functions passed to `with_tx`. For fallible database operations, you can throw an error inside the transaction function: ```typescript -export const maybe_insert_a_value = spacetimedb.procedure({ a: t.u32(), b: t.string() }, t.unit(), (ctx, { a, b }) => { +export const maybeInsertAValue = spacetimedb.procedure({ a: t.u32(), b: t.string() }, t.unit(), (ctx, { a, b }) => { ctx.withTx(ctx => { if (a < 10) { throw new SenderError("a is less than 10!"); } - ctx.db.my_table.insert({ a, b }); + ctx.db.myTable.insert({ a, b }); }); }) ``` @@ -1190,7 +1190,7 @@ A common use case for procedures is integrating with external APIs like OpenAI's import { schema, t, table, SenderError } from 'spacetimedb/server'; import { TimeDuration } from 'spacetimedb'; -const ai_message = table( +const aiMessage = table( { name: 'ai_message', public: true }, { user: t.identity(), @@ -1200,10 +1200,10 @@ const ai_message = table( } ); -const spacetimedb = schema({ ai_message }); +const spacetimedb = schema({ aiMessage }); export default spacetimedb; -export const ask_ai = spacetimedb.procedure( +export const askAi = spacetimedb.procedure( { prompt: t.string(), apiKey: t.string() }, t.string(), (ctx, { prompt, apiKey }) => { @@ -1235,7 +1235,7 @@ export const ask_ai = spacetimedb.procedure( // Store the conversation in the database ctx.withTx(txCtx => { - txCtx.db.ai_message.insert({ + txCtx.db.aiMessage.insert({ user: txCtx.sender, prompt, response: aiResponse, diff --git a/docs/docs/00200-core-concepts/00200-functions/00500-views.md b/docs/docs/00200-core-concepts/00200-functions/00500-views.md index d86af13564c..7e97e0078e3 100644 --- a/docs/docs/00200-core-concepts/00200-functions/00500-views.md +++ b/docs/docs/00200-core-concepts/00200-functions/00500-views.md @@ -40,20 +40,20 @@ const players = table( } ); -const player_levels = table( +const playerLevels = table( { name: 'player_levels', public: true }, { - player_id: t.u64().unique(), + playerId: t.u64().unique(), level: t.u64().index('btree'), } ); -const spacetimedb = schema({ players, player_levels }); +const spacetimedb = schema({ players, playerLevels }); export default spacetimedb; // At-most-one row: return Option via t.option(...) // Your function may return the row or null -export const my_player = spacetimedb.view( +export const myPlayer = spacetimedb.view( { name: 'my_player', public: true }, t.option(players.rowType), (ctx) => { @@ -70,13 +70,13 @@ const playerAndLevelRow = t.row('PlayerAndLevel', { }); // Multiple rows: return an array of rows via t.array(...) -export const players_for_level = spacetimedb.anonymousView( +export const playersForLevel = spacetimedb.anonymousView( { name: 'players_for_level', public: true }, t.array(playerAndLevelRow), (ctx) => { const out: Array<{ id: bigint; name: string; level: bigint }> = []; - for (const playerLevel of ctx.db.player_levels.level.filter(2n)) { - const p = ctx.db.players.id.find(playerLevel.player_id); + for (const playerLevel of ctx.db.playerLevels.level.filter(2n)) { + const p = ctx.db.players.id.find(playerLevel.playerId); if (p) out.push({ id: p.id, name: p.name, level: playerLevel.level }); } return out; @@ -327,7 +327,7 @@ This view returns the caller's own player data. Each connected client sees diffe ```typescript // Per-user: each client sees their own player -export const my_player = spacetimedb.view( +export const myPlayer = spacetimedb.view( { name: 'my_player', public: true }, t.option(players.rowType), (ctx) => { @@ -393,7 +393,7 @@ const spacetimedb = schema({ players }); export default spacetimedb; // Shared: same high scorers for all clients -export const high_scorers = spacetimedb.anonymousView( +export const highScorers = spacetimedb.anonymousView( { name: 'high_scorers', public: true }, t.array(players.rowType), (ctx) => { @@ -493,7 +493,7 @@ const entity = table( ); // Track which chunks each player is subscribed to -const player_chunk = table( +const playerChunk = table( { name: 'player_chunk', public: true }, { playerId: t.u64().primaryKey(), @@ -503,7 +503,7 @@ const player_chunk = table( ); // Shared: all players in chunk (0,0) share this view -export const entities_in_origin_chunk = spacetimedb.anonymousView( +export const entitiesInOriginChunk = spacetimedb.anonymousView( { name: 'entities_in_origin_chunk', public: true }, t.array(entity.rowType), (ctx) => { @@ -514,14 +514,14 @@ export const entities_in_origin_chunk = spacetimedb.anonymousView( ); // Per-user: returns entities in the chunk the player is currently in -export const entities_in_my_chunk = spacetimedb.view( +export const entitiesInMyChunk = spacetimedb.view( { name: 'entities_in_my_chunk', public: true }, t.array(entity.rowType), (ctx) => { const player = ctx.db.players.identity.find(ctx.sender); if (!player) return []; - const chunk = ctx.db.player_chunk.playerId.find(player.id); + const chunk = ctx.db.playerChunk.playerId.find(player.id); if (!chunk) return []; return Array.from(ctx.db.entity.chunkX.filter(chunk.chunkX)) @@ -735,7 +735,7 @@ const playerCountRow = t.row('PlayerCountRow', { count: t.u64(), }); -export const player_count = spacetimedb.anonymousView( +export const playerCount = spacetimedb.anonymousView( { name: 'player_count', public: true }, t.array(playerCountRow), (ctx) => [{ count: ctx.db.players.count() }] @@ -857,7 +857,7 @@ since it pushes work into the query engine, which can optimize and evaluate the ```typescript -export const high_scorers = spacetimedb.anonymousView( +export const highScorers = spacetimedb.anonymousView( { name: 'high_scorers', public: true }, t.array(players.rowType), (ctx) => { @@ -935,24 +935,24 @@ including reordering the join and starting from `moderators` if it thinks that w ```typescript // Procedural: row-by-row join in module code. -export const high_scoring_moderators_procedural = spacetimedb.anonymousView( +export const highScoringModeratorsProcedural = spacetimedb.anonymousView( { name: 'high_scoring_moderators_procedural', public: true }, t.array(players.rowType), (ctx) => { return Array.from(ctx.db.players.score.filter({ gte: 100n })) - .filter(p => ctx.db.moderators.player_id.find(p.id) != null); + .filter(p => ctx.db.moderators.playerId.find(p.id) != null); } ); // Query builder: equivalent logic pushed to the query engine. // The engine can reorder this join if it decides the smaller side is a better starting point. -export const high_scoring_moderators_declarative = spacetimedb.anonymousView( +export const highScoringModeratorsDeclarative = spacetimedb.anonymousView( { name: 'high_scoring_moderators_declarative', public: true }, t.array(players.rowType), (ctx) => { return ctx.from.players .where(p => p.score.gte(100n)) - .leftSemijoin(ctx.from.moderators, (p, m) => p.id.eq(m.player_id)); + .leftSemijoin(ctx.from.moderators, (p, m) => p.id.eq(m.playerId)); } ); ``` @@ -1067,27 +1067,27 @@ const players = table( } ); -const player_levels = table( +const playerLevels = table( { name: 'player_levels', public: true }, { - player_id: t.u64().unique(), + playerId: t.u64().unique(), level: t.u64().index('btree'), } ); -const spacetimedb = schema({ players, player_levels }); +const spacetimedb = schema({ players, playerLevels }); export default spacetimedb; -export const all_players = spacetimedb.anonymousView( +export const allPlayers = spacetimedb.anonymousView( { name: 'all_players', public: true }, t.array(players.rowType), (ctx) => ctx.from.players ); -export const all_player_levels = spacetimedb.anonymousView( +export const allPlayerLevels = spacetimedb.anonymousView( { name: 'all_player_levels', public: true }, - t.array(player_levels.rowType), - (ctx) => ctx.from.player_levels + t.array(playerLevels.rowType), + (ctx) => ctx.from.playerLevels ); ``` @@ -1221,7 +1221,7 @@ Use `where` to apply predicates. Chaining multiple filters combines them with lo ```typescript -export const high_scorers = spacetimedb.anonymousView( +export const highScorers = spacetimedb.anonymousView( { name: 'high_scorers', public: true }, t.array(players.rowType), (ctx) => { @@ -1337,22 +1337,22 @@ Semijoins keep rows from one side when a matching row exists on the other side. ```typescript -export const players_with_levels = spacetimedb.anonymousView( +export const playersWithLevels = spacetimedb.anonymousView( { name: 'players_with_levels', public: true }, t.array(players.rowType), (ctx) => { return ctx.from.players - .leftSemijoin(ctx.from.player_levels, (p, pl) => p.id.eq(pl.player_id)); + .leftSemijoin(ctx.from.playerLevels, (p, pl) => p.id.eq(pl.playerId)); } ); -export const levels_for_high_scorers = spacetimedb.anonymousView( +export const levelsForHighScorers = spacetimedb.anonymousView( { name: 'levels_for_high_scorers', public: true }, - t.array(player_levels.rowType), + t.array(playerLevels.rowType), (ctx) => { return ctx.from.players .where(p => p.score.gte(1000n)) - .rightSemijoin(ctx.from.player_levels, (p, pl) => p.id.eq(pl.player_id)) + .rightSemijoin(ctx.from.playerLevels, (p, pl) => p.id.eq(pl.playerId)) .where(pl => pl.level.gte(10n)); } ); @@ -1458,7 +1458,7 @@ const players = table( const spacetimedb = schema({ players }); export default spacetimedb; -export const my_players = spacetimedb.view( +export const myPlayers = spacetimedb.view( { name: 'my_players', public: true }, t.array(players.rowType), (ctx) => Array.from(ctx.db.players.owner.filter(ctx.sender)) diff --git a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md index 68087bcdda9..0e29291d1fe 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md +++ b/docs/docs/00200-core-concepts/00300-tables/00210-file-storage.md @@ -20,7 +20,7 @@ Store binary data using `Vec` (Rust), `List` (C#), `std::vector { // Delete existing avatar if present - ctx.db.user_avatar.userId.delete(userId); + ctx.db.userAvatar.userId.delete(userId); // Insert new avatar - ctx.db.user_avatar.insert({ + ctx.db.userAvatar.insert({ userId, mimeType, data, diff --git a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md index 666b68b5960..50bcc4abc22 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md +++ b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md @@ -568,7 +568,7 @@ Use views to return a custom type that omits sensitive columns. The view reads f import {schema, t, table} from 'spacetimedb/server'; // Private table with sensitive data -const user_account = table( +const userAccount = table( { name: 'user_account' }, // Private by default { id: t.u64().primaryKey().autoInc(), @@ -581,7 +581,7 @@ const user_account = table( } ); -const spacetimedb = schema({ user_account }); +const spacetimedb = schema({ userAccount }); export default spacetimedb; // Public type without sensitive columns @@ -592,12 +592,12 @@ const publicUserProfile = t.row('PublicUserProfile', { }); // Public view that returns the caller's profile without sensitive data -export const my_profile = spacetimedb.view( +export const myProfile = spacetimedb.view( { name: 'my_profile', public: true }, t.option(publicUserProfile), (ctx) => { // Look up the caller's account by their identity (unique index) - const user = ctx.db.user_account.identity.find(ctx.sender); + const user = ctx.db.userAccount.identity.find(ctx.sender); if (!user) return null; return { id: user.id, diff --git a/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md index 60d0b677c8d..c6a6a32c6e6 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md @@ -25,17 +25,17 @@ The table attribute uses `scheduled` (with a "d") because it refers to the **sch ```typescript const reminder = table( - { name: 'reminder', scheduled: (): any => send_reminder }, + { name: 'reminder', scheduled: (): any => sendReminder }, { - scheduled_id: t.u64().primaryKey().autoInc(), - scheduled_at: t.scheduleAt(), + scheduledId: t.u64().primaryKey().autoInc(), + scheduledAt: t.scheduleAt(), message: t.string(), } ); -export const send_reminder = spacetimedb.reducer({ arg: reminder.rowType }, (_ctx, { arg }) => { +export const sendReminder = spacetimedb.reducer({ arg: reminder.rowType }, (_ctx, { arg }) => { // Invoked automatically by the scheduler - // arg.message, arg.scheduled_at, arg.scheduled_id + // arg.message, arg.scheduledAt, arg.scheduledId }); ``` @@ -157,18 +157,18 @@ import { schema } from 'spacetimedb/server'; const spacetimedb = schema({ reminder }); // reminder table defined above export default spacetimedb; -export const schedule_periodic_tasks = spacetimedb.reducer((ctx) => { +export const schedulePeriodicTasks = spacetimedb.reducer((ctx) => { // Schedule to run every 5 seconds (5,000,000 microseconds) ctx.db.reminder.insert({ - scheduled_id: 0n, - scheduled_at: ScheduleAt.interval(5_000_000n), + scheduledId: 0n, + scheduledAt: ScheduleAt.interval(5_000_000n), message: "Check for updates", }); // Schedule to run every 100 milliseconds ctx.db.reminder.insert({ - scheduled_id: 0n, - scheduled_at: ScheduleAt.interval(100_000n), // 100ms in microseconds + scheduledId: 0n, + scheduledAt: ScheduleAt.interval(100_000n), // 100ms in microseconds message: "Game tick", }); }); @@ -232,14 +232,14 @@ fn schedule_periodic_tasks(ctx: &ReducerContext) { // Schedule to run every 5 seconds ctx.db[reminder].insert(Reminder{ 0, - ScheduleAt::interval(TimeDuration::from_seconds(5)), + ScheduleAt(TimeDuration::from_seconds(5)), "Check for updates" }); // Schedule to run every 100 milliseconds ctx.db[reminder].insert(Reminder{ 0, - ScheduleAt::interval(TimeDuration::from_millis(100)), + ScheduleAt(TimeDuration::from_millis(100)), "Game tick" }); ``` @@ -260,20 +260,20 @@ import { schema } from 'spacetimedb/server'; const spacetimedb = schema({ reminder }); // reminder table defined above export default spacetimedb; -export const schedule_timed_tasks = spacetimedb.reducer((ctx) => { +export const scheduleTimedTasks = spacetimedb.reducer((ctx) => { // Schedule for 10 seconds from now const tenSecondsFromNow = ctx.timestamp.microsSinceUnixEpoch + 10_000_000n; ctx.db.reminder.insert({ - scheduled_id: 0n, - scheduled_at: ScheduleAt.time(tenSecondsFromNow), + scheduledId: 0n, + scheduledAt: ScheduleAt.time(tenSecondsFromNow), message: "Your auction has ended", }); // Schedule for a specific Unix timestamp (microseconds since epoch) const targetTime = 1735689600_000_000n; // Jan 1, 2025 00:00:00 UTC ctx.db.reminder.insert({ - scheduled_id: 0n, - scheduled_at: ScheduleAt.time(targetTime), + scheduledId: 0n, + scheduledAt: ScheduleAt.time(targetTime), message: "Happy New Year!", }); }); @@ -343,14 +343,14 @@ fn schedule_timed_tasks(ctx: &ReducerContext) { Timestamp tenSecondsFromNow = ctx.timestamp + TimeDuration::from_seconds(10); ctx.db[reminder].insert(Reminder{ 0, - ScheduleAt::time(tenSecondsFromNow), + ScheduleAt(tenSecondsFromNow), "Your auction has ended" }); // Schedule for immediate execution (current timestamp) ctx.db[reminder].insert(Reminder{ 0, - ScheduleAt::time(ctx.timestamp), + ScheduleAt(ctx.timestamp), "Process now" }); ``` diff --git a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md index 306fa92272c..8922cde8884 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md @@ -21,18 +21,18 @@ To declare a table as an event table, add the `event` attribute to the table def ```typescript -const damage_event = table({ +const damageEvent = table({ name: 'damage_event', public: true, event: true, }, { - entity_id: t.identity(), + entityId: t.identity(), damage: t.u32(), source: t.string(), }); const spacetimedb = schema({ - damage_event, + damageEvent, }); export default spacetimedb; ``` @@ -93,13 +93,13 @@ To publish an event, simply insert a row into the event table from within a redu ```typescript export const attack = spacetimedb.reducer( - { target_id: t.identity(), damage: t.u32() }, - (ctx, { target_id, damage }) => { + { targetId: t.identity(), damage: t.u32() }, + (ctx, { targetId, damage }) => { // Game logic... // Publish the event - ctx.db.damage_event.insert({ - entity_id: target_id, + ctx.db.damageEvent.insert({ + entityId: targetId, damage, source: "melee_attack", }); diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index 9069559e07d..e70bad128de 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -190,15 +190,15 @@ spacetimedb.reducer('deal_damage', { target: t.identity(), amount: t.u32() }, (c **Server (module) -- after:** ```typescript // 2.0 server -- explicitly publish events via an event table -const damage_event = table({ name: 'damage_event', event: true }, { +const damageEvent = table({ name: 'damage_event', event: true }, { target: t.identity(), amount: t.u32(), }) -// schema() takes an object: schema({ damage_event }), never schema(damage_event) -const spacetimedb = schema({ damage_event }); +// schema() takes an object: schema({ damageEvent }), never schema(damageEvent) +const spacetimedb = schema({ damageEvent }); export const dealDamage = spacetimedb.reducer({ target: t.identity(), amount: t.u32() }, (ctx, { target, amount }) => { - ctx.db.damage_event.insert({ target, amount }); + ctx.db.damageEvent.insert({ target, amount }); }); ``` @@ -1084,14 +1084,14 @@ SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx) { In 2.0 modules, only columns with a `.primaryKey()` constraint expose an `update` method, whereas previously, `.unique()` constraints also provided that method. The previous behavior led to confusion, as only updates which preserved the value in the primary key column resulted in `onUpdate` callbacks being invoked on the client. ```typescript -const my_table = table({ name: 'my_table' }, { +const myTable = table({ name: 'my_table' }, { id: t.u32().unique(), name: t.string(), }) // 1.0 -- REMOVED in 2.0 spacetimedb.reducer('my_reducer', ctx => { - ctx.db.my_table.id.update({ + ctx.db.myTable.id.update({ id: 1, name: "Foobar", }); @@ -1100,8 +1100,8 @@ spacetimedb.reducer('my_reducer', ctx => { // 2.0 -- Perform a delete followed by an insert // OR change the `.unique()` constraint into `.primaryKey()` constraint spacetimedb.reducer(ctx => { - ctx.db.my_table.id.delete(1); - ctx.db.my_table.insert({ + ctx.db.myTable.id.delete(1); + ctx.db.myTable.insert({ id: 1, name: "Foobar" }); @@ -1366,14 +1366,14 @@ spacetimedb.reducer('runMyTimer', myTimer.rowType, (ctx, timer) => { ``` ```typescript -const my_timer = table({ name: 'my_timer', scheduled: (): any => runMyTimer }, { +const myTimer = table({ name: 'my_timer', scheduled: (): any => runMyTimer }, { scheduledId: t.u64().primaryKey().autoInc(), scheduledAt: t.scheduleAt(), }); -const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) // 2.0 -- Can only be called by the database -export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { // Do stuff }) ``` @@ -1479,17 +1479,17 @@ In the rare event that you have a reducer or procedure which is intended to be i ```typescript -const my_timer = table({ name: 'my_timer', scheduled: (): any => runMyTimerPrivate }, { +const myTimer = table({ name: 'my_timer', scheduled: (): any => runMyTimerPrivate }, { scheduledId: t.u64().primaryKey().autoInc(), scheduledAt: t.scheduleAt(), }); -const spacetimedb = schema({ my_timer }); // schema({ table }), never schema(table) +const spacetimedb = schema({ myTimer }); // schema({ table }), never schema(table) -export const runMyTimerPrivate = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { +export const runMyTimerPrivate = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { // Do stuff... }); -export const runMyTimer = spacetimedb.reducer({ arg: my_timer.rowType }, (ctx, { arg }) => { +export const runMyTimer = spacetimedb.reducer({ arg: myTimer.rowType }, (ctx, { arg }) => { // Same logic as runMyTimerPrivate — extract to a helper if needed }); ``` From ea144d715f18a4e87fac29702aadbb5a7a67640c Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 11 Aug 2026 08:10:48 -0400 Subject: [PATCH 32/35] docs: fix scheduled table id examples --- .../00300-tables/00500-schedule-tables.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md index c6a6a32c6e6..24c2a99284b 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md @@ -56,7 +56,7 @@ public static partial class Module { [SpacetimeDB.PrimaryKey] [SpacetimeDB.AutoInc] - public ulong Id; + public ulong ScheduledId; public uint UserId; public string Message; public ScheduleAt ScheduledAt; @@ -81,7 +81,7 @@ use std::time::Duration; pub struct Reminder { #[primary_key] #[auto_inc] - id: u64, + scheduled_id: u64, user_id: u32, message: String, scheduled_at: ScheduleAt, @@ -96,7 +96,7 @@ fn send_reminder(ctx: &ReducerContext, reminder: Reminder) -> Result<(), String> #[reducer(init)] fn init(ctx: &ReducerContext) { ctx.db.reminder_schedule().insert(Reminder { - id: 0, + scheduled_id: 0, user_id: 0, message: "Game tick".to_string(), scheduled_at: ScheduleAt::Interval(Duration::from_millis(50).into()), @@ -186,6 +186,7 @@ public static partial class Module // Schedule to run every 5 seconds ctx.Db.Reminder.Insert(new Reminder { + ScheduledId = 0, Message = "Check for updates", ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromSeconds(5)) }); @@ -193,6 +194,7 @@ public static partial class Module // Schedule to run every 100 milliseconds ctx.Db.Reminder.Insert(new Reminder { + ScheduledId = 0, Message = "Game tick", ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromMilliseconds(100)) }); @@ -211,14 +213,14 @@ use std::time::Duration; fn schedule_periodic_tasks(ctx: &ReducerContext) { // Schedule to run every 5 seconds ctx.db.reminder().insert(Reminder { - id: 0, + scheduled_id: 0, message: "Check for updates".to_string(), scheduled_at: ScheduleAt::Interval(Duration::from_secs(5).into()), }); // Schedule to run every 100 milliseconds ctx.db.reminder().insert(Reminder { - id: 0, + scheduled_id: 0, message: "Game tick".to_string(), scheduled_at: ScheduleAt::Interval(Duration::from_millis(100).into()), }); @@ -294,6 +296,7 @@ public static partial class Module var tenSecondsFromNow = ctx.Timestamp + new TimeDuration(10_000_000); ctx.Db.Reminder.Insert(new Reminder { + ScheduledId = 0, Message = "Your auction has ended", ScheduledAt = new ScheduleAt.Time(tenSecondsFromNow) }); @@ -302,6 +305,7 @@ public static partial class Module var targetTime = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero); ctx.Db.Reminder.Insert(new Reminder { + ScheduledId = 0, Message = "Happy New Year!", ScheduledAt = new ScheduleAt.Time(targetTime) }); @@ -321,14 +325,14 @@ fn schedule_timed_tasks(ctx: &ReducerContext) { // Schedule for 10 seconds from now let ten_seconds_from_now = ctx.timestamp + Duration::from_secs(10); ctx.db.reminder().insert(Reminder { - id: 0, + scheduled_id: 0, message: "Your auction has ended".to_string(), scheduled_at: ScheduleAt::Time(ten_seconds_from_now), }); // Schedule for immediate execution (current timestamp) ctx.db.reminder().insert(Reminder { - id: 0, + scheduled_id: 0, message: "Process now".to_string(), scheduled_at: ScheduleAt::Time(ctx.timestamp.clone()), }); From ccfbeb6b28a0872afe9db7275c493e373e5c48a3 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 11 Aug 2026 09:53:55 -0400 Subject: [PATCH 33/35] docs: address Copilot audit review --- .../00100-getting-started/00400-key-architecture.md | 4 +++- .../docs/00100-intro/00300-tutorials/00100-chat-app.md | 7 ++----- .../00300-tables/00550-event-tables.md | 2 +- .../00600-clients/00600-csharp-reference.md | 2 +- .../00600-clients/00700-typescript-reference.md | 2 +- .../00100-how-to/00600-migrating-to-2.0.md | 10 +++++----- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md index 10ba131faef..0a9b2f261e0 100644 --- a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md +++ b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md @@ -345,9 +345,11 @@ ctx.procedures.makeRequest().then( -C# modules can define procedures: +C# procedures are currently unstable. To use them, add `#pragma warning disable STDB_UNSTABLE` at the top of your file: ```csharp +#pragma warning disable STDB_UNSTABLE + [SpacetimeDB.Procedure] public static string MakeRequest(ProcedureContext ctx) { diff --git a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md index 954fb903712..3b6321b140f 100644 --- a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md +++ b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md @@ -2057,12 +2057,9 @@ The `ReducerEventContext` of the callback, which contains an `Event` that contai It also takes a variable amount of additional arguments that match the reducer's arguments. -These callbacks will be invoked in one of two cases: +These callbacks are invoked for reducer calls made by this connection, whether the reducer commits successfully or fails. -1. If the reducer was successful and altered any of our subscribed rows. -2. If we requested an invocation which failed. - -Note that a status of `Failed` or `OutOfEnergy` implies that the caller identity is our own identity. +Note that the caller identity is our own identity for these callbacks. We already handle successful `SetName` invocations using our `User.OnUpdate` callback, but if the module rejects a user's chosen name, we'd like that user's diff --git a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md index 8922cde8884..e109d892ef4 100644 --- a/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md +++ b/docs/docs/00200-core-concepts/00300-tables/00550-event-tables.md @@ -175,7 +175,7 @@ This behavior follows naturally from the fact that event table rows are never me ## Subscribing to Events -On the client side, event tables are subscribed to with explicit queries, just like regular tables. Subscribe-all helpers such as `subscribeToAllTables`, `SubscribeToAllTables`, and `subscribe_to_all_tables` do not include event tables. Once subscribed, event table rows are never stored in the client cache. Calling `count()` on an event table always returns 0, and `iter()` always yields no rows. Instead, you observe events through `on_insert` callbacks, which fire for each row that was inserted during the transaction. +On the client side, event tables are subscribed to like regular tables: either through subscribe-all helpers such as `subscribeToAllTables`, `SubscribeToAllTables`, and `subscribe_to_all_tables`, or through explicit typed queries. Once subscribed, event table rows are never stored in the client cache. Calling `count()` on an event table always returns 0, and `iter()` always yields no rows. Instead, you observe events through `on_insert` callbacks, which fire for each row that was inserted during the transaction. Because event table rows are ephemeral, only `on_insert` callbacks are available. There are no `on_delete`, `on_update`, or `on_before_delete` callbacks, since rows are never present in the client state to be deleted or updated. diff --git a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md index b8c8be459ad..d3c38196018 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md @@ -429,7 +429,7 @@ class SubscriptionBuilder } ``` -Subscribe to all rows from all public non-event tables. Event tables are excluded and must be subscribed to with explicit queries. This method is provided as a convenience for simple clients. The subscription initiated by `SubscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. +Subscribe to all rows from all public tables, including public event tables. This method is provided as a convenience for simple clients. The subscription initiated by `SubscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. #### Type `TypedSubscriptionBuilder` diff --git a/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md b/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md index 657ff8397b3..4980dc1b9f5 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00700-typescript-reference.md @@ -384,7 +384,7 @@ class SubscriptionBuilder { } ``` -Subscribe to all rows from all public non-event tables. Event tables are excluded and must be subscribed to with explicit queries. This method is provided as a convenience for simple clients. The subscription initiated by `subscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. +Subscribe to all rows from all public tables, including public event tables. This method is provided as a convenience for simple clients. The subscription initiated by `subscribeToAllTables` cannot be canceled after it is initiated. You should [`subscribe` to specific queries](#method-subscribe) if you need fine-grained control over the lifecycle of your subscriptions. ## Query Builder API diff --git a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md index e70bad128de..ad5fa1ef966 100644 --- a/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md +++ b/docs/docs/00300-resources/00100-how-to/00600-migrating-to-2.0.md @@ -424,7 +424,7 @@ Conn->SubscriptionBuilder() - On the client, `count()` always returns 0 and `iter()` is always empty. - Only `on_insert` callbacks are generated (no `on_delete` or `on_update`). - The `event` keyword in `#[table(..., event)]` marks the table as transient. -- Event tables must be subscribed to explicitly (they are excluded from `subscribeToAllTables` / `SubscribeToAllTables` / `subscribe_to_all_tables`). +- Event tables can be subscribed to with subscribe-all helpers or explicit typed queries, but clients observe them only through insert callbacks. ## Event Type Changes @@ -605,13 +605,13 @@ Unreal 2.0 now supports typed query-builder subscriptions in C++. Use `AddQuery( -Note that subscribing to event tables requires an explicit query: +Use explicit queries when you want to subscribe to event tables without subscribing to every public table: ```typescript -// Event tables are excluded from subscribe_to_all_tables(), so subscribe explicitly: +// Subscribe explicitly to an event table: import { tables } from "./module_bindings"; ctx.subscriptionBuilder() .onApplied((ctx) => { /* ... */ }) @@ -633,7 +633,7 @@ conn.SubscriptionBuilder() ```rust -// Event tables are excluded from subscribe_to_all_tables(), so subscribe explicitly: +// Subscribe explicitly to an event table: ctx.subscription_builder() .on_applied(|ctx| { /* ... */ }) .add_query(|q| q.from.damage_event()) @@ -644,7 +644,7 @@ ctx.subscription_builder() ```cpp -// Event tables are excluded from SubscribeToAllTables(), so subscribe explicitly: +// Subscribe explicitly to an event table: Conn->SubscriptionBuilder() ->OnApplied(OnAppliedDelegate) ->OnError(OnErrorDelegate) From 881481ab33c4f485ad1233736ef9fb2b050c076d Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 11 Aug 2026 10:36:09 -0400 Subject: [PATCH 34/35] docs: address follow-up review comments --- .../00100-getting-started/00400-key-architecture.md | 4 +--- .../00200-core-concepts/00600-clients/00300-connection.md | 2 +- .../00600-clients/00600-csharp-reference.md | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md index 0a9b2f261e0..10ba131faef 100644 --- a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md +++ b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md @@ -345,11 +345,9 @@ ctx.procedures.makeRequest().then( -C# procedures are currently unstable. To use them, add `#pragma warning disable STDB_UNSTABLE` at the top of your file: +C# modules can define procedures: ```csharp -#pragma warning disable STDB_UNSTABLE - [SpacetimeDB.Procedure] public static string MakeRequest(ProcedureContext ctx) { diff --git a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md index 92e310094fe..009053f2aaf 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00300-connection.md +++ b/docs/docs/00200-core-concepts/00600-clients/00300-connection.md @@ -399,7 +399,7 @@ Conn->Disconnect(); :::note[Reconnection behavior] -Lower-level `DbConnection` objects do not reconnect themselves. If you create a `DbConnection` directly and the connection is interrupted, create a new `DbConnection` to re-establish connectivity. +Lower-level `DbConnection` objects do not reconnect themselves. If you create a `DbConnection` directly and the connection is interrupted, create a new `DbConnection` to re-establish connectivity. We recommend implementing reconnection logic in your application if reliable connectivity is critical. The TypeScript React, Solid, and Svelte providers manage their connections through the SDK's shared connection manager. While a provider is mounted, that manager automatically rebuilds unexpectedly closed connections with exponential backoff and re-checks connection liveness when the page becomes visible, regains focus, returns online, or is restored from the back-forward cache. diff --git a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md index d3c38196018..e6de19c91d7 100644 --- a/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md +++ b/docs/docs/00200-core-concepts/00600-clients/00600-csharp-reference.md @@ -1095,6 +1095,8 @@ For a reducer named `send_message`, generated C# bindings use PascalCase names: - An invoke method, like `SendMessage(...)`. This requests that the module run the reducer. - A result event, like `OnSendMessage`. This event fires on the calling connection when SpacetimeDB reports that reducer call's result, including committed, failed, and out-of-energy statuses. +Subscribe to reducer result events with `+=` and unsubscribe with `-=`, as with any C# event. + Reducer result events are not global notifications. They are for reducer calls made by this connection. To notify other clients that something happened, write to a public table or event table and subscribe to it. ## Identify a client From e57096692fad528cc9b7062b714f9e7e2ed9c090 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Tue, 11 Aug 2026 11:44:56 -0400 Subject: [PATCH 35/35] docs: keep audit PR docs-only --- .../plugins/spacetimedb/skills/cli/SKILL.md | 4 ---- .../spacetimedb/skills/typescript-server/SKILL.md | 11 +++-------- .../plugins/spacetimedb/skills/unity/SKILL.md | 13 ++++++++----- skills/cli/SKILL.md | 4 ---- skills/typescript-server/SKILL.md | 11 +++-------- skills/unity/SKILL.md | 13 ++++++++----- templates/react-ts/README.md | 11 +++++------ 7 files changed, 27 insertions(+), 40 deletions(-) diff --git a/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md b/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md index f5cd3479128..534ad7ee4ec 100644 --- a/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md +++ b/codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md @@ -63,10 +63,6 @@ spacetime publish my-database --server local --yes spacetime publish my-database --delete-data=always --yes ``` -Bare `--delete-data` defaults to `always`. Keep simple interactive docs examples -bare, and use `--delete-data=always` for scripted/non-interactive examples that -also pass `--yes`. - ### Database Interaction ```bash diff --git a/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md b/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md index a870f680cbb..2e9326b3150 100644 --- a/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md +++ b/codex-plugin/plugins/spacetimedb/skills/typescript-server/SKILL.md @@ -52,8 +52,7 @@ import { ScheduleAt } from 'spacetimedb'; // for scheduled tables only ## Tables -`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field is optional; -when present, it overrides the canonical SQL name and should be snake_case: +`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field MUST be snake_case: ```typescript const entity = table( @@ -66,13 +65,9 @@ const entity = table( ); ``` -Options: `name` (optional canonical SQL name override), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` +Options: `name` (snake_case, recommended), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` -`ctx.db` accessors are the keys passed to `schema({ ... })`, verbatim: -`schema({ score_record })` -> `ctx.db.score_record`. The optional `name` -field overrides the canonical SQL name; it does not change the server -`ctx.db` accessor. Use snake_case keys matching the table `name`. Client -codegen converts case; server `ctx.db` does not. +`ctx.db` accessors are the keys passed to `schema({...})`, verbatim: `schema({ score_record })` → `ctx.db.score_record`. Use snake_case keys matching the table `name`. Client codegen converts case; server `ctx.db` does not. ## Column Types diff --git a/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md b/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md index aa604746793..61d523f1e97 100644 --- a/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md +++ b/codex-plugin/plugins/spacetimedb/skills/unity/SKILL.md @@ -82,6 +82,11 @@ public class SpacetimeManager : MonoBehaviour .Build(); } + void Update() + { + Connection?.FrameTick(); + } + void OnDestroy() { Connection?.Disconnect(); @@ -109,9 +114,9 @@ public class SpacetimeManager : MonoBehaviour --- -## FrameTick / Network Manager (Critical) +## FrameTick (Critical) -The Unity package includes `SpacetimeDBNetworkManager`, a MonoBehaviour that advances active connections from Unity's `Update` loop. Add exactly one `SpacetimeDBNetworkManager` component to a scene GameObject, or call `Connection.FrameTick()` yourself every frame. Without one of these, no callbacks fire and the client appears frozen. +**`FrameTick()` must be called every frame in `Update()`.** The SDK queues all network messages and only processes them when you call `FrameTick()`. Without it, no callbacks fire and the client appears frozen. See the `Update()` method in the SpacetimeManager above. **Thread safety**: `FrameTick()` processes messages on the calling thread (the main thread in Unity). Do NOT call it from a background thread. Do NOT access `conn.Db` from background threads. @@ -207,9 +212,6 @@ All SpacetimeDB SDK calls (`FrameTick`, `conn.Db` access, reducer calls) must ha ### Scene Loading Use `DontDestroyOnLoad(gameObject)` on the SpacetimeManager to prevent the connection from being destroyed during scene transitions. Without it, the connection drops every time you load a new scene. -### Domain Reloading -The SDK resets its own internal static Unity state when entering Play Mode, including projects that disable Domain Reloading in Unity's Enter Play Mode Options. Reset any static fields and singleton references in your own game code separately. - ### IL2CPP / AOT The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP builds: - Ensure generated bindings are up to date @@ -217,3 +219,4 @@ The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP bu ### Token Persistence Token save/load via `PlayerPrefs` is demonstrated in the SpacetimeManager singleton above. Persisting the server-issued token and passing it back on reconnect keeps the same identity; without a saved token the server issues a new identity in the `OnConnect` callback. This token does not expire and a lost one can't be recovered, so self-issued identities are for development. For production, authenticate with an OIDC provider such as SpacetimeAuth, which handles token lifecycle. + diff --git a/skills/cli/SKILL.md b/skills/cli/SKILL.md index f5cd3479128..534ad7ee4ec 100644 --- a/skills/cli/SKILL.md +++ b/skills/cli/SKILL.md @@ -63,10 +63,6 @@ spacetime publish my-database --server local --yes spacetime publish my-database --delete-data=always --yes ``` -Bare `--delete-data` defaults to `always`. Keep simple interactive docs examples -bare, and use `--delete-data=always` for scripted/non-interactive examples that -also pass `--yes`. - ### Database Interaction ```bash diff --git a/skills/typescript-server/SKILL.md b/skills/typescript-server/SKILL.md index a870f680cbb..2e9326b3150 100644 --- a/skills/typescript-server/SKILL.md +++ b/skills/typescript-server/SKILL.md @@ -52,8 +52,7 @@ import { ScheduleAt } from 'spacetimedb'; // for scheduled tables only ## Tables -`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field is optional; -when present, it overrides the canonical SQL name and should be snake_case: +`table(OPTIONS, COLUMNS)` takes two arguments. The `name` field MUST be snake_case: ```typescript const entity = table( @@ -66,13 +65,9 @@ const entity = table( ); ``` -Options: `name` (optional canonical SQL name override), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` +Options: `name` (snake_case, recommended), `public: true`, `event: true`, `scheduled: (): any => reducerRef`, `indexes: [...]` -`ctx.db` accessors are the keys passed to `schema({ ... })`, verbatim: -`schema({ score_record })` -> `ctx.db.score_record`. The optional `name` -field overrides the canonical SQL name; it does not change the server -`ctx.db` accessor. Use snake_case keys matching the table `name`. Client -codegen converts case; server `ctx.db` does not. +`ctx.db` accessors are the keys passed to `schema({...})`, verbatim: `schema({ score_record })` → `ctx.db.score_record`. Use snake_case keys matching the table `name`. Client codegen converts case; server `ctx.db` does not. ## Column Types diff --git a/skills/unity/SKILL.md b/skills/unity/SKILL.md index aa604746793..61d523f1e97 100644 --- a/skills/unity/SKILL.md +++ b/skills/unity/SKILL.md @@ -82,6 +82,11 @@ public class SpacetimeManager : MonoBehaviour .Build(); } + void Update() + { + Connection?.FrameTick(); + } + void OnDestroy() { Connection?.Disconnect(); @@ -109,9 +114,9 @@ public class SpacetimeManager : MonoBehaviour --- -## FrameTick / Network Manager (Critical) +## FrameTick (Critical) -The Unity package includes `SpacetimeDBNetworkManager`, a MonoBehaviour that advances active connections from Unity's `Update` loop. Add exactly one `SpacetimeDBNetworkManager` component to a scene GameObject, or call `Connection.FrameTick()` yourself every frame. Without one of these, no callbacks fire and the client appears frozen. +**`FrameTick()` must be called every frame in `Update()`.** The SDK queues all network messages and only processes them when you call `FrameTick()`. Without it, no callbacks fire and the client appears frozen. See the `Update()` method in the SpacetimeManager above. **Thread safety**: `FrameTick()` processes messages on the calling thread (the main thread in Unity). Do NOT call it from a background thread. Do NOT access `conn.Db` from background threads. @@ -207,9 +212,6 @@ All SpacetimeDB SDK calls (`FrameTick`, `conn.Db` access, reducer calls) must ha ### Scene Loading Use `DontDestroyOnLoad(gameObject)` on the SpacetimeManager to prevent the connection from being destroyed during scene transitions. Without it, the connection drops every time you load a new scene. -### Domain Reloading -The SDK resets its own internal static Unity state when entering Play Mode, including projects that disable Domain Reloading in Unity's Enter Play Mode Options. Reset any static fields and singleton references in your own game code separately. - ### IL2CPP / AOT The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP builds: - Ensure generated bindings are up to date @@ -217,3 +219,4 @@ The SpacetimeDB SDK uses code generation. If you encounter issues with IL2CPP bu ### Token Persistence Token save/load via `PlayerPrefs` is demonstrated in the SpacetimeManager singleton above. Persisting the server-issued token and passing it back on reconnect keeps the same identity; without a saved token the server issues a new identity in the `OnConnect` callback. This token does not expire and a lost one can't be recovered, so self-issued identities are for development. For production, authenticate with an OIDC provider such as SpacetimeAuth, which handles token lifecycle. + diff --git a/templates/react-ts/README.md b/templates/react-ts/README.md index 8a4ee015999..8db4516126e 100644 --- a/templates/react-ts/README.md +++ b/templates/react-ts/README.md @@ -33,18 +33,17 @@ The template includes a basic React app connected to SpacetimeDB. Your project contains both server and client code. -Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `src/App.tsx` to build your UI. +Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `client/src/App.tsx` to build your UI. ``` my-spacetime-app/ ├── spacetimedb/ # Your SpacetimeDB module │ └── src/ │ └── index.ts # Server-side logic -├── src/ # React frontend -│ ├── App.tsx -│ └── module_bindings/ # Auto-generated types -├── spacetime.json # Shared SpacetimeDB dev config -├── spacetime.local.json # Local database name +├── client/ # React frontend +│ └── src/ +│ ├── App.tsx +│ └── module_bindings/ # Auto-generated types └── package.json ```