From 62eafdee74cefda1a2e37fc8bb2bb6be21026cce Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 31 Jul 2025 12:09:46 -0400 Subject: [PATCH] serve service did doc --- slingshot/src/main.rs | 10 +++++++++- slingshot/src/server.rs | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/slingshot/src/main.rs b/slingshot/src/main.rs index 843f3e7..63a2fa0 100644 --- a/slingshot/src/main.rs +++ b/slingshot/src/main.rs @@ -23,6 +23,14 @@ struct Args { /// where to keep disk caches #[arg(long)] cache_dir: PathBuf, + /// the domain pointing to this server + /// + /// if present: + /// - a did:web document will be served at /.well-known/did.json + /// - TODO: HTTPS certs will be automatically configured with Acme/letsencrypt + /// - TODO: a rate-limiter will be installed + #[arg(long)] + host: Option, } #[tokio::main] @@ -78,7 +86,7 @@ async fn main() -> Result<(), String> { let server_shutdown = shutdown.clone(); let server_cache_handle = cache.clone(); tasks.spawn(async move { - serve(server_cache_handle, repo, server_shutdown).await?; + serve(server_cache_handle, repo, args.host, server_shutdown).await?; Ok(()) }); diff --git a/slingshot/src/server.rs b/slingshot/src/server.rs index a27121b..7e8619b 100644 --- a/slingshot/src/server.rs +++ b/slingshot/src/server.rs @@ -1,9 +1,10 @@ use crate::{CachedRecord, Repo, error::ServerError}; use foyer::HybridCache; +use serde::Serialize; use std::sync::Arc; use tokio_util::sync::CancellationToken; -use poem::{Route, Server, listener::TcpListener}; +use poem::{Endpoint, Route, Server, endpoint::make_sync, listener::TcpListener}; use poem_openapi::{ ApiResponse, Object, OpenApi, OpenApiService, param::Query, payload::Json, types::Example, }; @@ -191,9 +192,41 @@ impl Xrpc { // those are a little bit important } +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct AppViewService { + id: String, + r#type: String, + service_endpoint: String, +} +#[derive(Debug, Clone, Serialize)] +struct AppViewDoc { + id: String, + service: [AppViewService; 1], +} +/// Serve a did document for did:web for this to be an xrpc appview +/// +/// No slingshot endpoints currently require auth, so it's not necessary to do +/// service proxying, however clients may wish to: +/// +/// - PDS proxying offers a level of client IP anonymity from slingshot +/// - slingshot *may* implement more generous per-user rate-limits for proxied requests in the future +fn get_did_doc(host: String) -> impl Endpoint { + let doc = poem::web::Json(AppViewDoc { + id: format!("did:web:{host}"), + service: [AppViewService { + id: "#slingshot".to_string(), + r#type: "SlingshotRecordProxy".to_string(), + service_endpoint: format!("https://{host}"), + }], + }); + make_sync(move |_| doc.clone()) +} + pub async fn serve( cache: HybridCache, repo: Repo, + host: Option, _shutdown: CancellationToken, ) -> Result<(), ServerError> { let repo = Arc::new(repo); @@ -202,11 +235,15 @@ pub async fn serve( .server("http://localhost:3000") .url_prefix("/xrpc"); - let app = Route::new() + let mut app = Route::new() .nest("/", api_service.scalar()) .nest("/openapi.json", api_service.spec_endpoint()) .nest("/xrpc/", api_service); + if let Some(host) = host { + app = app.at("/.well-known/did.json", get_did_doc(host)); + }; + Server::new(TcpListener::bind("127.0.0.1:3000")) .run(app) .await -- 2.51.2