Our Personal Data Server from scratch! tranquil.farm
atproto pds rust postgresql fun oauth

fix: move code to more correct crates #51

merged opened by oyster.cafe targeting main from fix/move-code-to-right-crates
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:3fwecdnvtcscjnrx2p4n7alz/sh.tangled.repo.pull/3mh6yg6u4ln22
+1730 -1404
Diff #0
+142 -19
Cargo.lock
··· 6092 6092 "syn 2.0.111", 6093 6093 ] 6094 6094 6095 + [[package]] 6096 + name = "tranquil-api" 6097 + version = "0.4.2" 6098 + dependencies = [ 6099 + "anyhow", 6100 + "axum", 6101 + "backon", 6102 + "base32", 6103 + "base64 0.22.1", 6104 + "bcrypt", 6105 + "bs58", 6106 + "bytes", 6107 + "chrono", 6108 + "cid", 6109 + "ed25519-dalek", 6110 + "futures", 6111 + "hex", 6112 + "http 1.4.0", 6113 + "infer", 6114 + "ipld-core", 6115 + "jacquard-common", 6116 + "jacquard-repo", 6117 + "k256", 6118 + "multibase", 6119 + "multihash", 6120 + "rand 0.8.5", 6121 + "reqwest", 6122 + "serde", 6123 + "serde_ipld_dagcbor", 6124 + "serde_json", 6125 + "sha2", 6126 + "subtle", 6127 + "thiserror 2.0.17", 6128 + "tokio", 6129 + "tracing", 6130 + "tranquil-config", 6131 + "tranquil-db", 6132 + "tranquil-db-traits", 6133 + "tranquil-lexicon", 6134 + "tranquil-pds", 6135 + "tranquil-scopes", 6136 + "tranquil-types", 6137 + "urlencoding", 6138 + "uuid", 6139 + "webauthn-rs", 6140 + "zip", 6141 + ] 6142 + 6095 6143 [[package]] 6096 6144 name = "tranquil-auth" 6097 - version = "0.4.1" 6145 + version = "0.4.2" 6098 6146 dependencies = [ 6099 6147 "anyhow", 6100 6148 "base32", ··· 6117 6165 6118 6166 [[package]] 6119 6167 name = "tranquil-cache" 6120 - version = "0.4.1" 6168 + version = "0.4.2" 6121 6169 dependencies = [ 6122 6170 "async-trait", 6123 6171 "base64 0.22.1", ··· 6131 6179 6132 6180 [[package]] 6133 6181 name = "tranquil-comms" 6134 - version = "0.4.1" 6182 + version = "0.4.2" 6135 6183 dependencies = [ 6136 6184 "async-trait", 6137 6185 "base64 0.22.1", ··· 6146 6194 6147 6195 [[package]] 6148 6196 name = "tranquil-config" 6149 - version = "0.4.1" 6197 + version = "0.4.2" 6150 6198 dependencies = [ 6151 6199 "confique", 6152 6200 "serde", ··· 6154 6202 6155 6203 [[package]] 6156 6204 name = "tranquil-crypto" 6157 - version = "0.4.1" 6205 + version = "0.4.2" 6158 6206 dependencies = [ 6159 6207 "aes-gcm", 6160 6208 "base64 0.22.1", ··· 6170 6218 6171 6219 [[package]] 6172 6220 name = "tranquil-db" 6173 - version = "0.4.1" 6221 + version = "0.4.2" 6174 6222 dependencies = [ 6175 6223 "async-trait", 6176 6224 "chrono", ··· 6187 6235 6188 6236 [[package]] 6189 6237 name = "tranquil-db-traits" 6190 - version = "0.4.1" 6238 + version = "0.4.2" 6191 6239 dependencies = [ 6192 6240 "async-trait", 6193 6241 "base64 0.22.1", ··· 6203 6251 6204 6252 [[package]] 6205 6253 name = "tranquil-infra" 6206 - version = "0.4.1" 6254 + version = "0.4.2" 6207 6255 dependencies = [ 6208 6256 "async-trait", 6209 6257 "bytes", ··· 6214 6262 6215 6263 [[package]] 6216 6264 name = "tranquil-lexicon" 6217 - version = "0.4.1" 6265 + version = "0.4.2" 6218 6266 dependencies = [ 6219 6267 "chrono", 6220 6268 "hickory-resolver", ··· 6232 6280 6233 6281 [[package]] 6234 6282 name = "tranquil-oauth" 6235 - version = "0.4.1" 6283 + version = "0.4.2" 6236 6284 dependencies = [ 6237 6285 "anyhow", 6238 6286 "axum", ··· 6253 6301 "uuid", 6254 6302 ] 6255 6303 6304 + [[package]] 6305 + name = "tranquil-oauth-server" 6306 + version = "0.4.2" 6307 + dependencies = [ 6308 + "axum", 6309 + "base64 0.22.1", 6310 + "bcrypt", 6311 + "chrono", 6312 + "cid", 6313 + "hmac", 6314 + "http 1.4.0", 6315 + "jacquard-common", 6316 + "jacquard-repo", 6317 + "k256", 6318 + "rand 0.8.5", 6319 + "serde", 6320 + "serde_json", 6321 + "serde_urlencoded", 6322 + "sha2", 6323 + "subtle", 6324 + "tokio", 6325 + "tracing", 6326 + "tranquil-api", 6327 + "tranquil-config", 6328 + "tranquil-crypto", 6329 + "tranquil-db-traits", 6330 + "tranquil-pds", 6331 + "tranquil-types", 6332 + "urlencoding", 6333 + "uuid", 6334 + "webauthn-rs", 6335 + ] 6336 + 6256 6337 [[package]] 6257 6338 name = "tranquil-pds" 6258 - version = "0.4.1" 6339 + version = "0.4.2" 6259 6340 dependencies = [ 6260 6341 "aes-gcm", 6261 6342 "anyhow", ··· 6272 6353 "chrono", 6273 6354 "ciborium", 6274 6355 "cid", 6275 - "clap", 6276 6356 "ctor", 6277 - "dotenvy", 6278 6357 "ed25519-dalek", 6279 6358 "futures", 6280 6359 "futures-util", ··· 6319 6398 "tower-http", 6320 6399 "tower-layer", 6321 6400 "tracing", 6322 - "tracing-subscriber", 6401 + "tranquil-api", 6323 6402 "tranquil-auth", 6324 6403 "tranquil-cache", 6325 6404 "tranquil-comms", ··· 6329 6408 "tranquil-db-traits", 6330 6409 "tranquil-lexicon", 6331 6410 "tranquil-oauth", 6411 + "tranquil-oauth-server", 6332 6412 "tranquil-repo", 6333 6413 "tranquil-ripple", 6334 6414 "tranquil-scopes", 6335 6415 "tranquil-storage", 6416 + "tranquil-sync", 6336 6417 "tranquil-types", 6337 6418 "urlencoding", 6338 6419 "uuid", ··· 6343 6424 6344 6425 [[package]] 6345 6426 name = "tranquil-repo" 6346 - version = "0.4.1" 6427 + version = "0.4.2" 6347 6428 dependencies = [ 6348 6429 "bytes", 6349 6430 "cid", ··· 6355 6436 6356 6437 [[package]] 6357 6438 name = "tranquil-ripple" 6358 - version = "0.4.1" 6439 + version = "0.4.2" 6359 6440 dependencies = [ 6360 6441 "async-trait", 6361 6442 "backon", ··· 6380 6461 6381 6462 [[package]] 6382 6463 name = "tranquil-scopes" 6383 - version = "0.4.1" 6464 + version = "0.4.2" 6384 6465 dependencies = [ 6385 6466 "axum", 6386 6467 "futures", ··· 6394 6475 "urlencoding", 6395 6476 ] 6396 6477 6478 + [[package]] 6479 + name = "tranquil-server" 6480 + version = "0.4.2" 6481 + dependencies = [ 6482 + "axum", 6483 + "clap", 6484 + "dotenvy", 6485 + "ed25519-dalek", 6486 + "hex", 6487 + "tokio", 6488 + "tokio-util", 6489 + "tracing", 6490 + "tracing-subscriber", 6491 + "tranquil-api", 6492 + "tranquil-config", 6493 + "tranquil-oauth-server", 6494 + "tranquil-pds", 6495 + "tranquil-sync", 6496 + ] 6497 + 6397 6498 [[package]] 6398 6499 name = "tranquil-storage" 6399 - version = "0.4.1" 6500 + version = "0.4.2" 6400 6501 dependencies = [ 6401 6502 "async-trait", 6402 6503 "aws-config", ··· 6411 6512 "uuid", 6412 6513 ] 6413 6514 6515 + [[package]] 6516 + name = "tranquil-sync" 6517 + version = "0.4.2" 6518 + dependencies = [ 6519 + "anyhow", 6520 + "axum", 6521 + "bytes", 6522 + "chrono", 6523 + "cid", 6524 + "futures", 6525 + "ipld-core", 6526 + "jacquard-repo", 6527 + "serde", 6528 + "serde_ipld_dagcbor", 6529 + "tokio", 6530 + "tracing", 6531 + "tranquil-config", 6532 + "tranquil-db-traits", 6533 + "tranquil-pds", 6534 + "tranquil-types", 6535 + ] 6536 + 6414 6537 [[package]] 6415 6538 name = "tranquil-types" 6416 - version = "0.4.1" 6539 + version = "0.4.2" 6417 6540 dependencies = [ 6418 6541 "chrono", 6419 6542 "cid",
+10 -1
Cargo.toml
··· 16 16 "crates/tranquil-db-traits", 17 17 "crates/tranquil-db", 18 18 "crates/tranquil-pds", 19 + "crates/tranquil-server", 20 + "crates/tranquil-sync", 21 + "crates/tranquil-oauth-server", 22 + "crates/tranquil-api", 19 23 "crates/tranquil-lexicon", 20 24 ] 21 25 22 26 [workspace.package] 23 - version = "0.4.1" 27 + version = "0.4.2" 24 28 edition = "2024" 25 29 license = "AGPL-3.0-or-later" 26 30 ··· 40 44 tranquil-db = { path = "crates/tranquil-db" } 41 45 tranquil-ripple = { path = "crates/tranquil-ripple" } 42 46 tranquil-lexicon = { path = "crates/tranquil-lexicon" } 47 + tranquil-pds = { path = "crates/tranquil-pds" } 48 + tranquil-server = { path = "crates/tranquil-server" } 49 + tranquil-sync = { path = "crates/tranquil-sync" } 50 + tranquil-oauth-server = { path = "crates/tranquil-oauth-server" } 51 + tranquil-api = { path = "crates/tranquil-api" } 43 52 44 53 unicode-segmentation = "1" 45 54
+7 -3
Dockerfile
··· 25 25 COPY crates/tranquil-storage ./crates/tranquil-storage 26 26 COPY crates/tranquil-cache ./crates/tranquil-cache 27 27 COPY crates/tranquil-pds ./crates/tranquil-pds 28 + COPY crates/tranquil-sync ./crates/tranquil-sync 29 + COPY crates/tranquil-api ./crates/tranquil-api 30 + COPY crates/tranquil-oauth-server ./crates/tranquil-oauth-server 31 + COPY crates/tranquil-server ./crates/tranquil-server 28 32 COPY migrations ./crates/tranquil-pds/migrations 29 33 RUN --mount=type=cache,target=/usr/local/cargo/registry \ 30 34 --mount=type=cache,target=/app/target \ 31 35 if [ "$SLIM" = "true" ]; then \ 32 - SQLX_OFFLINE=true cargo build --release -p tranquil-pds --no-default-features; \ 36 + SQLX_OFFLINE=true cargo build --release -p tranquil-server --no-default-features; \ 33 37 else \ 34 - SQLX_OFFLINE=true cargo build --release -p tranquil-pds; \ 38 + SQLX_OFFLINE=true cargo build --release -p tranquil-server; \ 35 39 fi && \ 36 - cp target/release/tranquil-pds /tmp/tranquil-pds 40 + cp target/release/tranquil-server /tmp/tranquil-pds 37 41 38 42 FROM alpine:3.23 AS signal-cli 39 43 RUN apk add --no-cache curl tar
+50
crates/tranquil-api/Cargo.toml
··· 1 + [package] 2 + name = "tranquil-api" 3 + version.workspace = true 4 + edition.workspace = true 5 + license.workspace = true 6 + 7 + [dependencies] 8 + tranquil-pds = { workspace = true } 9 + tranquil-types = { workspace = true } 10 + tranquil-config = { workspace = true } 11 + tranquil-db = { workspace = true } 12 + tranquil-db-traits = { workspace = true } 13 + tranquil-lexicon = { workspace = true, features = ["resolve"] } 14 + tranquil-scopes = { workspace = true } 15 + 16 + anyhow = { workspace = true } 17 + axum = { workspace = true } 18 + backon = { workspace = true } 19 + base32 = { workspace = true } 20 + base64 = { workspace = true } 21 + bcrypt = { workspace = true } 22 + bs58 = { workspace = true } 23 + bytes = { workspace = true } 24 + chrono = { workspace = true } 25 + cid = { workspace = true } 26 + ed25519-dalek = { workspace = true } 27 + futures = { workspace = true } 28 + hex = { workspace = true } 29 + http = { workspace = true } 30 + infer = { workspace = true } 31 + ipld-core = { workspace = true } 32 + jacquard-common = { workspace = true } 33 + jacquard-repo = { workspace = true } 34 + k256 = { workspace = true } 35 + multibase = { workspace = true } 36 + multihash = { workspace = true } 37 + rand = { workspace = true } 38 + reqwest = { workspace = true } 39 + serde = { workspace = true } 40 + serde_json = { workspace = true } 41 + serde_ipld_dagcbor = { workspace = true } 42 + sha2 = { workspace = true } 43 + subtle = { workspace = true } 44 + thiserror = { workspace = true } 45 + tokio = { workspace = true } 46 + tracing = { workspace = true } 47 + urlencoding = { workspace = true } 48 + uuid = { workspace = true } 49 + webauthn-rs = { workspace = true } 50 + zip = { workspace = true }
crates/tranquil-pds/src/api/actor/mod.rs crates/tranquil-api/src/actor/mod.rs
+3 -3
crates/tranquil-pds/src/api/actor/preferences.rs crates/tranquil-api/src/actor/preferences.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::auth::{Auth, NotTakendown, Permissive}; 3 - use crate::state::AppState; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::auth::{Auth, NotTakendown, Permissive}; 3 + use tranquil_pds::state::AppState; 4 4 use axum::{ 5 5 Json, 6 6 extract::State,
+7 -7
crates/tranquil-pds/src/api/admin/account/delete.rs crates/tranquil-api/src/admin/account/delete.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{Admin, Auth}; 4 - use crate::state::AppState; 5 - use crate::types::Did; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{Admin, Auth}; 4 + use tranquil_pds::state::AppState; 5 + use tranquil_pds::types::Did; 6 6 use axum::{ 7 7 Json, 8 8 extract::State, ··· 36 36 .await 37 37 .log_db_err("deleting account")?; 38 38 39 - if let Err(e) = crate::api::repo::record::sequence_account_event( 39 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 40 40 &state, 41 41 did, 42 42 tranquil_db_traits::AccountStatus::Deleted, ··· 50 50 } 51 51 let _ = state 52 52 .cache 53 - .delete(&crate::cache_keys::handle_key(&handle)) 53 + .delete(&tranquil_pds::cache_keys::handle_key(&handle)) 54 54 .await; 55 55 Ok(EmptyResponse::ok().into_response()) 56 56 }
+4 -4
crates/tranquil-pds/src/api/admin/account/email.rs crates/tranquil-api/src/admin/account/email.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::auth::{Admin, Auth}; 3 - use crate::state::AppState; 4 - use crate::types::Did; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::auth::{Admin, Auth}; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::types::Did; 5 5 use axum::{ 6 6 Json, 7 7 extract::State,
+5 -5
crates/tranquil-pds/src/api/admin/account/info.rs crates/tranquil-api/src/admin/account/info.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::auth::{Admin, Auth}; 3 - use crate::state::AppState; 4 - use crate::types::{Did, Handle}; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::auth::{Admin, Auth}; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::types::{Did, Handle}; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, RawQuery, State}, ··· 196 196 _auth: Auth<Admin>, 197 197 RawQuery(raw_query): RawQuery, 198 198 ) -> Result<Response, ApiError> { 199 - let dids: Vec<String> = crate::util::parse_repeated_query_param(raw_query.as_deref(), "dids") 199 + let dids: Vec<String> = tranquil_pds::util::parse_repeated_query_param(raw_query.as_deref(), "dids") 200 200 .into_iter() 201 201 .filter(|d| !d.is_empty()) 202 202 .collect();
crates/tranquil-pds/src/api/admin/account/mod.rs crates/tranquil-api/src/admin/account/mod.rs
+4 -4
crates/tranquil-pds/src/api/admin/account/search.rs crates/tranquil-api/src/admin/account/search.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::auth::{Admin, Auth}; 3 - use crate::state::AppState; 4 - use crate::types::{Did, Handle}; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::auth::{Admin, Auth}; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::types::{Did, Handle}; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, State},
+9 -9
crates/tranquil-pds/src/api/admin/account/update.rs crates/tranquil-api/src/admin/account/update.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::ApiError; 3 - use crate::auth::{Admin, Auth}; 4 - use crate::state::AppState; 5 - use crate::types::{Did, Handle, PlainPassword}; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::ApiError; 3 + use tranquil_pds::auth::{Admin, Auth}; 4 + use tranquil_pds::state::AppState; 5 + use tranquil_pds::types::{Did, Handle, PlainPassword}; 6 6 use axum::{ 7 7 Json, 8 8 extract::State, ··· 101 101 if let Some(old) = old_handle { 102 102 let _ = state 103 103 .cache 104 - .delete(&crate::cache_keys::handle_key(&old)) 104 + .delete(&tranquil_pds::cache_keys::handle_key(&old)) 105 105 .await; 106 106 } 107 107 let _ = state 108 108 .cache 109 - .delete(&crate::cache_keys::handle_key(&handle)) 109 + .delete(&tranquil_pds::cache_keys::handle_key(&handle)) 110 110 .await; 111 - if let Err(e) = crate::api::repo::record::sequence_identity_event( 111 + if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event( 112 112 &state, 113 113 did, 114 114 Some(&handle_for_check), ··· 121 121 ); 122 122 } 123 123 if let Err(e) = 124 - crate::api::identity::did::update_plc_handle(&state, did, &handle_for_check).await 124 + crate::identity::did::update_plc_handle(&state, did, &handle_for_check).await 125 125 { 126 126 warn!("Failed to update PLC handle for admin handle update: {}", e); 127 127 }
+3 -3
crates/tranquil-pds/src/api/admin/config.rs crates/tranquil-api/src/admin/config.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::auth::{Admin, Auth}; 3 - use crate::state::AppState; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::auth::{Admin, Auth}; 3 + use tranquil_pds::state::AppState; 4 4 use axum::{Json, extract::State}; 5 5 use serde::{Deserialize, Serialize}; 6 6 use tracing::{error, warn};
+4 -4
crates/tranquil-pds/src/api/admin/invite.rs crates/tranquil-api/src/admin/invite.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{Admin, Auth}; 4 - use crate::state::AppState; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{Admin, Auth}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, State},
crates/tranquil-pds/src/api/admin/mod.rs crates/tranquil-api/src/admin/mod.rs
+3 -3
crates/tranquil-pds/src/api/admin/server_stats.rs crates/tranquil-api/src/admin/server_stats.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::auth::{Admin, Auth}; 3 - use crate::state::AppState; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::auth::{Admin, Auth}; 3 + use tranquil_pds::state::AppState; 4 4 use axum::{ 5 5 Json, 6 6 extract::State,
+7 -7
crates/tranquil-pds/src/api/admin/status.rs crates/tranquil-api/src/admin/status.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::auth::{Admin, Auth}; 3 - use crate::state::AppState; 4 - use crate::types::{CidLink, Did}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::auth::{Admin, Auth}; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::types::{CidLink, Did}; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, State}, ··· 215 215 tranquil_db_traits::AccountStatus::Active 216 216 }; 217 217 if let Err(e) = 218 - crate::api::repo::record::sequence_account_event(&state, &did, status).await 218 + tranquil_pds::repo_ops::sequence_account_event(&state, &did, status).await 219 219 { 220 220 warn!("Failed to sequence account event for takedown: {}", e); 221 221 } ··· 227 227 tranquil_db_traits::AccountStatus::Active 228 228 }; 229 229 if let Err(e) = 230 - crate::api::repo::record::sequence_account_event(&state, &did, status).await 230 + tranquil_pds::repo_ops::sequence_account_event(&state, &did, status).await 231 231 { 232 232 warn!("Failed to sequence account event for deactivation: {}", e); 233 233 } ··· 235 235 if let Ok(Some(handle)) = state.user_repo.get_handle_by_did(&did).await { 236 236 let _ = state 237 237 .cache 238 - .delete(&crate::cache_keys::handle_key(&handle)) 238 + .delete(&tranquil_pds::cache_keys::handle_key(&handle)) 239 239 .await; 240 240 } 241 241 return Ok((
+4 -4
crates/tranquil-pds/src/api/age_assurance.rs crates/tranquil-api/src/age_assurance.rs
··· 1 - use crate::auth::{AccountRequirement, extract_auth_token_from_header, validate_token_with_dpop}; 2 - use crate::state::AppState; 1 + use tranquil_pds::auth::{AccountRequirement, extract_auth_token_from_header, validate_token_with_dpop}; 2 + use tranquil_pds::state::AppState; 3 3 use axum::{ 4 4 Json, 5 5 extract::State, ··· 33 33 } 34 34 35 35 async fn get_account_created_at(state: &AppState, headers: &HeaderMap) -> Option<String> { 36 - let auth_header = crate::util::get_header_str(headers, http::header::AUTHORIZATION); 36 + let auth_header = tranquil_pds::util::get_header_str(headers, http::header::AUTHORIZATION); 37 37 tracing::debug!(?auth_header, "age assurance: extracting token"); 38 38 39 39 let extracted = extract_auth_token_from_header(auth_header)?; 40 40 tracing::debug!("age assurance: got token, validating"); 41 41 42 - let dpop_proof = crate::util::get_header_str(headers, crate::util::HEADER_DPOP); 42 + let dpop_proof = tranquil_pds::util::get_header_str(headers, tranquil_pds::util::HEADER_DPOP); 43 43 let http_uri = "/"; 44 44 45 45 let auth_user = match validate_token_with_dpop(
+13 -13
crates/tranquil-pds/src/api/backup.rs crates/tranquil-api/src/backup.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::api::{EmptyResponse, EnabledResponse}; 3 - use crate::auth::{Active, Auth}; 4 - use crate::scheduled::generate_full_backup; 5 - use crate::state::AppState; 6 - use crate::storage::{BackupStorage, backup_retention_count}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::api::{EmptyResponse, EnabledResponse}; 3 + use tranquil_pds::auth::{Active, Auth}; 4 + use tranquil_pds::scheduled::generate_full_backup; 5 + use tranquil_pds::state::AppState; 6 + use tranquil_pds::storage::{BackupStorage, backup_retention_count}; 7 7 use anyhow::Context; 8 8 use axum::{ 9 9 Json, ··· 39 39 pub async fn list_backups( 40 40 State(state): State<AppState>, 41 41 auth: Auth<Active>, 42 - ) -> Result<Response, crate::api::error::ApiError> { 42 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 43 43 let (user_id, backup_enabled) = match state.backup_repo.get_user_backup_status(&auth.did).await 44 44 { 45 45 Ok(Some(status)) => status, ··· 91 91 State(state): State<AppState>, 92 92 auth: Auth<Active>, 93 93 Query(query): Query<GetBackupQuery>, 94 - ) -> Result<Response, crate::api::error::ApiError> { 94 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 95 95 let backup_id = match uuid::Uuid::parse_str(&query.id) { 96 96 Ok(id) => id, 97 97 Err(_) => { ··· 157 157 pub async fn create_backup( 158 158 State(state): State<AppState>, 159 159 auth: Auth<Active>, 160 - ) -> Result<Response, crate::api::error::ApiError> { 160 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 161 161 let backup_storage = match state.backup_storage.as_ref() { 162 162 Some(storage) => storage, 163 163 None => { ··· 213 213 } 214 214 }; 215 215 216 - let block_count = crate::scheduled::count_car_blocks(&car_bytes); 216 + let block_count = tranquil_pds::scheduled::count_car_blocks(&car_bytes); 217 217 let size_bytes = i64::try_from(car_bytes.len()).unwrap_or(i64::MAX); 218 218 219 219 let storage_key = match backup_storage ··· 327 327 State(state): State<AppState>, 328 328 auth: Auth<Active>, 329 329 Query(query): Query<DeleteBackupQuery>, 330 - ) -> Result<Response, crate::api::error::ApiError> { 330 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 331 331 let backup_id = match uuid::Uuid::parse_str(&query.id) { 332 332 Ok(id) => id, 333 333 Err(_) => { ··· 384 384 State(state): State<AppState>, 385 385 auth: Auth<Active>, 386 386 Json(input): Json<SetBackupEnabledInput>, 387 - ) -> Result<Response, crate::api::error::ApiError> { 387 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 388 388 let deactivated_at = match state 389 389 .backup_repo 390 390 .get_user_deactivated_status(&auth.did) ··· 423 423 pub async fn export_blobs( 424 424 State(state): State<AppState>, 425 425 auth: Auth<Active>, 426 - ) -> Result<Response, crate::api::error::ApiError> { 426 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 427 427 let user_id = match state.backup_repo.get_user_id_by_did(&auth.did).await { 428 428 Ok(Some(id)) => id, 429 429 Ok(None) => {
+19 -19
crates/tranquil-pds/src/api/delegation.rs crates/tranquil-api/src/delegation.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::api::repo::record::utils::create_signed_commit; 3 - use crate::auth::{Active, Auth}; 4 - use crate::delegation::{ 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::repo_ops::create_signed_commit; 3 + use tranquil_pds::auth::{Active, Auth}; 4 + use tranquil_pds::delegation::{ 5 5 DelegationActionType, SCOPE_PRESETS, ValidatedDelegationScope, verify_can_add_controllers, 6 6 verify_can_be_controller, verify_can_control_accounts, 7 7 }; 8 - use crate::rate_limit::{AccountCreationLimit, RateLimited}; 9 - use crate::state::AppState; 10 - use crate::types::{Did, Handle}; 8 + use tranquil_pds::rate_limit::{AccountCreationLimit, RateLimited}; 9 + use tranquil_pds::state::AppState; 10 + use tranquil_pds::types::{Did, Handle}; 11 11 use axum::{ 12 12 Json, 13 13 extract::{Query, State}, ··· 449 449 .unwrap_or(&input.handle), 450 450 None => &input.handle, 451 451 }; 452 - match crate::api::validation::validate_short_handle(handle_to_validate) { 452 + match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) { 453 453 Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])), 454 454 Err(e) => { 455 455 return Ok(ApiError::InvalidRequest(e.to_string()).into_response()); ··· 465 465 .map(|e| e.trim().to_string()) 466 466 .filter(|e| !e.is_empty()); 467 467 if let Some(ref email) = email 468 - && !crate::api::validation::is_valid_email(email) 468 + && !tranquil_pds::api::validation::is_valid_email(email) 469 469 { 470 470 return Ok(ApiError::InvalidEmail.into_response()); 471 471 } ··· 502 502 .secrets 503 503 .plc_rotation_key 504 504 .clone() 505 - .unwrap_or_else(|| crate::plc::signing_key_to_did_key(&signing_key)); 505 + .unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&signing_key)); 506 506 507 - let genesis_result = match crate::plc::create_genesis_operation( 507 + let genesis_result = match tranquil_pds::plc::create_genesis_operation( 508 508 &signing_key, 509 509 &rotation_key, 510 510 &handle, ··· 520 520 } 521 521 }; 522 522 523 - let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 523 + let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 524 524 if let Err(e) = plc_client 525 525 .send_operation(&genesis_result.did, &genesis_result.signed_operation) 526 526 .await ··· 540 540 let handle: Handle = handle.parse().map_err(|_| ApiError::InvalidHandle(None))?; 541 541 info!(did = %did, handle = %handle, controller = %can_control.did(), "Created DID for delegated account"); 542 542 543 - let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) { 543 + let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) { 544 544 Ok(bytes) => bytes, 545 545 Err(e) => { 546 546 error!("Error encrypting signing key: {:?}", e); ··· 581 581 controller_did: can_control.did().clone(), 582 582 controller_scopes: input.controller_scopes.as_str().to_string(), 583 583 encrypted_key_bytes, 584 - encryption_version: crate::config::ENCRYPTION_VERSION, 584 + encryption_version: tranquil_pds::config::ENCRYPTION_VERSION, 585 585 commit_cid: commit_cid.to_string(), 586 586 repo_rev: rev.as_ref().to_string(), 587 587 genesis_block_cids, ··· 616 616 } 617 617 618 618 if let Err(e) = 619 - crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await 619 + tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle)).await 620 620 { 621 621 warn!("Failed to sequence identity event for {}: {}", did, e); 622 622 } 623 - if let Err(e) = crate::api::repo::record::sequence_account_event( 623 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 624 624 &state, 625 625 &did, 626 626 tranquil_db_traits::AccountStatus::Active, ··· 634 634 "$type": "app.bsky.actor.profile", 635 635 "displayName": handle 636 636 }); 637 - if let Err(e) = crate::api::repo::record::create_record_internal( 637 + if let Err(e) = tranquil_pds::repo_ops::create_record_internal( 638 638 &state, 639 639 &did, 640 - &crate::types::PROFILE_COLLECTION, 641 - &crate::types::PROFILE_RKEY, 640 + &tranquil_pds::types::PROFILE_COLLECTION, 641 + &tranquil_pds::types::PROFILE_RKEY, 642 642 &profile_record, 643 643 ) 644 644 .await
+3 -3
crates/tranquil-pds/src/api/discord_webhook.rs crates/tranquil-api/src/discord_webhook.rs
··· 10 10 use tracing::{debug, info, warn}; 11 11 use tranquil_types::Handle; 12 12 13 - use crate::comms::comms_repo; 14 - use crate::state::AppState; 15 - use crate::util::discord_public_key; 13 + use tranquil_pds::comms::comms_repo; 14 + use tranquil_pds::state::AppState; 15 + use tranquil_pds::util::discord_public_key; 16 16 17 17 #[derive(Deserialize)] 18 18 struct Interaction {
+38 -38
crates/tranquil-pds/src/api/identity/account.rs crates/tranquil-api/src/identity/account.rs
··· 1 1 use super::did::verify_did_web; 2 - use crate::api::error::ApiError; 3 - use crate::api::repo::record::utils::create_signed_commit; 4 - use crate::auth::{ServiceTokenVerifier, extract_auth_token_from_header, is_service_token}; 5 - use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key}; 6 - use crate::rate_limit::{AccountCreationLimit, RateLimited}; 7 - use crate::state::AppState; 8 - use crate::types::{Did, Handle, PlainPassword}; 9 - use crate::validation::validate_password; 2 + use tranquil_pds::api::error::ApiError; 3 + use tranquil_pds::repo_ops::create_signed_commit; 4 + use tranquil_pds::auth::{ServiceTokenVerifier, extract_auth_token_from_header, is_service_token}; 5 + use tranquil_pds::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key}; 6 + use tranquil_pds::rate_limit::{AccountCreationLimit, RateLimited}; 7 + use tranquil_pds::state::AppState; 8 + use tranquil_pds::types::{Did, Handle, PlainPassword}; 9 + use tranquil_pds::validation::validate_password; 10 10 use axum::{ 11 11 Json, 12 12 extract::State, ··· 73 73 } 74 74 75 75 let migration_auth = if let Some(extracted) = extract_auth_token_from_header( 76 - crate::util::get_header_str(&headers, http::header::AUTHORIZATION), 76 + tranquil_pds::util::get_header_str(&headers, http::header::AUTHORIZATION), 77 77 ) { 78 78 let token = extracted.token; 79 79 if is_service_token(&token) { ··· 155 155 .unwrap_or(&input.handle), 156 156 None => &input.handle, 157 157 }; 158 - match crate::api::validation::validate_short_handle(handle_to_validate) { 158 + match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) { 159 159 Ok(h) => h, 160 160 Err(e) => { 161 161 return ApiError::from(e).into_response(); 162 162 } 163 163 } 164 164 } else { 165 - match crate::api::validation::validate_full_domain_handle(&input.handle) { 165 + match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) { 166 166 Ok(h) => h, 167 167 Err(e) => return ApiError::from(e).into_response(), 168 168 } ··· 173 173 .map(|e| e.trim().to_string()) 174 174 .filter(|e| !e.is_empty()); 175 175 if let Some(ref email) = email 176 - && !crate::api::validation::is_valid_email(email) 176 + && !tranquil_pds::api::validation::is_valid_email(email) 177 177 { 178 178 return ApiError::InvalidEmail.into_response(); 179 179 } ··· 191 191 tranquil_db_traits::CommsChannel::Discord => match &input.discord_username { 192 192 Some(username) if !username.trim().is_empty() => { 193 193 let clean = username.trim().to_lowercase(); 194 - if !crate::api::validation::is_valid_discord_username(&clean) { 194 + if !tranquil_pds::api::validation::is_valid_discord_username(&clean) { 195 195 return ApiError::InvalidRequest( 196 196 "Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(), 197 197 ).into_response(); ··· 203 203 tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username { 204 204 Some(username) if !username.trim().is_empty() => { 205 205 let clean = username.trim().trim_start_matches('@'); 206 - if !crate::api::validation::is_valid_telegram_username(clean) { 206 + if !tranquil_pds::api::validation::is_valid_telegram_username(clean) { 207 207 return ApiError::InvalidRequest( 208 208 "Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(), 209 209 ).into_response(); ··· 257 257 let did_type = input.did_type.as_deref().unwrap_or("plc"); 258 258 let did = match did_type { 259 259 "web" => { 260 - if !crate::api::server::meta::is_self_hosted_did_web_enabled() { 260 + if !tranquil_pds::util::is_self_hosted_did_web_enabled() { 261 261 return ApiError::SelfHostedDidWebDisabled.into_response(); 262 262 } 263 263 let encoded_handle = handle.replace(':', "%3A"); ··· 408 408 .await 409 409 { 410 410 Ok(Some(key_info)) => { 411 - match crate::config::decrypt_key( 411 + match tranquil_pds::config::decrypt_key( 412 412 &key_info.key_bytes, 413 413 key_info.encryption_version, 414 414 ) { ··· 428 428 } 429 429 }; 430 430 let access_meta = 431 - match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) { 431 + match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) { 432 432 Ok(m) => m, 433 433 Err(e) => { 434 434 error!("Error creating access token: {:?}", e); 435 435 return ApiError::InternalError(None).into_response(); 436 436 } 437 437 }; 438 - let refresh_meta = match crate::auth::create_refresh_token_with_metadata( 438 + let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata( 439 439 &did, 440 440 &secret_key_bytes, 441 441 ) { ··· 463 463 } 464 464 let hostname = &tranquil_config::get().server.hostname; 465 465 let verification_required = if let Some(ref user_email) = email { 466 - let token = crate::auth::verification_token::generate_migration_token( 466 + let token = tranquil_pds::auth::verification_token::generate_migration_token( 467 467 &did_typed, user_email, 468 468 ); 469 469 let formatted_token = 470 - crate::auth::verification_token::format_token_for_display(&token); 471 - if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification( 470 + tranquil_pds::auth::verification_token::format_token_for_display(&token); 471 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification( 472 472 state.user_repo.as_ref(), 473 473 state.infra_repo.as_ref(), 474 474 reactivated.user_id, ··· 590 590 None 591 591 }; 592 592 593 - let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) { 593 + let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) { 594 594 Ok(enc) => enc, 595 595 Err(e) => { 596 596 error!("Error encrypting user key: {:?}", e); ··· 666 666 .map(|s| s.to_lowercase()), 667 667 deactivated_at, 668 668 encrypted_key_bytes, 669 - encryption_version: crate::config::ENCRYPTION_VERSION, 669 + encryption_version: tranquil_pds::config::ENCRYPTION_VERSION, 670 670 reserved_key_id, 671 671 commit_cid: commit_cid_str.clone(), 672 672 repo_rev: rev_str.clone(), ··· 697 697 }; 698 698 let user_id = create_result.user_id; 699 699 if !is_migration && !is_did_web_byod { 700 - if let Err(e) = crate::api::repo::record::sequence_identity_event( 700 + if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event( 701 701 &state, 702 702 &did_for_commit, 703 703 Some(&handle_typed), ··· 706 706 { 707 707 warn!("Failed to sequence identity event for {}: {}", did, e); 708 708 } 709 - if let Err(e) = crate::api::repo::record::sequence_account_event( 709 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 710 710 &state, 711 711 &did_for_commit, 712 712 tranquil_db_traits::AccountStatus::Active, ··· 715 715 { 716 716 warn!("Failed to sequence account event for {}: {}", did, e); 717 717 } 718 - if let Err(e) = crate::api::repo::record::sequence_genesis_commit( 718 + if let Err(e) = tranquil_pds::repo_ops::sequence_genesis_commit( 719 719 &state, 720 720 &did_for_commit, 721 721 &commit_cid, ··· 726 726 { 727 727 warn!("Failed to sequence commit event for {}: {}", did, e); 728 728 } 729 - if let Err(e) = crate::api::repo::record::sequence_sync_event( 729 + if let Err(e) = tranquil_pds::repo_ops::sequence_sync_event( 730 730 &state, 731 731 &did_for_commit, 732 732 &commit_cid_str, ··· 740 740 "$type": "app.bsky.actor.profile", 741 741 "displayName": input.handle 742 742 }); 743 - if let Err(e) = crate::api::repo::record::create_record_internal( 743 + if let Err(e) = tranquil_pds::repo_ops::create_record_internal( 744 744 &state, 745 745 &did_for_commit, 746 - &crate::types::PROFILE_COLLECTION, 747 - &crate::types::PROFILE_RKEY, 746 + &tranquil_pds::types::PROFILE_COLLECTION, 747 + &tranquil_pds::types::PROFILE_RKEY, 748 748 &profile_record, 749 749 ) 750 750 .await ··· 755 755 let hostname = &tranquil_config::get().server.hostname; 756 756 if !is_migration { 757 757 if let Some(ref recipient) = verification_recipient { 758 - let verification_token = crate::auth::verification_token::generate_signup_token( 758 + let verification_token = tranquil_pds::auth::verification_token::generate_signup_token( 759 759 &did_for_commit, 760 760 verification_channel, 761 761 recipient, 762 762 ); 763 763 let formatted_token = 764 - crate::auth::verification_token::format_token_for_display(&verification_token); 765 - if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification( 764 + tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 765 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 766 766 state.user_repo.as_ref(), 767 767 state.infra_repo.as_ref(), 768 768 user_id, ··· 781 781 } 782 782 } else if let Some(ref user_email) = email { 783 783 let token = 784 - crate::auth::verification_token::generate_migration_token(&did_for_commit, user_email); 785 - let formatted_token = crate::auth::verification_token::format_token_for_display(&token); 786 - if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification( 784 + tranquil_pds::auth::verification_token::generate_migration_token(&did_for_commit, user_email); 785 + let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token); 786 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification( 787 787 state.user_repo.as_ref(), 788 788 state.infra_repo.as_ref(), 789 789 user_id, ··· 797 797 } 798 798 } 799 799 800 - let access_meta = match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) 800 + let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) 801 801 { 802 802 Ok(m) => m, 803 803 Err(e) => { ··· 806 806 } 807 807 }; 808 808 let refresh_meta = 809 - match crate::auth::create_refresh_token_with_metadata(&did, &secret_key_bytes) { 809 + match tranquil_pds::auth::create_refresh_token_with_metadata(&did, &secret_key_bytes) { 810 810 Ok(m) => m, 811 811 Err(e) => { 812 812 error!("createAccount: Error creating refresh token: {:?}", e);
+37 -37
crates/tranquil-pds/src/api/identity/did.rs crates/tranquil-api/src/identity/did.rs
··· 1 - use crate::api::{ApiError, DidResponse, EmptyResponse}; 2 - use crate::auth::{Auth, NotTakendown}; 3 - use crate::plc::signing_key_to_did_key; 4 - use crate::rate_limit::{ 1 + use tranquil_pds::api::{ApiError, DidResponse, EmptyResponse}; 2 + use tranquil_pds::auth::{Auth, NotTakendown}; 3 + use tranquil_pds::plc::signing_key_to_did_key; 4 + use tranquil_pds::rate_limit::{ 5 5 HandleUpdateDailyLimit, HandleUpdateLimit, check_user_rate_limit_with_message, 6 6 }; 7 - use crate::state::AppState; 8 - use crate::types::Handle; 9 - use crate::util::get_header_str; 7 + use tranquil_pds::state::AppState; 8 + use tranquil_pds::types::Handle; 9 + use tranquil_pds::util::get_header_str; 10 10 use axum::{ 11 11 Json, 12 12 extract::{Path, Query, State}, ··· 42 42 if handle_str.is_empty() { 43 43 return ApiError::InvalidRequest("handle is required".into()).into_response(); 44 44 } 45 - let cache_key = crate::cache_keys::handle_key(handle_str); 45 + let cache_key = tranquil_pds::cache_keys::handle_key(handle_str); 46 46 if let Some(did) = state.cache.get(&cache_key).await { 47 47 return DidResponse::response(did).into_response(); 48 48 } ··· 61 61 .await; 62 62 DidResponse::response(row.did).into_response() 63 63 } 64 - Ok(None) => match crate::handle::resolve_handle(handle.as_str()).await { 64 + Ok(None) => match tranquil_pds::handle::resolve_handle(handle.as_str()).await { 65 65 Ok(did) => { 66 66 let _ = state 67 67 .cache ··· 148 148 "id": did, 149 149 "service": [{ 150 150 "id": "#atproto_pds", 151 - "type": crate::plc::ServiceType::Pds.as_str(), 151 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 152 152 "serviceEndpoint": format!("https://{}", hostname) 153 153 }] 154 154 })) ··· 158 158 async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) -> Response { 159 159 let encoded_handle = handle.replace(':', "%3A"); 160 160 let expected_did = format!("did:web:{}", encoded_handle); 161 - let expected_did_typed: crate::types::Did = match expected_did.parse() { 161 + let expected_did_typed: tranquil_pds::types::Did = match expected_did.parse() { 162 162 Ok(d) => d, 163 163 Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(), 164 164 }; ··· 216 216 })).collect::<Vec<_>>(), 217 217 "service": [{ 218 218 "id": "#atproto_pds", 219 - "type": crate::plc::ServiceType::Pds.as_str(), 219 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 220 220 "serviceEndpoint": service_endpoint 221 221 }] 222 222 })) ··· 229 229 Err(_) => return ApiError::InternalError(None).into_response(), 230 230 }; 231 231 let key_bytes: Vec<u8> = 232 - match crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) { 232 + match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) { 233 233 Ok(k) => k, 234 234 Err(_) => { 235 235 return ApiError::InternalError(None).into_response(); ··· 269 269 }], 270 270 "service": [{ 271 271 "id": "#atproto_pds", 272 - "type": crate::plc::ServiceType::Pds.as_str(), 272 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 273 273 "serviceEndpoint": service_endpoint 274 274 }] 275 275 })) ··· 351 351 })).collect::<Vec<_>>(), 352 352 "service": [{ 353 353 "id": "#atproto_pds", 354 - "type": crate::plc::ServiceType::Pds.as_str(), 354 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 355 355 "serviceEndpoint": service_endpoint 356 356 }] 357 357 })) ··· 364 364 Err(_) => return ApiError::InternalError(None).into_response(), 365 365 }; 366 366 let key_bytes: Vec<u8> = 367 - match crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) { 367 + match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) { 368 368 Ok(k) => k, 369 369 Err(_) => { 370 370 return ApiError::InternalError(None).into_response(); ··· 404 404 }], 405 405 "service": [{ 406 406 "id": "#atproto_pds", 407 - "type": crate::plc::ServiceType::Pds.as_str(), 407 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 408 408 "serviceEndpoint": service_endpoint 409 409 }] 410 410 })) ··· 480 480 let path = parts[3..].join("/"); 481 481 format!("{}://{}/{}/did.json", scheme, domain, path) 482 482 }; 483 - let client = crate::api::proxy_client::did_resolution_client(); 483 + let client = tranquil_pds::api::proxy_client::did_resolution_client(); 484 484 let resp = client 485 485 .get(&url) 486 486 .send() ··· 503 503 ))?; 504 504 let pds_endpoint = format!("https://{}", hostname); 505 505 let has_valid_service = services.iter().any(|s| { 506 - s["type"] == crate::plc::ServiceType::Pds.as_str() && s["serviceEndpoint"] == pds_endpoint 506 + s["type"] == tranquil_pds::plc::ServiceType::Pds.as_str() && s["serviceEndpoint"] == pds_endpoint 507 507 }); 508 508 if !has_valid_service { 509 509 return Err(DidWebVerifyError::PdsNotListed(pds_endpoint)); ··· 600 600 verification_methods: VerificationMethods { atproto: did_key }, 601 601 services: Services { 602 602 atproto_pds: AtprotoPds { 603 - service_type: crate::plc::ServiceType::Pds.as_str().to_string(), 603 + service_type: tranquil_pds::plc::ServiceType::Pds.as_str().to_string(), 604 604 endpoint: pds_endpoint, 605 605 }, 606 606 }, ··· 619 619 auth: Auth<NotTakendown>, 620 620 Json(input): Json<UpdateHandleInput>, 621 621 ) -> Result<Response, ApiError> { 622 - if let Err(e) = crate::auth::scope_check::check_identity_scope( 622 + if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope( 623 623 &auth.auth_source, 624 624 auth.scope.as_deref(), 625 - crate::oauth::scopes::IdentityAttr::Handle, 625 + tranquil_pds::oauth::scopes::IdentityAttr::Handle, 626 626 ) { 627 627 return Ok(e); 628 628 } ··· 672 672 "Handle segment cannot start or end with hyphen".into(), 673 673 ))); 674 674 } 675 - if crate::moderation::has_explicit_slur(&new_handle) { 675 + if tranquil_pds::moderation::has_explicit_slur(&new_handle) { 676 676 return Err(ApiError::InvalidHandle(Some( 677 677 "Inappropriate language in handle".into(), 678 678 ))); ··· 704 704 Err(_) => return Err(ApiError::InvalidHandle(None)), 705 705 }; 706 706 if let Err(e) = 707 - crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed)) 707 + tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed)) 708 708 .await 709 709 { 710 710 warn!("Failed to sequence identity event for handle update: {}", e); ··· 730 730 Err(_) => return Err(ApiError::InvalidHandle(None)), 731 731 }; 732 732 if let Err(e) = 733 - crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed)) 733 + tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed)) 734 734 .await 735 735 { 736 736 warn!("Failed to sequence identity event for handle update: {}", e); 737 737 } 738 738 return Ok(EmptyResponse::ok().into_response()); 739 739 } 740 - match crate::handle::verify_handle_ownership(&new_handle, &did).await { 740 + match tranquil_pds::handle::verify_handle_ownership(&new_handle, &did).await { 741 741 Ok(()) => {} 742 - Err(crate::handle::HandleResolutionError::NotFound) => { 742 + Err(tranquil_pds::handle::HandleResolutionError::NotFound) => { 743 743 return Err(ApiError::HandleNotAvailable(None)); 744 744 } 745 - Err(crate::handle::HandleResolutionError::DidMismatch { expected, actual }) => { 745 + Err(tranquil_pds::handle::HandleResolutionError::DidMismatch { expected, actual }) => { 746 746 return Err(ApiError::HandleNotAvailable(Some(format!( 747 747 "Handle points to different DID. Expected {}, got {}", 748 748 expected, actual ··· 781 781 if !current_handle.is_empty() { 782 782 let _ = state 783 783 .cache 784 - .delete(&crate::cache_keys::handle_key(&current_handle)) 784 + .delete(&tranquil_pds::cache_keys::handle_key(&current_handle)) 785 785 .await; 786 786 } 787 787 let _ = state 788 788 .cache 789 - .delete(&crate::cache_keys::handle_key(&handle)) 789 + .delete(&tranquil_pds::cache_keys::handle_key(&handle)) 790 790 .await; 791 791 if let Err(e) = 792 - crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed)).await 792 + tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed)).await 793 793 { 794 794 warn!("Failed to sequence identity event for handle update: {}", e); 795 795 } ··· 801 801 802 802 pub async fn update_plc_handle( 803 803 state: &AppState, 804 - did: &crate::types::Did, 804 + did: &tranquil_pds::types::Did, 805 805 new_handle: &Handle, 806 806 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { 807 807 if !did.as_str().starts_with("did:plc:") { ··· 811 811 Some(r) => r, 812 812 None => return Ok(()), 813 813 }; 814 - let key_bytes = crate::config::decrypt_key(&user_row.key_bytes, user_row.encryption_version)?; 814 + let key_bytes = tranquil_pds::config::decrypt_key(&user_row.key_bytes, user_row.encryption_version)?; 815 815 let signing_key = k256::ecdsa::SigningKey::from_slice(&key_bytes)?; 816 - let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 816 + let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 817 817 let last_op = plc_client.get_last_op(did).await?; 818 818 let new_also_known_as = vec![format!("at://{}", new_handle)]; 819 819 let update_op = 820 - crate::plc::create_update_op(&last_op, None, None, Some(new_also_known_as), None)?; 821 - let signed_op = crate::plc::sign_operation(&update_op, &signing_key)?; 820 + tranquil_pds::plc::create_update_op(&last_op, None, None, Some(new_also_known_as), None)?; 821 + let signed_op = tranquil_pds::plc::sign_operation(&update_op, &signing_key)?; 822 822 plc_client.send_operation(did, &signed_op).await?; 823 823 Ok(()) 824 824 } 825 825 826 826 pub async fn well_known_atproto_did(State(state): State<AppState>, headers: HeaderMap) -> Response { 827 - let host = match crate::util::get_header_str(&headers, http::header::HOST) { 827 + let host = match tranquil_pds::util::get_header_str(&headers, http::header::HOST) { 828 828 Some(h) => h, 829 829 None => return (StatusCode::BAD_REQUEST, "Missing host header").into_response(), 830 830 };
+4 -4
crates/tranquil-pds/src/api/identity/handle.rs crates/tranquil-api/src/identity/handle.rs
··· 1 - use crate::rate_limit::{HandleVerificationLimit, RateLimited}; 2 - use crate::types::{Did, Handle}; 1 + use tranquil_pds::rate_limit::{HandleVerificationLimit, RateLimited}; 2 + use tranquil_pds::types::{Did, Handle}; 3 3 use axum::{ 4 4 Json, 5 5 response::{IntoResponse, Response}, ··· 29 29 let handle_str = input.handle.as_str(); 30 30 let did_str = input.did.as_str(); 31 31 32 - let dns_mismatch = match crate::handle::resolve_handle_dns(handle_str).await { 32 + let dns_mismatch = match tranquil_pds::handle::resolve_handle_dns(handle_str).await { 33 33 Ok(did) if did == did_str => { 34 34 return Json(VerifyHandleOwnershipOutput { 35 35 verified: true, ··· 45 45 Err(_) => None, 46 46 }; 47 47 48 - match crate::handle::resolve_handle_http(handle_str).await { 48 + match tranquil_pds::handle::resolve_handle_http(handle_str).await { 49 49 Ok(did) if did == did_str => Json(VerifyHandleOwnershipOutput { 50 50 verified: true, 51 51 method: Some("http".to_string()),
crates/tranquil-pds/src/api/identity/mod.rs crates/tranquil-api/src/identity/mod.rs
crates/tranquil-pds/src/api/identity/plc/mod.rs crates/tranquil-api/src/identity/plc/mod.rs
+8 -8
crates/tranquil-pds/src/api/identity/plc/request.rs crates/tranquil-api/src/identity/plc/request.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{Auth, Permissive}; 4 - use crate::state::AppState; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{Auth, Permissive}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::{ 6 6 extract::State, 7 7 response::{IntoResponse, Response}, ··· 10 10 use tracing::{info, warn}; 11 11 12 12 fn generate_plc_token() -> String { 13 - crate::util::generate_token_code() 13 + tranquil_pds::util::generate_token_code() 14 14 } 15 15 16 16 pub async fn request_plc_operation_signature( 17 17 State(state): State<AppState>, 18 18 auth: Auth<Permissive>, 19 19 ) -> Result<Response, ApiError> { 20 - if let Err(e) = crate::auth::scope_check::check_identity_scope( 20 + if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope( 21 21 &auth.auth_source, 22 22 auth.scope.as_deref(), 23 - crate::oauth::scopes::IdentityAttr::Wildcard, 23 + tranquil_pds::oauth::scopes::IdentityAttr::Wildcard, 24 24 ) { 25 25 return Ok(e); 26 26 } ··· 41 41 .log_db_err("creating PLC token")?; 42 42 43 43 let hostname = &tranquil_config::get().server.hostname; 44 - if let Err(e) = crate::comms::comms_repo::enqueue_plc_operation( 44 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_plc_operation( 45 45 state.user_repo.as_ref(), 46 46 state.infra_repo.as_ref(), 47 47 user_id,
+9 -9
crates/tranquil-pds/src/api/identity/plc/sign.rs crates/tranquil-api/src/identity/plc/sign.rs
··· 1 - use crate::api::ApiError; 2 - use crate::api::error::DbResultExt; 3 - use crate::auth::{Auth, Permissive}; 4 - use crate::circuit_breaker::with_circuit_breaker; 5 - use crate::plc::{PlcClient, PlcError, PlcService, ServiceType, create_update_op, sign_operation}; 6 - use crate::state::AppState; 1 + use tranquil_pds::api::ApiError; 2 + use tranquil_pds::api::error::DbResultExt; 3 + use tranquil_pds::auth::{Auth, Permissive}; 4 + use tranquil_pds::circuit_breaker::with_circuit_breaker; 5 + use tranquil_pds::plc::{PlcClient, PlcError, PlcService, ServiceType, create_update_op, sign_operation}; 6 + use tranquil_pds::state::AppState; 7 7 use axum::{ 8 8 Json, 9 9 extract::State, ··· 44 44 auth: Auth<Permissive>, 45 45 Json(input): Json<SignPlcOperationInput>, 46 46 ) -> Result<Response, ApiError> { 47 - if let Err(e) = crate::auth::scope_check::check_identity_scope( 47 + if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope( 48 48 &auth.auth_source, 49 49 auth.scope.as_deref(), 50 - crate::oauth::scopes::IdentityAttr::Wildcard, 50 + tranquil_pds::oauth::scopes::IdentityAttr::Wildcard, 51 51 ) { 52 52 return Ok(e); 53 53 } ··· 86 86 .log_db_err("fetching user key")? 87 87 .ok_or_else(|| ApiError::InternalError(Some("User signing key not found".into())))?; 88 88 89 - let key_bytes = crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version) 89 + let key_bytes = tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version) 90 90 .map_err(|e| { 91 91 error!("Failed to decrypt user key: {}", e); 92 92 ApiError::InternalError(None)
+13 -13
crates/tranquil-pds/src/api/identity/plc/submit.rs crates/tranquil-api/src/identity/plc/submit.rs
··· 1 - use crate::api::error::DbResultExt; 2 - use crate::api::{ApiError, EmptyResponse}; 3 - use crate::auth::{Auth, Permissive}; 4 - use crate::circuit_breaker::with_circuit_breaker; 5 - use crate::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation}; 6 - use crate::state::AppState; 1 + use tranquil_pds::api::error::DbResultExt; 2 + use tranquil_pds::api::{ApiError, EmptyResponse}; 3 + use tranquil_pds::auth::{Auth, Permissive}; 4 + use tranquil_pds::circuit_breaker::with_circuit_breaker; 5 + use tranquil_pds::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation}; 6 + use tranquil_pds::state::AppState; 7 7 use axum::{ 8 8 Json, 9 9 extract::State, ··· 24 24 auth: Auth<Permissive>, 25 25 Json(input): Json<SubmitPlcOperationInput>, 26 26 ) -> Result<Response, ApiError> { 27 - if let Err(e) = crate::auth::scope_check::check_identity_scope( 27 + if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope( 28 28 &auth.auth_source, 29 29 auth.scope.as_deref(), 30 - crate::oauth::scopes::IdentityAttr::Wildcard, 30 + tranquil_pds::oauth::scopes::IdentityAttr::Wildcard, 31 31 ) { 32 32 return Ok(e); 33 33 } ··· 57 57 .log_db_err("fetching user key")? 58 58 .ok_or_else(|| ApiError::InternalError(Some("User signing key not found".into())))?; 59 59 60 - let key_bytes = crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version) 60 + let key_bytes = tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version) 61 61 .map_err(|e| { 62 62 error!("Failed to decrypt user key: {}", e); 63 63 ApiError::InternalError(None) ··· 89 89 { 90 90 let service_type = pds.get("type").and_then(|v| v.as_str()); 91 91 let endpoint = pds.get("endpoint").and_then(|v| v.as_str()); 92 - if service_type != Some(crate::plc::ServiceType::Pds.as_str()) { 92 + if service_type != Some(tranquil_pds::plc::ServiceType::Pds.as_str()) { 93 93 return Err(ApiError::InvalidRequest( 94 94 "Incorrect type on atproto_pds service".into(), 95 95 )); ··· 147 147 } 148 148 let _ = state 149 149 .cache 150 - .delete(&crate::cache_keys::handle_key(&user.handle)) 150 + .delete(&tranquil_pds::cache_keys::handle_key(&user.handle)) 151 151 .await; 152 152 let _ = state 153 153 .cache 154 - .delete(&crate::cache_keys::plc_doc_key(did)) 154 + .delete(&tranquil_pds::cache_keys::plc_doc_key(did)) 155 155 .await; 156 156 let _ = state 157 157 .cache 158 - .delete(&crate::cache_keys::plc_data_key(did)) 158 + .delete(&tranquil_pds::cache_keys::plc_data_key(did)) 159 159 .await; 160 160 if state.did_resolver.refresh_did(did).await.is_none() { 161 161 warn!(did = %did, "Failed to refresh DID cache after PLC update");
+273
crates/tranquil-api/src/lib.rs
··· 1 + pub mod actor; 2 + pub mod admin; 3 + pub mod age_assurance; 4 + pub mod backup; 5 + pub mod delegation; 6 + pub mod discord_webhook; 7 + pub mod identity; 8 + pub mod moderation; 9 + pub mod notification_prefs; 10 + pub mod repo; 11 + pub mod server; 12 + pub mod telegram_webhook; 13 + pub mod temp; 14 + pub mod verification; 15 + 16 + use tranquil_pds::state::AppState; 17 + 18 + pub fn api_routes() -> axum::Router<AppState> { 19 + use axum::routing::{get, post}; 20 + 21 + axum::Router::new() 22 + .route("/_health", get(server::health)) 23 + .route( 24 + "/com.atproto.server.describeServer", 25 + get(server::describe_server), 26 + ) 27 + .route( 28 + "/com.atproto.server.createAccount", 29 + post(identity::create_account), 30 + ) 31 + .route( 32 + "/com.atproto.server.createSession", 33 + post(server::create_session), 34 + ) 35 + .route( 36 + "/com.atproto.server.getSession", 37 + get(server::get_session), 38 + ) 39 + .route("/_account.listSessions", get(server::list_sessions)) 40 + .route("/_account.revokeSession", post(server::revoke_session)) 41 + .route( 42 + "/_account.revokeAllSessions", 43 + post(server::revoke_all_sessions), 44 + ) 45 + .route( 46 + "/com.atproto.server.deleteSession", 47 + post(server::delete_session), 48 + ) 49 + .route( 50 + "/com.atproto.server.refreshSession", 51 + post(server::refresh_session), 52 + ) 53 + .route( 54 + "/com.atproto.server.confirmSignup", 55 + post(server::confirm_signup), 56 + ) 57 + .route( 58 + "/com.atproto.server.resendVerification", 59 + post(server::resend_verification), 60 + ) 61 + .route( 62 + "/com.atproto.server.getServiceAuth", 63 + get(server::get_service_auth), 64 + ) 65 + .route( 66 + "/com.atproto.identity.resolveHandle", 67 + get(identity::resolve_handle), 68 + ) 69 + .route( 70 + "/com.atproto.repo.createRecord", 71 + post(repo::create_record), 72 + ) 73 + .route("/com.atproto.repo.putRecord", post(repo::put_record)) 74 + .route("/com.atproto.repo.getRecord", get(repo::get_record)) 75 + .route( 76 + "/com.atproto.repo.deleteRecord", 77 + post(repo::delete_record), 78 + ) 79 + .route( 80 + "/com.atproto.repo.listRecords", 81 + get(repo::list_records), 82 + ) 83 + .route( 84 + "/com.atproto.repo.describeRepo", 85 + get(repo::describe_repo), 86 + ) 87 + .route("/com.atproto.repo.uploadBlob", post(repo::upload_blob)) 88 + .route( 89 + "/com.atproto.repo.applyWrites", 90 + post(repo::apply_writes), 91 + ) 92 + .route( 93 + "/com.atproto.server.checkAccountStatus", 94 + get(server::check_account_status), 95 + ) 96 + .route( 97 + "/com.atproto.identity.getRecommendedDidCredentials", 98 + get(identity::get_recommended_did_credentials), 99 + ) 100 + .route( 101 + "/com.atproto.repo.listMissingBlobs", 102 + get(repo::list_missing_blobs), 103 + ) 104 + .route( 105 + "/com.atproto.moderation.createReport", 106 + post(moderation::create_report), 107 + ) 108 + .route( 109 + "/com.atproto.admin.getAccountInfo", 110 + get(admin::get_account_info), 111 + ) 112 + .route( 113 + "/com.atproto.admin.getAccountInfos", 114 + get(admin::get_account_infos), 115 + ) 116 + .route( 117 + "/com.atproto.admin.searchAccounts", 118 + get(admin::search_accounts), 119 + ) 120 + .route( 121 + "/com.atproto.server.activateAccount", 122 + post(server::activate_account), 123 + ) 124 + .route( 125 + "/com.atproto.server.deactivateAccount", 126 + post(server::deactivate_account), 127 + ) 128 + .route( 129 + "/com.atproto.server.requestAccountDelete", 130 + post(server::request_account_delete), 131 + ) 132 + .route( 133 + "/com.atproto.server.deleteAccount", 134 + post(server::delete_account), 135 + ) 136 + .route( 137 + "/com.atproto.server.requestPasswordReset", 138 + post(server::request_password_reset), 139 + ) 140 + .route( 141 + "/com.atproto.server.resetPassword", 142 + post(server::reset_password), 143 + ) 144 + .route("/_account.changePassword", post(server::change_password)) 145 + .route("/_account.removePassword", post(server::remove_password)) 146 + .route("/_account.setPassword", post(server::set_password)) 147 + .route("/_account.getPasswordStatus", get(server::get_password_status)) 148 + .route("/_account.getReauthStatus", get(server::get_reauth_status)) 149 + .route("/_account.reauthPassword", post(server::reauth_password)) 150 + .route("/_account.reauthTotp", post(server::reauth_totp)) 151 + .route("/_account.reauthPasskeyStart", post(server::reauth_passkey_start)) 152 + .route("/_account.reauthPasskeyFinish", post(server::reauth_passkey_finish)) 153 + .route("/_account.getLegacyLoginPreference", get(server::get_legacy_login_preference)) 154 + .route("/_account.updateLegacyLoginPreference", post(server::update_legacy_login_preference)) 155 + .route("/_account.updateLocale", post(server::update_locale)) 156 + .route("/_account.listTrustedDevices", get(server::list_trusted_devices)) 157 + .route("/_account.revokeTrustedDevice", post(server::revoke_trusted_device)) 158 + .route("/_account.updateTrustedDevice", post(server::update_trusted_device)) 159 + .route("/_account.createPasskeyAccount", post(server::create_passkey_account)) 160 + .route("/_account.startPasskeyRegistrationForSetup", post(server::start_passkey_registration_for_setup)) 161 + .route("/_account.completePasskeySetup", post(server::complete_passkey_setup)) 162 + .route("/_account.requestPasskeyRecovery", post(server::request_passkey_recovery)) 163 + .route("/_account.recoverPasskeyAccount", post(server::recover_passkey_account)) 164 + .route("/_account.updateDidDocument", post(server::update_did_document)) 165 + .route("/_account.getDidDocument", get(server::get_did_document)) 166 + .route("/com.atproto.server.requestEmailUpdate", post(server::request_email_update)) 167 + .route("/_checkEmailVerified", post(server::check_email_verified)) 168 + .route("/_checkChannelVerified", post(server::check_channel_verified)) 169 + .route("/com.atproto.server.confirmEmail", post(server::confirm_email)) 170 + .route("/com.atproto.server.updateEmail", post(server::update_email)) 171 + .route("/_account.authorizeEmailUpdate", get(server::authorize_email_update)) 172 + .route("/_account.checkEmailUpdateStatus", get(server::check_email_update_status)) 173 + .route("/_account.checkEmailInUse", post(server::check_email_in_use)) 174 + .route("/_account.checkCommsChannelInUse", post(server::check_comms_channel_in_use)) 175 + .route("/com.atproto.server.reserveSigningKey", post(server::reserve_signing_key)) 176 + .route("/com.atproto.server.verifyMigrationEmail", post(server::verify_migration_email)) 177 + .route("/com.atproto.server.resendMigrationVerification", post(server::resend_migration_verification)) 178 + .route("/com.atproto.identity.updateHandle", post(identity::update_handle)) 179 + .route("/com.atproto.identity.requestPlcOperationSignature", post(identity::request_plc_operation_signature)) 180 + .route("/com.atproto.identity.signPlcOperation", post(identity::sign_plc_operation)) 181 + .route("/com.atproto.identity.submitPlcOperation", post(identity::submit_plc_operation)) 182 + .route("/_identity.verifyHandleOwnership", post(identity::verify_handle_ownership)) 183 + .route("/com.atproto.repo.importRepo", post(repo::import_repo)) 184 + .route("/com.atproto.admin.deleteAccount", post(admin::delete_account)) 185 + .route("/com.atproto.admin.updateAccountEmail", post(admin::update_account_email)) 186 + .route("/com.atproto.admin.updateAccountHandle", post(admin::update_account_handle)) 187 + .route("/com.atproto.admin.updateAccountPassword", post(admin::update_account_password)) 188 + .route("/com.atproto.server.listAppPasswords", get(server::list_app_passwords)) 189 + .route("/com.atproto.server.createAppPassword", post(server::create_app_password)) 190 + .route("/com.atproto.server.revokeAppPassword", post(server::revoke_app_password)) 191 + .route("/com.atproto.server.createInviteCode", post(server::create_invite_code)) 192 + .route("/com.atproto.server.createInviteCodes", post(server::create_invite_codes)) 193 + .route("/com.atproto.server.getAccountInviteCodes", get(server::get_account_invite_codes)) 194 + .route("/com.atproto.server.createTotpSecret", post(server::create_totp_secret)) 195 + .route("/com.atproto.server.enableTotp", post(server::enable_totp)) 196 + .route("/com.atproto.server.disableTotp", post(server::disable_totp)) 197 + .route("/com.atproto.server.getTotpStatus", get(server::get_totp_status)) 198 + .route("/com.atproto.server.regenerateBackupCodes", post(server::regenerate_backup_codes)) 199 + .route("/com.atproto.server.startPasskeyRegistration", post(server::start_passkey_registration)) 200 + .route("/com.atproto.server.finishPasskeyRegistration", post(server::finish_passkey_registration)) 201 + .route("/com.atproto.server.listPasskeys", get(server::list_passkeys)) 202 + .route("/com.atproto.server.deletePasskey", post(server::delete_passkey)) 203 + .route("/com.atproto.server.updatePasskey", post(server::update_passkey)) 204 + .route("/com.atproto.admin.getInviteCodes", get(admin::get_invite_codes)) 205 + .route("/_admin.getServerStats", get(admin::get_server_stats)) 206 + .route("/_server.getConfig", get(admin::get_server_config)) 207 + .route("/_admin.updateServerConfig", post(admin::update_server_config)) 208 + .route("/com.atproto.admin.disableAccountInvites", post(admin::disable_account_invites)) 209 + .route("/com.atproto.admin.enableAccountInvites", post(admin::enable_account_invites)) 210 + .route("/com.atproto.admin.disableInviteCodes", post(admin::disable_invite_codes)) 211 + .route("/com.atproto.admin.getSubjectStatus", get(admin::get_subject_status)) 212 + .route("/com.atproto.admin.updateSubjectStatus", post(admin::update_subject_status)) 213 + .route("/com.atproto.admin.sendEmail", post(admin::send_email)) 214 + .route("/app.bsky.actor.getPreferences", get(actor::get_preferences)) 215 + .route("/app.bsky.actor.putPreferences", post(actor::put_preferences)) 216 + .route("/com.atproto.temp.checkSignupQueue", get(temp::check_signup_queue)) 217 + .route("/com.atproto.temp.dereferenceScope", post(temp::dereference_scope)) 218 + .route("/_account.getNotificationPrefs", get(notification_prefs::get_notification_prefs)) 219 + .route("/_account.updateNotificationPrefs", post(notification_prefs::update_notification_prefs)) 220 + .route("/_account.getNotificationHistory", get(notification_prefs::get_notification_history)) 221 + .route("/_account.confirmChannelVerification", post(verification::confirm_channel_verification)) 222 + .route("/_account.verifyToken", post(server::verify_token)) 223 + .route("/_delegation.listControllers", get(delegation::list_controllers)) 224 + .route("/_delegation.addController", post(delegation::add_controller)) 225 + .route("/_delegation.removeController", post(delegation::remove_controller)) 226 + .route("/_delegation.updateControllerScopes", post(delegation::update_controller_scopes)) 227 + .route("/_delegation.listControlledAccounts", get(delegation::list_controlled_accounts)) 228 + .route("/_delegation.getAuditLog", get(delegation::get_audit_log)) 229 + .route("/_delegation.getScopePresets", get(delegation::get_scope_presets)) 230 + .route("/_delegation.createDelegatedAccount", post(delegation::create_delegated_account)) 231 + .route("/_backup.listBackups", get(backup::list_backups)) 232 + .route("/_backup.getBackup", get(backup::get_backup)) 233 + .route("/_backup.createBackup", post(backup::create_backup)) 234 + .route("/_backup.deleteBackup", post(backup::delete_backup)) 235 + .route("/_backup.setEnabled", post(backup::set_backup_enabled)) 236 + .route("/_backup.exportBlobs", get(backup::export_blobs)) 237 + .route("/app.bsky.ageassurance.getState", get(age_assurance::get_state)) 238 + .route("/app.bsky.unspecced.getAgeAssuranceState", get(age_assurance::get_age_assurance_state)) 239 + } 240 + 241 + pub fn well_known_api_routes() -> axum::Router<AppState> { 242 + use axum::routing::get; 243 + 244 + axum::Router::new() 245 + .route("/did.json", get(identity::well_known_did)) 246 + .route("/atproto-did", get(identity::well_known_atproto_did)) 247 + } 248 + 249 + pub fn webhook_routes() -> axum::Router<AppState> { 250 + use axum::{extract::DefaultBodyLimit, routing::post}; 251 + 252 + axum::Router::new() 253 + .route( 254 + "/webhook/telegram", 255 + post(telegram_webhook::handle_telegram_webhook) 256 + .layer(DefaultBodyLimit::max(64 * 1024)), 257 + ) 258 + .route( 259 + "/webhook/discord", 260 + post(discord_webhook::handle_discord_webhook) 261 + .layer(DefaultBodyLimit::max(64 * 1024)), 262 + ) 263 + } 264 + 265 + pub fn misc_routes() -> axum::Router<AppState> { 266 + use axum::routing::get; 267 + 268 + axum::Router::new() 269 + .route("/health", get(server::health)) 270 + .route("/robots.txt", get(server::robots_txt)) 271 + .route("/favicon.ico", get(server::get_logo)) 272 + .route("/u/{handle}/did.json", get(identity::user_did_doc)) 273 + }
+8 -8
crates/tranquil-pds/src/api/moderation/mod.rs crates/tranquil-api/src/moderation/mod.rs
··· 1 - use crate::api::ApiError; 2 - use crate::api::proxy_client::{is_ssrf_safe, proxy_client}; 3 - use crate::auth::{AnyUser, Auth}; 4 - use crate::state::AppState; 1 + use tranquil_pds::api::ApiError; 2 + use tranquil_pds::api::proxy_client::{is_ssrf_safe, proxy_client}; 3 + use tranquil_pds::auth::{AnyUser, Auth}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::{ 6 6 Json, 7 7 extract::State, ··· 94 94 95 95 async fn proxy_to_report_service( 96 96 state: &AppState, 97 - auth_user: &crate::auth::AuthenticatedUser, 97 + auth_user: &tranquil_pds::auth::AuthenticatedUser, 98 98 service_url: &str, 99 99 service_did: &str, 100 100 input: &CreateReportInput, ··· 109 109 Some(kb) => kb.clone(), 110 110 None => match state.user_repo.get_with_key_by_did(&auth_user.did).await { 111 111 Ok(Some(user_with_key)) => { 112 - match crate::config::decrypt_key( 112 + match tranquil_pds::config::decrypt_key( 113 113 &user_with_key.key_bytes, 114 114 user_with_key.encryption_version, 115 115 ) { ··· 135 135 }, 136 136 }; 137 137 138 - let service_token = match crate::auth::create_service_token( 138 + let service_token = match tranquil_pds::auth::create_service_token( 139 139 &auth_user.did, 140 140 service_did, 141 141 "com.atproto.moderation.createReport", ··· 211 211 212 212 async fn create_report_locally( 213 213 state: &AppState, 214 - did: &crate::types::Did, 214 + did: &tranquil_pds::types::Did, 215 215 is_takendown: bool, 216 216 input: CreateReportInput, 217 217 ) -> Response {
+13 -13
crates/tranquil-pds/src/api/notification_prefs.rs crates/tranquil-api/src/notification_prefs.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::auth::{Active, Auth}; 3 - use crate::state::AppState; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::auth::{Active, Auth}; 3 + use tranquil_pds::state::AppState; 4 4 use axum::{ 5 5 Json, 6 6 extract::State, ··· 142 142 handle: Option<&str>, 143 143 ) -> Result<String, ApiError> { 144 144 let token = 145 - crate::auth::verification_token::generate_channel_update_token(did, channel, identifier); 146 - let formatted_token = crate::auth::verification_token::format_token_for_display(&token); 145 + tranquil_pds::auth::verification_token::generate_channel_update_token(did, channel, identifier); 146 + let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token); 147 147 148 148 match channel { 149 149 CommsChannel::Email => { 150 150 let hostname = &tranquil_config::get().server.hostname; 151 151 let handle_str = handle.unwrap_or("user"); 152 - crate::comms::comms_repo::enqueue_email_update( 152 + tranquil_pds::comms::comms_repo::enqueue_email_update( 153 153 state.infra_repo.as_ref(), 154 154 user_id, 155 155 identifier, ··· 183 183 .as_ref() 184 184 .and_then(|p| p.preferred_locale.as_deref()) 185 185 .unwrap_or("en"); 186 - let strings = crate::comms::get_strings(locale); 187 - let body = crate::comms::format_message( 186 + let strings = tranquil_pds::comms::get_strings(locale); 187 + let body = tranquil_pds::comms::format_message( 188 188 strings.channel_verification_body, 189 189 &[("code", &formatted_token), ("verify_link", &verify_link)], 190 190 ); 191 - let subject = crate::comms::format_message( 191 + let subject = tranquil_pds::comms::format_message( 192 192 strings.channel_verification_subject, 193 193 &[("hostname", hostname)], 194 194 ); ··· 277 277 return Err(ApiError::InvalidRequest("Email cannot be empty".into())); 278 278 } 279 279 280 - if !crate::api::validation::is_valid_email(&email_clean) { 280 + if !tranquil_pds::api::validation::is_valid_email(&email_clean) { 281 281 return Err(ApiError::InvalidEmail); 282 282 } 283 283 ··· 310 310 .await 311 311 .map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?; 312 312 info!(did = %auth.did, "Cleared Discord"); 313 - } else if !crate::api::validation::is_valid_discord_username(&discord_clean) { 313 + } else if !tranquil_pds::api::validation::is_valid_discord_username(&discord_clean) { 314 314 return Err(ApiError::InvalidRequest( 315 315 "Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)" 316 316 .into(), ··· 340 340 .await 341 341 .map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?; 342 342 info!(did = %auth.did, "Cleared Telegram username"); 343 - } else if !crate::api::validation::is_valid_telegram_username(telegram_clean) { 343 + } else if !tranquil_pds::api::validation::is_valid_telegram_username(telegram_clean) { 344 344 return Err(ApiError::InvalidRequest( 345 345 "Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore" 346 346 .into(), ··· 370 370 .await 371 371 .map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?; 372 372 info!(did = %auth.did, "Cleared Signal username"); 373 - } else if !crate::comms::is_valid_signal_username(&signal_clean) { 373 + } else if !tranquil_pds::comms::is_valid_signal_username(&signal_clean) { 374 374 return Err(ApiError::InvalidRequest( 375 375 "Invalid Signal username. Must be 3-32 characters followed by .XX (e.g. username.01)" 376 376 .into(),
+6 -6
crates/tranquil-pds/src/api/repo/blob.rs crates/tranquil-api/src/repo/blob.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::auth::{Auth, AuthAny, NotTakendown, Permissive, VerifyScope}; 3 - use crate::delegation::DelegationActionType; 4 - use crate::state::AppState; 5 - use crate::types::{CidLink, Did}; 6 - use crate::util::get_header_str; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::auth::{Auth, AuthAny, NotTakendown, Permissive, VerifyScope}; 3 + use tranquil_pds::delegation::DelegationActionType; 4 + use tranquil_pds::state::AppState; 5 + use tranquil_pds::types::{CidLink, Did}; 6 + use tranquil_pds::util::get_header_str; 7 7 use axum::body::Body; 8 8 use axum::{ 9 9 Json,
+16 -16
crates/tranquil-pds/src/api/repo/import.rs crates/tranquil-api/src/repo/import.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::api::repo::record::create_signed_commit; 4 - use crate::auth::{Auth, NotTakendown}; 5 - use crate::state::AppState; 6 - use crate::sync::import::{ImportError, apply_import, parse_car}; 7 - use crate::sync::verify::CarVerifier; 8 - use crate::types::Did; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::repo_ops::create_signed_commit; 4 + use tranquil_pds::auth::{Auth, NotTakendown}; 5 + use tranquil_pds::state::AppState; 6 + use tranquil_pds::sync::import::{ImportError, apply_import, parse_car}; 7 + use tranquil_pds::sync::verify::CarVerifier; 8 + use tranquil_pds::types::Did; 9 9 use axum::{ 10 10 body::Bytes, 11 11 extract::State, ··· 121 121 verified.rev, verified.data_cid 122 122 ); 123 123 } 124 - Err(crate::sync::verify::VerifyError::DidMismatch { 124 + Err(tranquil_pds::sync::verify::VerifyError::DidMismatch { 125 125 commit_did, 126 126 expected_did, 127 127 }) => { ··· 130 130 commit_did, expected_did 131 131 ))); 132 132 } 133 - Err(crate::sync::verify::VerifyError::MstValidationFailed(msg)) => { 133 + Err(tranquil_pds::sync::verify::VerifyError::MstValidationFailed(msg)) => { 134 134 return Err(ApiError::InvalidRequest(format!( 135 135 "MST validation failed: {}", 136 136 msg ··· 154 154 verified.rev, verified.data_cid 155 155 ); 156 156 } 157 - Err(crate::sync::verify::VerifyError::DidMismatch { 157 + Err(tranquil_pds::sync::verify::VerifyError::DidMismatch { 158 158 commit_did, 159 159 expected_did, 160 160 }) => { ··· 163 163 commit_did, expected_did 164 164 ))); 165 165 } 166 - Err(crate::sync::verify::VerifyError::InvalidSignature) => { 166 + Err(tranquil_pds::sync::verify::VerifyError::InvalidSignature) => { 167 167 return Err(ApiError::InvalidRequest( 168 168 "CAR file commit signature verification failed".into(), 169 169 )); 170 170 } 171 - Err(crate::sync::verify::VerifyError::DidResolutionFailed(msg)) => { 171 + Err(tranquil_pds::sync::verify::VerifyError::DidResolutionFailed(msg)) => { 172 172 warn!("DID resolution failed during import verification: {}", msg); 173 173 return Err(ApiError::InvalidRequest(format!( 174 174 "Failed to verify DID: {}", 175 175 msg 176 176 ))); 177 177 } 178 - Err(crate::sync::verify::VerifyError::NoSigningKey) => { 178 + Err(tranquil_pds::sync::verify::VerifyError::NoSigningKey) => { 179 179 return Err(ApiError::InvalidRequest( 180 180 "DID document does not contain a signing key".into(), 181 181 )); 182 182 } 183 - Err(crate::sync::verify::VerifyError::MstValidationFailed(msg)) => { 183 + Err(tranquil_pds::sync::verify::VerifyError::MstValidationFailed(msg)) => { 184 184 return Err(ApiError::InvalidRequest(format!( 185 185 "MST validation failed: {}", 186 186 msg ··· 264 264 ApiError::InternalError(Some("Signing key not found".into())) 265 265 })?; 266 266 let key_bytes = 267 - crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version) 267 + tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version) 268 268 .map_err(|e| { 269 269 error!("Failed to decrypt signing key: {}", e); 270 270 ApiError::InternalError(None)
+5 -5
crates/tranquil-pds/src/api/repo/meta.rs crates/tranquil-api/src/repo/meta.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::state::AppState; 3 - use crate::types::AtIdentifier; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::state::AppState; 3 + use tranquil_pds::types::AtIdentifier; 4 4 use axum::{ 5 5 Json, 6 6 extract::{Query, State}, ··· 20 20 ) -> Response { 21 21 let hostname_for_handles = tranquil_config::get().server.hostname_without_port(); 22 22 let user_row = if input.repo.is_did() { 23 - let did: crate::types::Did = match input.repo.as_str().parse() { 23 + let did: tranquil_pds::types::Did = match input.repo.as_str().parse() { 24 24 Ok(d) => d, 25 25 Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(), 26 26 }; ··· 36 36 } else { 37 37 repo_str.to_string() 38 38 }; 39 - let handle: crate::types::Handle = match handle_str.parse() { 39 + let handle: tranquil_pds::types::Handle = match handle_str.parse() { 40 40 Ok(h) => h, 41 41 Err(_) => { 42 42 return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
crates/tranquil-pds/src/api/repo/mod.rs crates/tranquil-api/src/repo/mod.rs
+11 -11
crates/tranquil-pds/src/api/repo/record/batch.rs crates/tranquil-api/src/repo/record/batch.rs
··· 1 1 use super::validation::validate_record_with_status; 2 2 use super::validation_mode::{ValidationMode, deserialize_validation_mode}; 3 - use crate::api::error::ApiError; 4 - use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids}; 5 - use crate::auth::{ 3 + use tranquil_pds::api::error::ApiError; 4 + use crate::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids}; 5 + use tranquil_pds::auth::{ 6 6 Active, Auth, WriteOpKind, require_not_migrated, require_verified_or_delegated, 7 7 verify_batch_write_scopes, 8 8 }; 9 - use crate::cid_types::CommitCid; 10 - use crate::delegation::DelegationActionType; 11 - use crate::repo::tracking::TrackingBlockStore; 12 - use crate::state::AppState; 13 - use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey}; 14 - use crate::validation::ValidationStatus; 9 + use tranquil_pds::cid_types::CommitCid; 10 + use tranquil_pds::delegation::DelegationActionType; 11 + use tranquil_pds::repo::tracking::TrackingBlockStore; 12 + use tranquil_pds::state::AppState; 13 + use tranquil_pds::types::{AtIdentifier, AtUri, Did, Nsid, Rkey}; 14 + use tranquil_pds::validation::ValidationStatus; 15 15 use axum::{ 16 16 Json, 17 17 extract::State, ··· 74 74 }; 75 75 all_blob_cids.extend(extract_blob_cids(value)); 76 76 let rkey = rkey.clone().unwrap_or_else(Rkey::generate); 77 - let record_ipld = crate::util::json_to_ipld(value); 77 + let record_ipld = tranquil_pds::util::json_to_ipld(value); 78 78 let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| { 79 79 ApiError::InvalidRecord("Failed to serialize record".into()).into_response() 80 80 })?; ··· 126 126 } 127 127 }; 128 128 all_blob_cids.extend(extract_blob_cids(value)); 129 - let record_ipld = crate::util::json_to_ipld(value); 129 + let record_ipld = tranquil_pds::util::json_to_ipld(value); 130 130 let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| { 131 131 ApiError::InvalidRecord("Failed to serialize record".into()).into_response() 132 132 })?;
+11 -11
crates/tranquil-pds/src/api/repo/record/delete.rs crates/tranquil-api/src/repo/record/delete.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::api::repo::record::utils::{ 1 + use tranquil_pds::api::error::ApiError; 2 + use crate::repo::record::utils::{ 3 3 CommitError, CommitParams, RecordOp, commit_and_log, get_current_root_cid, 4 4 }; 5 - use crate::api::repo::record::write::{CommitInfo, prepare_repo_write}; 6 - use crate::auth::{Active, Auth, VerifyScope}; 7 - use crate::cid_types::CommitCid; 8 - use crate::delegation::DelegationActionType; 9 - use crate::repo::tracking::TrackingBlockStore; 10 - use crate::state::AppState; 11 - use crate::types::{AtIdentifier, AtUri, Nsid, Rkey}; 5 + use crate::repo::record::write::{CommitInfo, prepare_repo_write}; 6 + use tranquil_pds::auth::{Active, Auth, VerifyScope}; 7 + use tranquil_pds::cid_types::CommitCid; 8 + use tranquil_pds::delegation::DelegationActionType; 9 + use tranquil_pds::repo::tracking::TrackingBlockStore; 10 + use tranquil_pds::state::AppState; 11 + use tranquil_pds::types::{AtIdentifier, AtUri, Nsid, Rkey}; 12 12 use axum::{ 13 13 Json, 14 14 extract::State, ··· 45 45 State(state): State<AppState>, 46 46 auth: Auth<Active>, 47 47 Json(input): Json<DeleteRecordInput>, 48 - ) -> Result<Response, crate::api::error::ApiError> { 48 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 49 49 let scope_proof = match auth.verify_repo_delete(&input.collection) { 50 50 Ok(proof) => proof, 51 51 Err(e) => return Ok(e.into_response()), ··· 229 229 .into_response()) 230 230 } 231 231 232 - use crate::types::Did; 232 + use tranquil_pds::types::Did; 233 233 use uuid::Uuid; 234 234 235 235 pub async fn delete_record_internal(
crates/tranquil-pds/src/api/repo/record/mod.rs crates/tranquil-api/src/repo/record/mod.rs
crates/tranquil-pds/src/api/repo/record/pagination.rs crates/tranquil-api/src/repo/record/pagination.rs
+8 -8
crates/tranquil-pds/src/api/repo/record/read.rs crates/tranquil-api/src/repo/record/read.rs
··· 1 1 use super::pagination::{PaginationDirection, deserialize_pagination_direction}; 2 - use crate::api::error::ApiError; 3 - use crate::state::AppState; 4 - use crate::types::{AtIdentifier, Nsid, Rkey}; 2 + use tranquil_pds::api::error::ApiError; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::types::{AtIdentifier, Nsid, Rkey}; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, State}, ··· 61 61 ) -> Response { 62 62 let hostname_for_handles = tranquil_config::get().server.hostname_without_port(); 63 63 let user_id_opt = if input.repo.is_did() { 64 - let did: crate::types::Did = match input.repo.as_str().parse() { 64 + let did: tranquil_pds::types::Did = match input.repo.as_str().parse() { 65 65 Ok(d) => d, 66 66 Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(), 67 67 }; ··· 73 73 } else { 74 74 repo_str.to_string() 75 75 }; 76 - let handle: crate::types::Handle = match handle_str.parse() { 76 + let handle: tranquil_pds::types::Handle = match handle_str.parse() { 77 77 Ok(h) => h, 78 78 Err(_) => { 79 79 return ApiError::InvalidRequest("Invalid handle format".into()).into_response(); ··· 160 160 ) -> Response { 161 161 let hostname_for_handles = tranquil_config::get().server.hostname_without_port(); 162 162 let user_id_opt = if input.repo.is_did() { 163 - let did: crate::types::Did = match input.repo.as_str().parse() { 163 + let did: tranquil_pds::types::Did = match input.repo.as_str().parse() { 164 164 Ok(d) => d, 165 165 Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(), 166 166 }; ··· 172 172 } else { 173 173 repo_str.to_string() 174 174 }; 175 - let handle: crate::types::Handle = match handle_str.parse() { 175 + let handle: tranquil_pds::types::Handle = match handle_str.parse() { 176 176 Ok(h) => h, 177 177 Err(_) => { 178 178 return ApiError::InvalidRequest("Invalid handle format".into()).into_response(); ··· 198 198 let cursor_rkey = input 199 199 .cursor 200 200 .as_ref() 201 - .and_then(|c| c.parse::<crate::types::Rkey>().ok()); 201 + .and_then(|c| c.parse::<tranquil_pds::types::Rkey>().ok()); 202 202 let rows = match state 203 203 .repo_repo 204 204 .list_records(
+1
crates/tranquil-api/src/repo/record/utils.rs
··· 1 + pub use tranquil_pds::repo_ops::*;
+3 -3
crates/tranquil-pds/src/api/repo/record/validation.rs crates/tranquil-api/src/repo/record/validation.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::types::{Nsid, Rkey}; 3 - use crate::validation::{RecordValidator, ValidationError, ValidationStatus}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::types::{Nsid, Rkey}; 3 + use tranquil_pds::validation::{RecordValidator, ValidationError, ValidationStatus}; 4 4 use axum::response::Response; 5 5 6 6 pub async fn validate_record_with_status(
crates/tranquil-pds/src/api/repo/record/validation_mode.rs crates/tranquil-api/src/repo/record/validation_mode.rs
+13 -13
crates/tranquil-pds/src/api/repo/record/write.rs crates/tranquil-api/src/repo/record/write.rs
··· 1 1 use super::validation::validate_record_with_status; 2 2 use super::validation_mode::{ValidationMode, deserialize_validation_mode}; 3 - use crate::api::error::ApiError; 4 - use crate::api::repo::record::utils::{ 3 + use tranquil_pds::api::error::ApiError; 4 + use crate::repo::record::utils::{ 5 5 CommitParams, RecordOp, commit_and_log, extract_backlinks, extract_blob_cids, 6 6 get_current_root_cid, 7 7 }; 8 - use crate::auth::{ 8 + use tranquil_pds::auth::{ 9 9 Active, Auth, AuthSource, RepoScopeAction, ScopeVerified, VerifyScope, require_not_migrated, 10 10 require_verified_or_delegated, 11 11 }; 12 - use crate::cid_types::CommitCid; 13 - use crate::delegation::DelegationActionType; 14 - use crate::repo::tracking::TrackingBlockStore; 15 - use crate::state::AppState; 16 - use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey}; 17 - use crate::validation::ValidationStatus; 12 + use tranquil_pds::cid_types::CommitCid; 13 + use tranquil_pds::delegation::DelegationActionType; 14 + use tranquil_pds::repo::tracking::TrackingBlockStore; 15 + use tranquil_pds::state::AppState; 16 + use tranquil_pds::types::{AtIdentifier, AtUri, Did, Nsid, Rkey}; 17 + use tranquil_pds::validation::ValidationStatus; 18 18 use axum::{ 19 19 Json, 20 20 extract::State, ··· 104 104 State(state): State<AppState>, 105 105 auth: Auth<Active>, 106 106 Json(input): Json<CreateRecordInput>, 107 - ) -> Result<Response, crate::api::error::ApiError> { 107 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 108 108 let scope_proof = match auth.verify_repo_create(&input.collection) { 109 109 Ok(proof) => proof, 110 110 Err(e) => return Ok(e.into_response()), ··· 232 232 } 233 233 } 234 234 235 - let record_ipld = crate::util::json_to_ipld(&input.record); 235 + let record_ipld = tranquil_pds::util::json_to_ipld(&input.record); 236 236 let mut record_bytes = Vec::new(); 237 237 if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() { 238 238 return Ok(ApiError::InvalidRecord("Failed to serialize record".into()).into_response()); ··· 408 408 State(state): State<AppState>, 409 409 auth: Auth<Active>, 410 410 Json(input): Json<PutRecordInput>, 411 - ) -> Result<Response, crate::api::error::ApiError> { 411 + ) -> Result<Response, tranquil_pds::api::error::ApiError> { 412 412 let upsert_proof = match auth.verify_repo_upsert(&input.collection) { 413 413 Ok(proof) => proof, 414 414 Err(e) => return Ok(e.into_response()), ··· 476 476 } 477 477 } 478 478 let existing_cid = mst.get(&key).await.ok().flatten(); 479 - let record_ipld = crate::util::json_to_ipld(&input.record); 479 + let record_ipld = tranquil_pds::util::json_to_ipld(&input.record); 480 480 let mut record_bytes = Vec::new(); 481 481 if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() { 482 482 return Ok(ApiError::InvalidRecord("Failed to serialize record".into()).into_response());
+31 -31
crates/tranquil-pds/src/api/server/account_status.rs crates/tranquil-api/src/server/account_status.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{Auth, NotTakendown, Permissive, require_legacy_session_mfa}; 4 - use crate::cache::Cache; 5 - use crate::plc::PlcClient; 6 - use crate::state::AppState; 7 - use crate::types::PlainPassword; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{Auth, NotTakendown, Permissive, require_legacy_session_mfa}; 4 + use tranquil_pds::cache::Cache; 5 + use tranquil_pds::plc::PlcClient; 6 + use tranquil_pds::state::AppState; 7 + use tranquil_pds::types::PlainPassword; 8 8 use axum::{ 9 9 Json, 10 10 extract::State, ··· 117 117 async fn is_valid_did_for_service( 118 118 user_repo: &dyn tranquil_db_traits::UserRepository, 119 119 cache: Arc<dyn Cache>, 120 - did: &crate::types::Did, 120 + did: &tranquil_pds::types::Did, 121 121 ) -> bool { 122 122 assert_valid_did_document_for_service(user_repo, cache, did, false) 123 123 .await ··· 127 127 async fn assert_valid_did_document_for_service( 128 128 user_repo: &dyn tranquil_db_traits::UserRepository, 129 129 cache: Arc<dyn Cache>, 130 - did: &crate::types::Did, 130 + did: &tranquil_pds::types::Did, 131 131 with_retry: bool, 132 132 ) -> Result<(), ApiError> { 133 133 let hostname = &tranquil_config::get().server.hostname; ··· 226 226 227 227 if let Some(key_info) = user_key { 228 228 let key_bytes = 229 - crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) 229 + tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) 230 230 .map_err(|e| { 231 231 error!("Failed to decrypt user key: {}", e); 232 232 ApiError::InternalError(None) ··· 235 235 error!("Failed to create signing key: {:?}", e); 236 236 ApiError::InternalError(None) 237 237 })?; 238 - let expected_did_key = crate::plc::signing_key_to_did_key(&signing_key); 238 + let expected_did_key = tranquil_pds::plc::signing_key_to_did_key(&signing_key); 239 239 240 240 if doc_signing_key != Some(&expected_did_key) { 241 241 warn!( ··· 248 248 } 249 249 } 250 250 } else if let Some(host_and_path) = did.as_str().strip_prefix("did:web:") { 251 - let client = crate::api::proxy_client::did_resolution_client(); 251 + let client = tranquil_pds::api::proxy_client::did_resolution_client(); 252 252 let decoded = host_and_path.replace("%3A", ":"); 253 253 let parts: Vec<&str> = decoded.split(':').collect(); 254 254 let (host, path_parts) = if parts.len() > 1 && parts[1].chars().all(|c| c.is_ascii_digit()) ··· 284 284 arr.iter().find(|svc| { 285 285 svc.get("id").and_then(|id| id.as_str()) == Some("#atproto_pds") 286 286 || svc.get("type").and_then(|t| t.as_str()) 287 - == Some(crate::plc::ServiceType::Pds.as_str()) 287 + == Some(tranquil_pds::plc::ServiceType::Pds.as_str()) 288 288 }) 289 289 }) 290 290 .and_then(|svc| svc.get("serviceEndpoint")) ··· 314 314 auth.did 315 315 ); 316 316 317 - if let Err(e) = crate::auth::scope_check::check_account_scope( 317 + if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope( 318 318 &auth.auth_source, 319 319 auth.scope.as_deref(), 320 - crate::oauth::scopes::AccountAttr::Repo, 321 - crate::oauth::scopes::AccountAction::Manage, 320 + tranquil_pds::oauth::scopes::AccountAttr::Repo, 321 + tranquil_pds::oauth::scopes::AccountAction::Manage, 322 322 ) { 323 323 info!("[MIGRATION] activateAccount: Scope check failed"); 324 324 return Ok(e); ··· 365 365 did 366 366 ); 367 367 if let Some(ref h) = handle { 368 - let _ = state.cache.delete(&crate::cache_keys::handle_key(h)).await; 368 + let _ = state.cache.delete(&tranquil_pds::cache_keys::handle_key(h)).await; 369 369 } 370 370 let _ = state 371 371 .cache 372 - .delete(&crate::cache_keys::plc_doc_key(&did)) 372 + .delete(&tranquil_pds::cache_keys::plc_doc_key(&did)) 373 373 .await; 374 374 let _ = state 375 375 .cache 376 - .delete(&crate::cache_keys::plc_data_key(&did)) 376 + .delete(&tranquil_pds::cache_keys::plc_data_key(&did)) 377 377 .await; 378 378 if state.did_resolver.refresh_did(did.as_str()).await.is_none() { 379 379 warn!( ··· 385 385 "[MIGRATION] activateAccount: Sequencing account event (active=true) for did={}", 386 386 did 387 387 ); 388 - if let Err(e) = crate::api::repo::record::sequence_account_event( 388 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 389 389 &state, 390 390 &did, 391 391 tranquil_db_traits::AccountStatus::Active, ··· 404 404 did, handle 405 405 ); 406 406 let handle_typed = handle.clone(); 407 - if let Err(e) = crate::api::repo::record::sequence_identity_event( 407 + if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event( 408 408 &state, 409 409 &did, 410 410 handle_typed.as_ref(), ··· 438 438 } else { 439 439 None 440 440 }; 441 - if let Err(e) = crate::api::repo::record::sequence_sync_event( 441 + if let Err(e) = tranquil_pds::repo_ops::sequence_sync_event( 442 442 &state, 443 443 &did, 444 444 root_cid_link.as_str(), ··· 483 483 auth: Auth<Permissive>, 484 484 Json(input): Json<DeactivateAccountInput>, 485 485 ) -> Result<Response, ApiError> { 486 - if let Err(e) = crate::auth::scope_check::check_account_scope( 486 + if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope( 487 487 &auth.auth_source, 488 488 auth.scope.as_deref(), 489 - crate::oauth::scopes::AccountAttr::Repo, 490 - crate::oauth::scopes::AccountAction::Manage, 489 + tranquil_pds::oauth::scopes::AccountAttr::Repo, 490 + tranquil_pds::oauth::scopes::AccountAction::Manage, 491 491 ) { 492 492 return Ok(e); 493 493 } ··· 507 507 match result { 508 508 Ok(true) => { 509 509 if let Some(ref h) = handle { 510 - let _ = state.cache.delete(&crate::cache_keys::handle_key(h)).await; 510 + let _ = state.cache.delete(&tranquil_pds::cache_keys::handle_key(h)).await; 511 511 } 512 - if let Err(e) = crate::api::repo::record::sequence_account_event( 512 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 513 513 &state, 514 514 &did, 515 515 tranquil_db_traits::AccountStatus::Deactivated, ··· 552 552 .await 553 553 .log_db_err("creating deletion token")?; 554 554 let hostname = &tranquil_config::get().server.hostname; 555 - if let Err(e) = crate::comms::comms_repo::enqueue_account_deletion( 555 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_account_deletion( 556 556 state.user_repo.as_ref(), 557 557 state.infra_repo.as_ref(), 558 558 user_id, ··· 569 569 570 570 #[derive(Deserialize)] 571 571 pub struct DeleteAccountInput { 572 - pub did: crate::types::Did, 572 + pub did: tranquil_pds::types::Did, 573 573 pub password: PlainPassword, 574 574 pub token: String, 575 575 } ··· 642 642 error!("DB error deleting account: {:?}", e); 643 643 return ApiError::InternalError(None).into_response(); 644 644 } 645 - let account_seq = crate::api::repo::record::sequence_account_event( 645 + let account_seq = tranquil_pds::repo_ops::sequence_account_event( 646 646 &state, 647 647 did, 648 648 tranquil_db_traits::AccountStatus::Deleted, ··· 666 666 } 667 667 let _ = state 668 668 .cache 669 - .delete(&crate::cache_keys::handle_key(&handle)) 669 + .delete(&tranquil_pds::cache_keys::handle_key(&handle)) 670 670 .await; 671 671 info!("Account {} deleted successfully", did); 672 672 EmptyResponse::ok().into_response()
+7 -7
crates/tranquil-pds/src/api/server/app_password.rs crates/tranquil-api/src/server/app_password.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{Auth, NotTakendown, Permissive, generate_app_password}; 4 - use crate::delegation::{DelegationActionType, intersect_scopes}; 5 - use crate::rate_limit::{AppPasswordLimit, RateLimited}; 6 - use crate::state::AppState; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{Auth, NotTakendown, Permissive, generate_app_password}; 4 + use tranquil_pds::delegation::{DelegationActionType, intersect_scopes}; 5 + use tranquil_pds::rate_limit::{AppPasswordLimit, RateLimited}; 6 + use tranquil_pds::state::AppState; 7 7 use axum::{ 8 8 Json, 9 9 extract::State, ··· 233 233 .log_db_err("revoking sessions for app password")?; 234 234 235 235 futures::future::join_all(sessions_to_invalidate.iter().map(|jti| { 236 - let cache_key = crate::cache_keys::session_key(&auth.did, jti); 236 + let cache_key = tranquil_pds::cache_keys::session_key(&auth.did, jti); 237 237 let cache = state.cache.clone(); 238 238 async move { 239 239 let _ = cache.delete(&cache_key).await;
+43 -43
crates/tranquil-pds/src/api/server/email.rs crates/tranquil-api/src/server/email.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::api::{EmptyResponse, TokenRequiredResponse, VerifiedResponse}; 3 - use crate::auth::{Auth, NotTakendown}; 4 - use crate::rate_limit::{EmailUpdateLimit, RateLimited, VerificationCheckLimit}; 5 - use crate::state::AppState; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::api::{EmptyResponse, TokenRequiredResponse, VerifiedResponse}; 3 + use tranquil_pds::auth::{Auth, NotTakendown}; 4 + use tranquil_pds::rate_limit::{EmailUpdateLimit, RateLimited, VerificationCheckLimit}; 5 + use tranquil_pds::state::AppState; 6 6 use axum::{ 7 7 Json, 8 8 extract::State, ··· 20 20 const EMAIL_UPDATE_TTL: Duration = Duration::from_secs(30 * 60); 21 21 22 22 fn email_update_cache_key(did: &str) -> String { 23 - crate::cache_keys::email_update_key(did) 23 + tranquil_pds::cache_keys::email_update_key(did) 24 24 } 25 25 26 26 fn hash_token(token: &str) -> String { ··· 49 49 auth: Auth<NotTakendown>, 50 50 input: Option<Json<RequestEmailUpdateInput>>, 51 51 ) -> Result<Response, ApiError> { 52 - if let Err(e) = crate::auth::scope_check::check_account_scope( 52 + if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope( 53 53 &auth.auth_source, 54 54 auth.scope.as_deref(), 55 - crate::oauth::scopes::AccountAttr::Email, 56 - crate::oauth::scopes::AccountAction::Manage, 55 + tranquil_pds::oauth::scopes::AccountAttr::Email, 56 + tranquil_pds::oauth::scopes::AccountAction::Manage, 57 57 ) { 58 58 return Ok(e); 59 59 } ··· 74 74 let token_required = user.email_verified; 75 75 76 76 if token_required { 77 - let token = crate::auth::email_token::create_email_token( 77 + let token = tranquil_pds::auth::email_token::create_email_token( 78 78 state.cache.as_ref(), 79 79 auth.did.as_str(), 80 - crate::auth::email_token::EmailTokenPurpose::UpdateEmail, 80 + tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail, 81 81 ) 82 82 .await 83 83 .map_err(|e| { ··· 89 89 && let Some(ref new_email) = inp.new_email 90 90 { 91 91 let new_email = new_email.trim().to_lowercase(); 92 - if !new_email.is_empty() && crate::api::validation::is_valid_email(&new_email) { 92 + if !new_email.is_empty() && tranquil_pds::api::validation::is_valid_email(&new_email) { 93 93 let pending = PendingEmailUpdate { 94 94 new_email, 95 95 token_hash: hash_token(&token), ··· 105 105 } 106 106 107 107 let hostname = &tranquil_config::get().server.hostname; 108 - if let Err(e) = crate::comms::comms_repo::enqueue_short_token_email( 108 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_short_token_email( 109 109 state.user_repo.as_ref(), 110 110 state.infra_repo.as_ref(), 111 111 user.id, ··· 135 135 auth: Auth<NotTakendown>, 136 136 Json(input): Json<ConfirmEmailInput>, 137 137 ) -> Result<Response, ApiError> { 138 - if let Err(e) = crate::auth::scope_check::check_account_scope( 138 + if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope( 139 139 &auth.auth_source, 140 140 auth.scope.as_deref(), 141 - crate::oauth::scopes::AccountAttr::Email, 142 - crate::oauth::scopes::AccountAction::Manage, 141 + tranquil_pds::oauth::scopes::AccountAttr::Email, 142 + tranquil_pds::oauth::scopes::AccountAction::Manage, 143 143 ) { 144 144 return Ok(e); 145 145 } ··· 167 167 } 168 168 169 169 let confirmation_code = 170 - crate::auth::verification_token::normalize_token_input(input.token.trim()); 170 + tranquil_pds::auth::verification_token::normalize_token_input(input.token.trim()); 171 171 172 - let verified = crate::auth::verification_token::verify_signup_token( 172 + let verified = tranquil_pds::auth::verification_token::verify_signup_token( 173 173 &confirmation_code, 174 174 CommsChannel::Email, 175 175 &provided_email, ··· 181 181 return Err(ApiError::InvalidToken(None)); 182 182 } 183 183 } 184 - Err(crate::auth::verification_token::VerifyError::Expired) => { 184 + Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => { 185 185 return Err(ApiError::ExpiredToken(None)); 186 186 } 187 187 Err(_) => { ··· 213 213 auth: Auth<NotTakendown>, 214 214 Json(input): Json<UpdateEmailInput>, 215 215 ) -> Result<Response, ApiError> { 216 - if let Err(e) = crate::auth::scope_check::check_account_scope( 216 + if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope( 217 217 &auth.auth_source, 218 218 auth.scope.as_deref(), 219 - crate::oauth::scopes::AccountAttr::Email, 220 - crate::oauth::scopes::AccountAction::Manage, 219 + tranquil_pds::oauth::scopes::AccountAttr::Email, 220 + tranquil_pds::oauth::scopes::AccountAction::Manage, 221 221 ) { 222 222 return Ok(e); 223 223 } ··· 235 235 let email_verified = user.email_verified; 236 236 let new_email = input.email.trim().to_lowercase(); 237 237 238 - if !crate::api::validation::is_valid_email(&new_email) { 238 + if !tranquil_pds::api::validation::is_valid_email(&new_email) { 239 239 return Err(ApiError::InvalidRequest( 240 240 "This email address is not supported, please use a different email.".into(), 241 241 )); ··· 255 255 .filter(|t| !t.is_empty()) 256 256 .ok_or(ApiError::TokenRequired)?; 257 257 258 - crate::auth::email_token::validate_email_token( 258 + tranquil_pds::auth::email_token::validate_email_token( 259 259 state.cache.as_ref(), 260 260 did.as_str(), 261 - crate::auth::email_token::EmailTokenPurpose::UpdateEmail, 261 + tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail, 262 262 token, 263 263 ) 264 264 .await 265 265 .map_err(|e| match e { 266 - crate::auth::email_token::TokenError::ExpiredToken => { 266 + tranquil_pds::auth::email_token::TokenError::ExpiredToken => { 267 267 ApiError::ExpiredToken(None) 268 268 } 269 269 _ => ApiError::InvalidToken(None), ··· 303 303 .filter(|t| !t.is_empty()) 304 304 .ok_or(ApiError::TokenRequired)?; 305 305 306 - let short_token_result = crate::auth::email_token::validate_email_token( 306 + let short_token_result = tranquil_pds::auth::email_token::validate_email_token( 307 307 state.cache.as_ref(), 308 308 did.as_str(), 309 - crate::auth::email_token::EmailTokenPurpose::UpdateEmail, 309 + tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail, 310 310 token, 311 311 ) 312 312 .await; 313 313 314 314 if let Err(e) = short_token_result { 315 315 let confirmation_token = 316 - crate::auth::verification_token::normalize_token_input(token.trim()); 316 + tranquil_pds::auth::verification_token::normalize_token_input(token.trim()); 317 317 318 318 let current_email_lower = current_email 319 319 .as_ref() 320 320 .map(|e| e.to_lowercase()) 321 321 .unwrap_or_default(); 322 322 323 - let verified = crate::auth::verification_token::verify_channel_update_token( 323 + let verified = tranquil_pds::auth::verification_token::verify_channel_update_token( 324 324 &confirmation_token, 325 325 CommsChannel::Email, 326 326 &current_email_lower, ··· 332 332 return Err(ApiError::InvalidToken(None)); 333 333 } 334 334 } 335 - Err(crate::auth::verification_token::VerifyError::Expired) => { 335 + Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => { 336 336 return Err(match e { 337 - crate::auth::email_token::TokenError::ExpiredToken => { 337 + tranquil_pds::auth::email_token::TokenError::ExpiredToken => { 338 338 ApiError::ExpiredToken(None) 339 339 } 340 340 _ => ApiError::InvalidToken(None), ··· 342 342 } 343 343 Err(_) => { 344 344 return Err(match e { 345 - crate::auth::email_token::TokenError::ExpiredToken => { 345 + tranquil_pds::auth::email_token::TokenError::ExpiredToken => { 346 346 ApiError::ExpiredToken(None) 347 347 } 348 348 _ => ApiError::InvalidToken(None), ··· 359 359 .await 360 360 .log_db_err("updating email")?; 361 361 362 - let verification_token = crate::auth::verification_token::generate_signup_token( 362 + let verification_token = tranquil_pds::auth::verification_token::generate_signup_token( 363 363 did, 364 364 CommsChannel::Email, 365 365 &new_email, 366 366 ); 367 367 let formatted_token = 368 - crate::auth::verification_token::format_token_for_display(&verification_token); 368 + tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 369 369 let hostname = &tranquil_config::get().server.hostname; 370 - if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification( 370 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 371 371 state.user_repo.as_ref(), 372 372 state.infra_repo.as_ref(), 373 373 user_id, ··· 423 423 424 424 #[derive(Deserialize)] 425 425 pub struct CheckChannelVerifiedInput { 426 - pub did: crate::types::Did, 426 + pub did: tranquil_pds::types::Did, 427 427 pub channel: CommsChannel, 428 428 } 429 429 ··· 456 456 _rate_limit: RateLimited<VerificationCheckLimit>, 457 457 axum::extract::Query(query): axum::extract::Query<AuthorizeEmailUpdateQuery>, 458 458 ) -> Response { 459 - let verified = crate::auth::verification_token::verify_token_signature(&query.token); 459 + let verified = tranquil_pds::auth::verification_token::verify_token_signature(&query.token); 460 460 461 461 let token_data = match verified { 462 462 Ok(data) => data, 463 - Err(crate::auth::verification_token::VerifyError::Expired) => { 463 + Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => { 464 464 warn!("authorize_email_update: token expired"); 465 465 return ApiError::ExpiredToken(None).into_response(); 466 466 } ··· 470 470 } 471 471 }; 472 472 473 - if token_data.purpose != crate::auth::verification_token::VerificationPurpose::ChannelUpdate { 473 + if token_data.purpose != tranquil_pds::auth::verification_token::VerificationPurpose::ChannelUpdate { 474 474 warn!( 475 475 "authorize_email_update: wrong purpose: {:?}", 476 476 token_data.purpose ··· 544 544 _rate_limit: RateLimited<VerificationCheckLimit>, 545 545 auth: Auth<NotTakendown>, 546 546 ) -> Result<Response, ApiError> { 547 - if let Err(e) = crate::auth::scope_check::check_account_scope( 547 + if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope( 548 548 &auth.auth_source, 549 549 auth.scope.as_deref(), 550 - crate::oauth::scopes::AccountAttr::Email, 551 - crate::oauth::scopes::AccountAction::Read, 550 + tranquil_pds::oauth::scopes::AccountAttr::Email, 551 + tranquil_pds::oauth::scopes::AccountAction::Read, 552 552 ) { 553 553 return Ok(e); 554 554 }
+5 -5
crates/tranquil-pds/src/api/server/invite.rs crates/tranquil-api/src/server/invite.rs
··· 1 - use crate::api::ApiError; 2 - use crate::api::error::DbResultExt; 3 - use crate::auth::{Admin, Auth, NotTakendown}; 4 - use crate::state::AppState; 5 - use crate::types::Did; 1 + use tranquil_pds::api::ApiError; 2 + use tranquil_pds::api::error::DbResultExt; 3 + use tranquil_pds::auth::{Admin, Auth, NotTakendown}; 4 + use tranquil_pds::state::AppState; 5 + use tranquil_pds::types::Did; 6 6 use axum::{ 7 7 Json, 8 8 extract::State,
+2 -2
crates/tranquil-pds/src/api/server/logo.rs crates/tranquil-api/src/server/logo.rs
··· 1 - use crate::state::AppState; 1 + use tranquil_pds::state::AppState; 2 2 use axum::{ 3 3 body::Body, 4 4 extract::State, ··· 21 21 Some(c) if !c.is_empty() => c, 22 22 _ => return StatusCode::NOT_FOUND.into_response(), 23 23 }; 24 - let cid = match crate::types::CidLink::new(&cid_str) { 24 + let cid = match tranquil_pds::types::CidLink::new(&cid_str) { 25 25 Ok(c) => c, 26 26 Err(_) => return StatusCode::NOT_FOUND.into_response(), 27 27 };
+3 -3
crates/tranquil-pds/src/api/server/meta.rs crates/tranquil-api/src/server/meta.rs
··· 1 - use crate::BUILD_VERSION; 2 - use crate::state::AppState; 3 - use crate::util::{discord_app_id, discord_bot_username, telegram_bot_username}; 1 + use tranquil_pds::BUILD_VERSION; 2 + use tranquil_pds::state::AppState; 3 + use tranquil_pds::util::{discord_app_id, discord_bot_username, telegram_bot_username}; 4 4 use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; 5 5 use serde_json::json; 6 6
+9 -9
crates/tranquil-pds/src/api/server/migration.rs crates/tranquil-api/src/server/migration.rs
··· 1 - use crate::api::ApiError; 2 - use crate::api::error::DbResultExt; 3 - use crate::auth::{Active, Auth}; 4 - use crate::state::AppState; 1 + use tranquil_pds::api::ApiError; 2 + use tranquil_pds::api::error::DbResultExt; 3 + use tranquil_pds::auth::{Active, Auth}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::{ 6 6 Json, 7 7 extract::State, ··· 145 145 Ok((StatusCode::OK, Json(json!({ "didDocument": did_doc }))).into_response()) 146 146 } 147 147 148 - async fn build_did_document(state: &AppState, did: &crate::types::Did) -> serde_json::Value { 148 + async fn build_did_document(state: &AppState, did: &tranquil_pds::types::Did) -> serde_json::Value { 149 149 let hostname = &tranquil_config::get().server.hostname; 150 150 151 151 let user = match state.user_repo.get_user_for_did_doc_build(did).await { ··· 195 195 })).collect::<Vec<_>>(), 196 196 "service": [{ 197 197 "id": "#atproto_pds", 198 - "type": crate::plc::ServiceType::Pds.as_str(), 198 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 199 199 "serviceEndpoint": service_endpoint 200 200 }] 201 201 }); ··· 209 209 .flatten(); 210 210 211 211 let public_key_multibase = match key_info { 212 - Some(info) => match crate::config::decrypt_key(&info.key_bytes, info.encryption_version) { 213 - Ok(key_bytes) => crate::api::identity::did::get_public_key_multibase(&key_bytes) 212 + Some(info) => match tranquil_pds::config::decrypt_key(&info.key_bytes, info.encryption_version) { 213 + Ok(key_bytes) => crate::identity::did::get_public_key_multibase(&key_bytes) 214 214 .unwrap_or_else(|_| "error".to_string()), 215 215 Err(_) => "error".to_string(), 216 216 }, ··· 243 243 }], 244 244 "service": [{ 245 245 "id": "#atproto_pds", 246 - "type": crate::plc::ServiceType::Pds.as_str(), 246 + "type": tranquil_pds::plc::ServiceType::Pds.as_str(), 247 247 "serviceEndpoint": service_endpoint 248 248 }] 249 249 })
crates/tranquil-pds/src/api/server/mod.rs crates/tranquil-api/src/server/mod.rs
+34 -34
crates/tranquil-pds/src/api/server/passkey_account.rs crates/tranquil-api/src/server/passkey_account.rs
··· 1 - use crate::api::SuccessResponse; 2 - use crate::api::error::ApiError; 3 - use crate::auth::NormalizedLoginIdentifier; 1 + use tranquil_pds::api::SuccessResponse; 2 + use tranquil_pds::api::error::ApiError; 3 + use tranquil_pds::auth::NormalizedLoginIdentifier; 4 4 use axum::{ 5 5 Json, 6 6 extract::State, ··· 19 19 use tranquil_db_traits::WebauthnChallengeType; 20 20 use uuid::Uuid; 21 21 22 - use crate::api::repo::record::utils::create_signed_commit; 23 - use crate::auth::{ServiceTokenVerifier, generate_app_password, is_service_token}; 24 - use crate::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited}; 25 - use crate::state::AppState; 26 - use crate::types::{Did, Handle, PlainPassword}; 27 - use crate::validation::validate_password; 22 + use tranquil_pds::repo_ops::create_signed_commit; 23 + use tranquil_pds::auth::{ServiceTokenVerifier, generate_app_password, is_service_token}; 24 + use tranquil_pds::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited}; 25 + use tranquil_pds::state::AppState; 26 + use tranquil_pds::types::{Did, Handle, PlainPassword}; 27 + use tranquil_pds::validation::validate_password; 28 28 29 29 fn generate_setup_token() -> String { 30 30 let mut rng = rand::thread_rng(); ··· 72 72 headers: HeaderMap, 73 73 Json(input): Json<CreatePasskeyAccountInput>, 74 74 ) -> Response { 75 - let byod_auth = if let Some(extracted) = crate::auth::extract_auth_token_from_header( 76 - crate::util::get_header_str(&headers, http::header::AUTHORIZATION), 75 + let byod_auth = if let Some(extracted) = tranquil_pds::auth::extract_auth_token_from_header( 76 + tranquil_pds::util::get_header_str(&headers, http::header::AUTHORIZATION), 77 77 ) { 78 78 let token = extracted.token; 79 79 if is_service_token(&token) { ··· 128 128 .unwrap_or(&input.handle), 129 129 None => &input.handle, 130 130 }; 131 - match crate::api::validation::validate_short_handle(handle_to_validate) { 131 + match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) { 132 132 Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])), 133 133 Err(_) => { 134 134 return ApiError::InvalidHandle(None).into_response(); 135 135 } 136 136 } 137 137 } else { 138 - match crate::api::validation::validate_full_domain_handle(&input.handle) { 138 + match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) { 139 139 Ok(h) => h, 140 140 Err(_) => return ApiError::InvalidHandle(None).into_response(), 141 141 } ··· 147 147 .map(|e| e.trim().to_string()) 148 148 .filter(|e| !e.is_empty()); 149 149 if let Some(ref email) = email 150 - && !crate::api::validation::is_valid_email(email) 150 + && !tranquil_pds::api::validation::is_valid_email(email) 151 151 { 152 152 return ApiError::InvalidEmail.into_response(); 153 153 } ··· 184 184 tranquil_db_traits::CommsChannel::Discord => match &input.discord_username { 185 185 Some(username) if !username.trim().is_empty() => { 186 186 let clean = username.trim().to_lowercase(); 187 - if !crate::api::validation::is_valid_discord_username(&clean) { 187 + if !tranquil_pds::api::validation::is_valid_discord_username(&clean) { 188 188 return ApiError::InvalidRequest( 189 189 "Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(), 190 190 ).into_response(); ··· 196 196 tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username { 197 197 Some(username) if !username.trim().is_empty() => { 198 198 let clean = username.trim().trim_start_matches('@'); 199 - if !crate::api::validation::is_valid_telegram_username(clean) { 199 + if !tranquil_pds::api::validation::is_valid_telegram_username(clean) { 200 200 return ApiError::InvalidRequest( 201 201 "Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(), 202 202 ).into_response(); ··· 250 250 251 251 let did = match did_type { 252 252 "web" => { 253 - if !crate::api::server::meta::is_self_hosted_did_web_enabled() { 253 + if !tranquil_pds::util::is_self_hosted_did_web_enabled() { 254 254 return ApiError::SelfHostedDidWebDisabled.into_response(); 255 255 } 256 256 let encoded_handle = handle.replace(':', "%3A"); ··· 284 284 } 285 285 info!(did = %d, "Creating external did:web passkey account (BYOD key)"); 286 286 } else { 287 - if let Err(e) = crate::api::identity::did::verify_did_web( 287 + if let Err(e) = crate::identity::did::verify_did_web( 288 288 d, 289 289 hostname, 290 290 &input.handle, ··· 328 328 .secrets 329 329 .plc_rotation_key 330 330 .clone() 331 - .unwrap_or_else(|| crate::plc::signing_key_to_did_key(&secret_key)); 331 + .unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&secret_key)); 332 332 333 - let genesis_result = match crate::plc::create_genesis_operation( 333 + let genesis_result = match tranquil_pds::plc::create_genesis_operation( 334 334 &secret_key, 335 335 &rotation_key, 336 336 &handle, ··· 346 346 } 347 347 }; 348 348 349 - let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 349 + let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 350 350 if let Err(e) = plc_client 351 351 .send_operation(&genesis_result.did, &genesis_result.signed_operation) 352 352 .await ··· 381 381 None 382 382 }; 383 383 384 - let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) { 384 + let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) { 385 385 Ok(bytes) => bytes, 386 386 Err(e) => { 387 387 error!("Error encrypting signing key: {:?}", e); ··· 458 458 setup_expires_at, 459 459 deactivated_at, 460 460 encrypted_key_bytes, 461 - encryption_version: crate::config::ENCRYPTION_VERSION, 461 + encryption_version: tranquil_pds::config::ENCRYPTION_VERSION, 462 462 reserved_key_id, 463 463 commit_cid: commit_cid.to_string(), 464 464 repo_rev: rev.as_ref().to_string(), ··· 487 487 let user_id = create_result.user_id; 488 488 489 489 if !is_byod_did_web { 490 - if let Err(e) = crate::api::repo::record::sequence_identity_event( 490 + if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event( 491 491 &state, 492 492 &did_typed, 493 493 Some(&handle_typed), ··· 496 496 { 497 497 warn!("Failed to sequence identity event for {}: {}", did, e); 498 498 } 499 - if let Err(e) = crate::api::repo::record::sequence_account_event( 499 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 500 500 &state, 501 501 &did_typed, 502 502 tranquil_db_traits::AccountStatus::Active, ··· 509 509 "$type": "app.bsky.actor.profile", 510 510 "displayName": handle 511 511 }); 512 - if let Err(e) = crate::api::repo::record::create_record_internal( 512 + if let Err(e) = tranquil_pds::repo_ops::create_record_internal( 513 513 &state, 514 514 &did_typed, 515 - &crate::types::PROFILE_COLLECTION, 516 - &crate::types::PROFILE_RKEY, 515 + &tranquil_pds::types::PROFILE_COLLECTION, 516 + &tranquil_pds::types::PROFILE_RKEY, 517 517 &profile_record, 518 518 ) 519 519 .await ··· 522 522 } 523 523 } 524 524 525 - let verification_token = crate::auth::verification_token::generate_signup_token( 525 + let verification_token = tranquil_pds::auth::verification_token::generate_signup_token( 526 526 &did_typed, 527 527 verification_channel, 528 528 &verification_recipient, 529 529 ); 530 530 let formatted_token = 531 - crate::auth::verification_token::format_token_for_display(&verification_token); 532 - if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification( 531 + tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 532 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 533 533 state.user_repo.as_ref(), 534 534 state.infra_repo.as_ref(), 535 535 user_id, ··· 546 546 info!(did = %did, handle = %handle, "Passkey-only account created, awaiting setup completion"); 547 547 548 548 let access_jwt = if byod_auth.is_some() { 549 - match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) { 549 + match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) { 550 550 Ok(token_meta) => { 551 551 let refresh_jti = uuid::Uuid::new_v4().to_string(); 552 552 let refresh_expires = chrono::Utc::now() + chrono::Duration::hours(24); ··· 887 887 urlencoding::encode(&recovery_token) 888 888 ); 889 889 890 - let _ = crate::comms::comms_repo::enqueue_passkey_recovery( 890 + let _ = tranquil_pds::comms::comms_repo::enqueue_passkey_recovery( 891 891 state.user_repo.as_ref(), 892 892 state.infra_repo.as_ref(), 893 893 user.id, ··· 968 968 } 969 969 if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user.id).await { 970 970 let actual_channel = 971 - crate::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel); 971 + tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel); 972 972 if let Err(e) = state 973 973 .user_repo 974 974 .set_channel_verified(&input.did, actual_channel)
+5 -5
crates/tranquil-pds/src/api/server/passkeys.rs crates/tranquil-api/src/server/passkeys.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{Active, Auth, require_legacy_session_mfa, require_reauth_window}; 4 - use crate::state::AppState; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{Active, Auth, require_legacy_session_mfa, require_reauth_window}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::{ 6 6 Json, 7 7 extract::State, ··· 271 271 } 272 272 } 273 273 274 - pub async fn has_passkeys_for_user(state: &AppState, did: &crate::types::Did) -> bool { 274 + pub async fn has_passkeys_for_user(state: &AppState, did: &tranquil_pds::types::Did) -> bool { 275 275 state.user_repo.has_passkeys(did).await.unwrap_or(false) 276 276 }
+12 -12
crates/tranquil-pds/src/api/server/password.rs crates/tranquil-api/src/server/password.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::api::{EmptyResponse, HasPasswordResponse, SuccessResponse}; 3 - use crate::auth::{ 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::api::{EmptyResponse, HasPasswordResponse, SuccessResponse}; 3 + use tranquil_pds::auth::{ 4 4 Active, Auth, NormalizedLoginIdentifier, require_legacy_session_mfa, require_reauth_window, 5 5 require_reauth_window_if_available, 6 6 }; 7 - use crate::rate_limit::{PasswordResetLimit, RateLimited, ResetPasswordLimit}; 8 - use crate::state::AppState; 9 - use crate::types::PlainPassword; 10 - use crate::validation::validate_password; 7 + use tranquil_pds::rate_limit::{PasswordResetLimit, RateLimited, ResetPasswordLimit}; 8 + use tranquil_pds::state::AppState; 9 + use tranquil_pds::types::PlainPassword; 10 + use tranquil_pds::validation::validate_password; 11 11 use axum::{ 12 12 Json, 13 13 extract::State, ··· 19 19 use tracing::{error, info, warn}; 20 20 21 21 fn generate_reset_code() -> String { 22 - crate::util::generate_token_code() 22 + tranquil_pds::util::generate_token_code() 23 23 } 24 24 25 25 #[derive(Deserialize)] ··· 78 78 return ApiError::InternalError(None).into_response(); 79 79 } 80 80 let hostname = &tranquil_config::get().server.hostname; 81 - if let Err(e) = crate::comms::comms_repo::enqueue_password_reset( 81 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_password_reset( 82 82 state.user_repo.as_ref(), 83 83 state.infra_repo.as_ref(), 84 84 user_id, ··· 170 170 } 171 171 }; 172 172 futures::future::join_all(result.session_jtis.iter().map(|jti| { 173 - let cache_key = crate::cache_keys::session_key(&result.did, jti); 173 + let cache_key = tranquil_pds::cache_keys::session_key(&result.did, jti); 174 174 let cache = state.cache.clone(); 175 175 async move { 176 176 if let Err(e) = cache.delete(&cache_key).await { ··· 184 184 .await; 185 185 if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user_id).await { 186 186 let actual_channel = 187 - crate::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel); 187 + tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel); 188 188 if let Err(e) = state 189 189 .user_repo 190 190 .set_channel_verified(&user.did, actual_channel) ··· 212 212 auth: Auth<Active>, 213 213 Json(input): Json<ChangePasswordInput>, 214 214 ) -> Result<Response, ApiError> { 215 - use crate::auth::verify_password_mfa; 215 + use tranquil_pds::auth::verify_password_mfa; 216 216 217 217 let session_mfa = match require_legacy_session_mfa(&state, &auth).await { 218 218 Ok(proof) => proof,
+18 -18
crates/tranquil-pds/src/api/server/reauth.rs crates/tranquil-api/src/server/reauth.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 2 use axum::{ 3 3 Json, 4 4 extract::State, ··· 10 10 use tracing::{error, info, warn}; 11 11 use tranquil_db_traits::{SessionRepository, UserRepository, WebauthnChallengeType}; 12 12 13 - use crate::auth::{Active, Auth}; 14 - use crate::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message}; 15 - use crate::state::AppState; 16 - use crate::types::PlainPassword; 13 + use tranquil_pds::auth::{Active, Auth}; 14 + use tranquil_pds::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message}; 15 + use tranquil_pds::state::AppState; 16 + use tranquil_pds::types::PlainPassword; 17 17 18 18 pub const REAUTH_WINDOW_SECONDS: i64 = 300; 19 19 ··· 125 125 .await?; 126 126 127 127 let valid = 128 - crate::api::server::totp::verify_totp_or_backup_for_user(&state, &auth.did, &input.code) 128 + crate::server::totp::verify_totp_or_backup_for_user(&state, &auth.did, &input.code) 129 129 .await; 130 130 131 131 if !valid { ··· 276 276 277 277 pub async fn update_last_reauth_cached( 278 278 session_repo: &dyn SessionRepository, 279 - cache: &std::sync::Arc<dyn crate::cache::Cache>, 280 - did: &crate::types::Did, 279 + cache: &std::sync::Arc<dyn tranquil_pds::cache::Cache>, 280 + did: &tranquil_pds::types::Did, 281 281 ) -> Result<DateTime<Utc>, tranquil_db_traits::DbError> { 282 282 let now = session_repo.update_last_reauth(did).await?; 283 - let cache_key = crate::cache_keys::reauth_key(did); 283 + let cache_key = tranquil_pds::cache_keys::reauth_key(did); 284 284 let _ = cache 285 285 .set( 286 286 &cache_key, ··· 304 304 async fn get_available_reauth_methods( 305 305 user_repo: &dyn UserRepository, 306 306 _session_repo: &dyn SessionRepository, 307 - did: &crate::types::Did, 307 + did: &tranquil_pds::types::Did, 308 308 ) -> Vec<ReauthMethod> { 309 309 let mut methods = Vec::new(); 310 310 ··· 334 334 335 335 pub async fn check_reauth_required( 336 336 session_repo: &dyn SessionRepository, 337 - did: &crate::types::Did, 337 + did: &tranquil_pds::types::Did, 338 338 ) -> bool { 339 339 match session_repo.get_last_reauth_at(did).await { 340 340 Ok(last_reauth_at) => is_reauth_required(last_reauth_at), ··· 344 344 345 345 pub async fn check_reauth_required_cached( 346 346 session_repo: &dyn SessionRepository, 347 - cache: &std::sync::Arc<dyn crate::cache::Cache>, 348 - did: &crate::types::Did, 347 + cache: &std::sync::Arc<dyn tranquil_pds::cache::Cache>, 348 + did: &tranquil_pds::types::Did, 349 349 ) -> bool { 350 - let cache_key = crate::cache_keys::reauth_key(did); 350 + let cache_key = tranquil_pds::cache_keys::reauth_key(did); 351 351 if let Some(timestamp_str) = cache.get(&cache_key).await 352 352 && let Ok(timestamp) = timestamp_str.parse::<i64>() 353 353 { ··· 376 376 pub async fn reauth_required_response( 377 377 user_repo: &dyn UserRepository, 378 378 session_repo: &dyn SessionRepository, 379 - did: &crate::types::Did, 379 + did: &tranquil_pds::types::Did, 380 380 ) -> Response { 381 381 let methods = get_available_reauth_methods(user_repo, session_repo, did).await; 382 382 ( ··· 392 392 393 393 pub async fn check_legacy_session_mfa( 394 394 session_repo: &dyn SessionRepository, 395 - did: &crate::types::Did, 395 + did: &tranquil_pds::types::Did, 396 396 ) -> bool { 397 397 match session_repo.get_session_mfa_status(did).await { 398 398 Ok(Some(status)) => { ··· 416 416 417 417 pub async fn update_mfa_verified( 418 418 session_repo: &dyn SessionRepository, 419 - did: &crate::types::Did, 419 + did: &tranquil_pds::types::Did, 420 420 ) -> Result<(), tranquil_db_traits::DbError> { 421 421 session_repo.update_mfa_verified(did).await 422 422 } ··· 424 424 pub async fn legacy_mfa_required_response( 425 425 user_repo: &dyn UserRepository, 426 426 session_repo: &dyn SessionRepository, 427 - did: &crate::types::Did, 427 + did: &tranquil_pds::types::Did, 428 428 ) -> Response { 429 429 let methods = get_available_reauth_methods(user_repo, session_repo, did).await; 430 430 (
+7 -7
crates/tranquil-pds/src/api/server/service_auth.rs crates/tranquil-api/src/server/service_auth.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::auth::extractor::{Auth, Permissive}; 3 - use crate::state::AppState; 4 - use crate::types::Did; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::auth::extractor::{Auth, Permissive}; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::types::Did; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, State}, ··· 75 75 match state.user_repo.get_user_info_by_did(&auth.did).await { 76 76 Ok(Some(info)) => match info.key_bytes { 77 77 Some(key_bytes_enc) => { 78 - match crate::config::decrypt_key(&key_bytes_enc, info.encryption_version) { 78 + match tranquil_pds::config::decrypt_key(&key_bytes_enc, info.encryption_version) { 79 79 Ok(key) => key, 80 80 Err(e) => { 81 81 error!(error = ?e, "Failed to decrypt user key for service auth"); ··· 112 112 let lxm_for_token = lxm.map_or("*", |n| n.as_str()); 113 113 114 114 if let Some(method) = lxm { 115 - if let Err(e) = crate::auth::scope_check::check_rpc_scope( 115 + if let Err(e) = tranquil_pds::auth::scope_check::check_rpc_scope( 116 116 &auth.auth_source, 117 117 auth.scope.as_deref(), 118 118 params.aud.as_str(), ··· 167 167 } 168 168 } 169 169 170 - let service_token = match crate::auth::create_service_token( 170 + let service_token = match tranquil_pds::auth::create_service_token( 171 171 &auth.did, 172 172 params.aud.as_str(), 173 173 lxm_for_token,
+51 -51
crates/tranquil-pds/src/api/server/session.rs crates/tranquil-api/src/server/session.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::api::{EmptyResponse, SuccessResponse}; 3 - use crate::auth::{ 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::api::{EmptyResponse, SuccessResponse}; 3 + use tranquil_pds::auth::{ 4 4 Active, Auth, NormalizedLoginIdentifier, Permissive, require_legacy_session_mfa, 5 5 require_reauth_window, 6 6 }; 7 - use crate::rate_limit::{LoginLimit, RateLimited, RefreshSessionLimit}; 8 - use crate::state::AppState; 9 - use crate::types::{AccountState, Did, Handle, PlainPassword}; 7 + use tranquil_pds::rate_limit::{LoginLimit, RateLimited, RefreshSessionLimit}; 8 + use tranquil_pds::state::AppState; 9 + use tranquil_pds::types::{AccountState, Did, Handle, PlainPassword}; 10 10 use axum::{ 11 11 Json, 12 12 extract::State, ··· 93 93 return ApiError::InternalError(None).into_response(); 94 94 } 95 95 }; 96 - let key_bytes = match crate::config::decrypt_key(&row.key_bytes, row.encryption_version) { 96 + let key_bytes = match tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version) { 97 97 Ok(k) => k, 98 98 Err(e) => { 99 99 error!("Failed to decrypt user key: {:?}", e); ··· 173 173 let has_totp = row.totp_enabled; 174 174 let email_2fa_enabled = row.email_2fa_enabled; 175 175 let is_legacy_login = has_totp || email_2fa_enabled; 176 - let twofa_ctx = crate::auth::legacy_2fa::Legacy2faContext { 176 + let twofa_ctx = tranquil_pds::auth::legacy_2fa::Legacy2faContext { 177 177 email_2fa_enabled, 178 178 has_totp, 179 179 allow_legacy_login: row.allow_legacy_login, 180 180 }; 181 - match crate::auth::legacy_2fa::process_legacy_2fa( 181 + match tranquil_pds::auth::legacy_2fa::process_legacy_2fa( 182 182 state.cache.as_ref(), 183 183 &row.did, 184 184 &twofa_ctx, ··· 186 186 ) 187 187 .await 188 188 { 189 - Ok(crate::auth::legacy_2fa::Legacy2faOutcome::NotRequired) => {} 190 - Ok(crate::auth::legacy_2fa::Legacy2faOutcome::Blocked) => { 189 + Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::NotRequired) => {} 190 + Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::Blocked) => { 191 191 warn!("Legacy login blocked for TOTP-enabled account: {}", row.did); 192 192 return ApiError::LegacyLoginBlocked.into_response(); 193 193 } 194 - Ok(crate::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => { 194 + Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => { 195 195 let hostname = &tranquil_config::get().server.hostname; 196 - if let Err(e) = crate::comms::comms_repo::enqueue_2fa_code( 196 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_2fa_code( 197 197 state.user_repo.as_ref(), 198 198 state.infra_repo.as_ref(), 199 199 row.id, ··· 203 203 .await 204 204 { 205 205 error!("Failed to send 2FA code: {:?}", e); 206 - crate::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &row.did).await; 206 + tranquil_pds::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &row.did).await; 207 207 return ApiError::InternalError(Some( 208 208 "Failed to send verification code. Please try again.".into(), 209 209 )) ··· 211 211 } 212 212 return ApiError::AuthFactorTokenRequired.into_response(); 213 213 } 214 - Ok(crate::auth::legacy_2fa::Legacy2faOutcome::Verified) => {} 215 - Err(crate::auth::legacy_2fa::Legacy2faFlowError::Challenge(e)) => { 216 - use crate::auth::legacy_2fa::ChallengeError; 214 + Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::Verified) => {} 215 + Err(tranquil_pds::auth::legacy_2fa::Legacy2faFlowError::Challenge(e)) => { 216 + use tranquil_pds::auth::legacy_2fa::ChallengeError; 217 217 return match e { 218 218 ChallengeError::CacheUnavailable => { 219 219 error!("Cache unavailable for 2FA, blocking legacy login"); ··· 232 232 } 233 233 }; 234 234 } 235 - Err(crate::auth::legacy_2fa::Legacy2faFlowError::Validation(e)) => { 236 - use crate::auth::legacy_2fa::ValidationError; 235 + Err(tranquil_pds::auth::legacy_2fa::Legacy2faFlowError::Validation(e)) => { 236 + use tranquil_pds::auth::legacy_2fa::ValidationError; 237 237 warn!("Invalid 2FA code for {}: {:?}", row.did, e); 238 238 let msg = match e { 239 239 ValidationError::TooManyAttempts => "Too many attempts. Please request a new code.", ··· 248 248 return ApiError::InvalidCode(Some(msg.into())).into_response(); 249 249 } 250 250 } 251 - let access_meta = match crate::auth::create_access_token_with_delegation( 251 + let access_meta = match tranquil_pds::auth::create_access_token_with_delegation( 252 252 &row.did, 253 253 &key_bytes, 254 254 app_password_scopes.as_deref(), ··· 261 261 return ApiError::InternalError(None).into_response(); 262 262 } 263 263 }; 264 - let refresh_meta = match crate::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) { 264 + let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) { 265 265 Ok(m) => m, 266 266 Err(e) => { 267 267 error!("Failed to create refresh token: {:?}", e); ··· 297 297 "Legacy login on TOTP-enabled account - sending notification" 298 298 ); 299 299 let hostname = &tranquil_config::get().server.hostname; 300 - if let Err(e) = crate::comms::comms_repo::enqueue_legacy_login( 300 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_legacy_login( 301 301 state.user_repo.as_ref(), 302 302 state.infra_repo.as_ref(), 303 303 row.id, ··· 406 406 headers: axum::http::HeaderMap, 407 407 _auth: Auth<Active>, 408 408 ) -> Result<Response, ApiError> { 409 - let extracted = crate::auth::extract_auth_token_from_header(crate::util::get_header_str( 409 + let extracted = tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str( 410 410 &headers, 411 411 http::header::AUTHORIZATION, 412 412 )) 413 413 .ok_or(ApiError::AuthenticationRequired)?; 414 - let jti = crate::auth::get_jti_from_token(&extracted.token) 414 + let jti = tranquil_pds::auth::get_jti_from_token(&extracted.token) 415 415 .map_err(|_| ApiError::AuthenticationFailed(None))?; 416 - let did = crate::auth::get_did_from_token(&extracted.token).ok(); 416 + let did = tranquil_pds::auth::get_did_from_token(&extracted.token).ok(); 417 417 match state.session_repo.delete_session_by_access_jti(&jti).await { 418 418 Ok(rows) if rows > 0 => { 419 419 if let Some(did) = did { 420 - let session_cache_key = crate::cache_keys::session_key(&did, &jti); 420 + let session_cache_key = tranquil_pds::cache_keys::session_key(&did, &jti); 421 421 let _ = state.cache.delete(&session_cache_key).await; 422 422 } 423 423 Ok(EmptyResponse::ok().into_response()) ··· 432 432 _rate_limit: RateLimited<RefreshSessionLimit>, 433 433 headers: axum::http::HeaderMap, 434 434 ) -> Response { 435 - let extracted = match crate::auth::extract_auth_token_from_header(crate::util::get_header_str( 435 + let extracted = match tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str( 436 436 &headers, 437 437 http::header::AUTHORIZATION, 438 438 )) { ··· 440 440 None => return ApiError::AuthenticationRequired.into_response(), 441 441 }; 442 442 let refresh_token = extracted.token; 443 - let refresh_jti = match crate::auth::get_jti_from_token(&refresh_token) { 443 + let refresh_jti = match tranquil_pds::auth::get_jti_from_token(&refresh_token) { 444 444 Ok(jti) => jti, 445 445 Err(_) => { 446 446 return ApiError::AuthenticationFailed(Some("Invalid token format".into())) ··· 473 473 return ApiError::InternalError(None).into_response(); 474 474 } 475 475 }; 476 - let key_bytes = match crate::config::decrypt_key( 476 + let key_bytes = match tranquil_pds::config::decrypt_key( 477 477 &session_row.key_bytes, 478 478 Some(session_row.encryption_version), 479 479 ) { ··· 483 483 return ApiError::InternalError(None).into_response(); 484 484 } 485 485 }; 486 - if crate::auth::verify_refresh_token(&refresh_token, &key_bytes).is_err() { 486 + if tranquil_pds::auth::verify_refresh_token(&refresh_token, &key_bytes).is_err() { 487 487 return ApiError::AuthenticationFailed(Some("Invalid refresh token".into())) 488 488 .into_response(); 489 489 } 490 - let new_access_meta = match crate::auth::create_access_token_with_delegation( 490 + let new_access_meta = match tranquil_pds::auth::create_access_token_with_delegation( 491 491 &session_row.did, 492 492 &key_bytes, 493 493 session_row.scope.as_deref(), ··· 501 501 } 502 502 }; 503 503 let new_refresh_meta = 504 - match crate::auth::create_refresh_token_with_metadata(&session_row.did, &key_bytes) { 504 + match tranquil_pds::auth::create_refresh_token_with_metadata(&session_row.did, &key_bytes) { 505 505 Ok(m) => m, 506 506 Err(e) => { 507 507 error!("Failed to create refresh token: {:?}", e); ··· 641 641 }; 642 642 643 643 let normalized_token = 644 - crate::auth::verification_token::normalize_token_input(&input.verification_code); 645 - match crate::auth::verification_token::verify_signup_token( 644 + tranquil_pds::auth::verification_token::normalize_token_input(&input.verification_code); 645 + match tranquil_pds::auth::verification_token::verify_signup_token( 646 646 &normalized_token, 647 647 row.channel, 648 648 &identifier, ··· 657 657 .into_response(); 658 658 } 659 659 } 660 - Err(crate::auth::verification_token::VerifyError::Expired) => { 660 + Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => { 661 661 warn!("Verification code expired for user: {}", input.did); 662 662 return ApiError::ExpiredToken(Some("Verification code has expired".into())) 663 663 .into_response(); ··· 668 668 } 669 669 } 670 670 671 - let key_bytes = match crate::config::decrypt_key(&row.key_bytes, row.encryption_version) { 671 + let key_bytes = match tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version) { 672 672 Ok(k) => k, 673 673 Err(e) => { 674 674 error!("Failed to decrypt user key: {:?}", e); ··· 676 676 } 677 677 }; 678 678 679 - let access_meta = match crate::auth::create_access_token_with_metadata(&row.did, &key_bytes) { 679 + let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&row.did, &key_bytes) { 680 680 Ok(m) => m, 681 681 Err(e) => { 682 682 error!("Failed to create access token: {:?}", e); 683 683 return ApiError::InternalError(None).into_response(); 684 684 } 685 685 }; 686 - let refresh_meta = match crate::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) { 686 + let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) { 687 687 Ok(m) => m, 688 688 Err(e) => { 689 689 error!("Failed to create refresh token: {:?}", e); ··· 718 718 } 719 719 720 720 let hostname = &tranquil_config::get().server.hostname; 721 - if let Err(e) = crate::comms::comms_repo::enqueue_welcome( 721 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome( 722 722 state.user_repo.as_ref(), 723 723 state.infra_repo.as_ref(), 724 724 row.id, ··· 749 749 } 750 750 751 751 pub async fn auto_resend_verification(state: &AppState, did: &Did) -> Option<AutoResendResult> { 752 - let debounce_key = crate::cache_keys::auto_verify_sent_key(did.as_str()); 752 + let debounce_key = tranquil_pds::cache_keys::auto_verify_sent_key(did.as_str()); 753 753 let debounced = state.cache.get(&debounce_key).await.is_some(); 754 754 let row = match state.user_repo.get_resend_verification_by_did(did).await { 755 755 Ok(Some(row)) => row, ··· 789 789 return Some(result); 790 790 } 791 791 let verification_token = 792 - crate::auth::verification_token::generate_signup_token(did, row.channel, &recipient); 792 + tranquil_pds::auth::verification_token::generate_signup_token(did, row.channel, &recipient); 793 793 let formatted_token = 794 - crate::auth::verification_token::format_token_for_display(&verification_token); 794 + tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 795 795 let hostname = &tranquil_config::get().server.hostname; 796 - if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification( 796 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 797 797 state.user_repo.as_ref(), 798 798 state.infra_repo.as_ref(), 799 799 row.id, ··· 856 856 }; 857 857 858 858 let verification_token = 859 - crate::auth::verification_token::generate_signup_token(&input.did, row.channel, &recipient); 859 + tranquil_pds::auth::verification_token::generate_signup_token(&input.did, row.channel, &recipient); 860 860 let formatted_token = 861 - crate::auth::verification_token::format_token_for_display(&verification_token); 861 + tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 862 862 863 863 let hostname = &tranquil_config::get().server.hostname; 864 - if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification( 864 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 865 865 state.user_repo.as_ref(), 866 866 state.infra_repo.as_ref(), 867 867 row.id, ··· 910 910 .get("authorization") 911 911 .and_then(|v| v.to_str().ok()) 912 912 .and_then(|v| v.strip_prefix("Bearer ")) 913 - .and_then(|token| crate::auth::get_jti_from_token(token).ok()); 913 + .and_then(|token| tranquil_pds::auth::get_jti_from_token(token).ok()); 914 914 915 915 let jwt_rows = state 916 916 .session_repo ··· 990 990 .delete_session_by_id(session_id) 991 991 .await 992 992 .log_db_err("deleting session")?; 993 - let cache_key = crate::cache_keys::session_key(&auth.did, &access_jti); 993 + let cache_key = tranquil_pds::cache_keys::session_key(&auth.did, &access_jti); 994 994 if let Err(e) = state.cache.delete(&cache_key).await { 995 995 warn!("Failed to invalidate session cache: {:?}", e); 996 996 } ··· 1020 1020 headers: HeaderMap, 1021 1021 auth: Auth<Active>, 1022 1022 ) -> Result<Response, ApiError> { 1023 - let jti = crate::auth::extract_auth_token_from_header( 1023 + let jti = tranquil_pds::auth::extract_auth_token_from_header( 1024 1024 headers.get("authorization").and_then(|v| v.to_str().ok()), 1025 1025 ) 1026 - .and_then(|extracted| crate::auth::get_jti_from_token(&extracted.token).ok()) 1026 + .and_then(|extracted| tranquil_pds::auth::get_jti_from_token(&extracted.token).ok()) 1027 1027 .ok_or(ApiError::InvalidToken(None))?; 1028 1028 1029 1029 if auth.is_oauth() { ··· 1119 1119 .into_response()) 1120 1120 } 1121 1121 1122 - use crate::comms::VALID_LOCALES; 1122 + use tranquil_pds::comms::VALID_LOCALES; 1123 1123 1124 1124 #[derive(Deserialize)] 1125 1125 #[serde(rename_all = "camelCase")]
+3 -3
crates/tranquil-pds/src/api/server/signing_key.rs crates/tranquil-api/src/server/signing_key.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::state::AppState; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::state::AppState; 3 3 use axum::{ 4 4 Json, 5 5 extract::State, ··· 25 25 26 26 #[derive(Deserialize)] 27 27 pub struct ReserveSigningKeyInput { 28 - pub did: Option<crate::types::Did>, 28 + pub did: Option<tranquil_pds::types::Did>, 29 29 } 30 30 31 31 #[derive(Serialize)]
+10 -10
crates/tranquil-pds/src/api/server/totp.rs crates/tranquil-api/src/server/totp.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 3 - use crate::auth::{ 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 + use tranquil_pds::auth::{ 4 4 Active, Auth, decrypt_totp_secret, encrypt_totp_secret, generate_backup_codes, 5 5 generate_qr_png_base64, generate_totp_secret, generate_totp_uri, hash_backup_code, 6 6 is_backup_code_format, require_legacy_session_mfa, verify_backup_code, verify_password_mfa, 7 7 verify_totp_code, verify_totp_mfa, 8 8 }; 9 - use crate::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message}; 10 - use crate::state::AppState; 11 - use crate::types::PlainPassword; 9 + use tranquil_pds::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message}; 10 + use tranquil_pds::state::AppState; 11 + use tranquil_pds::types::PlainPassword; 12 12 use axum::{ 13 13 Json, 14 14 extract::State, ··· 186 186 .await 187 187 .log_db_err("deleting TOTP")?; 188 188 189 - crate::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &auth.did).await; 189 + tranquil_pds::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &auth.did).await; 190 190 191 191 info!(did = %session_mfa.did(), "TOTP disabled (verified via {} and {})", password_mfa.method(), totp_mfa.method()); 192 192 ··· 280 280 281 281 async fn verify_backup_code_for_user( 282 282 state: &AppState, 283 - did: &crate::types::Did, 283 + did: &tranquil_pds::types::Did, 284 284 code: &str, 285 285 ) -> bool { 286 286 let code = code.trim().to_uppercase(); ··· 308 308 309 309 pub async fn verify_totp_or_backup_for_user( 310 310 state: &AppState, 311 - did: &crate::types::Did, 311 + did: &tranquil_pds::types::Did, 312 312 code: &str, 313 313 ) -> bool { 314 314 use tranquil_db_traits::TotpRecordState; ··· 340 340 false 341 341 } 342 342 343 - pub async fn has_totp_enabled(state: &AppState, did: &crate::types::Did) -> bool { 343 + pub async fn has_totp_enabled(state: &AppState, did: &tranquil_pds::types::Did) -> bool { 344 344 state.user_repo.has_totp_enabled(did).await.unwrap_or(false) 345 345 }
+4 -4
crates/tranquil-pds/src/api/server/trusted_devices.rs crates/tranquil-api/src/server/trusted_devices.rs
··· 1 - use crate::api::SuccessResponse; 2 - use crate::api::error::{ApiError, DbResultExt}; 1 + use tranquil_pds::api::SuccessResponse; 2 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 3 3 use axum::{ 4 4 Json, 5 5 extract::State, ··· 11 11 use tranquil_db_traits::OAuthRepository; 12 12 use tranquil_types::DeviceId; 13 13 14 - use crate::auth::{Active, Auth}; 15 - use crate::state::AppState; 14 + use tranquil_pds::auth::{Active, Auth}; 15 + use tranquil_pds::state::AppState; 16 16 17 17 const TRUST_DURATION_DAYS: i64 = 30; 18 18
+6 -6
crates/tranquil-pds/src/api/server/verify_email.rs crates/tranquil-api/src/server/verify_email.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::types::Did; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::types::Did; 3 3 use axum::{Json, extract::State}; 4 4 use serde::{Deserialize, Serialize}; 5 5 use tracing::{info, warn}; 6 6 7 - use crate::state::AppState; 7 + use tranquil_pds::state::AppState; 8 8 9 9 #[derive(Deserialize)] 10 10 #[serde(rename_all = "camelCase")] ··· 71 71 } 72 72 73 73 let hostname = &tranquil_config::get().server.hostname; 74 - let token = crate::auth::verification_token::generate_migration_token(&user.did, &email); 75 - let formatted_token = crate::auth::verification_token::format_token_for_display(&token); 74 + let token = tranquil_pds::auth::verification_token::generate_migration_token(&user.did, &email); 75 + let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token); 76 76 77 - if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification( 77 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification( 78 78 state.user_repo.as_ref(), 79 79 state.infra_repo.as_ref(), 80 80 user.id,
+6 -6
crates/tranquil-pds/src/api/server/verify_token.rs crates/tranquil-api/src/server/verify_token.rs
··· 1 - use crate::api::error::{ApiError, DbResultExt}; 2 - use crate::comms::comms_repo; 3 - use crate::types::Did; 1 + use tranquil_pds::api::error::{ApiError, DbResultExt}; 2 + use tranquil_pds::comms::comms_repo; 3 + use tranquil_pds::types::Did; 4 4 use axum::{Json, extract::State}; 5 5 use serde::{Deserialize, Serialize}; 6 6 use tracing::{info, warn}; 7 7 8 - use crate::auth::verification_token::{ 8 + use tranquil_pds::auth::verification_token::{ 9 9 VerificationPurpose, normalize_token_input, verify_token_signature, 10 10 }; 11 - use crate::state::AppState; 11 + use tranquil_pds::state::AppState; 12 12 use tranquil_db_traits::CommsChannel; 13 13 14 14 #[derive(Deserialize, Clone)] ··· 46 46 ApiError::from(e) 47 47 })?; 48 48 49 - let expected_hash = crate::auth::verification_token::hash_identifier(&identifier); 49 + let expected_hash = tranquil_pds::auth::verification_token::hash_identifier(&identifier); 50 50 if token_data.identifier_hash != expected_hash { 51 51 return Err(ApiError::IdentifierMismatch); 52 52 }
+2 -2
crates/tranquil-pds/src/api/telegram_webhook.rs crates/tranquil-api/src/telegram_webhook.rs
··· 6 6 use serde::Deserialize; 7 7 use tracing::{debug, info, warn}; 8 8 9 - use crate::comms::comms_repo; 10 - use crate::state::AppState; 9 + use tranquil_pds::comms::comms_repo; 10 + use tranquil_pds::state::AppState; 11 11 12 12 #[derive(Deserialize)] 13 13 struct TelegramUpdate {
+4 -4
crates/tranquil-pds/src/api/temp.rs crates/tranquil-api/src/temp.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::auth::{Active, Auth, Permissive}; 3 - use crate::state::AppState; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::auth::{Active, Auth, Permissive}; 3 + use tranquil_pds::state::AppState; 4 4 use axum::{ 5 5 Json, 6 6 extract::State, ··· 57 57 58 58 for part in scope_parts { 59 59 if let Some(cid_str) = part.strip_prefix("ref:") { 60 - let cache_key = crate::cache_keys::scope_ref_key(cid_str); 60 + let cache_key = tranquil_pds::cache_keys::scope_ref_key(cid_str); 61 61 if let Some(cached) = state.cache.get(&cache_key).await { 62 62 for s in cached.split_whitespace() { 63 63 if !resolved_scopes.contains(&s.to_string()) {
+4 -4
crates/tranquil-pds/src/api/verification.rs crates/tranquil-api/src/verification.rs
··· 1 - use crate::api::SuccessResponse; 2 - use crate::state::AppState; 1 + use tranquil_pds::api::SuccessResponse; 2 + use tranquil_pds::state::AppState; 3 3 use axum::{ 4 4 Json, 5 5 extract::State, ··· 19 19 State(state): State<AppState>, 20 20 Json(input): Json<ConfirmChannelVerificationInput>, 21 21 ) -> Response { 22 - let token_input = crate::api::server::VerifyTokenInput { 22 + let token_input = crate::server::VerifyTokenInput { 23 23 token: input.code, 24 24 identifier: input.identifier, 25 25 }; 26 26 27 - match crate::api::server::verify_token_internal(&state, token_input).await { 27 + match crate::server::verify_token_internal(&state, token_input).await { 28 28 Ok(_output) => SuccessResponse::ok().into_response(), 29 29 Err(e) => e.into_response(), 30 30 }
+35
crates/tranquil-oauth-server/Cargo.toml
··· 1 + [package] 2 + name = "tranquil-oauth-server" 3 + version.workspace = true 4 + edition.workspace = true 5 + license.workspace = true 6 + 7 + [dependencies] 8 + tranquil-pds = { workspace = true } 9 + tranquil-api = { workspace = true } 10 + tranquil-types = { workspace = true } 11 + tranquil-config = { workspace = true } 12 + tranquil-crypto = { workspace = true } 13 + tranquil-db-traits = { workspace = true } 14 + 15 + axum = { workspace = true } 16 + base64 = { workspace = true } 17 + bcrypt = { workspace = true } 18 + chrono = { workspace = true } 19 + cid = { workspace = true } 20 + hmac = { workspace = true } 21 + http = { workspace = true } 22 + jacquard-common = { workspace = true } 23 + jacquard-repo = { workspace = true } 24 + k256 = { workspace = true } 25 + rand = { workspace = true } 26 + serde = { workspace = true } 27 + serde_json = { workspace = true } 28 + serde_urlencoded = { workspace = true } 29 + sha2 = { workspace = true } 30 + subtle = { workspace = true } 31 + tokio = { workspace = true } 32 + tracing = { workspace = true } 33 + urlencoding = { workspace = true } 34 + uuid = { workspace = true } 35 + webauthn-rs = { workspace = true }
+33 -33
crates/tranquil-pds/src/oauth/endpoints/authorize.rs crates/tranquil-oauth-server/src/endpoints/authorize.rs
··· 1 - use crate::auth::{BareLoginIdentifier, NormalizedLoginIdentifier}; 2 - use crate::comms::comms_repo::enqueue_2fa_code; 3 - use crate::oauth::{ 1 + use tranquil_pds::auth::{BareLoginIdentifier, NormalizedLoginIdentifier}; 2 + use tranquil_pds::comms::comms_repo::enqueue_2fa_code; 3 + use tranquil_pds::oauth::{ 4 4 AuthFlow, ClientMetadataCache, Code, DeviceData, DeviceId, OAuthError, Prompt, SessionId, 5 5 db::should_show_consent, scopes::expand_include_scopes, 6 6 }; 7 - use crate::rate_limit::{ 7 + use tranquil_pds::rate_limit::{ 8 8 OAuthAuthorizeLimit, OAuthRateLimited, OAuthRegisterCompleteLimit, TotpVerifyLimit, 9 9 check_user_rate_limit, 10 10 }; 11 - use crate::state::AppState; 12 - use crate::types::{Did, Handle, PlainPassword}; 13 - use crate::util::extract_client_ip; 11 + use tranquil_pds::state::AppState; 12 + use tranquil_pds::types::{Did, Handle, PlainPassword}; 13 + use tranquil_pds::util::extract_client_ip; 14 14 use axum::{ 15 15 Json, 16 16 extract::{Query, State}, ··· 95 95 cookie_str.split(';').map(|c| c.trim()).find_map(|cookie| { 96 96 cookie 97 97 .strip_prefix(&format!("{}=", DEVICE_COOKIE_NAME)) 98 - .and_then(|value| crate::config::AuthConfig::get().verify_device_cookie(value)) 98 + .and_then(|value| tranquil_pds::config::AuthConfig::get().verify_device_cookie(value)) 99 99 .map(tranquil_types::DeviceId::new) 100 100 }) 101 101 }) ··· 109 109 } 110 110 111 111 fn make_device_cookie(device_id: &tranquil_types::DeviceId) -> String { 112 - let signed_value = crate::config::AuthConfig::get().sign_device_cookie(device_id.as_str()); 112 + let signed_value = tranquil_pds::config::AuthConfig::get().sign_device_cookie(device_id.as_str()); 113 113 format!( 114 114 "{}={}; Path=/oauth; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000", 115 115 DEVICE_COOKIE_NAME, signed_value ··· 343 343 .oauth_repo 344 344 .get_authorization_request(&request_id_json) 345 345 .await 346 - .map_err(crate::oauth::db_err_to_oauth)? 346 + .map_err(tranquil_pds::oauth::db_err_to_oauth)? 347 347 .ok_or_else(|| OAuthError::InvalidRequest("Invalid or expired request_uri".to_string()))?; 348 348 if request_data.expires_at < Utc::now() { 349 349 let _ = state ··· 626 626 } 627 627 let is_verified = user.channel_verification.has_any_verified(); 628 628 if !is_verified { 629 - let resend_info = crate::api::server::auto_resend_verification(&state, &user.did).await; 629 + let resend_info = tranquil_api::server::auto_resend_verification(&state, &user.did).await; 630 630 let handle = resend_info 631 631 .as_ref() 632 632 .map(|r| r.handle.to_string()) ··· 653 653 url_encode("account_not_verified") 654 654 )); 655 655 } 656 - let has_totp = crate::api::server::has_totp_enabled(&state, &user.did).await; 656 + let has_totp = tranquil_api::server::has_totp_enabled(&state, &user.did).await; 657 657 if has_totp { 658 658 let device_cookie = extract_device_cookie(&headers); 659 659 let device_is_trusted = if let Some(ref dev_id) = device_cookie { 660 - crate::api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &user.did) 660 + tranquil_api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &user.did) 661 661 .await 662 662 } else { 663 663 false ··· 665 665 666 666 if device_is_trusted { 667 667 if let Some(ref dev_id) = device_cookie { 668 - let _ = crate::api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id) 668 + let _ = tranquil_api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id) 669 669 .await; 670 670 } 671 671 } else { ··· 978 978 }; 979 979 let is_verified = user.channel_verification.has_any_verified(); 980 980 if !is_verified { 981 - let resend_info = crate::api::server::auto_resend_verification(&state, &did).await; 981 + let resend_info = tranquil_api::server::auto_resend_verification(&state, &did).await; 982 982 return ( 983 983 StatusCode::FORBIDDEN, 984 984 Json(serde_json::json!({ ··· 991 991 ) 992 992 .into_response(); 993 993 } 994 - let has_totp = crate::api::server::has_totp_enabled(&state, &did).await; 994 + let has_totp = tranquil_api::server::has_totp_enabled(&state, &did).await; 995 995 let select_early_device_typed = device_id.clone(); 996 996 if has_totp { 997 997 let device_is_trusted = 998 - crate::api::server::is_device_trusted(state.oauth_repo.as_ref(), &device_id, &did) 998 + tranquil_api::server::is_device_trusted(state.oauth_repo.as_ref(), &device_id, &did) 999 999 .await; 1000 1000 if !device_is_trusted { 1001 1001 if state ··· 1016 1016 .into_response(); 1017 1017 } 1018 1018 let _ = 1019 - crate::api::server::extend_device_trust(state.oauth_repo.as_ref(), &device_id).await; 1019 + tranquil_api::server::extend_device_trust(state.oauth_repo.as_ref(), &device_id).await; 1020 1020 } 1021 1021 if user.two_factor_enabled { 1022 1022 let _ = state ··· 1413 1413 }; 1414 1414 1415 1415 let effective_scope_str = if let Some(ref grant) = delegation_grant { 1416 - crate::delegation::intersect_scopes(requested_scope_str, grant.granted_scopes.as_str()) 1416 + tranquil_pds::delegation::intersect_scopes(requested_scope_str, grant.granted_scopes.as_str()) 1417 1417 } else { 1418 1418 requested_scope_str.to_string() 1419 1419 }; ··· 1445 1445 .iter() 1446 1446 .map(|scope| { 1447 1447 let (category, required, description, display_name) = if let Some(def) = 1448 - crate::oauth::scopes::SCOPE_DEFINITIONS.get(*scope) 1448 + tranquil_pds::oauth::scopes::SCOPE_DEFINITIONS.get(*scope) 1449 1449 { 1450 1450 let desc = if *scope == "atproto" && has_granular_scopes { 1451 1451 "AT Protocol baseline scope (permissions determined by selected options below)" ··· 1510 1510 .map(|h| h.to_string()); 1511 1511 1512 1512 let level = if let Some(ref grant) = delegation_grant { 1513 - let preset = crate::delegation::SCOPE_PRESETS 1513 + let preset = tranquil_pds::delegation::SCOPE_PRESETS 1514 1514 .iter() 1515 1515 .find(|p| p.scopes == grant.granted_scopes.as_str()); 1516 1516 preset ··· 1622 1622 }; 1623 1623 1624 1624 let effective_scope_str = if let Some(ref grant) = delegation_grant { 1625 - crate::delegation::intersect_scopes(original_scope_str, grant.granted_scopes.as_str()) 1625 + tranquil_pds::delegation::intersect_scopes(original_scope_str, grant.granted_scopes.as_str()) 1626 1626 } else { 1627 1627 original_scope_str.to_string() 1628 1628 }; ··· 1955 1955 ); 1956 1956 } 1957 1957 }; 1958 - if !crate::api::server::has_totp_enabled(&state, &did).await { 1958 + if !tranquil_api::server::has_totp_enabled(&state, &did).await { 1959 1959 return json_error( 1960 1960 StatusCode::BAD_REQUEST, 1961 1961 "invalid_request", ··· 1973 1973 } 1974 1974 }; 1975 1975 let totp_valid = 1976 - crate::api::server::verify_totp_or_backup_for_user(&state, &did, &form.code).await; 1976 + tranquil_api::server::verify_totp_or_backup_for_user(&state, &did, &form.code).await; 1977 1977 if !totp_valid { 1978 1978 return json_error( 1979 1979 StatusCode::FORBIDDEN, ··· 2011 2011 .oauth_repo 2012 2012 .upsert_account_device(&did, &trust_device_id) 2013 2013 .await; 2014 - let _ = crate::api::server::trust_device(state.oauth_repo.as_ref(), &trust_device_id).await; 2014 + let _ = tranquil_api::server::trust_device(state.oauth_repo.as_ref(), &trust_device_id).await; 2015 2015 } 2016 2016 let requested_scope_str = request_data 2017 2017 .parameters ··· 2110 2110 .await; 2111 2111 2112 2112 let has_passkeys = match user { 2113 - Ok(Some(u)) => crate::api::server::has_passkeys_for_user(&state, &u.did).await, 2113 + Ok(Some(u)) => tranquil_api::server::has_passkeys_for_user(&state, &u.did).await, 2114 2114 _ => false, 2115 2115 }; 2116 2116 ··· 2149 2149 Option<String>, 2150 2150 ) = match user { 2151 2151 Ok(Some(u)) => { 2152 - let passkeys = crate::api::server::has_passkeys_for_user(&state, &u.did).await; 2153 - let totp = crate::api::server::has_totp_enabled(&state, &u.did).await; 2152 + let passkeys = tranquil_api::server::has_passkeys_for_user(&state, &u.did).await; 2153 + let totp = tranquil_api::server::has_totp_enabled(&state, &u.did).await; 2154 2154 let has_pw = u.password_hash.is_some(); 2155 2155 let has_controllers = state 2156 2156 .delegation_repo ··· 2297 2297 let is_verified = user.channel_verification.has_any_verified(); 2298 2298 2299 2299 if !is_verified { 2300 - let resend_info = crate::api::server::auto_resend_verification(&state, &user.did).await; 2300 + let resend_info = tranquil_api::server::auto_resend_verification(&state, &user.did).await; 2301 2301 return ( 2302 2302 StatusCode::FORBIDDEN, 2303 2303 Json(serde_json::json!({ ··· 3125 3125 if has_totp { 3126 3126 let device_cookie = extract_device_cookie(&headers); 3127 3127 let device_is_trusted = if let Some(ref dev_id) = device_cookie { 3128 - crate::api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &did).await 3128 + tranquil_api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &did).await 3129 3129 } else { 3130 3130 false 3131 3131 }; 3132 3132 3133 3133 if device_is_trusted { 3134 3134 if let Some(ref dev_id) = device_cookie { 3135 - let _ = crate::api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id) 3135 + let _ = tranquil_api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id) 3136 3136 .await; 3137 3137 } 3138 3138 } else { ··· 3395 3395 }; 3396 3396 3397 3397 if !is_verified { 3398 - let resend_info = crate::api::server::auto_resend_verification(&state, &did).await; 3398 + let resend_info = tranquil_api::server::auto_resend_verification(&state, &did).await; 3399 3399 return ( 3400 3400 StatusCode::FORBIDDEN, 3401 3401 Json(serde_json::json!({ ··· 3503 3503 pub async fn establish_session( 3504 3504 State(state): State<AppState>, 3505 3505 headers: HeaderMap, 3506 - auth: crate::auth::Auth<crate::auth::Active>, 3506 + auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>, 3507 3507 ) -> Response { 3508 3508 let did = &auth.did; 3509 3509
+8 -8
crates/tranquil-pds/src/oauth/endpoints/delegation.rs crates/tranquil-oauth-server/src/endpoints/delegation.rs
··· 1 - use crate::auth::{Active, Auth}; 2 - use crate::delegation::DelegationActionType; 3 - use crate::rate_limit::{LoginLimit, OAuthRateLimited, TotpVerifyLimit}; 4 - use crate::state::AppState; 5 - use crate::types::PlainPassword; 6 - use crate::util::extract_client_ip; 1 + use tranquil_pds::auth::{Active, Auth}; 2 + use tranquil_pds::delegation::DelegationActionType; 3 + use tranquil_pds::rate_limit::{LoginLimit, OAuthRateLimited, TotpVerifyLimit}; 4 + use tranquil_pds::state::AppState; 5 + use tranquil_pds::types::PlainPassword; 6 + use tranquil_pds::util::extract_client_ip; 7 7 use axum::{ 8 8 Json, 9 9 extract::State, ··· 220 220 .into_response(); 221 221 } 222 222 223 - let has_totp = crate::api::server::has_totp_enabled(&state, &controller_did).await; 223 + let has_totp = tranquil_api::server::has_totp_enabled(&state, &controller_did).await; 224 224 if has_totp { 225 225 return Json(DelegationAuthResponse { 226 226 success: true, ··· 377 377 }; 378 378 379 379 let totp_valid = 380 - crate::api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code) 380 + tranquil_api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code) 381 381 .await; 382 382 if !totp_valid { 383 383 return Json(DelegationAuthResponse {
+4 -4
crates/tranquil-pds/src/oauth/endpoints/metadata.rs crates/tranquil-oauth-server/src/endpoints/metadata.rs
··· 1 1 use std::fmt::Debug; 2 2 3 - use crate::oauth::jwks::{JwkSet, create_jwk_set}; 4 - use crate::state::AppState; 3 + use crate::jwks::{JwkSet, create_jwk_set}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::{Json, extract::State}; 6 6 use http::{HeaderName, header}; 7 7 use serde::{Deserialize, Serialize}; ··· 129 129 } 130 130 131 131 pub async fn oauth_jwks(State(_state): State<AppState>) -> Json<JwkSet> { 132 - use crate::config::AuthConfig; 133 - use crate::oauth::jwks::Jwk; 132 + use tranquil_pds::config::AuthConfig; 133 + use crate::jwks::Jwk; 134 134 let config = AuthConfig::get(); 135 135 let server_key = Jwk { 136 136 kty: "EC".to_string(),
crates/tranquil-pds/src/oauth/endpoints/mod.rs crates/tranquil-oauth-server/src/endpoints/mod.rs
+5 -5
crates/tranquil-pds/src/oauth/endpoints/par.rs crates/tranquil-oauth-server/src/endpoints/par.rs
··· 1 - use crate::oauth::{ 1 + use tranquil_pds::oauth::{ 2 2 AuthorizationRequestParameters, ClientAuth, ClientMetadataCache, CodeChallengeMethod, 3 3 OAuthError, Prompt, RequestData, RequestId, ResponseMode, ResponseType, 4 4 scopes::{ParsedScope, parse_scope}, 5 5 }; 6 - use crate::rate_limit::{OAuthParLimit, OAuthRateLimited}; 7 - use crate::state::AppState; 6 + use tranquil_pds::rate_limit::{OAuthParLimit, OAuthRateLimited}; 7 + use tranquil_pds::state::AppState; 8 8 use axum::body::Bytes; 9 9 use axum::{Json, extract::State, http::HeaderMap}; 10 10 use chrono::{Duration, Utc}; ··· 118 118 .oauth_repo 119 119 .create_authorization_request(&request_id_typed, &request_data) 120 120 .await 121 - .map_err(crate::oauth::db_err_to_oauth)?; 121 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 122 122 tokio::spawn({ 123 123 let oauth_repo = state.oauth_repo.clone(); 124 124 async move { ··· 159 159 160 160 fn validate_scope( 161 161 requested_scope: &Option<String>, 162 - client_metadata: &crate::oauth::ClientMetadata, 162 + client_metadata: &tranquil_pds::oauth::ClientMetadata, 163 163 ) -> Result<Option<String>, OAuthError> { 164 164 let scope_str = match requested_scope { 165 165 Some(s) if !s.is_empty() => s,
+11 -11
crates/tranquil-pds/src/oauth/endpoints/token/grants.rs crates/tranquil-oauth-server/src/endpoints/token/grants.rs
··· 2 2 use super::types::{ 3 3 RequestClientAuth, TokenGrant, TokenResponse, TokenType, ValidatedTokenRequest, 4 4 }; 5 - use crate::config::AuthConfig; 6 - use crate::delegation::intersect_scopes; 7 - use crate::oauth::{ 5 + use tranquil_pds::config::AuthConfig; 6 + use tranquil_pds::delegation::intersect_scopes; 7 + use tranquil_pds::oauth::{ 8 8 AuthFlow, ClientAuth, ClientMetadataCache, DPoPVerifier, OAuthError, RefreshToken, TokenData, 9 9 TokenId, 10 10 db::{enforce_token_limit_for_user, lookup_refresh_token}, 11 11 scopes::expand_include_scopes, 12 12 verify_client_auth, 13 13 }; 14 - use crate::state::AppState; 14 + use tranquil_pds::state::AppState; 15 15 use axum::Json; 16 16 use axum::http::{HeaderMap, Method}; 17 17 use chrono::{Duration, Utc}; ··· 50 50 .oauth_repo 51 51 .consume_authorization_request_by_code(&auth_code) 52 52 .await 53 - .map_err(crate::oauth::db_err_to_oauth)? 53 + .map_err(tranquil_pds::oauth::db_err_to_oauth)? 54 54 .ok_or_else(|| OAuthError::InvalidGrant("Invalid or expired code".to_string()))?; 55 55 56 56 let flow = AuthFlow::from_request_data(auth_request) ··· 107 107 .oauth_repo 108 108 .check_and_record_dpop_jti(&result.jti) 109 109 .await 110 - .map_err(crate::oauth::db_err_to_oauth)? 110 + .map_err(tranquil_pds::oauth::db_err_to_oauth)? 111 111 { 112 112 return Err(OAuthError::InvalidDpopProof( 113 113 "DPoP proof has already been used".to_string(), ··· 201 201 .oauth_repo 202 202 .create_token(&token_data) 203 203 .await 204 - .map_err(crate::oauth::db_err_to_oauth)?; 204 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 205 205 tracing::info!( 206 206 did = %did, 207 207 token_id = %token_id.0, ··· 321 321 .oauth_repo 322 322 .delete_token_family(original_token_id) 323 323 .await 324 - .map_err(crate::oauth::db_err_to_oauth)?; 324 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 325 325 return Err(OAuthError::InvalidGrant( 326 326 "Refresh token reuse detected, token family revoked".to_string(), 327 327 )); ··· 332 332 .oauth_repo 333 333 .delete_token_family(db_id) 334 334 .await 335 - .map_err(crate::oauth::db_err_to_oauth)?; 335 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 336 336 return Err(OAuthError::InvalidGrant( 337 337 "Refresh token has expired".to_string(), 338 338 )); ··· 354 354 .oauth_repo 355 355 .check_and_record_dpop_jti(&result.jti) 356 356 .await 357 - .map_err(crate::oauth::db_err_to_oauth)? 357 + .map_err(tranquil_pds::oauth::db_err_to_oauth)? 358 358 { 359 359 return Err(OAuthError::InvalidDpopProof( 360 360 "DPoP proof has already been used".to_string(), ··· 387 387 .oauth_repo 388 388 .rotate_token(db_id, &new_refresh_typed, new_expires_at) 389 389 .await 390 - .map_err(crate::oauth::db_err_to_oauth)?; 390 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 391 391 tracing::info!( 392 392 did = %token_data.did, 393 393 new_expires_at = %new_expires_at,
+2 -2
crates/tranquil-pds/src/oauth/endpoints/token/helpers.rs crates/tranquil-oauth-server/src/endpoints/token/helpers.rs
··· 1 - use crate::config::AuthConfig; 2 - use crate::oauth::OAuthError; 1 + use tranquil_pds::config::AuthConfig; 2 + use tranquil_pds::oauth::OAuthError; 3 3 use base64::Engine; 4 4 use base64::engine::general_purpose::URL_SAFE_NO_PAD; 5 5 use chrono::Utc;
+6 -6
crates/tranquil-pds/src/oauth/endpoints/token/introspect.rs crates/tranquil-oauth-server/src/endpoints/token/introspect.rs
··· 1 1 use super::helpers::extract_token_claims; 2 - use crate::oauth::OAuthError; 3 - use crate::rate_limit::{OAuthIntrospectLimit, OAuthRateLimited}; 4 - use crate::state::AppState; 2 + use tranquil_pds::oauth::OAuthError; 3 + use tranquil_pds::rate_limit::{OAuthIntrospectLimit, OAuthRateLimited}; 4 + use tranquil_pds::state::AppState; 5 5 use axum::extract::State; 6 6 use axum::http::StatusCode; 7 7 use axum::{Form, Json}; ··· 27 27 .oauth_repo 28 28 .get_token_by_refresh_token(&refresh_token) 29 29 .await 30 - .map_err(crate::oauth::db_err_to_oauth)? 30 + .map_err(tranquil_pds::oauth::db_err_to_oauth)? 31 31 { 32 32 state 33 33 .oauth_repo 34 34 .delete_token_family(db_id) 35 35 .await 36 - .map_err(crate::oauth::db_err_to_oauth)?; 36 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 37 37 } else { 38 38 let token_id = TokenId::from(token.clone()); 39 39 state 40 40 .oauth_repo 41 41 .delete_token(&token_id) 42 42 .await 43 - .map_err(crate::oauth::db_err_to_oauth)?; 43 + .map_err(tranquil_pds::oauth::db_err_to_oauth)?; 44 44 } 45 45 } 46 46 Ok(StatusCode::OK)
+4 -4
crates/tranquil-pds/src/oauth/endpoints/token/mod.rs crates/tranquil-oauth-server/src/endpoints/token/mod.rs
··· 3 3 mod introspect; 4 4 mod types; 5 5 6 - use crate::oauth::OAuthError; 7 - use crate::rate_limit::{OAuthRateLimited, OAuthTokenLimit}; 8 - use crate::state::AppState; 6 + use tranquil_pds::oauth::OAuthError; 7 + use tranquil_pds::rate_limit::{OAuthRateLimited, OAuthTokenLimit}; 8 + use tranquil_pds::state::AppState; 9 9 use axum::body::Bytes; 10 10 use axum::{Json, extract::State, http::HeaderMap}; 11 11 ··· 41 41 )); 42 42 }; 43 43 let dpop_proof = headers 44 - .get(crate::util::HEADER_DPOP) 44 + .get(tranquil_pds::util::HEADER_DPOP) 45 45 .and_then(|v| v.to_str().ok()) 46 46 .map(|s| s.to_string()); 47 47 let validated = request.validate()?;
+1 -1
crates/tranquil-pds/src/oauth/endpoints/token/types.rs crates/tranquil-oauth-server/src/endpoints/token/types.rs
··· 1 - use crate::oauth::OAuthError; 1 + use tranquil_pds::oauth::OAuthError; 2 2 use serde::{Deserialize, Serialize}; 3 3 4 4 #[derive(Debug, Clone, PartialEq, Eq)]
crates/tranquil-pds/src/oauth/jwks.rs crates/tranquil-oauth-server/src/jwks.rs
+116
crates/tranquil-oauth-server/src/lib.rs
··· 1 + pub mod endpoints; 2 + pub mod jwks; 3 + pub mod sso_endpoints; 4 + 5 + use tranquil_pds::state::AppState; 6 + 7 + pub fn oauth_routes() -> axum::Router<AppState> { 8 + use axum::{middleware, routing::{get, post}}; 9 + 10 + axum::Router::new() 11 + .route("/jwks", get(endpoints::oauth_jwks)) 12 + .route("/par", post(endpoints::pushed_authorization_request)) 13 + .route("/authorize", get(endpoints::authorize_get)) 14 + .route("/authorize", post(endpoints::authorize_post)) 15 + .route( 16 + "/authorize/accounts", 17 + get(endpoints::authorize_accounts), 18 + ) 19 + .route( 20 + "/authorize/select", 21 + post(endpoints::authorize_select), 22 + ) 23 + .route("/authorize/2fa", get(endpoints::authorize_2fa_get)) 24 + .route("/authorize/2fa", post(endpoints::authorize_2fa_post)) 25 + .route( 26 + "/authorize/passkey", 27 + get(endpoints::authorize_passkey_start), 28 + ) 29 + .route( 30 + "/authorize/passkey", 31 + post(endpoints::authorize_passkey_finish), 32 + ) 33 + .route( 34 + "/passkey/check", 35 + get(endpoints::check_user_has_passkeys), 36 + ) 37 + .route( 38 + "/security-status", 39 + get(endpoints::check_user_security_status), 40 + ) 41 + .route("/passkey/start", post(endpoints::passkey_start)) 42 + .route("/passkey/finish", post(endpoints::passkey_finish)) 43 + .route("/authorize/deny", post(endpoints::authorize_deny)) 44 + .route( 45 + "/register/complete", 46 + post(endpoints::register_complete), 47 + ) 48 + .route( 49 + "/establish-session", 50 + post(endpoints::establish_session), 51 + ) 52 + .route("/authorize/consent", get(endpoints::consent_get)) 53 + .route("/authorize/consent", post(endpoints::consent_post)) 54 + .route("/authorize/renew", post(endpoints::authorize_renew)) 55 + .route( 56 + "/authorize/redirect", 57 + get(endpoints::authorize_redirect), 58 + ) 59 + .route("/delegation/auth", post(endpoints::delegation_auth)) 60 + .route( 61 + "/delegation/auth-token", 62 + post(endpoints::delegation_auth_token), 63 + ) 64 + .route( 65 + "/delegation/totp", 66 + post(endpoints::delegation_totp_verify), 67 + ) 68 + .route("/token", post(endpoints::token_endpoint)) 69 + .route("/revoke", post(endpoints::revoke_token)) 70 + .route("/introspect", post(endpoints::introspect_token)) 71 + .route("/sso/providers", get(sso_endpoints::get_sso_providers)) 72 + .route("/sso/initiate", post(sso_endpoints::sso_initiate)) 73 + .route( 74 + "/sso/callback", 75 + get(sso_endpoints::sso_callback).post(sso_endpoints::sso_callback_post), 76 + ) 77 + .route("/sso/linked", get(sso_endpoints::get_linked_accounts)) 78 + .route("/sso/unlink", post(sso_endpoints::unlink_account)) 79 + .route( 80 + "/sso/pending-registration", 81 + get(sso_endpoints::get_pending_registration), 82 + ) 83 + .route( 84 + "/sso/complete-registration", 85 + post(sso_endpoints::complete_registration), 86 + ) 87 + .route( 88 + "/sso/check-handle-available", 89 + get(sso_endpoints::check_handle_available), 90 + ) 91 + .layer(middleware::from_fn(tranquil_pds::oauth::verify::dpop_nonce_middleware)) 92 + } 93 + 94 + pub fn well_known_oauth_routes() -> axum::Router<AppState> { 95 + use axum::routing::get; 96 + 97 + axum::Router::new() 98 + .route( 99 + "/oauth-protected-resource", 100 + get(endpoints::oauth_protected_resource), 101 + ) 102 + .route( 103 + "/oauth-authorization-server", 104 + get(endpoints::oauth_authorization_server), 105 + ) 106 + } 107 + 108 + pub fn frontend_client_metadata_route() -> axum::Router<AppState> { 109 + use axum::routing::get; 110 + 111 + axum::Router::new() 112 + .route( 113 + "/oauth-client-metadata.json", 114 + get(endpoints::frontend_client_metadata), 115 + ) 116 + }
+40 -40
crates/tranquil-pds/src/sso/endpoints.rs crates/tranquil-oauth-server/src/sso_endpoints.rs
··· 9 9 use tranquil_db_traits::{SsoAction, SsoProviderType}; 10 10 use tranquil_types::RequestId; 11 11 12 - use super::config::SsoConfig; 13 - use crate::api::error::ApiError; 14 - use crate::auth::extractor::extract_auth_token_from_header; 15 - use crate::auth::{generate_app_password, validate_bearer_token_cached}; 16 - use crate::rate_limit::{ 12 + use tranquil_pds::sso::SsoConfig; 13 + use tranquil_pds::api::error::ApiError; 14 + use tranquil_pds::auth::extractor::extract_auth_token_from_header; 15 + use tranquil_pds::auth::{generate_app_password, validate_bearer_token_cached}; 16 + use tranquil_pds::rate_limit::{ 17 17 AccountCreationLimit, RateLimited, SsoCallbackLimit, SsoInitiateLimit, SsoUnlinkLimit, 18 18 check_user_rate_limit_with_message, 19 19 }; 20 - use crate::state::AppState; 20 + use tranquil_pds::state::AppState; 21 21 22 22 fn generate_state() -> String { 23 23 use rand::RngCore; ··· 367 367 state: &AppState, 368 368 request_uri: &str, 369 369 provider: SsoProviderType, 370 - user_info: &crate::sso::providers::SsoUserInfo, 370 + user_info: &tranquil_pds::sso::providers::SsoUserInfo, 371 371 ) -> Response { 372 372 let identity = match state 373 373 .sso_repo ··· 482 482 state: &AppState, 483 483 did: tranquil_types::Did, 484 484 provider: SsoProviderType, 485 - user_info: &crate::sso::providers::SsoUserInfo, 485 + user_info: &tranquil_pds::sso::providers::SsoUserInfo, 486 486 ) -> Response { 487 487 let existing = state 488 488 .sso_repo ··· 555 555 state: &AppState, 556 556 request_uri: &str, 557 557 provider: SsoProviderType, 558 - user_info: &crate::sso::providers::SsoUserInfo, 558 + user_info: &tranquil_pds::sso::providers::SsoUserInfo, 559 559 ) -> Response { 560 560 match state 561 561 .sso_repo ··· 616 616 617 617 pub async fn get_linked_accounts( 618 618 State(state): State<AppState>, 619 - auth: crate::auth::Auth<crate::auth::Active>, 619 + auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>, 620 620 ) -> Result<Json<LinkedAccountsResponse>, ApiError> { 621 621 let identities = state 622 622 .sso_repo ··· 651 651 652 652 pub async fn unlink_account( 653 653 State(state): State<AppState>, 654 - auth: crate::auth::Auth<crate::auth::Active>, 654 + auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>, 655 655 Json(input): Json<UnlinkAccountRequest>, 656 656 ) -> Result<Json<UnlinkAccountResponse>, ApiError> { 657 657 let _rate_limit = check_user_rate_limit_with_message::<SsoUnlinkLimit>( ··· 763 763 })); 764 764 } 765 765 766 - let validated = match crate::api::validation::validate_short_handle(&query.handle) { 766 + let validated = match tranquil_pds::api::validation::validate_short_handle(&query.handle) { 767 767 Ok(h) => h, 768 768 Err(e) => { 769 769 return Ok(Json(CheckHandleResponse { ··· 781 781 } 782 782 let domain = query.domain.as_deref().unwrap_or(&available_domains[0]); 783 783 let full_handle = format!("{}.{}", validated, domain); 784 - let handle_typed: crate::types::Handle = match full_handle.parse() { 784 + let handle_typed: tranquil_pds::types::Handle = match full_handle.parse() { 785 785 Ok(h) => h, 786 786 Err(_) => return Err(ApiError::InvalidHandle(None)), 787 787 }; ··· 879 879 .unwrap_or(&input.handle), 880 880 None => &input.handle, 881 881 }; 882 - match crate::api::validation::validate_short_handle(handle_to_validate) { 882 + match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) { 883 883 Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])), 884 884 Err(_) => return Err(ApiError::InvalidHandle(None)), 885 885 } 886 886 } else { 887 - match crate::api::validation::validate_full_domain_handle(&input.handle) { 887 + match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) { 888 888 Ok(h) => h, 889 889 Err(_) => return Err(ApiError::InvalidHandle(None)), 890 890 } ··· 914 914 tranquil_db_traits::CommsChannel::Discord => match &input.discord_username { 915 915 Some(username) if !username.trim().is_empty() => { 916 916 let clean = username.trim().to_lowercase(); 917 - if !crate::api::validation::is_valid_discord_username(&clean) { 917 + if !tranquil_pds::api::validation::is_valid_discord_username(&clean) { 918 918 return Err(ApiError::InvalidRequest( 919 919 "Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(), 920 920 )); ··· 926 926 tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username { 927 927 Some(username) if !username.trim().is_empty() => { 928 928 let clean = username.trim().trim_start_matches('@'); 929 - if !crate::api::validation::is_valid_telegram_username(clean) { 929 + if !tranquil_pds::api::validation::is_valid_telegram_username(clean) { 930 930 return Err(ApiError::InvalidRequest( 931 931 "Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(), 932 932 )); ··· 960 960 if e.len() > 254 { 961 961 return Err(ApiError::InvalidEmail); 962 962 } 963 - if !crate::api::validation::is_valid_email(e) { 963 + if !tranquil_pds::api::validation::is_valid_email(e) { 964 964 return Err(ApiError::InvalidEmail); 965 965 } 966 966 Some(e.clone()) ··· 981 981 None 982 982 }; 983 983 984 - let handle_typed: crate::types::Handle = 984 + let handle_typed: tranquil_pds::types::Handle = 985 985 handle.parse().map_err(|_| ApiError::InvalidHandle(None))?; 986 986 let reserved = state 987 987 .user_repo ··· 1008 1008 1009 1009 let did = match did_type { 1010 1010 "web" => { 1011 - if !crate::api::server::meta::is_self_hosted_did_web_enabled() { 1011 + if !tranquil_pds::util::is_self_hosted_did_web_enabled() { 1012 1012 return Err(ApiError::SelfHostedDidWebDisabled); 1013 1013 } 1014 1014 let encoded_handle = handle.replace(':', "%3A"); ··· 1038 1038 .secrets 1039 1039 .plc_rotation_key 1040 1040 .clone() 1041 - .unwrap_or_else(|| crate::plc::signing_key_to_did_key(&signing_key)); 1041 + .unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&signing_key)); 1042 1042 1043 - let genesis_result = match crate::plc::create_genesis_operation( 1043 + let genesis_result = match tranquil_pds::plc::create_genesis_operation( 1044 1044 &signing_key, 1045 1045 &rotation_key, 1046 1046 &handle, ··· 1055 1055 } 1056 1056 }; 1057 1057 1058 - let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 1058 + let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone())); 1059 1059 if let Err(e) = plc_client 1060 1060 .send_operation(&genesis_result.did, &genesis_result.signed_operation) 1061 1061 .await ··· 1071 1071 }; 1072 1072 tracing::info!(did = %did, handle = %handle, provider = %pending_preview.provider.as_str(), "Created DID for SSO account"); 1073 1073 1074 - let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) { 1074 + let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) { 1075 1075 Ok(bytes) => bytes, 1076 1076 Err(e) => { 1077 1077 tracing::error!("Error encrypting signing key: {:?}", e); ··· 1089 1089 }; 1090 1090 1091 1091 let rev = Tid::now(LimitedU32::MIN); 1092 - let did_typed: crate::types::Did = did 1092 + let did_typed: tranquil_pds::types::Did = did 1093 1093 .parse() 1094 1094 .map_err(|_| ApiError::InternalError(Some("Invalid DID".into())))?; 1095 - let (commit_bytes, _sig) = match crate::api::repo::record::utils::create_signed_commit( 1095 + let (commit_bytes, _sig) = match tranquil_pds::repo_ops::create_signed_commit( 1096 1096 &did_typed, 1097 1097 mst_root, 1098 1098 rev.as_ref(), ··· 1146 1146 .map(|s| s.trim().trim_start_matches('@').to_lowercase()) 1147 1147 .filter(|s| !s.is_empty()), 1148 1148 encrypted_key_bytes: encrypted_key_bytes.clone(), 1149 - encryption_version: crate::config::ENCRYPTION_VERSION, 1149 + encryption_version: tranquil_pds::config::ENCRYPTION_VERSION, 1150 1150 commit_cid: commit_cid.to_string(), 1151 1151 repo_rev: rev.as_ref().to_string(), 1152 1152 genesis_block_cids, ··· 1189 1189 .await; 1190 1190 1191 1191 if let Err(e) = 1192 - crate::api::repo::record::sequence_identity_event(&state, &did_typed, Some(&handle_typed)) 1192 + tranquil_pds::repo_ops::sequence_identity_event(&state, &did_typed, Some(&handle_typed)) 1193 1193 .await 1194 1194 { 1195 1195 tracing::warn!("Failed to sequence identity event for {}: {}", did, e); 1196 1196 } 1197 - if let Err(e) = crate::api::repo::record::sequence_account_event( 1197 + if let Err(e) = tranquil_pds::repo_ops::sequence_account_event( 1198 1198 &state, 1199 1199 &did_typed, 1200 1200 tranquil_db_traits::AccountStatus::Active, ··· 1208 1208 "$type": "app.bsky.actor.profile", 1209 1209 "displayName": handle_typed.as_str() 1210 1210 }); 1211 - if let Err(e) = crate::api::repo::record::create_record_internal( 1211 + if let Err(e) = tranquil_pds::repo_ops::create_record_internal( 1212 1212 &state, 1213 1213 &did_typed, 1214 - &crate::types::PROFILE_COLLECTION, 1215 - &crate::types::PROFILE_RKEY, 1214 + &tranquil_pds::types::PROFILE_COLLECTION, 1215 + &tranquil_pds::types::PROFILE_RKEY, 1216 1216 &profile_record, 1217 1217 ) 1218 1218 .await ··· 1287 1287 tracing::info!(did = %did, "Auto-verified email from SSO provider"); 1288 1288 1289 1289 if is_standalone { 1290 - let key_bytes = match crate::config::decrypt_key( 1290 + let key_bytes = match tranquil_pds::config::decrypt_key( 1291 1291 &encrypted_key_bytes, 1292 - Some(crate::config::ENCRYPTION_VERSION), 1292 + Some(tranquil_pds::config::ENCRYPTION_VERSION), 1293 1293 ) { 1294 1294 Ok(k) => k, 1295 1295 Err(e) => { ··· 1298 1298 } 1299 1299 }; 1300 1300 1301 - let access_meta = match crate::auth::create_access_token_with_metadata(&did, &key_bytes) 1301 + let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&did, &key_bytes) 1302 1302 { 1303 1303 Ok(m) => m, 1304 1304 Err(e) => { ··· 1307 1307 } 1308 1308 }; 1309 1309 let refresh_meta = 1310 - match crate::auth::create_refresh_token_with_metadata(&did, &key_bytes) { 1310 + match tranquil_pds::auth::create_refresh_token_with_metadata(&did, &key_bytes) { 1311 1311 Ok(m) => m, 1312 1312 Err(e) => { 1313 1313 tracing::error!("Failed to create refresh token: {:?}", e); ··· 1333 1333 } 1334 1334 1335 1335 let hostname = &tranquil_config::get().server.hostname; 1336 - if let Err(e) = crate::comms::comms_repo::enqueue_welcome( 1336 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome( 1337 1337 state.user_repo.as_ref(), 1338 1338 state.infra_repo.as_ref(), 1339 1339 user_id.unwrap_or(uuid::Uuid::nil()), ··· 1370 1370 } 1371 1371 1372 1372 if let Some(uid) = user_id { 1373 - let verification_token = crate::auth::verification_token::generate_signup_token( 1373 + let verification_token = tranquil_pds::auth::verification_token::generate_signup_token( 1374 1374 &did_typed, 1375 1375 verification_channel, 1376 1376 &verification_recipient, 1377 1377 ); 1378 1378 let formatted_token = 1379 - crate::auth::verification_token::format_token_for_display(&verification_token); 1380 - if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification( 1379 + tranquil_pds::auth::verification_token::format_token_for_display(&verification_token); 1380 + if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification( 1381 1381 state.user_repo.as_ref(), 1382 1382 state.infra_repo.as_ref(), 1383 1383 uid,
+3 -3
crates/tranquil-pds/Cargo.toml
··· 31 31 bytes = { workspace = true } 32 32 chrono = { workspace = true } 33 33 cid = { workspace = true } 34 - clap = { workspace = true } 35 - dotenvy = { workspace = true } 36 34 ed25519-dalek = { workspace = true } 37 35 futures = { workspace = true } 38 36 hex = { workspace = true } ··· 75 73 tower-http = { workspace = true } 76 74 tower-layer = { workspace = true } 77 75 tracing = { workspace = true } 78 - tracing-subscriber = { workspace = true } 79 76 urlencoding = { workspace = true } 80 77 uuid = { workspace = true } 81 78 webauthn-rs = { workspace = true } ··· 97 94 testcontainers = { workspace = true } 98 95 testcontainers-modules = { workspace = true } 99 96 tranquil-ripple = { workspace = true } 97 + tranquil-sync = { workspace = true } 98 + tranquil-api = { workspace = true } 99 + tranquil-oauth-server = { workspace = true } 100 100 wiremock = { workspace = true }
-14
crates/tranquil-pds/src/api/mod.rs
··· 1 - pub mod actor; 2 - pub mod admin; 3 - pub mod age_assurance; 4 - pub mod backup; 5 - pub mod delegation; 6 - pub mod discord_webhook; 7 1 pub mod error; 8 - pub mod identity; 9 - pub mod moderation; 10 - pub mod notification_prefs; 11 2 pub mod proxy; 12 3 pub mod proxy_client; 13 - pub mod repo; 14 4 pub mod responses; 15 - pub mod server; 16 - pub mod telegram_webhook; 17 - pub mod temp; 18 5 pub mod validation; 19 - pub mod verification; 20 6 21 7 pub use error::ApiError; 22 8 pub use proxy_client::{AtUriParts, proxy_client, validate_at_uri, validate_limit};
+3 -3
crates/tranquil-pds/src/auth/mfa_verified.rs
··· 74 74 state: &AppState, 75 75 user: &'a AuthenticatedUser, 76 76 ) -> Result<MfaVerified<'a>, Response> { 77 - use crate::api::server::reauth::{check_legacy_session_mfa, legacy_mfa_required_response}; 77 + use crate::auth::reauth::{check_legacy_session_mfa, legacy_mfa_required_response}; 78 78 79 79 if check_legacy_session_mfa(&*state.session_repo, &user.did).await { 80 80 Ok(MfaVerified::from_session_reauth(user)) ··· 87 87 state: &AppState, 88 88 user: &'a AuthenticatedUser, 89 89 ) -> Result<MfaVerified<'a>, Response> { 90 - use crate::api::server::reauth::{REAUTH_WINDOW_SECONDS, reauth_required_response}; 90 + use crate::auth::reauth::{REAUTH_WINDOW_SECONDS, reauth_required_response}; 91 91 use chrono::Utc; 92 92 93 93 let status = state ··· 117 117 state: &AppState, 118 118 user: &'a AuthenticatedUser, 119 119 ) -> Result<Option<MfaVerified<'a>>, Response> { 120 - use crate::api::server::reauth::{check_reauth_required_cached, reauth_required_response}; 120 + use crate::auth::reauth::{check_reauth_required_cached, reauth_required_response}; 121 121 122 122 let has_password = state 123 123 .user_repo
+1
crates/tranquil-pds/src/auth/mod.rs
··· 13 13 pub mod account_verified; 14 14 pub mod email_token; 15 15 pub mod extractor; 16 + pub mod reauth; 16 17 pub mod legacy_2fa; 17 18 pub mod login_identifier; 18 19 pub mod mfa_verified;
+156
crates/tranquil-pds/src/auth/reauth.rs
··· 1 + use axum::{ 2 + Json, 3 + http::StatusCode, 4 + response::{IntoResponse, Response}, 5 + }; 6 + use chrono::Utc; 7 + use serde::Serialize; 8 + use tranquil_db_traits::{SessionRepository, UserRepository}; 9 + 10 + pub const REAUTH_WINDOW_SECONDS: i64 = 300; 11 + 12 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] 13 + #[serde(rename_all = "lowercase")] 14 + pub enum ReauthMethod { 15 + Password, 16 + Totp, 17 + Passkey, 18 + } 19 + 20 + fn is_reauth_required(last_reauth_at: Option<chrono::DateTime<Utc>>) -> bool { 21 + match last_reauth_at { 22 + None => true, 23 + Some(t) => { 24 + let elapsed = Utc::now().signed_duration_since(t); 25 + elapsed.num_seconds() > REAUTH_WINDOW_SECONDS 26 + } 27 + } 28 + } 29 + 30 + async fn get_available_reauth_methods( 31 + user_repo: &dyn UserRepository, 32 + _session_repo: &dyn SessionRepository, 33 + did: &crate::types::Did, 34 + ) -> Vec<ReauthMethod> { 35 + let mut methods = Vec::new(); 36 + 37 + let has_password = user_repo 38 + .get_password_hash_by_did(did) 39 + .await 40 + .ok() 41 + .flatten() 42 + .is_some(); 43 + 44 + if has_password { 45 + methods.push(ReauthMethod::Password); 46 + } 47 + 48 + let has_totp = user_repo.has_totp_enabled(did).await.unwrap_or(false); 49 + if has_totp { 50 + methods.push(ReauthMethod::Totp); 51 + } 52 + 53 + let has_passkeys = user_repo.has_passkeys(did).await.unwrap_or(false); 54 + if has_passkeys { 55 + methods.push(ReauthMethod::Passkey); 56 + } 57 + 58 + methods 59 + } 60 + 61 + pub async fn check_reauth_required_cached( 62 + session_repo: &dyn SessionRepository, 63 + cache: &std::sync::Arc<dyn crate::cache::Cache>, 64 + did: &crate::types::Did, 65 + ) -> bool { 66 + let cache_key = crate::cache_keys::reauth_key(did); 67 + if let Some(timestamp_str) = cache.get(&cache_key).await 68 + && let Ok(timestamp) = timestamp_str.parse::<i64>() 69 + { 70 + let reauth_time = chrono::DateTime::from_timestamp(timestamp, 0); 71 + if let Some(t) = reauth_time { 72 + let elapsed = Utc::now().signed_duration_since(t); 73 + if elapsed.num_seconds() <= REAUTH_WINDOW_SECONDS { 74 + return false; 75 + } 76 + } 77 + } 78 + match session_repo.get_last_reauth_at(did).await { 79 + Ok(last_reauth_at) => is_reauth_required(last_reauth_at), 80 + _ => true, 81 + } 82 + } 83 + 84 + #[derive(Serialize)] 85 + #[serde(rename_all = "camelCase")] 86 + pub struct ReauthRequiredError { 87 + pub error: String, 88 + pub message: String, 89 + pub reauth_methods: Vec<ReauthMethod>, 90 + } 91 + 92 + pub async fn reauth_required_response( 93 + user_repo: &dyn UserRepository, 94 + session_repo: &dyn SessionRepository, 95 + did: &crate::types::Did, 96 + ) -> Response { 97 + let methods = get_available_reauth_methods(user_repo, session_repo, did).await; 98 + ( 99 + StatusCode::UNAUTHORIZED, 100 + Json(ReauthRequiredError { 101 + error: "ReauthRequired".to_string(), 102 + message: "Re-authentication required for this action".to_string(), 103 + reauth_methods: methods, 104 + }), 105 + ) 106 + .into_response() 107 + } 108 + 109 + pub async fn check_legacy_session_mfa( 110 + session_repo: &dyn SessionRepository, 111 + did: &crate::types::Did, 112 + ) -> bool { 113 + match session_repo.get_session_mfa_status(did).await { 114 + Ok(Some(status)) => { 115 + if status.login_type.is_modern() { 116 + return true; 117 + } 118 + if status.mfa_verified { 119 + return true; 120 + } 121 + if let Some(last_reauth) = status.last_reauth_at { 122 + let elapsed = chrono::Utc::now().signed_duration_since(last_reauth); 123 + if elapsed.num_seconds() <= REAUTH_WINDOW_SECONDS { 124 + return true; 125 + } 126 + } 127 + false 128 + } 129 + _ => true, 130 + } 131 + } 132 + 133 + #[derive(Serialize)] 134 + #[serde(rename_all = "camelCase")] 135 + pub struct MfaVerificationRequiredError { 136 + pub error: String, 137 + pub message: String, 138 + pub reauth_methods: Vec<ReauthMethod>, 139 + } 140 + 141 + pub async fn legacy_mfa_required_response( 142 + user_repo: &dyn UserRepository, 143 + session_repo: &dyn SessionRepository, 144 + did: &crate::types::Did, 145 + ) -> Response { 146 + let methods = get_available_reauth_methods(user_repo, session_repo, did).await; 147 + ( 148 + StatusCode::FORBIDDEN, 149 + Json(MfaVerificationRequiredError { 150 + error: "MfaVerificationRequired".to_string(), 151 + message: "This sensitive operation requires MFA verification. Your session was created via a legacy app that doesn't support MFA during login.".to_string(), 152 + reauth_methods: methods, 153 + }), 154 + ) 155 + .into_response() 156 + }
+1 -1
crates/tranquil-pds/src/crawlers.rs
··· 1 1 use crate::circuit_breaker::CircuitBreaker; 2 - use crate::sync::firehose::SequencedEvent; 2 + use tranquil_db_traits::SequencedEvent; 3 3 use reqwest::Client; 4 4 use std::sync::Arc; 5 5 use std::sync::atomic::{AtomicU64, Ordering};
+28 -601
crates/tranquil-pds/src/lib.rs
··· 17 17 pub mod plc; 18 18 pub mod rate_limit; 19 19 pub mod repo; 20 + pub mod repo_ops; 20 21 pub mod repo_write_lock; 21 22 pub mod scheduled; 22 23 pub mod sso; ··· 33 34 extract::DefaultBodyLimit, 34 35 http::Method, 35 36 middleware, 36 - routing::{get, post}, 37 + routing::get, 37 38 }; 38 39 use http::StatusCode; 39 40 use serde_json::json; ··· 56 57 #[cfg(not(debug_assertions))] 57 58 pub const BUILD_VERSION: &str = env!("CARGO_PKG_VERSION"); 58 59 60 + pub struct ExternalRoutes { 61 + pub xrpc: Router<AppState>, 62 + pub oauth: Router<AppState>, 63 + pub well_known: Router<AppState>, 64 + pub extra: Router<AppState>, 65 + } 66 + 67 + impl Default for ExternalRoutes { 68 + fn default() -> Self { 69 + Self { 70 + xrpc: Router::new(), 71 + oauth: Router::new(), 72 + well_known: Router::new(), 73 + extra: Router::new(), 74 + } 75 + } 76 + } 77 + 59 78 pub fn app(state: AppState) -> Router { 60 - let xrpc_router = Router::new() 61 - .route("/_health", get(api::server::health)) 62 - .route( 63 - "/com.atproto.server.describeServer", 64 - get(api::server::describe_server), 65 - ) 66 - .route( 67 - "/com.atproto.server.createAccount", 68 - post(api::identity::create_account), 69 - ) 70 - .route( 71 - "/com.atproto.server.createSession", 72 - post(api::server::create_session), 73 - ) 74 - .route( 75 - "/com.atproto.server.getSession", 76 - get(api::server::get_session), 77 - ) 78 - .route("/_account.listSessions", get(api::server::list_sessions)) 79 - .route("/_account.revokeSession", post(api::server::revoke_session)) 80 - .route( 81 - "/_account.revokeAllSessions", 82 - post(api::server::revoke_all_sessions), 83 - ) 84 - .route( 85 - "/com.atproto.server.deleteSession", 86 - post(api::server::delete_session), 87 - ) 88 - .route( 89 - "/com.atproto.server.refreshSession", 90 - post(api::server::refresh_session), 91 - ) 92 - .route( 93 - "/com.atproto.server.confirmSignup", 94 - post(api::server::confirm_signup), 95 - ) 96 - .route( 97 - "/com.atproto.server.resendVerification", 98 - post(api::server::resend_verification), 99 - ) 100 - .route( 101 - "/com.atproto.server.getServiceAuth", 102 - get(api::server::get_service_auth), 103 - ) 104 - .route( 105 - "/com.atproto.identity.resolveHandle", 106 - get(api::identity::resolve_handle), 107 - ) 108 - .route( 109 - "/com.atproto.repo.createRecord", 110 - post(api::repo::create_record), 111 - ) 112 - .route("/com.atproto.repo.putRecord", post(api::repo::put_record)) 113 - .route("/com.atproto.repo.getRecord", get(api::repo::get_record)) 114 - .route( 115 - "/com.atproto.repo.deleteRecord", 116 - post(api::repo::delete_record), 117 - ) 118 - .route( 119 - "/com.atproto.repo.listRecords", 120 - get(api::repo::list_records), 121 - ) 122 - .route( 123 - "/com.atproto.repo.describeRepo", 124 - get(api::repo::describe_repo), 125 - ) 126 - .route("/com.atproto.repo.uploadBlob", post(api::repo::upload_blob)) 127 - .route( 128 - "/com.atproto.repo.applyWrites", 129 - post(api::repo::apply_writes), 130 - ) 131 - .route( 132 - "/com.atproto.sync.getLatestCommit", 133 - get(sync::get_latest_commit), 134 - ) 135 - .route("/com.atproto.sync.listRepos", get(sync::list_repos)) 136 - .route("/com.atproto.sync.getBlob", get(sync::get_blob)) 137 - .route("/com.atproto.sync.listBlobs", get(sync::list_blobs)) 138 - .route( 139 - "/com.atproto.sync.getRepoStatus", 140 - get(sync::get_repo_status), 141 - ) 142 - .route( 143 - "/com.atproto.server.checkAccountStatus", 144 - get(api::server::check_account_status), 145 - ) 146 - .route( 147 - "/com.atproto.identity.getRecommendedDidCredentials", 148 - get(api::identity::get_recommended_did_credentials), 149 - ) 150 - .route( 151 - "/com.atproto.repo.listMissingBlobs", 152 - get(api::repo::list_missing_blobs), 153 - ) 154 - .route( 155 - "/com.atproto.sync.notifyOfUpdate", 156 - post(sync::notify_of_update), 157 - ) 158 - .route("/com.atproto.sync.requestCrawl", post(sync::request_crawl)) 159 - .route("/com.atproto.sync.getBlocks", get(sync::get_blocks)) 160 - .route("/com.atproto.sync.getRepo", get(sync::get_repo)) 161 - .route("/com.atproto.sync.getRecord", get(sync::get_record)) 162 - .route( 163 - "/com.atproto.sync.subscribeRepos", 164 - get(sync::subscribe_repos), 165 - ) 166 - .route("/com.atproto.sync.getHead", get(sync::get_head)) 167 - .route("/com.atproto.sync.getCheckout", get(sync::get_checkout)) 168 - .route( 169 - "/com.atproto.moderation.createReport", 170 - post(api::moderation::create_report), 171 - ) 172 - .route( 173 - "/com.atproto.admin.getAccountInfo", 174 - get(api::admin::get_account_info), 175 - ) 176 - .route( 177 - "/com.atproto.admin.getAccountInfos", 178 - get(api::admin::get_account_infos), 179 - ) 180 - .route( 181 - "/com.atproto.admin.searchAccounts", 182 - get(api::admin::search_accounts), 183 - ) 184 - .route( 185 - "/com.atproto.server.activateAccount", 186 - post(api::server::activate_account), 187 - ) 188 - .route( 189 - "/com.atproto.server.deactivateAccount", 190 - post(api::server::deactivate_account), 191 - ) 192 - .route( 193 - "/com.atproto.server.requestAccountDelete", 194 - post(api::server::request_account_delete), 195 - ) 196 - .route( 197 - "/com.atproto.server.deleteAccount", 198 - post(api::server::delete_account), 199 - ) 200 - .route( 201 - "/com.atproto.server.requestPasswordReset", 202 - post(api::server::request_password_reset), 203 - ) 204 - .route( 205 - "/com.atproto.server.resetPassword", 206 - post(api::server::reset_password), 207 - ) 208 - .route( 209 - "/_account.changePassword", 210 - post(api::server::change_password), 211 - ) 212 - .route( 213 - "/_account.removePassword", 214 - post(api::server::remove_password), 215 - ) 216 - .route( 217 - "/_account.setPassword", 218 - post(api::server::set_password), 219 - ) 220 - .route( 221 - "/_account.getPasswordStatus", 222 - get(api::server::get_password_status), 223 - ) 224 - .route( 225 - "/_account.getReauthStatus", 226 - get(api::server::get_reauth_status), 227 - ) 228 - .route( 229 - "/_account.reauthPassword", 230 - post(api::server::reauth_password), 231 - ) 232 - .route("/_account.reauthTotp", post(api::server::reauth_totp)) 233 - .route( 234 - "/_account.reauthPasskeyStart", 235 - post(api::server::reauth_passkey_start), 236 - ) 237 - .route( 238 - "/_account.reauthPasskeyFinish", 239 - post(api::server::reauth_passkey_finish), 240 - ) 241 - .route( 242 - "/_account.getLegacyLoginPreference", 243 - get(api::server::get_legacy_login_preference), 244 - ) 245 - .route( 246 - "/_account.updateLegacyLoginPreference", 247 - post(api::server::update_legacy_login_preference), 248 - ) 249 - .route("/_account.updateLocale", post(api::server::update_locale)) 250 - .route( 251 - "/_account.listTrustedDevices", 252 - get(api::server::list_trusted_devices), 253 - ) 254 - .route( 255 - "/_account.revokeTrustedDevice", 256 - post(api::server::revoke_trusted_device), 257 - ) 258 - .route( 259 - "/_account.updateTrustedDevice", 260 - post(api::server::update_trusted_device), 261 - ) 262 - .route( 263 - "/_account.createPasskeyAccount", 264 - post(api::server::create_passkey_account), 265 - ) 266 - .route( 267 - "/_account.startPasskeyRegistrationForSetup", 268 - post(api::server::start_passkey_registration_for_setup), 269 - ) 270 - .route( 271 - "/_account.completePasskeySetup", 272 - post(api::server::complete_passkey_setup), 273 - ) 274 - .route( 275 - "/_account.requestPasskeyRecovery", 276 - post(api::server::request_passkey_recovery), 277 - ) 278 - .route( 279 - "/_account.recoverPasskeyAccount", 280 - post(api::server::recover_passkey_account), 281 - ) 282 - .route( 283 - "/_account.updateDidDocument", 284 - post(api::server::update_did_document), 285 - ) 286 - .route( 287 - "/_account.getDidDocument", 288 - get(api::server::get_did_document), 289 - ) 290 - .route( 291 - "/com.atproto.server.requestEmailUpdate", 292 - post(api::server::request_email_update), 293 - ) 294 - .route( 295 - "/_checkEmailVerified", 296 - post(api::server::check_email_verified), 297 - ) 298 - .route( 299 - "/_checkChannelVerified", 300 - post(api::server::check_channel_verified), 301 - ) 302 - .route( 303 - "/com.atproto.server.confirmEmail", 304 - post(api::server::confirm_email), 305 - ) 306 - .route( 307 - "/com.atproto.server.updateEmail", 308 - post(api::server::update_email), 309 - ) 310 - .route( 311 - "/_account.authorizeEmailUpdate", 312 - get(api::server::authorize_email_update), 313 - ) 314 - .route( 315 - "/_account.checkEmailUpdateStatus", 316 - get(api::server::check_email_update_status), 317 - ) 318 - .route( 319 - "/_account.checkEmailInUse", 320 - post(api::server::check_email_in_use), 321 - ) 322 - .route( 323 - "/_account.checkCommsChannelInUse", 324 - post(api::server::check_comms_channel_in_use), 325 - ) 326 - .route( 327 - "/com.atproto.server.reserveSigningKey", 328 - post(api::server::reserve_signing_key), 329 - ) 330 - .route( 331 - "/com.atproto.server.verifyMigrationEmail", 332 - post(api::server::verify_migration_email), 333 - ) 334 - .route( 335 - "/com.atproto.server.resendMigrationVerification", 336 - post(api::server::resend_migration_verification), 337 - ) 338 - .route( 339 - "/com.atproto.identity.updateHandle", 340 - post(api::identity::update_handle), 341 - ) 342 - .route( 343 - "/com.atproto.identity.requestPlcOperationSignature", 344 - post(api::identity::request_plc_operation_signature), 345 - ) 346 - .route( 347 - "/com.atproto.identity.signPlcOperation", 348 - post(api::identity::sign_plc_operation), 349 - ) 350 - .route( 351 - "/com.atproto.identity.submitPlcOperation", 352 - post(api::identity::submit_plc_operation), 353 - ) 354 - .route( 355 - "/_identity.verifyHandleOwnership", 356 - post(api::identity::verify_handle_ownership), 357 - ) 358 - .route("/com.atproto.repo.importRepo", post(api::repo::import_repo)) 359 - .route( 360 - "/com.atproto.admin.deleteAccount", 361 - post(api::admin::delete_account), 362 - ) 363 - .route( 364 - "/com.atproto.admin.updateAccountEmail", 365 - post(api::admin::update_account_email), 366 - ) 367 - .route( 368 - "/com.atproto.admin.updateAccountHandle", 369 - post(api::admin::update_account_handle), 370 - ) 371 - .route( 372 - "/com.atproto.admin.updateAccountPassword", 373 - post(api::admin::update_account_password), 374 - ) 375 - .route( 376 - "/com.atproto.server.listAppPasswords", 377 - get(api::server::list_app_passwords), 378 - ) 379 - .route( 380 - "/com.atproto.server.createAppPassword", 381 - post(api::server::create_app_password), 382 - ) 383 - .route( 384 - "/com.atproto.server.revokeAppPassword", 385 - post(api::server::revoke_app_password), 386 - ) 387 - .route( 388 - "/com.atproto.server.createInviteCode", 389 - post(api::server::create_invite_code), 390 - ) 391 - .route( 392 - "/com.atproto.server.createInviteCodes", 393 - post(api::server::create_invite_codes), 394 - ) 395 - .route( 396 - "/com.atproto.server.getAccountInviteCodes", 397 - get(api::server::get_account_invite_codes), 398 - ) 399 - .route( 400 - "/com.atproto.server.createTotpSecret", 401 - post(api::server::create_totp_secret), 402 - ) 403 - .route( 404 - "/com.atproto.server.enableTotp", 405 - post(api::server::enable_totp), 406 - ) 407 - .route( 408 - "/com.atproto.server.disableTotp", 409 - post(api::server::disable_totp), 410 - ) 411 - .route( 412 - "/com.atproto.server.getTotpStatus", 413 - get(api::server::get_totp_status), 414 - ) 415 - .route( 416 - "/com.atproto.server.regenerateBackupCodes", 417 - post(api::server::regenerate_backup_codes), 418 - ) 419 - .route( 420 - "/com.atproto.server.startPasskeyRegistration", 421 - post(api::server::start_passkey_registration), 422 - ) 423 - .route( 424 - "/com.atproto.server.finishPasskeyRegistration", 425 - post(api::server::finish_passkey_registration), 426 - ) 427 - .route( 428 - "/com.atproto.server.listPasskeys", 429 - get(api::server::list_passkeys), 430 - ) 431 - .route( 432 - "/com.atproto.server.deletePasskey", 433 - post(api::server::delete_passkey), 434 - ) 435 - .route( 436 - "/com.atproto.server.updatePasskey", 437 - post(api::server::update_passkey), 438 - ) 439 - .route( 440 - "/com.atproto.admin.getInviteCodes", 441 - get(api::admin::get_invite_codes), 442 - ) 443 - .route("/_admin.getServerStats", get(api::admin::get_server_stats)) 444 - .route("/_server.getConfig", get(api::admin::get_server_config)) 445 - .route( 446 - "/_admin.updateServerConfig", 447 - post(api::admin::update_server_config), 448 - ) 449 - .route( 450 - "/com.atproto.admin.disableAccountInvites", 451 - post(api::admin::disable_account_invites), 452 - ) 453 - .route( 454 - "/com.atproto.admin.enableAccountInvites", 455 - post(api::admin::enable_account_invites), 456 - ) 457 - .route( 458 - "/com.atproto.admin.disableInviteCodes", 459 - post(api::admin::disable_invite_codes), 460 - ) 461 - .route( 462 - "/com.atproto.admin.getSubjectStatus", 463 - get(api::admin::get_subject_status), 464 - ) 465 - .route( 466 - "/com.atproto.admin.updateSubjectStatus", 467 - post(api::admin::update_subject_status), 468 - ) 469 - .route("/com.atproto.admin.sendEmail", post(api::admin::send_email)) 470 - .route( 471 - "/app.bsky.actor.getPreferences", 472 - get(api::actor::get_preferences), 473 - ) 474 - .route( 475 - "/app.bsky.actor.putPreferences", 476 - post(api::actor::put_preferences), 477 - ) 478 - .route( 479 - "/com.atproto.temp.checkSignupQueue", 480 - get(api::temp::check_signup_queue), 481 - ) 482 - .route( 483 - "/com.atproto.temp.dereferenceScope", 484 - post(api::temp::dereference_scope), 485 - ) 486 - .route( 487 - "/_account.getNotificationPrefs", 488 - get(api::notification_prefs::get_notification_prefs), 489 - ) 490 - .route( 491 - "/_account.updateNotificationPrefs", 492 - post(api::notification_prefs::update_notification_prefs), 493 - ) 494 - .route( 495 - "/_account.getNotificationHistory", 496 - get(api::notification_prefs::get_notification_history), 497 - ) 498 - .route( 499 - "/_account.confirmChannelVerification", 500 - post(api::verification::confirm_channel_verification), 501 - ) 502 - .route("/_account.verifyToken", post(api::server::verify_token)) 503 - .route( 504 - "/_delegation.listControllers", 505 - get(api::delegation::list_controllers), 506 - ) 507 - .route( 508 - "/_delegation.addController", 509 - post(api::delegation::add_controller), 510 - ) 511 - .route( 512 - "/_delegation.removeController", 513 - post(api::delegation::remove_controller), 514 - ) 515 - .route( 516 - "/_delegation.updateControllerScopes", 517 - post(api::delegation::update_controller_scopes), 518 - ) 519 - .route( 520 - "/_delegation.listControlledAccounts", 521 - get(api::delegation::list_controlled_accounts), 522 - ) 523 - .route( 524 - "/_delegation.getAuditLog", 525 - get(api::delegation::get_audit_log), 526 - ) 527 - .route( 528 - "/_delegation.getScopePresets", 529 - get(api::delegation::get_scope_presets), 530 - ) 531 - .route( 532 - "/_delegation.createDelegatedAccount", 533 - post(api::delegation::create_delegated_account), 534 - ) 535 - .route("/_backup.listBackups", get(api::backup::list_backups)) 536 - .route("/_backup.getBackup", get(api::backup::get_backup)) 537 - .route("/_backup.createBackup", post(api::backup::create_backup)) 538 - .route("/_backup.deleteBackup", post(api::backup::delete_backup)) 539 - .route("/_backup.setEnabled", post(api::backup::set_backup_enabled)) 540 - .route("/_backup.exportBlobs", get(api::backup::export_blobs)) 541 - .route( 542 - "/app.bsky.ageassurance.getState", 543 - get(api::age_assurance::get_state), 544 - ) 545 - .route( 546 - "/app.bsky.unspecced.getAgeAssuranceState", 547 - get(api::age_assurance::get_age_assurance_state), 548 - ) 79 + app_with_routes(state, ExternalRoutes::default()) 80 + } 81 + 82 + pub fn app_with_routes(state: AppState, external: ExternalRoutes) -> Router { 83 + let xrpc_router = external.xrpc 549 84 .fallback(async || ( 550 85 StatusCode::NOT_IMPLEMENTED, 551 86 Json(json!({"error": "MethodNotImplemented", "message": "Method not implemented. For app.bsky.* methods, include an atproto-proxy header specifying your AppView."})), ··· 558 93 .with_state(state.clone()), 559 94 ); 560 95 561 - let oauth_router = Router::new() 562 - .route("/jwks", get(oauth::endpoints::oauth_jwks)) 563 - .route("/par", post(oauth::endpoints::pushed_authorization_request)) 564 - .route("/authorize", get(oauth::endpoints::authorize_get)) 565 - .route("/authorize", post(oauth::endpoints::authorize_post)) 566 - .route( 567 - "/authorize/accounts", 568 - get(oauth::endpoints::authorize_accounts), 569 - ) 570 - .route( 571 - "/authorize/select", 572 - post(oauth::endpoints::authorize_select), 573 - ) 574 - .route("/authorize/2fa", get(oauth::endpoints::authorize_2fa_get)) 575 - .route("/authorize/2fa", post(oauth::endpoints::authorize_2fa_post)) 576 - .route( 577 - "/authorize/passkey", 578 - get(oauth::endpoints::authorize_passkey_start), 579 - ) 580 - .route( 581 - "/authorize/passkey", 582 - post(oauth::endpoints::authorize_passkey_finish), 583 - ) 584 - .route( 585 - "/passkey/check", 586 - get(oauth::endpoints::check_user_has_passkeys), 587 - ) 588 - .route( 589 - "/security-status", 590 - get(oauth::endpoints::check_user_security_status), 591 - ) 592 - .route("/passkey/start", post(oauth::endpoints::passkey_start)) 593 - .route("/passkey/finish", post(oauth::endpoints::passkey_finish)) 594 - .route("/authorize/deny", post(oauth::endpoints::authorize_deny)) 595 - .route( 596 - "/register/complete", 597 - post(oauth::endpoints::register_complete), 598 - ) 599 - .route( 600 - "/establish-session", 601 - post(oauth::endpoints::establish_session), 602 - ) 603 - .route("/authorize/consent", get(oauth::endpoints::consent_get)) 604 - .route("/authorize/consent", post(oauth::endpoints::consent_post)) 605 - .route("/authorize/renew", post(oauth::endpoints::authorize_renew)) 606 - .route( 607 - "/authorize/redirect", 608 - get(oauth::endpoints::authorize_redirect), 609 - ) 610 - .route("/delegation/auth", post(oauth::endpoints::delegation_auth)) 611 - .route( 612 - "/delegation/auth-token", 613 - post(oauth::endpoints::delegation_auth_token), 614 - ) 615 - .route( 616 - "/delegation/totp", 617 - post(oauth::endpoints::delegation_totp_verify), 618 - ) 619 - .route("/token", post(oauth::endpoints::token_endpoint)) 620 - .route("/revoke", post(oauth::endpoints::revoke_token)) 621 - .route("/introspect", post(oauth::endpoints::introspect_token)) 622 - .route("/sso/providers", get(sso::endpoints::get_sso_providers)) 623 - .route("/sso/initiate", post(sso::endpoints::sso_initiate)) 624 - .route( 625 - "/sso/callback", 626 - get(sso::endpoints::sso_callback).post(sso::endpoints::sso_callback_post), 627 - ) 628 - .route("/sso/linked", get(sso::endpoints::get_linked_accounts)) 629 - .route("/sso/unlink", post(sso::endpoints::unlink_account)) 630 - .route( 631 - "/sso/pending-registration", 632 - get(sso::endpoints::get_pending_registration), 633 - ) 634 - .route( 635 - "/sso/complete-registration", 636 - post(sso::endpoints::complete_registration), 637 - ) 638 - .route( 639 - "/sso/check-handle-available", 640 - get(sso::endpoints::check_handle_available), 641 - ) 642 - .layer(middleware::from_fn(oauth::verify::dpop_nonce_middleware)); 96 + let oauth_router = external.oauth; 643 97 644 - let well_known_router = Router::new() 645 - .route("/did.json", get(api::identity::well_known_did)) 646 - .route("/atproto-did", get(api::identity::well_known_atproto_did)) 647 - .route( 648 - "/oauth-protected-resource", 649 - get(oauth::endpoints::oauth_protected_resource), 650 - ) 651 - .route( 652 - "/oauth-authorization-server", 653 - get(oauth::endpoints::oauth_authorization_server), 654 - ); 98 + let well_known_router = external.well_known; 655 99 656 100 let router = Router::new() 657 101 .nest_service("/xrpc", xrpc_service) 658 102 .nest("/oauth", oauth_router) 659 103 .nest("/.well-known", well_known_router) 660 104 .route("/metrics", get(metrics::metrics_handler)) 661 - .route("/health", get(api::server::health)) 662 - .route("/robots.txt", get(api::server::robots_txt)) 663 - .route("/favicon.ico", get(api::server::get_logo)) 664 - .route("/u/{handle}/did.json", get(api::identity::user_did_doc)) 665 - .route( 666 - "/webhook/telegram", 667 - post(api::telegram_webhook::handle_telegram_webhook) 668 - .layer(DefaultBodyLimit::max(64 * 1024)), 669 - ) 670 - .route( 671 - "/webhook/discord", 672 - post(api::discord_webhook::handle_discord_webhook) 673 - .layer(DefaultBodyLimit::max(64 * 1024)), 674 - ) 105 + .merge(external.extra) 675 106 .layer(DefaultBodyLimit::max( 676 107 tranquil_config::get().server.max_blob_size as usize, 677 108 )) ··· 717 148 let serve_dir = ServeDir::new(frontend_dir).not_found_service(ServeFile::new(&index_path)); 718 149 719 150 return router 720 - .route( 721 - "/oauth-client-metadata.json", 722 - get(oauth::endpoints::frontend_client_metadata), 723 - ) 724 151 .route_service("/", ServeFile::new(&homepage_file)) 725 152 .nest("/app", spa_router) 726 153 .fallback_service(serve_dir);
-2
crates/tranquil-pds/src/oauth/mod.rs
··· 1 1 pub mod db; 2 - pub mod endpoints; 3 - pub mod jwks; 4 2 pub mod scopes; 5 3 pub mod verify; 6 4
crates/tranquil-pds/src/api/repo/record/utils.rs crates/tranquil-pds/src/repo_ops.rs
-1
crates/tranquil-pds/src/sso/mod.rs
··· 1 1 pub mod config; 2 - pub mod endpoints; 3 2 pub mod providers; 4 3 5 4 pub use config::SsoConfig;
+2 -2
crates/tranquil-pds/src/state.rs
··· 8 8 use crate::repo_write_lock::RepoWriteLocks; 9 9 use crate::sso::{SsoConfig, SsoManager}; 10 10 use crate::storage::{BackupStorage, BlobStorage, create_backup_storage, create_blob_storage}; 11 - use crate::sync::firehose::SequencedEvent; 11 + use tranquil_db_traits::SequencedEvent; 12 12 use sqlx::PgPool; 13 13 use std::error::Error; 14 14 use std::sync::Arc; ··· 240 240 .await, 241 241 ) { 242 242 (true, Ok(Some(0))) => { 243 - let code = crate::api::server::invite::gen_invite_code(); 243 + let code = crate::util::gen_invite_code(); 244 244 tracing::info!( 245 245 "No users exist and invite codes are required. Bootstrap invite code: {}", 246 246 code
+4 -14
crates/tranquil-pds/src/sync/mod.rs
··· 1 - pub mod blob; 2 1 pub mod car; 3 - pub mod commit; 4 - pub mod crawl; 5 - pub mod deprecated; 6 2 pub mod firehose; 7 3 pub mod frame; 8 4 pub mod import; 9 - pub mod listener; 10 - pub mod repo; 11 - pub mod subscribe_repos; 12 5 pub mod util; 13 6 pub mod verify; 14 7 15 - pub use blob::{get_blob, list_blobs}; 16 - pub use commit::{get_latest_commit, get_repo_status, list_repos}; 17 - pub use crawl::{notify_of_update, request_crawl}; 18 - pub use deprecated::{get_checkout, get_head}; 19 - pub use repo::{get_blocks, get_record, get_repo}; 20 - pub use subscribe_repos::subscribe_repos; 21 - pub use tranquil_db_traits::AccountStatus; 8 + #[cfg(test)] 9 + mod verify_tests; 10 + 11 + pub use firehose::SequencedEvent; 22 12 pub use util::{ 23 13 RepoAccessLevel, RepoAccount, RepoAvailabilityError, assert_repo_availability, 24 14 get_account_with_status,
+2 -2
crates/tranquil-pds/src/sync/verify.rs
··· 130 130 .ok_or(VerifyError::NoSigningKey) 131 131 } 132 132 133 - async fn resolve_did_document(&self, did: &Did) -> Result<DidDocument<'static>, VerifyError> { 133 + pub(crate) async fn resolve_did_document(&self, did: &Did) -> Result<DidDocument<'static>, VerifyError> { 134 134 let did_str = did.as_str(); 135 135 if did_str.starts_with("did:plc:") { 136 136 self.resolve_plc_did(did_str).await ··· 197 197 Ok(doc.into_static()) 198 198 } 199 199 200 - fn verify_mst_structure( 200 + pub(crate) fn verify_mst_structure( 201 201 &self, 202 202 data_cid: &Cid, 203 203 blocks: &HashMap<Cid, Bytes>,
+1 -1
crates/tranquil-pds/src/sync/verify_tests.rs
··· 1 - use crate::sync::verify::{CarVerifier, VerifyError}; 1 + use super::{CarVerifier, VerifyError}; 2 2 use bytes::Bytes; 3 3 use cid::Cid; 4 4 use sha2::{Digest, Sha256};
+21
crates/tranquil-pds/src/util.rs
··· 535 535 ); 536 536 } 537 537 } 538 + 539 + pub(crate) fn gen_invite_random_token() -> String { 540 + let mut rng = rand::thread_rng(); 541 + let chars: Vec<char> = BASE32_ALPHABET.chars().collect(); 542 + let gen_segment = |rng: &mut rand::rngs::ThreadRng, len: usize| -> String { 543 + (0..len) 544 + .map(|_| chars[rng.gen_range(0..chars.len())]) 545 + .collect() 546 + }; 547 + format!("{}-{}", gen_segment(&mut rng, 5), gen_segment(&mut rng, 5)) 548 + } 549 + 550 + pub fn gen_invite_code() -> String { 551 + let hostname = &tranquil_config::get().server.hostname; 552 + let hostname_prefix = hostname.replace('.', "-"); 553 + format!("{}-{}", hostname_prefix, gen_invite_random_token()) 554 + } 555 + 556 + pub fn is_self_hosted_did_web_enabled() -> bool { 557 + tranquil_config::get().server.enable_pds_hosted_did_web 558 + }
+1 -1
crates/tranquil-pds/tests/commit_signing.rs
··· 96 96 97 97 #[test] 98 98 fn test_create_signed_commit_helper() { 99 - use tranquil_pds::api::repo::record::utils::create_signed_commit; 99 + use tranquil_pds::repo_ops::create_signed_commit; 100 100 101 101 let signing_key = SigningKey::random(&mut rand::thread_rng()); 102 102 let did: Did = "did:plc:testuser123456789abcdef"
+13 -2
crates/tranquil-pds/tests/common/mod.rs
··· 563 563 if let Some((cache, distributed_rate_limiter)) = config.cache { 564 564 state = state.with_cache(cache, distributed_rate_limiter); 565 565 } 566 - tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await; 567 - let app = tranquil_pds::app(state); 566 + tranquil_sync::listener::start_sequencer_listener(state.clone()).await; 567 + let app = tranquil_pds::app_with_routes( 568 + state, 569 + tranquil_pds::ExternalRoutes { 570 + xrpc: tranquil_api::api_routes().merge(tranquil_sync::sync_routes()), 571 + oauth: tranquil_oauth_server::oauth_routes(), 572 + well_known: tranquil_oauth_server::well_known_oauth_routes() 573 + .merge(tranquil_api::well_known_api_routes()), 574 + extra: tranquil_api::misc_routes() 575 + .merge(tranquil_api::webhook_routes()) 576 + .merge(tranquil_oauth_server::frontend_client_metadata_route()), 577 + }, 578 + ); 568 579 tokio::spawn(async move { 569 580 axum::serve(listener, app).await.unwrap(); 570 581 });
+28
crates/tranquil-server/Cargo.toml
··· 1 + [package] 2 + name = "tranquil-server" 3 + version.workspace = true 4 + edition.workspace = true 5 + license.workspace = true 6 + 7 + [dependencies] 8 + tranquil-pds = { workspace = true } 9 + tranquil-sync = { workspace = true } 10 + tranquil-api = { workspace = true } 11 + tranquil-oauth-server = { workspace = true } 12 + tranquil-config = { workspace = true } 13 + 14 + axum = { workspace = true } 15 + clap = { workspace = true } 16 + dotenvy = { workspace = true } 17 + ed25519-dalek = { workspace = true } 18 + hex = { workspace = true } 19 + tokio = { workspace = true } 20 + tokio-util = { workspace = true } 21 + tracing = { workspace = true } 22 + tracing-subscriber = { workspace = true } 23 + 24 + [features] 25 + default = ["frontend", "s3", "valkey"] 26 + frontend = ["tranquil-pds/frontend"] 27 + s3 = ["tranquil-pds/s3"] 28 + valkey = ["tranquil-pds/valkey"]
+12
crates/tranquil-server/build.rs
··· 1 + use std::process::Command; 2 + 3 + fn main() { 4 + let timestamp = Command::new("date") 5 + .arg("+%Y-%m-%d %H:%M:%S UTC") 6 + .output() 7 + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) 8 + .unwrap_or_else(|_| "unknown".to_string()); 9 + 10 + println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp); 11 + println!("cargo:rerun-if-changed=build.rs"); 12 + }
+13 -9
crates/tranquil-pds/src/main.rs crates/tranquil-server/src/main.rs
··· 18 18 #[derive(Parser)] 19 19 #[command(name = "tranquil-pds", version = BUILD_VERSION, about = "Tranquil AT Protocol PDS")] 20 20 struct Cli { 21 - /// Path to a TOML configuration file (also settable via TRANQUIL_PDS_CONFIG env var) 22 21 #[arg(short, long, value_name = "FILE", env = "TRANQUIL_PDS_CONFIG")] 23 22 config: Option<PathBuf>, 24 23 ··· 28 27 29 28 #[derive(Subcommand)] 30 29 enum Command { 31 - /// Validate the configuration and exit 32 30 Validate { 33 - /// Skip validation of secrets and database URL (useful when secrets 34 - /// are provided at runtime via environment variables / secret files) 35 31 #[arg(long)] 36 32 ignore_secrets: bool, 37 33 }, 38 - /// Print a TOML configuration template to stdout 39 34 ConfigTemplate, 40 35 } 41 36 ··· 45 40 46 41 let cli = Cli::parse(); 47 42 48 - // Handle subcommands that don't need full startup 49 43 if let Some(command) = &cli.command { 50 44 return match command { 51 45 Command::ConfigTemplate => { ··· 116 110 spawn_signal_handler(shutdown.clone()); 117 111 118 112 let state = AppState::new(shutdown.clone()).await?; 119 - tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await; 113 + tranquil_sync::listener::start_sequencer_listener(state.clone()).await; 120 114 121 115 let backfill_repo_repo = state.repo_repo.clone(); 122 116 let backfill_block_store = state.block_store.clone(); ··· 189 183 } 190 184 191 185 if let Some(telegram_sender) = TelegramSender::from_config(cfg) { 192 - // Safe to unwrap: validated in TranquilConfig::validate() 193 186 let secret_token = tranquil_config::get() 194 187 .telegram 195 188 .webhook_secret ··· 262 255 shutdown.clone(), 263 256 )); 264 257 265 - let app = tranquil_pds::app(state); 258 + let app = tranquil_pds::app_with_routes( 259 + state, 260 + tranquil_pds::ExternalRoutes { 261 + xrpc: tranquil_api::api_routes().merge(tranquil_sync::sync_routes()), 262 + oauth: tranquil_oauth_server::oauth_routes(), 263 + well_known: tranquil_oauth_server::well_known_oauth_routes() 264 + .merge(tranquil_api::well_known_api_routes()), 265 + extra: tranquil_api::misc_routes() 266 + .merge(tranquil_api::webhook_routes()) 267 + .merge(tranquil_oauth_server::frontend_client_metadata_route()), 268 + }, 269 + ); 266 270 267 271 let cfg = tranquil_config::get(); 268 272 let host = &cfg.server.host;
+24
crates/tranquil-sync/Cargo.toml
··· 1 + [package] 2 + name = "tranquil-sync" 3 + version.workspace = true 4 + edition.workspace = true 5 + license.workspace = true 6 + 7 + [dependencies] 8 + tranquil-pds = { workspace = true } 9 + tranquil-types = { workspace = true } 10 + tranquil-config = { workspace = true } 11 + tranquil-db-traits = { workspace = true } 12 + 13 + anyhow = { workspace = true } 14 + axum = { workspace = true } 15 + bytes = { workspace = true } 16 + chrono = { workspace = true } 17 + cid = { workspace = true } 18 + futures = { workspace = true } 19 + ipld-core = { workspace = true } 20 + jacquard-repo = { workspace = true } 21 + serde = { workspace = true } 22 + serde_ipld_dagcbor = { workspace = true } 23 + tokio = { workspace = true } 24 + tracing = { workspace = true }
+3 -3
crates/tranquil-pds/src/sync/blob.rs crates/tranquil-sync/src/blob.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::state::AppState; 3 - use crate::sync::util::{RepoAccessLevel, assert_repo_availability}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::state::AppState; 3 + use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability}; 4 4 use axum::{ 5 5 Json, 6 6 body::Body,
+3 -3
crates/tranquil-pds/src/sync/commit.rs crates/tranquil-sync/src/commit.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::state::AppState; 3 - use crate::sync::util::{RepoAccessLevel, assert_repo_availability, get_account_with_status}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::state::AppState; 3 + use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability, get_account_with_status}; 4 4 use axum::{ 5 5 Json, 6 6 extract::{Query, State},
+2 -2
crates/tranquil-pds/src/sync/crawl.rs crates/tranquil-sync/src/crawl.rs
··· 1 - use crate::api::EmptyResponse; 2 - use crate::state::AppState; 1 + use tranquil_pds::api::EmptyResponse; 2 + use tranquil_pds::state::AppState; 3 3 use axum::{ 4 4 Json, 5 5 extract::{Query, State},
+8 -8
crates/tranquil-pds/src/sync/deprecated.rs crates/tranquil-sync/src/deprecated.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::state::AppState; 3 - use crate::sync::car::{encode_car_block, encode_car_header}; 4 - use crate::sync::util::{RepoAccessLevel, assert_repo_availability}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::state::AppState; 3 + use tranquil_pds::sync::car::{encode_car_block, encode_car_header}; 4 + use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability}; 5 5 use axum::{ 6 6 Json, 7 7 extract::{Query, State}, ··· 18 18 const MAX_REPO_BLOCKS_TRAVERSAL: usize = 20_000; 19 19 20 20 async fn check_admin_or_self(state: &AppState, headers: &HeaderMap, did: &Did) -> bool { 21 - let extracted = match crate::auth::extract_auth_token_from_header(crate::util::get_header_str( 21 + let extracted = match tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str( 22 22 headers, 23 23 axum::http::header::AUTHORIZATION, 24 24 )) { 25 25 Some(t) => t, 26 26 None => return false, 27 27 }; 28 - let dpop_proof = crate::util::get_header_str(headers, crate::util::HEADER_DPOP); 28 + let dpop_proof = tranquil_pds::util::get_header_str(headers, tranquil_pds::util::HEADER_DPOP); 29 29 let http_uri = "/"; 30 - match crate::auth::validate_token_with_dpop( 30 + match tranquil_pds::auth::validate_token_with_dpop( 31 31 state.user_repo.as_ref(), 32 32 state.oauth_repo.as_ref(), 33 33 &extracted.token, ··· 35 35 dpop_proof, 36 36 Method::GET.as_str(), 37 37 http_uri, 38 - crate::auth::AccountRequirement::AnyStatus, 38 + tranquil_pds::auth::AccountRequirement::AnyStatus, 39 39 ) 40 40 .await 41 41 {
+38
crates/tranquil-sync/src/lib.rs
··· 1 + pub mod blob; 2 + pub mod commit; 3 + pub mod crawl; 4 + pub mod deprecated; 5 + pub mod listener; 6 + pub mod repo; 7 + pub mod subscribe_repos; 8 + 9 + pub use blob::{get_blob, list_blobs}; 10 + pub use commit::{get_latest_commit, get_repo_status, list_repos}; 11 + pub use crawl::{notify_of_update, request_crawl}; 12 + pub use deprecated::{get_checkout, get_head}; 13 + pub use repo::{get_blocks, get_record, get_repo}; 14 + pub use subscribe_repos::subscribe_repos; 15 + 16 + use tranquil_pds::state::AppState; 17 + 18 + pub fn sync_routes() -> axum::Router<AppState> { 19 + use axum::routing::{get, post}; 20 + 21 + axum::Router::new() 22 + .route("/com.atproto.sync.getLatestCommit", get(get_latest_commit)) 23 + .route("/com.atproto.sync.listRepos", get(list_repos)) 24 + .route("/com.atproto.sync.getBlob", get(get_blob)) 25 + .route("/com.atproto.sync.listBlobs", get(list_blobs)) 26 + .route("/com.atproto.sync.getRepoStatus", get(get_repo_status)) 27 + .route("/com.atproto.sync.notifyOfUpdate", post(notify_of_update)) 28 + .route("/com.atproto.sync.requestCrawl", post(request_crawl)) 29 + .route("/com.atproto.sync.getBlocks", get(get_blocks)) 30 + .route("/com.atproto.sync.getRepo", get(get_repo)) 31 + .route("/com.atproto.sync.getRecord", get(get_record)) 32 + .route( 33 + "/com.atproto.sync.subscribeRepos", 34 + get(subscribe_repos), 35 + ) 36 + .route("/com.atproto.sync.getHead", get(get_head)) 37 + .route("/com.atproto.sync.getCheckout", get(get_checkout)) 38 + }
+2 -2
crates/tranquil-pds/src/sync/listener.rs crates/tranquil-sync/src/listener.rs
··· 1 - use crate::state::AppState; 2 - use crate::sync::firehose::SequencedEvent; 1 + use tranquil_pds::state::AppState; 2 + use tranquil_pds::sync::firehose::SequencedEvent; 3 3 use std::sync::atomic::{AtomicI64, Ordering}; 4 4 use tracing::{debug, error, info, warn}; 5 5 use tranquil_db_traits::SequenceNumber;
+8 -8
crates/tranquil-pds/src/sync/repo.rs crates/tranquil-sync/src/repo.rs
··· 1 - use crate::api::error::ApiError; 2 - use crate::scheduled::generate_repo_car_from_user_blocks; 3 - use crate::state::AppState; 4 - use crate::sync::car::{encode_car_block, encode_car_header}; 5 - use crate::sync::util::{RepoAccessLevel, assert_repo_availability}; 1 + use tranquil_pds::api::error::ApiError; 2 + use tranquil_pds::scheduled::generate_repo_car_from_user_blocks; 3 + use tranquil_pds::state::AppState; 4 + use tranquil_pds::sync::car::{encode_car_block, encode_car_header}; 5 + use tranquil_pds::sync::util::{RepoAccessLevel, assert_repo_availability}; 6 6 use axum::{ 7 7 extract::{Query, RawQuery, State}, 8 8 http::StatusCode, ··· 21 21 } 22 22 23 23 fn parse_get_blocks_query(query_string: &str) -> Result<GetBlocksParams, ApiError> { 24 - let did_str = crate::util::parse_repeated_query_param(Some(query_string), "did") 24 + let did_str = tranquil_pds::util::parse_repeated_query_param(Some(query_string), "did") 25 25 .into_iter() 26 26 .next() 27 27 .ok_or_else(|| ApiError::InvalidRequest("Missing required parameter: did".into()))?; 28 28 let did: Did = did_str 29 29 .parse() 30 30 .map_err(|_| ApiError::InvalidRequest("invalid did".into()))?; 31 - let cids = crate::util::parse_repeated_query_param(Some(query_string), "cids"); 31 + let cids = tranquil_pds::util::parse_repeated_query_param(Some(query_string), "cids"); 32 32 Ok(GetBlocksParams { did, cids }) 33 33 } 34 34 ··· 90 90 .into_response(); 91 91 } 92 92 93 - let header = match crate::sync::car::encode_car_header_null_root() { 93 + let header = match tranquil_pds::sync::car::encode_car_header_null_root() { 94 94 Ok(h) => h, 95 95 Err(e) => { 96 96 error!("Failed to encode CAR header: {}", e);
+9 -9
crates/tranquil-pds/src/sync/subscribe_repos.rs crates/tranquil-sync/src/subscribe_repos.rs
··· 1 - use crate::state::AppState; 2 - use crate::sync::firehose::SequencedEvent; 3 - use crate::sync::frame::{ErrorFrameName, InfoFrameName}; 4 - use crate::sync::util::{ 1 + use tranquil_pds::state::AppState; 2 + use tranquil_pds::sync::firehose::SequencedEvent; 3 + use tranquil_pds::sync::frame::{ErrorFrameName, InfoFrameName}; 4 + use tranquil_pds::sync::util::{ 5 5 format_error_frame, format_event_for_sending, format_event_with_prefetched_blocks, 6 6 format_info_frame, prefetch_blocks_for_events, 7 7 }; ··· 50 50 51 51 async fn handle_socket(mut socket: WebSocket, state: AppState, params: SubscribeReposParams) { 52 52 let count = SUBSCRIBER_COUNT.fetch_add(1, Ordering::SeqCst) + 1; 53 - crate::metrics::set_firehose_subscribers(count); 53 + tranquil_pds::metrics::set_firehose_subscribers(count); 54 54 info!(cursor = ?params.cursor, subscribers = count, "New firehose subscriber"); 55 55 let _ = handle_socket_inner(&mut socket, &state, params).await; 56 56 let count = SUBSCRIBER_COUNT.fetch_sub(1, Ordering::SeqCst) - 1; 57 - crate::metrics::set_firehose_subscribers(count); 57 + tranquil_pds::metrics::set_firehose_subscribers(count); 58 58 info!(subscribers = count, "Firehose subscriber disconnected"); 59 59 } 60 60 ··· 157 157 warn!("Failed to send backfill event: {}", e); 158 158 return Err(()); 159 159 } 160 - crate::metrics::record_firehose_event(); 160 + tranquil_pds::metrics::record_firehose_event(); 161 161 } 162 162 if i64::try_from(events_count).unwrap_or(i64::MAX) < BACKFILL_BATCH_SIZE { 163 163 break; ··· 197 197 warn!("Failed to send cutover event: {}", e); 198 198 return Err(()); 199 199 } 200 - crate::metrics::record_firehose_event(); 200 + tranquil_pds::metrics::record_firehose_event(); 201 201 } 202 202 } 203 203 } ··· 215 215 warn!("Failed to send event: {}", e); 216 216 break; 217 217 } 218 - crate::metrics::record_firehose_event(); 218 + tranquil_pds::metrics::record_firehose_event(); 219 219 } 220 220 Err(RecvError::Lagged(skipped)) => { 221 221 warn!(skipped = skipped, "Firehose subscriber lagged behind");
+1 -1
default.nix
··· 36 36 37 37 meta = { 38 38 license = lib.licenses.agpl3Plus; 39 - mainProgram = "tranquil-pds"; 39 + mainProgram = "tranquil-server"; 40 40 }; 41 41 }
+2 -2
justfile
··· 2 2 @just --list 3 3 4 4 run: 5 - cargo run 5 + cargo run -p tranquil-server 6 6 run-release: 7 - cargo run --release 7 + cargo run -p tranquil-server --release 8 8 build: 9 9 cargo build 10 10 build-release:
+1 -1
scripts/install-debian.sh
··· 317 317 318 318 log_info "Installing Tranquil PDS..." 319 319 id -u tranquil-pds &>/dev/null || useradd -r -s /sbin/nologin tranquil-pds 320 - cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/ 320 + cp /opt/tranquil-pds/target/release/tranquil-server /usr/local/bin/tranquil-pds 321 321 mkdir -p /var/lib/tranquil-pds 322 322 cp -r /opt/tranquil-pds/frontend/dist /var/lib/tranquil-pds/frontend 323 323 chown -R tranquil-pds:tranquil-pds /var/lib/tranquil-pds
+35 -15
test.nix
··· 17 17 enable = true; 18 18 database.createLocally = true; 19 19 20 - nginx = { 21 - enable = true; 22 - enableACME = false; 23 - }; 24 - 25 20 settings = { 26 - PDS_HOSTNAME = "pds.test"; 27 - SERVER_HOST = "0.0.0.0"; 28 - 29 - DISABLE_RATE_LIMITING = 1; 30 - TRANQUIL_PDS_ALLOW_INSECURE_SECRETS = 1; 21 + server.hostname = "pds.test"; 22 + server.host = "0.0.0.0"; 23 + server.disable_rate_limiting = true; 24 + server.invite_code_required = false; 25 + server.enable_pds_hosted_did_web = true; 26 + 27 + secrets.jwt_secret = "test-jwt-secret-must-be-32-chars-long"; 28 + secrets.dpop_secret = "test-dpop-secret-must-be-32-chars-long"; 29 + secrets.master_key = "test-master-key-must-be-32-chars-long"; 30 + secrets.allow_insecure = true; 31 + }; 32 + }; 31 33 32 - JWT_SECRET="test-jwt-secret-must-be-32-chars-long"; 33 - DPOP_SECRET="test-dpop-secret-must-be-32-chars-long"; 34 - MASTER_KEY="test-master-key-must-be-32-chars-long"; 34 + services.nginx = let 35 + vhost = { 36 + locations."/" = { 37 + proxyPass = "http://127.0.0.1:3000"; 38 + proxyWebsockets = true; 39 + extraConfig = '' 40 + proxy_set_header Host $host; 41 + proxy_set_header X-Forwarded-Proto $scheme; 42 + proxy_read_timeout 120s; 43 + ''; 44 + }; 35 45 }; 46 + in { 47 + enable = true; 48 + recommendedProxySettings = true; 49 + virtualHosts."pds.test" = vhost; 50 + virtualHosts."*.pds.test" = vhost; 36 51 }; 37 52 }; 38 53 ··· 50 65 base = "http://localhost" if via == "nginx" else "http://localhost:3000" 51 66 url = f"{base}/xrpc/{endpoint}" 52 67 53 - parts = ["curl", "-sf", "-X", method, host_header] 68 + parts = ["curl", "-s", "-w", r"'\n%{http_code}'", "-X", method, host_header] 54 69 if headers: 55 70 parts.extend(f"-H '{k}: {v}'" for k, v in headers.items()) 56 71 if data is not None: ··· 60 75 parts.append(f"--data-binary @{raw_body}") 61 76 parts.append(f"'{url}'") 62 77 63 - return server.succeed(" ".join(parts)) 78 + result = server.succeed(" ".join(parts)) 79 + lines = result.rsplit("\n", 1) 80 + body = lines[0] if len(lines) > 1 else result 81 + status = lines[1].strip() if len(lines) > 1 else "000" 82 + assert status.startswith("2"), f"xrpc {endpoint} returned HTTP {status}: {body}" 83 + return body 64 84 65 85 def xrpc_json(method, endpoint, **kwargs): 66 86 return json.loads(xrpc(method, endpoint, **kwargs))

History

1 round 0 comments
sign up or login to add to the discussion
oyster.cafe submitted #0
1 commit
expand
fix: move code to more correct crates
expand 0 comments
pull request successfully merged