Our Personal Data Server from scratch!
0

Configure Feed

Select the types of activity you want to include in your feed.

db: newtype Rkey and final newtype touches for now

Lewis: May this revision serve well! <lu5a@proton.me>

author
Lewis
date (Jul 12, 2026, 8:03 AM +0200) commit e41f3474 parent fbfa15b0 change-id urwkuylo
+324 -329
+1 -1
crates/tranquil-api/src/admin/account/info.rs
··· 204 204 let accounts = state 205 205 .repos 206 206 .infra 207 - .get_admin_account_infos_by_dids(&dids_typed) 207 + .get_admin_account_infos_by_dids(&dids) 208 208 .await 209 209 .log_db_err("fetching account infos")?; 210 210
+2 -4
crates/tranquil-api/src/admin/invite.rs
··· 29 29 { 30 30 error!("DB error disabling invite codes: {:?}", e); 31 31 } 32 - if let Some(accounts) = &input.accounts { 33 - let accounts_typed: Vec<tranquil_types::Did> = 34 - accounts.iter().filter_map(|a| a.parse().ok()).collect(); 35 - if let Err(e) = state 32 + if let Some(accounts) = &input.accounts 33 + && let Err(e) = state 36 34 .repos 37 35 .infra 38 36 .disable_invite_codes_by_account(accounts)
+1 -1
crates/tranquil-api/src/common.rs
··· 219 219 password_hash: Option<&PasswordHash>, 220 220 ) -> Option<CredentialMatch> { 221 221 let main_valid = password_hash 222 - .map(|h| bcrypt::verify(password, h).unwrap_or(false)) 222 + .map(|h| bcrypt::verify(password, h.as_str()).unwrap_or(false)) 223 223 .unwrap_or(false); 224 224 if main_valid { 225 225 return Some(CredentialMatch::MainPassword);
-2
crates/tranquil-api/src/delegation.rs
··· 471 471 Some(user) => user.did, 472 472 None => tranquil_pds::handle::resolve_handle(&handle) 473 473 .await 474 - .map_err(|_| ApiError::ControllerNotFound)? 475 - .parse() 476 474 .map_err(|_| ApiError::ControllerNotFound)?, 477 475 } 478 476 };
+20 -6
crates/tranquil-api/src/identity/account.rs
··· 204 204 let verifier = ServiceTokenVerifier::new(); 205 205 let create_account_lxm = Nsid::from("com.atproto.server.createAccount".to_string()); 206 206 match verifier 207 - .verify_service_token(&token, Some("com.atproto.server.createAccount")) 207 + .verify_service_token(&token, Some(&create_account_lxm)) 208 208 .await 209 209 { 210 210 Ok(claims) => { ··· 301 301 }; 302 302 let hostname = &cfg.server.hostname; 303 303 let key_result = 304 - match super::provision::resolve_signing_key(&state, input.signing_key.as_deref()).await { 304 + match super::provision::resolve_signing_key(&state, input.signing_key.as_ref()).await { 305 305 Ok(k) => k, 306 306 Err(e) => return e.into_response(), 307 307 }; ··· 339 339 return ApiError::InvalidDid(e.to_string()).into_response(); 340 340 } 341 341 info!(did = %d, "Creating external did:web account"); 342 - d.clone() 342 + match d.parse() { 343 + Ok(d) => d, 344 + Err(_) => return ApiError::InvalidDid("Invalid DID format".into()).into_response(), 345 + } 343 346 } 344 347 _ => { 345 348 if let Some(d) = &input.did { 346 349 if d.starts_with("did:plc:") && is_migration { 347 350 info!(did = %d, "Migration with existing did:plc"); 348 - d.clone() 351 + match d.parse() { 352 + Ok(d) => d, 353 + Err(_) => { 354 + return ApiError::InvalidDid("Invalid DID format".into()) 355 + .into_response(); 356 + } 357 + } 349 358 } else if d.starts_with("did:web:") { 350 359 if !is_did_web_byod 351 360 && let Err(e) = ··· 354 363 { 355 364 return ApiError::InvalidDid(e.to_string()).into_response(); 356 365 } 357 - d.clone() 366 + match d.parse() { 367 + Ok(d) => d, 368 + Err(_) => { 369 + return ApiError::InvalidDid("Invalid DID format".into()) 370 + .into_response(); 371 + } 372 + } 358 373 } else if !d.trim().is_empty() { 359 374 return ApiError::InvalidDid( 360 375 "Only did:web DIDs can be provided; leave empty for did:plc. For migration with existing did:plc, provide service auth.".into() ··· 528 543 let session = match super::provision::create_and_store_session( 529 544 &state, 530 545 &did, 531 - &did_for_commit, 532 546 &secret_key_bytes, 533 547 "transition:generic transition:chat.bsky", 534 548 None,
+2 -6
crates/tranquil-api/src/identity/did.rs
··· 167 167 let user = match state 168 168 .repos 169 169 .user 170 - .get_user_for_did_doc_build(&expected_did_typed) 170 + .get_user_for_did_doc_build(&expected_did) 171 171 .await 172 172 { 173 173 Ok(Some(u)) => u, ··· 221 221 let user = match state 222 222 .repos 223 223 .user 224 - .get_did_web_info_by_handle(&current_handle_typed) 224 + .get_did_web_info_by_handle(&current_handle) 225 225 .await 226 226 { 227 227 Ok(Some(u)) => u, ··· 627 627 .parse() 628 628 .map_err(|_| ApiError::InvalidHandle(Some("Invalid handle format".into())))?; 629 629 if new_handle == current_handle { 630 - let handle_typed: Handle = match new_handle.parse() { 631 - Ok(h) => h, 632 - Err(_) => return Err(ApiError::InvalidHandle(None)), 633 - }; 634 630 if let Err(e) = 635 631 tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle)).await 636 632 {
+1 -2
crates/tranquil-api/src/identity/provision.rs
··· 255 255 256 256 pub async fn create_and_store_session( 257 257 state: &AppState, 258 - did_str: &str, 259 258 did: &Did, 260 259 signing_key_bytes: &[u8], 261 260 scope: &str, ··· 269 268 let refresh_meta = 270 269 tranquil_pds::auth::create_refresh_token_with_metadata(did, signing_key_bytes).map_err( 271 270 |e| { 272 - tracing::error!("Error creating access token: {:?}", e); 271 + tracing::error!("Error creating refresh token: {:?}", e); 273 272 ApiError::InternalError(None) 274 273 }, 275 274 )?;
+2 -2
crates/tranquil-api/src/moderation/mod.rs
··· 6 6 }; 7 7 use serde::{Deserialize, Serialize}; 8 8 use serde_json::{Value, json}; 9 - use tracing::{error, info}; 9 + use tracing::{error, info, warn}; 10 10 use tranquil_pds::api::ApiError; 11 11 use tranquil_pds::api::proxy_client::{is_ssrf_safe, proxy_client}; 12 12 use tranquil_pds::auth::{AnyUser, Auth}; ··· 149 149 let service_token = match tranquil_pds::auth::create_service_token( 150 150 &auth_user.did, 151 151 service_did, 152 - Some("com.atproto.moderation.createReport"), 152 + Some(&report_lxm), 153 153 &key_bytes, 154 154 ) { 155 155 Ok(t) => t,
+2 -2
crates/tranquil-api/src/repo/import.rs
··· 237 237 state 238 238 .repos 239 239 .repo 240 - .update_repo_root(user_id, &new_root_cid_link, &new_rev_str) 240 + .update_repo_root(user_id, &new_root_cid_link, &new_rev_tid) 241 241 .await 242 242 .map_err(|e| { 243 243 error!("Failed to update repo root: {:?}", e); ··· 248 248 state 249 249 .repos 250 250 .repo 251 - .insert_user_blocks(user_id, &all_block_cids, &new_rev_str) 251 + .insert_user_blocks(user_id, &all_block_cids, &new_rev_tid) 252 252 .await 253 253 .map_err(|e| { 254 254 error!("Failed to insert user_blocks: {:?}", e);
+1 -1
crates/tranquil-api/src/server/app_password.rs
··· 191 191 } 192 192 Ok(Json(CreateAppPasswordOutput { 193 193 name: name.to_string(), 194 - password, 194 + password: password.into_inner(), 195 195 created_at: created_at.to_rfc3339(), 196 196 privileged: privilege.is_privileged(), 197 197 scopes: final_scopes,
+1 -1
crates/tranquil-api/src/server/email.rs
··· 395 395 396 396 #[derive(Deserialize)] 397 397 pub struct CheckEmailVerifiedInput { 398 - pub identifier: String, 398 + pub identifier: AtIdentifier, 399 399 } 400 400 401 401 pub async fn check_email_verified(
+3 -6
crates/tranquil-api/src/server/invite.rs
··· 18 18 19 19 #[derive(Serialize)] 20 20 pub struct CreateInviteCodeOutput { 21 - pub code: String, 21 + pub code: InviteCodeValue, 22 22 } 23 23 24 24 pub async fn create_invite_code( ··· 117 117 infra_repo 118 118 .create_invite_codes_batch(&codes, use_count, admin_user_id, Some(&account)) 119 119 .await 120 - .map(|_| AccountCodes { 121 - account: account.to_string(), 122 - codes, 123 - }) 120 + .map(|_| AccountCodes { account, codes }) 124 121 } 125 122 })) 126 123 .await; ··· 146 143 #[derive(Serialize)] 147 144 #[serde(rename_all = "camelCase")] 148 145 pub struct InviteCode { 149 - pub code: String, 146 + pub code: InviteCodeValue, 150 147 pub available: i32, 151 148 pub disabled: bool, 152 149 pub for_account: String,
+9 -6
crates/tranquil-api/src/server/passkey_account.rs
··· 71 71 let verifier = ServiceTokenVerifier::new(); 72 72 let create_account_lxm = Nsid::from("com.atproto.server.createAccount".to_string()); 73 73 match verifier 74 - .verify_service_token(&token, Some("com.atproto.server.createAccount")) 74 + .verify_service_token(&token, Some(&create_account_lxm)) 75 75 .await 76 76 { 77 77 Ok(claims) => { ··· 144 144 let did_type = input.did_type.as_deref().unwrap_or("plc"); 145 145 146 146 let key_result = 147 - match crate::identity::provision::resolve_signing_key(&state, input.signing_key.as_deref()) 147 + match crate::identity::provision::resolve_signing_key(&state, input.signing_key.as_ref()) 148 148 .await 149 149 { 150 150 Ok(k) => k, ··· 192 192 d, 193 193 hostname, 194 194 &input.handle, 195 - input.signing_key.as_deref(), 195 + input.signing_key.as_ref(), 196 196 ) 197 197 .await 198 198 { ··· 200 200 } 201 201 info!(did = %d, "Creating external did:web passkey account (reserved key)"); 202 202 } 203 - d.to_string() 203 + d.parse() 204 + .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))? 204 205 } 205 206 _ => { 206 207 if let Some(ref auth_did) = byod_auth { ··· 213 214 ))); 214 215 } 215 216 info!(did = %provided_did, "Creating BYOD did:plc passkey account (migration)"); 216 - provided_did.clone() 217 + provided_did 218 + .parse() 219 + .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))? 217 220 } else { 218 221 return Err(ApiError::InvalidRequest( 219 222 "BYOD migration requires a did:plc or did:web DID".into(), ··· 545 548 Ok(Json(CompletePasskeySetupOutput { 546 549 did: input.did.clone(), 547 550 handle: user.handle, 548 - app_password, 551 + app_password: app_password.into_inner(), 549 552 app_password_name, 550 553 })) 551 554 }
+1 -1
crates/tranquil-api/src/server/reauth.rs
··· 85 85 .unwrap_or_default(); 86 86 87 87 let app_password_valid = app_password_hashes.iter().fold(false, |acc, h| { 88 - acc | bcrypt::verify(&input.password, h).unwrap_or(false) 88 + acc | bcrypt::verify(&input.password, h.as_str()).unwrap_or(false) 89 89 }); 90 90 91 91 if !app_password_valid {
+1 -1
crates/tranquil-api/src/server/service_auth.rs
··· 118 118 &auth.auth_source, 119 119 auth.scope.as_deref(), 120 120 params.aud.as_str(), 121 - method.as_str(), 121 + method, 122 122 ) { 123 123 return e.into_response(); 124 124 }
+2 -2
crates/tranquil-api/src/server/session.rs
··· 91 91 let row = match state 92 92 .repos 93 93 .user 94 - .get_login_full_by_identifier(normalized_identifier.as_str()) 94 + .get_login_full_by_identifier(&login_identifier) 95 95 .await 96 96 { 97 97 Ok(Some(row)) => row, ··· 277 277 &row.did, 278 278 &key_bytes, 279 279 app_password_scopes.as_deref(), 280 - app_password_controller.as_deref(), 280 + app_password_controller.as_ref(), 281 281 None, 282 282 ) { 283 283 Ok(m) => m,
+2 -2
crates/tranquil-auth/src/types.rs
··· 248 248 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 249 249 pub enum TokenVerifyError { 250 250 Expired, 251 - Invalid, 251 + Invalid(&'static str), 252 252 } 253 253 254 254 impl fmt::Display for TokenVerifyError { 255 255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 256 256 match self { 257 257 Self::Expired => write!(f, "Token expired"), 258 - Self::Invalid => write!(f, "Token invalid"), 258 + Self::Invalid(reason) => write!(f, "{}", reason), 259 259 } 260 260 } 261 261 }
+6 -4
crates/tranquil-comms/src/email/mod.rs
··· 129 129 130 130 fn build_direct_mx(cfg: &tranquil_config::TranquilConfig) -> Result<SendMode, SendError> { 131 131 let helo = resolve_helo(cfg)?; 132 - let resolver = Arc::new(TokioAsyncResolver::tokio_from_system_conf().unwrap_or_else(|e| { 133 - tracing::warn!("falling back to default DNS resolvers: {}", e); 134 - TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()) 135 - })); 132 + let resolver = Arc::new( 133 + TokioAsyncResolver::tokio_from_system_conf().unwrap_or_else(|e| { 134 + tracing::warn!("falling back to default DNS resolvers: {}", e); 135 + TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()) 136 + }), 137 + ); 136 138 let max_concurrent = cfg.email.direct_mx.max_concurrent_sends.max(1); 137 139 Ok(SendMode::DirectMx { 138 140 resolver,
+1 -8
crates/tranquil-db-traits/src/repo.rs
··· 51 51 } 52 52 } 53 53 54 - pub fn for_firehose(&self) -> Option<&'static str> { 55 - match self { 56 - Self::Active => None, 57 - other => Some(other.as_str()), 58 - } 59 - } 60 - 61 - pub fn for_firehose_typed(&self) -> Option<Self> { 54 + pub fn for_firehose(&self) -> Option<Self> { 62 55 match self { 63 56 Self::Active => None, 64 57 other => Some(*other),
+4 -4
crates/tranquil-db-traits/src/user.rs
··· 149 149 150 150 async fn get_login_check_by_identifier( 151 151 &self, 152 - identifier: &str, 152 + identifier: &AtIdentifier, 153 153 ) -> Result<Option<UserLoginCheck>, DbError>; 154 154 155 155 async fn get_login_info_by_identifier( 156 156 &self, 157 - identifier: &str, 157 + identifier: &AtIdentifier, 158 158 ) -> Result<Option<UserLoginInfo>, DbError>; 159 159 160 160 async fn get_2fa_status_by_did(&self, did: &Did) -> Result<Option<User2faStatus>, DbError>; ··· 211 211 212 212 async fn check_email_verified_by_identifier( 213 213 &self, 214 - identifier: &str, 214 + identifier: &AtIdentifier, 215 215 ) -> Result<Option<bool>, DbError>; 216 216 217 217 async fn check_channel_verified_by_did( ··· 428 428 429 429 async fn get_login_full_by_identifier( 430 430 &self, 431 - identifier: &str, 431 + identifier: &AtIdentifier, 432 432 ) -> Result<Option<UserLoginFull>, DbError>; 433 433 434 434 async fn get_confirm_signup_by_did(
+1 -1
crates/tranquil-db/src/postgres/blob.rs
··· 118 118 FROM repo_seq 119 119 WHERE did = $1 AND rev > $2 AND blobs IS NOT NULL"#, 120 120 did.as_str(), 121 - since 121 + since.as_str() 122 122 ) 123 123 .fetch_all(&self.pool) 124 124 .await
+47 -16
crates/tranquil-db/src/postgres/infra.rs
··· 159 159 let result = sqlx::query!( 160 160 r#"INSERT INTO invite_codes (code, available_uses, created_by_user, for_account) 161 161 SELECT $1, $2, id, $3 FROM users WHERE is_admin = true LIMIT 1"#, 162 - code, 162 + code.as_str(), 163 163 use_count, 164 164 for_account_str 165 165 ) ··· 200 200 ) -> Result<Option<i32>, DbError> { 201 201 let result = sqlx::query_scalar!( 202 202 "SELECT available_uses FROM invite_codes WHERE code = $1 FOR UPDATE", 203 - code 203 + code.as_str() 204 204 ) 205 205 .fetch_optional(&self.pool) 206 206 .await ··· 215 215 ) -> Result<ValidatedInviteCode<'a>, InviteCodeError> { 216 216 let result = sqlx::query!( 217 217 r#"SELECT available_uses, COALESCE(disabled, false) as "disabled!" FROM invite_codes WHERE code = $1"#, 218 - code 218 + code.as_str() 219 219 ) 220 220 .fetch_optional(&self.pool) 221 221 .await ··· 270 270 JOIN users u ON icu.used_by_user = u.id 271 271 WHERE icu.code = $1 272 272 ORDER BY icu.used_at DESC"#, 273 - code 273 + code.as_str() 274 274 ) 275 275 .fetch_all(&self.pool) 276 276 .await ··· 279 279 Ok(results 280 280 .into_iter() 281 281 .map(|r| InviteCodeUse { 282 - code: code.to_string(), 282 + code: code.clone(), 283 283 used_by_did: Did::from(r.did), 284 284 used_by_handle: Some(Handle::from(r.handle)), 285 285 used_at: r.used_at, ··· 348 348 ) 349 349 .fetch_all(&self.pool) 350 350 .await 351 - .map_err(map_sqlx_error)?, 352 - (None, InviteCodeSortOrder::Recent) => sqlx::query_as!( 353 - InviteCodeRow, 351 + .map_err(map_sqlx_error)? 352 + .into_iter() 353 + .map(|r| { 354 + to_row( 355 + r.code, 356 + r.available_uses, 357 + r.disabled, 358 + r.created_by_user, 359 + r.created_at, 360 + ) 361 + }) 362 + .collect(), 363 + (None, InviteCodeSortOrder::Recent) => sqlx::query!( 354 364 r#"SELECT ic.code, ic.available_uses, ic.disabled, ic.created_by_user, ic.created_at 355 365 FROM invite_codes ic 356 366 ORDER BY created_at DESC ··· 382 392 ) 383 393 .fetch_all(&self.pool) 384 394 .await 385 - .map_err(map_sqlx_error)?, 386 - (None, InviteCodeSortOrder::Usage) => sqlx::query_as!( 387 - InviteCodeRow, 395 + .map_err(map_sqlx_error)? 396 + .into_iter() 397 + .map(|r| { 398 + to_row( 399 + r.code, 400 + r.available_uses, 401 + r.disabled, 402 + r.created_by_user, 403 + r.created_at, 404 + ) 405 + }) 406 + .collect(), 407 + (None, InviteCodeSortOrder::Usage) => sqlx::query!( 388 408 r#"SELECT ic.code, ic.available_uses, ic.disabled, ic.created_by_user, ic.created_at 389 409 FROM invite_codes ic 390 410 ORDER BY available_uses DESC ··· 393 413 ) 394 414 .fetch_all(&self.pool) 395 415 .await 396 - .map_err(map_sqlx_error)?, 416 + .map_err(map_sqlx_error)? 417 + .into_iter() 418 + .map(|r| { 419 + to_row( 420 + r.code, 421 + r.available_uses, 422 + r.disabled, 423 + r.created_by_user, 424 + r.created_at, 425 + ) 426 + }) 427 + .collect(), 397 428 }; 398 429 399 430 Ok(results) ··· 476 507 FROM invite_codes ic 477 508 JOIN users u ON ic.created_by_user = u.id 478 509 WHERE ic.code = $1"#, 479 - code 510 + code.as_str() 480 511 ) 481 512 .fetch_optional(&self.pool) 482 513 .await ··· 578 609 VALUES ($1, $2, $3, $4) 579 610 RETURNING id"#, 580 611 did_str, 581 - public_key_did_key, 612 + public_key_did_key.as_str(), 582 613 private_key_bytes, 583 614 expires_at 584 615 ) ··· 600 631 AND used_at IS NULL 601 632 AND expires_at > NOW() 602 633 FOR UPDATE"#, 603 - public_key_did_key 634 + public_key_did_key.as_str() 604 635 ) 605 636 .fetch_optional(&self.pool) 606 637 .await ··· 1165 1196 let row = sqlx::query!( 1166 1197 r#"SELECT id, did, public_key_did_key, private_key_bytes, expires_at, used_at 1167 1198 FROM reserved_signing_keys WHERE public_key_did_key = $1"#, 1168 - public_key_did_key 1199 + public_key_did_key.as_str() 1169 1200 ) 1170 1201 .fetch_optional(&self.pool) 1171 1202 .await
+6 -7
crates/tranquil-db/src/postgres/oauth.rs
··· 7 7 ScopePreference, TokenFamilyId, TrustedDeviceRow, TwoFactorChallenge, 8 8 }; 9 9 use tranquil_oauth::{ 10 - AuthorizationRequestParameters, AuthorizedClientData, ClientAuth, Code as OAuthCode, 11 - DeviceData, DeviceId as OAuthDeviceId, RefreshToken as OAuthRefreshToken, RequestData, 12 - SessionId as OAuthSessionId, TokenData, TokenId as OAuthTokenId, 10 + AuthorizationRequestParameters, AuthorizedClientData, ClientAuth, DeviceData, RequestData, 11 + SessionId as OAuthSessionId, TokenData, 13 12 }; 14 13 use tranquil_types::{ 15 14 AuthorizationCode, ClientId, DPoPProofId, DeviceId, Did, Handle, RefreshToken, RequestId, ··· 61 60 RETURNING id 62 61 "#, 63 62 data.did.as_str(), 64 - &data.token_id.0, 63 + data.token_id.as_str(), 65 64 data.created_at, 66 65 data.updated_at, 67 66 data.expires_at, ··· 442 441 client_auth_json, 443 442 parameters_json, 444 443 data.expires_at, 445 - data.code.as_ref().map(|c| c.0.as_str()), 444 + data.code.as_deref(), 446 445 ) 447 446 .execute(&self.pool) 448 447 .await ··· 720 719 VALUES ($1, $2, $3, $4, $5) 721 720 "#, 722 721 device_id.as_str(), 723 - &data.session_id.0, 722 + data.session_id.as_str(), 724 723 data.user_agent, 725 724 data.ip_address, 726 725 data.last_seen_at, ··· 744 743 .await 745 744 .map_err(map_sqlx_error)?; 746 745 Ok(row.map(|r| DeviceData { 747 - session_id: OAuthSessionId(r.session_id), 746 + session_id: OAuthSessionId::from(r.session_id), 748 747 user_agent: r.user_agent, 749 748 ip_address: r.ip_address, 750 749 last_seen_at: r.last_seen_at,
+12 -4
crates/tranquil-db/src/postgres/repo.rs
··· 770 770 "#, 771 771 ) 772 772 .bind(user_id) 773 - .bind(since_rev) 773 + .bind(since_rev.as_str()) 774 774 .fetch_all(&self.pool) 775 775 .await 776 776 .map_err(map_sqlx_error)?; ··· 780 780 781 781 async fn insert_commit_event(&self, data: &CommitEventData) -> Result<(), DbError> { 782 782 let (block_cids, block_data) = inline_to_paired_blocks(data.blocks.as_deref()); 783 + let blob_strs: Option<Vec<String>> = data 784 + .blobs 785 + .as_ref() 786 + .map(|blobs| blobs.iter().map(|c| c.to_string()).collect()); 783 787 sqlx::query!( 784 788 r#" 785 789 INSERT INTO repo_seq (did, event_type, commit_cid, prev_cid, ops, blobs, block_cids, block_data, prev_data_cid, rev) ··· 790 794 data.commit_cid.as_ref().map(|c| c.as_str()), 791 795 data.prev_cid.as_ref().map(|c| c.as_str()), 792 796 data.ops, 793 - data.blobs.as_deref(), 797 + blob_strs.as_deref(), 794 798 &block_cids as &[Vec<u8>], 795 799 &block_data as &[Vec<u8>], 796 800 data.prev_data_cid.as_ref().map(|c| c.as_str()), ··· 828 832 829 833 async fn insert_account_event(&self, did: &Did, status: AccountStatus) -> Result<(), DbError> { 830 834 let active = status.is_active(); 831 - let status_str = status.for_firehose(); 835 + let status_str = status.for_firehose().map(|s| s.as_str()); 832 836 sqlx::query!( 833 837 r#" 834 838 INSERT INTO repo_seq (did, event_type, active, status) ··· 1467 1471 1468 1472 let event = input.commit_event; 1469 1473 let (event_block_cids, event_block_data) = inline_into_paired_blocks(event.blocks); 1474 + let event_blob_strs: Option<Vec<String>> = event 1475 + .blobs 1476 + .as_ref() 1477 + .map(|blobs| blobs.iter().map(|c| c.to_string()).collect()); 1470 1478 sqlx::query!( 1471 1479 r#" 1472 1480 INSERT INTO repo_seq (did, event_type, commit_cid, prev_cid, ops, blobs, block_cids, block_data, prev_data_cid, rev) ··· 1477 1485 event.commit_cid.as_ref().map(|c| c.as_str()), 1478 1486 event.prev_cid.as_ref().map(|c| c.as_str()), 1479 1487 event.ops, 1480 - event.blobs.as_deref(), 1488 + event_blob_strs.as_deref(), 1481 1489 &event_block_cids as &[Vec<u8>], 1482 1490 &event_block_data as &[Vec<u8>], 1483 1491 event.prev_data_cid.as_ref().map(|c| c.as_str()),
+1 -1
crates/tranquil-db/src/postgres/session.rs
··· 161 161 let result = sqlx::query!( 162 162 "DELETE FROM session_tokens WHERE did = $1 AND access_jti != $2", 163 163 did.as_str(), 164 - except_jti 164 + except_jti.as_str() 165 165 ) 166 166 .execute(&self.pool) 167 167 .await
+11 -11
crates/tranquil-db/src/postgres/user.rs
··· 201 201 JOIN users u ON t.did = u.did 202 202 LEFT JOIN user_keys k ON u.id = k.user_id 203 203 WHERE t.token_id = $1"#, 204 - token_id 204 + token_id.as_str() 205 205 ) 206 206 .fetch_optional(&self.pool) 207 207 .await ··· 588 588 589 589 async fn check_email_verified_by_identifier( 590 590 &self, 591 - identifier: &str, 591 + identifier: &AtIdentifier, 592 592 ) -> Result<Option<bool>, DbError> { 593 593 let row = sqlx::query_scalar!( 594 594 "SELECT email_verified FROM users WHERE did = $1 OR email = $1 OR handle = $1", 595 - identifier 595 + identifier.as_str() 596 596 ) 597 597 .fetch_optional(&self.pool) 598 598 .await ··· 1423 1423 1424 1424 async fn get_login_check_by_identifier( 1425 1425 &self, 1426 - identifier: &str, 1426 + identifier: &AtIdentifier, 1427 1427 ) -> Result<Option<UserLoginCheck>, DbError> { 1428 1428 sqlx::query!( 1429 1429 "SELECT did, password_hash FROM users WHERE handle = $1 OR did = $1", 1430 - identifier 1430 + identifier.as_str() 1431 1431 ) 1432 1432 .fetch_optional(&self.pool) 1433 1433 .await ··· 1442 1442 1443 1443 async fn get_login_info_by_identifier( 1444 1444 &self, 1445 - identifier: &str, 1445 + identifier: &AtIdentifier, 1446 1446 ) -> Result<Option<UserLoginInfo>, DbError> { 1447 1447 sqlx::query!( 1448 1448 r#" ··· 1454 1454 FROM users 1455 1455 WHERE handle = $1 OR did = $1 1456 1456 "#, 1457 - identifier 1457 + identifier.as_str() 1458 1458 ) 1459 1459 .fetch_optional(&self.pool) 1460 1460 .await ··· 1602 1602 1603 1603 async fn get_login_full_by_identifier( 1604 1604 &self, 1605 - identifier: &str, 1605 + identifier: &AtIdentifier, 1606 1606 ) -> Result<Option<UserLoginFull>, DbError> { 1607 1607 sqlx::query!( 1608 1608 r#"SELECT ··· 1616 1616 FROM users u 1617 1617 JOIN user_keys k ON u.id = k.user_id 1618 1618 WHERE u.handle = $1 OR u.did = $1"#, 1619 - identifier 1619 + identifier.as_str() 1620 1620 ) 1621 1621 .fetch_optional(&self.pool) 1622 1622 .await ··· 3236 3236 "UPDATE users SET discord_id = $2, discord_verified = TRUE, updated_at = NOW() WHERE LOWER(discord_username) = LOWER($1) AND discord_username IS NOT NULL AND handle = $3 RETURNING id", 3237 3237 discord_username, 3238 3238 discord_id, 3239 - h 3239 + h.as_str() 3240 3240 ) 3241 3241 .fetch_optional(&self.pool) 3242 3242 .await ··· 3298 3298 "UPDATE users SET telegram_chat_id = $2, telegram_verified = TRUE, updated_at = NOW() WHERE LOWER(telegram_username) = LOWER($1) AND telegram_username IS NOT NULL AND handle = $3 RETURNING id", 3299 3299 telegram_username, 3300 3300 chat_id, 3301 - h 3301 + h.as_str() 3302 3302 ) 3303 3303 .fetch_optional(&self.pool) 3304 3304 .await
+4 -4
crates/tranquil-lexicon/src/dynamic.rs
··· 341 341 .unwrap_err(); 342 342 343 343 assert!( 344 - !matches!(err, ResolveError::InvalidNsid(_)), 345 - "negative cache hit should not return InvalidNsid - the NSID is valid, it just failed resolution recently. got: {}", 344 + matches!(err, ResolveError::NegativelyCached { .. }), 345 + "negative cache hit must surface as NegativelyCached, got: {}", 346 346 err 347 347 ); 348 348 } ··· 438 438 let result = registry 439 439 .resolve_and_cache_with(&nsid("pet.nel.flaky"), |n| async move { 440 440 Err::<LexiconDoc, _>(ResolveError::DnsLookup { 441 - domain: n, 441 + domain: n.into_inner(), 442 442 reason: "simulated failure".to_string(), 443 443 }) 444 444 }) ··· 597 597 calls.fetch_add(1, Ordering::SeqCst); 598 598 tokio::time::sleep(Duration::from_millis(50)).await; 599 599 Err::<LexiconDoc, _>(ResolveError::DnsLookup { 600 - domain: n, 600 + domain: n.into_inner(), 601 601 reason: "simulated".to_string(), 602 602 }) 603 603 }
+1 -1
crates/tranquil-lexicon/src/registry.rs
··· 70 70 pub fn resolve_ref(&self, reference: &str, context_nsid: &str) -> Option<ResolvedRef> { 71 71 match parse_ref(reference) { 72 72 ParsedRef::Local(local) => { 73 - let doc = self.get_doc(context_nsid)?; 73 + let doc = self.get_doc_by_key(context_nsid)?; 74 74 Self::def_to_resolved(&doc, local) 75 75 } 76 76 ParsedRef::Qualified { nsid, fragment } => {
+1 -4
crates/tranquil-lexicon/src/resolve.rs
··· 53 53 54 54 #[derive(Debug, thiserror::Error)] 55 55 pub enum ResolveError { 56 - #[error("failed to derive authority from NSID: {0}")] 57 - InvalidNsid(String), 58 56 #[error("DNS lookup failed for {domain}: {reason}")] 59 57 DnsLookup { domain: String, reason: String }, 60 58 #[error("no DID found in DNS TXT records for {domain}")] ··· 79 77 let mut segments: Vec<&str> = nsid.split('.').collect(); 80 78 segments.pop(); 81 79 segments.reverse(); 82 - Ok(segments.join(".")) 80 + segments.join(".") 83 81 } 84 82 85 83 pub async fn resolve_did_from_dns(authority: &str) -> Result<Did, ResolveError> { ··· 308 306 nsid_to_authority(&nsid("com.germnetwork.social.post")), 309 307 "social.germnetwork.com" 310 308 ); 311 - assert!(nsid_to_authority("tooShort").is_err()); 312 309 } 313 310 314 311 #[test]
+1 -1
crates/tranquil-oauth-server/src/endpoints/authorize/consent.rs
··· 420 420 let redirect_uri = &request_data.parameters.redirect_uri; 421 421 let intermediate_url = build_intermediate_redirect_url( 422 422 redirect_uri, 423 - &code.0, 423 + code.as_str(), 424 424 request_data.parameters.state.as_deref(), 425 425 request_data.parameters.response_mode.map(|m| m.as_str()), 426 426 );
+15 -15
crates/tranquil-oauth-server/src/endpoints/authorize/login.rs
··· 105 105 let normalized = NormalizedLoginIdentifier::normalize(login_hint, hostname_for_handles); 106 106 tracing::info!(normalized = %normalized, "Normalized login_hint"); 107 107 108 - match state 109 - .repos 110 - .user 111 - .get_login_check_by_identifier(normalized.as_str()) 112 - .await 113 - { 108 + let hint_identifier = tranquil_types::AtIdentifier::new(normalized.as_str()).ok(); 109 + let hint_lookup = match hint_identifier { 110 + Some(ref id) => state.repos.user.get_login_check_by_identifier(id).await, 111 + None => Ok(None), 112 + }; 113 + match hint_lookup { 114 114 Ok(Some(user)) => { 115 115 tracing::info!(did = %user.did, has_password = user.password_hash.is_some(), "Found user for login_hint"); 116 116 let is_delegated = state ··· 399 399 pds_hostname = %tranquil_config::get().server.hostname, 400 400 "Normalized username for lookup" 401 401 ); 402 - let user = match state 403 - .repos 404 - .user 405 - .get_login_info_by_identifier(normalized_username.as_str()) 406 - .await 407 - { 402 + let login_identifier = tranquil_types::AtIdentifier::new(normalized_username.as_str()).ok(); 403 + let login_lookup = match login_identifier { 404 + Some(ref id) => state.repos.user.get_login_info_by_identifier(id).await, 405 + None => Ok(None), 406 + }; 407 + let user = match login_lookup { 408 408 Ok(Some(u)) => u, 409 409 Ok(None) => { 410 410 let _ = bcrypt::verify( ··· 478 478 } 479 479 480 480 let password_valid = match &user.password_hash { 481 - Some(hash) => match bcrypt::verify(&form.password, hash) { 481 + Some(hash) => match bcrypt::verify(&form.password, hash.as_str()) { 482 482 Ok(valid) => valid, 483 483 Err(_) => { 484 484 return show_login_error("An error occurred. Please try again.", json_response); ··· 708 708 if json_response { 709 709 let redirect_url = build_intermediate_redirect_url( 710 710 &request_data.parameters.redirect_uri, 711 - &code.0, 711 + code.as_str(), 712 712 request_data.parameters.state.as_deref(), 713 713 request_data.parameters.response_mode.map(|m| m.as_str()), 714 714 ); ··· 725 725 } else { 726 726 let redirect_url = build_success_redirect( 727 727 &request_data.parameters.redirect_uri, 728 - &code.0, 728 + code.as_str(), 729 729 request_data.parameters.state.as_deref(), 730 730 request_data.parameters.response_mode.map(|m| m.as_str()), 731 731 );
+14 -17
crates/tranquil-oauth-server/src/endpoints/authorize/passkey.rs
··· 19 19 let bare_identifier = 20 20 BareLoginIdentifier::from_identifier(&query.identifier, hostname_for_handles); 21 21 22 - let user = state 23 - .repos 24 - .user 25 - .get_login_check_by_identifier(bare_identifier.as_str()) 26 - .await; 22 + let user = match tranquil_types::AtIdentifier::new(bare_identifier.as_str()) { 23 + Ok(ref id) => state.repos.user.get_login_check_by_identifier(id).await, 24 + Err(_) => Ok(None), 25 + }; 27 26 28 27 let has_passkeys = match user { 29 28 Ok(Some(u)) => tranquil_api::server::has_passkeys_for_user(&state, &u.did).await, ··· 52 51 let normalized_identifier = 53 52 NormalizedLoginIdentifier::normalize(&query.identifier, hostname_for_handles); 54 53 55 - let user = state 56 - .repos 57 - .user 58 - .get_login_check_by_identifier(normalized_identifier.as_str()) 59 - .await; 54 + let user = match tranquil_types::AtIdentifier::new(normalized_identifier.as_str()) { 55 + Ok(ref id) => state.repos.user.get_login_check_by_identifier(id).await, 56 + Err(_) => Ok(None), 57 + }; 60 58 61 59 let (has_passkeys, has_totp, has_password, is_delegated, did): ( 62 60 bool, ··· 238 236 let normalized_username = 239 237 NormalizedLoginIdentifier::normalize(&identifier, hostname_for_handles); 240 238 241 - let user = match state 242 - .repos 243 - .user 244 - .get_login_info_by_identifier(normalized_username.as_str()) 245 - .await 246 - { 239 + let passkey_lookup = match tranquil_types::AtIdentifier::new(normalized_username.as_str()) { 240 + Ok(ref id) => state.repos.user.get_login_info_by_identifier(id).await, 241 + Err(_) => Ok(None), 242 + }; 243 + let user = match passkey_lookup { 247 244 Ok(Some(u)) => u, 248 245 Ok(None) => { 249 246 return ( ··· 670 667 671 668 let redirect_url = build_intermediate_redirect_url( 672 669 &request_data.parameters.redirect_uri, 673 - &code.0, 670 + code.as_str(), 674 671 request_data.parameters.state.as_deref(), 675 672 request_data.parameters.response_mode.map(|m| m.as_str()), 676 673 );
+3 -3
crates/tranquil-oauth-server/src/endpoints/authorize/registration.rs
··· 140 140 }; 141 141 142 142 let mut password_valid = password_hashes.iter().fold(false, |acc, hash| { 143 - acc | bcrypt::verify(&form.app_password, hash).unwrap_or(false) 143 + acc | bcrypt::verify(&form.app_password, hash.as_str()).unwrap_or(false) 144 144 }); 145 145 146 146 if !password_valid 147 147 && let Ok(Some(account_hash)) = state.repos.user.get_password_hash_by_did(&did).await 148 148 { 149 - password_valid = bcrypt::verify(&form.app_password, &account_hash).unwrap_or(false); 149 + password_valid = bcrypt::verify(&form.app_password, account_hash.as_str()).unwrap_or(false); 150 150 } 151 151 152 152 if !password_valid { ··· 290 290 291 291 let redirect_url = build_intermediate_redirect_url( 292 292 &request_data.parameters.redirect_uri, 293 - &code.0, 293 + code.as_str(), 294 294 request_data.parameters.state.as_deref(), 295 295 request_data.parameters.response_mode.map(|m| m.as_str()), 296 296 );
+2 -2
crates/tranquil-oauth-server/src/endpoints/authorize/two_factor.rs
··· 185 185 } 186 186 let redirect_url = build_intermediate_redirect_url( 187 187 &request_data.parameters.redirect_uri, 188 - &code.0, 188 + code.as_str(), 189 189 request_data.parameters.state.as_deref(), 190 190 request_data.parameters.response_mode.map(|m| m.as_str()), 191 191 ); ··· 329 329 } 330 330 let redirect_url = build_intermediate_redirect_url( 331 331 &request_data.parameters.redirect_uri, 332 - &code.0, 332 + code.as_str(), 333 333 request_data.parameters.state.as_deref(), 334 334 request_data.parameters.response_mode.map(|m| m.as_str()), 335 335 );
+1 -1
crates/tranquil-oauth-server/src/endpoints/delegation.rs
··· 294 294 let password_valid = controller 295 295 .password_hash 296 296 .as_ref() 297 - .map(|hash| bcrypt::verify(password, hash).unwrap_or_default()) 297 + .map(|hash| bcrypt::verify(password, hash.as_str()).unwrap_or_default()) 298 298 .unwrap_or_default(); 299 299 300 300 if !password_valid {
+1 -1
crates/tranquil-oauth-server/src/endpoints/par.rs
··· 31 31 #[serde(default)] 32 32 pub login_hint: Option<String>, 33 33 #[serde(default)] 34 - pub dpop_jkt: Option<String>, 34 + pub dpop_jkt: Option<JwkThumbprint>, 35 35 #[serde(default)] 36 36 pub client_secret: Option<String>, 37 37 #[serde(default)]
+9 -12
crates/tranquil-oauth-server/src/endpoints/token/grants.rs
··· 16 16 verify_client_auth, 17 17 }; 18 18 use tranquil_pds::state::AppState; 19 - use tranquil_types::{AuthorizationCode, Did, RefreshToken as RefreshTokenType}; 20 19 21 20 const ACCESS_TOKEN_EXPIRY_SECONDS: u64 = 300; 22 21 const REFRESH_TOKEN_EXPIRY_DAYS_CONFIDENTIAL: i64 = 60; ··· 115 114 )); 116 115 } 117 116 if let Some(expected_jkt) = &authorized.parameters.dpop_jkt 118 - && result.jkt.as_str() != expected_jkt 117 + && result.jkt != *expected_jkt 119 118 { 120 119 return Err(OAuthError::InvalidDpopProof( 121 120 "DPoP key binding mismatch".to_string(), 122 121 )); 123 122 } 124 - Some(result.jkt.as_str().to_string()) 123 + Some(result.jkt.clone()) 125 124 } else if authorized.parameters.dpop_jkt.is_some() || client_metadata.requires_dpop() { 126 125 return Err(OAuthError::UseDpopNonce( 127 126 DPoPVerifier::new(AuthConfig::get().dpop_secret().as_bytes()).generate_nonce(), ··· 169 168 }; 170 169 171 170 let access_token = create_access_token_with_delegation( 172 - &token_id.0, 171 + &token_id, 173 172 &did, 174 - dpop_jkt.as_deref(), 173 + dpop_jkt.as_ref(), 175 174 final_scope.as_deref(), 176 175 controller_did.as_ref(), 177 176 )?; ··· 207 206 .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 208 207 tracing::info!( 209 208 did = %did, 210 - token_id = %token_id.0, 209 + token_id = %token_id, 211 210 client_id = %authorized.client_id, 212 211 "Authorization code grant completed, token created" 213 212 ); ··· 215 214 let oauth_repo = state.repos.oauth.clone(); 216 215 let did_clone = did.clone(); 217 216 async move { 218 - if let Ok(did_typed) = did_clone.parse::<tranquil_types::Did>() 219 - && let Err(e) = enforce_token_limit_for_user(oauth_repo.as_ref(), &did_typed).await 220 - { 217 + if let Err(e) = enforce_token_limit_for_user(oauth_repo.as_ref(), &did_clone).await { 221 218 tracing::warn!("Failed to enforce token limit for user: {:?}", e); 222 219 } 223 220 } ··· 284 281 rotated_at = %rotated_at, 285 282 "Refresh token reuse within grace period, returning existing tokens" 286 283 ); 287 - let dpop_jkt = token_data.parameters.dpop_jkt.as_deref(); 284 + let dpop_jkt = token_data.parameters.dpop_jkt.as_ref(); 288 285 let access_token = create_access_token_with_delegation( 289 286 &token_data.token_id, 290 287 &token_data.did, ··· 367 364 )); 368 365 } 369 366 if let Some(expected_jkt) = &token_data.parameters.dpop_jkt 370 - && result.jkt.as_str() != expected_jkt 367 + && result.jkt != *expected_jkt 371 368 { 372 369 return Err(OAuthError::InvalidDpopProof( 373 370 "DPoP key binding mismatch".to_string(), 374 371 )); 375 372 } 376 - Some(result.jkt.as_str().to_string()) 373 + Some(result.jkt.clone()) 377 374 } else if token_data.parameters.dpop_jkt.is_some() { 378 375 return Err(OAuthError::InvalidRequest( 379 376 "DPoP proof required".to_string(),
+4 -4
crates/tranquil-oauth-server/src/endpoints/token/helpers.rs
··· 46 46 let actual_scope = scope.unwrap_or("atproto"); 47 47 let mut payload = json!({ 48 48 "iss": issuer, 49 - "sub": sub, 49 + "sub": sub.as_str(), 50 50 "aud": issuer, 51 51 "iat": now, 52 52 "exp": exp, 53 53 "jti": jti, 54 - "sid": session_id, 54 + "sid": session_id.as_str(), 55 55 "scope": actual_scope 56 56 }); 57 57 if let Some(jkt) = dpop_jkt { 58 - payload["cnf"] = json!({ "jkt": jkt }); 58 + payload["cnf"] = json!({ "jkt": jkt.as_str() }); 59 59 } 60 60 if let Some(controller) = controller_did { 61 - payload["act"] = json!({ "sub": controller }); 61 + payload["act"] = json!({ "sub": controller.as_str() }); 62 62 } 63 63 let header = json!({ 64 64 "alg": "HS256",
+1 -1
crates/tranquil-oauth-server/src/endpoints/token/mod.rs
··· 10 10 use tranquil_pds::state::AppState; 11 11 12 12 pub use grants::{handle_authorization_code_grant, handle_refresh_token_grant}; 13 - pub use helpers::{TokenClaims, create_access_token, extract_token_claims, verify_pkce}; 13 + pub use helpers::{TokenClaims, extract_token_claims, verify_pkce}; 14 14 pub use introspect::{ 15 15 IntrospectRequest, IntrospectResponse, RevokeRequest, introspect_token, revoke_token, 16 16 };
+5 -4
crates/tranquil-oauth-server/src/sso_endpoints.rs
··· 1042 1042 )); 1043 1043 } 1044 1044 tracing::info!(did = %d, "Creating external did:web SSO account"); 1045 - d.to_string() 1045 + d.parse() 1046 + .map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))? 1046 1047 } 1047 1048 _ => { 1048 1049 let genesis_result = match tranquil_pds::plc::create_genesis_operation( ··· 1345 1346 redirect_url: "/app/dashboard".to_string(), 1346 1347 access_jwt: Some(access_meta.token), 1347 1348 refresh_jwt: Some(refresh_meta.token), 1348 - app_password: Some(app_password), 1349 + app_password: Some(app_password.into_inner()), 1349 1350 app_password_name: Some(app_password_name), 1350 1351 })); 1351 1352 } ··· 1359 1360 ), 1360 1361 access_jwt: None, 1361 1362 refresh_jwt: None, 1362 - app_password: Some(app_password), 1363 + app_password: Some(app_password.into_inner()), 1363 1364 app_password_name: Some(app_password_name), 1364 1365 })); 1365 1366 } ··· 1403 1404 redirect_url, 1404 1405 access_jwt: None, 1405 1406 refresh_jwt: None, 1406 - app_password: Some(app_password), 1407 + app_password: Some(app_password.into_inner()), 1407 1408 app_password_name: Some(app_password_name), 1408 1409 })) 1409 1410 }
+13 -42
crates/tranquil-oauth/src/types.rs
··· 8 8 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] 9 9 #[serde(transparent)] 10 10 #[sqlx(transparent)] 11 - pub struct RequestId(pub String); 12 - 13 - #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] 14 - #[serde(transparent)] 15 - #[sqlx(transparent)] 16 - pub struct TokenId(pub String); 11 + pub struct SessionId(String); 17 12 18 - #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] 19 - #[serde(transparent)] 20 - #[sqlx(transparent)] 21 - pub struct DeviceId(pub String); 22 - 23 - pub fn as_str(&self) -> &str { 24 - &self.0 13 + impl SessionId { 14 + pub fn new(s: impl Into<String>) -> Self { 15 + Self(s.into()) 25 16 } 26 - } 27 17 28 - impl TokenId { 29 18 pub fn generate() -> Self { 30 19 Self(uuid::Uuid::new_v4().to_string()) 31 20 } 32 - } 33 21 34 - impl DeviceId { 35 - pub fn generate() -> Self { 36 - Self(uuid::Uuid::new_v4().to_string()) 22 + pub fn as_str(&self) -> &str { 23 + &self.0 37 24 } 38 25 } 39 26 40 - impl SessionId { 41 - pub fn generate() -> Self { 42 - Self(uuid::Uuid::new_v4().to_string()) 43 - } 44 - } 45 - 46 - impl Code { 47 - pub fn generate() -> Self { 48 - use rand::Rng; 49 - let bytes: [u8; 32] = rand::thread_rng().r#gen(); 50 - Self(base64::Engine::encode( 51 - &base64::engine::general_purpose::URL_SAFE_NO_PAD, 52 - bytes, 53 - )) 27 + impl From<String> for SessionId { 28 + fn from(s: String) -> Self { 29 + Self(s) 54 30 } 55 31 } 56 32 57 - impl RefreshToken { 58 - pub fn generate() -> Self { 59 - use rand::Rng; 60 - let bytes: [u8; 32] = rand::thread_rng().r#gen(); 61 - Self(base64::Engine::encode( 62 - &base64::engine::general_purpose::URL_SAFE_NO_PAD, 63 - bytes, 64 - )) 33 + impl std::fmt::Display for SessionId { 34 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 + write!(f, "{}", self.0) 65 36 } 66 37 } 67 38 ··· 146 117 pub code_challenge_method: CodeChallengeMethod, 147 118 pub response_mode: Option<ResponseMode>, 148 119 pub login_hint: Option<String>, 149 - pub dpop_jkt: Option<String>, 120 + pub dpop_jkt: Option<tranquil_types::JwkThumbprint>, 150 121 pub prompt: Option<Prompt>, 151 122 #[serde(flatten)] 152 123 pub extra: Option<JsonValue>,
+2 -2
crates/tranquil-pds/src/api/proxy.rs
··· 291 291 &auth_user.auth_source, 292 292 auth_user.scope.as_deref(), 293 293 &resolved.did, 294 - method, 294 + &method_nsid, 295 295 ) { 296 296 return e.into_response(); 297 297 } ··· 353 353 match crate::auth::create_service_token( 354 354 &auth_user.did, 355 355 &token_aud, 356 - Some(token_lxm), 356 + Some(&token_lxm), 357 357 &key_bytes, 358 358 ) { 359 359 Ok(new_token) => {
+1 -1
crates/tranquil-pds/src/api/validation.rs
··· 214 214 "{}.{}", 215 215 validated, 216 216 matched_domain.unwrap_or(&available_domains[0]) 217 - )) 217 + ))) 218 218 } else { 219 219 validate_full_domain_handle(input) 220 220 }
+1 -1
crates/tranquil-pds/src/auth/mfa_verified.rs
··· 201 201 202 202 match hash { 203 203 Some(h) => { 204 - if bcrypt::verify(password, &h).unwrap_or(false) { 204 + if bcrypt::verify(password, h.as_str()).unwrap_or(false) { 205 205 Ok(MfaVerified::from_password(user)) 206 206 } else { 207 207 Err(crate::api::error::ApiError::InvalidPassword(
+10 -7
crates/tranquil-pds/src/auth/mod.rs
··· 82 82 crate::config::decrypt_key(encrypted, Some(version)) 83 83 } 84 84 85 - pub fn generate_app_password() -> String { 85 + pub fn generate_app_password() -> crate::types::PlainPassword { 86 86 use rand::Rng; 87 87 let chars: &[u8] = b"abcdefghijklmnopqrstuvwxyz234567"; 88 88 let mut rng = rand::thread_rng(); ··· 93 93 .collect() 94 94 }) 95 95 .collect(); 96 - segments.join("-") 96 + crate::types::PlainPassword::new(segments.join("-")) 97 97 } 98 98 99 99 const KEY_CACHE_TTL_SECS: u64 = 300; ··· 631 631 fn test_lxm_permits_exact_match() { 632 632 assert!(lxm_permits( 633 633 "com.atproto.repo.uploadBlob", 634 - "com.atproto.repo.uploadBlob" 634 + &n("com.atproto.repo.uploadBlob") 635 635 )); 636 636 } 637 637 638 638 #[test] 639 639 fn test_lxm_permits_wildcard() { 640 - assert!(lxm_permits("*", "com.atproto.repo.uploadBlob")); 641 - assert!(lxm_permits("*", "anything.at.all")); 640 + assert!(lxm_permits("*", &n("com.atproto.repo.uploadBlob"))); 641 + assert!(lxm_permits("*", &n("anything.at.all"))); 642 642 } 643 643 644 644 #[test] 645 645 fn test_lxm_permits_mismatch() { 646 646 assert!(!lxm_permits( 647 647 "com.atproto.repo.uploadBlob", 648 - "com.atproto.repo.createRecord" 648 + &n("com.atproto.repo.createRecord") 649 649 )); 650 650 } 651 651 652 652 #[test] 653 653 fn test_lxm_permits_partial_not_wildcard() { 654 - assert!(!lxm_permits("com.atproto.*", "com.atproto.repo.uploadBlob")); 654 + assert!(!lxm_permits( 655 + "com.atproto.*", 656 + &n("com.atproto.repo.uploadBlob") 657 + )); 655 658 } 656 659 }
+7 -7
crates/tranquil-pds/src/handle/mod.rs
··· 75 75 expected_did: &Did, 76 76 ) -> Result<(), HandleResolutionError> { 77 77 let resolved_did = resolve_handle(handle).await?; 78 - if resolved_did == expected_did { 78 + if resolved_did == *expected_did { 79 79 Ok(()) 80 80 } else { 81 81 Err(HandleResolutionError::DidMismatch { 82 - expected: expected_did.to_string(), 82 + expected: expected_did.clone(), 83 83 actual: resolved_did, 84 84 }) 85 85 } ··· 103 103 104 104 #[test] 105 105 fn test_is_service_domain_handle() { 106 - assert!(is_service_domain_handle("user.example.com", "example.com")); 107 - assert!(is_service_domain_handle("example.com", "example.com")); 108 - assert!(is_service_domain_handle("myhandle", "example.com")); 109 - assert!(!is_service_domain_handle("user.other.com", "example.com")); 110 - assert!(!is_service_domain_handle("myhandle.xyz", "example.com")); 106 + assert!(is_service_domain_handle("nel.oyster.cafe", "oyster.cafe")); 107 + assert!(is_service_domain_handle("oyster.cafe", "oyster.cafe")); 108 + assert!(is_service_domain_handle("myhandle", "oyster.cafe")); 109 + assert!(!is_service_domain_handle("lyna.nel.pet", "oyster.cafe")); 110 + assert!(!is_service_domain_handle("myhandle.xyz", "oyster.cafe")); 111 111 } 112 112 }
+1 -3
crates/tranquil-pds/src/oauth/verify.rs
··· 24 24 pub did: Did, 25 25 pub token_id: TokenId, 26 26 pub scope: Option<String>, 27 - pub dpop_jkt: Option<String>, 28 27 pub controller_did: Option<Did>, 29 28 } 30 29 ··· 87 86 "DPoP proof has already been used".to_string(), 88 87 )); 89 88 } 90 - if result.jkt.as_str() != expected_jkt { 89 + if result.jkt != *expected_jkt { 91 90 return Err(OAuthError::InvalidDpopProof( 92 91 "DPoP key binding mismatch".to_string(), 93 92 )); ··· 179 178 did, 180 179 token_id, 181 180 scope, 182 - dpop_jkt, 183 181 controller_did, 184 182 }) 185 183 }
-2
crates/tranquil-pds/src/repo_ops.rs
··· 25 25 #[derive(Debug)] 26 26 pub enum CommitError { 27 27 InvalidDid(String), 28 - InvalidTid(String), 29 28 SigningFailed(String), 30 29 SerializationFailed(String), 31 30 KeyNotFound, ··· 47 46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 48 47 match self { 49 48 Self::InvalidDid(e) => write!(f, "Invalid DID: {}", e), 50 - Self::InvalidTid(e) => write!(f, "Invalid TID: {}", e), 51 49 Self::SigningFailed(e) => write!(f, "Failed to sign commit: {}", e), 52 50 Self::SerializationFailed(e) => write!(f, "Failed to serialize signed commit: {}", e), 53 51 Self::KeyNotFound => write!(f, "Signing key not found"),
+7 -6
crates/tranquil-pds/src/sync/frame.rs
··· 2 2 use cid::Cid; 3 3 use serde::{Deserialize, Serialize}; 4 4 use std::str::FromStr; 5 + use tranquil_db_traits::SequenceNumber; 5 6 use tranquil_scopes::RepoAction; 6 7 use tranquil_types::{CidLink, Did, Handle, Tid}; 7 8 ··· 27 28 28 29 #[derive(Debug, Serialize, Deserialize)] 29 30 pub struct CommitFrame { 30 - pub seq: i64, 31 + pub seq: SequenceNumber, 31 32 pub rebase: bool, 32 33 #[serde(rename = "tooBig")] 33 34 pub too_big: bool, ··· 76 77 pub active: bool, 77 78 #[serde(skip_serializing_if = "Option::is_none")] 78 79 pub status: Option<tranquil_db_traits::AccountStatus>, 79 - pub seq: i64, 80 + pub seq: SequenceNumber, 80 81 pub time: String, 81 82 } 82 83 ··· 86 87 pub rev: Tid, 87 88 #[serde(with = "serde_bytes")] 88 89 pub blocks: Vec<u8>, 89 - pub seq: i64, 90 + pub seq: SequenceNumber, 90 91 pub time: String, 91 92 } 92 93 ··· 139 140 impl std::error::Error for CommitFrameError {} 140 141 141 142 pub struct CommitFrameBuilder { 142 - seq: i64, 143 + seq: SequenceNumber, 143 144 did: Did, 144 145 commit_cid: Cid, 145 146 ops_json: serde_json::Value, ··· 150 151 151 152 impl CommitFrameBuilder { 152 153 pub fn new( 153 - seq: i64, 154 + seq: SequenceNumber, 154 155 did: Did, 155 156 commit_cid: &CidLink, 156 157 ops_json: serde_json::Value, ··· 222 223 CommitFrameError::InvalidCommitCid("Missing commit_cid in event".to_string()) 223 224 })?; 224 225 let builder = CommitFrameBuilder::new( 225 - event.seq.as_i64(), 226 + event.seq, 226 227 event.did.clone(), 227 228 &commit_cid, 228 229 event.ops.unwrap_or_default(),
+3 -3
crates/tranquil-pds/src/sync/import.rs
··· 143 143 .and_then(|v| v.as_str()) 144 144 .map(String::from); 145 145 return vec![BlobRef { 146 - cid: link.clone(), 146 + cid, 147 147 mime_type: mime, 148 148 }]; 149 149 } ··· 173 173 #[derive(Debug)] 174 174 pub struct ImportedRecord { 175 175 pub collection: Nsid, 176 - pub rkey: String, 176 + pub rkey: Rkey, 177 177 pub cid: Cid, 178 178 pub blob_refs: Vec<BlobRef>, 179 179 } ··· 229 229 let parts: Vec<&str> = full_key.split('/').collect(); 230 230 if parts.len() >= 2 { 231 231 let collection = Nsid::from(parts[..parts.len() - 1].join("/")); 232 - let rkey = parts[parts.len() - 1].to_string(); 232 + let rkey = Rkey::from(parts[parts.len() - 1].to_string()); 233 233 records.push(ImportedRecord { 234 234 collection, 235 235 rkey,
+2 -2
crates/tranquil-pds/src/sync/util.rs
··· 258 258 did: event.did.clone(), 259 259 active: event.active.unwrap_or(true), 260 260 status: event.status.filter(|s| !s.is_active()), 261 - seq: event.seq.as_i64(), 261 + seq: event.seq, 262 262 time: format_atproto_time(event.created_at), 263 263 }; 264 264 let bytes = serialize_event_frame(FrameType::Account, &frame, 256)?; ··· 355 355 did: event.did.clone(), 356 356 rev, 357 357 blocks: car_bytes, 358 - seq: event.seq.as_i64(), 358 + seq: event.seq, 359 359 time: format_atproto_time(event.created_at), 360 360 }, 361 361 512,
+2 -2
crates/tranquil-pds/src/validation/mod.rs
··· 75 75 &self, 76 76 record: &Value, 77 77 collection: &Nsid, 78 - rkey: Option<&str>, 78 + rkey: Option<&Rkey>, 79 79 ) -> Result<ValidationStatus, ValidationError> { 80 80 let (record_type, obj) = validate_preamble(record, collection)?; 81 81 let registry = tranquil_lexicon::LexiconRegistry::global(); ··· 135 135 fn check_banned_content( 136 136 record_type: &str, 137 137 obj: &serde_json::Map<String, Value>, 138 - rkey: Option<&str>, 138 + rkey: Option<&Rkey>, 139 139 ) -> Result<(), ValidationError> { 140 140 match record_type { 141 141 "app.bsky.feed.post" => {
+4 -2
crates/tranquil-pds/tests/oauth_token_eviction.rs
··· 121 121 .expect("list failed"); 122 122 assert_eq!(remaining.len(), 3, "three newest tokens should remain"); 123 123 124 - let remaining_ids: std::collections::HashSet<String> = 125 - remaining.iter().map(|t| t.token_id.0.clone()).collect(); 124 + let remaining_ids: std::collections::HashSet<String> = remaining 125 + .iter() 126 + .map(|t| t.token_id.as_str().to_string()) 127 + .collect(); 126 128 let expected_ids: std::collections::HashSet<String> = token_ids[2..].iter().cloned().collect(); 127 129 assert_eq!( 128 130 remaining_ids, expected_ids,
+12 -10
crates/tranquil-pds/tests/record_validation.rs
··· 17 17 fn test_type_mismatch() { 18 18 let validator = RecordValidator::new(); 19 19 let record = json!({ 20 - "$type": "com.example.other", 20 + "$type": "cafe.oyster.other", 21 21 "createdAt": now() 22 22 }); 23 23 assert!(matches!( 24 - validator.validate(&record, "com.example.expected"), 24 + validator.validate(&record, &c("cafe.oyster.expected")), 25 25 Err(ValidationError::TypeMismatch { expected, actual }) 26 - if expected == "com.example.expected" && actual == "com.example.other" 26 + if expected == "cafe.oyster.expected" && actual == "cafe.oyster.other" 27 27 )); 28 28 } 29 29 ··· 32 32 let validator = RecordValidator::new(); 33 33 let record = json!({"text": "Hello"}); 34 34 assert!(matches!( 35 - validator.validate(&record, "com.example.test"), 35 + validator.validate(&record, &c("cafe.oyster.record")), 36 36 Err(ValidationError::MissingType) 37 37 )); 38 38 } ··· 42 42 let validator = RecordValidator::new(); 43 43 let record = json!("just a string"); 44 44 assert!(matches!( 45 - validator.validate(&record, "com.example.test"), 45 + validator.validate(&record, &c("cafe.oyster.record")), 46 46 Err(ValidationError::InvalidRecord(_)) 47 47 )); 48 48 } ··· 52 52 let validator = RecordValidator::new(); 53 53 let record = json!({"$type": "com.custom.record", "data": "test"}); 54 54 assert_eq!( 55 - validator.validate(&record, "com.custom.record").unwrap(), 55 + validator 56 + .validate(&record, &c("com.custom.record")) 57 + .unwrap(), 56 58 ValidationStatus::Unknown 57 59 ); 58 60 } ··· 62 64 let validator = RecordValidator::new().require_lexicon(true); 63 65 let record = json!({"$type": "com.custom.record", "data": "test"}); 64 66 assert!(matches!( 65 - validator.validate(&record, "com.custom.record"), 67 + validator.validate(&record, &c("com.custom.record")), 66 68 Err(ValidationError::UnknownType(_)) 67 69 )); 68 70 } ··· 73 75 74 76 let valid = json!({"$type": "com.custom.record", "createdAt": "2024-01-15T10:30:00.000Z"}); 75 77 assert_eq!( 76 - validator.validate(&valid, "com.custom.record").unwrap(), 78 + validator.validate(&valid, &c("com.custom.record")).unwrap(), 77 79 ValidationStatus::Unknown 78 80 ); 79 81 ··· 81 83 json!({"$type": "com.custom.record", "createdAt": "2024-01-15T10:30:00+05:30"}); 82 84 assert_eq!( 83 85 validator 84 - .validate(&with_offset, "com.custom.record") 86 + .validate(&with_offset, &c("com.custom.record")) 85 87 .unwrap(), 86 88 ValidationStatus::Unknown 87 89 ); 88 90 89 91 let invalid = json!({"$type": "com.custom.record", "createdAt": "2024/01/15"}); 90 92 assert!(matches!( 91 - validator.validate(&invalid, "com.custom.record"), 93 + validator.validate(&invalid, &c("com.custom.record")), 92 94 Err(ValidationError::InvalidDatetime { .. }) 93 95 )); 94 96 }
+4 -4
crates/tranquil-pds/tests/scope_edge_cases.rs
··· 166 166 #[test] 167 167 fn test_permissions_repo_collection_wildcard_prefix() { 168 168 let perms = ScopePermissions::from_scope_string(Some("repo:app.bsky.*?action=create")); 169 - assert!(perms.allows_repo(RepoAction::Create, "app.bsky.feed.post")); 170 - assert!(perms.allows_repo(RepoAction::Create, "app.bsky.actor.profile")); 171 - assert!(!perms.allows_repo(RepoAction::Create, "com.atproto.repo.blob")); 172 - assert!(!perms.allows_repo(RepoAction::Update, "app.bsky.feed.post")); 169 + assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post"))); 170 + assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.actor.profile"))); 171 + assert!(!perms.allows_repo(RepoAction::Create, &c("com.atproto.repo.blob"))); 172 + assert!(!perms.allows_repo(RepoAction::Update, &c("app.bsky.feed.post"))); 173 173 } 174 174 175 175 #[test]
+2 -2
crates/tranquil-pds/tests/store_parity.rs
··· 1420 1420 let new_root = test_cid(99); 1421 1421 let rev1 = Tid::from("rev1".to_string()); 1422 1422 f.pg.repo 1423 - .update_repo_root(pg_uid, &new_root, "rev1") 1423 + .update_repo_root(pg_uid, &new_root, &rev1) 1424 1424 .await 1425 1425 .unwrap(); 1426 1426 f.store 1427 1427 .repo 1428 - .update_repo_root(store_uid, &new_root, "rev1") 1428 + .update_repo_root(store_uid, &new_root, &rev1) 1429 1429 .await 1430 1430 .unwrap(); 1431 1431
-5
crates/tranquil-scopes/src/permission_set.rs
··· 406 406 } 407 407 408 408 #[test] 409 - fn test_extract_namespace_authority_single_segment() { 410 - assert_eq!(extract_namespace_authority("single"), "single"); 411 - } 412 - 413 - #[test] 414 409 fn test_is_under_authority() { 415 410 assert!(is_under_authority("io.atcr.manifest", "io.atcr")); 416 411 assert!(is_under_authority("io.atcr.sailor.star", "io.atcr"));
+10 -10
crates/tranquil-scopes/src/permissions.rs
··· 350 350 fn test_atproto_scope_is_identity_only() { 351 351 let perms = ScopePermissions::from_scope_string(Some("atproto")); 352 352 assert!(!perms.has_full_access()); 353 - assert!(!perms.allows_repo(RepoAction::Create, "app.bsky.feed.post")); 353 + assert!(!perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post"))); 354 354 assert!(!perms.allows_blob("image/png")); 355 355 assert!(!perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getTimeline"))); 356 356 assert!(!perms.allows_account(AccountAttr::Email, AccountAction::Manage)); ··· 359 359 #[test] 360 360 fn test_transition_generic_allows_everything() { 361 361 let perms = ScopePermissions::from_scope_string(Some("transition:generic")); 362 - assert!(perms.allows_repo(RepoAction::Create, "app.bsky.feed.post")); 362 + assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post"))); 363 363 assert!(perms.allows_blob("image/png")); 364 364 } 365 365 ··· 410 410 assert!(perms.allows_email_read()); 411 411 assert!(perms.allows_account(AccountAttr::Email, AccountAction::Read)); 412 412 assert!(!perms.allows_account(AccountAttr::Email, AccountAction::Manage)); 413 - assert!(!perms.allows_repo(RepoAction::Create, "app.bsky.feed.post")); 413 + assert!(!perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post"))); 414 414 } 415 415 416 416 #[test] ··· 427 427 let perms = ScopePermissions::from_scope_string(Some( 428 428 "repo:app.bsky.feed.post?action=create&action=delete", 429 429 )); 430 - assert!(perms.allows_repo(RepoAction::Create, "app.bsky.feed.post")); 431 - assert!(perms.allows_repo(RepoAction::Delete, "app.bsky.feed.post")); 432 - assert!(!perms.allows_repo(RepoAction::Update, "app.bsky.feed.post")); 433 - assert!(!perms.allows_repo(RepoAction::Create, "app.bsky.feed.like")); 430 + assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post"))); 431 + assert!(perms.allows_repo(RepoAction::Delete, &c("app.bsky.feed.post"))); 432 + assert!(!perms.allows_repo(RepoAction::Update, &c("app.bsky.feed.post"))); 433 + assert!(!perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.like"))); 434 434 } 435 435 436 436 #[test] ··· 561 561 assert!(perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getAuthorFeed"))); 562 562 assert!(perms.allows_rpc( 563 563 "did:web:api.bsky.app#bsky_appview", 564 - "app.bsky.feed.getAuthorFeed" 564 + &c("app.bsky.feed.getAuthorFeed") 565 565 )); 566 566 assert!(perms.allows_rpc( 567 567 "did:web:api.bsky.app#other_service", 568 - "app.bsky.feed.getAuthorFeed" 568 + &c("app.bsky.feed.getAuthorFeed") 569 569 )); 570 570 assert!(!perms.allows_rpc("did:web:other.app", &c("app.bsky.feed.getAuthorFeed"))); 571 571 assert!(!perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getTimeline"))); ··· 579 579 assert!(perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getAuthorFeed"))); 580 580 assert!(perms.allows_rpc( 581 581 "did:web:api.bsky.app#bsky_appview", 582 - "app.bsky.feed.getAuthorFeed" 582 + &c("app.bsky.feed.getAuthorFeed") 583 583 )); 584 584 } 585 585 }
+4 -1
crates/tranquil-store/src/eventlog/payload.rs
··· 106 106 prev_cid: event.prev_cid.as_ref().and_then(cid_link_to_bytes), 107 107 prev_data_cid: event.prev_data_cid.as_ref().and_then(cid_link_to_bytes), 108 108 ops: ops_bytes, 109 - blobs: event.blobs.clone(), 109 + blobs: event 110 + .blobs 111 + .as_ref() 112 + .map(|v| v.iter().map(|c| c.as_str().to_owned()).collect()), 110 113 blocks: match event.blocks.as_ref() { 111 114 Some(EventBlocks::Inline(v)) => Some(v.clone()), 112 115 Some(EventBlocks::LegacyCids(_)) | None => None,
+21 -21
crates/tranquil-store/src/metastore/client.rs
··· 916 916 self.pool 917 917 .send(MetastoreRequest::Blob(BlobRequest::ListBlobsSinceRev { 918 918 did: did.clone(), 919 - since: since.to_owned(), 919 + since: since.to_string(), 920 920 tx, 921 921 }))?; 922 922 recv(rx).await ··· 1515 1515 self.pool.send(MetastoreRequest::Session( 1516 1516 SessionRequest::DeleteSessionsByDidExceptJti { 1517 1517 did: did.clone(), 1518 - except_jti: except_jti.to_owned(), 1518 + except_jti: except_jti.to_string(), 1519 1519 tx, 1520 1520 }, 1521 1521 ))?; ··· 1841 1841 let (tx, rx) = oneshot::channel(); 1842 1842 self.pool 1843 1843 .send(MetastoreRequest::Infra(InfraRequest::CreateInviteCode { 1844 - code: code.to_owned(), 1844 + code: code.clone(), 1845 1845 use_count, 1846 1846 for_account: for_account.cloned(), 1847 1847 tx, ··· 1876 1876 let (tx, rx) = oneshot::channel(); 1877 1877 self.pool.send(MetastoreRequest::Infra( 1878 1878 InfraRequest::GetInviteCodeAvailableUses { 1879 - code: code.to_owned(), 1879 + code: code.clone(), 1880 1880 tx, 1881 1881 }, 1882 1882 ))?; ··· 1890 1890 let (tx, rx) = oneshot::channel(); 1891 1891 self.pool 1892 1892 .send(MetastoreRequest::Infra(InfraRequest::ValidateInviteCode { 1893 - code: code.to_owned(), 1893 + code: code.clone(), 1894 1894 tx, 1895 1895 })) 1896 1896 .map_err(|e| InviteCodeError::DatabaseError(DbError::Connection(e.to_string())))?; ··· 1916 1916 let (tx, rx) = oneshot::channel(); 1917 1917 self.pool 1918 1918 .send(MetastoreRequest::Infra(InfraRequest::GetInviteCodeUses { 1919 - code: code.to_owned(), 1919 + code: code.to_string(), 1920 1920 tx, 1921 1921 }))?; 1922 1922 recv(rx).await ··· 1926 1926 let (tx, rx) = oneshot::channel(); 1927 1927 self.pool.send(MetastoreRequest::Infra( 1928 1928 InfraRequest::DisableInviteCodesByCode { 1929 - codes: codes.to_vec(), 1929 + codes: codes.iter().map(|c| c.to_string()).collect(), 1930 1930 tx, 1931 1931 }, 1932 1932 ))?; ··· 1978 1978 let (tx, rx) = oneshot::channel(); 1979 1979 self.pool.send(MetastoreRequest::Infra( 1980 1980 InfraRequest::GetInviteCodeUsesBatch { 1981 - codes: codes.to_vec(), 1981 + codes: codes.iter().map(|c| c.to_string()).collect(), 1982 1982 tx, 1983 1983 }, 1984 1984 ))?; ··· 2003 2003 let (tx, rx) = oneshot::channel(); 2004 2004 self.pool 2005 2005 .send(MetastoreRequest::Infra(InfraRequest::GetInviteCodeInfo { 2006 - code: code.to_owned(), 2006 + code: code.to_string(), 2007 2007 tx, 2008 2008 }))?; 2009 2009 recv(rx).await ··· 2063 2063 self.pool 2064 2064 .send(MetastoreRequest::Infra(InfraRequest::ReserveSigningKey { 2065 2065 did: did.cloned(), 2066 - public_key_did_key: public_key_did_key.to_owned(), 2066 + public_key_did_key: public_key_did_key.to_string(), 2067 2067 private_key_bytes: private_key_bytes.to_vec(), 2068 2068 expires_at, 2069 2069 tx, ··· 2078 2078 let (tx, rx) = oneshot::channel(); 2079 2079 self.pool.send(MetastoreRequest::Infra( 2080 2080 InfraRequest::GetReservedSigningKey { 2081 - public_key_did_key: public_key_did_key.to_owned(), 2081 + public_key_did_key: public_key_did_key.to_string(), 2082 2082 tx, 2083 2083 }, 2084 2084 ))?; ··· 2489 2489 let (tx, rx) = oneshot::channel(); 2490 2490 self.pool.send(MetastoreRequest::Infra( 2491 2491 InfraRequest::GetReservedSigningKeyFull { 2492 - public_key_did_key: public_key_did_key.to_owned(), 2492 + public_key_did_key: public_key_did_key.to_string(), 2493 2493 tx, 2494 2494 }, 2495 2495 ))?; ··· 3370 3370 let (tx, rx) = oneshot::channel(); 3371 3371 self.pool 3372 3372 .send(MetastoreRequest::User(UserRequest::GetOAuthTokenWithUser { 3373 - token_id: token_id.to_owned(), 3373 + token_id: token_id.to_string(), 3374 3374 tx, 3375 3375 }))?; 3376 3376 recv(rx).await ··· 3447 3447 3448 3448 async fn get_login_check_by_identifier( 3449 3449 &self, 3450 - identifier: &str, 3450 + identifier: &AtIdentifier, 3451 3451 ) -> Result<Option<UserLoginCheck>, DbError> { 3452 3452 let (tx, rx) = oneshot::channel(); 3453 3453 self.pool.send(MetastoreRequest::User( 3454 3454 UserRequest::GetLoginCheckByIdentifier { 3455 - identifier: identifier.to_owned(), 3455 + identifier: identifier.to_string(), 3456 3456 tx, 3457 3457 }, 3458 3458 ))?; ··· 3461 3461 3462 3462 async fn get_login_info_by_identifier( 3463 3463 &self, 3464 - identifier: &str, 3464 + identifier: &AtIdentifier, 3465 3465 ) -> Result<Option<UserLoginInfo>, DbError> { 3466 3466 let (tx, rx) = oneshot::channel(); 3467 3467 self.pool.send(MetastoreRequest::User( 3468 3468 UserRequest::GetLoginInfoByIdentifier { 3469 - identifier: identifier.to_owned(), 3469 + identifier: identifier.to_string(), 3470 3470 tx, 3471 3471 }, 3472 3472 ))?; ··· 3698 3698 3699 3699 async fn check_email_verified_by_identifier( 3700 3700 &self, 3701 - identifier: &str, 3701 + identifier: &AtIdentifier, 3702 3702 ) -> Result<Option<bool>, DbError> { 3703 3703 let (tx, rx) = oneshot::channel(); 3704 3704 self.pool.send(MetastoreRequest::User( 3705 3705 UserRequest::CheckEmailVerifiedByIdentifier { 3706 - identifier: identifier.to_owned(), 3706 + identifier: identifier.to_string(), 3707 3707 tx, 3708 3708 }, 3709 3709 ))?; ··· 4442 4442 4443 4443 async fn get_login_full_by_identifier( 4444 4444 &self, 4445 - identifier: &str, 4445 + identifier: &AtIdentifier, 4446 4446 ) -> Result<Option<UserLoginFull>, DbError> { 4447 4447 let (tx, rx) = oneshot::channel(); 4448 4448 self.pool.send(MetastoreRequest::User( 4449 4449 UserRequest::GetLoginFullByIdentifier { 4450 - identifier: identifier.to_owned(), 4450 + identifier: identifier.to_string(), 4451 4451 tx, 4452 4452 }, 4453 4453 ))?;
+1 -1
crates/tranquil-store/src/metastore/commit_ops.rs
··· 203 203 204 204 let mutation_set = CommitMutationSet { 205 205 new_root_cid: new_cid_bytes.clone(), 206 - new_rev: input.new_rev.clone(), 206 + new_rev: input.new_rev.to_string(), 207 207 record_upserts: input 208 208 .record_upserts 209 209 .iter()
+2 -7
crates/tranquil-store/src/metastore/event_ops.rs
··· 304 304 305 305 let user_seqs = self.scan_did_events(user_hash, start_seq)?; 306 306 307 - let mut seen = std::collections::BTreeSet::new(); 307 + let mut seen = std::collections::HashSet::new(); 308 308 user_seqs 309 309 .into_iter() 310 310 .try_fold(Vec::new(), |mut acc, seq_u64| { ··· 318 318 match self.bridge.get_event_by_seq(seq_sn)? { 319 319 Some(event) if event.rev.is_some() => { 320 320 if let Some(blobs) = event.blobs { 321 - acc.extend( 322 - blobs 323 - .into_iter() 324 - .filter(|b| seen.insert(b.clone())) 325 - .map(CidLink::from), 326 - ); 321 + acc.extend(blobs.into_iter().filter(|b| seen.insert(b.clone()))); 327 322 } 328 323 Ok(acc) 329 324 }
+1 -3
crates/tranquil-store/src/metastore/handler.rs
··· 736 736 | Self::RevokeDelegation { delegated_did, .. } 737 737 | Self::UpdateDelegationScopes { delegated_did, .. } 738 738 | Self::GetDelegation { delegated_did, .. } 739 - | Self::LogDelegationAction { delegated_did, .. } => { 740 - did_to_routing(delegated_did.as_str()) 741 - } 739 + | Self::LogDelegationAction { delegated_did, .. } => did_to_routing(delegated_did), 742 740 Self::GetAccountsControlledBy { controller_did, .. } 743 741 | Self::ControlsAnyAccounts { 744 742 did: controller_did,
+2 -2
crates/tranquil-store/src/metastore/infra_ops.rs
··· 374 374 } 375 375 376 376 let value = InviteCodeValue { 377 - code: code.to_owned(), 377 + code: code.as_str().to_owned(), 378 378 available_uses: use_count, 379 379 disabled: false, 380 380 for_account: for_account.map(|d| d.to_string()), ··· 400 400 401 401 codes.iter().try_for_each(|code| { 402 402 let value = InviteCodeValue { 403 - code: code.clone(), 403 + code: code.as_str().to_owned(), 404 404 available_uses: use_count, 405 405 disabled: false, 406 406 for_account: for_account.map(|d| d.to_string()),
+3 -3
crates/tranquil-store/src/metastore/oauth_ops.rs
··· 247 247 refresh_token: data 248 248 .current_refresh_token 249 249 .as_ref() 250 - .map(|r| r.0.clone()) 250 + .map(|r| r.to_string()) 251 251 .unwrap_or_default(), 252 252 previous_refresh_token: None, 253 253 scope: data.scope.clone().unwrap_or_default(), ··· 877 877 ) -> Result<(), MetastoreError> { 878 878 let now_ms = Utc::now().timestamp_millis(); 879 879 let value = OAuthDeviceValue { 880 - session_id: data.session_id.0.clone(), 880 + session_id: data.session_id.as_str().to_owned(), 881 881 user_agent: data.user_agent.clone(), 882 882 ip_address: data.ip_address.clone(), 883 883 last_seen_at_ms: data.last_seen_at.timestamp_millis(), ··· 900 900 )?; 901 901 902 902 Ok(val.map(|v| DeviceData { 903 - session_id: tranquil_oauth::SessionId(v.session_id), 903 + session_id: tranquil_oauth::SessionId::from(v.session_id), 904 904 user_agent: v.user_agent, 905 905 ip_address: v.ip_address, 906 906 last_seen_at: DateTime::from_timestamp_millis(v.last_seen_at_ms).unwrap_or_default(),
+1 -1
crates/tranquil-store/src/metastore/user_ops.rs
··· 2931 2931 2932 2932 let user_hash = UserHash::from_did(input.did.as_str()); 2933 2933 let recovery = RecoveryTokenValue { 2934 - token_hash: input.setup_token_hash.clone(), 2934 + token_hash: input.setup_token_hash.as_str().to_owned(), 2935 2935 expires_at_ms: input.setup_expires_at.timestamp_millis(), 2936 2936 }; 2937 2937 self.users
+3 -5
crates/tranquil-sync/src/blob.rs
··· 98 98 .list_blobs_since_rev(&did, &Tid::from(since.clone())) 99 99 .await 100 100 .map(|cids| { 101 - let mut cid_strs: Vec<String> = cids.into_iter().map(|c| c.to_string()).collect(); 102 - cid_strs.sort(); 103 - cid_strs 104 - .into_iter() 101 + let mut cids = cids; 102 + cids.sort(); 103 + cids.into_iter() 105 104 .filter(|c| c.as_str() > cursor_cid) 106 105 .take(usize::try_from(limit + 1).unwrap_or(0)) 107 106 .collect() ··· 112 111 .blob 113 112 .list_blobs_by_user(user_id, Some(cursor_cid), limit + 1) 114 113 .await 115 - .map(|cids| cids.into_iter().map(|c| c.to_string()).collect()) 116 114 }; 117 115 match cids_result { 118 116 Ok(cids) => {
+3 -2
crates/tranquil-sync/src/commit.rs
··· 118 118 let rev = get_rev_from_commit(&state, &cid_str) 119 119 .await 120 120 .or_else(|| row.repo_rev.clone()) 121 + .map(|t| t.into_inner()) 121 122 .unwrap_or_default(); 122 123 let status = if row.takedown_ref.is_some() { 123 124 AccountStatus::Takendown ··· 131 132 head: cid_str, 132 133 rev, 133 134 active: status.is_active(), 134 - status: status.for_firehose_typed(), 135 + status: status.for_firehose(), 135 136 }); 136 137 } 137 138 let next_cursor = if has_more { ··· 203 204 Json(GetRepoStatusOutput { 204 205 did: account.did, 205 206 active: account.status.is_active(), 206 - status: account.status.for_firehose_typed(), 207 + status: account.status.for_firehose(), 207 208 rev, 208 209 }), 209 210 )