From 1decd9746ae10b7def7b0480ed584efd5314bcbf Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Tue, 23 Sep 2025 14:33:13 -0500 Subject: [PATCH 01/10] Add abi for credentials --- crates/bindings-sys/src/lib.rs | 37 +++++++++++ crates/core/src/host/mod.rs | 2 + crates/core/src/host/wasm_common.rs | 2 + crates/core/src/host/wasmtime/mod.rs | 12 ++++ .../src/host/wasmtime/wasm_instance_env.rs | 66 ++++++++++++++++++- 5 files changed, 118 insertions(+), 1 deletion(-) diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index b901de7e5d7..42268c3abb3 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -588,6 +588,15 @@ pub mod raw { /// /// - `out_ptr` is NULL or `out` is not in bounds of WASM memory. pub fn identity(out_ptr: *mut u8); + + /// Check the size of the jwt associated with the given connection. + /// Returns 0 if there is no jwt for the connection. + pub fn jwt_len(connection_id_ptr: *const u8, out_ptr: *mut u32); + + /// Write the jwt payload for the given connection id to the out_ptr. + /// target_ptr_len will be set to the number of bytes written to the buffer. + /// If the buffer is too small, or the connection has no jwt, target_ptr_len will be set to 0. + pub fn get_jwt(connection_id_ptr: *const u8, target_ptr: *mut u8, target_ptr_len: *mut u32); } /// What strategy does the database index use? @@ -1089,6 +1098,34 @@ pub fn identity() -> [u8; 32] { buf } +#[inline] +pub fn jwt_length(connection_id: [u8; 16]) -> Option { + let mut v: u32 = 0; + unsafe { raw::jwt_len(connection_id.as_ptr(), &mut v) } + if v == 0 { + None + } else { + Some(v) + } +} + +#[inline] +pub fn get_jwt(connection_id: [u8; 16]) -> Option { + let Some(jwt_len) = jwt_length(connection_id) else { + return None; // No JWT found. + }; + let mut buf = vec![0u8; jwt_len as usize]; + + let mut v: u32 = buf.len() as u32; + unsafe { + raw::get_jwt(connection_id.as_ptr(), buf.as_mut_ptr(), &mut v); + } + if v == 0 { + return None; // No JWT found. + } + Some(std::str::from_utf8(&buf[..v as usize]).unwrap().to_string()) +} + pub struct RowIter { raw: raw::RowIter, } diff --git a/crates/core/src/host/mod.rs b/crates/core/src/host/mod.rs index beb72fc2f99..e7a7f06c88a 100644 --- a/crates/core/src/host/mod.rs +++ b/crates/core/src/host/mod.rs @@ -144,6 +144,8 @@ pub enum AbiCall { ConsoleTimerStart, ConsoleTimerEnd, Identity, + JwtLength, + GetJwt, VolatileNonatomicScheduleImmediate, } diff --git a/crates/core/src/host/wasm_common.rs b/crates/core/src/host/wasm_common.rs index 666cc808ea3..ac4b49415a9 100644 --- a/crates/core/src/host/wasm_common.rs +++ b/crates/core/src/host/wasm_common.rs @@ -392,6 +392,8 @@ macro_rules! abi_funcs { "spacetime_10.0"::datastore_btree_scan_bsatn, "spacetime_10.0"::datastore_delete_by_btree_scan_bsatn, "spacetime_10.0"::identity, + "spacetime_10.0"::get_jwt, + "spacetime_10.0"::jwt_len, // unstable: "spacetime_10.0"::volatile_nonatomic_schedule_immediate, diff --git a/crates/core/src/host/wasmtime/mod.rs b/crates/core/src/host/wasmtime/mod.rs index 5a61d5e23ab..63b780289d8 100644 --- a/crates/core/src/host/wasmtime/mod.rs +++ b/crates/core/src/host/wasmtime/mod.rs @@ -179,6 +179,18 @@ impl WasmPointee for spacetimedb_lib::Identity { } } +impl WasmPointee for spacetimedb_lib::ConnectionId { + type Pointer = u32; + fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), MemError> { + let bytes = self.as_le_byte_array(); + mem.deref_slice_mut(ptr, bytes.len() as u32)?.copy_from_slice(&bytes); + Ok(()) + } + fn read_from(mem: &mut MemView, ptr: Self::Pointer) -> Result { + Ok(Self::from_le_byte_array(*mem.deref_array(ptr)?)) + } +} + type WasmPtr = ::Pointer; /// Wraps access to WASM linear memory with some additional functionality. diff --git a/crates/core/src/host/wasmtime/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs index f4605274986..c01ca9f7fc1 100644 --- a/crates/core/src/host/wasmtime/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -12,7 +12,7 @@ use crate::host::wasm_common::{ }; use crate::host::AbiCall; use anyhow::Context as _; -use spacetimedb_lib::Timestamp; +use spacetimedb_lib::{ConnectionId, Timestamp}; use spacetimedb_primitives::{errno, ColId}; use wasmtime::{AsContext, Caller, StoreContextMut}; @@ -22,6 +22,7 @@ use super::{Mem, MemView, NullableMemOp, WasmError, WasmPointee, WasmPtr}; use instrumentation::noop as span; #[cfg(feature = "spacetimedb-wasm-instance-env-times")] use instrumentation::op as span; +use spacetimedb_datastore::locking_tx_datastore::state_view::StateView; /// A `WasmInstanceEnv` provides the connection between a module /// and the database. @@ -1208,6 +1209,69 @@ impl WasmInstanceEnv { }) } + pub fn jwt_len( + caller: Caller<'_, Self>, + connection_id: WasmPtr, + out_ptr: WasmPtr, + ) -> RtResult<()> { + log::info!("Calling has_jwt"); + Self::with_span(caller, AbiCall::JwtLength, |caller| { + let (mem, env) = Self::mem_env(caller); + let cid = ConnectionId::read_from(mem, connection_id)?; + let length = env + .instance_env + .tx + .get() + .unwrap() + .get_jwt_payload(cid)? + .map(|p| p.len() as u32) + .unwrap_or(0u32); + length.write_to(mem, out_ptr)?; + Ok(()) + }) + } + + pub fn get_jwt( + caller: Caller<'_, Self>, + connection_id: WasmPtr, + target_ptr: WasmPtr, + target_ptr_len: WasmPtr, + ) -> RtResult<()> { + log::info!("Calling get_jwt"); + Self::with_span(caller, AbiCall::GetJwt, |caller| { + let (mem, env) = Self::mem_env(caller); + let cid = ConnectionId::read_from(mem, connection_id)?; + let jwt = match env.instance_env.tx.get().unwrap().get_jwt_payload(cid)? { + None => { + // Consider logging here, since this should only happen during an upgrade. + 0u32.write_to(mem, target_ptr_len)?; + return Ok(()); + } + Some(jwt) => jwt, + }; + log::info!("JWT payload found for connection ID: {:?}: {}", cid.to_hex(), jwt); + let jwt_len = jwt.len(); + // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`. + let buffer_len = u32::read_from(mem, target_ptr_len)?; + log::info!("buffer_len: {buffer_len}"); + if buffer_len < jwt_len as u32 { + return Err(anyhow::anyhow!("buffer too small to hold JWT payload")); + } + log::info!("About to write length"); + // Write the length of the JWT payload to the target pointer. + (jwt_len as u32).write_to(mem, target_ptr_len)?; + log::info!("wrote length of {jwt_len}"); + log::info!("Byte len {}", jwt.len()); + + // Write the JWT payload to the target pointer. + // Get a mutable view to the `buffer`. + let buffer = mem.deref_slice_mut(target_ptr, buffer_len)?; + buffer[..jwt_len].copy_from_slice(jwt.as_bytes()); + log::info!("wrote jwt bytes to slice"); + Ok(()) + }) + } + /// Writes the identity of the module into `out = out_ptr[..32]`. /// /// # Traps From 3f3b225456b5288ecb73bb4cb8c3ed3ad32259bf Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Wed, 24 Sep 2025 11:28:12 -0500 Subject: [PATCH 02/10] Add AuthCtx to the ReducerContext --- Cargo.lock | 2 + crates/bindings/Cargo.toml | 2 + crates/bindings/src/lib.rs | 169 ++++++++++++++++++++++++++++++++++++- crates/bindings/src/rt.rs | 8 +- 4 files changed, 171 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bed876a5bc..a8605f72849 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5476,8 +5476,10 @@ dependencies = [ "getrandom 0.2.16", "insta", "log", + "once_cell", "rand 0.8.5", "scoped-tls", + "serde_json", "spacetimedb-bindings-macro 1.4.0", "spacetimedb-bindings-sys 1.4.0", "spacetimedb-lib 1.4.0", diff --git a/crates/bindings/Cargo.toml b/crates/bindings/Cargo.toml index 8e75cc3fce3..89a3f1f2a2e 100644 --- a/crates/bindings/Cargo.toml +++ b/crates/bindings/Cargo.toml @@ -35,6 +35,8 @@ rand08 = { workspace = true, optional = true } # if someone tries to use rand's ThreadRng, it will fail to link # because no one defined __getrandom_custom getrandom02 = { workspace = true, optional = true, features = ["custom"] } +serde_json.workspace = true +once_cell = "1.21.3" [dev-dependencies] insta.workspace = true diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 4bd241ee732..69d9626116d 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -12,12 +12,13 @@ pub mod rt; #[doc(hidden)] pub mod table; -use spacetimedb_lib::bsatn; -use std::cell::RefCell; - pub use log; #[cfg(feature = "rand")] pub use rand08 as rand; +use spacetimedb_lib::bsatn; +use std::cell::{OnceCell, RefCell}; +use std::ops::Deref; +use std::sync::LazyLock; #[cfg(feature = "unstable")] pub use client_visibility_filter::Filter; @@ -732,6 +733,8 @@ pub struct ReducerContext { /// See the [`#[table]`](macro@crate::table) macro for more information. pub db: Local, + sender_auth: AuthCtx, + #[cfg(feature = "rand08")] rng: std::cell::OnceCell, } @@ -744,6 +747,24 @@ impl ReducerContext { sender: Identity::__dummy(), timestamp: Timestamp::UNIX_EPOCH, connection_id: None, + sender_auth: AuthCtx::internal(), + rng: std::cell::OnceCell::new(), + } + } + + #[doc(hidden)] + fn new(db: Local, sender: Identity, connection_id: Option, timestamp: Timestamp) -> Self { + let sender_auth = match connection_id { + Some(cid) => AuthCtx::from_connection_id(cid), + None => AuthCtx::internal(), + }; + Self { + db, + sender, + timestamp, + connection_id, + sender_auth, + #[cfg(feature = "rand08")] rng: std::cell::OnceCell::new(), } } @@ -796,6 +817,114 @@ impl DbContext for ReducerContext { #[non_exhaustive] pub struct Local {} +#[non_exhaustive] +pub struct JwtClaims { + payload: String, + parsed: OnceCell, + audience: OnceCell>, +} + +/// Authentication information for the caller of a reducer. +pub struct AuthCtx { + is_internal: bool, + // I can't directly use a LazyLock without making this struct generic. + jwt: Box>>, +} + +impl AuthCtx { + fn new(is_internal: bool, jwt_fn: F) -> Self + where + F: FnOnce() -> Option + 'static, + { + AuthCtx { + is_internal, + jwt: Box::new(LazyLock::new(jwt_fn)), + } + } + + /// Create an AuthCtx for an internal call, with no JWT. + /// This represents a scheduled reducer. + pub fn internal() -> AuthCtx { + Self::new(true, || None) + } + + /// Create an AuthCtx using the json claims from a JWT. + /// This can be used to write unit tests. + pub fn from_jwt_payload(jwt_payload: String) -> AuthCtx { + Self::new(false, move || Some(JwtClaims::new(jwt_payload))) + } + + /// Create an AuthCtx that reads the JWT for the given connection id. + fn from_connection_id(connection_id: ConnectionId) -> AuthCtx { + Self::new(false, move || { + spacetimedb_bindings_sys::get_jwt(connection_id.as_le_byte_array()).map(JwtClaims::new) + }) + } + + /// True if this reducer was spawned from inside the database. + pub fn is_internal(&self) -> bool { + self.is_internal + } + + /// Check if there is a JWT without loading it. + /// If is_internal is true, this will be false. + pub fn has_jwt(&self) -> bool { + self.jwt.is_some() + } + + /// Load the jwt. + pub fn jwt(&self) -> Option<&JwtClaims> { + self.jwt.as_ref().deref().as_ref() + } +} + +impl JwtClaims { + fn new(jwt: String) -> Self { + Self { + payload: jwt, + parsed: OnceCell::new(), + audience: OnceCell::new(), + } + } + + fn get_parsed(&self) -> &serde_json::Value { + self.parsed + .get_or_init(|| serde_json::from_str(&self.payload).expect("Failed to parse JWT payload")) + } + + pub fn subject(&self) -> &str { + // TODO: Add more error messages here. + self.get_parsed().get("sub").unwrap().as_str().unwrap() + } + + pub fn issuer(&self) -> &str { + self.get_parsed().get("iss").unwrap().as_str().unwrap() + } + + fn extract_audience(&self) -> Vec { + let aud = self.get_parsed().get("aud").unwrap(); + match aud { + serde_json::Value::String(s) => vec![s.clone()], + serde_json::Value::Array(arr) => arr.iter().filter_map(|v| v.as_str().map(String::from)).collect(), + _ => panic!("Unexpected type for 'aud' claim in JWT"), + } + } + + pub fn audience(&self) -> &[String] { + self.audience.get_or_init(|| self.extract_audience()) + } + + // A convenience method, since this may not be in the token. + pub fn identity(&self) -> Identity { + Identity::from_claims(self.issuer(), self.subject()) + } + + // We can expose the whole payload for users that want to parse custom claims. + pub fn raw_payload(&self) -> &str { + &self.payload + } +} + // #[cfg(target_arch = "wasm32")] // #[global_allocator] // static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; @@ -900,3 +1029,37 @@ macro_rules! __volatile_nonatomic_schedule_immediate_impl { } }; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_single_audience() { + let example_payload = r#" + { + "iss": "https://securetoken.google.com/my-project-id", + "aud": "my-project-id", + "auth_time": 1695560000, + "user_id": "abc123XYZ", + "sub": "abc123XYZ", + "iat": 1695560100, + "exp": 1695563700, + "email": "user@example.com", + "email_verified": true, + "firebase": { + "identities": { + "email": ["user@example.com"] + }, + "sign_in_provider": "password" + }, + "name": "Jane Doe", + "picture": "https://lh3.googleusercontent.com/a-/profile.jpg" + } + "#; + let auth = AuthCtx::from_jwt_payload(example_payload.to_string()); + let audience = auth.jwt().unwrap().audience(); + assert_eq!(audience.len(), 1); + assert_eq!(audience, &["my-project-id".to_string()]); + } +} diff --git a/crates/bindings/src/rt.rs b/crates/bindings/src/rt.rs index e169e916fd1..fffa7062cd5 100644 --- a/crates/bindings/src/rt.rs +++ b/crates/bindings/src/rt.rs @@ -488,13 +488,7 @@ extern "C" fn __call_reducer__( // Assemble the `ReducerContext`. let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64); - let ctx = ReducerContext { - db: crate::Local {}, - sender, - timestamp, - connection_id: conn_id, - rng: std::cell::OnceCell::new(), - }; + let ctx = ReducerContext::new(crate::Local {}, sender, conn_id, timestamp); // Fetch reducer function. let reducers = REDUCERS.get().unwrap(); From 61ffd83863b1facabb07f2e90c40ba894304ea4a Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Wed, 24 Sep 2025 11:39:59 -0500 Subject: [PATCH 03/10] Expose sender_auth with a function. --- crates/bindings/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 69d9626116d..025039e9fb4 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -769,6 +769,10 @@ impl ReducerContext { } } + pub fn sender_auth(&self) -> &AuthCtx { + &self.sender_auth + } + /// Read the current module's [`Identity`]. pub fn identity(&self) -> Identity { // Hypothetically, we *could* read the module identity out of the system tables. From 58b5f264ebef9d7035c078c2f5a82398bd298c83 Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Wed, 24 Sep 2025 11:52:57 -0500 Subject: [PATCH 04/10] Cleanup --- crates/core/src/host/instance_env.rs | 8 ++++++++ crates/core/src/host/wasmtime/wasm_instance_env.rs | 12 ++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index 4d6863a7f27..0939fdd5ea2 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -16,6 +16,7 @@ use spacetimedb_sats::{ }; use spacetimedb_table::indexes::RowPointer; use spacetimedb_table::table::RowRef; +use std::fmt::Display; use std::ops::DerefMut; use std::sync::Arc; @@ -499,6 +500,13 @@ impl From for NodesError { } } +impl Display for GetTxError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "not in a transaction") + } +} +impl std::error::Error for GetTxError {} + #[cfg(test)] mod test { use super::*; diff --git a/crates/core/src/host/wasmtime/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs index c01ca9f7fc1..ffa1d98d6bc 100644 --- a/crates/core/src/host/wasmtime/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -1214,15 +1214,13 @@ impl WasmInstanceEnv { connection_id: WasmPtr, out_ptr: WasmPtr, ) -> RtResult<()> { - log::info!("Calling has_jwt"); Self::with_span(caller, AbiCall::JwtLength, |caller| { let (mem, env) = Self::mem_env(caller); let cid = ConnectionId::read_from(mem, connection_id)?; let length = env .instance_env .tx - .get() - .unwrap() + .get()? .get_jwt_payload(cid)? .map(|p| p.len() as u32) .unwrap_or(0u32); @@ -1241,7 +1239,7 @@ impl WasmInstanceEnv { Self::with_span(caller, AbiCall::GetJwt, |caller| { let (mem, env) = Self::mem_env(caller); let cid = ConnectionId::read_from(mem, connection_id)?; - let jwt = match env.instance_env.tx.get().unwrap().get_jwt_payload(cid)? { + let jwt = match env.instance_env.tx.get()?.get_jwt_payload(cid)? { None => { // Consider logging here, since this should only happen during an upgrade. 0u32.write_to(mem, target_ptr_len)?; @@ -1249,25 +1247,19 @@ impl WasmInstanceEnv { } Some(jwt) => jwt, }; - log::info!("JWT payload found for connection ID: {:?}: {}", cid.to_hex(), jwt); let jwt_len = jwt.len(); // Read `buffer_len`, i.e., the capacity of `buffer` pointed to by `buffer_ptr`. let buffer_len = u32::read_from(mem, target_ptr_len)?; - log::info!("buffer_len: {buffer_len}"); if buffer_len < jwt_len as u32 { return Err(anyhow::anyhow!("buffer too small to hold JWT payload")); } - log::info!("About to write length"); // Write the length of the JWT payload to the target pointer. (jwt_len as u32).write_to(mem, target_ptr_len)?; - log::info!("wrote length of {jwt_len}"); - log::info!("Byte len {}", jwt.len()); // Write the JWT payload to the target pointer. // Get a mutable view to the `buffer`. let buffer = mem.deref_slice_mut(target_ptr, buffer_len)?; buffer[..jwt_len].copy_from_slice(jwt.as_bytes()); - log::info!("wrote jwt bytes to slice"); Ok(()) }) } From 57a024fe189e6160234538e751c5a308293a15f0 Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Wed, 24 Sep 2025 12:42:53 -0500 Subject: [PATCH 05/10] Update dep snapshot --- .../deps__spacetimedb_bindings_dependencies.snap | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap index 6c1e5485c8e..06a0b350f75 100644 --- a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap +++ b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap @@ -2,7 +2,7 @@ source: crates/bindings/tests/deps.rs expression: "cargo tree -p spacetimedb -e no-dev --color never --target wasm32-unknown-unknown -f {lib}" --- -total crates: 60 +total crates: 65 spacetimedb ├── bytemuck ├── derive_more @@ -21,6 +21,7 @@ spacetimedb ├── getrandom │ └── cfg_if ├── log +├── once_cell ├── rand │ ├── rand_chacha │ │ ├── ppv_lite86 @@ -29,6 +30,11 @@ spacetimedb │ │ └── getrandom (*) │ └── rand_core (*) ├── scoped_tls +├── serde_json +│ ├── itoa +│ ├── memchr +│ ├── ryu +│ └── serde ├── spacetimedb_bindings_macro │ ├── heck │ ├── humantime From bfdc6368b50e7948b622fb3076695b9716f241da Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Tue, 30 Sep 2025 15:18:59 -0700 Subject: [PATCH 06/10] Cleanup --- crates/bindings-sys/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index 8fa6d7aa1c6..462917aaae0 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -1138,8 +1138,7 @@ pub fn get_jwt(connection_id: [u8; 16]) -> Option { let ret = unsafe { raw::bytes_source_remaining_length(source, &raw mut len) }; match ret { 0 => len, - INVALID => panic!("invalid source passed"), - _ => unreachable!(), + _ => panic!("invalid source"), } }; let mut buf = vec![0u8; len as usize]; From 7c4da19b554d557a4774e80c56b4db727375dfaf Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Thu, 2 Oct 2025 09:27:03 -0700 Subject: [PATCH 07/10] Update comments and use helper to read byte source of jwt --- crates/auth/src/identity.rs | 6 ++--- crates/bindings-sys/src/lib.rs | 27 +++++-------------- crates/bindings/src/lib.rs | 10 +++---- crates/bindings/src/rt.rs | 19 +++++++++++-- ...ps__spacetimedb_bindings_dependencies.snap | 6 +++-- crates/cli/src/config.rs | 4 +-- crates/cli/src/lib.rs | 2 +- crates/cli/src/subcommands/energy.rs | 2 +- crates/cli/src/subcommands/login.rs | 2 +- crates/cli/src/subcommands/server.rs | 8 +++--- crates/cli/src/util.rs | 6 ++--- crates/core/src/auth/token_validation.rs | 4 +-- crates/core/src/db/update.rs | 3 +-- crates/core/src/host/host_controller.rs | 2 +- crates/core/src/host/scheduler.rs | 7 +++-- .../src/host/wasmtime/wasm_instance_env.rs | 13 ++++++++- .../module_subscription_manager.rs | 12 ++++----- crates/core/src/subscription/subscription.rs | 4 +-- .../locking_tx_datastore/committed_state.rs | 3 +-- .../src/locking_tx_datastore/datastore.rs | 2 +- .../src/locking_tx_datastore/mut_tx.rs | 19 ++++++------- crates/datastore/src/system_tables.rs | 2 +- crates/execution/src/iter.rs | 2 +- crates/pg/src/pg_server.rs | 2 +- crates/sqltest/src/main.rs | 4 +-- crates/standalone/src/lib.rs | 2 +- modules/quickstart-chat/src/lib.rs | 4 +++ .../src/module_bindings/mod.rs | 2 +- sdks/rust/tests/test-client/src/main.rs | 12 +++------ .../test-client/src/module_bindings/mod.rs | 2 +- .../tests/test-client/src/pk_test_table.rs | 20 +++----------- .../test-client/src/unique_test_table.rs | 10 ++----- 32 files changed, 102 insertions(+), 121 deletions(-) diff --git a/crates/auth/src/identity.rs b/crates/auth/src/identity.rs index 576e850d7ee..c4432c414b0 100644 --- a/crates/auth/src/identity.rs +++ b/crates/auth/src/identity.rs @@ -16,7 +16,7 @@ impl TryFrom for ConnectionAuthCtx { type Error = anyhow::Error; fn try_from(claims: SpacetimeIdentityClaims) -> Result { let payload = - serde_json::to_string(&claims).map_err(|e| anyhow::anyhow!("Failed to serialize claims: {}", e))?; + serde_json::to_string(&claims).map_err(|e| anyhow::anyhow!("Failed to serialize claims: {e}"))?; Ok(ConnectionAuthCtx { claims, jwt_payload: payload, @@ -111,9 +111,7 @@ impl TryInto for IncomingClaims { if let Some(token_identity) = self.identity { if token_identity != computed_identity { return Err(anyhow::anyhow!( - "Identity mismatch: token identity {:?} does not match computed identity {:?}", - token_identity, - computed_identity, + "Identity mismatch: token identity {token_identity:?} does not match computed identity {computed_identity:?}", )); } } diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index 462917aaae0..ba179a2f3e3 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -620,8 +620,8 @@ pub mod raw { pub fn bytes_source_remaining_length(source: BytesSource, out: *mut u32) -> i16; /// Find the jwt payload for the given connection id, and write the - /// BytesSourceId to the given pointer. - /// If this is not found, BytesSourceId::INVALID (aka 0) will be written. + /// [`BytesSource`] to the given pointer. + /// If this is not found, [`BytesSource::INVALID`] (aka 0) will be written. pub fn get_jwt(connection_id_ptr: *const u8, bytes_source_id: *mut BytesSource); } @@ -1125,31 +1125,16 @@ pub fn identity() -> [u8; 32] { } #[inline] -pub fn get_jwt(connection_id: [u8; 16]) -> Option { +pub fn get_jwt(connection_id: [u8; 16]) -> Option { let mut source: raw::BytesSource = raw::BytesSource::INVALID; unsafe { raw::get_jwt(connection_id.as_ptr(), &mut source); } if source == raw::BytesSource::INVALID { - return None; // No JWT found. + None // No JWT found. + } else { + Some(source) } - let len = { - let mut len = 0; - let ret = unsafe { raw::bytes_source_remaining_length(source, &raw mut len) }; - match ret { - 0 => len, - _ => panic!("invalid source"), - } - }; - let mut buf = vec![0u8; len as usize]; - let mut bytes_read = len as usize; - let ret = unsafe { raw::bytes_source_read(source, buf.as_mut_ptr(), &mut bytes_read) }; - // We should have exhausted the source. - assert_eq!(ret, -1); - // We should get exactly `len` bytes. - assert_eq!(bytes_read, len as usize); - - Some(std::str::from_utf8(&buf[..bytes_read]).unwrap().to_string()) } pub struct RowIter { diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 025039e9fb4..d5550afc7ac 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -846,23 +846,21 @@ impl AuthCtx { } } - /// Create an AuthCtx for an internal call, with no JWT. + /// Create an [`AuthCtx`] for an internal call, with no JWT. /// This represents a scheduled reducer. pub fn internal() -> AuthCtx { Self::new(true, || None) } - /// Create an AuthCtx using the json claims from a JWT. + /// Creates an [`AuthCtx`] using the json claims from a JWT. /// This can be used to write unit tests. pub fn from_jwt_payload(jwt_payload: String) -> AuthCtx { Self::new(false, move || Some(JwtClaims::new(jwt_payload))) } - /// Create an AuthCtx that reads the JWT for the given connection id. + /// Creates an [`AuthCtx`] that reads the JWT for the given connection id. fn from_connection_id(connection_id: ConnectionId) -> AuthCtx { - Self::new(false, move || { - spacetimedb_bindings_sys::get_jwt(connection_id.as_le_byte_array()).map(JwtClaims::new) - }) + Self::new(false, move || rt::get_jwt(connection_id).map(JwtClaims::new)) } /// True if this reducer was spawned from inside the database. diff --git a/crates/bindings/src/rt.rs b/crates/bindings/src/rt.rs index e0318f712c1..70c462c00bd 100644 --- a/crates/bindings/src/rt.rs +++ b/crates/bindings/src/rt.rs @@ -2,6 +2,7 @@ use crate::table::IndexAlgo; use crate::{sys, IterBuf, ReducerContext, ReducerResult, SpacetimeType, Table}; +use spacetimedb_bindings_sys::raw; pub use spacetimedb_lib::db::raw_def::v9::Lifecycle as LifecycleReducer; use spacetimedb_lib::db::raw_def::v9::{RawIndexAlgorithm, RawModuleDefV9Builder, TableType}; use spacetimedb_lib::de::{self, Deserialize, Error as _, SeqProductAccess}; @@ -525,6 +526,20 @@ fn with_read_args(args: BytesSource, logic: impl FnOnce(&[u8]) -> R) -> R { const NO_SPACE: u16 = errno::NO_SPACE.get(); const NO_SUCH_BYTES: u16 = errno::NO_SUCH_BYTES.get(); +/// Look up the jwt associated with `connection_id`. +pub fn get_jwt(connection_id: ConnectionId) -> Option { + let mut buf = IterBuf::take(); + let mut source: BytesSource = BytesSource::INVALID; + unsafe { + raw::get_jwt(connection_id.as_le_byte_array().as_ptr(), &mut source); + }; + if source == BytesSource::INVALID { + return None; + } + read_bytes_source_into(source, &mut buf); + Some(std::str::from_utf8(&buf).unwrap().to_string()) +} + /// Read `source` from the host fully into `buf`. fn read_bytes_source_into(source: BytesSource, buf: &mut Vec) { const INVALID: i16 = NO_SUCH_BYTES as i16; @@ -559,8 +574,8 @@ fn read_bytes_source_into(source: BytesSource, buf: &mut Vec) { let buf_ptr = buf_ptr.as_mut_ptr().cast(); let ret = unsafe { sys::raw::bytes_source_read(source, buf_ptr, &mut buf_len) }; if ret <= 0 { - // SAFETY: `bytes_source_read` just appended `spare_len` bytes to `buf`. - unsafe { buf.set_len(buf.len() + spare_len) }; + // SAFETY: `bytes_source_read` just appended `buf_len` bytes to `buf`. + unsafe { buf.set_len(buf.len() + buf_len) }; } match ret { // Host side source exhausted, we're done. diff --git a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap index 06a0b350f75..e82ec6d811e 100644 --- a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap +++ b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap @@ -2,7 +2,7 @@ source: crates/bindings/tests/deps.rs expression: "cargo tree -p spacetimedb -e no-dev --color never --target wasm32-unknown-unknown -f {lib}" --- -total crates: 65 +total crates: 67 spacetimedb ├── bytemuck ├── derive_more @@ -34,7 +34,7 @@ spacetimedb │ ├── itoa │ ├── memchr │ ├── ryu -│ └── serde +│ └── serde_core ├── spacetimedb_bindings_macro │ ├── heck │ ├── humantime @@ -64,6 +64,7 @@ spacetimedb │ │ └── constant_time_eq │ │ [build-dependencies] │ │ └── cc +│ │ ├── find_msvc_tools │ │ └── shlex │ ├── chrono │ │ └── num_traits @@ -94,6 +95,7 @@ spacetimedb │ │ ├── enum_as_inner (*) │ │ ├── ethnum │ │ │ └── serde +│ │ │ └── serde_core │ │ ├── hex │ │ ├── itertools (*) │ │ ├── second_stack diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index 233a1461dfc..0c7114e18ce 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -234,10 +234,10 @@ impl RawConfig { if let Ok(cfg) = self.find_server(&host) { if let Some(nick) = &cfg.nickname { if nick == &host { - anyhow::bail!("Server host name is ambiguous with existing server nickname: {}", nick); + anyhow::bail!("Server host name is ambiguous with existing server nickname: {nick}"); } } - anyhow::bail!("Server already configured for host: {}", host); + anyhow::bail!("Server already configured for host: {host}"); } self.server_configs.push(ServerConfig { diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index a836c01e464..1eb7b322232 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -67,7 +67,7 @@ pub async fn exec_subcommand( "login" => login::exec(config, args).await, "logout" => logout::exec(config, args).await, "version" => return subcommands::version::exec(paths, root_dir, args).await, - unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)), + unknown => Err(anyhow::anyhow!("Invalid subcommand: {unknown}")), } .map(|()| ExitCode::SUCCESS) } diff --git a/crates/cli/src/subcommands/energy.rs b/crates/cli/src/subcommands/energy.rs index f4c5b7b8aef..fb07bdb1f32 100644 --- a/crates/cli/src/subcommands/energy.rs +++ b/crates/cli/src/subcommands/energy.rs @@ -35,7 +35,7 @@ fn get_energy_subcommands() -> Vec { async fn exec_subcommand(config: Config, cmd: &str, args: &ArgMatches) -> Result<(), anyhow::Error> { match cmd { "balance" => exec_status(config, args).await, - unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)), + unknown => Err(anyhow::anyhow!("Invalid subcommand: {unknown}")), } } diff --git a/crates/cli/src/subcommands/login.rs b/crates/cli/src/subcommands/login.rs index a05afe33072..bebd71db2d9 100644 --- a/crates/cli/src/subcommands/login.rs +++ b/crates/cli/src/subcommands/login.rs @@ -74,7 +74,7 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E async fn exec_subcommand(config: Config, cmd: &str, args: &ArgMatches) -> Result<(), anyhow::Error> { match cmd { "show" => exec_show(config, args).await, - unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)), + unknown => Err(anyhow::anyhow!("Invalid subcommand: {unknown}")), } } diff --git a/crates/cli/src/subcommands/server.rs b/crates/cli/src/subcommands/server.rs index 19e36f32f68..6c46ceacc78 100644 --- a/crates/cli/src/subcommands/server.rs +++ b/crates/cli/src/subcommands/server.rs @@ -134,7 +134,7 @@ async fn exec_subcommand( "ping" => exec_ping(config, args).await, "edit" => exec_edit(config, args).await, "clear" => exec_clear(config, paths, args).await, - unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)), + unknown => Err(anyhow::anyhow!("Invalid subcommand: {unknown}")), } } @@ -181,7 +181,7 @@ pub async fn exec_set_default(mut config: Config, args: &ArgMatches) -> Result<( fn valid_protocol_or_error(protocol: &str) -> anyhow::Result<()> { if !VALID_PROTOCOLS.contains(&protocol) { - Err(anyhow::anyhow!("Invalid protocol: {}", protocol)) + Err(anyhow::anyhow!("Invalid protocol: {protocol}")) } else { Ok(()) } @@ -196,7 +196,7 @@ pub async fn exec_add(mut config: Config, args: &ArgMatches) -> Result<(), anyho let no_fingerprint = *args.get_one::("no-fingerprint").unwrap(); let (host, protocol) = host_or_url_to_host_and_protocol(url); - let protocol = protocol.ok_or_else(|| anyhow::anyhow!("Invalid url: {}", url))?; + let protocol = protocol.ok_or_else(|| anyhow::anyhow!("Invalid url: {url}"))?; valid_protocol_or_error(protocol)?; @@ -313,7 +313,7 @@ pub async fn exec_edit(mut config: Config, args: &ArgMatches) -> Result<(), anyh None => (None, None), Some(new_url) => { let (new_host, new_proto) = host_or_url_to_host_and_protocol(new_url); - let new_proto = new_proto.ok_or_else(|| anyhow::anyhow!("Invalid url: {}", new_url))?; + let new_proto = new_proto.ok_or_else(|| anyhow::anyhow!("Invalid url: {new_url}"))?; (Some(new_host), Some(new_proto)) } }; diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index 7161d4c9fec..24d5befee0e 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -230,12 +230,12 @@ pub fn url_to_host_and_protocol(url: &str) -> anyhow::Result<(&str, &str)> { let host = url.split("://").last().unwrap(); if !VALID_PROTOCOLS.contains(&protocol) { - Err(anyhow::anyhow!("Invalid protocol: {}", protocol)) + Err(anyhow::anyhow!("Invalid protocol: {protocol}")) } else { Ok((host, protocol)) } } else { - Err(anyhow::anyhow!("Invalid url: {}", url)) + Err(anyhow::anyhow!("Invalid url: {url}")) } } @@ -283,7 +283,7 @@ pub fn decode_identity(token: &String) -> anyhow::Result { // But signature verification would require getting the public key from a server, and we don't necessarily want to do that. let token_parts: Vec<_> = token.split('.').collect(); if token_parts.len() != 3 { - return Err(anyhow::anyhow!("Token does not look like a JSON web token: {}", token)); + return Err(anyhow::anyhow!("Token does not look like a JSON web token: {token}")); } let decoded_bytes = BASE_64_STD_NO_PAD.decode(token_parts[1])?; let decoded_string = String::from_utf8(decoded_bytes)?; diff --git a/crates/core/src/auth/token_validation.rs b/crates/core/src/auth/token_validation.rs index f06d278d900..a4203b59cf7 100644 --- a/crates/core/src/auth/token_validation.rs +++ b/crates/core/src/auth/token_validation.rs @@ -232,7 +232,7 @@ impl TokenValidator for CachingOidcTokenValidator { .cache .get(raw_issuer.clone().into()) .await - .ok_or_else(|| anyhow::anyhow!("Error fetching public key for issuer {}", raw_issuer))?; + .ok_or_else(|| anyhow::anyhow!("Error fetching public key for issuer {raw_issuer}"))?; validator.validate_token(token).await } } @@ -425,7 +425,7 @@ mod tests { let result = validator.validate_token(token).await; if result.is_ok() { let claims = result.unwrap(); - anyhow::bail!("Validation succeeded when it should have failed: {:?}", claims); + anyhow::bail!("Validation succeeded when it should have failed: {claims:?}"); } Ok(()) } diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index fa3ea5bdc31..7e7e0e6c17b 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -118,8 +118,7 @@ fn auto_migrate_database( .is_some() { anyhow::bail!( - "Precheck failed: added sequence {} already has values in range", - sequence_name, + "Precheck failed: added sequence {sequence_name} already has values in range", ); } } diff --git a/crates/core/src/host/host_controller.rs b/crates/core/src/host/host_controller.rs index 6c586bb1465..d24f3a30194 100644 --- a/crates/core/src/host/host_controller.rs +++ b/crates/core/src/host/host_controller.rs @@ -697,7 +697,7 @@ async fn update_module( ) -> anyhow::Result { let addr = db.database_identity(); match stored_program_hash(db)? { - None => Err(anyhow!("database `{}` not yet initialized", addr)), + None => Err(anyhow!("database `{addr}` not yet initialized")), Some(stored) => { let res = if stored == program.hash { info!("database `{}` up to date with program `{}`", addr, program.hash); diff --git a/crates/core/src/host/scheduler.rs b/crates/core/src/host/scheduler.rs index 6852982109c..1c20fd422f8 100644 --- a/crates/core/src/host/scheduler.rs +++ b/crates/core/src/host/scheduler.rs @@ -347,7 +347,7 @@ impl SchedulerActor { let (reducer_id, reducer_seed) = module_info .module_def .reducer_arg_deserialize_seed(&reducer[..]) - .ok_or_else(|| anyhow!("Reducer not found: {}", reducer))?; + .ok_or_else(|| anyhow!("Reducer not found: {reducer}"))?; let reducer_args = ReducerArgs::Bsatn(bsatn_args.into()).into_tuple(reducer_seed)?; @@ -370,7 +370,7 @@ impl SchedulerActor { let (reducer_id, reducer_seed) = module_info .module_def .reducer_arg_deserialize_seed(&reducer_name[..]) - .ok_or_else(|| anyhow!("Reducer not found: {}", reducer_name))?; + .ok_or_else(|| anyhow!("Reducer not found: {reducer_name}"))?; let reducer_args = args.into_tuple(reducer_seed)?; Ok(Some(CallReducerParams { @@ -507,8 +507,7 @@ fn process_schedule( .next() .ok_or_else(|| { anyhow!( - "Scheduled table with id {} entry does not exist in `st_scheduled`", - table_id + "Scheduled table with id {table_id} entry does not exist in `st_scheduled`" ) })?; let reducer = st_scheduled_row.read_col::>(reducer_name_col)?; diff --git a/crates/core/src/host/wasmtime/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs index 7fd05b29384..39fa9b7997c 100644 --- a/crates/core/src/host/wasmtime/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -1355,12 +1355,23 @@ impl WasmInstanceEnv { }) } + /// Finds the JWT payload associated with `connection_id`. + /// A `[ByteSourceId]` for the payload will be written to `target_ptr`. + /// If nothing is found for the connection, `[ByteSourceId::INVALID]` (zero) is written to `target_ptr`. + /// + /// This must be called inside a transaction (because it reads from a system table). + /// + /// # Traps + /// + /// Traps if: + /// + /// - `connection_id` does not point to a valid little-endian `ConnectionId`. + /// - This is called outside a transaction. pub fn get_jwt( caller: Caller<'_, Self>, connection_id: WasmPtr, target_ptr: WasmPtr, ) -> RtResult<()> { - log::info!("Calling get_jwt"); Self::with_span(caller, AbiCall::GetJwt, |caller| { let (mem, env) = Self::mem_env(caller); let cid = ConnectionId::read_from(mem, connection_id)?; diff --git a/crates/core/src/subscription/module_subscription_manager.rs b/crates/core/src/subscription/module_subscription_manager.rs index eab10f22aa9..8cf32595672 100644 --- a/crates/core/src/subscription/module_subscription_manager.rs +++ b/crates/core/src/subscription/module_subscription_manager.rs @@ -788,20 +788,20 @@ impl SubscriptionManager { .get_mut(&client_id) .filter(|ci| !ci.dropped.load(Ordering::Acquire)) else { - return Err(anyhow::anyhow!("Client not found: {:?}", client_id).into()); + return Err(anyhow::anyhow!("Client not found: {client_id:?}").into()); }; #[cfg(test)] ci.assert_ref_count_consistency(); let Some(query_hashes) = ci.subscriptions.remove(&subscription_id) else { - return Err(anyhow::anyhow!("Subscription not found: {:?}", subscription_id).into()); + return Err(anyhow::anyhow!("Subscription not found: {subscription_id:?}").into()); }; let mut queries_to_return = Vec::new(); for hash in query_hashes { let remaining_refs = { let Some(count) = ci.subscription_ref_count.get_mut(&hash) else { - return Err(anyhow::anyhow!("Query count not found for query hash: {:?}", hash).into()); + return Err(anyhow::anyhow!("Query count not found for query hash: {hash:?}").into()); }; *count -= 1; *count @@ -813,7 +813,7 @@ impl SubscriptionManager { // The client is no longer subscribed to this query. ci.subscription_ref_count.remove(&hash); let Some(query_state) = self.queries.get_mut(&hash) else { - return Err(anyhow::anyhow!("Query state not found for query hash: {:?}", hash).into()); + return Err(anyhow::anyhow!("Query state not found for query hash: {hash:?}").into()); }; queries_to_return.push(query_state.query.clone()); query_state.subscriptions.remove(&client_id); @@ -870,9 +870,7 @@ impl SubscriptionManager { let hash_set = match ci.subscriptions.try_insert(subscription_id, HashSet::new()) { Err(OccupiedError { .. }) => { return Err(anyhow::anyhow!( - "Subscription with id {:?} already exists for client: {:?}", - query_id, - client_id + "Subscription with id {query_id:?} already exists for client: {client_id:?}" ) .into()); } diff --git a/crates/core/src/subscription/subscription.rs b/crates/core/src/subscription/subscription.rs index 5550602d4bb..2cf3192fefb 100644 --- a/crates/core/src/subscription/subscription.rs +++ b/crates/core/src/subscription/subscription.rs @@ -212,10 +212,10 @@ impl IncrementalJoin { /// An error is returned if the expression is not well-formed. pub fn new(expr: &QueryExpr) -> anyhow::Result { if expr.query.len() != 1 { - return Err(anyhow::anyhow!("expected a single index join, but got {:#?}", expr)); + return Err(anyhow::anyhow!("expected a single index join, but got {expr:#?}")); } let expr::Query::IndexJoin(ref join) = expr.query[0] else { - return Err(anyhow::anyhow!("expected a single index join, but got {:#?}", expr)); + return Err(anyhow::anyhow!("expected a single index join, but got {expr:#?}")); }; let index_table = join diff --git a/crates/datastore/src/locking_tx_datastore/committed_state.rs b/crates/datastore/src/locking_tx_datastore/committed_state.rs index 1f17695e135..9f13653c8d3 100644 --- a/crates/datastore/src/locking_tx_datastore/committed_state.rs +++ b/crates/datastore/src/locking_tx_datastore/committed_state.rs @@ -305,8 +305,7 @@ impl CommittedState { if in_memory != in_st_tables { return Err(anyhow!( - "System table schema mismatch for table id {table_id}. Expected: {schema:?}, found: {:?}", - in_memory + "System table schema mismatch for table id {table_id}. Expected: {schema:?}, found: {in_memory:?}" ) .into()); } diff --git a/crates/datastore/src/locking_tx_datastore/datastore.rs b/crates/datastore/src/locking_tx_datastore/datastore.rs index 8b8a8b5d1d9..605f4dbfe88 100644 --- a/crates/datastore/src/locking_tx_datastore/datastore.rs +++ b/crates/datastore/src/locking_tx_datastore/datastore.rs @@ -1194,7 +1194,7 @@ impl spacetimedb_commitlog::payload::txdata::Visitor for ReplayVi if let Some(name) = self.dropped_table_names.remove(&table_id) { name } else { - return Err(anyhow!("Error looking up name for truncated table {:?}", table_id).into()); + return Err(anyhow!("Error looking up name for truncated table {table_id:?}").into()); } } }; diff --git a/crates/datastore/src/locking_tx_datastore/mut_tx.rs b/crates/datastore/src/locking_tx_datastore/mut_tx.rs index e311af7688b..fb23baefbcd 100644 --- a/crates/datastore/src/locking_tx_datastore/mut_tx.rs +++ b/crates/datastore/src/locking_tx_datastore/mut_tx.rs @@ -196,7 +196,7 @@ impl MutTxId { pub fn create_table(&mut self, mut table_schema: TableSchema) -> Result { let matching_system_table_schema = system_tables().iter().find(|s| **s == table_schema).cloned(); if table_schema.table_id != TableId::SENTINEL && matching_system_table_schema.is_none() { - return Err(anyhow::anyhow!("`table_id` must be `TableId::SENTINEL` in `{:#?}`", table_schema).into()); + return Err(anyhow::anyhow!("`table_id` must be `TableId::SENTINEL` in `{table_schema:#?}`").into()); // checks for children are performed in the relevant `create_...` functions. } @@ -522,14 +522,12 @@ impl MutTxId { .checked_sub(original_table_schema.columns.len()) .ok_or_else(|| { anyhow::anyhow!( - "new column schemas must be more than existing ones for table_id: {}", - table_id + "new column schemas must be more than existing ones for table_id: {table_id}" ) })?; let older_defaults = default_values.len().checked_sub(new_cols).ok_or_else(|| { anyhow::anyhow!( - "not enough default values provided for new columns for table_id: {}", - table_id + "not enough default values provided for new columns for table_id: {table_id}" ) })?; default_values.drain(..older_defaults); @@ -638,7 +636,7 @@ impl MutTxId { pub fn create_index(&mut self, mut index_schema: IndexSchema, is_unique: bool) -> Result { let table_id = index_schema.table_id; if table_id == TableId::SENTINEL { - return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{:#?}`", index_schema).into()); + return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{index_schema:#?}`").into()); } log::trace!( @@ -1005,10 +1003,10 @@ impl MutTxId { /// - The returned ID is unique and not `SequenceId::SENTINEL`. pub fn create_sequence(&mut self, seq: SequenceSchema) -> Result { if seq.sequence_id != SequenceId::SENTINEL { - return Err(anyhow::anyhow!("`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`", seq).into()); + return Err(anyhow::anyhow!("`sequence_id` must be `SequenceId::SENTINEL` in `{seq:#?}`").into()); } if seq.table_id == TableId::SENTINEL { - return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{:#?}`", seq).into()); + return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{seq:#?}`").into()); } let table_id = seq.table_id; @@ -1099,7 +1097,7 @@ impl MutTxId { /// - The returned ID is unique and is not `constraintId::SENTINEL`. fn create_constraint(&mut self, mut constraint: ConstraintSchema) -> Result { if constraint.table_id == TableId::SENTINEL { - return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{:#?}`", constraint).into()); + return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{constraint:#?}`").into()); } let table_id = constraint.table_id; @@ -1193,8 +1191,7 @@ impl MutTxId { pub fn create_row_level_security(&mut self, row_level_security_schema: RowLevelSecuritySchema) -> Result { if row_level_security_schema.table_id == TableId::SENTINEL { return Err(anyhow::anyhow!( - "`table_id` must not be `TableId::SENTINEL` in `{:#?}`", - row_level_security_schema + "`table_id` must not be `TableId::SENTINEL` in `{row_level_security_schema:#?}`" ) .into()); } diff --git a/crates/datastore/src/system_tables.rs b/crates/datastore/src/system_tables.rs index 1ecd69df51f..da97e186e4f 100644 --- a/crates/datastore/src/system_tables.rs +++ b/crates/datastore/src/system_tables.rs @@ -1176,7 +1176,7 @@ impl FromStr for StVarName { ST_VARNAME_SLOW_QRY => Ok(StVarName::SlowQryThreshold), ST_VARNAME_SLOW_SUB => Ok(StVarName::SlowSubThreshold), ST_VARNAME_SLOW_INC => Ok(StVarName::SlowIncThreshold), - _ => Err(anyhow::anyhow!("Invalid system variable {}", s)), + _ => Err(anyhow::anyhow!("Invalid system variable {s}")), } } } diff --git a/crates/execution/src/iter.rs b/crates/execution/src/iter.rs index 0dee161ca5e..259f9f5e236 100644 --- a/crates/execution/src/iter.rs +++ b/crates/execution/src/iter.rs @@ -392,7 +392,7 @@ pub(super) fn get_index(tx: &impl Datastore, table_id: TableId, index_id: IndexI let table = tx.table_or_err(table_id)?; table .get_index_by_id_with_table(tx.blob_store(), index_id) - .ok_or_else(|| anyhow!("IndexId `{}` does not exist", index_id)) + .ok_or_else(|| anyhow!("IndexId `{index_id}` does not exist")) } impl<'a> UniqueIxJoin<'a> { diff --git a/crates/pg/src/pg_server.rs b/crates/pg/src/pg_server.rs index 39b0fcacacd..c8590a4faa4 100644 --- a/crates/pg/src/pg_server.rs +++ b/crates/pg/src/pg_server.rs @@ -238,7 +238,7 @@ impl anyhow::Result<(bool, Duration)> { let filename = filename.as_ref(); - let records = sqllogictest::parse_file(filename).map_err(|e| anyhow!("{:?}", e))?; + let records = sqllogictest::parse_file(filename).map_err(|e| anyhow!("{e:?}"))?; let mut begin_times = vec![]; let mut did_pop = false; @@ -336,7 +336,7 @@ async fn update_test_file anyhow::Result<()> { let filename = filename.as_ref(); - let records = tokio::task::block_in_place(|| sqllogictest::parse_file(filename).map_err(|e| anyhow!("{:?}", e))) + let records = tokio::task::block_in_place(|| sqllogictest::parse_file(filename).map_err(|e| anyhow!("{e:?}"))) .context("failed to parse sqllogictest file")?; let mut begin_times = vec![]; diff --git a/crates/standalone/src/lib.rs b/crates/standalone/src/lib.rs index 8e9ec1272aa..7cf5bff8850 100644 --- a/crates/standalone/src/lib.rs +++ b/crates/standalone/src/lib.rs @@ -511,7 +511,7 @@ pub async fn exec_subcommand(cmd: &str, args: &ArgMatches, db_cores: JobCores) - match cmd { "start" => start::exec(args, db_cores).await, "extract-schema" => extract_schema::exec(args).await, - unknown => Err(anyhow::anyhow!("Invalid subcommand: {}", unknown)), + unknown => Err(anyhow::anyhow!("Invalid subcommand: {unknown}")), } } diff --git a/modules/quickstart-chat/src/lib.rs b/modules/quickstart-chat/src/lib.rs index de77f34bde2..863a35ef2a0 100644 --- a/modules/quickstart-chat/src/lib.rs +++ b/modules/quickstart-chat/src/lib.rs @@ -67,6 +67,10 @@ pub fn init(_ctx: &ReducerContext) {} #[spacetimedb::reducer(client_connected)] pub fn identity_connected(ctx: &ReducerContext) { + log::info!( + "Client connected with jwt payload {:?}", + ctx.sender_auth().jwt().unwrap().raw_payload() + ); if let Some(user) = ctx.db.user().identity().find(ctx.sender) { // If this is a returning user, i.e. we already have a `User` with this `Identity`, // set `online: true`, but leave `name` and `identity` unchanged. diff --git a/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs b/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs index 37e04dc5294..b427d952882 100644 --- a/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/connect_disconnect_client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.4.0 (commit f26e20f1d99a66e7422ff86a037bd4aa80b44963). +// This was generated using spacetimedb cli version 1.5.0 (commit a89b88351ca471e27fb8cd1aef9846de36840f44). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/sdks/rust/tests/test-client/src/main.rs b/sdks/rust/tests/test-client/src/main.rs index f3c1525e21f..50f62523ee9 100644 --- a/sdks/rust/tests/test-client/src/main.rs +++ b/sdks/rust/tests/test-client/src/main.rs @@ -911,7 +911,7 @@ fn exec_on_reducer() { } let row = ctx.db.one_u_8().iter().next().unwrap(); if row.n != value { - anyhow::bail!("Unexpected row value. Expected {} but found {:?}", value, row); + anyhow::bail!("Unexpected row value. Expected {value} but found {row:?}"); } Ok(()) }; @@ -992,10 +992,7 @@ fn exec_fail_reducer() { let row = ctx.db.pk_u_8().iter().next().unwrap(); if row.n != key || row.data != initial_data { anyhow::bail!( - "Unexpected row value. Expected ({}, {}) but found {:?}", - key, - initial_data, - row + "Unexpected row value. Expected ({key}, {initial_data}) but found {row:?}" ); } Ok(()) @@ -1052,10 +1049,7 @@ fn exec_fail_reducer() { let row = ctx.db.pk_u_8().iter().next().unwrap(); if row.n != key || row.data != initial_data { anyhow::bail!( - "Unexpected row value. Expected ({}, {}) but found {:?}", - key, - initial_data, - row + "Unexpected row value. Expected ({key}, {initial_data}) but found {row:?}" ); } Ok(()) diff --git a/sdks/rust/tests/test-client/src/module_bindings/mod.rs b/sdks/rust/tests/test-client/src/module_bindings/mod.rs index 9c8008c7e3c..f82b861677e 100644 --- a/sdks/rust/tests/test-client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/test-client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.4.0 (commit f26e20f1d99a66e7422ff86a037bd4aa80b44963). +// This was generated using spacetimedb cli version 1.5.0 (commit a89b88351ca471e27fb8cd1aef9846de36840f44). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/sdks/rust/tests/test-client/src/pk_test_table.rs b/sdks/rust/tests/test-client/src/pk_test_table.rs index 0e3ef404b97..23e9558fe17 100644 --- a/sdks/rust/tests/test-client/src/pk_test_table.rs +++ b/sdks/rust/tests/test-client/src/pk_test_table.rs @@ -39,10 +39,7 @@ pub fn insert_update_delete_one( let run_checks = || { if row.primary_key() != &key_dup || row.as_value() != update_value { anyhow::bail!( - "Unexpected row value. Expected ({:?}, {}) but found {:?}", - key_dup, - update_value, - row + "Unexpected row value. Expected ({key_dup:?}, {update_value}) but found {row:?}" ); } let Event::Reducer(reducer_event) = &ctx.event else { @@ -68,18 +65,12 @@ pub fn insert_update_delete_one( let run_checks = || { if old.primary_key() != &key_dup || old.as_value() != initial_value { anyhow::bail!( - "Unexpected old row value. Expected ({:?}, {}) but found {:?}", - key_dup, - initial_value, - old, + "Unexpected old row value. Expected ({key_dup:?}, {initial_value}) but found {old:?}", ); } if new.primary_key() != &key_dup || new.as_value() != update_value { anyhow::bail!( - "Unexpected new row value. Expected ({:?}, {}) but found {:?}", - key_dup, - update_value, - new, + "Unexpected new row value. Expected ({key_dup:?}, {update_value}) but found {new:?}", ); } let Event::Reducer(reducer_event) = &ctx.event else { @@ -109,10 +100,7 @@ pub fn insert_update_delete_one( let run_checks = || { if row.primary_key() != &key_dup || row.as_value() != initial_value { anyhow::bail!( - "Unexpected row value. Expected ({:?}, {}) but found {:?}", - key_dup, - initial_value, - row + "Unexpected row value. Expected ({key_dup:?}, {initial_value}) but found {row:?}" ); } let Event::Reducer(reducer_event) = &ctx.event else { diff --git a/sdks/rust/tests/test-client/src/unique_test_table.rs b/sdks/rust/tests/test-client/src/unique_test_table.rs index 58852625e48..1abfe0eb011 100644 --- a/sdks/rust/tests/test-client/src/unique_test_table.rs +++ b/sdks/rust/tests/test-client/src/unique_test_table.rs @@ -35,10 +35,7 @@ pub fn insert_then_delete_one( let run_checks = || { if row.as_key() != &key_dup || row.as_value() != value { anyhow::bail!( - "Unexpected row value. Expected ({:?}, {}) but found {:?}", - key_dup, - value, - row + "Unexpected row value. Expected ({key_dup:?}, {value}) but found {row:?}" ); } let Event::Reducer(reducer_event) = &ctx.event else { @@ -64,10 +61,7 @@ pub fn insert_then_delete_one( let run_checks = || { if row.as_key() != &key_dup || row.as_value() != value { anyhow::bail!( - "Unexpected row value. Expected ({:?}, {}) but found {:?}", - key_dup, - value, - row + "Unexpected row value. Expected ({key_dup:?}, {value}) but found {row:?}" ); } let Event::Reducer(reducer_event) = &ctx.event else { From 0f61291a586dc892732b4fe85eaca13ddb22a086 Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Thu, 2 Oct 2025 14:30:18 -0700 Subject: [PATCH 08/10] fmt --- crates/auth/src/identity.rs | 3 +-- crates/core/src/db/update.rs | 4 +--- crates/core/src/host/scheduler.rs | 6 +----- crates/datastore/src/locking_tx_datastore/mut_tx.rs | 8 ++------ sdks/rust/tests/test-client/src/main.rs | 8 ++------ sdks/rust/tests/test-client/src/pk_test_table.rs | 8 ++------ sdks/rust/tests/test-client/src/unique_test_table.rs | 8 ++------ 7 files changed, 11 insertions(+), 34 deletions(-) diff --git a/crates/auth/src/identity.rs b/crates/auth/src/identity.rs index c4432c414b0..59e16a61d3d 100644 --- a/crates/auth/src/identity.rs +++ b/crates/auth/src/identity.rs @@ -15,8 +15,7 @@ pub struct ConnectionAuthCtx { impl TryFrom for ConnectionAuthCtx { type Error = anyhow::Error; fn try_from(claims: SpacetimeIdentityClaims) -> Result { - let payload = - serde_json::to_string(&claims).map_err(|e| anyhow::anyhow!("Failed to serialize claims: {e}"))?; + let payload = serde_json::to_string(&claims).map_err(|e| anyhow::anyhow!("Failed to serialize claims: {e}"))?; Ok(ConnectionAuthCtx { claims, jwt_payload: payload, diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index 5d172d4872c..0770fea1a40 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -125,9 +125,7 @@ fn auto_migrate_database( .next() .is_some() { - anyhow::bail!( - "Precheck failed: added sequence {sequence_name} already has values in range", - ); + anyhow::bail!("Precheck failed: added sequence {sequence_name} already has values in range",); } } } diff --git a/crates/core/src/host/scheduler.rs b/crates/core/src/host/scheduler.rs index 1c20fd422f8..620c8f059dc 100644 --- a/crates/core/src/host/scheduler.rs +++ b/crates/core/src/host/scheduler.rs @@ -505,11 +505,7 @@ fn process_schedule( let st_scheduled_row = db .iter_by_col_eq_mut(tx, ST_SCHEDULED_ID, table_id_col, &table_id.into())? .next() - .ok_or_else(|| { - anyhow!( - "Scheduled table with id {table_id} entry does not exist in `st_scheduled`" - ) - })?; + .ok_or_else(|| anyhow!("Scheduled table with id {table_id} entry does not exist in `st_scheduled`"))?; let reducer = st_scheduled_row.read_col::>(reducer_name_col)?; Ok(ScheduledReducer { diff --git a/crates/datastore/src/locking_tx_datastore/mut_tx.rs b/crates/datastore/src/locking_tx_datastore/mut_tx.rs index 7100276b4d3..8b48a8c810c 100644 --- a/crates/datastore/src/locking_tx_datastore/mut_tx.rs +++ b/crates/datastore/src/locking_tx_datastore/mut_tx.rs @@ -521,14 +521,10 @@ impl MutTxId { .len() .checked_sub(original_table_schema.columns.len()) .ok_or_else(|| { - anyhow::anyhow!( - "new column schemas must be more than existing ones for table_id: {table_id}" - ) + anyhow::anyhow!("new column schemas must be more than existing ones for table_id: {table_id}") })?; let older_defaults = default_values.len().checked_sub(new_cols).ok_or_else(|| { - anyhow::anyhow!( - "not enough default values provided for new columns for table_id: {table_id}" - ) + anyhow::anyhow!("not enough default values provided for new columns for table_id: {table_id}") })?; default_values.drain(..older_defaults); diff --git a/sdks/rust/tests/test-client/src/main.rs b/sdks/rust/tests/test-client/src/main.rs index 50f62523ee9..ea54e5e67ab 100644 --- a/sdks/rust/tests/test-client/src/main.rs +++ b/sdks/rust/tests/test-client/src/main.rs @@ -991,9 +991,7 @@ fn exec_fail_reducer() { } let row = ctx.db.pk_u_8().iter().next().unwrap(); if row.n != key || row.data != initial_data { - anyhow::bail!( - "Unexpected row value. Expected ({key}, {initial_data}) but found {row:?}" - ); + anyhow::bail!("Unexpected row value. Expected ({key}, {initial_data}) but found {row:?}"); } Ok(()) }; @@ -1048,9 +1046,7 @@ fn exec_fail_reducer() { } let row = ctx.db.pk_u_8().iter().next().unwrap(); if row.n != key || row.data != initial_data { - anyhow::bail!( - "Unexpected row value. Expected ({key}, {initial_data}) but found {row:?}" - ); + anyhow::bail!("Unexpected row value. Expected ({key}, {initial_data}) but found {row:?}"); } Ok(()) }; diff --git a/sdks/rust/tests/test-client/src/pk_test_table.rs b/sdks/rust/tests/test-client/src/pk_test_table.rs index 23e9558fe17..5dce2828c35 100644 --- a/sdks/rust/tests/test-client/src/pk_test_table.rs +++ b/sdks/rust/tests/test-client/src/pk_test_table.rs @@ -38,9 +38,7 @@ pub fn insert_update_delete_one( if delete_result.is_some() { let run_checks = || { if row.primary_key() != &key_dup || row.as_value() != update_value { - anyhow::bail!( - "Unexpected row value. Expected ({key_dup:?}, {update_value}) but found {row:?}" - ); + anyhow::bail!("Unexpected row value. Expected ({key_dup:?}, {update_value}) but found {row:?}"); } let Event::Reducer(reducer_event) = &ctx.event else { anyhow::bail!("Expected a reducer event"); @@ -99,9 +97,7 @@ pub fn insert_update_delete_one( if insert_result.is_some() { let run_checks = || { if row.primary_key() != &key_dup || row.as_value() != initial_value { - anyhow::bail!( - "Unexpected row value. Expected ({key_dup:?}, {initial_value}) but found {row:?}" - ); + anyhow::bail!("Unexpected row value. Expected ({key_dup:?}, {initial_value}) but found {row:?}"); } let Event::Reducer(reducer_event) = &ctx.event else { anyhow::bail!("Expected a reducer event"); diff --git a/sdks/rust/tests/test-client/src/unique_test_table.rs b/sdks/rust/tests/test-client/src/unique_test_table.rs index 1abfe0eb011..cf1304765dc 100644 --- a/sdks/rust/tests/test-client/src/unique_test_table.rs +++ b/sdks/rust/tests/test-client/src/unique_test_table.rs @@ -34,9 +34,7 @@ pub fn insert_then_delete_one( if delete_result.is_some() { let run_checks = || { if row.as_key() != &key_dup || row.as_value() != value { - anyhow::bail!( - "Unexpected row value. Expected ({key_dup:?}, {value}) but found {row:?}" - ); + anyhow::bail!("Unexpected row value. Expected ({key_dup:?}, {value}) but found {row:?}"); } let Event::Reducer(reducer_event) = &ctx.event else { anyhow::bail!("Expected a reducer event"); @@ -60,9 +58,7 @@ pub fn insert_then_delete_one( if insert_result.is_some() { let run_checks = || { if row.as_key() != &key_dup || row.as_value() != value { - anyhow::bail!( - "Unexpected row value. Expected ({key_dup:?}, {value}) but found {row:?}" - ); + anyhow::bail!("Unexpected row value. Expected ({key_dup:?}, {value}) but found {row:?}"); } let Event::Reducer(reducer_event) = &ctx.event else { anyhow::bail!("Expected a reducer event"); From 7d872c5460618e644c78a42515eaae7177dbdd1c Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Thu, 2 Oct 2025 16:00:06 -0700 Subject: [PATCH 09/10] Cargo update --- Cargo.lock | 150 ++++++++++++++++++++++++++--------------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfeb0f646ad..4fe7a715f4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,9 +108,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -1513,9 +1513,9 @@ dependencies = [ [[package]] name = "deflate64" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" +checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" [[package]] name = "deranged" @@ -4960,9 +4960,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.6" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "aws-lc-rs", "ring", @@ -5505,40 +5505,40 @@ dependencies = [ [[package]] name = "spacetimedb" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c17ac7241cf7e4dc8d92e76a9b198ee95712800ee39a6b2b3e2794053383878" +version = "1.5.0" dependencies = [ "bytemuck", "derive_more", "getrandom 0.2.16", + "insta", "log", + "once_cell", "rand 0.8.5", "scoped-tls", - "spacetimedb-bindings-macro 1.4.0", - "spacetimedb-bindings-sys 1.4.0", - "spacetimedb-lib 1.4.0", - "spacetimedb-primitives 1.4.0", + "serde_json", + "spacetimedb-bindings-macro 1.5.0", + "spacetimedb-bindings-sys 1.5.0", + "spacetimedb-lib 1.5.0", + "spacetimedb-primitives 1.5.0", + "trybuild", ] [[package]] name = "spacetimedb" version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd17d19bdeff3f0c83588a2112fafab2eec51dd321fc2f0762be5cb6f84291" dependencies = [ "bytemuck", "derive_more", "getrandom 0.2.16", - "insta", "log", - "once_cell", "rand 0.8.5", "scoped-tls", - "serde_json", - "spacetimedb-bindings-macro 1.5.0", - "spacetimedb-bindings-sys 1.5.0", - "spacetimedb-lib 1.5.0", - "spacetimedb-primitives 1.5.0", - "trybuild", + "spacetimedb-bindings-macro 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-bindings-sys 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-lib 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-primitives 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5602,44 +5602,44 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-macro" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b67c97f816405cb028c5ad28beef523b7ffbd0555adbe311e96b7d20859231f" +version = "1.5.0" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", - "spacetimedb-primitives 1.4.0", + "spacetimedb-primitives 1.5.0", "syn 2.0.106", ] [[package]] name = "spacetimedb-bindings-macro" version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b8b68c082bfe3165f0c0fddf24446a89de9d3ec6bb2a47eb4060c05788b775" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", - "spacetimedb-primitives 1.5.0", + "spacetimedb-primitives 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "syn 2.0.106", ] [[package]] name = "spacetimedb-bindings-sys" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850c852da7938f4f0ccfe6351288b3a109403b9525296a0ad356eb927b86d1a8" +version = "1.5.0" dependencies = [ - "spacetimedb-primitives 1.4.0", + "spacetimedb-primitives 1.5.0", ] [[package]] name = "spacetimedb-bindings-sys" version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c318bc9e80e05d45650f3506ebf73f4104a899b65f281ec41438fae3e973df" dependencies = [ - "spacetimedb-primitives 1.5.0", + "spacetimedb-primitives 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6091,49 +6091,49 @@ dependencies = [ [[package]] name = "spacetimedb-lib" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03deee45ef9169141855a852720a86cffacfbf49e4f083a2dfb1fce1244ae588" +version = "1.5.0" dependencies = [ "anyhow", "bitflags 2.9.4", "blake3", + "bytes", "chrono", "derive_more", "enum-as-inner", + "enum-map", "hex", + "insta", "itertools 0.12.1", - "spacetimedb-bindings-macro 1.4.0", - "spacetimedb-primitives 1.4.0", - "spacetimedb-sats 1.4.0", + "proptest", + "proptest-derive", + "ron", + "serde", + "serde_json", + "spacetimedb-bindings-macro 1.5.0", + "spacetimedb-memory-usage", + "spacetimedb-metrics", + "spacetimedb-primitives 1.5.0", + "spacetimedb-sats 1.5.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-lib" version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7916cf88443f49f1bc4ea0c533a2ada72c1871c6537fcb508fd5ca0bf7ddb72b" dependencies = [ "anyhow", "bitflags 2.9.4", "blake3", - "bytes", "chrono", "derive_more", "enum-as-inner", - "enum-map", "hex", - "insta", "itertools 0.12.1", - "proptest", - "proptest-derive", - "ron", - "serde", - "serde_json", - "spacetimedb-bindings-macro 1.5.0", - "spacetimedb-memory-usage", - "spacetimedb-metrics", - "spacetimedb-primitives 1.5.0", - "spacetimedb-sats 1.5.0", + "spacetimedb-bindings-macro 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-primitives 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-sats 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", ] @@ -6209,26 +6209,26 @@ dependencies = [ [[package]] name = "spacetimedb-primitives" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f4f32ceb58cef39442db5d25022d7a9d9bf21af65345c1313b92b9c75f32e5" +version = "1.5.0" dependencies = [ "bitflags 2.9.4", "either", "itertools 0.12.1", "nohash-hasher", + "proptest", + "spacetimedb-memory-usage", ] [[package]] name = "spacetimedb-primitives" version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949951659579c71b72bf432ad4729f95e47c6c260d368e418b0790927fa70b21" dependencies = [ "bitflags 2.9.4", "either", "itertools 0.12.1", "nohash-hasher", - "proptest", - "spacetimedb-memory-usage", ] [[package]] @@ -6250,15 +6250,16 @@ dependencies = [ [[package]] name = "spacetimedb-sats" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ba7301f5ed39dbf2e520ce3d2ad436ab943b3bb2cdecd035a64970ccedce4d" +version = "1.5.0" dependencies = [ + "ahash 0.8.12", "anyhow", "arrayvec", "bitflags 2.9.4", + "blake3", "bytemuck", "bytes", + "bytestring", "chrono", "decorum", "derive_more", @@ -6266,26 +6267,32 @@ dependencies = [ "ethnum", "hex", "itertools 0.12.1", + "proptest", + "proptest-derive", + "rand 0.9.2", "second-stack", + "serde", + "serde_json", "sha3", "smallvec", - "spacetimedb-bindings-macro 1.4.0", - "spacetimedb-primitives 1.4.0", + "spacetimedb-bindings-macro 1.5.0", + "spacetimedb-memory-usage", + "spacetimedb-metrics", + "spacetimedb-primitives 1.5.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-sats" version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b393d53adce6aa28b1b65f0b85dc3b1c9f796b32fb00514fd49c32605599e1" dependencies = [ - "ahash 0.8.12", "anyhow", "arrayvec", "bitflags 2.9.4", - "blake3", "bytemuck", "bytes", - "bytestring", "chrono", "decorum", "derive_more", @@ -6293,18 +6300,11 @@ dependencies = [ "ethnum", "hex", "itertools 0.12.1", - "proptest", - "proptest-derive", - "rand 0.9.2", "second-stack", - "serde", - "serde_json", "sha3", "smallvec", - "spacetimedb-bindings-macro 1.5.0", - "spacetimedb-memory-usage", - "spacetimedb-metrics", - "spacetimedb-primitives 1.5.0", + "spacetimedb-bindings-macro 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spacetimedb-primitives 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", ] @@ -7627,9 +7627,9 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "typescript-test-app" @@ -7637,7 +7637,7 @@ version = "0.1.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.4.0", + "spacetimedb 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] From 291c20bb8bb4cb0d44ab0cb877cd1af39ccc0d6c Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Thu, 2 Oct 2025 16:01:33 -0700 Subject: [PATCH 10/10] Update comment --- crates/bindings-sys/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bindings-sys/src/lib.rs b/crates/bindings-sys/src/lib.rs index ba179a2f3e3..65908161d92 100644 --- a/crates/bindings-sys/src/lib.rs +++ b/crates/bindings-sys/src/lib.rs @@ -622,6 +622,14 @@ pub mod raw { /// Find the jwt payload for the given connection id, and write the /// [`BytesSource`] to the given pointer. /// If this is not found, [`BytesSource::INVALID`] (aka 0) will be written. + /// This must be called inside a transaction (because it reads from a system table). + /// + /// # Traps + /// + /// Traps if: + /// + /// - `connection_id` does not point to a valid little-endian `ConnectionId`. + /// - This is called outside a transaction. pub fn get_jwt(connection_id_ptr: *const u8, bytes_source_id: *mut BytesSource); }