Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions codex-plugin/plugins/spacetimedb/skills/cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ spacetime generate --lang typescript|csharp|rust --out-dir ./bindings --module-p
spacetime generate --lang unrealcpp --uproject-dir ./MyGame --module-path ./server --unreal-module-name MyGame
```

### Client and Module Bindings

`spacetime generate --bindings client` (default) generates bindings for clients connecting to a database. `--bindings module` generates receiver bindings used inside a **calling database module** to call reducers in another **receiving database** through asynchronous IDC. Rust and TypeScript module bindings are supported; C#/C++ module bindings are not implemented yet. `unrealcpp` is client-only; `cpp` is accepted only in module mode.

In `spacetime.json`, each calling database's non-inherited `dependencies` array requires its own `module-path` and dependency `name` plus receiving-module `module-path` or `database`. A `database` reference resolves another target's local source/build settings in the same config; it does not fetch a running database's schema. A direct receiving module source takes precedence. Dependencies do not themselves publish receiving databases or inherit the calling module's build settings. `generate <calling-database>` processes both its regular `generate` entries (client bindings by default) and `dependencies` (module bindings automatically); no `--bindings` flag or second `generate` entry is needed for a dependency. For dependencies, module binding language defaults to the calling module's language and Rust/TypeScript output defaults to `<calling module-path>/src/remote_bindings`. Explicit config `out-dir` is config-relative. A regular `generate` entry can select `"bindings": "module"`, but does not use dependency language or output defaults. The CLI `--bindings` override is allowed only when a single generation entry is selected.

`dev` generates only root dependencies before building the calling module, and watches their source paths. Child targets are available for source lookup, not recursive dependency generation. `--skip-generate` skips receiver/client generation and receiver-only watching; `--skip-publish` does not. See https://spacetimedb.com/docs/cli-reference/spacetime-json#receiver-dependencies for examples.

### Publishing & Deployment

```bash
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/subcommands/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ pub fn cli() -> clap::Command {
.long("bindings")
.value_parser(clap::value_parser!(BindingKind))
.default_value("client")
.help("The kind of bindings to generate (module bindings are not implemented yet)"),
.help("The kind of bindings to generate (client applications or inter-database module calls)"),
)
.arg(
Arg::new("build_options")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ After completing setup, `spacetime dev`:

Your database will be available at `https://maincloud.spacetimedb.com`.

### Receiver dependencies

For inter-database calls, the **calling database** needs module bindings for the **receiving database** whose reducers it calls. These are distinct from the client bindings used by clients. Rust and TypeScript modules support receiver bindings.

When the root database in `spacetime.json` declares `dependencies`, `dev` generates those receiver bindings **before** building the calling module, including on rebuilds. It watches receiving module source paths as well as published module sources, even when the receiving modules are outside the calling module directory. Child targets may provide receiving module source settings, but their own dependency arrays are not processed by `dev`.

`--skip-generate` skips both receiver and client binding generation and removes receiver-only watch paths; it does not supply missing bindings. `--skip-publish` does not skip generation. See the [receiver dependency reference](/cli-reference/spacetime-json#receiver-dependencies) for configuration and language support.

### Client Development Server

`spacetime dev` can automatically run your client's development server alongside the SpacetimeDB module. This is configured via the `spacetime.json` file in your project root:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ Start development mode with auto-regenerate client module bindings, auto-rebuild
* `--module-path <MODULE-PATH>` — Path to the SpacetimeDB server module, relative to current directory. Defaults to `<project-path>/spacetimedb`.
* `--client-lang <CLIENT-LANG>` — The programming language for the generated client module bindings (e.g., typescript, csharp, rust, unrealcpp). If not specified, it will be detected from the project.

Possible values: `csharp`, `typescript`, `rust`, `unrealcpp`
Possible values: `csharp`, `typescript`, `rust`, `cpp`, `unrealcpp`

* `-s`, `--server <SERVER>` — The nickname, host name or URL of the server to publish to
* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com).
Expand Down Expand Up @@ -468,7 +468,13 @@ Run `spacetime help generate` for more detailed information.

* `-l`, `--lang <LANG>` — The language to generate

Possible values: `csharp`, `typescript`, `rust`, `unrealcpp`
Possible values: `csharp`, `typescript`, `rust`, `cpp`, `unrealcpp`

* `--bindings <BINDINGS>` — The kind of bindings to generate (client applications or inter-database module calls)

Default value: `client`

Possible values: `client`, `module`

* `--build-options <BUILD_OPTIONS>` — Options to pass to the build command, for example --build-options='--lint-dir='

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ These fields can appear at any level (root or child):
| `num-replicas` | number | Yes | Number of database replicas |
| `anonymous` | boolean | Yes | Use anonymous identity |
| `organization` | string | Yes | Organization name or identity |
| `dependencies` | array | No | Receiver binding inputs for this calling database (see [Receiver dependencies](#receiver-dependencies)) |
| `generate` | array | No | Generate targets (see [Generate configuration](#generate-configuration)) |
| `children` | array | No | Child database entities (see [Children and inheritance](#children-and-inheritance)) |
| `dev` | object | No | Dev server configuration, root-level only (see [`spacetime dev` configuration](#spacetime-dev-configuration)) |
Expand All @@ -52,14 +53,15 @@ The `generate` key is an array of objects. Each object configures bindings gener

| Field | Type | Description |
|-------|------|-------------|
| `language` | string | Target language: `typescript`, `csharp`, `rust`, `unrealcpp` (required) |
| `language` | string | Target language: `typescript`, `csharp`, `rust`, `unrealcpp`; `cpp` is reserved for module bindings |
| `bindings` | string | `client` (default) for application clients, or `module` for inter-database calls |
| `out-dir` | string | Output directory for generated files |
| `namespace` | string | C# namespace (`csharp` only) |
| `unreal-module-name` | string | Unreal module name (`unrealcpp` only) |
| `uproject-dir` | string | Unreal project directory (`unrealcpp` only) |
| `include-private` | boolean | Include private tables in generated code |

Generate entries use the `module-path` (or `bin-path`/`js-path`) from their parent entity to determine which module to build and generate from.
By default, generate entries use the `module-path` (or `bin-path`/`js-path`) from their parent entity to determine which module to build and generate from. An entry can specify its own module source instead.

When `spacetime generate` runs, it deduplicates by module path. If multiple databases share the same module and generate config (for example, via inheritance), bindings are generated once.

Expand All @@ -80,6 +82,78 @@ When `spacetime generate` runs, it deduplicates by module path. If multiple data
}
```

## Receiver dependencies

`spacetime generate` produces two kinds of bindings:

- **Client bindings** are used by clients to connect to a database, subscribe to data, and call its reducers.
- **Module bindings**, also called **receiver bindings**, are used inside one database module to make asynchronous inter-database communication (IDC) calls to another database's reducers. Rust and TypeScript modules support these bindings; C# and C++ module bindings are not implemented yet.

With `spacetime.json`, put client bindings in `generate` and receiver bindings in `dependencies`. Running `spacetime generate` processes both arrays: regular `generate` entries default to client bindings, and dependencies automatically generate module bindings. No `--bindings` flag or second `generate` entry is needed for a dependency.

A regular `generate` entry can also set `"bindings": "module"` explicitly. Unlike a dependency, it uses its parent target's module source unless overridden; if its language or output directory is omitted, those defaults are resolved from the config directory rather than the calling module.

The **calling database** contains the code making the inter-database call. The **receiving database** contains the reducers being called. In the examples below, `lobby` is the calling database and `game-world` is the receiving database. Declare receiver binding inputs in the calling database's `dependencies` array.

Each calling database with dependencies must define `module-path`. Dependencies are not inherited by children.

| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Local dependency name passed to receiver binding generation; not the receiver's database name |
| `module-path` | One source required | Receiver source directory; takes precedence over `database` when both are set |
| `database` | One source required | Exact name of another target in the same config, used to look up its local module source; not a live-schema fetch |
| `out-dir` | No | Output directory, relative to the config directory |
| `build-options` | No | Receiver build options when using a direct `module-path` |
| `dotnet-version` | No | Receiver .NET SDK version when using a direct `module-path` |

### Receiver source without a publish target

A receiving module source does not need its own database target:

```json
{
"database": "lobby",
"module-path": "./lobby",
"dependencies": [
{
"name": "game_world",
"module-path": "./game_world",
"out-dir": "./lobby/src/remote_bindings"
}
]
}
```

Declaring a dependency does not publish the receiver. To make it a publish target too, define it under `children` and reference its `database`:

```json
{
"database": "lobby",
"module-path": "./lobby",
"dependencies": [
{ "name": "game_world", "database": "game-world" }
],
"children": [
{ "database": "game-world", "module-path": "./game_world" }
]
}
```

A `database` reference uses the target's `module-path`, `bin-path`, or `js-path`, and its resolved receiver build options and .NET SDK version. It fails if no matching target or module source exists. A dependency does not inherit the calling module's build options or .NET SDK version.

`spacetime generate lobby` selects the calling database's regular `generate` entries and dependencies; without a database filter, generation includes all targets' entries. For `dependencies` entries, the module binding language is inferred from the calling module, not the external client project. Rust and TypeScript dependency bindings default to `<calling module-path>/src/remote_bindings`.

### Explicit module-binding request

Without configuration, select the binding kind with `--bindings client` (the default) or `--bindings module`. When using `spacetime.json`, `--bindings` can override the binding kind only if a single generation entry is selected; it is rejected when multiple entries are selected. Prefer the config fields described above for configured generation. Here `--module-path` identifies the **receiving module**, while `--out-dir` explicitly places output in the **calling module** project:

```bash
spacetime generate --no-config --bindings module --lang rust \
--module-path ./game_world --out-dir ./lobby/src/remote_bindings
```

This generates Rust module bindings for `game_world` in the `lobby` module's source tree. Use `--lang typescript` for a TypeScript calling module. `unrealcpp` is client-only; the CLI accepts `cpp` only with `--bindings module`, but C++ module binding generation is not implemented yet.

## Children and inheritance

The `children` array defines additional database targets. Each child inherits most fields from the root by default.
Expand All @@ -91,6 +165,7 @@ All fields listed in the [Fields reference](#fields-reference) with "Yes" in the
The following fields are never inherited:

- `database`: each child must define its own.
- `dependencies`: receiver binding inputs belong to each calling database.
- `generate`: tied to a specific module and output location. Inheriting generate targets is redundant when the child shares the parent's module (deduplication already handles it) and dangerous when the child uses a different module (two modules would write bindings into the same output directory).
- `children`: structural, not a database property.
- `dev`: root-level only.
Expand Down Expand Up @@ -170,6 +245,14 @@ When running `spacetime dev`:

The `--server` flag overrides the server for all databases. The `--skip-publish` flag skips step 3, and `--skip-generate` skips step 2.

#### Additional behavior with receiver dependencies

If the root database declares `dependencies`, `dev` generates its receiver bindings before building that database's module, both on startup and on rebuilds. This adds a pre-build step without changing the normal development workflow above.

Receiving module source paths are watched even when they are outside the calling module directory or not selected for publishing. Child targets can supply receiving module sources for lookup, but `dev` does not process their own `dependencies` arrays.

`--skip-generate` skips both receiver and client binding generation and excludes receiver-only sources from watching. It does not create any bindings the calling module needs to compile. `--skip-publish` skips publishing, not binding generation.

### Config auto-generation

If no config file exists, `spacetime dev` generates a `spacetime.json` with the server and module path, and a `spacetime.local.json` with the database name. The command prompts for the database name (pre-filled with the directory name as the default).
Expand Down
8 changes: 8 additions & 0 deletions skills/cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ spacetime generate --lang typescript|csharp|rust --out-dir ./bindings --module-p
spacetime generate --lang unrealcpp --uproject-dir ./MyGame --module-path ./server --unreal-module-name MyGame
```

### Client and Module Bindings

`spacetime generate --bindings client` (default) generates bindings for clients connecting to a database. `--bindings module` generates receiver bindings used inside a **calling database module** to call reducers in another **receiving database** through asynchronous IDC. Rust and TypeScript module bindings are supported; C#/C++ module bindings are not implemented yet. `unrealcpp` is client-only; `cpp` is accepted only in module mode.

In `spacetime.json`, each calling database's non-inherited `dependencies` array requires its own `module-path` and dependency `name` plus receiving-module `module-path` or `database`. A `database` reference resolves another target's local source/build settings in the same config; it does not fetch a running database's schema. A direct receiving module source takes precedence. Dependencies do not themselves publish receiving databases or inherit the calling module's build settings. `generate <calling-database>` processes both its regular `generate` entries (client bindings by default) and `dependencies` (module bindings automatically); no `--bindings` flag or second `generate` entry is needed for a dependency. For dependencies, module binding language defaults to the calling module's language and Rust/TypeScript output defaults to `<calling module-path>/src/remote_bindings`. Explicit config `out-dir` is config-relative. A regular `generate` entry can select `"bindings": "module"`, but does not use dependency language or output defaults. The CLI `--bindings` override is allowed only when a single generation entry is selected.

`dev` generates only root dependencies before building the calling module, and watches their source paths. Child targets are available for source lookup, not recursive dependency generation. `--skip-generate` skips receiver/client generation and receiver-only watching; `--skip-publish` does not. See https://spacetimedb.com/docs/cli-reference/spacetime-json#receiver-dependencies for examples.

### Publishing & Deployment

```bash
Expand Down
Loading