diff --git a/.cursor/rules/spacetimedb.md b/.cursor/rules/spacetimedb.md
new file mode 100644
index 00000000..2bf12e8d
--- /dev/null
+++ b/.cursor/rules/spacetimedb.md
@@ -0,0 +1,4 @@
+- Before implementing SpacetimeDB code, reference the LLMs text and ensure you are using the proper syntax.
+- If you aren't certain about the SpacetimeDB implementation, ask the user
+- You're executing commands in Powershell, so make sure if you want to execute multiple commands to use ; instead of &&
+- All your commands that you execute start from the project directory, so make sure you take that into account when you're navigating directories.
\ No newline at end of file
diff --git a/.github/workflows/check-cli-reference.yml b/.github/workflows/check-cli-reference.yml
new file mode 100644
index 00000000..de4f8597
--- /dev/null
+++ b/.github/workflows/check-cli-reference.yml
@@ -0,0 +1,48 @@
+on:
+ pull_request:
+ workflow_dispatch:
+ inputs:
+ ref:
+ description: 'SpacetimeDB ref'
+ required: false
+ default: ''
+permissions: read-all
+
+name: Check CLI docs
+
+jobs:
+ cli_docs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Find Git ref
+ shell: bash
+ run: |
+ echo "GIT_REF=${{ github.event.inputs.ref || 'master' }}" >>"$GITHUB_ENV"
+ - name: Checkout sources
+ uses: actions/checkout@v4
+ with:
+ repository: clockworklabs/SpacetimeDB
+ ref: ${{ env.GIT_REF }}
+ - uses: dsherret/rust-toolchain-file@v1
+ - name: Checkout docs
+ uses: actions/checkout@v4
+ with:
+ path: spacetime-docs
+ - name: Check for docs change
+ run: |
+ cargo run --features markdown-docs -p spacetimedb-cli > ../spacetime-docs/docs/cli-reference.md
+ cd spacetime-docs
+ # This is needed because our website doesn't render markdown quite properly.
+ # See the README in spacetime-docs for more details.
+ sed -i'' -E 's!^(##) `(.*)`$!\1 \2!' docs/cli-reference.md
+ sed -i'' -E 's!^(######) \*\*(.*)\*\*$!\1 \2!' docs/cli-reference.md
+ git status
+ if git diff --exit-code HEAD; then
+ echo "No docs changes detected"
+ else
+ echo "It looks like the CLI docs have changed."
+ echo "These docs are expected to match exactly the helptext generated by the CLI in SpacetimeDB (${{env.GIT_REF}})."
+ echo "Once a corresponding change has merged in SpacetimeDB, re-run this check."
+ echo "See https://github.com/clockworklabs/spacetime-docs/#cli-reference-section for more info on how to generate these docs from SpacetimeDB."
+ exit 1
+ fi
diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml
new file mode 100644
index 00000000..1053fe7d
--- /dev/null
+++ b/.github/workflows/check-links.yml
@@ -0,0 +1,26 @@
+name: Check Link Validity in Documentation
+
+on:
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ check-links:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16' # or the version of Node.js you're using
+
+ - name: Install dependencies
+ run: |
+ npm install
+
+ - name: Run link check
+ run: |
+ npm run check-links
diff --git a/.github/workflows/git-tree-checks.yml b/.github/workflows/git-tree-checks.yml
new file mode 100644
index 00000000..1166e526
--- /dev/null
+++ b/.github/workflows/git-tree-checks.yml
@@ -0,0 +1,22 @@
+name: Git tree checks
+
+on:
+ pull_request:
+ types: [opened, edited, reopened, synchronize]
+ merge_group:
+permissions: read-all
+
+jobs:
+ check_base_ref:
+ name: Release branch restriction
+ runs-on: ubuntu-latest
+ steps:
+ - if: |
+ github.event_name == 'pull_request' &&
+ github.event.pull_request.base.ref == 'release' &&
+ ! startsWith(github.event.pull_request.head.ref, 'release-')
+ run: |
+ echo 'Only `release-*` branches are allowed to merge into the release branch `release`.'
+ echo 'Are you **sure** that you want to merge into release?'
+ echo 'Is this **definitely** just cherrypicking commits that are already in `master`?'
+ exit 1
diff --git a/.github/workflows/validate-nav-build.yml b/.github/workflows/validate-nav-build.yml
new file mode 100644
index 00000000..b76378d6
--- /dev/null
+++ b/.github/workflows/validate-nav-build.yml
@@ -0,0 +1,40 @@
+name: Validate nav.ts Matches nav.js
+
+on:
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ validate-build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16'
+
+ - name: Install dependencies
+ run: |
+ npm install
+
+ - name: Backup existing nav.js
+ run: |
+ mv docs/nav.js docs/nav.js.original
+
+ - name: Build nav.ts
+ run: |
+ npm run build
+
+ - name: Compare generated nav.js with original nav.js
+ run: |
+ diff -q docs/nav.js docs/nav.js.original || (echo "Generated nav.js differs from committed version. Run 'npm run build' and commit the updated file." && exit 1)
+
+ - name: Restore original nav.js
+ if: success() || failure()
+ run: |
+ mv docs/nav.js.original docs/nav.js
diff --git a/.gitignore b/.gitignore
index 55f71abd..d839abde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,4 @@
.idea
*.log
node_modules
-dist
\ No newline at end of file
+.DS_store
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..2921455b
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": true,
+ "arrowParens": "avoid",
+ "jsxSingleQuote": false,
+ "trailingComma": "es5",
+ "endOfLine": "auto",
+ "printWidth": 80
+}
diff --git a/LICENSE.txt b/LICENSE.txt
index dd5b3a58..d9a10c0d 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -172,3 +172,5 @@
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
index cfe1e0af..b5c66551 100644
--- a/README.md
+++ b/README.md
@@ -13,13 +13,14 @@ To make changes to our docs, you can open a pull request in this repository. You
git clone ssh://git@github.com//spacetime-docs
```
-3. Make your edits to the docs that you want to make + test them locally (see Testing Your Edits below)
+3. Make your edits to the docs that you want to make + test them locally
4. Commit your changes:
```bash
git add .
git commit -m "A specific description of the changes I made and why"
```
+
5. Push your changes to your fork as a branch
```bash
@@ -29,6 +30,22 @@ git push -u origin a-branch-name-that-describes-my-change
6. Go to our GitHub and open a PR that references your branch in your fork on your GitHub
+> NOTE! If you make a change to `nav.ts` you will have to run `npm run build` to generate a new `docs/nav.js` file.
+
+#### CLI Reference Section
+1. Make sure that https://github.com/clockworklabs/SpacetimeDB/pull/2276 is included in your `spacetimedb-cli` binary
+1. Run `cargo run --features markdown-docs -p spacetimedb-cli > cli-reference.md`
+
+We currently don't properly render markdown backticks and bolding that are inside of headers, so do these two manual replacements to make them look okay (these have only been tested on Linux):
+```bash
+sed -i'' -E 's!^(##) `(.*)`$!\1 \2!' docs/cli-reference.md
+sed -i'' -E 's!^(######) \*\*(.*)\*\*$!\1 \2!' docs/cli-reference.md
+```
+
+### Checking Links
+
+We have a CI job which validates internal links. You can run it locally with `npm run check-links`. This will print any internal links (i.e. links to other docs pages) whose targets do not exist, including fragment links (i.e. `#`-ey links to anchors).
+
## License
This documentation repository is licensed under Apache 2.0. See LICENSE.txt for more details.
diff --git a/STYLE.md b/STYLE.md
new file mode 100644
index 00000000..81de954f
--- /dev/null
+++ b/STYLE.md
@@ -0,0 +1,412 @@
+# SpacetimeDB Documentation Style Guide
+
+## Purpose of this document
+
+This document describes how the documentation in this repo, which winds up on the SpacetimeDB website, should be written. Much of the content in this repository currently does not meet these standards. Reworking everything to meet these standards is a significant undertaking, and in all honesty will probably never be complete, but at the very least we want to avoid generating new text which doesn't meet our standards. We will request changes on or reject docs PRs which do not obey these rules, even if they are updating or replacing existing docs which also did not obey these rules.
+
+## General guidelines
+
+### Target audience
+
+The SpacetimeDB documentation should be digestable and clear for someone who is a competent web or game developer, but does not have a strong grounding in theoretical math or CS. This means we generally want to steer clear of overly terse formal notations, instead using natural language (like, English words) to describe what's going on.
+
+#### The exception: internals docs
+
+We offer some level of leeway on this for documentation of internal, low-level or advanced interfaces. For example, we don't expect the average user to ever need to know the details of the BSATN binary encoding, so we can make some stronger assumptions about the technical background of readers in that context.
+
+On the other hand, this means that docs for these low-level interfaces should be up-front that they're not for everyone. Start each page with something like, "SUBJECT is a low-level implementation detail of HIGHER-LEVEL SYSTEM. Users of HIGHER-LEVEL SYSTEM should not need to worry about SUBJECT. This document is provided for advanced users and those curious about SpacetimeDB internals." Also make the "HIGHER-LEVEL SYSTEM" a link to the documentation for the user-facing component.
+
+### Code formatting
+
+Use triple-backtick code blocks for any example longer than half a line on a 100-character-wide terminal. Always include a relevant language for syntax highlighting; reasonable choices are:
+
+- `csharp`.
+- `rust`.
+- `typescript`.
+- `sql`.
+
+Use single-backtick inline code highlighting for names of variables, functions, methods &c. Where possible, make these links, usually sharpsign anchor links, to the section of documentation which describes that variable.
+
+In normal text, use italics without any backticks for meta-variables which the user is expected to fill in. Always include an anchor, sentence or "where" clause which describes the meaning of the meta-variable. (E.g. is it a table name? A reducer? An arbitrary string the user can choose? The output of some previous command?)
+
+For meta-variables in code blocks, enclose the meta-variable name in `{}` curly braces. Use the same meta-variable names in code as in normal text. Always include a sentence or "where" clause which describes the meaning of the meta-variable.
+
+Do not use single-backtick code highlighting for words which are not variable, function, method or type names. (Or other sorts of defined symbols that appear in actual code.) Similarly, do not use italics for words which are not meta-variables that the reader is expected to substitute. In particular, do not use code highlighting for emphasis or to introduce vocabulary.
+
+Because this meta-syntax is not valid syntax, it should be followed by an example that shows what the result would look like in a
+concrete situation.
+
+For example:
+
+> To find rows in a table *table* with a given value in a `#[unique]` or `#[primary_key]` column, do:
+>
+> ```rust
+> ctx.db.{table}().{column}().find({value})
+> ```
+>
+> where *column* is the name of the unique column and *value* is the value you're looking for in that column.
+> For example:
+>
+> ```rust
+> ctx.db.people().name().find("Billy")
+> ```
+>
+> This is equivalent to:
+>
+> ```sql
+> SELECT * FROM {table} WHERE {column} = {value}
+> ```
+
+### Pseudocode
+
+Avoid writing pseudocode whenever possible; just write actual code in one of our supported languages. If the file you're writing in is relevant to a specific supported language, use that. If the file applies to the system as a whole, write it in as many of our supported languages as you're comfortable, then ping another team member to help with the languages you don't know.
+
+If it's just for instructional purposes, it can be high-level and include calls to made-up functions, so long as those functions have descriptive names. If you do this, include a note before the code block which clarifies that it's not intended to be runnable as-is.
+
+### Describing limitations and future plans
+
+Call missing features "current limitations" and bugs "known issues."
+
+Be up-front about what isn't implemented right now. It's better for our users to be told up front that something is broken or not done yet than for them to expect it to work and to be surprised when it doesn't.
+
+Don't make promises, even weak ones, about what we plan to do in the future, within tutorials or reference documents. Statements about the future belong in a separate "roadmap" or "future plans" document. Our idea of "soon" is often very different from our users', and our priorities shift rapidly and frequently enough that statements about our future plans rarely end up being accurate.
+
+If your document needs to describe a feature that isn't implemented yet, either rewrite to not depend on that feature, or just say that it's a "current limitation" without elaborating further. Include a workaround if there is one.
+
+### Menu items and paths
+
+When describing GUI elements and menu items, like the **Unity Registry** tab, use bolded text to draw attention to any phrases that will appear in the actual UI. Readers will see this bolded text in the documentation and look for it on their screen. Where applicable, include a short description of the type or category of element, like "tab" above, or the **File** menu. This category should not be bolded, since it is not a word the reader can expect to find on their screen.
+
+When describing a chain of accesses through menus and submenus, use the **->** thin arrow (that's `->`, a hyphen followed by a greater-than sign) as a separator, like **File -> Quit** or **Window -> Package Manager**. List the top-level menu first, and proceed left-to-right until you reach the option you want the user to interact with. Include all nested submenus, like **Foo -> Bar -> Baz -> Quux**. Bold the whole sequence, including the arrows.
+
+It's generally not necessary or desirable to tell users where to look for the top-level menu. You may be tempted to write something like, "Open the **File** menu in the upper left, and navigate **File -> Export as -> Export as PDF**." Do not include "in the upper left" unless you are absolutely confident that the menu will be located there on any combination of OS, version, desktop environment, window manager, theming configuration &c. Even within a single system, UI designers are known to move graphical elements around during updates, making statements like "upper left" obsolete and stale. We can generally trust our readers to be familiar with their own systems and the software they use, and none of our documents involve introducing readers to new GUI software. (E.g. the Unity tutorial is targeted at introducing SpacetimeDB to people who already know Unity.) "Open the **File** menu and navigate **File -> Export as -> Export as PDF**" is sufficient.
+
+### Table names
+
+Table names should be in the singular. `user` rather than `users`, `player` rather than `players`, &c. This applies both to SQL code snippets and to modules. In module code, table names should obey the language's casing for method names: in Rust, `snake_case`, and in C#, `PascalCase`. A table which has a row for each player, containing their most recent login time, might be named `player_last_login_time` in a Rust module, or `PlayerLastLoginTime` in a C# module.
+
+## Key vocabulary
+
+There are a small number of key terms that we need to use consistently throughout the documentation.
+
+The most important distinction is the following:
+
+- **Database**: This is the active, running entity that lives on a host. It contains a bunch of tables, like a normal database. It also has extra features: clients can connect to it directly and remotely call its stored procedures.
+- **Module**: This is the source code that a developer uses to specify a database. It is a combination of a database schema and a collection of stored procedures. Once built and published, it becomes part of the running database.
+
+A database **has** a module; the module **is part of** the database.
+
+The module does NOT run on a host. The **database** runs on a host.
+
+A client does NOT "connect to the module". A client **connects to the database**.
+
+This distinction is subtle but important. People know what databases are, and we should reinforce that SpacetimeDB is a database. "Module" is a quirky bit of vocabulary we use to refer to collections of stored procedures. A RUNNING APPLICATION IS NOT CALLED A MODULE.
+
+Other key vocabulary:
+- (SpacetimeDB) **Host**: the application that hosts **databases**. It is multi-tenant and can host many **databases** at once.
+- **Client**: any application that connects to a **database**.
+- **End user**: anybody using a **client**.
+- **Database developer**: the person who maintains a **database**.
+ - DO NOT refer to database developers as "users" in documentation.
+ Sometimes we colloquially refer to them as "our users" internally,
+ but it is clearer to use the term "database developers" in public.
+- **Table**: A set of typed, labeled **rows**. Each row stores data for a number of **columns**. Used to store data in a **database**.
+- **Column**: you know what this is.
+- **Row**: you know what this is.
+ - DO NOT refer to rows as "tuples", because the term overlaps confusingly with "tuple types" in module languages.
+ We reserve the word "tuple" to refer to elements of these types.
+- **Reducer**: A stored procedure that can be called remotely in order to update a **database**.
+ - Confusingly, reducers do not actually "reduce" data in the sense of querying and compressing it to return a result.
+ But it is too late to change it. C'est la vie.
+- **Connection**: a connection between a **client** and a **database**. Receives an **Address**. A single connection may open multiple **subscriptions**.
+- **Subscription**: an active query that mirrors data from the database to a **client**.
+- **Address**: identifier for an active connection.
+- **Identity**: A combination of an issuing OpenID Connect provider and an Identity Token issued by that provider. Globally unique and public.
+ - Technically, "Identity" should be called "Identifier", but it is too late to change it.
+ - A particular **end user** may have multiple Identities issued by different providers.
+ - Each **database** also has an **Identity**.
+
+## Reference pages
+
+Reference pages are where intermediate users will look to get a view of all of the capabilities of a tool, and where experienced users will check for specific information on behaviors of the types, functions, methods &c they're using. Each user-facing component in the SpacetimeDB ecosystem should have a reference page.
+
+Each reference page should start with an introduction paragraph that says what the component is and when and how the user will interact with it. It should then either include a section describing how to install or set up that component, or a link to another page which accomplishes the same thing.
+
+### Tone, tense and voice
+
+Reference pages should be written in relatively formal language that would seem at home in an encyclopedia or a textbook. Or, say, [the Microsoft .NET API reference](https://learn.microsoft.com/en-us/dotnet/api/?view=net-8.0).
+
+#### Declarative present tense, for behavior of properties, functions and methods
+
+Use the declarative voice when describing how code works or what it does. [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-8.0):
+
+> Public static (`Shared` in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
+>
+> An `ArrayList` can support multiple readers concurrently, as long as the collection is not modified. To guarantee the thread safety of the `ArrayList`, all operations must be done through the wrapper returned by the `Synchronized(IList)` method.
+
+#### *Usually* don't refer to the reader
+
+Use second-person pronouns (i.e. "you") sparingly to draw attention to actions the reader should take to work around bugs or avoid footguns. Often these advisories should be pulled out into note, warning or quote-blocks. [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-8.0):
+
+> Enumerating through a collection is intrinsically not a thread-safe procedure. Even when a collection is synchronized, other threads can still modify the collection, which causes the enumerator to throw an exception. To guarantee thread safety during enumeration, you can either lock the collection during the entire enumeration or catch the exceptions resulting from changes made by other threads.
+
+#### *Usually* don't refer to "we" or "us"
+
+Use first-person pronouns sparingly to draw attention to non-technical information like design advice. Always use the first-person plural (i.e. "we" or "us") and never the singular (i.e. "I" or "me"). Often these should be accompanied by marker words like "recommend," "advise," "encourage" or "discourage." [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-8.0):
+
+> We don't recommend that you use the `ArrayList` class for new development. Instead, we recommend that you use the generic `List` class.
+
+#### *Usually* Avoid Passive Voice
+
+Use active voice rather than passive voice to avoid ambiguity regarding who is doing the action. Active voice directly attributes actions to the subject, making sentences easier to understand. For example:
+
+- Passive voice: "The method was invoked."
+- Active voice: "The user invoked the method."
+
+The second example is more straightforward and clarifies who is performing the action. In most cases, prefer using the active voice to maintain a clear and direct explanation of code behavior.
+
+However, passive voice may be appropriate in certain contexts where the actor is either unknown or irrelevant. In these cases, the emphasis is placed on the action or result rather than the subject performing it. For example:
+
+- "The `Dispose` method is called automatically when the object is garbage collected."
+### Tables and links
+
+Each reference page should have one or more two-column tables, where the left column are namespace-qualified names or signatures, and the right column are one-sentence descriptions. Headers are optional. If the table contains multiple different kinds of items (e.g. types and functions), the left column should include the kind as a suffix. [For example](https://learn.microsoft.com/en-us/dotnet/api/?view=net-8.0):
+
+> | Name | Description |
+> |-|-|
+> | `Microsoft.CSharp.RuntimeBinder` Namespace | Provides classes and interfaces that support interoperation between Dynamic Language Runtime and C#. |
+> | `Microsoft.VisualBasic` Namespace | Contains types that support the Visual Basic Runtime in Visual Basic. |
+
+The names should be code-formatted, and should be links to a page or section for that definition. The short descriptions should be the same as are used at the start of the linked page or section (see below).
+
+Authors are encouraged to write multiple different tables on the same page, with headers between introducing them. E.g. it may be useful to divide classes from interfaces, or to divide names by conceptual purpose. [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections?view=net-8.0):
+
+> # Classes
+>
+> | ArrayList | Implements the IList interface using an array whose size is dynamically increased as required. |
+> | BitArray | Manages a compact array of bit values, which are represented as Booleans, where true indicates that the bit is on (1) and false indicates the bit is off (0). |
+>
+> ...
+>
+> # Interfaces
+>
+> | ICollection | Defines size, enumerators, and synchronization methods for all nongeneric collections. |
+> | IComparer | Exposes a method that compares two objects. |
+>
+> ...
+
+### Sections for individual definitions
+
+#### Header
+
+When writing a section for an individual definition, start with any metadata that users will need to refer to the defined object, like its namespace. Then write a short paragraph, usually just a single sentence, which gives a high-level description of the thing. This sentence should be in the declarative present tense with an active verb. Start with the verb, with the thing being defined as the implied subject. [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-8.0):
+
+> ArrayList Class
+> [...]
+> Namespace: `System.Collections`
+> [...]
+> Implements the IList interface using an array whose size is dynamically increased as required.
+
+Next, add a triple-backtick code block that contains just the declaration or signature of the variable, function or method you're describing.
+
+What, specifically, counts as the declaration or signature is somewhat context-dependent. A good general rule is that it's everything in the source code to the left of the equals sign `=` or curly braces `{}`. You can edit this to remove implementation details (e.g. superclasses that users aren't supposed to see), or to add information that would be helpful but isn't in the source (e.g. trait bounds on generic parameters of types which aren't required to instantiate the type, but which most methods require, like `Eq + Hash` for `HashMap`). [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-8.0):
+
+> ```csharp
+> public class ArrayList : ICloneable, System.Collections.IList
+> ```
+
+If necessary, this should be followed by one or more paragraphs of more in-depth description.
+
+#### Examples
+
+Next, within a subheader named "Examples," include a code block with examples.
+
+To the extent possible, this code block should be freestanding. If it depends on external definitions that aren't included in the standard library or are not otherwise automatically accessible, add a note so that users know what they need to supply themselves (e.g. that the `mod module_bindings;` refers to the `quickstart-chat` module). Do not be afraid to paste the same "header" or "prelude" code (e.g. a table declaration) into a whole bunch of code blocks, but try to avoid making easy-to-miss minor edits to such "header" code.
+
+Add comments to this code block which describe what it does. In particular, if the example prints to the console, show the expected output in a comment. [For example](https://learn.microsoft.com/en-us/dotnet/api/system.collections.arraylist?view=net-8.0):
+
+> ```csharp
+> using System;
+> using System.Collections;
+> public class SamplesArrayList {
+>
+> public static void Main() {
+>
+> // Creates and initializes a new ArrayList.
+> ArrayList myAL = new ArrayList();
+> myAL.Add("Hello");
+> myAL.Add("World");
+> myAL.Add("!");
+>
+> // Displays the properties and values of the ArrayList.
+> Console.WriteLine( "myAL" );
+> Console.WriteLine( " Count: {0}", myAL.Count );
+> Console.WriteLine( " Capacity: {0}", myAL.Capacity );
+> Console.Write( " Values:" );
+> PrintValues( myAL );
+> }
+>
+> public static void PrintValues( IEnumerable myList ) {
+> foreach ( Object obj in myList )
+> Console.Write( " {0}", obj );
+> Console.WriteLine();
+> }
+> }
+>
+>
+> /*
+> This code produces output similar to the following:
+>
+> myAL
+> Count: 3
+> Capacity: 4
+> Values: Hello World !
+>
+> */
+> ```
+
+#### Child items
+
+If the described item has any children (e.g. properties and methods of classes, variants of enums), include one or more tables for those children, as described above, followed by subsections for each child item. These subsections follow the same format as for the parent items, with a header, declaration, description, examples and tables of any (grand-)children.
+
+If a documentation page ends up with more than 3 layers of nested items, split it so that each top-level item has its own page.
+
+### Grammars and syntax
+
+Reference documents, particularly for SQL or our serialization formats, will sometimes need to specify grammars. Before doing this, be sure you need to, as a grammar specification is scary and confusing to even moderately technical readers. If you're describing data that obeys some other language that readers will be familiar with, write a definition in or suited to that language instead of defining the grammar. For example, when describing a JSON encoding, consider writing a TypeScript-style type instead of a grammar.
+
+If you really do need to describe a grammar, write an EBNF description inside a triple-backticks code block with the `ebnf` language marker. (I assume that any grammar we need to describe will be context-free.) Start with the "topmost" or "entry" nonterminal, i.e. the syntactic construction that we actually want to parse, and work "downward" towards the terminals. For example, when describing SQL, `statement` is at the top, and `literal` and `ident` are at or near the bottom. You don't have to include trivial rules like those for literals.
+
+Then, write a whole bunch of examples under a subheader "Examples" in another tripple-backtick code block, this one with an appropriate language marker for what you're describing. Include at least one simple example and at least one complicated example. Try to include examples which exercise all of the features your grammar can express.
+
+## Overview pages
+
+Landing page type things, usually named `index.md`.
+
+### Tone, tense and voice
+
+Use the same guidelines as for reference pages, except that you can refer to the reader as "you" more often.
+
+### Links
+
+Include as many links to more specific docs pages as possible within the text. Sharp-links to anchors/headers within other docs pages are super valuable here!
+
+### FAQs
+
+If there's any information you want to impart to users but you're not sure how to shoehorn it into any other page or section, just slap it in an "FAQ" section at the bottom of an overview page.
+
+Each FAQ item should start with a subheader, which is phrased as a question a user would ask.
+
+Answer these questions starting with a declarative or conversational sentence. Refer to the asker as "you," and their project as "your client," "your module" or "your app."
+
+For example:
+
+> #### What's the difference between a subscription query and a one-off query?
+>
+> Subscription queries are incremental: your client receives updates whenever the database state changes, containing only the altered rows. This is an efficient way to maintain a "materialized view," that is, a local copy of some subset of the database. Use subscriptions when you want to watch rows and react to changes, or to keep local copies of rows which you'll read frequently.
+>
+> A one-off query happens once, and then is done. Use one-off queries to look at rows you only need once.
+>
+> #### How do I get an authorization token?
+>
+> You can supply your users with authorization tokens in several different ways; which one is best for you will depend on the needs of your app. [...] (I don't actually want to write a real answer to this question - pgoldman 2024-11-19.)
+>
+> #### Can my client connect to multiple databases at the same time?
+>
+> Yes! Your client can construct as many `DbConnection`s simultaneously as it wants to, each of which will operate independently. If you want to connect to two databases with different schemas, use `spacetime generate` to include bindings for both of them in your client project. Note that SpacetimeDB may reject multiple concurrent connections to the same database by a single client.
+
+## Tutorial pages
+
+Tutorials are where we funnel new-to-intermediate users to introduce them to new concepts.
+
+Some tutorials are associated with specific SpacetimeDB components, and should be included in (sub)directories alongside the documentation for those components. Other tutorials are more general or holisitc, touching many different parts of SpacetimeDB to produce a complete game or app, and should stand alone or be grouped into a "tutorials" or "projects" directory.
+
+### Tone, tense and voice
+
+Be friendly, but still precise and professional. Refer to the reader as "you." Make gentle suggestions for optional actions with "can" or "could." When telling them to do something that's required to advance the tutorial, use the imperative voice. When reminding them of past tutorials or preparing them for future ones, say "we," grouping you (the writer) together with the reader. You two are going on a journey together, so get comfortable!
+
+### Scope
+
+You don't have to teach the reader non-SpacetimeDB-specific things. If you're writing a tutorial on Rust modules, for example, assume basic-to-intermediate familiarity with "Rust," so you can focus on teaching the reader about the "modules" part.
+
+### Introduction: tell 'em what you're gonna tell 'em
+
+Each tutorial should start with a statement of its scope (what new concepts are introduced), goal (what you build or do during the tutorial) and prerequisites (what other tutorials you should have finished first).
+
+> In this tutorial, we'll implement a simple chat server as a SpacetimeDB module. We'll learn how to declare tables and to write reducers, functions which run in the database to modify those tables in response to client requests. Before starting, make sure you've [installed SpacetimeDB](/install) and [logged in with a developer `Identity`](/auth/for-devs).
+
+### Introducing and linking to definitions
+
+The first time a tutorial or series introduces a new type / function / method / &c, include a short paragraph describing what it is and how it's being used in this tutorial. Make sure to link to the reference section on that item.
+
+### Tutorial code
+
+If the tutorial involves writing code, e.g. for a module or client, the tutorial should include the complete result code within its text. Ideally, it should be possible for a reader to copy and paste all the code blocks in the document into a file, effectively concatenating them together, and wind up with a coherent and runnable program. Sometimes this is not possible, e.g. because C# requires wrapping your whole file in a bunch of scopes. In this case, precede each code block with a sentence that describes where the reader is going to paste it.
+
+Include even uninteresting code, like imports! You can rush through these without spending too much time on them, but make sure that every line of code required to make the project work appears in the tutorial.
+
+> spacetime init should have pre-populated server/src/lib.rs with a trivial module. Clear it out so we can write a new, simple module: a bare-bones chat server.
+>
+> To the top of server/src/lib.rs, add some imports we'll be using:
+>
+> ```rust
+> use spacetimedb::{table, reducer, Table, ReducerContext, Identity, Timestamp};
+> ```
+
+For code that *is* interesting, after the code block, add a description of what the code does. Usually this will be pretty succinct, as the code should hopefully be pretty clear on its own.
+
+### Words for telling the user to write code
+
+When introducing a code block that the user should put in their file, don't say "copy" or "paste." Instead, tell them (in the imperative) to "add" or "write" the code. This emphasizes active participation, as opposed to passive consumption, and implicitly encourages the user to modify the tutorial code if they'd like. Readers who just want to copy and paste will do so without our telling them.
+
+> To `server/src/lib.rs`, add the definition of the connect reducer:
+>
+> ```rust
+> I don't actually need to fill this in.
+> ```
+
+### Conclusion
+
+Each tutorial should end with a conclusion section, with a title like "What's next?"
+
+#### Tell 'em what you told 'em
+
+Start the conclusion with a sentence or paragraph that reminds the reader what they accomplished:
+
+> You've just set up your first database in SpacetimeDB, complete with its very own tables and reducers!
+
+#### Tell them what to do next
+
+If this tutorial is part of a series, link to the next entry:
+
+> You can use any of SpacetimDB's supported client languages to do this. Take a look at the quickstart guide for your client language of choice: [Rust](/docs/sdks/rust/quickstart), [C#](/docs/sdks/c-sharp/quickstart), or [TypeScript](/docs/sdks/typescript/quickstart). If you are planning to use SpacetimeDB with the Unity game engine, you can skip right to the [Unity Comprehensive Tutorial](/docs/unity/part-1).
+
+If this tutorial is about a specific component, link to its reference page:
+
+> Check out the [Rust SDK Reference](/docs/sdks/rust) for a more comprehensive view of the SpacetimeDB Rust SDK.
+
+If this tutorial is the end of a series, or ends with a reasonably complete app, throw in some ideas about how the reader could extend it:
+
+> Our basic terminal interface has some limitations. Incoming messages can appear while the user is typing, which is less than ideal. Additionally, the user's input gets mixed with the program's output, making messages the user sends appear twice. You might want to try improving the interface by using [Rustyline](https://crates.io/crates/rustyline), [Cursive](https://crates.io/crates/cursive), or even creating a full-fledged GUI.
+>
+> Once your chat server runs for a while, you might want to limit the messages your client loads by refining your `Message` subscription query, only subscribing to messages sent within the last half-hour.
+>
+> You could also add features like:
+>
+> - Styling messages by interpreting HTML tags and printing appropriate [ANSI escapes](https://en.wikipedia.org/wiki/ANSI_escape_code).
+> - Adding a `moderator` flag to the `User` table, allowing moderators to manage users (e.g., time-out, ban).
+> - Adding rooms or channels that users can join or leave.
+> - Supporting direct messages or displaying user statuses next to their usernames.
+
+#### Complete code
+
+If the tutorial involved writing code, add a link to the complete code. This should be somewhere on GitHub, either as its own repo, or as an example project within an existing repo. Ensure the linked folder has a README.md file which includes:
+
+- The name of the tutorial project.
+- How to run or interact with the tutorial project, whatever that means (e.g. publish to maincloud and then `spacetime call`).
+- Links to external dependencies (e.g. for client projects, the module which it runs against).
+- A back-link to the tutorial that builds this project.
+
+At the end of the tutorial that builds the `quickstart-chat` module in Rust, you might write:
+
+> You can find the full code for this module in [the SpacetimeDB module examples](https://github.com/clockworklabs/SpacetimeDB/tree/master/modules/quickstart-chat).
diff --git a/docs/Client SDK Languages/C#/SDK Reference.md b/docs/Client SDK Languages/C#/SDK Reference.md
deleted file mode 100644
index 3284e6fe..00000000
--- a/docs/Client SDK Languages/C#/SDK Reference.md
+++ /dev/null
@@ -1,932 +0,0 @@
-# The SpacetimeDB C# client SDK
-
-The SpacetimeDB client C# for Rust contains all the tools you need to build native clients for SpacetimeDB modules using C#.
-
-## Table of Contents
-
-- [The SpacetimeDB C# client SDK](#the-spacetimedb-c-client-sdk)
- - [Table of Contents](#table-of-contents)
- - [Install the SDK](#install-the-sdk)
- - [Using the `dotnet` CLI tool](#using-the-dotnet-cli-tool)
- - [Using Unity](#using-unity)
- - [Generate module bindings](#generate-module-bindings)
- - [Initialization](#initialization)
- - [Static Method `SpacetimeDBClient.CreateInstance`](#static-method-spacetimedbclientcreateinstance)
- - [Property `SpacetimeDBClient.instance`](#property-spacetimedbclientinstance)
- - [Class `NetworkManager`](#class-networkmanager)
- - [Method `SpacetimeDBClient.Connect`](#method-spacetimedbclientconnect)
- - [Event `SpacetimeDBClient.onIdentityReceived`](#event-spacetimedbclientonidentityreceived)
- - [Event `SpacetimeDBClient.onConnect`](#event-spacetimedbclientonconnect)
- - [Subscribe to queries](#subscribe-to-queries)
- - [Method `SpacetimeDBClient.Subscribe`](#method-spacetimedbclientsubscribe)
- - [Event `SpacetimeDBClient.onSubscriptionApplied`](#event-spacetimedbclientonsubscriptionapplied)
- - [View rows of subscribed tables](#view-rows-of-subscribed-tables)
- - [Class `{TABLE}`](#class-table)
- - [Static Method `{TABLE}.Iter`](#static-method-tableiter)
- - [Static Method `{TABLE}.FilterBy{COLUMN}`](#static-method-tablefilterbycolumn)
- - [Static Method `{TABLE}.Count`](#static-method-tablecount)
- - [Static Event `{TABLE}.OnInsert`](#static-event-tableoninsert)
- - [Static Event `{TABLE}.OnBeforeDelete`](#static-event-tableonbeforedelete)
- - [Static Event `{TABLE}.OnDelete`](#static-event-tableondelete)
- - [Static Event `{TABLE}.OnUpdate`](#static-event-tableonupdate)
- - [Observe and invoke reducers](#observe-and-invoke-reducers)
- - [Class `Reducer`](#class-reducer)
- - [Static Method `Reducer.{REDUCER}`](#static-method-reducerreducer)
- - [Static Event `Reducer.On{REDUCER}`](#static-event-reduceronreducer)
- - [Class `ReducerEvent`](#class-reducerevent)
- - [Enum `Status`](#enum-status)
- - [Variant `Status.Committed`](#variant-statuscommitted)
- - [Variant `Status.Failed`](#variant-statusfailed)
- - [Variant `Status.OutOfEnergy`](#variant-statusoutofenergy)
- - [Identity management](#identity-management)
- - [Class `AuthToken`](#class-authtoken)
- - [Static Method `AuthToken.Init`](#static-method-authtokeninit)
- - [Static Property `AuthToken.Token`](#static-property-authtokentoken)
- - [Static Method `AuthToken.SaveToken`](#static-method-authtokensavetoken)
- - [Class `Identity`](#class-identity)
- - [Customizing logging](#customizing-logging)
- - [Interface `ISpacetimeDBLogger`](#interface-ispacetimedblogger)
- - [Class `ConsoleLogger`](#class-consolelogger)
- - [Class `UnityDebugLogger`](#class-unitydebuglogger)
-
-## Install the SDK
-
-### Using the `dotnet` CLI tool
-
-If you would like to create a console application using .NET, you can create a new project using `dotnet new console` and add the SpacetimeDB SDK to your dependencies:
-
-```bash
-dotnet add package spacetimedbsdk
-```
-
-(See also the [CSharp Quickstart](./CSharpSDKQuickStart) for an in-depth example of such a console application.)
-
-### Using Unity
-
-To install the SpacetimeDB SDK into a Unity project, download the SpacetimeDB SDK from the following link.
-
-https://sdk.spacetimedb.com/SpacetimeDBUnitySDK.unitypackage
-
-In Unity navigate to the `Assets > Import Package > Custom Package...` menu in the menu bar. Select your `SpacetimeDBUnitySDK.unitypackage` file and leave all folders checked.
-
-(See also the [Unity Quickstart](./UnityQuickStart) and [Unity Tutorial](./UnityTutorialPart1).)
-
-## 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:
-
-```bash
-mkdir -p module_bindings
-spacetime generate --lang cs --out-dir module_bindings --project-path PATH-TO-MODULE-DIRECTORY
-```
-
-Replace `PATH-TO-MODULE-DIRECTORY` with the path to your SpacetimeDB module.
-
-## Initialization
-
-### Static Method `SpacetimeDBClient.CreateInstance`
-
-```cs
-namespace SpacetimeDB {
-
-public class SpacetimeDBClient {
- public static void CreateInstance(ISpacetimeDBLogger loggerToUse);
-}
-
-}
-```
-
-Create a global SpacetimeDBClient instance, accessible via [`SpacetimeDBClient.instance`](#property-spacetimedbclientinstance)
-
-| Argument | Type | Meaning |
-| ------------- | ----------------------------------------------------- | --------------------------------- |
-| `loggerToUse` | [`ISpacetimeDBLogger`](#interface-ispacetimedblogger) | The logger to use to log messages |
-
-There is a provided logger called [`ConsoleLogger`](#class-consolelogger) which logs to `System.Console`, and can be used as follows:
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-SpacetimeDBClient.CreateInstance(new ConsoleLogger());
-```
-
-### Property `SpacetimeDBClient.instance`
-
-```cs
-namespace SpacetimeDB {
-
-public class SpacetimeDBClient {
- public static SpacetimeDBClient instance;
-}
-
-}
-```
-
-This is the global instance of a SpacetimeDB client in a particular .NET/Unity process. Much of the SDK is accessible through this instance.
-
-### Class `NetworkManager`
-
-The Unity SpacetimeDB SDK relies on there being a `NetworkManager` somewhere in the scene. Click on the GameManager object in the scene, and in the inspector, add the `NetworkManager` component.
-
-
-
-This component will handle calling [`SpacetimeDBClient.CreateInstance`](#static-method-spacetimedbclientcreateinstance) for you, but will not call [`SpacetimeDBClient.Connect`](#method-spacetimedbclientconnect), you still need to handle that yourself. See the [Unity Quickstart](./UnityQuickStart) and [Unity Tutorial](./UnityTutorialPart1) for more information.
-
-### Method `SpacetimeDBClient.Connect`
-
-```cs
-namespace SpacetimeDB {
-
-class SpacetimeDBClient {
- public void Connect(
- string? token,
- string host,
- string addressOrName,
- bool sslEnabled = true
- );
-}
-
-}
-```
-
-
-
-Connect to a database named `addressOrName` accessible over the internet at the URI `host`.
-
-| Argument | Type | Meaning |
-| --------------- | --------- | -------------------------------------------------------------------------- |
-| `token` | `string?` | Identity token to use, if one is available. |
-| `host` | `string` | URI of the SpacetimeDB instance running the module. |
-| `addressOrName` | `string` | Address or name of the module. |
-| `sslEnabled` | `bool` | Whether or not to use SSL when connecting to SpacetimeDB. Default: `true`. |
-
-If a `token` is supplied, it will be passed to the new connection to identify and authenticate the user. Otherwise, a new token and [`Identity`](#class-identity) will be generated by the server and returned in [`onConnect`](#event-spacetimedbclientonconnect).
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-const string DBNAME = "chat";
-
-// Connect to a local DB with a fresh identity
-SpacetimeDBClient.instance.Connect(null, "localhost:3000", DBNAME, false);
-
-// Connect to cloud with a fresh identity
-SpacetimeDBClient.instance.Connect(null, "dev.spacetimedb.net", DBNAME, true);
-
-// Connect to cloud using a saved identity from the filesystem, or get a new one and save it
-AuthToken.Init();
-Identity localIdentity;
-SpacetimeDBClient.instance.Connect(AuthToken.Token, "dev.spacetimedb.net", DBNAME, true);
-SpacetimeDBClient.instance.onIdentityReceived += (string authToken, Identity identity) {
- AuthToken.SaveToken(authToken);
- localIdentity = identity;
-}
-```
-
-(You should probably also store the returned `Identity` somewhere; see the [`onIdentityReceived`](#event-spacetimedbclientonidentityreceived) event.)
-
-### Event `SpacetimeDBClient.onIdentityReceived`
-
-```cs
-namespace SpacetimeDB {
-
-class SpacetimeDBClient {
- public event Action onIdentityReceived;
-}
-
-}
-```
-
-Called when we receive an auth token and [`Identity`](#class-identity) from the server. The [`Identity`](#class-identity) serves as a unique public identifier for a client connected to the database. It can be for several purposes, such as filtering rows in a database for the rows created by a particular user. The auth token is a private access token that allows us to assume an identity.
-
-To store the auth token to the filesystem, use the static method [`AuthToken.SaveToken`](#static-method-authtokensavetoken). You may also want to store the returned [`Identity`](#class-identity) in a local variable.
-
-If an existing auth token is used to connect to the database, the same auth token and the identity it came with will be returned verbatim in `onIdentityReceived`.
-
-```cs
-// Connect to cloud using a saved identity from the filesystem, or get a new one and save it
-AuthToken.Init();
-Identity localIdentity;
-SpacetimeDBClient.instance.Connect(AuthToken.Token, "dev.spacetimedb.net", DBNAME, true);
-SpacetimeDBClient.instance.onIdentityReceived += (string authToken, Identity identity) {
- AuthToken.SaveToken(authToken);
- localIdentity = identity;
-}
-```
-
-### Event `SpacetimeDBClient.onConnect`
-
-```cs
-namespace SpacetimeDB {
-
-class SpacetimeDBClient {
- public event Action onConnect;
-}
-
-}
-```
-
-Allows registering delegates to be invoked upon authentication with the database.
-
-Once this occurs, the SDK is prepared for calls to [`SpacetimeDBClient.Subscribe`](#method-spacetimedbclientsubscribe).
-
-## Subscribe to queries
-
-### Method `SpacetimeDBClient.Subscribe`
-
-```cs
-namespace SpacetimeDB {
-
-class SpacetimeDBClient {
- public void Subscribe(List queries);
-}
-
-}
-```
-
-| Argument | Type | Meaning |
-| --------- | -------------- | ---------------------------- |
-| `queries` | `List` | SQL queries to subscribe to. |
-
-Subscribe to a set of queries, to be notified when rows which match those queries are altered.
-
-`Subscribe` will return an error if called before establishing a connection with the [`SpacetimeDBClient.Connect`](#method-connect) function. In that case, the queries are not registered.
-
-The `Subscribe` method does not return data directly. `spacetime generate` will generate classes [`SpacetimeDB.Types.{TABLE}`](#class-table) for each table in your module. These classes are used to reecive information from the database. See the section [View Rows of Subscribed Tables](#view-rows-of-subscribed-tables) for more information.
-
-A new call to `Subscribe` will remove all previous subscriptions and replace them with the new `queries`. If any rows matched the previous subscribed queries but do not match the new queries, those rows will be removed from the client cache, and [`{TABLE}.OnDelete`](#event-tableondelete) callbacks will be invoked for them.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-void Main()
-{
- AuthToken.Init();
- SpacetimeDBClient.CreateInstance(new ConsoleLogger());
-
- SpacetimeDBClient.instance.onConnect += OnConnect;
-
- // Our module contains a table named "Loot"
- Loot.OnInsert += Loot_OnInsert;
-
- SpacetimeDBClient.instance.Connect(/* ... */);
-}
-
-void OnConnect()
-{
- SpacetimeDBClient.instance.Subscribe(new List {
- "SELECT * FROM Loot"
- });
-}
-
-void Loot_OnInsert(
- Loot loot,
- ReducerEvent? event
-) {
- Console.Log($"Loaded loot {loot.itemType} at coordinates {loot.position}");
-}
-```
-
-### Event `SpacetimeDBClient.onSubscriptionApplied`
-
-```cs
-namespace SpacetimeDB {
-
-class SpacetimeDBClient {
- public event Action onSubscriptionApplied;
-}
-
-}
-```
-
-Register a delegate to be invoked when a subscription is registered with the database.
-
-```cs
-using SpacetimeDB;
-
-void OnSubscriptionApplied()
-{
- Console.WriteLine("Now listening on queries.");
-}
-
-void Main()
-{
- // ...initialize...
- SpacetimeDBClient.instance.onSubscriptionApplied += OnSubscriptionApplied;
-}
-```
-
-## View rows of subscribed tables
-
-The SDK maintains a local view of the database called the "client cache". This cache contains whatever rows are selected via a call to [`SpacetimeDBClient.Subscribe`](#method-spacetimedbclientsubscribe). These rows are represented in the SpacetimeDB .Net SDK as instances of [`SpacetimeDB.Types.{TABLE}`](#class-table).
-
-ONLY the rows selected in a [`SpacetimeDBClient.Subscribe`](#method-spacetimedbclientsubscribe) call will be available in the client cache. All operations in the client sdk operate on these rows exclusively, and have no information about the state of the rest of the database.
-
-In particular, SpacetimeDB does not support foreign key constraints. This means that if you are using a column as a foreign key, SpacetimeDB will not automatically bring in all of the rows that key might reference. You will need to manually subscribe to all tables you need information from.
-
-To optimize network performance, prefer selecting as few rows as possible in your [`Subscribe`](#method-spacetimedbclientsubscribe) query. Processes that need to view the entire state of the database are better run inside the database -- that is, inside modules.
-
-### Class `{TABLE}`
-
-For each table defined by a module, `spacetime generate` will generate a class [`SpacetimeDB.Types.{TABLE}`](#class-table) whose name is that table's name converted to `PascalCase`. The generated class contains a property for each of the table's columns, whose names are the column names converted to `camelCase`. It also contains various static events and methods.
-
-Static Methods:
-
-- [`{TABLE}.Iter()`](#static-method-tableiter) iterates all subscribed rows in the client cache.
-- [`{TABLE}.FilterBy{COLUMN}(value)`](#static-method-tablefilterbycolumn) filters subscribed rows in the client cache by a column value.
-- [`{TABLE}.Count()`](#static-method-tablecount) counts the number of subscribed rows in the client cache.
-
-Static Events:
-
-- [`{TABLE}.OnInsert`](#static-event-tableoninsert) is called when a row is inserted into the client cache.
-- [`{TABLE}.OnBeforeDelete`](#static-event-tableonbeforedelete) is called when a row is about to be removed from the client cache.
-- If the table has a primary key attribute, [`{TABLE}.OnUpdate`](#static-event-tableonupdate) is called when a row is updated.
-- [`{TABLE}.OnDelete`](#static-event-tableondelete) is called while a row is being removed from the client cache. You should almost always use [`{TABLE}.OnBeforeDelete`](#static-event-tableonbeforedelete) instead.
-
-Note that it is not possible to directly insert into the database from the client SDK! All insertion validation should be performed inside serverside modules for security reasons. You can instead [invoke reducers](#observe-and-invoke-reducers), which run code inside the database that can insert rows for you.
-
-#### Static Method `{TABLE}.Iter`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- public static System.Collections.Generic.IEnumerable
Iter();
-}
-
-}
-```
-
-Iterate over all the subscribed rows in the table. This method is only available after [`SpacetimeDBClient.onSubscriptionApplied`](#event-spacetimedbclientonsubscriptionapplied) has occurred.
-
-When iterating over rows and filtering for those containing a particular column, [`TableType::filter`](#method-filter) will be more efficient, so prefer it when possible.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-SpacetimeDBClient.instance.onConnect += (string authToken, Identity identity) => {
- SpacetimeDBClient.instance.Subscribe(new List { "SELECT * FROM User" });
-};
-SpacetimeDBClient.instance.onSubscriptionApplied += () => {
- // Will print a line for each `User` row in the database.
- foreach (var user in User.Iter()) {
- Console.WriteLine($"User: {user.Name}");
- }
-};
-SpacetimeDBClient.instance.connect(/* ... */);
-```
-
-#### Static Method `{TABLE}.FilterBy{COLUMN}`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- // If the column has no #[unique] or #[primarykey] constraint
- public static System.Collections.Generic.IEnumerable
FilterBySender(COLUMNTYPE value);
-
- // If the column has a #[unique] or #[primarykey] constraint
- public static TABLE? FilterBySender(COLUMNTYPE value);
-}
-
-}
-```
-
-For each column of a table, `spacetime generate` generates a static method on the [table class](#class-table) to filter or seek subscribed rows where that column matches a requested value. These methods are named `filterBy{COLUMN}`, where `{COLUMN}` is the column name converted to `PascalCase`.
-
-The method's return type depends on the column's attributes:
-
-- For unique columns, including those annotated `#[unique]` and `#[primarykey]`, the `filterBy{COLUMN}` method returns a `{TABLE}?`, where `{TABLE}` is the [table class](#class-table).
-- For non-unique columns, the `filter_by` method returns an `IEnumerator<{TABLE}>`.
-
-#### Static Method `{TABLE}.Count`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- public static int Count();
-}
-
-}
-```
-
-Return the number of subscribed rows in the table, or 0 if there is no active connection.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-SpacetimeDBClient.instance.onConnect += (string authToken, Identity identity) => {
- SpacetimeDBClient.instance.Subscribe(new List { "SELECT * FROM User" });
-};
-SpacetimeDBClient.instance.onSubscriptionApplied += () => {
- Console.WriteLine($"There are {User.Count()} users in the database.");
-};
-SpacetimeDBClient.instance.connect(/* ... */);
-```
-
-#### Static Event `{TABLE}.OnInsert`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- public delegate void InsertEventHandler(
- TABLE insertedValue,
- ReducerEvent? dbEvent
- );
- public static event InsertEventHandler OnInsert;
-}
-
-}
-```
-
-Register a delegate for when a subscribed row is newly inserted into the database.
-
-The delegate takes two arguments:
-
-- A [`{TABLE}`](#class-table) instance with the data of the inserted row
-- A [`ReducerEvent?`], which contains the data of the reducer that inserted the row, or `null` if the row is being inserted while initializing a subscription.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-/* initialize, subscribe to table User... */
-
-User.OnInsert += (User user, ReducerEvent? reducerEvent) => {
- if (reducerEvent == null) {
- Console.WriteLine($"New user '{user.Name}' received during subscription update.");
- } else {
- Console.WriteLine($"New user '{user.Name}' inserted by reducer {reducerEvent.Reducer}.");
- }
-};
-```
-
-#### Static Event `{TABLE}.OnBeforeDelete`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- public delegate void DeleteEventHandler(
- TABLE deletedValue,
- ReducerEvent dbEvent
- );
- public static event DeleteEventHandler OnBeforeDelete;
-}
-
-}
-```
-
-Register a delegate for when a subscribed row is about to be deleted from the database. If a reducer deletes many rows at once, this delegate will be invoked for each of those rows before any of them is deleted.
-
-The delegate takes two arguments:
-
-- A [`{TABLE}`](#class-table) instance with the data of the deleted row
-- A [`ReducerEvent`](#class-reducerevent), which contains the data of the reducer that deleted the row.
-
-This event should almost always be used instead of [`OnDelete`](#static-event-tableondelete). This is because often, many rows will be deleted at once, and `OnDelete` can be invoked in an arbitrary order on these rows. This means that data related to a row may already be missing when `OnDelete` is called. `OnBeforeDelete` does not have this problem.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-/* initialize, subscribe to table User... */
-
-User.OnBeforeDelete += (User user, ReducerEvent reducerEvent) => {
- Console.WriteLine($"User '{user.Name}' deleted by reducer {reducerEvent.Reducer}.");
-};
-```
-
-#### Static Event `{TABLE}.OnDelete`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- public delegate void DeleteEventHandler(
- TABLE deletedValue,
- SpacetimeDB.ReducerEvent dbEvent
- );
- public static event DeleteEventHandler OnDelete;
-}
-
-}
-```
-
-Register a delegate for when a subscribed row is being deleted from the database. If a reducer deletes many rows at once, this delegate will be invoked on those rows in arbitrary order, and data for some rows may already be missing when it is invoked. For this reason, prefer the event [`{TABLE}.OnBeforeDelete`](#static-event-tableonbeforedelete).
-
-The delegate takes two arguments:
-
-- A [`{TABLE}`](#class-table) instance with the data of the deleted row
-- A [`ReducerEvent`](#class-reducerevent), which contains the data of the reducer that deleted the row.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-/* initialize, subscribe to table User... */
-
-User.OnBeforeDelete += (User user, ReducerEvent reducerEvent) => {
- Console.WriteLine($"User '{user.Name}' deleted by reducer {reducerEvent.Reducer}.");
-};
-```
-
-#### Static Event `{TABLE}.OnUpdate`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class TABLE {
- public delegate void UpdateEventHandler(
- TABLE oldValue,
- TABLE newValue,
- ReducerEvent dbEvent
- );
- public static event UpdateEventHandler OnUpdate;
-}
-
-}
-```
-
-Register a delegate for when a subscribed row is being updated. This event is only available if the row has a column with the `#[primary_key]` attribute.
-
-The delegate takes three arguments:
-
-- A [`{TABLE}`](#class-table) instance with the old data of the updated row
-- A [`{TABLE}`](#class-table) instance with the new data of the updated row
-- A [`ReducerEvent`](#class-reducerevent), which contains the data of the reducer that updated the row.
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-
-/* initialize, subscribe to table User... */
-
-User.OnUpdate += (User oldUser, User newUser, ReducerEvent reducerEvent) => {
- Debug.Assert(oldUser.UserId == newUser.UserId, "Primary key never changes in an update");
-
- Console.WriteLine($"User with ID {oldUser.UserId} had name changed "+
- $"from '{oldUser.Name}' to '{newUser.Name}' by reducer {reducerEvent.Reducer}.");
-};
-```
-
-## Observe and invoke reducers
-
-"Reducer" is SpacetimeDB's name for the stored procedures that run in modules inside the database. You can invoke reducers from a connected client SDK, and also receive information about which reducers are running.
-
-`spacetime generate` generates a class [`SpacetimeDB.Types.Reducer`](#class-reducer) that contains methods and events for each reducer defined in a module. To invoke a reducer, use the method [`Reducer.{REDUCER}`](#static-method-reducerreducer) generated for it. To receive a callback each time a reducer is invoked, use the static event [`Reducer.On{REDUCER}`](#static-event-reduceronreducer).
-
-### Class `Reducer`
-
-```cs
-namespace SpacetimeDB.Types {
-
-class Reducer {}
-
-}
-```
-
-This class contains a static method and event for each reducer defined in a module.
-
-#### Static Method `Reducer.{REDUCER}`
-
-```cs
-namespace SpacetimeDB.Types {
-class Reducer {
-
-/* void {REDUCER_NAME}(...ARGS...) */
-
-}
-}
-```
-
-For each reducer defined by a module, `spacetime generate` generates a static method which sends a request to the database to invoke that reducer. The generated function's name is the reducer's name converted to `PascalCase`.
-
-Reducers don't run immediately! They run as soon as the request reaches the database. Don't assume data inserted by a reducer will be available immediately after you call this method.
-
-For reducers which accept a `ReducerContext` as their first argument, the `ReducerContext` is not included in the generated function's argument list.
-
-For example, if we define a reducer in Rust as follows:
-
-```rust
-#[spacetimedb(reducer)]
-pub fn set_name(
- ctx: ReducerContext,
- user_id: u64,
- name: String
-) -> Result<(), Error>;
-```
-
-The following C# static method will be generated:
-
-```cs
-namespace SpacetimeDB.Types {
-class Reducer {
-
-public static void SendMessage(UInt64 userId, string name);
-
-}
-}
-```
-
-#### Static Event `Reducer.On{REDUCER}`
-
-```cs
-namespace SpacetimeDB.Types {
-class Reducer {
-
-public delegate void /*{REDUCER}*/Handler(ReducerEvent reducerEvent, /* {ARGS...} */);
-
-public static event /*{REDUCER}*/Handler On/*{REDUCER}*/Event;
-
-}
-}
-```
-
-For each reducer defined by a module, `spacetime generate` generates an event to run each time the reducer is invoked. The generated functions are named `on{REDUCER}Event`, where `{REDUCER}` is the reducer's name converted to `PascalCase`.
-
-The first argument to the event handler is an instance of [`SpacetimeDB.Types.ReducerEvent`](#class-reducerevent) describing the invocation -- its timestamp, arguments, and whether it succeeded or failed. The remaining arguments are the arguments passed to the reducer. Reducers cannot have return values, so no return value information is included.
-
-For example, if we define a reducer in Rust as follows:
-
-```rust
-#[spacetimedb(reducer)]
-pub fn set_name(
- ctx: ReducerContext,
- user_id: u64,
- name: String
-) -> Result<(), Error>;
-```
-
-The following C# static method will be generated:
-
-```cs
-namespace SpacetimeDB.Types {
-class Reducer {
-
-public delegate void SetNameHandler(
- ReducerEvent reducerEvent,
- UInt64 userId,
- string name
-);
-public static event SetNameHandler OnSetNameEvent;
-
-}
-}
-```
-
-Which can be used as follows:
-
-```cs
-/* initialize, wait for onSubscriptionApplied... */
-
-Reducer.SetNameHandler += (
- ReducerEvent reducerEvent,
- UInt64 userId,
- string name
-) => {
- if (reducerEvent.Status == ClientApi.Event.Types.Status.Committed) {
- Console.WriteLine($"User with id {userId} set name to {name}");
- } else if (reducerEvent.Status == ClientApi.Event.Types.Status.Failed) {
- Console.WriteLine(
- $"User with id {userId} failed to set name to {name}:"
- + reducerEvent.ErrMessage
- );
- } else if (reducerEvent.Status == ClientApi.Event.Types.Status.OutOfEnergy) {
- Console.WriteLine(
- $"User with id {userId} failed to set name to {name}:"
- + "Invoker ran out of energy"
- );
- }
-};
-Reducer.SetName(USER_ID, NAME);
-```
-
-### Class `ReducerEvent`
-
-`spacetime generate` defines an class `ReducerEvent` containing an enum `ReducerType` with a variant for each reducer defined by a module. The variant's name will be the reducer's name converted to `PascalCase`.
-
-For example, the example project shown in the Rust Module quickstart will generate the following (abridged) code.
-
-```cs
-namespace SpacetimeDB.Types {
-
-public enum ReducerType
-{
- /* A member for each reducer in the module, with names converted to PascalCase */
- None,
- SendMessage,
- SetName,
-}
-public partial class SendMessageArgsStruct
-{
- /* A member for each argument of the reducer SendMessage, with names converted to PascalCase. */
- public string Text;
-}
-public partial class SetNameArgsStruct
-{
- /* A member for each argument of the reducer SetName, with names converted to PascalCase. */
- public string Name;
-}
-public partial class ReducerEvent : ReducerEventBase {
- // Which reducer was invoked
- public ReducerType Reducer { get; }
- // If event.Reducer == ReducerType.SendMessage, the arguments
- // sent to the SendMessage reducer. Otherwise, accesses will
- // throw a runtime error.
- public SendMessageArgsStruct SendMessageArgs { get; }
- // If event.Reducer == ReducerType.SetName, the arguments
- // passed to the SetName reducer. Otherwise, accesses will
- // throw a runtime error.
- public SetNameArgsStruct SetNameArgs { get; }
-
- /* Additional information, present on any ReducerEvent */
- // The name of the reducer.
- public string ReducerName { get; }
- // The timestamp of the reducer invocation inside the database.
- public ulong Timestamp { get; }
- // The identity of the client that invoked the reducer.
- public SpacetimeDB.Identity Identity { get; }
- // Whether the reducer succeeded, failed, or ran out of energy.
- public ClientApi.Event.Types.Status Status { get; }
- // If event.Status == Status.Failed, the error message returned from inside the module.
- public string ErrMessage { get; }
-}
-
-}
-```
-
-#### Enum `Status`
-
-```cs
-namespace ClientApi {
-public sealed partial class Event {
-public static partial class Types {
-
-public enum Status {
- Committed = 0,
- Failed = 1,
- OutOfEnergy = 2,
-}
-
-}
-}
-}
-```
-
-An enum whose variants represent possible reducer completion statuses of a reducer invocation.
-
-##### Variant `Status.Committed`
-
-The reducer finished successfully, and its row changes were committed to the database.
-
-##### Variant `Status.Failed`
-
-The reducer failed, either by panicking or returning a `Err`.
-
-##### Variant `Status.OutOfEnergy`
-
-The reducer was canceled because the module owner had insufficient energy to allow it to run to completion.
-
-## Identity management
-
-### Class `AuthToken`
-
-The AuthToken helper class handles creating and saving SpacetimeDB identity tokens in the filesystem.
-
-#### Static Method `AuthToken.Init`
-
-```cs
-namespace SpacetimeDB {
-
-class AuthToken {
- public static void Init(
- string configFolder = ".spacetime_csharp_sdk",
- string configFile = "settings.ini",
- string? configRoot = null
- );
-}
-
-}
-```
-
-Creates a file `$"{configRoot}/{configFolder}/{configFile}"` to store tokens.
-If no arguments are passed, the default is `"%HOME%/.spacetime_csharp_sdk/settings.ini"`.
-
-| Argument | Type | Meaning |
-| -------------- | -------- | ---------------------------------------------------------------------------------- |
-| `configFolder` | `string` | The folder to store the config file in. Default is `"spacetime_csharp_sdk"`. |
-| `configFile` | `string` | The name of the config file. Default is `"settings.ini"`. |
-| `configRoot` | `string` | The root folder to store the config file in. Default is the user's home directory. |
-
-#### Static Property `AuthToken.Token`
-
-```cs
-namespace SpacetimeDB {
-
-class AuthToken {
- public static string? Token { get; }
-}
-
-}
-```
-
-The auth token stored on the filesystem, if one exists.
-
-#### Static Method `AuthToken.SaveToken`
-
-```cs
-namespace SpacetimeDB {
-
-class AuthToken {
- public static void SaveToken(string token);
-}
-
-}
-```
-
-Save a token to the filesystem.
-
-### Class `Identity`
-
-```cs
-namespace SpacetimeDB {
-
-public struct Identity : IEquatable
-{
- public byte[] Bytes { get; }
- public static Identity From(byte[] bytes);
- public bool Equals(Identity other);
- public static bool operator ==(Identity a, Identity b);
- public static bool operator !=(Identity a, Identity b);
-}
-
-}
-```
-
-A unique public identifier for a client connected to a database.
-
-Columns of type `Identity` inside a module will be represented in the C# SDK as properties of type `byte[]`. `Identity` is essentially just a wrapper around `byte[]`, and you can use the `Bytes` property to get a `byte[]` that can be used to filter tables and so on.
-
-## Customizing logging
-
-The SpacetimeDB C# SDK performs internal logging. Instances of [`ISpacetimeDBLogger`](#interface-ispacetimedblogger) can be passed to [`SpacetimeDBClient.CreateInstance`](#static-method-spacetimedbclientcreateinstance) to customize how SDK logs are delivered to your application.
-
-This is set up automatically for you if you use Unity-- adding a [`NetworkManager`](#class-networkmanager) component to your unity scene will automatically initialize the `SpacetimeDBClient` with a [`UnityDebugLogger`](#class-unitydebuglogger).
-
-Outside of unity, all you need to do is the following:
-
-```cs
-using SpacetimeDB;
-using SpacetimeDB.Types;
-SpacetimeDBClient.CreateInstance(new ConsoleLogger());
-```
-
-### Interface `ISpacetimeDBLogger`
-
-```cs
-namespace SpacetimeDB
-{
-
-public interface ISpacetimeDBLogger
-{
- void Log(string message);
- void LogError(string message);
- void LogWarning(string message);
- void LogException(Exception e);
-}
-
-}
-```
-
-This interface provides methods that are invoked when the SpacetimeDB C# SDK needs to log at various log levels. You can create custom implementations if needed to integrate with existing logging solutions.
-
-### Class `ConsoleLogger`
-
-```cs
-namespace SpacetimeDB {
-
-public class ConsoleLogger : ISpacetimeDBLogger {}
-
-}
-```
-
-An `ISpacetimeDBLogger` implementation for regular .NET applications, using `Console.Write` when logs are received.
-
-### Class `UnityDebugLogger`
-
-```cs
-namespace SpacetimeDB {
-
-public class UnityDebugLogger : ISpacetimeDBLogger {}
-
-}
-```
-
-An `ISpacetimeDBLogger` implementation for Unity, using the Unity `Debug.Log` api.
diff --git a/docs/Client SDK Languages/C#/_category.json b/docs/Client SDK Languages/C#/_category.json
deleted file mode 100644
index 60238f8e..00000000
--- a/docs/Client SDK Languages/C#/_category.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "title": "C#",
- "disabled": false,
- "index": "index.md"
-}
\ No newline at end of file
diff --git a/docs/Client SDK Languages/C#/index.md b/docs/Client SDK Languages/C#/index.md
deleted file mode 100644
index b64ca13d..00000000
--- a/docs/Client SDK Languages/C#/index.md
+++ /dev/null
@@ -1,425 +0,0 @@
-# C# Client SDK Quick Start
-
-In this guide we'll show you how to get up and running with a simple SpacetimDB app with a client written in C#.
-
-We'll implement a command-line client for the module created in our Rust or C# Module Quickstart guides. Make sure you follow one of these guides before you start on this one.
-
-## Project structure
-
-Enter the directory `quickstart-chat` you created in the [Rust Module Quickstart](/docs/server-languages/rust/rust-module-quickstart-guide) or [C# Module Quickstart](/docs/server-languages/csharp/csharp-module-quickstart-guide) guides:
-
-```bash
-cd quickstart-chat
-```
-
-Within it, create a new C# console application project called `client` using either Visual Studio or the .NET CLI:
-
-```bash
-dotnet new console -o client
-```
-
-Open the project in your IDE of choice.
-
-## Add the NuGet package for the C# SpacetimeDB SDK
-
-Add the `spacetimedbsdk` [NuGet package](https://www.nuget.org/packages/spacetimedbsdk) using Visual Studio NuGet package manager or via the .NET CLI
-
-```bash
-dotnet add package spacetimedbsdk
-```
-
-## Generate your module types
-
-The `spacetime` CLI's `generate` command will generate client-side interfaces for the tables, reducers and types defined in your server module.
-
-In your `quickstart-chat` directory, run:
-
-```bash
-mkdir -p client/module_bindings
-spacetime generate --lang csharp --out-dir client/module_bindings --project-path server
-```
-
-Take a look inside `client/module_bindings`. The CLI should have generated five files:
-
-```
-module_bindings
-├── Message.cs
-├── ReducerEvent.cs
-├── SendMessageReducer.cs
-├── SetNameReducer.cs
-└── User.cs
-```
-
-## Add imports to Program.cs
-
-Open `client/Program.cs` and add the following imports:
-
-```csharp
-using SpacetimeDB;
-using SpacetimeDB.Types;
-using System.Collections.Concurrent;
-```
-
-We will also need to create some global variables that will be explained when we use them later. Add the following to the top of `Program.cs`:
-
-```csharp
-// our local client SpacetimeDB identity
-Identity? local_identity = null;
-// declare a thread safe queue to store commands in format (command, args)
-ConcurrentQueue<(string,string)> input_queue = new ConcurrentQueue<(string, string)>();
-// declare a threadsafe cancel token to cancel the process loop
-CancellationTokenSource cancel_token = new CancellationTokenSource();
-```
-
-## Define Main function
-
-We'll work outside-in, first defining our `Main` function at a high level, then implementing each behavior it needs. We need `Main` to do several things:
-
-1. Initialize the AuthToken module, which loads and stores our authentication token to/from local storage.
-2. Create the SpacetimeDBClient instance.
-3. Register callbacks on any events we want to handle. These will print to standard output messages received from the database and updates about users' names and online statuses.
-4. Start our processing thread, which connects to the SpacetimeDB module, updates the SpacetimeDB client and processes commands that come in from the input loop running in the main thread.
-5. Start the input loop, which reads commands from standard input and sends them to the processing thread.
-6. When the input loop exits, stop the processing thread and wait for it to exit.
-
-```csharp
-void Main()
-{
- AuthToken.Init(".spacetime_csharp_quickstart");
-
- // create the client, pass in a logger to see debug messages
- SpacetimeDBClient.CreateInstance(new ConsoleLogger());
-
- RegisterCallbacks();
-
- // spawn a thread to call process updates and process commands
- var thread = new Thread(ProcessThread);
- thread.Start();
-
- InputLoop();
-
- // this signals the ProcessThread to stop
- cancel_token.Cancel();
- thread.Join();
-}
-```
-
-## Register callbacks
-
-We need to handle several sorts of events:
-
-1. `onConnect`: When we connect, we will call `Subscribe` to tell the module what tables we care about.
-2. `onIdentityReceived`: When we receive our credentials, we'll use the `AuthToken` module to save our token so that the next time we connect, we can re-authenticate as the same user.
-3. `onSubscriptionApplied`: When we get the onSubscriptionApplied callback, that means our local client cache has been fully populated. At this time we'll print the user menu.
-4. `User.OnInsert`: When a new user joins, we'll print a message introducing them.
-5. `User.OnUpdate`: When a user is updated, we'll print their new name, or declare their new online status.
-6. `Message.OnInsert`: When we receive a new message, we'll print it.
-7. `Reducer.OnSetNameEvent`: If the server rejects our attempt to set our name, we'll print an error.
-8. `Reducer.OnSendMessageEvent`: If the server rejects a message we send, we'll print an error.
-
-```csharp
-void RegisterCallbacks()
-{
- SpacetimeDBClient.instance.onConnect += OnConnect;
- SpacetimeDBClient.instance.onIdentityReceived += OnIdentityReceived;
- SpacetimeDBClient.instance.onSubscriptionApplied += OnSubscriptionApplied;
-
- User.OnInsert += User_OnInsert;
- User.OnUpdate += User_OnUpdate;
-
- Message.OnInsert += Message_OnInsert;
-
- Reducer.OnSetNameEvent += Reducer_OnSetNameEvent;
- Reducer.OnSendMessageEvent += Reducer_OnSendMessageEvent;
-}
-```
-
-### Notify about new users
-
-For each table, we can register on-insert and on-delete callbacks to be run whenever a subscribed row is inserted or deleted. We register these callbacks using the `OnInsert` and `OnDelete` methods, which are automatically generated for each table by `spacetime generate`.
-
-These callbacks can fire in two contexts:
-
-- After a reducer runs, when the client's cache is updated about changes to subscribed rows.
-- After calling `subscribe`, when the client's cache is initialized with all existing matching rows.
-
-This second case means that, even though the module only ever inserts online users, the client's `User.OnInsert` callbacks may be invoked with users who are offline. We'll only notify about online users.
-
-`OnInsert` and `OnDelete` callbacks take two arguments: the altered row, and a `ReducerEvent`. This will be `null` for rows inserted when initializing the cache for a subscription. `ReducerEvent` is an enum autogenerated by `spacetime generate` with a variant for each reducer defined by the module. For now, we can ignore this argument.
-
-Whenever we want to print a user, if they have set a name, we'll use that. If they haven't set a name, we'll instead print the first 8 bytes of their identity, encoded as hexadecimal. We'll define a function `UserNameOrIdentity` to handle this.
-
-```csharp
-string UserNameOrIdentity(User user) => user.Name ?? Identity.From(user.Identity).ToString()!.Substring(0, 8);
-
-void User_OnInsert(User insertedValue, ReducerEvent? dbEvent)
-{
- if(insertedValue.Online)
- {
- Console.WriteLine($"{UserNameOrIdentity(insertedValue)} is online");
- }
-}
-```
-
-### Notify about updated users
-
-Because we declared a primary key column in our `User` table, we can also register on-update callbacks. These run whenever a row is replaced by a row with the same primary key, like our module's `User::update_by_identity` calls. We register these callbacks using the `OnUpdate` method, which is automatically implemented by `spacetime generate` for any table with a primary key column.
-
-`OnUpdate` callbacks take three arguments: the old row, the new row, and a `ReducerEvent`.
-
-In our module, users can be updated for three reasons:
-
-1. They've set their name using the `SetName` reducer.
-2. They're an existing user re-connecting, so their `Online` has been set to `true`.
-3. They've disconnected, so their `Online` has been set to `false`.
-
-We'll print an appropriate message in each of these cases.
-
-```csharp
-void User_OnUpdate(User oldValue, User newValue, ReducerEvent dbEvent)
-{
- if(oldValue.Name != newValue.Name)
- {
- Console.WriteLine($"{UserNameOrIdentity(oldValue)} renamed to {newValue.Name}");
- }
- if(oldValue.Online != newValue.Online)
- {
- if(newValue.Online)
- {
- Console.WriteLine($"{UserNameOrIdentity(newValue)} connected.");
- }
- else
- {
- Console.WriteLine($"{UserNameOrIdentity(newValue)} disconnected.");
- }
- }
-}
-```
-
-### Print messages
-
-When we receive a new message, we'll print it to standard output, along with the name of the user who sent it. Keep in mind that we only want to do this for new messages, i.e. those inserted by a `SendMessage` reducer invocation. We have to handle the backlog we receive when our subscription is initialized separately, to ensure they're printed in the correct order. To that effect, our `OnInsert` callback will check if its `ReducerEvent` argument is not `null`, and only print in that case.
-
-To find the `User` based on the message's `Sender` identity, we'll use `User::FilterByIdentity`, which behaves like the same function on the server. The key difference is that, unlike on the module side, the client's `FilterByIdentity` accepts a `byte[]`, rather than an `Identity`. The `Sender` identity stored in the message is also a `byte[]`, not an `Identity`, so we can just pass it to the filter method.
-
-We'll print the user's name or identity in the same way as we did when notifying about `User` table events, but here we have to handle the case where we don't find a matching `User` row. This can happen when the module owner sends a message using the CLI's `spacetime call`. In this case, we'll print `unknown`.
-
-```csharp
-void PrintMessage(Message message)
-{
- var sender = User.FilterByIdentity(message.Sender);
- var senderName = "unknown";
- if(sender != null)
- {
- senderName = UserNameOrIdentity(sender);
- }
-
- Console.WriteLine($"{senderName}: {message.Text}");
-}
-
-void Message_OnInsert(Message insertedValue, ReducerEvent? dbEvent)
-{
- if(dbEvent != null)
- {
- PrintMessage(insertedValue);
- }
-}
-```
-
-### 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`.
-
-Each reducer callback takes one fixed argument:
-
-The ReducerEvent that triggered the callback. It contains several fields. The ones we care about are:
-
-1. The `Identity` of the client that called the reducer.
-2. The `Status` of the reducer run, one of `Committed`, `Failed` or `OutOfEnergy`.
-3. The error message, if any, that the reducer returned.
-
-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:
-
-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.
-
-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'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.
-
-```csharp
-void Reducer_OnSetNameEvent(ReducerEvent reducerEvent, string name)
-{
- if(reducerEvent.Identity == local_identity && reducerEvent.Status == ClientApi.Event.Types.Status.Failed)
- {
- Console.Write($"Failed to change name to {name}");
- }
-}
-```
-
-### Warn if our message was rejected
-
-We handle warnings on rejected messages the same way as rejected names, though the types and the error message are different.
-
-```csharp
-void Reducer_OnSendMessageEvent(ReducerEvent reducerEvent, string text)
-{
- if (reducerEvent.Identity == local_identity && reducerEvent.Status == ClientApi.Event.Types.Status.Failed)
- {
- Console.Write($"Failed to send message {text}");
- }
-}
-```
-
-## Connect callback
-
-Once we are connected, we can send our subscription to the SpacetimeDB module. SpacetimeDB is set up so that each client subscribes via SQL queries to some subset of the database, and is notified about changes only to that subset. For complex apps with large databases, judicious subscriptions can save each client significant network bandwidth, memory and computation compared. For example, in [BitCraft](https://bitcraftonline.com), each player's client subscribes only to the entities in the "chunk" of the world where that player currently resides, rather than the entire game world. Our app is much simpler than BitCraft, so we'll just subscribe to the whole database.
-
-```csharp
-void OnConnect()
-{
- SpacetimeDBClient.instance.Subscribe(new List { "SELECT * FROM User", "SELECT * FROM Message" });
-}
-```
-
-## OnIdentityReceived callback
-
-This callback is executed when we receive our credentials from the SpacetimeDB module. We'll use the `AuthToken` module to save our token to local storage, so that we can re-authenticate as the same user the next time we connect. We'll also store the identity in a global variable `local_identity` so that we can use it to check if we are the sender of a message or name change.
-
-```csharp
-void OnIdentityReceived(string authToken, Identity identity)
-{
- local_identity = identity;
- AuthToken.SaveToken(authToken);
-}
-```
-
-## OnSubscriptionApplied callback
-
-Once our subscription is applied, we'll print all the previously sent messages. We'll define a function `PrintMessagesInOrder` to do this. `PrintMessagesInOrder` calls the automatically generated `Iter` function on our `Message` table, which returns an iterator over all rows in the table. We'll use the `OrderBy` method on the iterator to sort the messages by their `Sent` timestamp.
-
-```csharp
-void PrintMessagesInOrder()
-{
- foreach (Message message in Message.Iter().OrderBy(item => item.Sent))
- {
- PrintMessage(message);
- }
-}
-
-void OnSubscriptionApplied()
-{
- Console.WriteLine("Connected");
- PrintMessagesInOrder();
-}
-```
-
-
-
-## Process thread
-
-Since the input loop will be blocking, we'll run our processing code in a separate thread. This thread will:
-
-1. Connect to the module. We'll store the SpacetimeDB host name and our module name in constants `HOST` and `DB_NAME`. We will also store if SSL is enabled in a constant called `SSL_ENABLED`. This only needs to be `true` if we are using `SpacetimeDB Cloud`. Replace `` with the name you chose when publishing your module during the module quickstart.
-
-`Connect` takes an auth token, which is `null` for a new connection, or a stored string for a returning user. We are going to use the optional AuthToken module which uses local storage to store the auth token. If you want to use your own way to associate an auth token with a user, you can pass in your own auth token here.
-
-2. Loop until the thread is signaled to exit, calling `Update` on the SpacetimeDBClient to process any updates received from the module, and `ProcessCommand` to process any commands received from the input loop.
-
-3. Finally, Close the connection to the module.
-
-```csharp
-const string HOST = "localhost:3000";
-const string DBNAME = "chat";
-const bool SSL_ENABLED = false;
-
-void ProcessThread()
-{
- SpacetimeDBClient.instance.Connect(AuthToken.Token, HOST, DBNAME, SSL_ENABLED);
-
- // loop until cancellation token
- while (!cancel_token.IsCancellationRequested)
- {
- SpacetimeDBClient.instance.Update();
-
- ProcessCommands();
-
- Thread.Sleep(100);
- }
-
- SpacetimeDBClient.instance.Close();
-}
-```
-
-## Input loop and ProcessCommands
-
-The input loop will read commands from standard input and send them to the processing thread using the input queue. The `ProcessCommands` function is called every 100ms by the processing thread to process any pending commands.
-
-Supported Commands:
-
-1. Send a message: `message`, send the message to the module by calling `Reducer.SendMessage` which is automatically generated by `spacetime generate`.
-
-2. Set name: `name`, will send the new name to the module by calling `Reducer.SetName` which is automatically generated by `spacetime generate`.
-
-```csharp
-void InputLoop()
-{
- while (true)
- {
- var input = Console.ReadLine();
- if(input == null)
- {
- break;
- }
-
- if(input.StartsWith("/name "))
- {
- input_queue.Enqueue(("name", input.Substring(6)));
- continue;
- }
- else
- {
- input_queue.Enqueue(("message", input));
- }
- }
-}
-
-void ProcessCommands()
-{
- // process input queue commands
- while (input_queue.TryDequeue(out var command))
- {
- switch (command.Item1)
- {
- case "message":
- Reducer.SendMessage(command.Item2);
- break;
- case "name":
- Reducer.SetName(command.Item2);
- break;
- }
- }
-}
-```
-
-## Run the client
-
-Finally we just need to add a call to `Main` in `Program.cs`:
-
-```csharp
-Main();
-```
-
-Now we can run the client, by hitting start in Visual Studio or running the following command in the `client` directory:
-
-```bash
-dotnet run --project client
-```
-
-## What's next?
-
-Congratulations! You've built a simple chat app using SpacetimeDB. You can look at the C# SDK Reference for more information about the client SDK. If you are interested in developing in the Unity3d game engine, check out our Unity3d Comprehensive Tutorial and BitcraftMini game example.
diff --git a/docs/Client SDK Languages/Python/SDK Reference.md b/docs/Client SDK Languages/Python/SDK Reference.md
deleted file mode 100644
index 8cd4b4ca..00000000
--- a/docs/Client SDK Languages/Python/SDK Reference.md
+++ /dev/null
@@ -1,525 +0,0 @@
-# The SpacetimeDB Python client SDK
-
-The SpacetimeDB client SDK for Python contains all the tools you need to build native clients for SpacetimeDB modules using Python.
-
-## Install the SDK
-
-Use pip to install the SDK:
-
-```bash
-pip install spacetimedb-sdk
-```
-
-## 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 Python interface files using the Spacetime CLI. From your project directory, run:
-
-```bash
-mkdir -p module_bindings
-spacetime generate --lang python \
- --out-dir module_bindings \
- --project-path PATH-TO-MODULE-DIRECTORY
-```
-
-Replace `PATH-TO-MODULE-DIRECTORY` with the path to your SpacetimeDB module.
-
-Import your bindings in your client's code:
-
-```python
-import module_bindings
-```
-
-## Basic vs Async SpacetimeDB Client
-
-This SDK provides two different client modules for interacting with your SpacetimeDB module.
-
-The Basic client allows you to have control of the main loop of your application and you are responsible for regularly calling the client's `update` function. This is useful in settings like PyGame where you want to have full control of the main loop.
-
-The Async client has a run function that you call after you set up all your callbacks and it will take over the main loop and handle updating the client for you. With the async client, you can have a regular "tick" function by using the `schedule_event` function.
-
-## Common Client Reference
-
-The following functions and types are used in both the Basic and Async clients.
-
-### API at a glance
-
-| Definition | Description |
-| ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
-| Type [`Identity`](#type-identity) | A unique public identifier for a client. |
-| Type [`ReducerEvent`](#type-reducerevent) | `class` containing information about the reducer that triggered a row update event. |
-| Type [`module_bindings::{TABLE}`](#type-table) | Autogenerated `class` type for a table, holding one row. |
-| Method [`module_bindings::{TABLE}::filter_by_{COLUMN}`](#method-filter_by_column) | Autogenerated method to iterate over or seek subscribed rows where a column matches a value. |
-| Method [`module_bindings::{TABLE}::iter`](#method-iter) | Autogenerated method to iterate over all subscribed rows. |
-| Method [`module_bindings::{TABLE}::register_row_update`](#method-register_row_update) | Autogenerated method to register a callback that fires when a row changes. |
-| Function [`module_bindings::{REDUCER_NAME}::{REDUCER_NAME}`](#function-reducer) | Autogenerated function to invoke a reducer. |
-| Function [`module_bindings::{REDUCER_NAME}::register_on_{REDUCER_NAME}`](#function-register_on_reducer) | Autogenerated function to register a callback to run whenever the reducer is invoked. |
-
-### Type `Identity`
-
-```python
-class Identity:
- @staticmethod
- def from_string(string)
-
- @staticmethod
- def from_bytes(data)
-
- def __str__(self)
-
- def __eq__(self, other)
-```
-
-| Member | Args | Meaning |
-| ------------- | ---------- | ------------------------------------ |
-| `from_string` | `str` | Create an Identity from a hex string |
-| `from_bytes` | `bytes` | Create an Identity from raw bytes |
-| `__str__` | `None` | Convert the Identity to a hex string |
-| `__eq__` | `Identity` | Compare two Identities for equality |
-
-A unique public identifier for a client connected to a database.
-
-### Type `ReducerEvent`
-
-```python
-class ReducerEvent:
- def __init__(self, caller_identity, reducer_name, status, message, args):
- self.caller_identity = caller_identity
- self.reducer_name = reducer_name
- self.status = status
- self.message = message
- self.args = args
-```
-
-| Member | Args | Meaning |
-| ----------------- | ----------- | --------------------------------------------------------------------------- |
-| `caller_identity` | `Identity` | The identity of the user who invoked the reducer |
-| `reducer_name` | `str` | The name of the reducer that was invoked |
-| `status` | `str` | The status of the reducer invocation ("committed", "failed", "outofenergy") |
-| `message` | `str` | The message returned by the reducer if it fails |
-| `args` | `List[str]` | The arguments passed to the reducer |
-
-This class contains the information about a reducer event to be passed to row update callbacks.
-
-### Type `{TABLE}`
-
-```python
-class TABLE:
- is_table_class = True
-
- primary_key = "identity"
-
- @classmethod
- def register_row_update(cls, callback: Callable[[str,TABLE,TABLE,ReducerEvent], None])
-
- @classmethod
- def iter(cls) -> Iterator[User]
-
- @classmethod
- def filter_by_COLUMN_NAME(cls, COLUMN_VALUE) -> TABLE
-```
-
-This class is autogenerated for each table in your module. It contains methods for filtering and iterating over subscribed rows.
-
-### Method `filter_by_{COLUMN}`
-
-```python
-def filter_by_COLUMN(self, COLUMN_VALUE) -> TABLE
-```
-
-| Argument | Type | Meaning |
-| -------------- | ------------- | ---------------------- |
-| `column_value` | `COLUMN_TYPE` | The value to filter by |
-
-For each column of a table, `spacetime generate` generates a `classmethod` on the [table class](#type-table) to filter or seek subscribed rows where that column matches a requested value. These methods are named `filter_by_{COLUMN}`, where `{COLUMN}` is the column name converted to `snake_case`.
-
-The method's return type depends on the column's attributes:
-
-- For unique columns, including those annotated `#[unique]` and `#[primarykey]`, the `filter_by` method returns a `{TABLE}` or None, where `{TABLE}` is the [table struct](#type-table).
-- For non-unique columns, the `filter_by` method returns an `Iterator` that can be used in a `for` loop.
-
-### Method `iter`
-
-```python
-def iter(self) -> Iterator[TABLE]
-```
-
-Iterate over all the subscribed rows in the table.
-
-### Method `register_row_update`
-
-```python
-def register_row_update(self, callback: Callable[[str,TABLE,TABLE,ReducerEvent], None])
-```
-
-| Argument | Type | Meaning |
-| ---------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
-| `callback` | `Callable[[str,TABLE,TABLE,ReducerEvent]` | Callback to be invoked when a row is updated (Args: row_op, old_value, new_value, reducer_event) |
-
-Register a callback function to be executed when a row is updated. Callback arguments are:
-
-- `row_op`: The type of row update event. One of `"insert"`, `"delete"`, or `"update"`.
-- `old_value`: The previous value of the row, `None` if the row was inserted.
-- `new_value`: The new value of the row, `None` if the row was deleted.
-- `reducer_event`: The [`ReducerEvent`](#type-reducerevent) that caused the row update, or `None` if the row was updated as a result of a subscription change.
-
-### Function `{REDUCER_NAME}`
-
-```python
-def {REDUCER_NAME}(arg1, arg2)
-```
-
-This function is autogenerated for each reducer in your module. It is used to invoke the reducer. The arguments match the arguments defined in the reducer's `#[reducer]` attribute.
-
-### Function `register_on_{REDUCER_NAME}`
-
-```python
-def register_on_{REDUCER_NAME}(callback: Callable[[Identity, str, str, ARG1_TYPE, ARG1_TYPE], None])
-```
-
-| Argument | Type | Meaning |
-| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
-| `callback` | `Callable[[Identity, str, str, ARG1_TYPE, ARG1_TYPE], None]` | Callback to be invoked when the reducer is invoked (Args: caller_identity, status, message, args) |
-
-Register a callback function to be executed when the reducer is invoked. Callback arguments are:
-
-- `caller_identity`: The identity of the user who invoked the reducer.
-- `status`: The status of the reducer invocation ("committed", "failed", "outofenergy").
-- `message`: The message returned by the reducer if it fails.
-- `args`: Variable number of arguments passed to the reducer.
-
-## Async Client Reference
-
-### API at a glance
-
-| Definition | Description |
-| ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
-| Function [`SpacetimeDBAsyncClient::run`](#function-run) | Run the client. This function will not return until the client is closed. |
-| Function [`SpacetimeDBAsyncClient::subscribe`](#function-subscribe) | Subscribe to receive data and transaction updates for the provided queries. |
-| Function [`SpacetimeDBAsyncClient::register_on_subscription_applied`](#function-register_on_subscription_applied) | Register a callback when the local cache is updated as a result of a change to the subscription queries. |
-| Function [`SpacetimeDBAsyncClient::force_close`](#function-force_close) | Signal the client to stop processing events and close the connection to the server. |
-| Function [`SpacetimeDBAsyncClient::schedule_event`](#function-schedule_event) | Schedule an event to be fired after a delay |
-
-### Function `run`
-
-```python
-async def run(
- self,
- auth_token,
- host,
- address_or_name,
- ssl_enabled,
- on_connect,
- subscription_queries=[],
- )
-```
-
-Run the client. This function will not return until the client is closed.
-
-| Argument | Type | Meaning |
-| ---------------------- | --------------------------------- | -------------------------------------------------------------- |
-| `auth_token` | `str` | Auth token to authenticate the user. (None if new user) |
-| `host` | `str` | Hostname of SpacetimeDB server |
-| `address_or_name` | `&str` | Name or address of the module. |
-| `ssl_enabled` | `bool` | Whether to use SSL when connecting to the server. |
-| `on_connect` | `Callable[[str, Identity], None]` | Callback to be invoked when the client connects to the server. |
-| `subscription_queries` | `List[str]` | List of queries to subscribe to. |
-
-If `auth_token` is not None, they will be passed to the new connection to identify and authenticate the user. Otherwise, a new Identity and auth token will be generated by the server. An optional [local_config](#local_config) module can be used to store the user's auth token to local storage.
-
-If you are connecting to SpacetimeDB Cloud `testnet` the host should be `testnet.spacetimedb.com` and `ssl_enabled` should be `True`. If you are connecting to SpacetimeDB Standalone locally, the host should be `localhost:3000` and `ssl_enabled` should be `False`. For instructions on how to deploy to these environments, see the [Deployment Section](/docs/DeploymentOverview.md)
-
-```python
-asyncio.run(
- spacetime_client.run(
- AUTH_TOKEN,
- "localhost:3000",
- "my-module-name",
- False,
- on_connect,
- ["SELECT * FROM User", "SELECT * FROM Message"],
- )
-)
-```
-
-### Function `subscribe`
-
-```rust
-def subscribe(self, queries: List[str])
-```
-
-Subscribe to a set of queries, to be notified when rows which match those queries are altered.
-
-| Argument | Type | Meaning |
-| --------- | ----------- | ---------------------------- |
-| `queries` | `List[str]` | SQL queries to subscribe to. |
-
-The `queries` should be a slice of strings representing SQL queries.
-
-A new call to `subscribe` will remove all previous subscriptions and replace them with the new `queries`. If any rows matched the previous subscribed queries but do not match the new queries, those rows will be removed from the client cache. Row update events will be dispatched for any inserts and deletes that occur as a result of the new queries. For these events, the [`ReducerEvent`](#type-reducerevent) argument will be `None`.
-
-This should be called before the async client is started with [`run`](#function-run).
-
-```python
-spacetime_client.subscribe(["SELECT * FROM User;", "SELECT * FROM Message;"])
-```
-
-Subscribe to a set of queries, to be notified when rows which match those queries are altered.
-
-### Function `register_on_subscription_applied`
-
-```python
-def register_on_subscription_applied(self, callback)
-```
-
-Register a callback function to be executed when the local cache is updated as a result of a change to the subscription queries.
-
-| Argument | Type | Meaning |
-| ---------- | -------------------- | ------------------------------------------------------ |
-| `callback` | `Callable[[], None]` | Callback to be invoked when subscriptions are applied. |
-
-The callback will be invoked after a successful [`subscribe`](#function-subscribe) call when the initial set of matching rows becomes available.
-
-```python
-spacetime_client.register_on_subscription_applied(on_subscription_applied)
-```
-
-### Function `force_close`
-
-```python
-def force_close(self)
-)
-```
-
-Signal the client to stop processing events and close the connection to the server.
-
-```python
-spacetime_client.force_close()
-```
-
-### Function `schedule_event`
-
-```python
-def schedule_event(self, delay_secs, callback, *args)
-```
-
-Schedule an event to be fired after a delay
-
-To create a repeating event, call schedule_event() again from within the callback function.
-
-| Argument | Type | Meaning |
-| ------------ | -------------------- | -------------------------------------------------------------- |
-| `delay_secs` | `float` | number of seconds to wait before firing the event |
-| `callback` | `Callable[[], None]` | Callback to be invoked when the event fires. |
-| `args` | `*args` | Variable number of arguments to pass to the callback function. |
-
-```python
-def application_tick():
- # ... do some work
-
- spacetime_client.schedule_event(0.1, application_tick)
-
-spacetime_client.schedule_event(0.1, application_tick)
-```
-
-## Basic Client Reference
-
-### API at a glance
-
-| Definition | Description |
-| ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
-| Function [`SpacetimeDBClient::init`](#function-init) | Create a network manager instance. |
-| Function [`SpacetimeDBClient::subscribe`](#function-subscribe) | Subscribe to receive data and transaction updates for the provided queries. |
-| Function [`SpacetimeDBClient::register_on_event`](#function-register_on_event) | Register a callback function to handle transaction update events. |
-| Function [`SpacetimeDBClient::unregister_on_event`](#function-unregister_on_event) | Unregister a callback function that was previously registered using `register_on_event`. |
-| Function [`SpacetimeDBClient::register_on_subscription_applied`](#function-register_on_subscription_applied) | Register a callback function to be executed when the local cache is updated as a result of a change to the subscription queries. |
-| Function [`SpacetimeDBClient::unregister_on_subscription_applied`](#function-unregister_on_subscription_applied) | Unregister a callback function from the subscription update event. |
-| Function [`SpacetimeDBClient::update`](#function-update) | Process all pending incoming messages from the SpacetimeDB module. |
-| Function [`SpacetimeDBClient::close`](#function-close) | Close the WebSocket connection. |
-| Type [`TransactionUpdateMessage`](#type-transactionupdatemessage) | Represents a transaction update message. |
-
-### Function `init`
-
-```python
-@classmethod
-def init(
- auth_token: str,
- host: str,
- address_or_name: str,
- ssl_enabled: bool,
- autogen_package: module,
- on_connect: Callable[[], NoneType] = None,
- on_disconnect: Callable[[str], NoneType] = None,
- on_identity: Callable[[str, Identity], NoneType] = None,
- on_error: Callable[[str], NoneType] = None
-)
-```
-
-Create a network manager instance.
-
-| Argument | Type | Meaning |
-| ----------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
-| `auth_token` | `str` | This is the token generated by SpacetimeDB that matches the user's identity. If None, token will be generated |
-| `host` | `str` | Hostname:port for SpacetimeDB connection |
-| `address_or_name` | `str` | The name or address of the database to connect to |
-| `ssl_enabled` | `bool` | Whether to use SSL when connecting to the server. |
-| `autogen_package` | `ModuleType` | Python package where SpacetimeDB module generated files are located. |
-| `on_connect` | `Callable[[], None]` | Optional callback called when a connection is made to the SpacetimeDB module. |
-| `on_disconnect` | `Callable[[str], None]` | Optional callback called when the Python client is disconnected from the SpacetimeDB module. The argument is the close message. |
-| `on_identity` | `Callable[[str, Identity], None]` | Called when the user identity is recieved from SpacetimeDB. First argument is the auth token used to login in future sessions. |
-| `on_error` | `Callable[[str], None]` | Optional callback called when the Python client connection encounters an error. The argument is the error message. |
-
-This function creates a new SpacetimeDBClient instance. It should be called before any other functions in the SpacetimeDBClient class. This init will call connect for you.
-
-```python
-SpacetimeDBClient.init(autogen, on_connect=self.on_connect)
-```
-
-### Function `subscribe`
-
-```python
-def subscribe(queries: List[str])
-```
-
-Subscribe to receive data and transaction updates for the provided queries.
-
-| Argument | Type | Meaning |
-| --------- | ----------- | -------------------------------------------------------------------------------------------------------- |
-| `queries` | `List[str]` | A list of queries to subscribe to. Each query is a string representing an sql formatted query statement. |
-
-This function sends a subscription request to the SpacetimeDB module, indicating that the client wants to receive data and transaction updates related to the specified queries.
-
-```python
-queries = ["SELECT * FROM table1", "SELECT * FROM table2 WHERE col2 = 0"]
-SpacetimeDBClient.instance.subscribe(queries)
-```
-
-### Function `register_on_event`
-
-```python
-def register_on_event(callback: Callable[[TransactionUpdateMessage], NoneType])
-```
-
-Register a callback function to handle transaction update events.
-
-| Argument | Type | Meaning |
-| ---------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `callback` | `Callable[[TransactionUpdateMessage], None]` | A callback function that takes a single argument of type `TransactionUpdateMessage`. This function will be invoked with a `TransactionUpdateMessage` instance containing information about the transaction update event. |
-
-This function registers a callback function that will be called when a reducer modifies a table matching any of the subscribed queries or if a reducer called by this Python client encounters a failure.
-
-```python
-def handle_event(transaction_update):
- # Code to handle the transaction update event
-
-SpacetimeDBClient.instance.register_on_event(handle_event)
-```
-
-### Function `unregister_on_event`
-
-```python
-def unregister_on_event(callback: Callable[[TransactionUpdateMessage], NoneType])
-```
-
-Unregister a callback function that was previously registered using `register_on_event`.
-
-| Argument | Type | Meaning |
-| ---------- | -------------------------------------------- | ------------------------------------ |
-| `callback` | `Callable[[TransactionUpdateMessage], None]` | The callback function to unregister. |
-
-```python
-SpacetimeDBClient.instance.unregister_on_event(handle_event)
-```
-
-### Function `register_on_subscription_applied`
-
-```python
-def register_on_subscription_applied(callback: Callable[[], NoneType])
-```
-
-Register a callback function to be executed when the local cache is updated as a result of a change to the subscription queries.
-
-| Argument | Type | Meaning |
-| ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `callback` | `Callable[[], None]` | A callback function that will be invoked on each subscription update. The callback function should not accept any arguments and should not return any value. |
-
-```python
-def subscription_callback():
- # Code to be executed on each subscription update
-
-SpacetimeDBClient.instance.register_on_subscription_applied(subscription_callback)
-```
-
-### Function `unregister_on_subscription_applied`
-
-```python
-def unregister_on_subscription_applied(callback: Callable[[], NoneType])
-```
-
-Unregister a callback function from the subscription update event.
-
-| Argument | Type | Meaning |
-| ---------- | -------------------- | -------------------------------------------------------------------------------------------------------- |
-| `callback` | `Callable[[], None]` | A callback function that was previously registered with the `register_on_subscription_applied` function. |
-
-```python
-def subscription_callback():
- # Code to be executed on each subscription update
-
-SpacetimeDBClient.instance.register_on_subscription_applied(subscription_callback)
-```
-
-### Function `update`
-
-```python
-def update()
-```
-
-Process all pending incoming messages from the SpacetimeDB module.
-
-This function must be called on a regular interval in the main loop to process incoming messages.
-
-```python
-while True:
- SpacetimeDBClient.instance.update() # Call the update function in a loop to process incoming messages
- # Additional logic or code can be added here
-```
-
-### Function `close`
-
-```python
-def close()
-```
-
-Close the WebSocket connection.
-
-This function closes the WebSocket connection to the SpacetimeDB module.
-
-```python
-SpacetimeDBClient.instance.close()
-```
-
-### Type `TransactionUpdateMessage`
-
-```python
-class TransactionUpdateMessage:
- def __init__(
- self,
- caller_identity: Identity,
- status: str,
- message: str,
- reducer_name: str,
- args: Dict
- )
-```
-
-| Member | Args | Meaning |
-| ----------------- | ---------- | ------------------------------------------------- |
-| `caller_identity` | `Identity` | The identity of the caller. |
-| `status` | `str` | The status of the transaction. |
-| `message` | `str` | A message associated with the transaction update. |
-| `reducer_name` | `str` | The reducer used for the transaction. |
-| `args` | `Dict` | Additional arguments for the transaction. |
-
-Represents a transaction update message. Used in on_event callbacks.
-
-For more details, see [`register_on_event`](#function-register_on_event).
diff --git a/docs/Client SDK Languages/Python/_category.json b/docs/Client SDK Languages/Python/_category.json
deleted file mode 100644
index 4e08cfa1..00000000
--- a/docs/Client SDK Languages/Python/_category.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "title": "Python",
- "disabled": false,
- "index": "index.md"
-}
\ No newline at end of file
diff --git a/docs/Client SDK Languages/Python/index.md b/docs/Client SDK Languages/Python/index.md
deleted file mode 100644
index 52630452..00000000
--- a/docs/Client SDK Languages/Python/index.md
+++ /dev/null
@@ -1,377 +0,0 @@
-# Python Client SDK Quick Start
-
-In this guide, we'll show you how to get up and running with a simple SpacetimDB app with a client written in Python.
-
-We'll implement a command-line client for the module created in our [Rust Module Quickstart](/docs/languages/rust/rust-module-quickstart-guide) or [C# Module Quickstart](/docs/languages/csharp/csharp-module-reference) guides. Make sure you follow one of these guides before you start on this one.
-
-## Install the SpacetimeDB SDK Python Package
-
-1. Run pip install
-
-```bash
-pip install spacetimedb_sdk
-```
-
-## Project structure
-
-Enter the directory `quickstart-chat` you created in the Rust or C# Module Quickstart guides and create a `client` folder:
-
-```bash
-cd quickstart-chat
-mkdir client
-```
-
-## Create the Python main file
-
-Create a file called `main.py` in the `client` and open it in your favorite editor. We prefer [VS Code](https://code.visualstudio.com/).
-
-## Add imports
-
-We need to add several imports for this quickstart:
-
-- [`asyncio`](https://docs.python.org/3/library/asyncio.html) is required to run the async code in the SDK.
-- [`multiprocessing.Queue`](https://docs.python.org/3/library/multiprocessing.html) allows us to pass our input to the async code, which we will run in a separate thread.
-- [`threading`](https://docs.python.org/3/library/threading.html) allows us to spawn our async code in a separate thread so the main thread can run the input loop.
-
-- `spacetimedb_sdk.spacetimedb_async_client.SpacetimeDBAsyncClient` is the async wrapper around the SpacetimeDB client which we use to interact with our SpacetimeDB module.
-- `spacetimedb_sdk.local_config` is an optional helper module to load the auth token from local storage.
-
-```python
-import asyncio
-from multiprocessing import Queue
-import threading
-
-from spacetimedb_sdk.spacetimedb_async_client import SpacetimeDBAsyncClient
-import spacetimedb_sdk.local_config as local_config
-```
-
-## Generate your module types
-
-The `spacetime` CLI's `generate` command will generate client-side interfaces for the tables, reducers and types defined in your server module.
-
-In your `client` directory, run:
-
-```bash
-mkdir -p module_bindings
-spacetime generate --lang python --out-dir src/module_bindings --project_path ../server
-```
-
-Take a look inside `client/module_bindings`. The CLI should have generated five files:
-
-```
-module_bindings
-+-- message.py
-+-- send_message_reducer.py
-+-- set_name_reducer.py
-+-- user.py
-```
-
-Now we import these types by adding the following lines to `main.py`:
-
-```python
-import module_bindings
-from module_bindings.user import User
-from module_bindings.message import Message
-import module_bindings.send_message_reducer as send_message_reducer
-import module_bindings.set_name_reducer as set_name_reducer
-```
-
-## Global variables
-
-Next we will add our global `input_queue` and `local_identity` variables which we will explain later when they are used.
-
-```python
-input_queue = Queue()
-local_identity = None
-```
-
-## Define main function
-
-We'll work outside-in, first defining our `main` function at a high level, then implementing each behavior it needs. We need `main` to do four things:
-
-1. Init the optional local config module. The first parameter is the directory name to be created in the user home directory.
-1. Create our async SpacetimeDB client.
-1. Register our callbacks.
-1. Start the async client in a thread.
-1. Run a loop to read user input and send it to a repeating event in the async client.
-1. When the user exits, stop the async client and exit the program.
-
-```python
-if __name__ == "__main__":
- local_config.init(".spacetimedb-python-quickstart")
-
- spacetime_client = SpacetimeDBAsyncClient(module_bindings)
-
- register_callbacks(spacetime_client)
-
- thread = threading.Thread(target=run_client, args=(spacetime_client,))
- thread.start()
-
- input_loop()
-
- spacetime_client.force_close()
- thread.join()
-```
-
-## Register callbacks
-
-We need to handle several sorts of events:
-
-1. OnSubscriptionApplied is a special callback that is executed when the local client cache is populated. We will talk more about this later.
-2. When a new user joins or a user is updated, we'll print an appropriate message.
-3. When we receive a new message, we'll print it.
-4. If the server rejects our attempt to set our name, we'll print an error.
-5. If the server rejects a message we send, we'll print an error.
-6. We use the `schedule_event` function to register a callback to be executed after 100ms. This callback will check the input queue for any user input and execute the appropriate command.
-
-Because python requires functions to be defined before they're used, the following code must be added to `main.py` before main block:
-
-```python
-def register_callbacks(spacetime_client):
- spacetime_client.client.register_on_subscription_applied(on_subscription_applied)
-
- User.register_row_update(on_user_row_update)
- Message.register_row_update(on_message_row_update)
-
- set_name_reducer.register_on_set_name(on_set_name_reducer)
- send_message_reducer.register_on_send_message(on_send_message_reducer)
-
- spacetime_client.schedule_event(0.1, check_commands)
-```
-
-### Handling User row updates
-
-For each table, we can register a row update callback to be run whenever a subscribed row is inserted, updated or deleted. We register these callbacks using the `register_row_update` methods that are generated automatically for each table by `spacetime generate`.
-
-These callbacks can fire in two contexts:
-
-- After a reducer runs, when the client's cache is updated about changes to subscribed rows.
-- After calling `subscribe`, when the client's cache is initialized with all existing matching rows.
-
-This second case means that, even though the module only ever inserts online users, the client's `User::row_update` callbacks may be invoked with users who are offline. We'll only notify about online users.
-
-We are also going to check for updates to the user row. This can happen for three reasons:
-
-1. They've set their name using the `set_name` reducer.
-2. They're an existing user re-connecting, so their `online` has been set to `true`.
-3. They've disconnected, so their `online` has been set to `false`.
-
-We'll print an appropriate message in each of these cases.
-
-`row_update` callbacks take four arguments: the row operation ("insert", "update", or "delete"), the old row if it existed, the new or updated row, and a `ReducerEvent`. This will `None` for rows inserted when initializing the cache for a subscription. `ReducerEvent` is an class that contains information about the reducer that triggered this row update event.
-
-Whenever we want to print a user, if they have set a name, we'll use that. If they haven't set a name, we'll instead print the first 8 bytes of their identity, encoded as hexadecimal. We'll define a function `user_name_or_identity` handle this.
-
-Add these functions before the `register_callbacks` function:
-
-```python
-def user_name_or_identity(user):
- if user.name:
- return user.name
- else:
- return (str(user.identity))[:8]
-
-def on_user_row_update(row_op, user_old, user, reducer_event):
- if row_op == "insert":
- if user.online:
- print(f"User {user_name_or_identity(user)} connected.")
- elif row_op == "update":
- if user_old.online and not user.online:
- print(f"User {user_name_or_identity(user)} disconnected.")
- elif not user_old.online and user.online:
- print(f"User {user_name_or_identity(user)} connected.")
-
- if user_old.name != user.name:
- print(
- f"User {user_name_or_identity(user_old)} renamed to {user_name_or_identity(user)}."
- )
-```
-
-### Print messages
-
-When we receive a new message, we'll print it to standard output, along with the name of the user who sent it. Keep in mind that we only want to do this for new messages, i.e. those inserted by a `send_message` reducer invocation. We have to handle the backlog we receive when our subscription is initialized separately, to ensure they're printed in the correct order. To that effect, our `on_message_row_update` callback will check if its `reducer_event` argument is not `None`, and only print in that case.
-
-To find the `User` based on the message's `sender` identity, we'll use `User::filter_by_identity`, which behaves like the same function on the server. The key difference is that, unlike on the module side, the client's `filter_by_identity` accepts a `bytes`, rather than an `&Identity`. The `sender` identity stored in the message is also a `bytes`, not an `Identity`, so we can just pass it to the filter method.
-
-We'll print the user's name or identity in the same way as we did when notifying about `User` table events, but here we have to handle the case where we don't find a matching `User` row. This can happen when the module owner sends a message using the CLI's `spacetime call`. In this case, we'll print `unknown`.
-
-Add these functions before the `register_callbacks` function:
-
-```python
-def on_message_row_update(row_op, message_old, message, reducer_event):
- if reducer_event is not None and row_op == "insert":
- print_message(message)
-
-def print_message(message):
- user = User.filter_by_identity(message.sender)
- user_name = "unknown"
- if user is not None:
- user_name = user_name_or_identity(user)
-
- print(f"{user_name}: {message.text}")
-```
-
-### 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 `register_on_` method, which is automatically implemented for each reducer by `spacetime generate`.
-
-Each reducer callback takes three fixed arguments:
-
-1. The `Identity` of the client who requested the reducer invocation.
-2. The `Status` of the reducer run, one of `committed`, `failed` or `outofenergy`.
-3. The `Message` returned by the reducer in error cases, or `None` if the reducer succeeded.
-
-It also takes a variable number of arguments which match the calling arguments of the reducer.
-
-These callbacks will be invoked in one of two cases:
-
-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.
-
-We already handle successful `set_name` invocations using our `User::on_update` 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 `on_set_name_reducer` as a callback which 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.
-
-Add this function before the `register_callbacks` function:
-
-```python
-def on_set_name_reducer(sender, status, message, name):
- if sender == local_identity:
- if status == "failed":
- print(f"Failed to set name: {message}")
-```
-
-### Warn if our message was rejected
-
-We handle warnings on rejected messages the same way as rejected names, though the types and the error message are different.
-
-Add this function before the `register_callbacks` function:
-
-```python
-def on_send_message_reducer(sender, status, message, msg):
- if sender == local_identity:
- if status == "failed":
- print(f"Failed to send message: {message}")
-```
-
-### OnSubscriptionApplied callback
-
-This callback fires after the client cache is updated as a result in a change to the client subscription. This happens after connect and if after calling `subscribe` to modify the subscription.
-
-In this case, we want to print all the existing messages when the subscription is applied. `print_messages_in_order` iterates over all the `Message`s we've received, sorts them, and then prints them. `Message.iter()` is generated for all table types, and returns an iterator over all the messages in the client's cache.
-
-Add these functions before the `register_callbacks` function:
-
-```python
-def print_messages_in_order():
- all_messages = sorted(Message.iter(), key=lambda x: x.sent)
- for entry in all_messages:
- print(f"{user_name_or_identity(User.filter_by_identity(entry.sender))}: {entry.text}")
-
-def on_subscription_applied():
- print(f"\nSYSTEM: Connected.")
- print_messages_in_order()
-```
-
-### Check commands repeating event
-
-We'll use a repeating event to check the user input queue every 100ms. If there's a command in the queue, we'll execute it. If not, we'll just keep waiting. Notice that at the end of the function we call `schedule_event` again to so the event will repeat.
-
-If the command is to send a message, we'll call the `send_message` reducer. If the command is to set our name, we'll call the `set_name` reducer.
-
-Add these functions before the `register_callbacks` function:
-
-```python
-def check_commands():
- global input_queue
-
- if not input_queue.empty():
- choice = input_queue.get()
- if choice[0] == "name":
- set_name_reducer.set_name(choice[1])
- else:
- send_message_reducer.send_message(choice[1])
-
- spacetime_client.schedule_event(0.1, check_commands)
-```
-
-### OnConnect callback
-
-This callback fires after the client connects to the server. We'll use it to save our credentials to a file so that we can re-authenticate as the same user next time we connect.
-
-The `on_connect` callback takes two arguments:
-
-1. The `Auth Token` is the equivalent of your private key. This is the only way to authenticate with the SpacetimeDB module as this user.
-2. The `Identity` is the equivalent of your public key. This is used to uniquely identify this user and will be sent to other clients. We store this in a global variable so we can use it to identify that a given message or transaction was sent by us.
-
-To store our auth token, we use the optional component `local_config`, which provides a simple interface for storing and retrieving a single `Identity` from a file. We'll use the `local_config::set_string` method to store the auth token. Other projects might want to associate this token with some other identifier such as an email address or Steam ID.
-
-The `on_connect` callback is passed to the client connect function so it just needs to be defined before the `run_client` described next.
-
-```python
-def on_connect(auth_token, identity):
- global local_identity
- local_identity = identity
-
- local_config.set_string("auth_token", auth_token)
-```
-
-## Async client thread
-
-We are going to write a function that starts the async client, which will be executed on a separate thread.
-
-```python
-def run_client(spacetime_client):
- asyncio.run(
- spacetime_client.run(
- local_config.get_string("auth_token"),
- "localhost:3000",
- "chat",
- False,
- on_connect,
- ["SELECT * FROM User", "SELECT * FROM Message"],
- )
- )
-```
-
-## Input loop
-
-Finally, we need a function to be executed on the main loop which listens for user input and adds it to the queue.
-
-```python
-def input_loop():
- global input_queue
-
- while True:
- user_input = input()
- if len(user_input) == 0:
- return
- elif user_input.startswith("/name "):
- input_queue.put(("name", user_input[6:]))
- else:
- input_queue.put(("message", user_input))
-```
-
-## Run the client
-
-Make sure your module from the Rust or C# module quickstart is published. If you used a different module name than `chat`, you will need to update the `connect` call in the `run_client` function.
-
-Run the client:
-
-```bash
-python main.py
-```
-
-If you want to connect another client, you can use the --client command line option, which is built into the local_config module. This will create different settings file for the new client's auth token.
-
-```bash
-python main.py --client 2
-```
-
-## Next steps
-
-Congratulations! You've built a simple chat app with a Python client. You can now use this as a starting point for your own SpacetimeDB apps.
-
-For a more complex example of the Spacetime Python SDK, check out our [AI Agent](https://github.com/clockworklabs/spacetime-mud/tree/main/ai-agent-python-client) for the [Spacetime Multi-User Dungeon](https://github.com/clockworklabs/spacetime-mud). The AI Agent uses the OpenAI API to create dynamic content on command.
diff --git a/docs/Client SDK Languages/Rust/SDK Reference.md b/docs/Client SDK Languages/Rust/SDK Reference.md
deleted file mode 100644
index c61a06f3..00000000
--- a/docs/Client SDK Languages/Rust/SDK Reference.md
+++ /dev/null
@@ -1,1153 +0,0 @@
-# The SpacetimeDB Rust client SDK
-
-The SpacetimeDB client SDK for Rust contains all the tools you need to build native clients for SpacetimeDB modules using Rust.
-
-## Install the SDK
-
-First, create a new project using `cargo new` and add the SpacetimeDB SDK to your dependencies:
-
-```bash
-cargo add spacetimedb
-```
-
-## Generate module bindings
-
-Each SpacetimeDB client depends on some bindings specific to your module. Create a `module_bindings` directory in your project's `src` directory and generate the Rust interface files using the Spacetime CLI. From your project directory, run:
-
-```bash
-mkdir -p src/module_bindings
-spacetime generate --lang rust \
- --out-dir src/module_bindings \
- --project-path PATH-TO-MODULE-DIRECTORY
-```
-
-Replace `PATH-TO-MODULE-DIRECTORY` with the path to your SpacetimeDB module.
-
-Declare a `mod` for the bindings in your client's `src/main.rs`:
-
-```rust
-mod module_bindings;
-```
-
-## API at a glance
-
-| Definition | Description |
-| ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
-| Function [`module_bindings::connect`](#function-connect) | Autogenerated function to connect to a database. |
-| Function [`spacetimedb_sdk::disconnect`](#function-disconnect) | Close the active connection. |
-| Function [`spacetimedb_sdk::on_disconnect`](#function-on_disconnect) | Register a `FnMut` callback to run when a connection ends. |
-| Function [`spacetimedb_sdk::once_on_disconnect`](#function-once_on_disconnect) | Register a `FnOnce` callback to run the next time a connection ends. |
-| Function [`spacetimedb_sdk::remove_on_disconnect`](#function-remove_on_disconnect) | Cancel an `on_disconnect` or `once_on_disconnect` callback. |
-| Function [`spacetimedb_sdk::subscribe`](#function-subscribe) | Subscribe to queries with a `&[&str]`. |
-| Function [`spacetimedb_sdk::subscribe_owned`](#function-subscribe_owned) | Subscribe to queries with a `Vec`. |
-| Function [`spacetimedb_sdk::on_subscription_applied`](#function-on_subscription_applied) | Register a `FnMut` callback to run when a subscription's initial rows become available. |
-| Function [`spacetimedb_sdk::once_on_subscription_applied`](#function-once_on_subscription_applied) | Register a `FnOnce` callback to run the next time a subscription's initial rows become available. |
-| Function [`spacetimedb_sdk::remove_on_subscription_applied`](#function-remove_on_subscription_applied) | Cancel an `on_subscription_applied` or `once_on_subscription_applied` callback. |
-| Type [`spacetimedb_sdk::identity::Identity`](#type-identity) | A unique public identifier for a client. |
-| Type [`spacetimedb_sdk::identity::Token`](#type-token) | A private authentication token corresponding to an `Identity`. |
-| Type [`spacetimedb_sdk::identity::Credentials`](#type-credentials) | An `Identity` paired with its `Token`. |
-| Function [`spacetimedb_sdk::identity::identity`](#function-identity) | Return the current connection's `Identity`. |
-| Function [`spacetimedb_sdk::identity::token`](#function-token) | Return the current connection's `Token`. |
-| Function [`spacetimedb_sdk::identity::credentials`](#function-credentials) | Return the current connection's [`Credentials`](#type-credentials). |
-| Function [`spacetimedb_sdk::identity::on_connect`](#function-on-connect) | Register a `FnMut` callback to run when the connection's [`Credentials`](#type-credentials) are verified with the database. |
-| Function [`spacetimedb_sdk::identity::once_on_connect`](#function-once_on_connect) | Register a `FnOnce` callback to run when the connection's [`Credentials`](#type-credentials) are verified with the database. |
-| Function [`spacetimedb_sdk::identity::remove_on_connect`](#function-remove_on_connect) | Cancel an `on_connect` or `once_on_connect` callback. |
-| Function [`spacetimedb_sdk::identity::load_credentials`](#function-load_credentials) | Load a saved [`Credentials`](#type-credentials) from a file. |
-| Function [`spacetimedb_sdk::identity::save_credentials`](#function-save_credentials) | Save a [`Credentials`](#type-credentials) to a file. |
-| Type [`module_bindings::{TABLE}`](#type-table) | Autogenerated `struct` type for a table, holding one row. |
-| Method [`module_bindings::{TABLE}::filter_by_{COLUMN}`](#method-filter_by_column) | Autogenerated method to iterate over or seek subscribed rows where a column matches a value. |
-| Trait [`spacetimedb_sdk::table::TableType`](#trait-tabletype) | Automatically implemented for all tables defined by a module. |
-| Method [`spacetimedb_sdk::table::TableType::count`](#method-count) | Count the number of subscribed rows in a table. |
-| Method [`spacetimedb_sdk::table::TableType::iter`](#method-iter) | Iterate over all subscribed rows. |
-| Method [`spacetimedb_sdk::table::TableType::filter`](#method-filter) | Iterate over a subset of subscribed rows matching a predicate. |
-| Method [`spacetimedb_sdk::table::TableType::find`](#method-find) | Return one subscribed row matching a predicate. |
-| Method [`spacetimedb_sdk::table::TableType::on_insert`](#method-on_insert) | Register a `FnMut` callback to run whenever a new subscribed row is inserted. |
-| Method [`spacetimedb_sdk::table::TableType::remove_on_insert`](#method-remove_on_insert) | Cancel an `on_insert` callback. |
-| Method [`spacetimedb_sdk::table::TableType::on_delete`](#method-on_delete) | Register a `FnMut` callback to run whenever a subscribed row is deleted. |
-| Method [`spacetimedb_sdk::table::TableType::remove_on_delete`](#method-remove_on_delete) | Cancel an `on_delete` callback. |
-| Trait [`spacetimedb_sdk::table::TableWithPrimaryKey`](#trait-tablewithprimarykey) | Automatically implemented for tables with a column designated `#[primarykey]`. |
-| Method [`spacetimedb_sdk::table::TableWithPrimaryKey::on_update`](#method-on_update) | Register a `FnMut` callback to run whenever an existing subscribed row is updated. |
-| Method [`spacetimedb_sdk::table::TableWithPrimaryKey::remove_on_update`](#method-remove_on_update) | Cancel an `on_update` callback. |
-| Type [`module_bindings::ReducerEvent`](#type-reducerevent) | Autogenerated enum with a variant for each reducer defined by the module. |
-| Type [`module_bindings::{REDUCER}Args`](#type-reducerargs) | Autogenerated `struct` type for a reducer, holding its arguments. |
-| Function [`module_bindings::{REDUCER}`](#function-reducer) | Autogenerated function to invoke a reducer. |
-| Function [`module_bindings::on_{REDUCER}`](#function-on_reducer) | Autogenerated function to register a `FnMut` callback to run whenever the reducer is invoked. |
-| Function [`module_bindings::once_on_{REDUCER}`](#function-once_on_reducer) | Autogenerated function to register a `FnOnce` callback to run the next time the reducer is invoked. |
-| Function [`module_bindings::remove_on_{REDUCER}`](#function-remove_on_reducer) | Autogenerated function to cancel an `on_{REDUCER}` or `once_on_{REDUCER}` callback. |
-| Type [`spacetimedb_sdk::reducer::Status`](#type-status) | Enum representing reducer completion statuses. |
-
-## Connect to a database
-
-### Function `connect`
-
-```rust
-module_bindings::connect(
- spacetimedb_uri: impl TryInto,
- db_name: &str,
- credentials: Option,
-) -> anyhow::Result<()>
-```
-
-Connect to a database named `db_name` accessible over the internet at the URI `spacetimedb_uri`.
-
-| Argument | Type | Meaning |
-| ----------------- | --------------------- | ------------------------------------------------------------ |
-| `spacetimedb_uri` | `impl TryInto` | URI of the SpacetimeDB instance running the module. |
-| `db_name` | `&str` | Name of the module. |
-| `credentials` | `Option` | [`Credentials`](#type-credentials) to authenticate the user. |
-
-If `credentials` are supplied, they will be passed to the new connection to identify and authenticate the user. Otherwise, a set of [`Credentials`](#type-credentials) will be generated by the server.
-
-```rust
-const MODULE_NAME: &str = "my-module-name";
-
-// Connect to a local DB with a fresh identity
-connect("http://localhost:3000", MODULE_NAME, None)
- .expect("Connection failed");
-
-// Connect to cloud with a fresh identity.
-connect("https://testnet.spacetimedb.com", MODULE_NAME, None)
- .expect("Connection failed");
-
-// Connect with a saved identity
-const CREDENTIALS_DIR: &str = ".my-module";
-connect(
- "https://testnet.spacetimedb.com",
- MODULE_NAME,
- load_credentials(CREDENTIALS_DIR)
- .expect("Error while loading credentials"),
-).expect("Connection failed");
-```
-
-### Function `disconnect`
-
-```rust
-spacetimedb_sdk::disconnect()
-```
-
-Gracefully close the current WebSocket connection.
-
-If there is no active connection, this operation does nothing.
-
-```rust
-connect(SPACETIMEDB_URI, MODULE_NAME, credentials)
- .expect("Connection failed");
-
-run_app();
-
-disconnect();
-```
-
-### Function `on_disconnect`
-
-```rust
-spacetimedb_sdk::on_disconnect(
- callback: impl FnMut() + Send + 'static,
-) -> DisconnectCallbackId
-```
-
-Register a callback to be invoked when a connection ends.
-
-| Argument | Type | Meaning |
-| ---------- | ------------------------------- | ------------------------------------------------------ |
-| `callback` | `impl FnMut() + Send + 'static` | Callback to be invoked when subscriptions are applied. |
-
-The callback will be invoked after calling [`disconnect`](#function-disconnect), or when a connection is closed by the server.
-
-The returned `DisconnectCallbackId` can be passed to [`remove_on_disconnect`](#function-remove_on_disconnect) to unregister the callback.
-
-```rust
-on_disconnect(|| println!("Disconnected!"));
-
-connect(SPACETIMEDB_URI, MODULE_NAME, credentials)
- .expect("Connection failed");
-
-disconnect();
-
-// Will print "Disconnected!"
-```
-
-### Function `once_on_disconnect`
-
-```rust
-spacetimedb_sdk::once_on_disconnect(
- callback: impl FnOnce() + Send + 'static,
-) -> DisconnectCallbackId
-```
-
-Register a callback to be invoked the next time a connection ends.
-
-| Argument | Type | Meaning |
-| ---------- | ------------------------------- | ------------------------------------------------------ |
-| `callback` | `impl FnMut() + Send + 'static` | Callback to be invoked when subscriptions are applied. |
-
-The callback will be invoked after calling [`disconnect`](#function-disconnect), or when a connection is closed by the server.
-
-The callback will be unregistered after running.
-
-The returned `DisconnectCallbackId` can be passed to [`remove_on_disconnect`](#function-remove_on_disconnect) to unregister the callback.
-
-```rust
-once_on_disconnect(|| println!("Disconnected!"));
-
-connect(SPACETIMEDB_URI, MODULE_NAME, credentials)
- .expect("Connection failed");
-
-disconnect();
-
-// Will print "Disconnected!"
-
-connect(SPACETIMEDB_URI, MODULE_NAME, credentials)
- .expect("Connection failed");
-
-disconnect();
-
-// Nothing printed this time.
-```
-
-### Function `remove_on_disconnect`
-
-```rust
-spacetimedb_sdk::remove_on_disconnect(
- id: DisconnectCallbackId,
-)
-```
-
-Unregister a previously-registered [`on_disconnect`](#function-on_disconnect) callback.
-
-| Argument | Type | Meaning |
-| -------- | ---------------------- | ------------------------------------------ |
-| `id` | `DisconnectCallbackId` | Identifier for the callback to be removed. |
-
-If `id` does not refer to a currently-registered callback, this operation does nothing.
-
-```rust
-let id = on_disconnect(|| unreachable!());
-
-remove_on_disconnect(id);
-
-disconnect();
-
-// No `unreachable` panic.
-```
-
-## Subscribe to queries
-
-### Function `subscribe`
-
-```rust
-spacetimedb_sdk::subscribe(queries: &[&str]) -> anyhow::Result<()>
-```
-
-Subscribe to a set of queries, to be notified when rows which match those queries are altered.
-
-| Argument | Type | Meaning |
-| --------- | --------- | ---------------------------- |
-| `queries` | `&[&str]` | SQL queries to subscribe to. |
-
-The `queries` should be a slice of strings representing SQL queries.
-
-`subscribe` will return an error if called before establishing a connection with the autogenerated [`connect`](#function-connect) function. In that case, the queries are not registered.
-
-`subscribe` does not return data directly. The SDK will generate types [`module_bindings::{TABLE}`](#type-table) corresponding to each of the tables in your module. These types implement the trait [`spacetimedb_sdk::table_type::TableType`](#trait-tabletype), which contains methods such as [`TableType::on_insert`](#method-on_insert). Use these methods to receive data from the queries you subscribe to.
-
-A new call to `subscribe` (or [`subscribe_owned`](#function-subscribe_owned)) will remove all previous subscriptions and replace them with the new `queries`. If any rows matched the previous subscribed queries but do not match the new queries, those rows will be removed from the client cache, and [`TableType::on_delete`](#method-on_delete) callbacks will be invoked for them.
-
-```rust
-subscribe(&["SELECT * FROM User;", "SELECT * FROM Message;"])
- .expect("Called `subscribe` before `connect`");
-```
-
-### Function `subscribe_owned`
-
-```rust
-spacetimedb_sdk::subscribe_owned(queries: Vec) -> anyhow::Result<()>
-```
-
-Subscribe to a set of queries, to be notified when rows which match those queries are altered.
-
-| Argument | Type | Meaning |
-| --------- | ------------- | ---------------------------- |
-| `queries` | `Vec` | SQL queries to subscribe to. |
-
-The `queries` should be a `Vec` of `String`s representing SQL queries.
-
-A new call to `subscribe_owned` (or [`subscribe`](#function-subscribe)) will remove all previous subscriptions and replace them with the new `queries`.
-If any rows matched the previous subscribed queries but do not match the new queries, those rows will be removed from the client cache, and [`TableType::on_delete`](#method-on_delete) callbacks will be invoked for them.
-
-`subscribe_owned` will return an error if called before establishing a connection with the autogenerated [`connect`](#function-connect) function. In that case, the queries are not registered.
-
-```rust
-let query = format!("SELECT * FROM User WHERE name = '{}';", compute_my_name());
-
-subscribe_owned(vec![query])
- .expect("Called `subscribe_owned` before `connect`");
-```
-
-### Function `on_subscription_applied`
-
-```rust
-spacetimedb_sdk::on_subscription_applied(
- callback: impl FnMut() + Send + 'static,
-) -> SubscriptionCallbackId
-```
-
-Register a callback to be invoked the first time a subscription's matching rows becoming available.
-
-| Argument | Type | Meaning |
-| ---------- | ------------------------------- | ------------------------------------------------------ |
-| `callback` | `impl FnMut() + Send + 'static` | Callback to be invoked when subscriptions are applied. |
-
-The callback will be invoked after a successful [`subscribe`](#function-subscribe) or [`subscribe_owned`](#function-subscribe_owned) call when the initial set of matching rows becomes available.
-
-The returned `SubscriptionCallbackId` can be passed to [`remove_on_subscription_applied`](#function-remove_on_subscription_applied) to unregister the callback.
-
-```rust
-on_subscription_applied(|| println!("Subscription applied!"));
-
-subscribe(&["SELECT * FROM User;"])
- .expect("Called `subscribe` before `connect`");
-
-sleep(Duration::from_secs(1));
-
-// Will print "Subscription applied!"
-
-subscribe(&["SELECT * FROM User;", "SELECT * FROM Message;"])
- .expect("Called `subscribe` before `connect`");
-
-// Will print again.
-```
-
-### Function `once_on_subscription_applied`
-
-```rust
-spacetimedb_sdk::once_on_subscription_applied(
- callback: impl FnOnce() + Send + 'static,
-) -> SubscriptionCallbackId
-```
-
-Register a callback to be invoked the next time a subscription's matching rows become available.
-
-| Argument | Type | Meaning |
-| ---------- | ------------------------------- | ------------------------------------------------------ |
-| `callback` | `impl FnMut() + Send + 'static` | Callback to be invoked when subscriptions are applied. |
-
-The callback will be invoked after a successful [`subscribe`](#function-subscribe) or [`subscribe_owned`](#function-subscribe_owned) call when the initial set of matching rows becomes available.
-
-The callback will be unregistered after running.
-
-The returned `SubscriptionCallbackId` can be passed to [`remove_on_subscription_applied`](#function-remove_on_subscription_applied) to unregister the callback.
-
-```rust
-once_on_subscription_applied(|| println!("Subscription applied!"));
-
-subscribe(&["SELECT * FROM User;"])
- .expect("Called `subscribe` before `connect`");
-
-sleep(Duration::from_secs(1));
-
-// Will print "Subscription applied!"
-
-subscribe(&["SELECT * FROM User;", "SELECT * FROM Message;"])
- .expect("Called `subscribe` before `connect`");
-
-// Nothing printed this time.
-```
-
-### Function `remove_on_subscription_applied`
-
-```rust
-spacetimedb_sdk::remove_on_subscription_applied(
- id: SubscriptionCallbackId,
-)
-```
-
-Unregister a previously-registered [`on_subscription_applied`](#function-on_subscription_applied) callback.
-
-| Argument | Type | Meaning |
-| -------- | ------------------------ | ------------------------------------------ |
-| `id` | `SubscriptionCallbackId` | Identifier for the callback to be removed. |
-
-If `id` does not refer to a currently-registered callback, this operation does nothing.
-
-```rust
-let id = on_subscription_applied(|| println!("Subscription applied!"));
-
-subscribe(&["SELECT * FROM User;"])
- .expect("Called `subscribe` before `connect`");
-
-sleep(Duration::from_secs(1));
-
-// Will print "Subscription applied!"
-
-remove_on_subscription_applied(id);
-
-subscribe(&["SELECT * FROM User;", "SELECT * FROM Message;"])
- .expect("Called `subscribe` before `connect`");
-
-// Nothing printed this time.
-```
-
-## Identify a client
-
-### Type `Identity`
-
-```rust
-spacetimedb_sdk::identity::Identity
-```
-
-A unique public identifier for a client connected to a database.
-
-### Type `Token`
-
-```rust
-spacetimedb_sdk::identity::Token
-```
-
-A private access token for a client connected to a database.
-
-### Type `Credentials`
-
-```rust
-spacetimedb_sdk::identity::Credentials
-```
-
-Credentials, including a private access token, sufficient to authenticate a client connected to a database.
-
-| Field | Type |
-| ---------- | ---------------------------- |
-| `identity` | [`Identity`](#type-identity) |
-| `token` | [`Token`](#type-token) |
-
-### Function `identity`
-
-```rust
-spacetimedb_sdk::identity::identity() -> Result
-```
-
-Read the current connection's public [`Identity`](#type-identity).
-
-Returns an error if:
-
-- [`connect`](#function-connect) has not yet been called.
-- We connected anonymously, and we have not yet received our credentials.
-
-```rust
-connect(SPACETIMEDB_URI, DB_NAME, None)
- .expect("Failed to connect");
-
-sleep(Duration::from_secs(1));
-
-println!("My identity is {:?}", identity());
-
-// Prints "My identity is Ok(Identity { bytes: [...several u8s...] })"
-```
-
-### Function `token`
-
-```rust
-spacetimedb_sdk::identity::token() -> Result
-```
-
-Read the current connection's private [`Token`](#type-token).
-
-Returns an error if:
-
-- [`connect`](#function-connect) has not yet been called.
-- We connected anonymously, and we have not yet received our credentials.
-
-```rust
-connect(SPACETIMEDB_URI, DB_NAME, None)
- .expect("Failed to connect");
-
-sleep(Duration::from_secs(1));
-
-println!("My token is {:?}", token());
-
-// Prints "My token is Ok(Token {string: "...several Base64 digits..." })"
-```
-
-### Function `credentials`
-
-```rust
-spacetimedb_sdk::identity::credentials() -> Result
-```
-
-Read the current connection's [`Credentials`](#type-credentials), including a public [`Identity`](#type-identity) and a private [`Token`](#type-token).
-
-Returns an error if:
-
-- [`connect`](#function-connect) has not yet been called.
-- We connected anonymously, and we have not yet received our credentials.
-
-```rust
-connect(SPACETIMEDB_URI, DB_NAME, None)
- .expect("Failed to connect");
-
-sleep(Duration::from_secs(1));
-
-println!("My credentials are {:?}", credentials());
-
-// Prints "My credentials are Ok(Credentials {
-// identity: Identity { bytes: [...several u8s...] },
-// token: Token { string: "...several Base64 digits..."},
-// })"
-```
-
-### Function `on_connect`
-
-```rust
-spacetimedb_sdk::identity::on_connect(
- callback: impl FnMut(&Credentials) + Send + 'static,
-) -> ConnectCallbackId
-```
-
-Register a callback to be invoked upon authentication with the database.
-
-| Argument | Type | Meaning |
-| ---------- | ----------------------------------------- | ------------------------------------------------------ |
-| `callback` | `impl FnMut(&Credentials) + Send + 'sync` | Callback to be invoked upon successful authentication. |
-
-The callback will be invoked with the [`Credentials`](#type-credentials) provided by the database to identify this connection. If [`Credentials`](#type-credentials) were supplied to [`connect`](#function-connect), those passed to the callback will be equivalent to the ones used to connect. If the initial connection was anonymous, a new set of [`Credentials`](#type-credentials) will be generated by the database to identify this user.
-
-The [`Credentials`](#type-credentials) passed to the callback can be saved and used to authenticate the same user in future connections.
-
-The returned `ConnectCallbackId` can be passed to [`remove_on_connect`](#function-remove_on_connect) to unregister the callback.
-
-```rust
-on_connect(
- |creds| println!("Successfully connected! My credentials are: {:?}", creds)
-);
-
-connect(SPACETIMEDB_URI, DB_NAME, None)
- .expect("Failed to connect");
-
-sleep(Duration::from_secs(1));
-
-// Will print "Successfully connected! My credentials are: "
-// followed by a printed representation of the client's `Credentials`.
-```
-
-### Function `once_on_connect`
-
-```rust
-spacetimedb_sdk::identity::once_on_connect(
- callback: impl FnOnce(&Credentials) + Send + 'static,
-) -> ConnectCallbackId
-```
-
-Register a callback to be invoked once upon authentication with the database.
-
-| Argument | Type | Meaning |
-| ---------- | ------------------------------------------ | ---------------------------------------------------------------- |
-| `callback` | `impl FnOnce(&Credentials) + Send + 'sync` | Callback to be invoked once upon next successful authentication. |
-
-The callback will be invoked with the [`Credentials`](#type-credentials) provided by the database to identify this connection. If [`Credentials`](#type-credentials) were supplied to [`connect`](#function-connect), those passed to the callback will be equivalent to the ones used to connect. If the initial connection was anonymous, a new set of [`Credentials`](#type-credentials) will be generated by the database to identify this user.
-
-The [`Credentials`](#type-credentials) passed to the callback can be saved and used to authenticate the same user in future connections.
-
-The callback will be unregistered after running.
-
-The returned `ConnectCallbackId` can be passed to [`remove_on_connect`](#function-remove_on_connect) to unregister the callback.
-
-### Function `remove_on_connect`
-
-```rust
-spacetimedb_sdk::identity::remove_on_connect(id: ConnectCallbackId)
-```
-
-Unregister a previously-registered [`on_connect`](#function-on_connect) or [`once_on_connect`](#function-once_on_connect) callback.
-
-| Argument | Type | Meaning |
-| -------- | ------------------- | ------------------------------------------ |
-| `id` | `ConnectCallbackId` | Identifier for the callback to be removed. |
-
-If `id` does not refer to a currently-registered callback, this operation does nothing.
-
-```rust
-let id = on_connect(|_creds| unreachable!());
-
-remove_on_connect(id);
-
-connect(SPACETIMEDB_URI, DB_NAME, None)
- .expect("Failed to connect");
-
-sleep(Duration::from_secs(1));
-
-// No `unreachable` panic.
-```
-
-### Function `load_credentials`
-
-```rust
-spacetimedb_sdk::identity::load_credentials(
- dirname: &str,
-) -> Result