diff --git a/docs/docs/08-SQL/01-sql-reference.md b/docs/docs/08-SQL/01-sql-reference.md deleted file mode 100644 index 9c820a59b71..00000000000 --- a/docs/docs/08-SQL/01-sql-reference.md +++ /dev/null @@ -1,664 +0,0 @@ ---- -title: SQL Reference -slug: /sql ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# SQL Support - -SpacetimeDB supports two subsets of SQL: -One for queries issued through the [cli] or [http] api. -Another for subscriptions issued via the [sdk] or WebSocket api. - -## Subscriptions - -```ebnf -SELECT projection FROM relation [ WHERE predicate ] -``` - -The subscription language is strictly a query language. -Its sole purpose is to replicate a subset of the rows in the database, -and to **automatically** update them in realtime as the database changes. - -There is no context for manually updating this view. -Hence data manipulation commands like `INSERT` and `DELETE` are not supported. - -> NOTE: Because subscriptions are evaluated in realtime, -> performance is critical, and as a result, -> additional restrictions are applied over ad hoc queries. -> These restrictions are highlighted below. - -### SELECT - -```ebnf -SELECT ( '*' | table '.' '*' ) -``` - -The `SELECT` clause determines the table that is being subscribed to. -Since the subscription api is purely a replication api, -a query may only return rows from a single table, -and it must return the entire row. -Individual column projections are not allowed. - -A `*` projection is allowed when the table is unambiguous, -otherwise it must be qualified with the appropriate table name. - -#### Examples - -```sql --- Subscribe to all rows of a table -SELECT * FROM Inventory - --- Qualify the `*` projection with the table -SELECT item.* from Inventory item - --- Subscribe to all customers who have orders totaling more than $1000 -SELECT customer.* -FROM Customers customer JOIN Orders o ON customer.id = o.customer_id -WHERE o.amount > 1000 - --- INVALID: Must return `Customers` or `Orders`, but not both -SELECT * -FROM Customers customer JOIN Orders o ON customer.id = o.customer_id -WHERE o.amount > 1000 -``` - -### FROM - -```ebnf -FROM table [ [AS] alias ] [ [INNER] JOIN table [ [AS] alias ] ON column '=' column ] -``` - -While you can only subscribe to rows from a single table, -you may reference two tables in the `FROM` clause using a `JOIN`. -A `JOIN` selects all combinations of rows from its input tables, -and `ON` determines which combinations are considered. - -Subscriptions do not support joins of more than two tables. - -For any column referenced in `ON` clause of a `JOIN`, -it must be qualified with the appropriate table name or alias. - -In order for a `JOIN` to be evaluated efficiently, -subscriptions require an index to be defined on both join columns. - -#### Example - -```sql --- Subscribe to all orders of products with less than 10 items in stock. --- Must have an index on the `product_id` column of the `Orders` table, --- as well as the `id` column of the `Product` table. -SELECT o.* -FROM Orders o JOIN Inventory product ON o.product_id = product.id -WHERE product.quantity < 10 - --- Subscribe to all products that have at least one purchase -SELECT product.* -FROM Orders o JOIN Inventory product ON o.product_id = product.id - --- INVALID: Must qualify the column names referenced in `ON` -SELECT product.* FROM Orders JOIN Inventory product ON product_id = id -``` - -### WHERE - -```ebnf -predicate - = expr - | predicate AND predicate - | predicate OR predicate - ; - -expr - = literal - | column - | expr op expr - ; - -op - = '=' - | '<' - | '>' - | '<' '=' - | '>' '=' - | '!' '=' - | '<' '>' - ; - -literal - = INTEGER - | STRING - | HEX - | TRUE - | FALSE - ; -``` - -While the `SELECT` clause determines the table, -the `WHERE` clause determines the rows in the subscription. - -Arithmetic expressions are not supported. - -#### Examples - -```sql --- Find products that sell for more than $X -SELECT * FROM Inventory WHERE price > {X} - --- Find products that sell for more than $X and have fewer than Y items in stock -SELECT * FROM Inventory WHERE price > {X} AND amount < {Y} -``` - -## Query and DML (Data Manipulation Language) - -### Statements - -- [SELECT](#select-1) -- [INSERT](#insert) -- [DELETE](#delete) -- [UPDATE](#update) -- [SET](#set) -- [SHOW](#show) - -### SELECT - -```ebnf -SELECT projection FROM relation [ WHERE predicate ] [LIMIT NUM] -``` - -The query languge is a strict superset of the subscription language. -The main differences are seen in column projections and [joins](#from-clause). - -The subscription api only supports `*` projections, -but the query api supports both individual column projections, -as well as aggregations in the form of `COUNT`. - -The subscription api limits the number of tables you can join, -and enforces index constraints on the join columns, -but the query language has no such constraints or limitations. - -#### SELECT Clause - -```ebnf -projection - = '*' - | table '.' '*' - | projExpr { ',' projExpr } - | aggExpr - ; - -projExpr - = column [ [ AS ] alias ] - ; - -aggExpr - = COUNT '(' '*' ')' [AS] alias - ; -``` - -The `SELECT` clause determines the columns that are returned. - -##### Examples - -```sql --- Select the items in my inventory -SELECT * FROM Inventory; - --- Select the names and prices of the items in my inventory -SELECT item_name, price FROM Inventory -``` - -It also allows for counting the number of input rows via the `COUNT` function. -`COUNT` always returns a single row, even if the input is empty. - -##### Example - -```sql --- Count the items in my inventory -SELECT COUNT(*) AS n FROM Inventory -``` - -#### FROM Clause - -```ebnf -FROM table [ [AS] alias ] { [INNER] JOIN table [ [AS] alias ] ON predicate } -``` - -Unlike [subscriptions](#from), the query api supports joining more than two tables. - -##### Examples - -```sql --- Find all customers who ordered a particular product and when they ordered it -SELECT customer.first_name, customer.last_name, o.date -FROM Customers customer -JOIN Orders o ON customer.id = o.customer_id -JOIN Inventory product ON o.product_id = product.id -WHERE product.name = {product_name} -``` - -#### WHERE Clause - -See [Subscriptions](#where). - -#### LIMIT clause - -Limits the number of rows a query returns by specifying an upper bound. -The `LIMIT` may return fewer rows if the query itself returns fewer rows. -`LIMIT` does not order or transform its input in any way. - -##### Examples - -```sql --- Fetch an example row from my inventory -SELECT * FROM Inventory LIMIT 1 -``` - -### INSERT - -```ebnf -INSERT INTO table [ '(' column { ',' column } ')' ] VALUES '(' literal { ',' literal } ')' -``` - -#### Examples - -```sql --- Inserting one row -INSERT INTO Inventory (item_id, item_name) VALUES (1, 'health1'); - --- Inserting two rows -INSERT INTO Inventory (item_id, item_name) VALUES (1, 'health1'), (2, 'health2'); -``` - -### DELETE - -```ebnf -DELETE FROM table [ WHERE predicate ] -``` - -Deletes all rows from a table. -If `WHERE` is specified, only the matching rows are deleted. - -`DELETE` does not support joins. - -#### Examples - -```sql --- Delete all rows -DELETE FROM Inventory; - --- Delete all rows with a specific item_id -DELETE FROM Inventory WHERE item_id = 1; -``` - -### UPDATE - -```ebnf -UPDATE table SET [ '(' assignment { ',' assignment } ')' ] [ WHERE predicate ] -``` - -Updates column values of existing rows in a table. -The columns are identified by the `assignment` defined as `column '=' literal`. -The column values are updated for all rows that match the `WHERE` condition. -The rows are updated after the `WHERE` condition is evaluated for all rows. - -`UPDATE` does not support joins. - -#### Examples - -```sql --- Update the item_name for all rows with a specific item_id -UPDATE Inventory SET item_name = 'new name' WHERE item_id = 1; -``` - -### SET - -> WARNING: The `SET` statement is experimental. -> Compatibility with future versions of SpacetimeDB is not guaranteed. - -```ebnf -SET var ( TO | '=' ) literal -``` - -Updates the value of a system variable. - -### SHOW - -> WARNING: The `SHOW` statement is experimental. -> Compatibility with future versions of SpacetimeDB is not guaranteed. - -```ebnf -SHOW var -``` - -Returns the value of a system variable. - -## System Variables - -> WARNING: System variables are experimental. -> Compatibility with future versions of SpacetimeDB is not guaranteed. - -- `row_limit` - - ```sql - -- Reject queries that scan more than 10K rows - SET row_limit = 10000 - ``` - -## Data types - -The set of data types that SpacetimeDB supports is defined by SATS, -the Spacetime Algebraic Type System. - -Spacetime SQL however does not support all of SATS, -specifically in the way of product and sum types. -The language itself does not provide a way to construct them, -nore does it provide any scalar operators for them. -Nevertheless rows containing them can be returned to clients. - -## Literals - -```ebnf -literal = INTEGER | FLOAT | STRING | HEX | TRUE | FALSE ; -``` - -The following describes how to construct literal values for SATS data types in Spacetime SQL. - -### Booleans - -Booleans are represented using the canonical atoms `true` or `false`. - -### Integers - -```ebnf -INTEGER - = [ '+' | '-' ] NUM - | [ '+' | '-' ] NUM 'E' [ '+' ] NUM - ; - -NUM - = DIGIT { DIGIT } - ; - -DIGIT - = 0..9 - ; -``` - -SATS supports multiple fixed width integer types. -The concrete type of a literal is inferred from the context. - -#### Examples - -```sql --- All products that sell for more than $1000 -SELECT * FROM Inventory WHERE price > 1000 -SELECT * FROM Inventory WHERE price > 1e3 -SELECT * FROM Inventory WHERE price > 1E3 -``` - -### Floats - -```ebnf -FLOAT - = [ '+' | '-' ] [ NUM ] '.' NUM - | [ '+' | '-' ] [ NUM ] '.' NUM 'E' [ '+' | '-' ] NUM - ; -``` - -SATS supports both 32 and 64 bit floating point types. -The concrete type of a literal is inferred from the context. - -#### Examples - -```sql --- All measurements where the temperature is greater than 105.3 -SELECT * FROM Measurements WHERE temperature > 105.3 -SELECT * FROM Measurements WHERE temperature > 1053e-1 -SELECT * FROM Measurements WHERE temperature > 1053E-1 -``` - -### Strings - -```ebnf -STRING - = "'" { "''" | CHAR } "'" - ; -``` - -`CHAR` is defined as a `utf-8` encoded unicode character. - -#### Examples - -```sql -SELECT * FROM Customers WHERE first_name = 'John' -``` - -### Hex - -```ebnf -HEX - = 'X' "'" { HEXIT } "'" - | '0' 'x' { HEXIT } - ; - -HEXIT - = DIGIT | a..f | A..F - ; -``` - -Hex literals can represent [Identity], [ConnectionId], or binary types. -The type is ultimately inferred from the context. - -#### Examples - -```sql -SELECT * FROM Program WHERE hash_value = 0xABCD1234 -``` - -## Identifiers - -```ebnf -identifier - = LATIN { LATIN | DIGIT | '_' } - | '"' { '""' | CHAR } '"' - ; - -LATIN - = a..z | A..Z - ; -``` - -Identifiers are tokens that identify database objects like tables or columns. -Spacetime SQL supports both quoted and unquoted identifiers. -Both types of identifiers are case sensitive. -Use quoted identifiers to avoid conflict with reserved SQL keywords, -or if your table or column contains non-alphanumeric characters. - -Because SpacetimeDB uses a postgres compatible parser, identifiers which are -reserved in postgres are automatically reserved in Spacetime SQL. See [SQL Key Words in the PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-keywords-appendix.html). - -### Example - -```sql --- `ORDER` is a sql keyword and therefore needs to be quoted -SELECT * FROM "Order" - --- A table containing `$` needs to be quoted as well -SELECT * FROM "Balance$" -``` - -## Best Practices for Performance and Scalability - -When designing your schema or crafting your queries, -consider the following best practices to ensure optimal performance: - -- **Add Primary Key and/or Unique Constraints:** - Constrain columns whose values are guaranteed to be distinct as either unique or primary keys. - The query planner can further optimize joins if it knows the join values to be unique. - -- **Index Filtered Columns:** - Index columns frequently used in a `WHERE` clause. - Indexes reduce the number of rows scanned by the query engine. - -- **Index Join Columns:** - Index columns whose values are frequently used as join keys. - These are columns that are used in the `ON` condition of a `JOIN`. - - Again, this reduces the number of rows that must be scanned to answer a query. - It is also critical for the performance of subscription updates -- - so much so that it is a compiler-enforced requirement, - as mentioned in the [subscription](#from) section. - - If a column that has already been constrained as unique or a primary key, - it is not necessary to explicitly index it as well, - since these constraints automatically index the column in question. - -- **Optimize Join Order:** - Place tables with the most selective filters first in your `FROM` clause. - This minimizes intermediate result sizes and improves query efficiency. - -### Example - -Take the following query that was used in a previous example: - -```sql --- Find all customers who ordered a particular product and when they ordered it -SELECT customer.first_name, customer.last_name, o.date -FROM Customers customer -JOIN Orders o ON customer.id = o.customer_id -JOIN Inventory product ON o.product_id = product.id -WHERE product.name = {product_name} -``` - -In order to conform with the best practices for optimizing performance and scalability: - -- An index should be defined on `Inventory.name` because we are filtering on that column. -- `Inventory.id` and `Customers.id` should be defined as primary keys. -- Additionally non-unique indexes should be defined on `Orders.product_id` and `Orders.customer_id`. -- `Inventory` should appear first in the `FROM` clause because it is the only table mentioned in the `WHERE` clause. -- `Orders` should come next because it joins directly with `Inventory`. -- `Customers` should come next because it joins directly with `Orders`. - - - - -```rust -#[table( - name = Inventory, - index(name = product_name, btree = [name]), - public -)] -struct Inventory { - #[primary_key] - id: u64, - name: String, - .. -} - -#[table( - name = Customers, - public -)] -struct Customers { - #[primary_key] - id: u64, - first_name: String, - last_name: String, - .. -} - -#[table( - name = Orders, - public -)] -struct Orders { - #[primary_key] - id: u64, - #[unique] - product_id: u64, - #[unique] - customer_id: u64, - .. -} -``` - - - - -```cs -[SpacetimeDB.Table(Name = "Inventory")] -[SpacetimeDB.Index(Name = "product_name", BTree = ["name"])] -public partial struct Inventory -{ - [SpacetimeDB.PrimaryKey] - public long id; - public string name; - .. -} - -[SpacetimeDB.Table(Name = "Customers")] -public partial struct Customers -{ - [SpacetimeDB.PrimaryKey] - public long id; - public string first_name; - public string last_name; - .. -} - -[SpacetimeDB.Table(Name = "Orders")] -public partial struct Orders -{ - [SpacetimeDB.PrimaryKey] - public long id; - [SpacetimeDB.Unique] - public long product_id; - [SpacetimeDB.Unique] - public long customer_id; - .. -} -``` - - - - -```sql --- Find all customers who ordered a particular product and when they ordered it -SELECT c.first_name, c.last_name, o.date -FROM Inventory product -JOIN Orders o ON product.id = o.product_id -JOIN Customers c ON c.id = o.customer_id -WHERE product.name = {product_name}; -``` - -## Appendix - -Common production rules that have been used throughout this document. - -```ebnf -table - = identifier - ; - -alias - = identifier - ; - -var - = identifier - ; - -column - = identifier - | identifier '.' identifier - ; -``` - -[sdk]: /sdks/rust#subscribe-to-queries -[http]: /http/database#post-v1databasename_or_identitysql -[cli]: /cli-reference#spacetime-sql -[Identity]: /#identity -[ConnectionId]: /#connectionid diff --git a/docs/docs/09-Subscriptions/02-subscription-semantics.md b/docs/docs/09-Subscriptions/02-subscription-semantics.md deleted file mode 100644 index 689a19b0aaa..00000000000 --- a/docs/docs/09-Subscriptions/02-subscription-semantics.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Subscription Semantics -slug: /subscriptions/semantics ---- - - - -# SpacetimeDB Subscription Semantics - -This document describes the subscription semantics maintained by the SpacetimeDB host over WebSocket connections. These semantics outline message ordering guarantees, subscription handling, transaction updates, and client cache consistency. - -## WebSocket Communication Channels - -A single WebSocket connection between a client and the SpacetimeDB host consists of two distinct message channels: - -- **Client → Server:** Sends requests such as reducer invocations and subscription queries. -- **Server → Client:** Sends responses to client requests and database transaction updates. - -### Ordering Guarantees - -The server maintains the following guarantees: - -1. **Sequential Response Ordering:** - - Responses to client requests are always sent back in the same order the requests were received. If request A precedes request B, the response to A will always precede the response to B, even if A takes longer to process. - -2. **Atomic Transaction Updates:** - - Each database transaction (e.g., reducer invocation, INSERT, UPDATE, DELETE queries) generates exactly zero or one update message sent to clients. These updates are atomic and reflect the exact order of committed transactions. - -3. **Atomic Subscription Initialization:** - - When subscriptions are established, clients receive exactly one response containing all initially matching rows from a consistent database state snapshot taken between two transactions. - - The state snapshot reflects a committed database state that includes all previous transaction updates received and excludes all future transaction updates. - -## Subscription Workflow - -When invoking `SubscriptionBuilder::subscribe(QUERIES)` from the client SDK: - -1. **Client SDK → Host:** - - Sends a `Subscribe` message containing the specified QUERIES. - -2. **Host Processing:** - - Captures a snapshot of the committed database state. - - Evaluates the QUERIES against this snapshot to determine matching rows. - -3. **Host → Client SDK:** - - Sends a `SubscribeApplied` message containing the matching rows. - -4. **Client SDK Processing:** - - Receives and processes the message. - - Locks the client cache and inserts all rows atomically. - - Invokes relevant callbacks: - - `on_insert` callback for each row. - - `on_applied` callback for the subscription. - :::note - No relative ordering guarantees are made regarding the invocation order of these callbacks. - ::: - -## Transaction Update Workflow - -Upon committing a database transaction: - -1. **Transaction Results in a State Delta:** - - The result of a transaction is a state delta, i.e. an unordered set of inserted and deleted rows. - -2. **Host Evaluates Queries:** - - Evaluates the QUERIES against the state delta to determine matching altered rows. - -3. **Host → Client SDK:** - - Sends a `TransactionUpdate` message if relevant updates exist, containing affected rows and transaction metadata. - -4. **Client SDK Processing:** - - Receives and processes the message. - - Locks the client cache, applying deletions and insertions atomically. - - Invokes relevant callbacks: - - `on_insert`, `on_delete`, `on_update` callbacks for modified rows. - - Reducer callbacks, if the transaction was the result of a reducer. - :::note - No relative ordering guarantees are made regarding the invocation order of these callbacks. - ::: - -## Multiple Subscription Sets - -If multiple subscription sets are active, updates across these sets are bundled together into a single `TransactionUpdate` message. - -## Client Cache Guarantees - -- The client cache always maintains a consistent and correct subset of the committed database state. -- Callback functions invoked due to events have guaranteed visibility into a fully updated cache state. -- Reads from the client cache are effectively free as they access locally cached data. -- During callback execution, the client cache accurately reflects the database state immediately following the event-triggering transaction. - -### Pending Callbacks and Cache Consistency - -While processing a `TransactionUpdate` message, callbacks are queued within the SDK and deferred until the cache updates (inserts/deletes) from a transaction are fully applied. This ensures all callbacks see the fully consistent state of the cache, preventing callbacks from observing an inconsistent intermediate state. diff --git a/docs/docs/12-SpacetimeAuth/01-overview.md b/docs/docs/12-SpacetimeAuth/01-overview.md deleted file mode 100644 index c4a12165a58..00000000000 --- a/docs/docs/12-SpacetimeAuth/01-overview.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Overview -slug: /spacetimeauth ---- - -# SpacetimeAuth - Overview - -:::warning - -SpacetimeAuth is currently in beta, some features may not be available yet or may change in the future. You might encounter bugs or issues while using the service. Please report any problems you encounter to help us improve SpacetimeAuth. - -::: - -SpacetimeAuth is a service for managing authentication for your SpacetimeDB -applications. This allows you to authenticate users without needing -an external authentication service or even a hosting server. -SpacetimeAuth is an [OpenID Connect (OIDC)](https://openid.net/developers/how-connect-works/) provider, which means it can be used with -any OIDC-compatible client library. - -At the end of the authentication flow, your application receives an ID token -containing identity claims (such as email, username, and roles). Your -application can then use this token with any SpacetimeDB SDK to authenticate and -authorize users with the SpacetimeDB server. - -## Features - -### Authentication methods - -- Magic link -- Github -- Google -- Discord -- Twitch -- Kick - -### User & role management - -- Create, update, and manage users -- Assign roles to users for role-based access control - -### Customization - -- Customizable theme for login pages -- Enable/disable anonymous and magic link authentication - -## Terminology - -Feel free to check out our blog post on OpenID -Connect [Who are you? Who am I to you?](https://spacetimedb.com/blog/who-are-you) -for more information about OpenID Connect and how it works. - -### Projects - -SpacetimeAuth uses the concept of "projects" to manage authentication for -different applications. - -- Each project has its own set of users, roles, and authentication methods. -- Each project has its own configuration for email templates, web pages, and - other settings. -- Each project is independent of a SpacetimeDB database and can be used by one - or many databases. - -Here are some examples of how you might use projects: - -- You have a web application and a mobile application that both use the same - SpacetimeDB database. You can create a single SpacetimeAuth project for both - applications, therefore sharing a single user base. -- You have multiple SpacetimeDB databases for different environments (e.g. dev, - staging, production). You can create a separate SpacetimeAuth project for each - environment, therefore separating your users between environments. -- You have multiple SpacetimeDB databases for different applications. You can - create a separate SpacetimeAuth project for each application, therefore - separating your users between applications. - -### Users - -Users are the individuals who will be authenticating to your application via -SpacetimeAuth. Each user has a unique identifier (user ID) and can have one or -more roles assigned to them. - -### Clients - -:::note -Clients must not be confused with Users. -::: - -Clients, also known as Relying Parties in OpenID Connect terminology, are the -applications that are relying on SpacetimeAuth for authentication. Each client -is associated with a single project and has its own client ID and client -secret. - -Clients are applications that request an OpenID Connect ID token from -SpacetimeAuth, which can then be used to authenticate with the SpacetimeDB -server. - -### Roles - -Roles are used to manage access control within your application. Each role is -represented by a string (e.g. "admin", "user") that can be assigned to one or -more users. - -Roles are included as claims in the ID token that is issued to the user upon -authentication. - -Inside your reducers, you can check the user's roles to determine what -actions they are allowed to perform. - -## Getting Started - -Check out our [Creating a project guide](/spacetimeauth/creating-a-project) to -learn how to create and configure a SpacetimeAuth project. diff --git a/docs/docs/12-SpacetimeAuth/02-creating-a-project.md b/docs/docs/12-SpacetimeAuth/02-creating-a-project.md deleted file mode 100644 index 31e012f0c44..00000000000 --- a/docs/docs/12-SpacetimeAuth/02-creating-a-project.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Creating a project -slug: /spacetimeauth/creating-a-project ---- - -# Setting up a SpacetimeAuth Project - -:::warning - -SpacetimeAuth is currently in beta, some features may not be available yet or may change in the future. You might encounter bugs or issues while using the service. Please report any problems you encounter to help us improve SpacetimeAuth. - -::: - -## 1. Accessing the SpacetimeAuth Dashboard - -To get started with SpacetimeAuth, log in to the website and navigate to the -SpacetimeAuth dashboard: [https://spacetimedb.com/spacetimeauth](https://spacetimedb.com/spacetimeauth) - -You can also click on your profile icon in the top right corner and select -"SpacetimeAuth" from the dropdown menu. - -![Access the menu](/images/spacetimeauth/menu-access.png) - -## 2. Creating a New Project - -Once you're in the SpacetimeAuth dashboard, you can create a new project by -clicking the "New Project" button. - -![Create project](/images/spacetimeauth/new-project.png) - -## 3. Exploring the Dashboard - -After creating a project, you can access the dashboard by clicking on the project -name in the list of projects. -![Project list](/images/spacetimeauth/projects-list.png) - -The dashboard provides you with multiple tabs to manage different aspects of -your project: - -- **Overview**: A summary of your project, including a table of recent users. -- **Clients**: A list of all clients (applications) that can be used to - authenticate in your applications. - A default client is created for you when you create a new project. -- **Users**: A list of all users in your project, with options to search, filter, - and manage users. -- **Identity Providers**: A list of all identity providers (e.g. Google, GitHub, - etc.) that can be used to authenticate users in your project. -- **Customization**: Live editor to customize colors, logos, and authentication methods. - -![Project overview](/images/spacetimeauth/project-overview.png) - -## 4. Next Steps - -Now that you have created a SpacetimeAuth project, you can start configuring it -to suit your application's needs. Check out our [configuration guide](/spacetimeauth/configuring-a-project) -for more information on setting up identity providers, customizing templates, -and managing users and roles. diff --git a/docs/docs/12-SpacetimeAuth/03-configuring-a-project.md b/docs/docs/12-SpacetimeAuth/03-configuring-a-project.md deleted file mode 100644 index f630bd3f4aa..00000000000 --- a/docs/docs/12-SpacetimeAuth/03-configuring-a-project.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Configuring your project -slug: /spacetimeauth/configuring-a-project ---- - -# Configuring a SpacetimeAuth Project - -:::warning - -SpacetimeAuth is currently in beta, some features may not be available yet or may change in the future. You might encounter bugs or issues while using the service. Please report any problems you encounter to help us improve SpacetimeAuth. - -::: - -SpacetimeAuth projects can be configured to suit your application's needs. -This guide will walk you through the various configuration options available in the -SpacetimeAuth dashboard and through various common use cases like setting up -third-party identity providers. - -## Managing Clients - -Clients represent applications that will use SpacetimeAuth for authentication. -Each client has its own set of settings, including redirect URIs, -post logout URIs, and name. - -You can manage clients by navigating to the "Clients" tab in your -SpacetimeAuth project dashboard. -![Clients tab](/images/spacetimeauth/clients-tab.png) -Every project comes with a default client that you can use to get started. -You can also create additional clients by clicking the "Create Client" button. - -The majority of projects will only need a single client to authenticate users to -their SpacetimeDB module. You may want to create multiple clients if you have -multiple applications (e.g. a sidecar, admin panel, etc.) and want to use different -authentication flows or settings for each application. - -When creating or editing a client, you can configure the following settings: - -- **Name**: The name of the client (e.g. "My Web App"). -- **Redirect URIs**: The URIs to which SpacetimeAuth allows to redirect - the user after a successful login. These must match the URIs used in your application. -- **Post Logout Redirect URIs**: The URIs to which SpacetimeAuth allows to redirect - the user after a logout. These must match the URIs used in your application. - -:::danger -Remember to keep your client secret secure, **never** expose it in client-side code or public repositories. You can freely share the client ID as it is not sensitive information. Client secrets are only used during the `client_credentials` flow, allowing you to get a token with no user context (the `sub` claim will be set to the client ID).` -::: - -![Edit client](/images/spacetimeauth/edit-client.png) - -### Scopes and Claims - -Scopes are not yet editable and are currently limited to `openid`, `profile`, -and `email`. These scopes are sufficient for most applications, as they provide -all the necessary information about the authenticated user. -Here are the claims (i.e, user information available in the ID token) provided by -each scope: - -| Scope | Claims | -| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| openid (required) | `sub` (unique user identifier) | -| profile | `name`, `family_name`, `given_name`, `middle_name`, `nickname`, `preferred_username`, `picture`, `website`, `gender`, `birthdate`, `zoneinfo`, `locale`, `updated_at` | -| email | `email`, `email_verified` | - -You can request all or a subset of these scopes when initiating the -authentication flow in your application. - -### Redirect URIs - -Redirect URIs are a critical part of the OAuth2 and OpenID Connect flows. -They ensure that after a user authenticates, they are redirected back to a -trusted location in your application. - -When configuring redirect URIs for your client, ensure that they match the URIs -used in your application. This includes the scheme (http or https), domain, -port (if applicable), and path. -For example, if your application is hosted at `https://myapp.com` and you -initiate the authentication flow from `https://myapp.com/login`, you might -set the redirect URI to `https://myapp.com/callback`. - -To find the correct redirect URIs for your application, refer to the -documentation of the authentication library you are using or check out our -integration guides with various frameworks. - -## Setting Up Third-Party Identity Providers - -SpacetimeAuth supports multiple third-party identity providers, allowing users to -authenticate using their existing accounts. Supported providers include: - -- Google -- GitHub -- Discord -- Twitch -- Kick -- More providers will be added in the future - -User's information from third-party identity providers is mapped to the standard -OpenID Connect claims used by SpacetimeAuth. This ensures a consistent user -experience regardless of the identity provider used. -For example the username claim is mapped to the standard `preferred_username` claim. - -You can manage identity providers by navigating to the "Identity Providers" tab -in your SpacetimeAuth project dashboard. - -![Identity Providers tab](/images/spacetimeauth/identity-providers.png) - -Since SpacetimeAuth acts as a client for the external identity provider, -you need to provide the client ID and client secret obtained -from the provider's developer console in order to enable the provider. -You must also configure the redirect URI in the provider's developer console to -point to SpacetimeAuth (see below). -You can also choose to enable or disable the provider. -After entering the required information, click "Save" and the provider will be -available on the login page of your application. - -Here are guides to help you create the required OAuth application (sometimes -called an OAuth App or OAuth Client): - -- [Google](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid#get_your_google_api_client_id) -- [GitHub](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) -- [Discord](https://discord.com/developers/docs/quick-start/getting-started#step-1-creating-an-app) -- [Twitch](https://dev.twitch.tv/docs/authentication/register-app/) -- [Kick](https://docs.kick.com/getting-started/kick-apps-setup) - -Here are the redirect URIs you need to configure for each enabled provider: - -| Provider | Redirect URI | -| -------- | ---------------------------------------------------------------------- | -| Google | `https://auth.spacetimedb.com/interactions/federated/callback/google` | -| GitHub | `https://auth.spacetimedb.com/interactions/federated/callback/github` | -| Discord | `https://auth.spacetimedb.com/interactions/federated/callback/discord` | -| Twitch | `https://auth.spacetimedb.com/interactions/federated/callback/twitch` | -| Kick | `https://auth.spacetimedb.com/interactions/federated/callback/kick` | - -## Next Steps - -Now that you have created and configured a SpacetimeAuth project, you can -start integrating it into your application. Before writing code, we recommend -verifying your setup with a quick test. - -- [Test your configuration with OIDC Debugger](/spacetimeauth/testing) -- [React integration guide](/spacetimeauth/react-integration) diff --git a/docs/docs/12-SpacetimeAuth/04-testing.md b/docs/docs/12-SpacetimeAuth/04-testing.md deleted file mode 100644 index 8c119e1fb05..00000000000 --- a/docs/docs/12-SpacetimeAuth/04-testing.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Testing -slug: /spacetimeauth/testing ---- - -# Testing Your SpacetimeAuth Setup with OIDC Debugger - -:::warning - -SpacetimeAuth is currently in beta, some features may not be available yet or may change in the future. You might encounter bugs or issues while using the service. Please report any problems you encounter to help us improve SpacetimeAuth. - -::: - -Before integrating SpacetimeAuth into your application code, it’s a good idea -to verify that your client and redirect URIs are working correctly. One of the -easiest ways to do this is with [OIDC Debugger](https://oidcdebugger.com). - -## Why Use OIDC Debugger? - -OIDC Debugger simulates the OAuth2 / OIDC Authorization Code flow in your browser. -It allows you to: - -- Confirm that your **redirect URIs** are configured properly. -- Verify that your **client ID** works. -- Inspect the **ID Token** and claims (`email`, `sub`, `preferred_username`, etc.). -- Catch configuration issues before writing code. - ---- - -## Step 1: Gather Your Configuration - -- **Authorization Endpoint**: - `https://auth.spacetimedb.com/oidc/auth` - -- **Token Endpoint**: - `https://auth.spacetimedb.com/oidc/token` - -- **Client ID**: From your SpacetimeAuth dashboard, you can use any available client. -- **Redirect URI**: [https://oidcdebugger.com/debug](https://oidcdebugger.com/debug) must be added to your - client’s allowed redirect URIs in the SpacetimeAuth dashboard. - ---- - -## Step 2: Open OIDC Debugger - -1. Go to [https://oidcdebugger.com](https://oidcdebugger.com). -2. Fill out the fields as follows, leave all other fields at their defaults - (e.g., response type = code, state, nonce). - - | Field | Value | - | ------------- | ----------------------------------------- | - | Authorize URI | `https://auth.spacetimedb.com/oidc/auth` | - | Client ID | Your SpacetimeAuth client ID | - | Scope | `openid profile email` (or a subset) | - | Use PKCE? | Checked | - | Token URI | `https://auth.spacetimedb.com/oidc/token` | - -:::warning -You do not need to enter the client secret here since the tool runs in the browser. -::: - -![OIDC Debugger Setup](/images/spacetimeauth/oidcdebugger-config.png) - ---- - -## Step 3: Run the Flow - -1. Click **Send Request**. -2. Log in via SpacetimeAuth using any configured providers. -3. You’ll be redirected back to OIDC Debugger with an authorization code. -4. OIDC Debugger will automatically exchange the code for tokens and display the - results. - ![OIDC Debugger results](/images/spacetimeauth/oidcdebugger-results.png) - ---- - -## Step 4: Inspect your tokens - -Depending on the scopes you requested, you should receive an ID token like this: -![OIDC Debugger Result](/images/spacetimeauth/jwtio.png) - -You can decode the ID token using any JWT decoder (e.g. [jwt.io](https://jwt.io/)) -to see the claims included. For example: - -```json -{ - "sub": "user_ergqg1q5eg15fdd54", - "project_id": "project_xyz123", - "email": "user@example.com", - "email_verified": true, - "preferred_username": "exampleuser", - "first_name": "Example", - "last_name": "User", - "name": "Example User" -} -``` diff --git a/docs/docs/12-SpacetimeAuth/05-react-integration.md b/docs/docs/12-SpacetimeAuth/05-react-integration.md deleted file mode 100644 index 70a65a17a71..00000000000 --- a/docs/docs/12-SpacetimeAuth/05-react-integration.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: React Integration -slug: /spacetimeauth/react-integration ---- - -# React Integration - -:::warning - -SpacetimeAuth is currently in beta, some features may not be available yet or may change in the future. You might encounter bugs or issues while using the service. Please report any problems you encounter to help us improve SpacetimeAuth. - -::: - -This guide will walk you through integrating SpacetimeAuth into a React -application using the [react-oidc-context](https://www.npmjs.com/package/react-oidc-context) -library. -This library provides a simple way to handle OpenID Connect (OIDC) authentication -in React. - -## Prerequisites - -1. Create a SpacetimeAuth project and configure a client as described in the - [Getting Started](/spacetimeauth/creating-a-project) and - [Configuration](/spacetimeauth/configuring-a-project) guides. -2. Have a React application set up. You can use Create React App or any other - React framework. -3. Install the `react-oidc-context` package in your React application: - -## Configuring react-oidc-context - -### 1. Add an OIDC configuration object - -Create an OIDC configuration object with your SpacetimeAuth project details. -Make sure to replace `YOUR_CLIENT_ID` with the actual client ID from your -SpacetimeAuth dashboard. - -```tsx -const oidcConfig = { - authority: 'https://auth.spacetimedb.com/oidc', - client_id: 'YOUR_CLIENT_ID', - redirect_uri: `${window.location.origin}/callback`, // Where the user is redirected after login - post_logout_redirect_uri: window.location.origin, // Where the user is redirected after logout - scope: 'openid profile email', - response_type: 'code', - automaticSilentRenew: true, -}; -``` - -### 2. Create a debug component - -This component will log various authentication events and state changes to -the console for debugging purposes. - -```tsx -export function OidcDebug() { - const auth = useAuth(); - - useEffect(() => { - const ev = auth.events; - - const onUserLoaded = (u: any) => - console.log('[OIDC] userLoaded', u?.profile?.sub, u); - const onUserUnloaded = () => console.log('[OIDC] userUnloaded'); - const onAccessTokenExpiring = () => - console.log('[OIDC] accessTokenExpiring'); - const onAccessTokenExpired = () => console.log('[OIDC] accessTokenExpired'); - const onSilentRenewError = (e: any) => - console.warn('[OIDC] silentRenewError', e); - const onUserSignedOut = () => console.log('[OIDC] userSignedOut'); - - ev.addUserLoaded(onUserLoaded); - ev.addUserUnloaded(onUserUnloaded); - ev.addAccessTokenExpiring(onAccessTokenExpiring); - ev.addAccessTokenExpired(onAccessTokenExpired); - ev.addSilentRenewError(onSilentRenewError); - ev.addUserSignedOut(onUserSignedOut); - - return () => { - ev.removeUserLoaded(onUserLoaded); - ev.removeUserUnloaded(onUserUnloaded); - ev.removeAccessTokenExpiring(onAccessTokenExpiring); - ev.removeAccessTokenExpired(onAccessTokenExpired); - ev.removeSilentRenewError(onSilentRenewError); - ev.removeUserSignedOut(onUserSignedOut); - }; - }, [auth.events]); - - useEffect(() => { - console.log('[OIDC] state', { - isLoading: auth.isLoading, - isAuthenticated: auth.isAuthenticated, - error: auth.error?.message, - activeNavigator: auth.activeNavigator, - user: !!auth.user, - }); - }, [ - auth.isLoading, - auth.isAuthenticated, - auth.error, - auth.activeNavigator, - auth.user, - ]); - - return null; -} -``` - -### 3. Wrap Your Application with AuthProvider - -Wrap your React application with the `AuthProvider` component to provide -authentication context. - -```tsx -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { AuthProvider, useAuth } from 'react-oidc-context'; -import App from './App'; -import { OidcDebug } from './OidcDebug'; - -const oidcConfig = {...}; - -function onSigninCallback() { - window.history.replaceState({}, document.title, window.location.pathname); -} - -const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); -root.render( - - - - -); - -``` - -### 4. Implement Authentication Logic in Your App - -In your main component (e.g., `App.tsx`), use the `useAutoSignin` hook to automatically - -sign in users if they are not authenticated. - -```tsx -import React from 'react'; - -import { useAuth, useAutoSignin } from 'react-oidc-context'; - -import './App.css'; - -function App() { - const auth = useAuth(); - - useAutoSignin(); - - if (auth.isLoading) { - return
Loading...
; - } - - if (auth.error) { - return
Error: {auth.error.message}
; - } - - if (!auth.isAuthenticated) { - return
Redirecting to login...
; - } - - return ( -
-
- Welcome, {auth.user?.profile.name} (id: {auth.user?.profile.sub})! - -
-
- ); -} -``` - -You're now set up to use SpacetimeAuth in your React application. When users -access your app, they will be redirected to the SpacetimeAuth login page for authentication. diff --git a/docs/docs/14-Internals/02-sats-json.md b/docs/docs/14-Internals/02-sats-json.md deleted file mode 100644 index 37b502f4ce4..00000000000 --- a/docs/docs/14-Internals/02-sats-json.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -slug: /sats-json ---- - -# SATS-JSON Data Format - -The Spacetime Algebraic Type System JSON format defines how Spacetime `AlgebraicType`s and `AlgebraicValue`s are encoded as JSON. Algebraic types and values are JSON-encoded for transport via the [HTTP Databases API](/http/database) and the WebSocket text protocol. Note that SATS-JSON is not self-describing, and so a SATS value represented in JSON requires knowing the value's schema to meaningfully understand it - for example, it's not possible to tell whether a JSON object with a single field is a `ProductValue` with one element or a `SumValue`. - -## Values - -### At a glance - -| Type | Description | -| ---------------- | ---------------------------------------------------------------- | -| `AlgebraicValue` | A value whose type may be any [`AlgebraicType`](#algebraictype). | -| `SumValue` | A value whose type is a [`SumType`](#sumtype). | -| `ProductValue` | A value whose type is a [`ProductType`](#producttype). | -| `BuiltinValue` | A value whose type is a [`BuiltinType`](#builtintype). | -| | | - -### `AlgebraicValue` - -```json -SumValue | ProductValue | BuiltinValue -``` - -### `SumValue` - -An instance of a [`SumType`](#sumtype). `SumValue`s are encoded as a JSON object with a single key, a non-negative integer tag which identifies the variant. The value associated with this key is the variant data. Variants which hold no data will have an empty array as their value. - -The tag is an index into the [`SumType.variants`](#sumtype) array of the value's [`SumType`](#sumtype). - -```json -{ - "": AlgebraicValue -} -``` - -The tag may also be the name of one of the variants. - -### `ProductValue` - -An instance of a [`ProductType`](#producttype). `ProductValue`s are encoded as JSON arrays. Each element of the `ProductValue` array is of the type of the corresponding index in the [`ProductType.elements`](#producttype) array of the value's [`ProductType`](#producttype). - -```json -array -``` - -`ProductValue`s may also be encoded as a JSON object with the keys as the field -names of the `ProductValue` and the values as the corresponding -`AlgebraicValue`s. - -### `BuiltinValue` - -An instance of a [`BuiltinType`](#builtintype). `BuiltinValue`s are encoded as JSON values of corresponding types. - -```json -boolean | number | string | array | map -``` - -| [`BuiltinType`](#builtintype) | JSON type | -| ----------------------------- | ------------------------------------- | -| `Bool` | `boolean` | -| Integer types | `number` | -| Float types | `number` | -| `String` | `string` | -| Array types | `array` | -| Map types | `map` | - -All SATS integer types are encoded as JSON `number`s, so values of 64-bit and 128-bit integer types may lose precision when encoding values larger than 2⁵². - -## Types - -All SATS types are JSON-encoded by converting them to an `AlgebraicValue`, then JSON-encoding that meta-value. - -### At a glance - -| Type | Description | -| --------------------------------------- | ------------------------------------------------------------------------------------ | -| [`AlgebraicType`](#algebraictype) | Any SATS type. | -| [`SumType`](#sumtype) | Sum types, i.e. tagged unions. | -| [`ProductType`](#producttype) | Product types, i.e. structures. | -| [`BuiltinType`](#builtintype) | Built-in and primitive types, including booleans, numbers, strings, arrays and maps. | -| [`AlgebraicTypeRef`](#algebraictyperef) | An indirect reference to a type, used to implement recursive types. | - -#### `AlgebraicType` - -`AlgebraicType` is the most general meta-type in the Spacetime Algebraic Type System. Any SATS type can be represented as an `AlgebraicType`. `AlgebraicType` is encoded as a tagged union, with variants for [`SumType`](#sumtype), [`ProductType`](#producttype), [`BuiltinType`](#builtintype) and [`AlgebraicTypeRef`](#algebraictyperef). - -```json -{ "Sum": SumType } -| { "Product": ProductType } -| { "Builtin": BuiltinType } -| { "Ref": AlgebraicTypeRef } -``` - -#### `SumType` - -The meta-type `SumType` represents sum types, also called tagged unions or Rust `enum`s. A sum type has some number of variants, each of which has an `AlgebraicType` of variant data, and an optional string discriminant. For each instance, exactly one variant will be active. The instance will contain only that variant's data. - -A `SumType` with zero variants is called an empty type or never type because it is impossible to construct an instance. - -Instances of `SumType`s are [`SumValue`s](#sumvalue), and store a tag which identifies the active variant. - -```json -// SumType: -{ - "variants": array, -} - -// SumTypeVariant: -{ - "algebraic_type": AlgebraicType, - "name": { "some": string } | { "none": [] } -} -``` - -### `ProductType` - -The meta-type `ProductType` represents product types, also called structs or tuples. A product type has some number of fields, each of which has an `AlgebraicType` of field data, and an optional string field name. Each instance will contain data for all of the product type's fields. - -A `ProductType` with zero fields is called a unit type because it has a single instance, the unit, which is empty. - -Instances of `ProductType`s are [`ProductValue`s](#productvalue), and store an array of field data. - -```json -// ProductType: -{ - "elements": array, -} - -// ProductTypeElement: -{ - "algebraic_type": AlgebraicType, - "name": { "some": string } | { "none": [] } -} -``` - -### `BuiltinType` - -The meta-type `BuiltinType` represents SATS primitive types: booleans, integers, floating-point numbers, strings, arrays and maps. `BuiltinType` is encoded as a tagged union, with a variant for each SATS primitive type. - -SATS integer types are identified by their signedness and width in bits. SATS supports the same set of integer types as Rust, i.e. 8, 16, 32, 64 and 128-bit signed and unsigned integers. - -SATS floating-point number types are identified by their width in bits. SATS supports 32 and 64-bit floats, which correspond to [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) single- and double-precision binary floats, respectively. - -SATS array and map types are homogeneous, meaning that each array has a single element type to which all its elements must conform, and each map has a key type and a value type to which all of its keys and values must conform. - -```json -{ "Bool": [] } -| { "I8": [] } -| { "U8": [] } -| { "I16": [] } -| { "U16": [] } -| { "I32": [] } -| { "U32": [] } -| { "I64": [] } -| { "U64": [] } -| { "I128": [] } -| { "U128": [] } -| { "F32": [] } -| { "F64": [] } -| { "String": [] } -| { "Array": AlgebraicType } -| { "Map": { - "key_ty": AlgebraicType, - "ty": AlgebraicType, - } } -``` - -### `AlgebraicTypeRef` - -`AlgebraicTypeRef`s are JSON-encoded as non-negative integers. These are indices into a typespace, like the one returned by the [`GET /v1/database/:name_or_identity/schema` HTTP endpoint](/http/database#get-v1databasename_or_identityschema). diff --git a/docs/docs/14-Internals/03-bsatn.md b/docs/docs/14-Internals/03-bsatn.md deleted file mode 100644 index 2115e44b5a2..00000000000 --- a/docs/docs/14-Internals/03-bsatn.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: BSATN Data Format -slug: /bsatn ---- - -# Binary SATN Format (BSATN) - -The Spacetime Algebraic Type Notation binary (BSATN) format defines -how Spacetime `AlgebraicValue`s and friends are encoded as byte strings. - -Algebraic values and product values are BSATN-encoded for e.g., -module-host communication and for storing row data in the database. - -## Notes on notation - -In this reference, we give a formal definition of the format. -To do this, we use inductive definitions, and define the following notation: - -- `bsatn(x)` denotes a function converting some value `x` to a list of bytes. -- `a: B` means that `a` is of type `B`. -- `Foo(x)` denotes extracting `x` out of some variant or type `Foo`. -- `a ++ b` denotes concatenating two byte lists `a` and `b`. -- `bsatn(A) = bsatn(B) | ... | bsatn(Z)` where `B` to `Z` are variants of `A` - means that `bsatn(A)` is defined as e.g., - `bsatn(B)`, `bsatn(C)`, .., `bsatn(Z)` depending on what variant of `A` it was. -- `[]` denotes the empty list of bytes. - -## Values - -### At a glance - -| Type | Description | -| ----------------------------------- | ---------------------------------------------------- | -| [`AlgebraicValue`](#algebraicvalue) | A value of any type. | -| [`SumValue`](#sumvalue) | A value of a sum type, i.e. an enum or tagged union. | -| [`ProductValue`](#productvalue) | A value of a product type, i.e. a struct or tuple. | - -### `AlgebraicValue` - -The BSATN encoding of an `AlgebraicValue` defers to the encoding of each variant: - -```fsharp -bsatn(AlgebraicValue) - = bsatn(SumValue) - | bsatn(ProductValue) - | bsatn(ArrayValue) - | bsatn(String) - | bsatn(Bool) - | bsatn(U8) | bsatn(U16) | bsatn(U32) | bsatn(U64) | bsatn(U128) | bsatn(U256) - | bsatn(I8) | bsatn(I16) | bsatn(I32) | bsatn(I64) | bsatn(I128) | bsatn(I256) - | bsatn(F32) | bsatn(F64) -``` - -Algebraic values include sums, products, arrays, strings, and primitives types. -The primitive types include booleans, unsigned and signed integers up to 256-bits, and floats, both single and double precision. - -### `SumValue` - -An instance of a sum type, i.e. an enum or tagged union. -`SumValue`s are binary-encoded as `bsatn(tag) ++ bsatn(variant_data)` -where `tag: u8` is an index into the `SumType.variants` -array of the value's `SumType`, -and where `variant_data` is the data of the variant. -For variants holding no data, i.e., of some zero sized type, -`bsatn(variant_data) = []`. - -### `ProductValue` - -An instance of a product type, i.e. a struct or tuple. -`ProductValue`s are binary encoded as: - -```fsharp -bsatn(elems) = bsatn(elem_0) ++ .. ++ bsatn(elem_n) -``` - -Field names are not encoded. - -### `ArrayValue` - -The encoding of an `ArrayValue` is: - -``` -bsatn(ArrayValue(a)) - = bsatn(len(a) as u32) - ++ bsatn(normalize(a)_0) - ++ .. - ++ bsatn(normalize(a)_n) -``` - -where `normalize(a)` for `a: ArrayValue` converts `a` to a list of `AlgebraicValue`s. - -### Strings - -For strings, the encoding is defined as: - -```fsharp -bsatn(String(s)) = bsatn(len(s) as u32) ++ bsatn(utf8_to_bytes(s)) -``` - -That is, the BSATN encoding is the concatenation of - -- the bsatn of the string's length as a `u32` integer byte -- the utf8 representation of the string as a byte array - -### Primitives - -For the primitive variants of `AlgebraicValue`, the BSATN encodings are:s - -```fsharp -bsatn(Bool(false)) = [0] -bsatn(Bool(true)) = [1] -bsatn(U8(x)) = [x] -bsatn(U16(x: u16)) = to_little_endian_bytes(x) -bsatn(U32(x: u32)) = to_little_endian_bytes(x) -bsatn(U64(x: u64)) = to_little_endian_bytes(x) -bsatn(U128(x: u128)) = to_little_endian_bytes(x) -bsatn(U256(x: u256)) = to_little_endian_bytes(x) -bsatn(I8(x: i8)) = to_little_endian_bytes(x) -bsatn(I16(x: i16)) = to_little_endian_bytes(x) -bsatn(I32(x: i32)) = to_little_endian_bytes(x) -bsatn(I64(x: i64)) = to_little_endian_bytes(x) -bsatn(I128(x: i128)) = to_little_endian_bytes(x) -bsatn(I256(x: i256)) = to_little_endian_bytes(x) -bsatn(F32(x: f32)) = bsatn(f32_to_raw_bits(x)) // lossless conversion -bsatn(F64(x: f64)) = bsatn(f64_to_raw_bits(x)) // lossless conversion -bsatn(String(s)) = bsatn(len(s) as u32) ++ bsatn(bytes(s)) -``` - -Where - -- `f32_to_raw_bits(x)` extracts the raw bits of `x: f32` to `u32` -- `f64_to_raw_bits(x)` extracts the raw bits of `x: f64` to `u64` - -## Types - -All SATS types are BSATN-encoded by converting them to an `AlgebraicValue`, -then BSATN-encoding that meta-value. - -See [the SATN JSON Format](/sats-json) -for more details of the conversion to meta values. -Note that these meta values are converted to BSATN and _not JSON_. diff --git a/docs/docs/15-Appendix/index.md b/docs/docs/15-Appendix/index.md deleted file mode 100644 index 40e34834d37..00000000000 --- a/docs/docs/15-Appendix/index.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -slug: /appendix ---- - -# Appendix - -## SEQUENCE - -For each table containing an `#[auto_inc]` column, SpacetimeDB creates a sequence number generator behind the scenes, which functions similarly to `postgres`'s `SEQUENCE`. - -### How It Works - -:::warning - -Sequence number generation is not transactional. - -::: - -- Sequences in SpacetimeDB use Rust’s `i128` integer type. -- The field type marked with `#[auto_inc]` is cast to `i128` and increments by `1` for each new row. -- Sequences are pre-allocated in chunks of `4096` to speed up number generation, and then are only persisted to disk when the pre-allocated chunk is exhausted. -- Numbers are incremented even if a transaction is later rolled back. -- Unused numbers are not reclaimed, meaning sequences may have _gaps_. -- If the server restarts or a transaction rolls back, the sequence continues from the next pre-allocated chunk + `1`: - -**Example:** - -```rust -#[spacetimedb::table(name = users, public)] -struct Users { - #[auto_inc] - user_id: u64, - name: String, -} - -#[spacetimedb::reducer] -pub fn insert_user(ctx: &ReducerContext, count: u8) { - for i in 0..count { - let name = format!("User {}", i); - ctx.db.users().insert(Users { user_id: 0, name }); - } - // Query the table to see the effect of the `[auto_inc]` attribute: - for user in ctx.db.users().iter() { - log::info!("User: {:?}", user); - } -} -``` - -Then: - -```bash -❯ cargo run --bin spacetimedb-cli call sample insert_user 3 - -❯ spacetimedb-cli logs sample -... -.. User: Users { user_id: 1, name: "User 0" } -.. User: Users { user_id: 2, name: "User 1" } -.. User: Users { user_id: 3, name: "User 2" } - -# Database restart, then - -❯ cargo run --bin spacetimedb-cli call sample insert_user 1 - -❯ spacetimedb-cli logs sample -... -.. User: Users { user_id: 3, name: "User 2" } -.. User: Users { user_id: 4098, name: "User 0" } -```