diff --git a/docs/docs/90600-server-module-languages/00100-overview.md b/docs/docs/90600-server-module-languages/00100-overview.md
deleted file mode 100644
index 87e9ae91862..00000000000
--- a/docs/docs/90600-server-module-languages/00100-overview.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-title: Overview
-slug: /old-modules
----
-
-# Server Module Overview
-
-Server modules are the core of a SpacetimeDB application. They define the structure of the database and the server-side logic that processes and handles client requests. These functions are called reducers and are transactional, meaning they ensure data consistency and integrity. Reducers can perform operations such as inserting, updating, and deleting data in the database.
-
-In the following sections, we'll cover the basics of server modules and how to create and deploy them.
-
-## Supported Languages
-
-### Rust
-
-Rust is the only fully supported language for server modules. Rust is a great option for server modules because it is fast, safe, and has a small runtime.
-
-- [Rust Module Reference](/old-modules/rust)
-- [Rust Module Quickstart Guide](/docs/quickstarts/rust)
-
-### C
-
-We have C# support available. C# can be a good choice for developers who are already using Unity or .net for their client applications.
-
-- [C# Module Reference](/old-modules/c-sharp)
-- [C# Module Quickstart Guide](/docs/quickstarts/c-sharp)
diff --git a/docs/docs/90600-server-module-languages/00200-rust-reference.md b/docs/docs/90600-server-module-languages/00200-rust-reference.md
deleted file mode 100644
index 91f7faa3d2b..00000000000
--- a/docs/docs/90600-server-module-languages/00200-rust-reference.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-title: Rust Reference
-slug: /old-modules/rust
----
-
-# Rust Module SDK Reference
-
-The Rust Module SDK docs are [hosted on docs.rs](https://docs.rs/spacetimedb/latest/spacetimedb/).
diff --git a/docs/docs/90600-server-module-languages/00300-csharp-reference.md b/docs/docs/90600-server-module-languages/00300-csharp-reference.md
deleted file mode 100644
index 4d5aa223e9f..00000000000
--- a/docs/docs/90600-server-module-languages/00300-csharp-reference.md
+++ /dev/null
@@ -1,1505 +0,0 @@
----
-title: C# Reference
-slug: /old-modules/c-sharp
-toc_max_heading_level: 6
----
-
-# C# Module Library
-
-[SpacetimeDB](https://spacetimedb.com/) allows using the C# language to write server-side applications called **modules**. Modules, which run inside a relational database, have direct access to database tables, and expose public functions called **reducers** that can be invoked over the network. Clients connect directly to the database to read data.
-
-```text
- Client Application SpacetimeDB
-┌───────────────────────┐ ┌───────────────────────┐
-│ │ │ │
-│ ┌─────────────────┐ │ SQL Query │ ┌─────────────────┐ │
-│ │ Subscribed Data │<─────────────────────│ Database │ │
-│ └─────────────────┘ │ │ └─────────────────┘ │
-│ │ │ │ ^ │
-│ │ │ │ │ │
-│ v │ │ v │
-│ +─────────────────┐ │ call_reducer() │ ┌─────────────────┐ │
-│ │ Client Code │─────────────────────>│ Module Code │ │
-│ └─────────────────┘ │ │ └─────────────────┘ │
-│ │ │ │
-└───────────────────────┘ └───────────────────────┘
-```
-
-C# modules are written with the the C# Module Library (this package). They are built using the [dotnet CLI tool](https://learn.microsoft.com/en-us/dotnet/core/tools/) and deployed using the [`spacetime` CLI tool](https://spacetimedb.com/install). C# modules can import any [NuGet package](https://www.nuget.org/packages) that supports being compiled to WebAssembly.
-
-(Note: C# can also be used to write **clients** of SpacetimeDB databases, but this requires using a different library, the SpacetimeDB C# Client SDK. See the documentation on [clients] for more information.)
-
-This reference assumes you are familiar with the basics of C#. If you aren't, check out the [C# language documentation](https://learn.microsoft.com/en-us/dotnet/csharp/). For a guided introduction to C# Modules, see the [C# Module Quickstart](/docs/quickstarts/c-sharp).
-
-## Overview
-
-SpacetimeDB modules have two ways to interact with the outside world: tables and reducers.
-
-- [Tables](#tables) store data and optionally make it readable by [clients].
-
-- [Reducers](#reducers) are functions that modify data and can be invoked by [clients] over the network. They can read and write data in tables, and write to a private debug log.
-
-These are the only ways for a SpacetimeDB module to interact with the outside world. Calling functions from `System.IO` or `System.Net` inside a reducer will result in runtime errors.
-
-Declaring tables and reducers is straightforward:
-
-```csharp
-static partial class Module
-{
- [SpacetimeDB.Table(Name = "player")]
- public partial struct Player
- {
- public int Id;
- public string Name;
- }
-
- [SpacetimeDB.Reducer]
- public static void AddPerson(ReducerContext ctx, int Id, string Name) {
- ctx.Db.player.Insert(new Player { Id = Id, Name = Name });
- }
-}
-```
-
-Note that reducers don't return data directly; they can only modify the database. Clients connect directly to the database and use SQL to query [public](#public-and-private-tables) tables. Clients can also subscribe to a set of rows using SQL queries and receive streaming updates whenever any of those rows change.
-
-Tables and reducers in C# modules can use any type annotated with [`[SpacetimeDB.Type]`](#attribute-spacetimedbtype).
-
-
-
-## Setup
-
-To create a C# module, install the [`spacetime` CLI tool](https://spacetimedb.com/install) in your preferred shell. Navigate to your work directory and run the following command:
-
-```bash
-spacetime init --lang csharp --project-path my-project-directory my-spacetimedb-project
-```
-
-This creates a `dotnet` project in `my-project-directory` with the following `StdbModule.csproj`:
-
-```xml
-
-
-
- net8.0
- wasi-wasm
- enable
- enable
-
-
-
-
-
-
-
-```
-
-:::note
-
-It is important to not change the `StdbModule.csproj` name because SpacetimeDB assumes that this will be the name of the file.
-
-:::
-
-This is a standard `csproj`, with the exception of the line `wasi-wasm`.
-This line is important: it allows the project to be compiled to a WebAssembly module.
-
-The project's `Lib.cs` will contain the following skeleton:
-
-```csharp
-public static partial class Module
-{
- [SpacetimeDB.Table]
- public partial struct Person
- {
- [SpacetimeDB.AutoInc]
- [SpacetimeDB.PrimaryKey]
- public int Id;
- public string Name;
- public int Age;
- }
-
- [SpacetimeDB.Reducer]
- public static void Add(ReducerContext ctx, string name, int age)
- {
- var person = ctx.Db.Person.Insert(new Person { Name = name, Age = age });
- Log.Info($"Inserted {person.Name} under #{person.Id}");
- }
-
- [SpacetimeDB.Reducer]
- public static void SayHello(ReducerContext ctx)
- {
- foreach (var person in ctx.Db.Person.Iter())
- {
- Log.Info($"Hello, {person.Name}!");
- }
- Log.Info("Hello, World!");
- }
-}
-```
-
-This skeleton declares a [table](#tables) and some [reducers](#reducers).
-
-You can also add some [lifecycle reducers](#lifecycle-reducers) to the `Module` class using the following code:
-
-```csharp
-[Reducer(ReducerKind.Init)]
-public static void Init(ReducerContext ctx)
-{
- // Run when the module is first loaded.
-}
-
-[Reducer(ReducerKind.ClientConnected)]
-public static void ClientConnected(ReducerContext ctx)
-{
- // Called when a client connects.
-}
-
-[Reducer(ReducerKind.ClientDisconnected)]
-public static void ClientDisconnected(ReducerContext ctx)
-{
- // Called when a client connects.
-}
-```
-
-To compile the project, run the following command:
-
-```bash
-spacetime build
-```
-
-SpacetimeDB requires a WebAssembly-compatible `dotnet` toolchain. If the `spacetime` cli finds a compatible version of [`dotnet`](https://rustup.rs/) that it can run, it will automatically install the `wasi-experimental` workload and use it to build your application. This can also be done manually using the command:
-
-```bash
-dotnet workload install wasi-experimental
-```
-
-If you are managing your dotnet installation in some other way, you will need to install the `wasi-experimental` workload yourself.
-
-To build your application and upload it to the public SpacetimeDB network, run:
-
-```bash
-spacetime login
-```
-
-And then:
-
-```bash
-spacetime publish [MY_DATABASE_NAME]
-```
-
-For example:
-
-```bash
-spacetime publish silly_demo_app
-```
-
-When you publish your module, a database named `silly_demo_app` will be created with the requested tables, and the module will be installed inside it.
-
-The output of `spacetime publish` will end with a line:
-
-```text
-Created new database with name: , identity:
-```
-
-This name is the human-readable name of the created database, and the hex string is its [`Identity`](#struct-identity). These distinguish the created database from the other databases running on the SpacetimeDB network. They are used when administering the application, for example using the [`spacetime logs `](#class-log) command. You should probably write the database name down in a text file so that you can remember it.
-
-After modifying your project, you can run:
-
-`spacetime publish `
-
-to update the module attached to your database. Note that SpacetimeDB tries to [automatically migrate](#automatic-migrations) your database schema whenever you run `spacetime publish`.
-
-You can also generate code for clients of your module using the `spacetime generate` command. See the [client SDK documentation] for more information.
-
-## How it works
-
-Under the hood, SpacetimeDB modules are WebAssembly modules that import a [specific WebAssembly ABI](https://spacetimedb.com/docs/webassembly-abi) and export a small number of special functions. This is automatically configured when you add the `SpacetimeDB.Runtime` package as a dependency of your application.
-
-The SpacetimeDB host is an application that hosts SpacetimeDB databases. [Its source code is available](https://github.com/clockworklabs/SpacetimeDB) under [the Business Source License with an Additional Use Grant](https://github.com/clockworklabs/SpacetimeDB/blob/master/LICENSE.txt). You can run your own host, or you can upload your module to the public SpacetimeDB network. The network will create a database for you and install your module in it to serve client requests.
-
-### In More Detail: Publishing a Module
-
-The `spacetime publish [DATABASE_IDENTITY]` command compiles a module and uploads it to a SpacetimeDB host. After this:
-
-- The host finds the database with the requested `DATABASE_IDENTITY`.
- - (Or creates a fresh database and identity, if no identity was provided).
-- The host loads the new module and inspects its requested database schema. If there are changes to the schema, the host tries perform an [automatic migration](#automatic-migrations). If the migration fails, publishing fails.
-- The host terminates the old module attached to the database.
-- The host installs the new module into the database. It begins running the module's [lifecycle reducers](#lifecycle-reducers) and [scheduled reducers](#scheduled-reducers), starting with the `Init` reducer.
-- The host begins allowing clients to call the module's reducers.
-
-From the perspective of clients, this process is seamless. Open connections are maintained and subscriptions continue functioning. [Automatic migrations](#automatic-migrations) forbid most table changes except for adding new tables, so client code does not need to be recompiled.
-However:
-
-- Clients may witness a brief interruption in the execution of scheduled reducers (for example, game loops.)
-- New versions of a module may remove or change reducers that were previously present. Client code calling those reducers will receive runtime errors.
-
-## Tables
-
-Tables are declared using the `[SpacetimeDB.Table]` attribute.
-
-This macro is applied to a C# `partial class` or `partial struct` with named fields. (The `partial` modifier is required to allow code generation to add methods.) All of the fields of the table must be marked with [`[SpacetimeDB.Type]`](#attribute-spacetimedbtype).
-
-The resulting type is used to store rows of the table. It's a normal class (or struct). Row values are not special -- operations on row types do not, by themselves, modify the table. Instead, a [`ReducerContext`](#class-reducercontext) is needed to get a handle to the table.
-
-```csharp
-public static partial class Module {
-
- ///
- /// A Person is a row of the table person.
- ///
- [SpacetimeDB.Table(Name = "person", Public)]
- public partial struct Person {
- [SpacetimeDB.PrimaryKey]
- [SpacetimeDB.AutoInc]
- ulong Id;
- [SpacetimeDB.Index.BTree]
- string Name;
- }
-
- // `Person` is a normal C# struct type.
- // Operations on a `Person` do not, by themselves, do anything.
- // The following function does not interact with the database at all.
- public static void DoNothing() {
- // Creating a `Person` DOES NOT modify the database.
- var person = new Person { Id = 0, Name = "Joe Average" };
- // Updating a `Person` DOES NOT modify the database.
- person.Name = "Joanna Average";
- // Deallocating a `Person` DOES NOT modify the database.
- person = null;
- }
-
- // To interact with the database, you need a `ReducerContext`,
- // which is provided as the first parameter of any reducer.
- [SpacetimeDB.Reducer]
- public static void DoSomething(ReducerContext ctx) {
- // The following inserts a row into the table:
- var examplePerson = ctx.Db.person.Insert(new Person { id = 0, name = "Joe Average" });
-
- // `examplePerson` is a COPY of the row stored in the database.
- // If we update it:
- examplePerson.name = "Joanna Average".to_string();
- // Our copy is now updated, but the database's copy is UNCHANGED.
- // To push our change through, we can call `UniqueIndex.Update()`:
- examplePerson = ctx.Db.person.Id.Update(examplePerson);
- // Now the database and our copy are in sync again.
-
- // We can also delete the row in the database using `UniqueIndex.Delete()`.
- ctx.Db.person.Id.Delete(examplePerson.Id);
- }
-}
-```
-
-(See [reducers](#reducers) for more information on declaring reducers.)
-
-This library generates a custom API for each table, depending on the table's name and structure.
-
-All tables support getting a handle implementing the [`ITableView`](#interface-itableview) interface from a [`ReducerContext`](#class-reducercontext), using:
-
-```text
-ctx.Db.{table_name}
-```
-
-For example,
-
-```csharp
-ctx.Db.person
-```
-
-[Unique and primary key columns](#unique-and-primary-key-columns) and [indexes](#indexes) generate additional accessors, such as `ctx.Db.person.Id` and `ctx.Db.person.Name`.
-
-### Interface `ITableView`
-
-```csharp
-namespace SpacetimeDB.Internal;
-
-public interface ITableView
- where Row : IStructuralReadWrite, new()
-{
- /* ... */
-}
-```
-
-
-
-Implemented for every table handle generated by the [`Table`](#tables) attribute.
-For a table named `{name}`, a handle can be extracted from a [`ReducerContext`](#class-reducercontext) using `ctx.Db.{name}`. For example, `ctx.Db.person`.
-
-Contains methods that are present for every table handle, regardless of what unique constraints
-and indexes are present.
-
-The type `Row` is the type of rows in the table.
-
-| Name | Description |
-| --------------------------------------------- | ----------------------------- |
-| [Method `Insert`](#method-itableviewinsert) | Insert a row into the table |
-| [Method `Delete`](#method-itableviewdelete) | Delete a row from the table |
-| [Method `Iter`](#method-itableviewiter) | Iterate all rows of the table |
-| [Property `Count`](#property-itableviewcount) | Count all rows of the table |
-
-#### Method `ITableView.Insert`
-
-```csharp
-Row Insert(Row row);
-```
-
-Inserts `row` into the table.
-
-The return value is the inserted row, with any auto-incrementing columns replaced with computed values.
-The `insert` method always returns the inserted row, even when the table contains no auto-incrementing columns.
-
-(The returned row is a copy of the row in the database.
-Modifying this copy does not directly modify the database.
-See [`UniqueIndex.Update()`](#method-uniqueindexupdate) if you want to update the row.)
-
-Throws an exception if inserting the row violates any constraints.
-
-Inserting a duplicate row in a table is a no-op,
-as SpacetimeDB is a set-semantic database.
-
-#### Method `ITableView.Delete`
-
-```csharp
-bool Delete(Row row);
-```
-
-Deletes a row equal to `row` from the table.
-
-Returns `true` if the row was present and has been deleted,
-or `false` if the row was not present and therefore the tables have not changed.
-
-Unlike [`Insert`](#method-itableviewinsert), there is no need to return the deleted row,
-as it must necessarily have been exactly equal to the `row` argument.
-No analogue to auto-increment placeholders exists for deletions.
-
-Throws an exception if deleting the row would violate any constraints.
-
-#### Method `ITableView.Iter`
-
-```csharp
-IEnumerable Iter();
-```
-
-Iterate over all rows of the table.
-
-(This keeps track of changes made to the table since the start of this reducer invocation. For example, if rows have been [deleted](#method-itableviewdelete) since the start of this reducer invocation, those rows will not be returned by `Iter`. Similarly, [inserted](#method-itableviewinsert) rows WILL be returned.)
-
-For large tables, this can be a slow operation! Prefer [filtering](#method-indexfilter) by an [`Index`](#class-index) or [finding](#method-uniqueindexfind) a [`UniqueIndex`](#class-uniqueindex) if possible.
-
-#### Property `ITableView.Count`
-
-```csharp
-ulong Count { get; }
-```
-
-Returns the number of rows of this table.
-
-This takes into account modifications by the current transaction,
-even though those modifications have not yet been committed or broadcast to clients.
-This applies generally to insertions, deletions, updates, and iteration as well.
-
-### Public and Private Tables
-
-By default, tables are considered **private**. This means that they are only readable by the database owner and by reducers. Reducers run inside the database, so clients cannot see private tables at all or even know of their existence.
-
-Using the `[SpacetimeDB.Table(Name = "table_name", Public)]` flag makes a table public. **Public** tables are readable by all clients. They can still only be modified by reducers.
-
-(Note that, when run by the module owner, the `spacetime sql ` command can also read private tables. This is for debugging convenience. Only the module owner can see these tables. This is determined by the `Identity` stored by the `spacetime login` command. Run `spacetime login show` to print your current logged-in `Identity`.)
-
-To learn how to subscribe to a public table, see the [client SDK documentation](https://spacetimedb.com/docs/sdks).
-
-### Unique and Primary Key Columns
-
-Columns of a table (that is, fields of a [`[Table]`](#tables) struct) can be annotated with `[Unique]` or `[PrimaryKey]`. Multiple columns can be `[Unique]`, but only one can be `[PrimaryKey]`. For example:
-
-```csharp
-[SpacetimeDB.Table(Name = "citizen")]
-public partial struct Citizen {
- [SpacetimeDB.PrimaryKey]
- ulong Id;
-
- [SpacetimeDB.Unique]
- string Ssn;
-
- [SpacetimeDB.Unique]
- string Email;
-
- string name;
-}
-```
-
-Every row in the table `Person` must have unique entries in the `id`, `ssn`, and `email` columns. Attempting to insert multiple `Person`s with the same `id`, `ssn`, or `email` will throw an exception.
-
-Any `[Unique]` or `[PrimaryKey]` column supports getting a [`UniqueIndex`](#class-uniqueindex) from a [`ReducerContext`](#class-reducercontext) using:
-
-```text
-ctx.Db.{table}.{unique_column}
-```
-
-For example,
-
-```csharp
-ctx.Db.citizen.Ssn
-```
-
-Notice that updating a row is only possible if a row has a unique column -- there is no `update` method in the base [`ITableView`](#interface-itableview) interface. SpacetimeDB has no notion of rows having an "identity" aside from their unique / primary keys.
-
-The `[PrimaryKey]` annotation implies a `[Unique]` annotation, but avails additional methods in the [client]-side SDKs.
-
-It is not currently possible to mark a group of fields as collectively unique.
-
-Filtering on unique columns is only supported for a limited number of types.
-
-### Class `UniqueIndex`
-
-```csharp
-namespace SpacetimeDB.Internal;
-
-public abstract class UniqueIndex : IndexBase
- where Handle : ITableView
- where Row : IStructuralReadWrite, new()
- where Column : IEquatable
-{
- /* ... */
-}
-```
-
-
-
-A unique index on a column. Available for `[Unique]` and `[PrimaryKey]` columns.
-(A custom class derived from `UniqueIndex` is generated for every such column.)
-
-`Row` is the type decorated with `[SpacetimeDB.Table]`, `Column` is the type of the column,
-and `Handle` is the type of the generated table handle.
-
-For a table _table_ with a column _column_, use `ctx.Db.{table}.{column}`
-to get a `UniqueColumn` from a [`ReducerContext`](#class-reducercontext).
-
-Example:
-
-```csharp
-using SpacetimeDB;
-
-public static partial class Module {
- [Table(Name = "user")]
- public partial struct User {
- [PrimaryKey]
- uint Id;
- [Unique]
- string Username;
- ulong DogCount;
- }
-
- [Reducer]
- void Demo(ReducerContext ctx) {
- var idIndex = ctx.Db.user.Id;
- var exampleUser = idIndex.Find(357).Value;
- exampleUser.DogCount += 5;
- idIndex.Update(exampleUser);
-
- var usernameIndex = ctx.Db.user.Username;
- usernameIndex.Delete("Evil Bob");
- }
-}
-```
-
-| Name | Description |
-| -------------------------------------------- | -------------------------------------------- |
-| [Method `Find`](#method-uniqueindexfind) | Find a row by the value of a unique column |
-| [Method `Update`](#method-uniqueindexupdate) | Update a row with a unique column |
-| [Method `Delete`](#method-uniqueindexdelete) | Delete a row by the value of a unique column |
-
-
-
-#### Method `UniqueIndex.Find`
-
-```csharp
-Row? Find(Column key);
-```
-
-Finds and returns the row where the value in the unique column matches the supplied `key`,
-or `null` if no such row is present in the database state.
-
-#### Method `UniqueIndex.Update`
-
-```csharp
-Row Update(Row row);
-```
-
-Deletes the row where the value in the unique column matches that in the corresponding field of `row` and then inserts `row`.
-
-Returns the new row as actually inserted, with any auto-inc placeholders substituted for computed values.
-
-Throws if no row was previously present with the matching value in the unique column,
-or if either the delete or the insertion would violate a constraint.
-
-#### Method `UniqueIndex.Delete`
-
-```csharp
-bool Delete(Column key);
-```
-
-Deletes the row where the value in the unique column matches the supplied `key`, if any such row is present in the database state.
-
-Returns `true` if a row with the specified `key` was previously present and has been deleted,
-or `false` if no such row was present.
-
-### Auto-inc columns
-
-Columns can be marked `[SpacetimeDB.AutoInc]`. This can only be used on integer types (`int`, `ulong`, etc.)
-
-When inserting into or updating a row in a table with an `[AutoInc]` column, if the annotated column is set to zero (`0`), the database will automatically overwrite that zero with an atomically increasing value.
-
-[`ITableView.Insert`] and [`UniqueIndex.Update()`](#method-uniqueindexupdate) returns rows with `[AutoInc]` columns set to the values that were actually written into the database.
-
-```csharp
-public static partial class Module
-{
- [SpacetimeDB.Table(Name = "example")]
- public partial struct Example
- {
- [SpacetimeDB.AutoInc]
- public int Field;
- }
-
- [SpacetimeDB.Reducer]
- public static void InsertAutoIncExample(ReducerContext ctx, int Id, string Name) {
- for (var i = 0; i < 10; i++) {
- // These will have distinct, unique values
- // at rest in the database, since they
- // are inserted with the sentinel value 0.
- var actual = ctx.Db.example.Insert(new Example { Field = 0 });
- Debug.Assert(actual.Field != 0);
- }
- }
-}
-```
-
-`[AutoInc]` is often combined with `[Unique]` or `[PrimaryKey]` to automatically assign unique integer identifiers to rows.
-
-### Indexes
-
-SpacetimeDB supports both single- and multi-column [B-Tree](https://en.wikipedia.org/wiki/B-tree) indexes.
-
-Indexes are declared using the syntax:
-
-```csharp
-[SpacetimeDB.Index.BTree(Name = "IndexName", Columns = [nameof(Column1), nameof(Column2), nameof(Column3)])]
-```
-
-For example:
-
-```csharp
-[SpacetimeDB.Table(Name = "paper")]
-[SpacetimeDB.Index.BTree(Name = "TitleAndDate", Columns = [nameof(Title), nameof(Date)])]
-[SpacetimeDB.Index.BTree(Name = "UrlAndCountry", Columns = [nameof(Url), nameof(Country)])]
-public partial struct AcademicPaper {
- public string Title;
- public string Url;
- public string Date;
- public string Venue;
- public string Country;
-}
-```
-
-Multiple indexes can be declared.
-
-Single-column indexes can also be declared using an annotation on a column:
-
-```csharp
-[SpacetimeDB.Table(Name = "academic_paper")]
-public partial struct AcademicPaper {
- public string Title;
- public string Url;
- [SpacetimeDB.Index.BTree] // The index will be named "Date".
- public string Date;
- [SpacetimeDB.Index.BTree] // The index will be named "Venue".
- public string Venue;
- [SpacetimeDB.Index.BTree(Name = "ByCountry")] // The index will be named "ByCountry".
- public string Country;
-}
-```
-
-Any table supports getting an [`Index`](#class-index) using `ctx.Db.{table}.{index}`. For example, `ctx.Db.academic_paper.TitleAndDate` or `ctx.Db.academic_paper.Venue`.
-
-### Indexable Types
-
-SpacetimeDB supports only a restricted set of types as index keys:
-
-- Signed and unsigned integers of various widths.
-- `bool`.
-- `string`.
-- [`Identity`](#struct-identity).
-- [`ConnectionId`](#struct-connectionid).
-- `enum`s annotated with [`SpacetimeDB.Type`](#attribute-spacetimedbtype).
-
-### Class `Index`
-
-```csharp
-public abstract class IndexBase
- where Row : IStructuralReadWrite, new()
-{
- // ...
-}
-```
-
-Each index generates a subclass of `IndexBase`, which is accessible via `ctx.Db.{table}.{index}`. For example, `ctx.Db.academic_paper.TitleAndDate`.
-
-Indexes can be applied to a variable number of columns, referred to as `Column1`, `Column2`, `Column3`... in the following examples.
-
-| Name | Description |
-| -------------------------------------- | ----------------------- |
-| Method [`Filter`](#method-indexfilter) | Filter rows in an index |
-| Method [`Delete`](#method-indexdelete) | Delete rows in an index |
-
-#### Method `Index.Filter`
-
-```csharp
-public IEnumerable Filter(Column1 bound);
-public IEnumerable Filter(Bound bound);
-public IEnumerable Filter((Column1, Column2) bound);
-public IEnumerable Filter((Column1, Bound) bound);
-public IEnumerable Filter((Column1, Column2, Column3) bound);
-public IEnumerable Filter((Column1, Column2, Bound) bound);
-// ...
-```
-
-Returns an iterator over all rows in the database state where the indexed column(s) match the passed `bound`. Bound is a tuple of column values, possibly terminated by a `Bound`. A `Bound` is simply a tuple `(LastColumn Min, LastColumn Max)`. Any prefix of the indexed columns can be passed, for example:
-
-```csharp
-using SpacetimeDB;
-
-public static partial class Module
-{
- [SpacetimeDB.Table(Name = "zoo_animal")]
- [SpacetimeDB.Index.BTree(Name = "SpeciesAgeName", Columns = [nameof(Species), nameof(Age), nameof(Name)])]
- public partial struct ZooAnimal
- {
- public string Species;
- public uint Age;
- public string Name;
- [SpacetimeDB.PrimaryKey]
- public uint Id;
- }
-
- [SpacetimeDB.Reducer]
- public static void Example(ReducerContext ctx)
- {
- foreach (var baboon in ctx.Db.zoo_animal.SpeciesAgeName.Filter("baboon"))
- {
- // Work with the baboon.
- }
- foreach (var animal in ctx.Db.zoo_animal.SpeciesAgeName.Filter(("b", "e")))
- {
- // Work with the animal.
- // The name of the species starts with a character between "b" and "e".
- }
- foreach (var babyBaboon in ctx.Db.zoo_animal.SpeciesAgeName.Filter(("baboon", 1)))
- {
- // Work with the baby baboon.
- }
- foreach (var youngBaboon in ctx.Db.zoo_animal.SpeciesAgeName.Filter(("baboon", (1, 5))))
- {
- // Work with the young baboon.
- }
- foreach (var babyBaboonNamedBob in ctx.Db.zoo_animal.SpeciesAgeName.Filter(("baboon", 1, "Bob")))
- {
- // Work with the baby baboon named "Bob".
- }
- foreach (var babyBaboon in ctx.Db.zoo_animal.SpeciesAgeName.Filter(("baboon", 1, ("a", "f"))))
- {
- // Work with the baby baboon, whose name starts with a letter between "a" and "f".
- }
- }
-}
-```
-
-#### Method `Index.Delete`
-
-```csharp
-public ulong Delete(Column1 bound);
-public ulong Delete(Bound bound);
-public ulong Delete((Column1, Column2) bound);
-public ulong Delete((Column1, Bound) bound);
-public ulong Delete((Column1, Column2, Column3) bound);
-public ulong Delete((Column1, Column2, Bound) bound);
-// ...
-```
-
-Delete all rows in the database state where the indexed column(s) match the passed `bound`. Returns the count of rows deleted. Note that there may be multiple rows deleted even if only a single column value is passed, since the index is not guaranteed to be unique.
-
-## Reducers
-
-Reducers are declared using the `[SpacetimeDB.Reducer]` attribute.
-
-`[SpacetimeDB.Reducer]` is always applied to static C# functions. The first parameter of a reducer must be a [`ReducerContext`]. The remaining parameters must be types marked with [`SpacetimeDB.Type`]. Reducers should return `void`.
-
-```csharp
-public static partial class Module {
- [SpacetimeDB.Reducer]
- public static void GivePlayerItem(
- ReducerContext context,
- ulong PlayerId,
- ulong ItemId
- )
- {
- // ...
- }
-}
-```
-
-Every reducer runs inside a [database transaction](https://en.wikipedia.org/wiki/Database_transaction). This means that reducers will not observe the effects of other reducers modifying the database while they run. If a reducer fails, all of its changes to the database will automatically be rolled back. Reducers can fail by throwing an exception.
-
-### Class `ReducerContext`
-
-```csharp
-public sealed record ReducerContext : DbContext, Internal.IReducerContext
-{
- // ...
-}
-```
-
-Reducers have access to a special [`ReducerContext`] parameter. This parameter allows reading and writing the database attached to a module. It also provides some additional functionality, like generating random numbers and scheduling future operations.
-
-[`ReducerContext`] provides access to the database tables via [the `.Db` property](#property-reducercontextdb). The [`[Table]`](#tables) attribute generated code that adds table accessors to this property.
-
-| Name | Description |
-| --------------------------------------------------------------- | ------------------------------------------------------------------------------- |
-| Property [`Db`](#property-reducercontextdb) | The current state of the database |
-| Property [`Sender`](#property-reducercontextsender) | The [`Identity`](#struct-identity) of the caller of the reducer |
-| Property [`ConnectionId`](#property-reducercontextconnectionid) | The [`ConnectionId`](#struct-connectionid) of the caller of the reducer, if any |
-| Property [`Rng`](#property-reducercontextrng) | A [`System.Random`] instance. |
-| Property [`Timestamp`](#property-reducercontexttimestamp) | The [`Timestamp`](#struct-timestamp) of the reducer invocation |
-| Property [`Identity`](#property-reducercontextidentity) | The [`Identity`](#struct-identity) of the module |
-
-#### Property `ReducerContext.Db`
-
-```csharp
-DbView Db;
-```
-
-Allows accessing the local database attached to a module.
-
-The `[Table]` attribute generates a field of this property.
-
-For a table named _table_, use `ctx.Db.{table}` to get a [table view](#interface-itableview).
-For example, `ctx.Db.users`.
-
-You can also use `ctx.Db.{table}.{index}` to get an [index](#class-index) or [unique index](#class-uniqueindex).
-
-#### Property `ReducerContext.Sender`
-
-```csharp
-Identity Sender;
-```
-
-The [`Identity`](#struct-identity) of the client that invoked the reducer.
-
-#### Property `ReducerContext.ConnectionId`
-
-```csharp
-ConnectionId? ConnectionId;
-```
-
-The [`ConnectionId`](#struct-connectionid) of the client that invoked the reducer.
-
-`null` if no `ConnectionId` was supplied to the `/database/call` HTTP endpoint,
-or via the CLI's `spacetime call` subcommand.
-
-#### Property `ReducerContext.Rng`
-
-```csharp
-Random Rng;
-```
-
-A [`System.Random`] that can be used to generate random numbers.
-
-#### Property `ReducerContext.Timestamp`
-
-```csharp
-Timestamp Timestamp;
-```
-
-The time at which the reducer was invoked.
-
-#### Property `ReducerContext.Identity`
-
-```csharp
-Identity Identity;
-```
-
-The [`Identity`](#struct-identity) of the module.
-
-This can be used to [check whether a scheduled reducer is being called by a user](#restricting-scheduled-reducers).
-
-Note: this is not the identity of the caller, that's [`ReducerContext.Sender`](#property-reducercontextsender).
-
-### Lifecycle Reducers
-
-A small group of reducers are called at set points in the module lifecycle. These are used to initialize
-the database and respond to client connections. You can have one of each per module.
-
-These reducers cannot be called manually and may not have any parameters except for `ReducerContext`.
-
-#### The `Init` reducer
-
-This reducer is marked with `[SpacetimeDB.Reducer(ReducerKind.Init)]`. It is run the first time a module is published and any time the database is cleared.
-
-If an error occurs when initializing, the module will not be published.
-
-This reducer can be used to configure any static data tables used by your module. It can also be used to start running [scheduled reducers](#scheduled-reducers).
-
-#### The `ClientConnected` reducer
-
-This reducer is marked with `[SpacetimeDB.Reducer(ReducerKind.ClientConnected)]`. It is run when a client connects to the SpacetimeDB database. Their identity can be found in the sender value of the `ReducerContext`.
-
-If an error occurs in the reducer, the client will be disconnected.
-
-#### The `ClientDisconnected` reducer
-
-This reducer is marked with `[SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)]`. It is run when a client disconnects from the SpacetimeDB database. Their identity can be found in the sender value of the `ReducerContext`.
-
-If an error occurs in the disconnect reducer, the client is still recorded as disconnected.
-
-### Scheduled Reducers
-
-Reducers can schedule other reducers to run asynchronously. This allows calling the reducers at a particular time, or at repeating intervals. This can be used to implement timers, game loops, and maintenance tasks.
-
-The scheduling information for a reducer is stored in a table.
-This table has two mandatory fields:
-
-- An `[AutoInc] [PrimaryKey] ulong` field that identifies scheduled reducer calls.
-- A [`ScheduleAt`](#record-scheduleat) field that says when to call the reducer.
-
-Managing timers with a scheduled table is as simple as inserting or deleting rows from the table.
-This makes scheduling transactional in SpacetimeDB. If a reducer A first schedules B but then errors for some other reason, B will not be scheduled to run.
-
-A [`ScheduleAt`](#record-scheduleat) can be created from a [`Timestamp`](#struct-timestamp), in which case the reducer will be scheduled once, or from a [`TimeDuration`](#struct-timeduration), in which case the reducer will be scheduled in a loop.
-
-Example:
-
-```csharp
-using SpacetimeDB;
-
-public static partial class Module
-{
-
- // First, we declare the table with scheduling information.
-
- [Table(Name = "send_message_schedule", Scheduled = nameof(SendMessage), ScheduledAt = nameof(ScheduledAt))]
- public partial struct SendMessageSchedule
- {
-
- // Mandatory fields:
-
- [PrimaryKey]
- [AutoInc]
- public ulong Id;
-
- public ScheduleAt ScheduledAt;
-
- // Custom fields:
-
- public string Message;
- }
-
- // Then, we declare the scheduled reducer.
- // The first argument of the reducer should be, as always, a `ReducerContext`.
- // The second argument should be a row of the scheduling information table.
-
- [Reducer]
- public static void SendMessage(ReducerContext ctx, SendMessageSchedule schedule)
- {
- Log.Info($"Sending message {schedule.Message}");
- // ...
- }
-
- // Finally, we want to actually start scheduling reducers.
- // It's convenient to do this inside the `init` reducer.
-
- [Reducer(ReducerKind.Init)]
- public static void Init(ReducerContext ctx)
- {
- var currentTime = ctx.Timestamp;
- var tenSeconds = new TimeDuration { Microseconds = +10_000_000 };
- var futureTimestamp = currentTime + tenSeconds;
-
- ctx.Db.send_message_schedule.Insert(new()
- {
- Id = 0, // Have [AutoInc] assign an Id.
- ScheduledAt = new ScheduleAt.Time(futureTimestamp),
- Message = "I'm a bot sending a message one time!"
- });
-
- ctx.Db.send_message_schedule.Insert(new()
- {
- Id = 0, // Have [AutoInc] assign an Id.
- ScheduledAt = new ScheduleAt.Interval(tenSeconds),
- Message = "I'm a bot sending a message every ten seconds!"
- });
- }
-}
-```
-
-Scheduled reducers are called on a best-effort basis and may be slightly delayed in their execution
-when a database is under heavy load.
-
-#### Restricting scheduled reducers
-
-Scheduled reducers are normal reducers, and may still be called by clients.
-If a scheduled reducer should only be called by the scheduler, consider beginning it with a check that the caller `Identity` is the module:
-
-```csharp
-[Reducer]
-public static void SendMessage(ReducerContext ctx, SendMessageSchedule schedule)
-{
- if (ctx.Sender != ctx.Identity)
- {
- throw new Exception("Reducer SendMessage may not be invoked by clients, only via scheduling.");
- }
- // ...
-}
-```
-
-## Views
-
-Views are declared using the `[SpacetimeDB.View]` attribute.
-
-Views are read‑only functions that compute and return results from your tables.
-`[SpacetimeDB.View]` is always applied to static C# functions.
-Views must be declared as `Public`, with an explicit `Name`, and do not accept user parameters beyond the context type.
-Views can return either a single row (`T?`) or a list of rows (`List`/`T[]`) where `T` can be a table type or any product type in general.
-
-```csharp
-using SpacetimeDB;
-
-public static partial class Module
-{
- [SpacetimeDB.Table(Name = "player")]
- public partial struct Player {
- [SpacetimeDB.PrimaryKey]
- [SpacetimeDB.AutoInc]
- ulong Id;
-
- [SpacetimeDB.Unique]
- Identity Identity;
-
- string Name;
- }
-
- [SpacetimeDB.Table(Name = "player_level")]
- public partial struct PlayerLevel {
- [SpacetimeDB.Unique]
- ulong PlayerId;
-
- [SpacetimeDB.Index.BTree]
- ulong Level;
- }
-
- [SpacetimeDB.Type]
- public partial record PlayerAndLevel(ulong Id, Identity Identity, string Name, ulong Level);
-
- // At-most-one row: return T?
- [SpacetimeDB.View(Name = "my_player", Public = true)]
- public static Player? MyPlayer(ViewContext ctx)
- {
- return ctx.Db.Player.Identity.Find(ctx.Sender) as Player;
- }
-
- // Multiple rows: return a list
- [SpacetimeDB.View(Name = "players_for_level", Public = true)]
- public static List PlayersForLevel(AnonymousViewContext ctx)
- {
- var rows = new List();
- foreach (var player in ctx.Db.PlayerLevel.Level.Filter(2))
- {
- if (ctx.Db.Player.Id.Find(player.PlayerId) is Player p)
- {
- rows.Add(new PlayerAndLevel(p.Id, p.Identity, p.Name, player.Level));
- }
- }
- return rows;
- }
-}
-```
-
-Views can be queried and subscribed to like normal tables and are updated atomically in realtime.
-
-```sql
-SELECT * FROM my_player;
-SELECT * FROM players_for_level;
-```
-
-#### `ViewContext` and `AnonymousViewContext`
-
-Views must take a `ViewContext` or an `AnonymousViewContext` as their first parameter.
-Like reducers, these types allow reading from the database attached to a module.
-However, unlike reducers, they do not allow writing to the database.
-Both types expose read-only portions of the `Table` and `Index` accessors generated by the `[SpacetimeDB.Table]` attribute.
-
-## Automatic migrations
-
-When you `spacetime publish` a module that has already been published using `spacetime publish `,
-SpacetimeDB attempts to automatically migrate your existing database to the new schema. (The "schema" is just the collection
-of tables and reducers you've declared in your code, together with the types they depend on.) This form of migration is limited and only supports a few kinds of changes.
-On the plus side, automatic migrations usually don't break clients. The situations that may break clients are documented below.
-
-The following changes are always allowed and never breaking:
-
-- ✅ **Adding tables**. Non-updated clients will not be able to see the new tables.
-- ✅ **Adding indexes**.
-- ✅ **Adding or removing `[AutoInc]` annotations.**
-- ✅ **Changing tables from private to public**.
-- ✅ **Adding reducers**.
-- ✅ **Removing `[Unique]` annotations.**
-
-The following changes are allowed, but may break clients:
-
-- ⚠️ **Changing or removing reducers**. Clients that attempt to call the old version of a changed reducer will receive runtime errors.
-- ⚠️ **Changing tables from public to private**. Clients that are subscribed to a newly-private table will receive runtime errors.
-- ⚠️ **Removing `[PrimaryKey]` annotations**. Non-updated clients will still use the old `[PrimaryKey]` as a unique key in their local cache, which can result in non-deterministic behavior when updates are received.
-- ⚠️ **Removing indexes**. This is only breaking in some situtations.
- The specific problem is subscription queries involving semijoins, such as:
-
- ```sql
- SELECT Employee.*
- FROM Employee JOIN Dept
- ON Employee.DeptName = Dept.DeptName
- )
- ```
-
- For performance reasons, SpacetimeDB will only allow this kind of subscription query if there are indexes on `Employee.DeptName` and `Dept.DeptName`. Removing either of these indexes will invalidate this subscription query, resulting in client-side runtime errors.
-
-The following changes are forbidden without a manual migration:
-
-- ❌ **Removing tables**.
-- ❌ **Changing the columns of a table**. This includes changing the order of columns of a table.
-- ❌ **Changing whether a table is used for [scheduling](#scheduled-reducers).**
-- ❌ **Adding `[Unique]` or `[PrimaryKey]` constraints.** This could result in existing tables being in an invalid state.
-
-Currently, manual migration support is limited. The `spacetime publish --delete-data ` command can be used to **COMPLETELY DELETE** and reinitialize your database, but naturally it should be used with EXTREME CAUTION.
-
-## Other infrastructure
-
-### Class `Log`
-
-```csharp
-namespace SpacetimeDB
-{
- public static class Log
- {
- public static void Debug(string message);
- public static void Error(string message);
- public static void Exception(string message);
- public static void Exception(Exception exception);
- public static void Info(string message);
- public static void Trace(string message);
- public static void Warn(string message);
- }
-}
-```
-
-Methods for writing to a private debug log. Log messages will include file and line numbers.
-
-Log outputs of a running database can be inspected using the `spacetime logs` command:
-
-```text
-spacetime logs
-```
-
-These are only visible to the database owner, not to clients or other developers.
-
-Note that `Log.Error` and `Log.Exception` only write to the log, they do not throw exceptions themselves.
-
-Example:
-
-```csharp
-using SpacetimeDB;
-
-public static partial class Module {
- [Table(Name = "user")]
- public partial struct User {
- [PrimaryKey]
- uint Id;
- [Unique]
- string Username;
- ulong DogCount;
- }
-
- [Reducer]
- public static void LogDogs(ReducerContext ctx) {
- Log.Info("Examining users.");
-
- var totalDogCount = 0;
-
- foreach (var user in ctx.Db.user.Iter()) {
- Log.Info($" User: Id = {user.Id}, Username = {user.Username}, DogCount = {user.DogCount}");
-
- totalDogCount += user.DogCount;
- }
-
- if (totalDogCount < 300) {
- Log.Warn("Insufficient dogs.");
- }
-
- if (totalDogCount < 100) {
- Log.Error("Dog population is critically low!");
- }
- }
-}
-```
-
-### Attribute `[SpacetimeDB.Type]`
-
-This attribute makes types self-describing, allowing them to automatically register their structure
-with SpacetimeDB. Any C# type annotated with `[SpacetimeDB.Type]` can be used as a table column or reducer argument.
-
-Types marked `[SpacetimeDB.Table]` are automatically marked `[SpacetimeDB.Type]`.
-
-`[SpacetimeDB.Type]` can be combined with [`SpacetimeDB.TaggedEnum`] to use tagged enums in tables or reducers.
-
-```csharp
-using SpacetimeDB;
-
-public static partial class Module {
-
- [Type]
- public partial struct Coord {
- public int X;
- public int Y;
- }
-
- [Type]
- public partial struct TankData {
- public int Ammo;
- public int LeftTreadHealth;
- public int RightTreadHealth;
- }
-
- [Type]
- public partial struct TransportData {
- public int TroopCount;
- }
-
- // A type that could be either the data for a Tank or the data for a Transport.
- // See SpacetimeDB.TaggedEnum docs.
- [Type]
- public partial record VehicleData : TaggedEnum<(TankData Tank, TransportData Transport)> {}
-
- [Table(Name = "vehicle")]
- public partial struct Vehicle {
- [PrimaryKey]
- [AutoInc]
- public uint Id;
- public Coord Coord;
- public VehicleData Data;
- }
-
- [SpacetimeDB.Reducer]
- public static void InsertVehicle(ReducerContext ctx, Coord Coord, VehicleData Data) {
- ctx.Db.vehicle.Insert(new Vehicle { Id = 0, Coord = Coord, Data = Data });
- }
-}
-```
-
-The fields of the struct/enum must also be marked with `[SpacetimeDB.Type]`.
-
-Some types from the standard library are also considered to be marked with `[SpacetimeDB.Type]`, including:
-
-- `byte`
-- `sbyte`
-- `ushort`
-- `short`
-- `uint`
-- `int`
-- `ulong`
-- `long`
-- `SpacetimeDB.U128`
-- `SpacetimeDB.I128`
-- `SpacetimeDB.U256`
-- `SpacetimeDB.I256`
-- `List` where `T` is a `[SpacetimeDB.Type]`
-
-### Struct `Identity`
-
-```csharp
-namespace SpacetimeDB;
-
-public readonly record struct Identity
-{
- public static Identity FromHexString(string hex);
- public string ToString();
-}
-```
-
-An `Identity` for something interacting with the database.
-
-This is a record struct, so it can be printed, compared with `==`, and used as a `Dictionary` key.
-
-`ToString()` returns a hex encoding of the Identity, suitable for printing.
-
-
-
-### Struct `ConnectionId`
-
-```csharp
-namespace SpacetimeDB;
-
-public readonly record struct ConnectionId
-{
- public static ConnectionId? FromHexString(string hex);
- public string ToString();
-}
-```
-
-A unique identifier for a client connection to a SpacetimeDB database.
-
-This is a record struct, so it can be printed, compared with `==`, and used as a `Dictionary` key.
-
-`ToString()` returns a hex encoding of the `ConnectionId`, suitable for printing.
-
-### Struct `Timestamp`
-
-```csharp
-namespace SpacetimeDB;
-
-public record struct Timestamp(long MicrosecondsSinceUnixEpoch)
- : IStructuralReadWrite,
- IComparable
-{
- // ...
-}
-```
-
-A point in time, measured in microseconds since the Unix epoch.
-This can be converted to/from a standard library [`DateTimeOffset`]. It is provided for consistency of behavior between SpacetimeDB's supported module and SDK languages.
-
-| Name | Description |
-| ------------------------------------- | ----------------------------------------------------- |
-| Property `MicrosecondsSinceUnixEpoch` | Microseconds since the [unix epoch]. |
-| Conversion to/from `DateTimeOffset` | Convert to/from a standard library [`DateTimeOffset`] |
-| Static property `UNIX_EPOCH` | The [unix epoch] as a `Timestamp` |
-| Method `TimeDurationSince` | Measure the time elapsed since another `Timestamp` |
-| Operator `+` | Add a [`TimeDuration`] to a `Timestamp` |
-| Method `CompareTo` | Compare to another `Timestamp` |
-
-#### Property `Timestamp.MicrosecondsSinceUnixEpoch`
-
-```csharp
-long MicrosecondsSinceUnixEpoch;
-```
-
-The number of microseconds since the [unix epoch].
-
-A positive value means a time after the Unix epoch, and a negative value means a time before.
-
-#### Conversion to/from `DateTimeOffset`
-
-```csharp
-public static implicit operator DateTimeOffset(Timestamp t);
-public static implicit operator Timestamp(DateTimeOffset offset);
-```
-
-`Timestamp` may be converted to/from a [`DateTimeOffset`], but the conversion can lose precision.
-This type has less precision than DateTimeOffset (units of microseconds rather than units of 100ns).
-
-#### Static property `Timestamp.UNIX_EPOCH`
-
-```csharp
-public static readonly Timestamp UNIX_EPOCH = new Timestamp { MicrosecondsSinceUnixEpoch = 0 };
-```
-
-The [unix epoch] as a `Timestamp`.
-
-#### Method `Timestamp.TimeDurationSince`
-
-```csharp
-public readonly TimeDuration TimeDurationSince(Timestamp earlier) =>
-```
-
-Create a new [`TimeDuration`] that is the difference between two `Timestamps`.
-
-#### Operator `Timestamp.+`
-
-```csharp
-public static Timestamp operator +(Timestamp point, TimeDuration interval);
-```
-
-Create a new `Timestamp` that occurs `interval` after `point`.
-
-#### Method `Timestamp.CompareTo`
-
-```csharp
-public int CompareTo(Timestamp that)
-```
-
-Compare two `Timestamp`s.
-
-### Struct `TimeDuration`
-
-```csharp
-namespace SpacetimeDB;
-
-public record struct TimeDuration(long Microseconds) : IStructuralReadWrite {
- // ...
-}
-```
-
-A duration that represents an interval between two [`Timestamp`]s.
-
-This type may be converted to/from a [`TimeSpan`]. It is provided for consistency of behavior between SpacetimeDB's supported module and SDK languages.
-
-| Name | Description |
-| ------------------------------------------------------------- | ------------------------------------------------- |
-| Property [`Microseconds`](#property-timedurationmicroseconds) | Microseconds between the [`Timestamp`]s. |
-| [Conversion to/from `TimeSpan`](#conversion-tofrom-timespan) | Convert to/from a standard library [`TimeSpan`] |
-| Static property [`ZERO`](#static-property-timedurationzero) | The duration between any [`Timestamp`] and itself |
-
-#### Property `TimeDuration.Microseconds`
-
-```csharp
-long Microseconds;
-```
-
-The number of microseconds between two [`Timestamp`]s.
-
-#### Conversion to/from `TimeSpan`
-
-```csharp
-public static implicit operator TimeSpan(TimeDuration d) =>
- new(d.Microseconds * Util.TicksPerMicrosecond);
-
-public static implicit operator TimeDuration(TimeSpan timeSpan) =>
- new(timeSpan.Ticks / Util.TicksPerMicrosecond);
-```
-
-`TimeDuration` may be converted to/from a [`TimeSpan`], but the conversion can lose precision.
-This type has less precision than [`TimeSpan`] (units of microseconds rather than units of 100ns).
-
-#### Static property `TimeDuration.ZERO`
-
-```csharp
-public static readonly TimeDuration ZERO = new TimeDuration { Microseconds = 0 };
-```
-
-The duration between any `Timestamp` and itself.
-
-### Record `TaggedEnum`
-
-```csharp
-namespace SpacetimeDB;
-
-public abstract record TaggedEnum : IEquatable> where Variants : struct, ITuple
-```
-
-A [tagged enum](https://en.wikipedia.org/wiki/Tagged_union) is a type that can hold a value from any one of several types. `TaggedEnum` uses code generation to accomplish this.
-
-For example, to declare a type that can be either a `string` or an `int`, write:
-
-```csharp
-[SpacetimeDB.Type]
-public partial record ProductId : SpacetimeDB.TaggedEnum<(string Text, uint Number)> { }
-```
-
-Here there are two **variants**: one is named `Text` and holds a `string`, the other is named `Number` and holds a `uint`.
-
-To create a value of this type, use `new {Type}.{Variant}({data})`. For example:
-
-```csharp
-ProductId a = new ProductId.Text("apple");
-ProductId b = new ProductId.Number(57);
-ProductId c = new ProductId.Number(59);
-```
-
-To use a value of this type, you need to check which variant it stores.
-This is done with [C# pattern matching syntax](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). For example:
-
-```csharp
-public static void Print(ProductId id)
-{
- if (id is ProductId.Text(var s))
- {
- Log.Info($"Textual product ID: '{s}'");
- }
- else if (id is ProductId.Number(var i))
- {
- Log.Info($"Numeric Product ID: {i}");
- }
-}
-```
-
-A `TaggedEnum` can have up to 255 variants, and the variants can be any type marked with [`[SpacetimeDB.Type]`].
-
-```csharp
-[SpacetimeDB.Type]
-public partial record ManyChoices : SpacetimeDB.TaggedEnum<(
- string String,
- int Int,
- List IntList,
- Banana Banana,
- List> BananaMatrix
-)> { }
-
-[SpacetimeDB.Type]
-public partial struct Banana {
- public int Sweetness;
- public int Rot;
-}
-```
-
-`TaggedEnums` are an excellent alternative to nullable fields when groups of fields are always set together. Consider a data type like:
-
-```csharp
-[SpacetimeDB.Type]
-public partial struct ShapeData {
- public int? CircleRadius;
- public int? RectWidth;
- public int? RectHeight;
-}
-```
-
-Often this is supposed to be a circle XOR a rectangle -- that is, not both at the same time. If this is the case, then we don't want to set `circleRadius` at the same time as `rectWidth` or `rectHeight`. Also, if `rectWidth` is set, we expect `rectHeight` to be set.
-However, C# doesn't know about this, so code using this type will be littered with extra null checks.
-
-If we instead write:
-
-```csharp
-[SpacetimeDB.Type]
-public partial struct CircleData {
- public int Radius;
-}
-
-[SpacetimeDB.Type]
-public partial struct RectData {
- public int Width;
- public int Height;
-}
-
-[SpacetimeDB.Type]
-public partial record ShapeData : SpacetimeDB.TaggedEnum<(CircleData Circle, RectData Rect)> { }
-```
-
-Then code using a `ShapeData` will only have to do one check -- do I have a circle or a rectangle?
-And in each case, the data will be guaranteed to have exactly the fields needed.
-
-### Record `ScheduleAt`
-
-```csharp
-namespace SpacetimeDB;
-
-public partial record ScheduleAt : TaggedEnum<(TimeDuration Interval, Timestamp Time)>
-```
-
-When a [scheduled reducer](#scheduled-reducers) should execute, either at a specific point in time, or at regular intervals for repeating schedules.
-
-Stored in reducer-scheduling tables as a column.
-
-[client]: https://spacetimedb.com/docs/#client
-[clients]: https://spacetimedb.com/docs/#client
-[client SDK documentation]: https://spacetimedb.com/docs/#client
-[`DateTimeOffset`]: https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset?view=net-9.0
-[`TimeSpan`]: https://learn.microsoft.com/en-us/dotnet/api/system.timespan?view=net-9.0
-[unix epoch]: https://en.wikipedia.org/wiki/Unix_time
-[`System.Random`]: https://learn.microsoft.com/en-us/dotnet/api/system.random?view=net-9.0
diff --git a/docs/docs/90600-server-module-languages/00400-typescript-reference.md b/docs/docs/90600-server-module-languages/00400-typescript-reference.md
deleted file mode 100644
index 6e4d71a741c..00000000000
--- a/docs/docs/90600-server-module-languages/00400-typescript-reference.md
+++ /dev/null
@@ -1,839 +0,0 @@
----
-title: TypeScript Reference
-slug: /old-modules/typescript
-toc_max_heading_level: 6
----
-
-# SpacetimeDB TypeScript Module Library
-
-[SpacetimeDB](https://spacetimedb.com/) lets you write server-side applications (called **modules**) that run inside a relational database. Modules define **tables** (your data) and **reducers** (your server endpoints). Clients connect directly to the database to read public data via SQL subscriptions and queries, and they invoke reducers to mutate state.
-
-```text
- Client Application SpacetimeDB
-┌───────────────────────┐ ┌───────────────────────┐
-│ │ │ │
-│ ┌─────────────────┐ │ SQL Query │ ┌─────────────────┐ │
-│ │ Subscribed Data │<─────────────────────│ Database │ │
-│ └─────────────────┘ │ │ └─────────────────┘ │
-│ │ │ │ ^ │
-│ │ │ │ │ │
-│ v │ │ v │
-│ +─────────────────┐ │ call_reducer() │ ┌─────────────────┐ │
-│ │ Client Code │─────────────────────>│ Module Code │ │
-│ └─────────────────┘ │ │ └─────────────────┘ │
-│ │ │ │
-└───────────────────────┘ └───────────────────────┘
-```
-
-TypeScript modules are built with the TypeScript Module Library from [`spacetimedb/server`](https://www.npmjs.com/package/spacetimedb). You define your schema and reducers in TypeScript, and then build and deploy with the [`spacetime` CLI](https://spacetimedb.com/install) using the `spacetime publish` command. Under the hood, SpacetimeDB uses [Rolldown](https://rolldown.rs/) to bundle your application into a single JavaScript artifact before uploading it to the SpacetimeDB host.
-
-:::note
-SpacetimeDB also provides a TypeScript **client** SDK at `spacetimedb/sdk`, as well as integrations for frameworks like `spacetimedb/react`. This guide focuses exclusively on the **server-side module** library.
-:::
-
-If you’re new to TypeScript, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html). For a guided introduction to modules, see the [TypeScript Module Quickstart](/docs/quickstarts/typescript).
-
-## Overview
-
-SpacetimeDB modules interact with the outside world via two mechanisms: **tables** and **reducers**.
-
-- [Tables](#tables) store data; public tables are queryable and subscribable by clients.
-- [Reducers](#reducers) are functions that can read and write tables and are callable over the network.
-
-Reducers are atomic and deterministic, there’s no direct filesystem or network access (e.g., `fs`, `fetch`). They execute inside the database with ACID guarantees.
-
-A minimal module looks like this:
-
-```ts
-import { schema, table, t, type RowObj } from 'spacetimedb/server';
-
-// Define a table that is publicly readable by clients
-const players = table(
- { name: 'players', public: true },
- {
- id: t.u32().primaryKey().autoInc(),
- name: t.string(),
- }
-);
-
-// Compose a schema from one or more tables
-const spacetimedb = schema(players);
-
-// Define a reducer that inserts a row
-spacetimedb.reducer('add_player', { name: t.string() }, (ctx, { name }) => {
- ctx.db.players.insert({ id: 0, name });
-});
-```
-
-Reducers don’t return data directly; instead, clients read from tables or subscribe for live updates.
-
-Tables and reducers can use any types built with `t.*` (e.g., `t.string()`, `t.i32()`) or composite types defined with `t.object`, `t.enum`, `t.array`, or `t.option`.
-
-## Setup
-
-1. **[Install the CLI](https://spacetimedb.com/install)**
-
-2. **Initialize a TypeScript module project**
-
- ```bash
- spacetime init --lang typescript my-project
- cd my-project
- ```
-
- This creates a scaffold with a sample module entrypoint and `package.json`.
-
-3. **Develop**
- - Add tables with `table(...)` and reducers with `spacetimedb.reducer(...)` in your source.
-
-4. **Build and publish**
-
- ```bash
- spacetime login
- spacetime publish
- # Example: spacetime publish silly_demo_app
- ```
-
-Publishing bundles your code into a JavaScript bundle, and creates a database and installs your bundle in that database. The CLI outputs the database’s **name** and **Identity** (a hex string). Save this identity for administration tasks like `spacetime logs `.
-
-:::warning
-IMPORTANT! In order to build and publish your module, you must have a `src/index.ts` file in your project. If you have multiple files that define reducers, you must import them from that file. e.g.
-
-```ts
-import './schema';
-import './my_reducers';
-import './my_other_reducers';
-```
-
-This ensures that those files are included in the bundle.
-:::
-
-Re-publishing updates your module in place with [automatic migrations](#automatic-migrations) where possible:
-
-```bash
-spacetime publish
-```
-
-where `` is the name of your existing database.
-
-You can also generate client bindings for your schema with `spacetime generate`. See the [client SDK documentation](https://spacetimedb.com/docs/sdks/typescript#generate-module-bindings) for more information.
-
-# How it works
-
-SpacetimeDB transpiles and bundles your code into a JavaScript bundle that conform to its host ABI (application binary interface). The **host** loads your module, applies schema migrations, initializes lifecycle reducers, and serves client calls. During module updates, active connections and subscriptions remain intact, allowing you to hotswap your server code without affecting or disconnecting any clients.
-
-## Publishing Flow
-
-When you run `spacetime publish `, the following happens:
-
-- The host locates or creates the target database.
-- The new schema is compared against the current version; if compatible, an [automatic migration](#automatic-migrations) runs.
-- The host atomically swaps in the new module, invoking lifecycle reducers such as `Init`.
-- The module becomes live, serving new reducer calls.
-
-## Tables
-
-All data in SpacetimeDB is stored in the form of **tables**. SpacetimeDB tables are hosted in memory, in the same process as your code, for extremely low latency and high throughput access to your data. SpacetimeDB also automatically persists all data in tables to disk behind the scenes.
-
-In TypeScript you can declare a new table with the `table` function.
-
-```ts
-import { table, t } from 'spacetimedb/server';
-
-const people = table(
- { name: 'people', public: true },
- {
- id: t.u32().primaryKey().autoInc(),
- name: t.string().index('btree'),
- email: t.string().unique(),
- }
-);
-```
-
-The first argument to the `table` function is where you can define options for the table, and the second argument is an object which defines the type of each column in the table.
-
-You can set the following options on tables:
-
-| **Property** | **Type** | **Description** | **Default** |
-| ------------ | ------------------------ | --------------------------------------------------------------------------------- | ----------- |
-| `name` | `string` | The name of the table. | - |
-| `public` | `boolean` | Whether the table is publicly accessible. | `false` |
-| `indexes` | `IndexOpts[]` | Declarative multi-column indexes for the table. | - |
-| `scheduled` | `string` | The name of the reducer to be executed based on the scheduled rows in this table. | - |
-
-:::note
-All tables are **private** by default, meaning that they are visible only to the module owner. You can explicitly make them public to all users by setting `public: true` in the options.
-:::
-
-### `IndexOpts`
-
-Defines configuration for a table index.
-Each index must specify an algorithm and one or more columns.
-
-| **Property** | **Type** | **Description** |
-| ------------ | ----------------------- | ----------------------------------------------------------------- |
-| `name` | `string` _(optional)_ | A custom name for the index. |
-| `algorithm` | `'btree'` \| `'direct'` | The indexing algorithm used. |
-| `columns` | `readonly AllowedCol[]` | _(Required for `btree`)_ Columns included in the B-Tree index. |
-| `column` | `AllowedCol` | _(Required for `direct`)_ Column used for direct lookup indexing. |
-
-Each table generates a database accessor at `ctx.db.` with methods like:
-
-| Operation | Example |
-| ------------ | ------------------------------------------------- |
-| Insert row | `ctx.db.people.insert({ id: 0, name, email })` |
-| Delete row | `ctx.db.people.delete({ id, name, email })` |
-| Iterate rows | `for (const row of ctx.db.people.iter()) { ... }` |
-| Count rows | `ctx.db.people.count` |
-
-:::tip
-**Performance:** Prefer using indexes or unique accessors for targeted lookups instead of full iterations.
-:::
-
-### Public and Private Tables
-
-- **Private tables**: Visible only to reducers and the database owner (e.g., via CLI debugging). Clients cannot access them.
-- **Public tables**: Exposed for client read access. Writes still occur only through reducers.
-
-# Types
-
-Types for tables are constructed with SpacetimeDB's `TypeBuilder` API which is exported as `t` from `spacetimedb/server`. This type is very similar to other type validation libraries like [Zod](https://github.com/colinhacks/zod). These types tell SpacetimeDB what the schema of your database should be. They also allow you to provide very specific datatypes like unsigned 8-bit integers for maximum performance.
-
-```ts
-import { t } from 'spacetimedb/server';
-```
-
-`t` provides a collection of factory functions for creating SpacetimeDB algebraic types used in table definitions. Each function returns a corresponding _builder_ (e.g., `BoolBuilder`, `StringBuilder`, `F64Builder`) that implements `TypeBuilder`, enabling type-safe schema construction.
-
-- Primitive types map to native TypeScript: `bool` → `boolean`, `string` → `string`, `number`/`f32`/`f64` → `number`, and large integers to `bigint`.
-- Complex types (`object`, `row`, `array`, `enum`) support nested/structured schemas.
-- The `scheduleAt` function creates a special column type used for scheduling reducers.
-
-### Primitives
-
-| Factory | Returns | TS Representation | Description |
-| ------------ | --------------- | ----------------- | ------------------------------- |
-| `t.bool()` | `BoolBuilder` | `boolean` | Boolean column type. |
-| `t.string()` | `StringBuilder` | `string` | UTF-8 string type. |
-| `t.number()` | `F64Builder` | `number` | Alias for `f64` (64-bit float). |
-| `t.f32()` | `F32Builder` | `number` | 32-bit float. |
-| `t.f64()` | `F64Builder` | `number` | 64-bit float. |
-| `t.i8()` | `I8Builder` | `number` | Signed 8-bit integer. |
-| `t.u8()` | `U8Builder` | `number` | Unsigned 8-bit integer. |
-| `t.i16()` | `I16Builder` | `number` | Signed 16-bit integer. |
-| `t.u16()` | `U16Builder` | `number` | Unsigned 16-bit integer. |
-| `t.i32()` | `I32Builder` | `number` | Signed 32-bit integer. |
-| `t.u32()` | `U32Builder` | `number` | Unsigned 32-bit integer. |
-| `t.i64()` | `I64Builder` | `bigint` | Signed 64-bit integer. |
-| `t.u64()` | `U64Builder` | `bigint` | Unsigned 64-bit integer. |
-| `t.i128()` | `I128Builder` | `bigint` | Signed 128-bit integer. |
-| `t.u128()` | `U128Builder` | `bigint` | Unsigned 128-bit integer. |
-| `t.i256()` | `I256Builder` | `bigint` | Signed 256-bit integer. |
-| `t.u256()` | `U256Builder` | `bigint` | Unsigned 256-bit integer. |
-
-### Structured Types
-
-| Factory | Returns | TypeScript Representation | Description / Usage |
-| ------------------------ | ------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
-| `t.object(name, obj)` | `ProductBuilder` | `{ [K in keyof Obj]: T }` | Product/object type (fields are `TypeBuilder`s). Used for nested or structured data types. |
-| `t.row(obj)` | `RowBuilder` | `{ [K in keyof Obj]: T }` | Row type for table schemas. Same TS shape as `object`, but allows keys which can have column metadata on them. |
-| `t.enum(name, variants)` | `SumBuilder \| SimpleSumBuilder` | Union of tagged objects: `{ tag: 'variant' } \| { tag: 'variant', value: T }` | Sum/enum type. If all variants are empty (unit), yields a simple string-like enum; otherwise a tagged union. |
-| `t.array(element)` | `ArrayBuilder` | `T[]` | Array of the given element type. |
-| `t.unit()` | `UnitBuilder` | `{}` (in some cases `undefined`, as in the case of the simplified enum above) | Zero-field product type (unit). Used for empty payloads or tag-only enum variants. |
-
-:::note
-`t.object` and `t.enum` require a `name` parameter which defines the name of this type in SpacetimeDB. This parameter is not strictly required by TypeScript but it allows SpacetimeDB to code generate those types in other languages that require names for types.
-:::
-
-### Special / Scheduling
-
-| Factory | Returns | TypeScript Representation | Description |
-| ------------------ | ------------------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
-| `t.scheduleAt()` | `ColumnBuilder` | `ScheduleAt` | Special column type for scheduling reducer execution. Automatically sets `isScheduleAt: true` in metadata. |
-| `t.option(value)` | `OptionBuilder` | `Value \| undefined` | Optional value type (equivalent to an enum with `some` / `none`). In TypeScript, represented as the inner value type or `undefined`. |
-| `t.identity()` | `IdentityBuilder` | `Identity` | Unique identity type. Used for identifying entities within SpacetimeDB. |
-| `t.connectionId()` | `ConnectionIdBuilder` | `ConnectionId` | Represents a client connection identifier. |
-| `t.timestamp()` | `TimestampBuilder` | `Timestamp` | Represents an absolute point in time (microseconds since Unix epoch). |
-| `t.timeDuration()` | `TimeDurationBuilder` | `TimeDuration` | Represents a relative duration in microseconds. |
-
-Use `t` to define advanced types for rows or arguments:
-
-```ts
-const simpleEnum = t.enum('SimpleEnum', {
- Zero: t.unit(),
- One: t.unit(),
- Two: t.unit(),
-});
-
-const everyPrimitive = t.object('EveryPrimitiveStruct', {
- a: t.u8(),
- b: t.u16(),
- c: t.u32(),
- d: t.u64(),
- e: t.u128(),
- f: t.u256(),
- g: t.i8(),
- h: t.i16(),
- i: t.i32(),
- j: t.i64(),
- k: t.i128(),
- l: t.i256(),
- m: t.bool(),
- n: t.f32(),
- o: t.f64(),
- p: t.string(),
- q: t.identity(),
- r: t.connectionId(),
- s: t.timestamp(),
- t: t.timeDuration(),
-});
-
-const container = t.object('Container', {
- maybe: t.option(t.i32()),
- list: t.array(t.string()),
- enums: t.array(simpleEnum),
-});
-```
-
-Row types are reusable:
-
-```ts
-const a = table({ name: 'a' }, { n: t.u8() });
-const b = table({ name: 'b' }, { a: a.rowType, text: t.string() });
-```
-
-### Column Attributes
-
-You can convert a plain type into a column by adding one or more column attributes to that type. This will convert the `TypeBuilder` into a `ColumnBuilder` which stores metadata about the column attributes. `ColumnBuilder` types must be either passed to the `table` function directly, or as a field of a type constructed with `t.row()`:
-
-```ts
-import { t } from 'spacetimedb/server';
-
-const peopleRowType = t.row({
- id: t.u32().primaryKey().autoInc(),
- name: t.string().index('btree'),
- email: t.string().unique(),
-});
-
-const people = table({ name: 'people', public: true }, peopleRowType);
-```
-
-### Unique and Primary Key Columns
-
-Columns can be marked `.unique()` or `.primaryKey()`. Only one primary key is allowed, but multiple unique columns are supported. The primary key column represents the identity of the row. Changes to a row that don't affect the primary key are considered to be updates, while changes to the primary key are considered to be a new row (i.e. a delete followed by an insert).
-
-The unique and primary key column attributes guarantee that only a single row can exist with a given value for the column and generate accessors at `ctx.db..`:
-
-- `find(key)` - returns a row or `null`
-- `update(row)` - replaces the existing row with the same primary key and returns the updated row
-- `delete(key)` - removes the row, returns a boolean
-
-Example:
-
-```ts
-const users = table(
- { name: 'users', public: true },
- {
- id: t.u32().primaryKey(),
- username: t.string().unique(),
- dogCount: t.u64(),
- }
-);
-
-const spacetimedb = schema(users);
-
-spacetimedb.reducer('give_dogs', { id: t.u32(), n: t.u32() }, (ctx, { id, n }) => {
- const row = ctx.db.users.id.find(id);
- if (!row) {
- throw new SenderError('User not found');
- }
- row.dogCount += n;
- ctx.db.users.id.update(row);
-});
-
-spacetimedb.reducer('ban_username', { username: t.string() }, (ctx, { username }) => {
- ctx.db.users.username.delete(username);
-});
-```
-
-:::note
-Updates require a unique or primary key column. The base table view has no direct `update` method.
-:::
-
-### Auto-increment Columns
-
-Use `.autoInc()` for automatically increasing integer identifiers. Inserting a row with a zero-valued field causes the database to assign a new unique value.
-
-```ts
-const posts = table(
- { name: 'posts', public: true },
- {
- id: t.u64().primaryKey().autoInc(),
- title: t.string(),
- }
-);
-
-const spacetimedb = schema(posts);
-
-spacetimedb.reducer('add_post', { title: t.string() }, (ctx, { title }) => {
- const inserted = ctx.db.posts.insert({ id: 0, title });
- // inserted.id now contains the assigned auto-incremented value
-});
-```
-
-## Indexes
-
-You can define indexes either directly on a column or on a table for efficient data access and filtering:
-
-- Single-column: `.index('btree')` on a column.
-- Multi-column: use `indexes` in the table options.
-
-```ts
-const scores = table(
- {
- name: 'scores',
- public: true,
- indexes: [
- {
- name: 'byPlayerAndLevel',
- algorithm: 'btree',
- columns: ['player_id', 'level'],
- },
- ],
- },
- {
- player_id: t.u32(),
- level: t.u32(),
- points: t.i64(),
- }
-);
-```
-
-Access indexes at `ctx.db..` with:
-
-- `filter(bound)` - iterate rows by prefix or range
-- `delete(bound)` - remove rows matching the bound
-
-Example:
-
-```ts
-for (const row of ctx.db.scores.byPlayerAndLevel.filter(123)) {
- // rows with player_id = 123
-}
-for (const row of ctx.db.scores.byPlayerAndLevel.filter([123, [1, 10]])) {
- // player_id = 123, 1 <= level <= 10
-}
-```
-
-Indexable key types include integers, booleans, strings, `identity`, `connectionId`, and simple enums defined with `t.enum`.
-
-## Reducers
-
-Reducers are declared with `spacetimedb.reducer(name, argTypes, handler)`, where `spacetimedb` is the value returned from the `schema` function.
-
-:::note
-By convention in our examples we use the name `spacetimedb` for this value, but you can call it whatever you like. `s` is a shorter alternative if you prefer. This value provides access to the database and also context for the TypeScript type system to ensure your
-:::
-
-- The handler signature is `(ctx, args)`.
-- Arguments are validated against the types defined in the `argTypes`.
-- Reducers modify tables and do not return any values.
-
-```ts
-spacetimedb.reducer('give_item', { player_id: t.u64(), item_id: t.u64() }, (ctx, { player_id, item_id }) => {
- // modify tables
-});
-```
-
-### Reducer Errors
-
-Reducers execute in an "atomic" transactional context, meaning either all of the changes from the function are applied or none of them. If your reducer encounters an error during execution, all of the changes you've applied during that call will be rolled back as if the reducer had never been called at all.
-
-In SpacetimeDB there are two classes of errors that you reducer might encounter:
-
-1. Sender errors, which are caused by the caller of the reducer (called the "sender")
-2. Programmer errors, which are errors caused by incorrect logic in your module code.
-
-#### Sender Errors
-
-There are two ways you can return a sender error from a reducer:
-
-1. By throwing a `SenderError` via `throw new SenderError("message")` where `message` is the error string
-2. By returning a value of type `{ tag: 'err', value: string }` where `value` is the error string
-
-For example:
-
-```ts
-spacetimedb.reducer('give_item', { player_id: t.u64(), item_id: t.u64() }, (ctx, { player_id, item_id }) => {
- if (!ctx.db.owner.id.find(ctx.sender)) {
- throw new SenderError('Reducer may only be invoked by module owner');
- }
- // ...
-});
-// or
-spacetimedb.reducer('give_item', { player_id: t.u64(), item_id: t.u64() }, (ctx, { player_id, item_id }) => {
- if (!ctx.db.owner.id.find(ctx.sender)) {
- return {
- tag: 'err',
- value: 'Reducer may only be invoked by module owner',
- };
- }
- // ...
-});
-```
-
-#### Programmer Errors
-
-SpacetimeDB considers all uncaught errors thrown by your reducer which are not of the type `SenderError` to be programmer errors or "panics". These errors will be shown to you in your project's dashboard, or you can configure alerting so that you find out when these errors occur.
-
-Just as with `SenderError` if an error is uncaught, all changes made during the transaction will be rolled back.
-
-### ReducerContext
-
-Within a reducer, the context (`ctx`) provides:
-
-- `ctx.db` - access to tables and indexes
-- `ctx.sender` - caller `Identity`
-- `ctx.connectionId` - caller connection ID, or `undefined`
-- `ctx.timestamp` - invocation `Timestamp`
-
-Examples:
-
-```ts
-spacetimedb.reducer('insert_caller_identity', ctx => {
- ctx.db.users.insert({ identity: ctx.sender, name: 'me' });
-});
-
-spacetimedb.reducer('record_call_time', ctx => {
- ctx.db.calls.insert({ t: ctx.timestamp });
-});
-```
-
-# Scheduled Reducers
-
-Define recurring or delayed operations with **scheduled tables** containing a `scheduleAt` column.
-
-```ts
-const ScheduledJobs = table(
- { name: 'scheduled_jobs', scheduled: 'send_message', public: true },
- {
- scheduled_id: t.u64().primaryKey().autoInc(),
- scheduled_at: t.scheduleAt(),
- text: t.string(),
- }
-);
-
-const spacetimedb = schema(ScheduledJobs);
-
-spacetimedb.reducer('send_message', { arg: ScheduledJobs.rowType }, (_ctx, { arg }) => {
- // Called automatically by scheduler with job row data
-});
-```
-
-Insert rows to schedule jobs; delete to cancel. Scheduling is transactional-failed reducers prevent scheduling persistence.
-
-**Restricting manual calls:**
-
-```ts
-spacetimedb.reducer('send_message', { arg: ScheduledJobs.rowType }, (ctx, { arg }) => {
- if (!ctx.db.owner.id.find(ctx.sender)) {
- throw new SenderError(
- 'Reducer may only be invoked by the database owner'
- );
- }
- // ...
-});
-```
-
-## Views
-
-Views are read‑only functions that compute and return results from your tables.
-They are declared with either `spacetimedb.view(viewOpts, returnType, handler)` or `spacetimedb.anonymousView(viewOpts, returnType, handler)`,
-where `spacetimedb` is the value returned from the `schema` function, and `viewOpts` is defined as `{ name: string; public: true; }`
-
-The handler signature is `(ctx) => rows`, where `rows` must be either an array or option of product values.
-Views must be declared as `public`, with an explicit `name`, and do not accept user parameters beyond the context type.
-
-```ts
-import { schema, table, t, type RowObj } from 'spacetimedb/server';
-
-const players = table('players', {
- id: t.u64().primaryKey().autoInc(),
- identity: t.identity().unique(),
- name: t.string(),
-});
-
-const playerLevels = table('player_levels', {
- player_id: t.u64().unique(),
- level: t.u64(),
-});
-
-const spacetimedb = schema(players, playerLevels);
-
-// At-most-one row: return Option via t.option(...)
-// Your function may return the row or null
-spacetimedb.view(
- { name: 'my_player', public: true },
- t.option(players.row()),
- (ctx) => {
- const row = ctx.db.players.identity.find(ctx.sender);
- return row ?? null;
- }
-);
-
-// Multiple rows: return an array of rows via t.array(...)
-spacetimedb.anonymousView(
- { name: 'players_for_level', public: true },
- t.array(
- t.product({
- id: t.u64(),
- name: t.string(),
- level: t.u64(),
- })
- ),
- (ctx) => {
- const out: Array<{ id: bigint; name: string; level: bigint }> = [];
- for (const player of ctx.db.playerLevels.level.find(2)) {
- const p = ctx.db.players.id.find(player.player_id);
- if (p) out.push({ id: p.id, name: p.name, level: player.level });
- }
- return out;
- }
-);
-```
-
-Views can be queried and subscribed to like normal tables and are updated atomically in realtime.
-
-```sql
-SELECT * FROM my_player;
-SELECT * FROM players_for_level;
-```
-
-#### `ViewContext` and `AnonymousViewContext`
-
-Within a view, the context (`ctx`) provides:
-
-- `ctx.db` - read-only access to tables and indexes
-- `ctx.sender` - caller `Identity` (for `ViewContext` only)
-
-## Automatic Migrations
-
-Re-publishing attempts schema migrations automatically. Safe operations:
-
-- ✅ Add tables or indexes
-- ✅ Toggle auto-increment
-- ✅ Make private tables public
-
-Potentially breaking:
-
-- ⚠️ Modify or remove reducers
-- ⚠️ Make public tables private
-- ⚠️ Remove primary keys or indexes used in client queries
-- ⚠️ Add columns (these can break old clients)
-
-Forbidden without manual migration:
-
-- ❌ Remove tables
-- ❌ Change column definitions or order
-- ❌ Alter scheduling status
-- ❌ Add new constraints that invalidate existing data
-
-:::warning
-The following deletes all data stored in the database.
-:::
-
-To fully reset your database and clear all data, run:
-
-```bash
-spacetime publish --delete-data
-# or
-spacetime publish -c
-```
-
-## Logging & Diagnostics
-
-SpacetimeDB provides a lightweight, high-performance logging system modeled after the standard JavaScript `console` API. You can use familiar logging calls like `console.log()`, `console.error()`, or `console.debug()`, and they will automatically be routed through SpacetimeDB’s internal `sys.console_log` system.
-
-Logs are visible only to the database owner and can be viewed via the CLI:
-
-```bash
-spacetime logs
-```
-
-Client applications cannot access logs, they are private to your database instance.
-
-### Console API
-
-SpacetimeDB implements a `console` object compatible with the standard `Console` interface, but adapted for a WASM/SpacetimeDB environment. Use the following methods exactly as you would in the browser or Node.js:
-
-```ts
-console.log('Hello SpacetimeDB!');
-console.info('Connected to database');
-console.warn('Cache is nearly full');
-console.error('Failed to fetch entity');
-console.debug('Reducer input:', data);
-console.trace('Reducer execution trace');
-```
-
-### Assertions
-
-`console.assert(condition, ...data)` logs an error if the condition is falsy:
-
-```ts
-console.assert(userId !== undefined, 'Missing user ID!');
-```
-
-If the assertion fails, the message is logged at **error level**.
-
-### Tables and Object Logging
-
-`console.table()` logs structured or tabular data for inspection.
-Properties are ignored, only the `tabularData` object is formatted as a string.
-
-```ts
-console.table({ x: 10, y: 20 });
-```
-
-### Timers
-
-SpacetimeDB’s console supports named timers via `console.time()`, `console.timeLog()`, and `console.timeEnd()`.
-
-| Method | Description |
-| --------------------------------- | ----------------------------------------------------------------------- |
-| `console.time(label)` | Starts a new timer. Warns if a timer with the same label exists. |
-| `console.timeLog(label, ...data)` | Logs intermediate timing info (does **not** stop the timer). |
-| `console.timeEnd(label)` | Ends a timer and logs the total elapsed time. Warns if no timer exists. |
-
-Example:
-
-```ts
-console.time('load');
-loadWorldData();
-console.timeLog('load', 'Halfway through loading');
-finalizeLoad();
-console.timeEnd('load'); // Logs elapsed time
-```
-
-### Additional Console Methods
-
-The following methods are present for API completeness but are currently **no-ops**:
-
-- `console.clear()`
-- `console.dir()`
-- `console.dirxml()`
-- `console.count()`
-- `console.countReset()`
-- `console.group()`
-- `console.groupCollapsed()`
-- `console.groupEnd()`
-- `console.timeStamp()`
-- `console.profile()`
-- `console.profileEnd()`
-
-## Cheatsheet
-
-This section summarizes the most common patterns for declaring tables, reducers, and indexes in TypeScript modules.
-Each example assumes:
-
-```ts
-import { schema, table, t } from 'spacetimedb/server';
-```
-
----
-
-### Tables
-
-```ts
-const products = table(
- { name: 'products', public: true },
- {
- id: t.u32().primaryKey().autoInc(),
- sku: t.string().unique(),
- name: t.string().index('btree'),
- }
-);
-```
-
-- `.primaryKey()` defines a primary key column (only one per table).
-- `.autoInc()` assigns increasing integer IDs automatically when you insert with zero.
-- `.unique()` defines a unique constraint (non-primary).
-- `.index('btree')` adds a searchable index to speed up lookups and range filters.
-
----
-
-### Reducers
-
-```ts
-const spacetimedb = schema(products);
-
-// Insert a new product
-spacetimedb.reducer('insert_product', products.rowType, (ctx, product) => {
- ctx.db.products.insert(product);
-});
-
-// Update by SKU (unique key)
-spacetimedb.reducer('update_product_by_sku', products.rowType, (ctx, product) => {
- ctx.db.products.sku.update(product);
-});
-
-// Delete by SKU
-spacetimedb.reducer('delete_product_by_sku', { sku: t.string() }, (ctx, { sku }) => {
- ctx.db.products.sku.delete(sku);
-});
-```
-
-Reducers mutate tables via `ctx.db.`.
-Reducers are transactional and automatically roll back if they throw an exception.
-
----
-
-### Indexes
-
-```ts
-for (const row of ctx.db.products.name.filter(['A', ['M', 'Z']])) {
- // All products whose names start with a letter between "A" and "Z"
-}
-
-const deletedCount = ctx.db.products.name.delete(['G']);
-```
-
-Indexes may be filtered by a prefix or a bounded range.
-They are generated automatically from `.index('btree')` annotations or declared explicitly in table options.
-
----
-
-### Scheduled Reducers
-
-```ts
-const Reminders = table(
- { name: 'reminders', scheduled: 'send_reminder' },
- {
- scheduled_id: t.u64().primaryKey().autoInc(),
- scheduled_at: t.scheduleAt(),
- message: t.string(),
- }
-);
-
-spacetimedb.reducer('send_reminder', { arg: Reminders.rowType }, (_ctx, { arg }) => {
- // Invoked automatically by the scheduler
- // arg.message, arg.scheduled_at, arg.scheduled_id
-});
-```
-
-Insert rows into a scheduled table to queue work; delete them to cancel.
-Reducers may guard against manual invocation by checking `ctx.sender`.
-
----
-
-### Common Context Properties
-
-| Property | Description |
-| ------------------ | ---------------------------------------------------------- |
-| `ctx.db` | Handle to all tables and indexes in the current database. |
-| `ctx.sender` | The `Identity` of the reducer caller. |
-| `ctx.connectionId` | The `ConnectionId` of the reducer caller (or `undefined`). |
-| `ctx.timestamp` | A `Timestamp` for when the reducer was invoked. |
-
----
-
-This cheatsheet provides concise operational examples.
-For detailed behavior and lifecycle semantics, see the sections on [Tables](#tables), [Reducers](#reducers), and [Indexes](#indexes) above.
diff --git a/docs/docs/90600-server-module-languages/_category_.json b/docs/docs/90600-server-module-languages/_category_.json
deleted file mode 100644
index e4519c01f0c..00000000000
--- a/docs/docs/90600-server-module-languages/_category_.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "label": "Old Modules"
-}
\ No newline at end of file