···2323 /// where to keep disk caches
2424 #[arg(long)]
2525 cache_dir: PathBuf,
2626+ /// the domain pointing to this server
2727+ ///
2828+ /// if present:
2929+ /// - a did:web document will be served at /.well-known/did.json
3030+ /// - TODO: HTTPS certs will be automatically configured with Acme/letsencrypt
3131+ /// - TODO: a rate-limiter will be installed
3232+ #[arg(long)]
3333+ host: Option<String>,
2634}
27352836#[tokio::main]
···7886 let server_shutdown = shutdown.clone();
7987 let server_cache_handle = cache.clone();
8088 tasks.spawn(async move {
8181- serve(server_cache_handle, repo, server_shutdown).await?;
8989+ serve(server_cache_handle, repo, args.host, server_shutdown).await?;
8290 Ok(())
8391 });
8492
+39-2
slingshot/src/server.rs
···11use crate::{CachedRecord, Repo, error::ServerError};
22use foyer::HybridCache;
33+use serde::Serialize;
34use std::sync::Arc;
45use tokio_util::sync::CancellationToken;
5666-use poem::{Route, Server, listener::TcpListener};
77+use poem::{Endpoint, Route, Server, endpoint::make_sync, listener::TcpListener};
78use poem_openapi::{
89 ApiResponse, Object, OpenApi, OpenApiService, param::Query, payload::Json, types::Example,
910};
···191192 // those are a little bit important
192193}
193194195195+#[derive(Debug, Clone, Serialize)]
196196+#[serde(rename_all = "camelCase")]
197197+struct AppViewService {
198198+ id: String,
199199+ r#type: String,
200200+ service_endpoint: String,
201201+}
202202+#[derive(Debug, Clone, Serialize)]
203203+struct AppViewDoc {
204204+ id: String,
205205+ service: [AppViewService; 1],
206206+}
207207+/// Serve a did document for did:web for this to be an xrpc appview
208208+///
209209+/// No slingshot endpoints currently require auth, so it's not necessary to do
210210+/// service proxying, however clients may wish to:
211211+///
212212+/// - PDS proxying offers a level of client IP anonymity from slingshot
213213+/// - slingshot *may* implement more generous per-user rate-limits for proxied requests in the future
214214+fn get_did_doc(host: String) -> impl Endpoint {
215215+ let doc = poem::web::Json(AppViewDoc {
216216+ id: format!("did:web:{host}"),
217217+ service: [AppViewService {
218218+ id: "#slingshot".to_string(),
219219+ r#type: "SlingshotRecordProxy".to_string(),
220220+ service_endpoint: format!("https://{host}"),
221221+ }],
222222+ });
223223+ make_sync(move |_| doc.clone())
224224+}
225225+194226pub async fn serve(
195227 cache: HybridCache<String, CachedRecord>,
196228 repo: Repo,
229229+ host: Option<String>,
197230 _shutdown: CancellationToken,
198231) -> Result<(), ServerError> {
199232 let repo = Arc::new(repo);
···202235 .server("http://localhost:3000")
203236 .url_prefix("/xrpc");
204237205205- let app = Route::new()
238238+ let mut app = Route::new()
206239 .nest("/", api_service.scalar())
207240 .nest("/openapi.json", api_service.spec_endpoint())
208241 .nest("/xrpc/", api_service);
242242+243243+ if let Some(host) = host {
244244+ app = app.at("/.well-known/did.json", get_did_doc(host));
245245+ };
209246210247 Server::new(TcpListener::bind("127.0.0.1:3000"))
211248 .run(app)