Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

serve service did doc

Changed files
+48 -3
slingshot
+9 -1
slingshot/src/main.rs
···
/// 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<String>,
}
#[tokio::main]
···
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(())
});
+39 -2
slingshot/src/server.rs
···
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,
};
···
// 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<String, CachedRecord>,
repo: Repo,
+
host: Option<String>,
_shutdown: CancellationToken,
) -> Result<(), ServerError> {
let repo = Arc::new(repo);
···
.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)