# Description of Changes
Fixes a bug in client disconnect logic that would mark a client's views
as dropped(unsubscribed). However it was marking the identity's views as
dropped, not the connection. So if an identity had multiple connections
open, each subscribing to different views, and one of them disconnected,
the subscriptions for the other connections would break. The observed
behavior would be that they would stop receiving subscription updates.
This could potentially lead to their client cache getting into a
corrupted state.
Now, instead of dropping all of the views for a particular identity on
disconnect, we drop only the views for that particular connection. And
when I say drop, what I really mean is decrement. A view is not dropped
completely unless it no longer had any subscribers.
# API and ABI breaking changes
None
# Expected complexity level and risk
2
# Testing
Regression smoketest was added
# Description of Changes
Query builder views return a subset of rows from a physical table. If
that table has a primary key, then so should the view. What this means
concretely is that the view should expose the same api as the table,
specifically as it relates to the primary key column.
With that in mind, this patch commits the following changes:
1. Annotates `ViewDef` with a `primary_key`
2. Updates the return type of query builder views in the raw module def
to a special product type
3. Adds an index for the primary key on the view's backing table
4. Updates the query planner to use this index
5. Updates rust client codegen to generate `on_update` for such views
# API and ABI breaking changes
None
Old `impl Query` views compiled with an older version of SpacetimeDB
will continue to work as they did before - without a primary key.
# Expected complexity level and risk
3
# Testing
- [x] New rust sdk integration suite exercising `on_update` for PK views
and semijoin scenarios
- [x] Smoketests for PK views and semijoin scenarios
# Description of Changes
It doesn't have permission to run on external PRs, but that's kinda okay
since external PRs are rarely authored by clockwork-labs-bot anyway.
# API and ABI breaking changes
None.
# Expected complexity level and risk
1
# Testing
None
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
`and` and `or` are scoped to only a single table. So using them in a
semijoin with two tables will fail to compile. Previously this resulted
in a gnarly error message (see #4586). This patch improves the error
message.
# API and ABI breaking changes
None
# Expected complexity level and risk
2
# Testing
Adds error message assertion tests.
Fixes#4591
## Root Cause
The `isFixedSizeProduct` fast-path deserializer in `algebraic_type.ts`
maps `Bool` to `view.getUint8()`, which returns a **number** (0 or 1)
instead of a JavaScript **boolean**. The slow path (`reader.readBool()`)
correctly converts via `!== 0`.
This means any product type containing only fixed-size primitives
(bools, ints, floats) hits the fast path and returns numbers for boolean
fields. Products containing strings, arrays, or nested objects go
through the slow path and work correctly.
This explains the inconsistency in the issue: a flat `{ foo: bool }`
returns `{ foo: 1 }`, but adding a nested object pushes it to the slow
path where `foo` becomes `true` (though the nested bool still hits the
fast path within its own product).
## Fix
Special-case `Bool` in the fast-path code generation to emit:
```js
result.foo = view.getUint8(reader.offset) !== 0;
```
instead of:
```js
result.foo = view.getUint8(reader.offset);
```
One-line change in the ternary within `makeDeserializer`.
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
# Description of Changes
<!-- Please describe your change, mention any related tickets, and so on
here. -->
- Bumps version to 2.0.4
# API and ABI breaking changes
None - this is just a version bump
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
1 - this is just a version bump
<!--
How complicated do you think these changes are? Grade on a scale from 1
to 5,
where 1 is a trivial change, and 5 is a deep-reaching and complex
change.
This complexity rating applies not only to the complexity apparent in
the diff,
but also to its interactions with existing and future code.
If you answered more than a 2, explain what is complex about the PR,
and what other components it interacts with in potentially concerning
ways. -->
# Testing
<!-- Describe any testing you've done, and any testing you'd like your
reviewers to do,
so that you're confident that all the changes work as expected! -->
- [x] License file has been updated with correct version + time
---------
Signed-off-by: John Detter <4099508+jdetter@users.noreply.github.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
# Description of Changes
This shouldn't actually be required in the long-term, because it runs on
new PRs and any label changes, but the case where it _is_ required? PRs
that are currently already open and merge in `master`. That triggers a
`synchronize` event but not any of the events that this check runs on.
Sigh.
# API and ABI breaking changes
None
# Expected complexity level and risk
1
# Testing
None
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
`cargo ci lint` was running `cargo fmt`, but that didn't pick up all
files. As a result, our pre-commit hook (which just runs `rustfmt` on
any changed `.rs` files) would change otherwise-untouched files in merge
commits.
This PR addresses that discrepancy by having `cargo ci lint` run
`rustfmt` on all tracked `.rs` files.
The entire diff is just `rustfmt` changes except for the changes in
`tools/ci/src/main.rs`.
# API and ABI breaking changes
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
2
# Testing
- [x] `cargo ci lint` fails
- [x] `cargo ci lint` passes after running `rustfmt` on everything
- [x] `cargo fmt --all` doesn't cause any diff after doing the above
---------
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
The CI check for the Do Not Merge label was only running on label-change
events, so if a PR never received a label (any label), the check would
never run. This meant that the check couldn't be marked required because
it was missing on PRs that were never labeled anything.
Additionally, the check was not configured to run properly in the merge
queue, so it would have blocked the merge queue if it were a required
check.
This PR fixes those issues by making the check run on more pull request
events, and by trivially succeeding in the merge queue.
This enables the check to be made required.
# API and ABI breaking changes
None. CI only.
# Expected complexity level and risk
1
# Testing
- [x] The check is now showing as run on this PR
- [x] The check still fails if I add a "do not merge" label
---------
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
Fixes two issues that would prevent updating a database while also
changing the host type:
- We never actually updated the `ModuleKind` in `st_module` (hardcoded
to wasm)
- We never actually honored the value from `st_module` when
instantiating a module
To do so, the `Program` type from the datastore crate now carries the
`ModuleKind`, forcing call sites to make a decision.
Small adjustments to the smoketests / guard crate where made when
writing the test for this.
# Expected complexity level and risk
1
# Testing
- [x] Added smoketest
# Description of Changes
These have been running alongside the Rust smoketests for a while, and
we feel fairly confident that the Rust ones are doing a good job.
(However, I leave them in the repo because we still use them elsewhere).
# API and ABI breaking changes
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
1
# Testing
None
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
## Bug
PR #4367 (login/logout UX overhaul) accidentally removed the early
`return Ok(())` after saving a token via `spacetime login --token`. This
caused the command to fall through into the web login flow
(`spacetimedb_login_and_save`), which fails when no browser or server is
available.
## Impact
All tests that use `spacetime login --token` are broken:
- 4 replication tests (`test_enable_disable_replication`,
`test_enable_replication_on_suspended_database`,
`test_enable_replication_fails_if_not_suspended`, `test_prefer_leader`)
- 5 teams tests (`test_permissions_clear_org`,
`test_permissions_delete_org`,
`test_org_permissions_mut_sql_org_members`,
`test_org_permissions_private_tables`,
`test_permissions_publish_org_members`)
## Fix
One line: restore `return Ok(())` after the `--token` branch.
---------
Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
# Description of Changes
The previous version ended up incurring two different entries on the PR
(one for the `pull_request` event and one for the `pull_request_review`
event). Both versions were marked "required", so PRs could end up in an
unmergeable state if one check had succeeded but the other had failed
(e.g. if you submitted a PR approval, the previous `pull_request`
version of the check would still be failed since it didn't refresh).
See the entries at top and bottom here:
<img width="481" height="225" alt="image"
src="https://github.com/user-attachments/assets/5b7a4302-6bc2-47e9-93c8-812cb9ece60b"
/>
This PR fixes it by only allowing the `pull_request_review` events. I
_think_ this covers all the cases, but I'm not sure.
# API and ABI breaking changes
None.
# Expected complexity level and risk
1
# Testing
I don't know how to test it really 🤷
---------
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
Audited all registered routes in `database.rs` and `identity.rs` against
the HTTP API documentation.
## Fixes
**database.md:**
- `/v1/database/:name_or_identity` publish: `POST` → `PUT` (code has
`db_put: put(publish::<S>)`)
- `DELETE` row in summary table: anchor pointed to POST section instead
of DELETE section
- Typo: `Updgrade` → `Upgrade` in WebSocket subscribe headers
**identity.md:**
- Removed `POST /v1/identity/:identity/set-email` — documented but has
no handler registered in `IdentityRoutes` (dead documentation)
## Routes in code but intentionally not documented (internal/unstable)
- `POST /v1/database/:noi/pre_publish`
- `PUT /v1/database/:noi/reset`
- `GET /v1/database/:noi/unstable/timestamp`
These are used internally by the CLI and are not part of the public API.
---------
Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
# Description of Changes
PRs created by `clockwork-labs-bot` require 2 approvals.
After merging, we would need to make this check required.
# API and ABI breaking changes
CI only.
# Expected complexity level and risk
1. This is copy-pasted and simplified from another repo that has the
same workflow.
# Testing
None
---------
Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
## Summary
Improves the `spacetime login` and `spacetime logout` UX to behave more
like standard CLI tools.
### `spacetime login`
**Before:** If already logged in, prints "You are already logged in" and
exits. User must manually run `logout` first.
**After:** If already logged in, automatically logs out the previous
session and proceeds with a fresh login. Prints the old identity being
logged out and the new identity on success.
```
$ spacetime login
Logged out of previous session (identity 0xabc...).
Opening https://spacetimedb.com/login/cli?token=... in your browser.
Waiting to hear response from the server...
Logged in with identity 0xdef...
```
### `spacetime logout`
**Before:** No output on success. Hard failure if offline.
**After:**
- Prints confirmation: `Logged out (identity 0xabc...).`
- Prints `You are not logged in.` if already logged out
- Best-effort server-side session invalidation with 5s timeout (prints
warning if offline instead of failing)
### Changes
- `login.rs`: Remove `spacetimedb_token_cached` early-return; instead
log out previous session and proceed. Show identity on login success.
- `logout.rs`: Add identity display, not-logged-in check, 5s timeout for
server call with warning on failure.
Note: This subsumes the offline fix from #4361.
---------
Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
## Summary
Adds a version update check to the `spacetimedb-update` proxy, so users
are notified when a newer version of SpacetimeDB is available.
## Changes
Adds `crates/update/src/update_notice.rs` — a lightweight update check
that runs in the proxy path before exec'ing the CLI:
- **Cache-based**: Stores the last check result in
`~/.spacetime/.update_check_cache`. Only hits the network once every 24
hours.
- **Non-blocking on cache hit**: If the cache is fresh, it's a single
file read — no network, no delay.
- **Short timeout**: When the cache is stale, makes a single HTTP
request to GitHub releases API with a 5-second timeout. Uses the same
`SPACETIME_UPDATE_RELEASES_URL` env var as `spacetime version upgrade`.
- **Best-effort**: Any failure (network, parse, file I/O) is silently
ignored. The update check never interferes with the user's command.
- **Uses semver**: Proper version comparison via the `semver` crate
(already a dependency).
## Output
When a newer version is available:
```
A new version of SpacetimeDB is available: v2.1.0 (current: v2.0.0)
Run `spacetime version upgrade` to update.
```
# Testing
- [x] I get a warning if my local version is less than 2.0.2
- [x] If I have a cached update check, I get the same error even if I
have no network connection
- [x] if the cache is old, the next command checks again
- [x] if I'm not connected and the cache is stale, I'm still able to use
the CLI
---------
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <bot@clockworklabs.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
The README was significantly out of date. This overhaul brings it in
line with the current docs and product state.
## What changed
**Content updates:**
- "What is SpacetimeDB" now mentions all 4 module languages (Rust, C#,
TypeScript, C++) instead of only Rust
- Removed the smart contracts comparison (outdated framing)
- Updated BitCraft description to match current docs wording
- Shortened the architecture description to be punchier
**New Quick Start section:**
- 5-step flow: install, login, init, dev, publish
- Introduces `spacetime dev` (the primary local development experience)
- Introduces `spacetime publish` to Maincloud
- Much more approachable than the old "Installation" wall of text
**New "How It Works" section:**
- Rust module code example showing tables and reducers
- TypeScript client code example showing `useTable` subscriptions
- Shows the core value prop in < 20 lines of code
**Language support:**
- Added TypeScript and C++ as server module languages
- Added Unreal Engine (C++) as a client SDK
- Listed all supported web frameworks (React, Next.js, Vue, Svelte,
Angular, Node.js, Bun, Deno)
- Fixed all doc links to current URL patterns (`/docs/quickstarts/...`)
**Build from source:**
- Collapsed into expandable `<details>` sections (macOS/Linux and
Windows)
- Removed duplicate Git for Windows instructions
- Still complete, just not the first thing you see
**Badges:**
- Added npm download count badge for the TypeScript SDK
**License:**
- Shortened to essentials with link to full license file
---------
Signed-off-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
# Description of Changes
We apparently have stale usage of `--project-path` in a ton of our
templates. This was renamed to `module-path` a while ago, but it looks
like that was only partially fixed in templates.
# API and ABI breaking changes
None
# Expected complexity level and risk
1
# Testing
I don't think it's _more_ broken 🤷
---------
Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
Adds implicit query builder conversions from `bool` to `BoolExpr` so
that you can write:
```rust
ctx.from.user().r#where(|u| u.online)
```
instead of
```rust
ctx.from.user().r#where(|u| u.online.eq(true))
```
Also removes `NullableCol` and `NullableIxCol` types from C# query
builder.
# API and ABI breaking changes
None
# Expected complexity level and risk
1
# Testing
Unit and smoketests
## Summary
This PR creates a comprehensive self-hosted JWT key rotation how-to for
SpacetimeDB with reproducible defaults, identity-preserving rotation
guidance, and host-scoped data migration runbooks. It is written for
both operators and automation workflows, with explicit guardrails for
production-like VM environments.
## Key Changes
- Adds a top-level assumptions/risk section for a multi-host topology
(`prod`, `test`, `dev`, optional `local`) including backup prerequisites
before rotation or sync.
- Defines an opinionated, end-to-end path contract for keys:
- host source: `./.generated/spacetimedb-keys`
- runtime mount: `/etc/spacetimedb`
- startup args pinned to `/etc/spacetimedb/id_ecdsa` and
`/etc/spacetimedb/id_ecdsa.pub`
- Includes reproducible quickstart scaffolding:
- OpenSSL commands for compatible ES256/P-256 key generation
- non-container and Docker startup examples
- rotation/verify command sequence and marker checks
- Documents one unified tooling surface around `spacetimedb-tooling.ts`
for:
- rotate (`--dry-run`, `--yes`)
- verify (`--verify-only`)
- token continuity (`--resign-token-only`)
- explicit token/key path overrides (`--publisher-cli-toml-path`,
`--private-key-path`)
- Clarifies 401 vs 403 behavior and why identity drift causes publish
failures after rotation.
- Covers all rotation strategies in one place:
- clean-slate rotation (stateless/dev)
- identity-preserving rotation (stateful)
- OIDC-backed identity model for production
- Adds host-scoped migration runbooks for staged sync (`prod -> test ->
dev`) with destination-side token re-signing semantics (run on
destination host context, including SSH examples).
- Explicitly separates conceptual topology guidance from currently
implemented sync primitives in reference tooling to avoid over-claiming.
- Adds operational guardrails for sync promotions:
- `rsync --delete` is destructive
- stop/start ordering around sync
- required acceptance gates: key parity, destination re-sign,
restart/redeploy, publish marker validation
- Expands verification and automation content:
- PEM/parity/fingerprint/newline checks
- AI/automation contract with inputs, outputs, required command order,
and success/failure markers
- troubleshooting flow for `InvalidSignature` and ownership mismatch
errors
## Why This Matters
The most common failure mode in self-hosted fleets is identity drift:
signatures validate, but `spacetime publish` still fails with `403
Forbidden` because destination ownership no longer matches the
publishing identity after key rotation or data movement.
The updated how-to makes these operator requirements explicit:
1. preserve identity continuity across rotations and host promotions
2. re-sign destination tokens after `rsync` in destination host context
3. treat restart/redeploy and publish markers as promotion gates
Following this runbook prevents regressions where key material is
correct but publish still fails due to stale signatures or owner
mismatch state.
## Why JWT Key Rotation Is Essential for Self-Hosters
In self-hosted deployments, operators are the signing authority.
Rotation is both a security control and an operational correctness
requirement.
### Security value
- reduces exposure window for compromised private keys
- invalidates old/stolen tokens after rotation
- mitigates risk from snapshots, backups, clones, and long-lived VM
access
### Operational value
- keeps key material consistent across multi-VM environments
- reduces publish outages after promotion/sync operations
- prevents avoidable `401`/`403` failures in routine release workflows
## Scope
- docs-only change
- public-safe examples only (no internal org names, private vault IDs,
or private secret prefixes)
- includes non-container and Docker workflows, self-publish/versioning
guidance, and marker-based validation (`PUBLISH_SUCCESS`,
`PUBLISH_FAILED`)
- includes data migration workflow guidance (`rsync` + destination
re-sign + restart/redeploy + publish checks)
---------
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
This pull request updates the template system to support richer metadata
and improves the interactive CLI experience for selecting templates. The
main changes are the addition of a `client_framework` field to template
metadata, a refactor of the templates JSON generation and parsing, and a
redesign of the interactive template selection flow to group templates
by language/framework and use fuzzy search for easier navigation.
<img width="407" height="409" alt="image"
src="https://github.com/user-attachments/assets/d3548505-80e8-4778-8bfb-71d5e3fe31e9"
/>
**Template Metadata and Serialization Improvements:**
* Added a new `client_framework` field to all template metadata files
(e.g., `.template.json`) and updated the Rust structs (`TemplateInfo`,
`TemplateDefinition`, etc.) to support this field, enabling more
descriptive and flexible template selection.
[[1]](diffhunk://#diff-5438edbe7b41e3e0a1a62ce0ebde2a833a0441d76c6ffdf16093a3cff258f462R3)
[[2]](diffhunk://#diff-34a44755a07373e9c2872db87a64c3fd752cfee7d8d536045909daf861ea4728R3)
[[3]](diffhunk://#diff-a826c5a2eac977cff44022d72856ec4b1af176ffbb50697f22857e1e3d478aa1R3)
[[4]](diffhunk://#diff-7a21474fbc5bbbd989e89f32aa8dcc25fcc6fe4ac43d418c42b2b38603d21714R3)
[[5]](diffhunk://#diff-d8ee9fd0ef15ed23f0f7f38e556d789bdefc6c37dab67fbfccebc513b9da871cR3)
[[6]](diffhunk://#diff-7224fd4300f9c5af7834ff5989a76a399ce9117c28f5d7e2c1fbd6bdaf45be1bR3)
[[7]](diffhunk://#diff-8b015b0ce6339c444ef5ef4dc5e862275d98967e5c189da37b51bdaa58443606R3)
[[8]](diffhunk://#diff-89e32f7fc9fc42863998d7651bc7fa1fdfb352e8ac3ef45792e3f1374052c9ddR3)
[[9]](diffhunk://#diff-8259d300fda149c968a68989aec2bc2afa479aded649ac6b8d3c4b277f610542R3)
[[10]](diffhunk://#diff-bb8b9202e51fb7038efa2f2bb23872afa02caf71ab3a0fb7f513ca7822c1b8faR3)
[[11]](diffhunk://#diff-18497b72a2306fc2560475e2548cfe1b96b6206c395380437ba1fafefd66e126L199-R221)
[[12]](diffhunk://#diff-18497b72a2306fc2560475e2548cfe1b96b6206c395380437ba1fafefd66e126R279)
* Refactored the templates JSON generation to use `serde` for
serialization, replacing manual string building, and ensured that
optional string fields serialize as empty strings when not present.
**CLI Interactive Template Selection Redesign:**
* Removed the previous "highlights" concept and reworked the interactive
selection to group templates by language/framework combinations, showing
counts and using fuzzy search for easier filtering.
[[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL38-L48)
[[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL772-R833)
[[3]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL813-L850)
* Updated the selection menus to allow users to pick a
language/framework group, then choose from multiple templates if
available, or opt to clone from GitHub or select "None."
[[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL772-R833)
[[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL813-L850)
* Improved language label formatting for better user experience in the
CLI prompt.
**Codebase Cleanup and API Changes:**
* Removed unused highlight-related structs and logic from the CLI,
simplifying the template fetching API to return only templates.
[[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL38-L48)
[[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL198-R198)
* Updated all template selection logic to use the new API and data
structures.
[[1]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL683-R679)
[[2]](diffhunk://#diff-7fcfe09d0f54dde6b2fc0cb2b9ff02ab064add692f5602d6f627fe3840e282caL748-R744)
These changes make template selection more intuitive and scalable as
more templates and frameworks are added.
---------
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
We run the "package CLI" job for every `master` commit, but I think we
basically never use those. Instead I added the work flow dispatch option
which can run as a one off if needed. (I didn't test it, but now that
it's added, we'll be able to fix it in a PR if needed).
# API and ABI breaking changes
None
# Expected complexity level and risk
1
# Testing
None
---------
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
## Summary
When hitting `/v1/schema` while a database is still loading (replaying
the log, running init reducers, etc.), the endpoint returned a 500 error
because the module host was not yet available.
## Changes
- Add `Host::wait_for_module(timeout)` in `crates/client-api/src/lib.rs`
-- polls `get_module_host` with exponential backoff (100ms, 200ms,
400ms, 800ms, 1s, 1s, ...) up to the given timeout
- Update the `/v1/schema` route to use `wait_for_module(10s)` instead of
the immediate `module()` call
If the database finishes loading within 10 seconds, the schema is
returned normally. If it does not load in time, the existing 500 error
is returned (same behavior as before, just delayed).
No other routes are changed -- this is scoped to the schema endpoint per
the issue description. Other routes (SQL, call, etc.) could adopt the
same pattern if needed.
Fixesclockworklabs/SpacetimeDBPrivate#2748
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
# Description of Changes
Just ignoring `*.local` so that I can commit e.g. custom shell configs.
# API and ABI breaking changes
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
1
# Testing
My `.fishrc.local` is no longer picked up 🤷
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
Add `?version=10` as an option to
`/v1/database/:name-or-identity/schema`, where previously only
`?version=9` was supported. This seems to have been forgotten when we
introduced `RawModuleDefV10`.
Also, in an unrelated minor fixup, fix a copy-paste error in a doc
comment in the V2 WebSocket format definition.
# API and ABI breaking changes
Additive extension to HTTP API.
# Expected complexity level and risk
1
# Testing
- [x] Did a local get against this route and got a JSON-ified
`RawModuleDefV10`:
```bash
$ curl http://localhost:3000/v1/database/chat-console-rs/schema?version=10
{"sections":[{"Typespace":{"types":[{"Product":{"elements":[{"name":{"some":"sender"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__identity__"},"algebraic_type":{"U256":[]}}]}}},{"name":{"some":"sent"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__timestamp_micros_since_unix_epoch__"},"algebraic_type":{"I64":[]}}]}}},{"name":{"some":"text"},"algebraic_type":{"String":[]}}]}},{"Product":{"elements":[{"name":{"some":"identity"},"algebraic_type":{"Product":{"elements":[{"name":{"some":"__identity__"},"algebraic_type":{"U256":[]}}]}}},{"name":{"some":"name"},"algebraic_type":{"Sum":{"variants":[{"name":{"some":"some"},"algebraic_type":{"String":[]}},{"name":{"some":"none"},"algebraic_type":{"Product":{"elements":[]}}}]}}},{"name":{"some":"online"},"algebraic_type":{"Bool":[]}}]}}]}},{"Types":[{"source_name":{"scope":[],"source_name":"Message"},"ty":0,"custom_ordering":true},{"source_name":{"scope":[],"source_name":"User"},"ty":1,"custom_ordering":true}]},{"Tables":[{"source_name":"message","product_type_ref":0,"primary_key":[],"indexes":[],"constraints":[],"sequences":[],"table_type":{"User":[]},"table_access":{"Public":[]},"default_values":[],"is_event":false},{"source_name":"user","product_type_ref":1,"primary_key":[0],"indexes":[{"source_name":{"some":"user_identity_idx_btree"},"accessor_name":{"some":"identity"},"algorithm":{"BTree":[0]}}],"constraints":[{"source_name":{"some":"user_identity_key"},"data":{"Unique":{"columns":[0]}}}],"sequences":[],"table_type":{"User":[]},"table_access":{"Public":[]},"default_values":[],"is_event":false}]},{"Reducers":[{"source_name":"identity_connected","params":{"elements":[]},"visibility":{"Private":[]},"ok_return_type":{"Product":{"elements":[]}},"err_return_type":{"String":[]}},{"source_name":"identity_disconnected","params":{"elements":[]},"visibility":{"Private":[]},"ok_return_type":{"Product":{"elements":[]}},"err_return_type":{"String":[]}},{"source_name":"init","params":{"elements":[]},"visibility":{"Private":[]},"ok_return_type":{"Product":{"elements":[]}},"err_return_type":{"String":[]}},{"source_name":"send_message","params":{"elements":[{"name":{"some":"text"},"algebraic_type":{"String":[]}}]},"visibility":{"ClientCallable":[]},"ok_return_type":{"Product":{"elements":[]}},"err_return_type":{"String":[]}},{"source_name":"set_name","params":{"elements":[{"name":{"some":"name"},"algebraic_type":{"String":[]}}]},"visibility":{"ClientCallable":[]},"ok_return_type":{"Product":{"elements":[]}},"err_return_type":{"String":[]}}]},{"LifeCycleReducers":[{"lifecycle_spec":{"Init":[]},"function_name":"init"},{"lifecycle_spec":{"OnConnect":[]},"function_name":"identity_connected"},{"lifecycle_spec":{"OnDisconnect":[]},"function_name":"identity_disconnected"}]},{"ExplicitNames":{"entries":[{"Table":{"source_name":"message","canonical_name":"message"}},{"Table":{"source_name":"user","canonical_name":"user"}},{"Function":{"source_name":"identity_connected","canonical_name":"identity_connected"}},{"Function":{"source_name":"identity_disconnected","canonical_name":"identity_disconnected"}},{"Function":{"source_name":"init","canonical_name":"init"}},{"Function":{"source_name":"send_message","canonical_name":"send_message"}},{"Function":{"source_name":"set_name","canonical_name":"set_name"}}]}}]}
```
# Description of Changes
We've updated to 2024
(https://github.com/clockworklabs/SpacetimeDB/pull/3802).
I'm just adding `edition` to `.rustfmt.toml`, and removing the hardcoded
edition from the pre-commit hook. This fixes the pre-commit hook
complaining about us using Rust 2024 features.
# API and ABI breaking changes
None
# Expected complexity level and risk
1
# Testing
- [x] `rustfmt crates/auth/src/identity.rs` now succeeds for me
---------
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
Our flake.nix relies on Crane, which begins by scanning the repo for
`Cargo.toml` files in order to pre-compute the full set of dependencies
in order to record them in the Nix store. A previous PR, #4413,
introduced a `Cargo.toml` which was intentionally invalid to our
repository, with a script that modified it as part of a test. This
`Cargo.toml` was excluded from our workspace, but unfortunately, Crane's
`buildDepsOnly` doesn't respect the workspace, and just searches the
whole repository. I consider this a bug in Crane, but in the interest of
doing useful work on SpacetimeDB in the near future rather than spending
hours hacking on my build script, this commit changes the `Cargo.toml`
in question to be valid at rest, so that Crane doesn't get angry due to
failing to parse it.
# API and ABI breaking changes
N/a
# Expected complexity level and risk
1
# Testing
- [x] `nix build` and `nix develop` work locally.
- [ ] @bradleyshep should please run `cargo llm run` to his satisfaction
to verify that I haven't broken it.
**Note**: This change requires the addition of new entries in the
secrets to work properly. These should be added prior to this merging.
# Description of Changes
* Add a tag-only Windows signing job that runs on a self-hosted signing
runner.
* This is an alternative/separate code-path just for the signing job.
See **Alternatives Considered** for details.
* Skip the unsigned Windows matrix build on tags so signed artifacts are
the only Windows release outputs.
* Sign `spacetimedb-update.exe`, `spacetimedb-cli.exe`, and
`spacetimedb-standalone.exe` before packaging, then upload the signed
artifacts as usual.
# Alternatives Considered
**Inline signing in the existing Windows packaging step**. This was
rejected because it would require all Windows builds (including non-tag
builds) to run on the signing-capable runner or to install/signing
tooling on GitHub-hosted runners. The chosen approach isolates signing
to tag releases, avoids exposing credentials in standard builds, and
keeps routine CI behavior unchanged.
# API and ABI breaking changes
None
# Expected complexity level and risk
2 – low risk. CI-only change that adds a new signing job and preserves
existing artifact layout.
# Testing
- [X] None (Not running, workflow change only)
---------
Signed-off-by: Ryan <r.ekhoff@clockworklabs.io>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
# Description of Changes
* Enable pipelining by default
* Set defaults for `MAX_INFLIGHT_PER_WORKER` for spacetime and convex
* Add a warmup period
* Reduce allocations in sdk (not that much of an effect but still
improved things)
This gives improved parity with the rust client on my machine.
# Expected complexity level and risk
1
# Testing
- [x] Yup:
```
══════════════════════════════════════════════════════════════════════
RESULTS
══════════════════════════════════════════════════════════════════════
spacetimedb ████████████████████████████████████████ 80,617 TPS
convex █░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 235 TPS
╔════════════════════════════════════════════════════════════╗
║ ║
║ 🚀 spacetimedb is 343x FASTER than convex! 🚀 ║
║ ║
╚════════════════════════════════════════════════════════════╝
```
# Description of Changes
Make `Accessor` a required argument for table-level index defs in C# to
align with rust and typescript. The same change was done for typescript
in https://github.com/clockworklabs/SpacetimeDB/pull/4525.
# API and ABI breaking changes
Technically breaks the module api, although I believe this is the
behavior that is outlined in the spec, and so the current behavior
should really be considered a bug.
# Expected complexity level and risk
1
# Testing
Added negative compile tests
## Summary
Add AgentSkills.io integration so developers can give their AI coding
assistants SpacetimeDB expertise.
## What is AgentSkills.io?
[AgentSkills.io](https://agentskills.io) is an open standard for
distributing domain knowledge to AI coding assistants. After this PR is
merged, developers can run:
```bash
npx skills add clockworklabs/SpacetimeDB
```
The skills are installed into whichever AI coding tools they use -
Claude Code, Cursor, Cline, GitHub Copilot, Windsurf, and 40+ others.
The AI then has access to SpacetimeDB-specific patterns, common mistakes
to avoid, and correct API usage.
### Test Now
You can test this PR before it's merged:
```bash
npx skills add douglance/SpacetimeDB
```
## Why This Matters
LLMs frequently hallucinate SpacetimeDB APIs that don't exist:
- `#[spacetimedb::table]` instead of `#[table]`
- `ctx.db.player` instead of `ctx.db.player()`
- `conn.reducers.foo("value")` instead of `conn.reducers.foo({ param:
"value" })`
These skills teach AI assistants the **correct** patterns and warn about
common mistakes, reducing debugging time for developers using AI tools.
## Skills Included
| Skill | Lines | What It Teaches |
|-------|-------|-----------------|
| `spacetimedb-rust` | 895 | Server modules, reducers, tables, RLS,
procedures |
| `spacetimedb-typescript` | 1004 | Client SDK, React hooks,
subscriptions, views |
| `spacetimedb-csharp` | 1463 | Unity integration, BSATN, sum types,
server modules |
| `spacetimedb-cli` | 562 | All CLI commands and workflows |
| `spacetimedb-concepts` | 518 | Architecture, when to use SpacetimeDB |
Each skill includes:
- **HALLUCINATED APIs** section - wrong patterns LLMs commonly generate
- **Common Mistakes Table** - server/client errors with fixes
- **Hard Requirements** - critical rules that must be followed
- **Code Examples** - correct usage patterns
## Directory Structure
```
skills/
├── spacetimedb-rust/SKILL.md
├── spacetimedb-typescript/SKILL.md
├── spacetimedb-csharp/SKILL.md
├── spacetimedb-cli/SKILL.md
└── spacetimedb-concepts/SKILL.md
```
## Usage (after merge)
```bash
# Install all SpacetimeDB skills
npx skills add clockworklabs/SpacetimeDB
# Install specific skill
npx skills add clockworklabs/SpacetimeDB -s spacetimedb-rust
# List available skills
npx skills add clockworklabs/SpacetimeDB --list
```
## Test Plan
- [x] `npx skills add . --list` shows 5 skills
- [x] `npx skills add . -s spacetimedb-rust --yes` installs to 28+
agents
- [x] YAML frontmatter validates against agentskills.io spec
- [x] Skills contain hallucinated APIs warnings
- [x] Skills contain common mistakes tables
---------
Co-authored-by: bradleyshep <148254416+bradleyshep@users.noreply.github.com>
# Description of Changes
Followup to #3802; was causing issues in modules in private. Rust 2024
now requires `unsafe()` around certain attributes, but the `settings`
macro was added after I first opened that PR, so I didn't wrap it.
Additionally, `settings` wasn't tested in this repo, so it wasn't
caught.
# Expected complexity level and risk
1
# Testing
- [x] Added use of `#[spacetimedb::settings]` in `module-test`
# Description of Changes
- Add SSR prefetching for Tanstack Start
Closes https://github.com/clockworklabs/SpacetimeDB/issues/4438
<!-- Please describe your change, mention any related tickets, and so on
here. -->
# API and ABI breaking changes
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
1
<!--
How complicated do you think these changes are? Grade on a scale from 1
to 5,
where 1 is a trivial change, and 5 is a deep-reaching and complex
change.
This complexity rating applies not only to the complexity apparent in
the diff,
but also to its interactions with existing and future code.
If you answered more than a 2, explain what is complex about the PR,
and what other components it interacts with in potentially concerning
ways. -->
# Testing
<!-- Describe any testing you've done, and any testing you'd like your
reviewers to do,
so that you're confident that all the changes work as expected! -->
- [x] SSR prefetch works from testing
# Description of Changes
This patch contains two main changes:
1. It makes `accessor` a required argument for table-level index defs in
typescript to align with rust.
2. It removes unsound index typing in the TypeScript bindings by
splitting index data into two explicit representations:
- `indexes`: declarative user config (`IndexOpts`) used for type
inference
- `resolvedIndexes`: normalized runtime metadata (`UntypedIndex`)
derived from `RawTableDefV10`
`TableCacheImpl` now builds index accessors from `resolvedIndexes`
instead of re-casting `indexes` at runtime.
This addressed the following comment:
```
// TODO: horrible horrible horrible. we smuggle this
`Array<UntypedIndex>`
// by casting it to an `Array<IndexOpts>` as `TableToSchema` expects.
// This is then used in `TableCacheImpl.constructor` and who knows where
else.
// We should stop lying about our types.
```
> Why?
We were conflating two different concepts under `tableDef.indexes`:
1. declared table-level index options authored by users
2. resolved runtime index definitions (including field-level inferred
indexes)
This required unsafe casts (`T['idxs']` / `UntypedIndex`) and made the
type model unsound.
Note, (2) was largely ai assisted.
# API and ABI breaking changes
Technically breaks the module api, although I believe this is the
behavior that is outlined in the spec, and so the current behavior
should really be considered a bug.
# Expected complexity level and risk
2
# Testing
Added unit tests covering the following:
1. Table-level explicit index without accessor throws.
2. Table-level duplicate accessor throws.
3. Table-level explicit accessor is accepted and used.
4. Field-level `.index(...)` derives accessor from the field name
(`displayName`).
# Description of Changes
- Fix useSpacetimeDBQuery returns untyped rows for TanStack Start
Closes https://github.com/clockworklabs/SpacetimeDB/issues/4441
<!-- Please describe your change, mention any related tickets, and so on
here. -->
# API and ABI breaking changes
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
<!--
How complicated do you think these changes are? Grade on a scale from 1
to 5,
where 1 is a trivial change, and 5 is a deep-reaching and complex
change.
This complexity rating applies not only to the complexity apparent in
the diff,
but also to its interactions with existing and future code.
If you answered more than a 2, explain what is complex about the PR,
and what other components it interacts with in potentially concerning
ways. -->
# Testing
<!-- Describe any testing you've done, and any testing you'd like your
reviewers to do,
so that you're confident that all the changes work as expected! -->
- [ ] <!-- maybe a test you want to do -->
- [ ] <!-- maybe a test you want a reviewer to do, so they can check it
off when they're satisfied. -->
Fixes#1972
Adds client-side log level filtering to `spacetime logs`:
- `--level <LEVEL>` / `-l <LEVEL>`: Show only logs at the specified
severity or higher. Valid values: `trace`, `debug`, `info`, `warn`,
`error`, `panic`. Default: show all logs (no filtering).
- `--level-exact`: When combined with `--level`, show only logs at
exactly the specified level.
Severity order (most to least): panic > error > warn > info > debug >
trace.
Examples:
```
spacetime logs mydb --level warn # Show only warn, error, and panic
spacetime logs mydb --level info # Show info and above
spacetime logs mydb --level error --level-exact # Show only errors
```
Filtering is done client-side.
---------
Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
Use prepared statements for postgres keynote benchmark.
# API and ABI breaking changes
None
# Expected complexity level and risk
2
# Testing
Manually ran inside of docker to verify
# Description of Changes
Closes https://github.com/clockworklabs/SpacetimeDB/issues/4496.
The docs refer to a block mentioning `/v1/publish` but we don't have
such a block in our example config. What we actually have is:
```
# Block all other routes explicitly. Only localhost can use these routes. If you want to open your
# server up so that anyone can publish to it you should comment this section out.
location / {
allow 127.0.0.1;
deny all;
}
```
# API and ABI breaking changes
None.
# Expected complexity level and risk
1.
# Testing
None
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
# Description of Changes
Previously, it didn't uncapitalize the first character. This makes the
runtime behavior match with the typed behavior.
# Expected complexity level and risk
1
# Testing
- [x] Added a test for `toCamelCase`
# Description of Changes
Contains 2 changes to documentation:
* Updates Unity Client Blackholio tutorial, part 2, with a corrected
`call` command. `Debug` became `debug` to match the automatic case
conversion that now occurs. Originally found and reported by discord
user `chrispavs`
* Updates Unity Client Blackholio tutorial, part 2, with the `out-dir`
path to match the path given in the [Entering the
Game](https://spacetimedb.com/docs/tutorials/unity/part-3#entering-the-game)
of `step 3`.
# API and ABI breaking changes
None
# Expected complexity level and risk
1 - Docs correction
# Testing
- [X] Tested all `blackholio` tutorial steps using C# server and Unity
client
# Description of Changes
<!-- Please describe your change, mention any related tickets, and so on
here. -->
This prompt is now skipped when `-y` is passed:
<img width="1053" height="530" alt="image"
src="https://github.com/user-attachments/assets/7237df85-4a12-4ab7-b377-95abbd0084c2"
/>
# API and ABI breaking changes
None
<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->
# Expected complexity level and risk
1 - this just skips a prompt in the CLI
<!--
How complicated do you think these changes are? Grade on a scale from 1
to 5,
where 1 is a trivial change, and 5 is a deep-reaching and complex
change.
This complexity rating applies not only to the complexity apparent in
the diff,
but also to its interactions with existing and future code.
If you answered more than a 2, explain what is complex about the PR,
and what other components it interacts with in potentially concerning
ways. -->
# Testing
- [x] publish a 1.0 database to maincloud, then upgrade it to 2.0 and
pass `-y`. You should no longer get the upgrade prompt.
- [x] publish a 1.0 database to maincloud, then upgrade it to 2.0
without passing `-y`. You should still get the upgrade prompt.
# Description of Changes
Index offset `truncate` methods returns `IndexError::KeyNotFound` when
asked to truncate on empty index offset file or the key in input is
smaller than the first entry.
This was causing `commitlog::reset_to` method to return error, and stuck
replicas in re-spawn loop.
# API and ABI breaking changes
NA
# Expected complexity level and risk
1
# Testing
Added new tests to cover edge scenerios.