diff --git a/slingshot/api-description.md b/slingshot/api-description.md index 851a9d4..e029693 100644 --- a/slingshot/api-description.md +++ b/slingshot/api-description.md @@ -16,7 +16,8 @@ Large projects like [Bluesky](https://bsky.app/) control their performance and r ### Current status -Slingshot is currently in a **v0, pre-release state**. There is one production instance and you can use it! Expect short downtimes for restarts as development progresses and lower cache hit-rates as the internal storage caches are adjusted and reset. +> [!important] +> Slingshot is currently in a **v0, pre-release state**. There is one production instance and you can use it! Expect short downtimes for restarts as development progresses and lower cache hit-rates as the internal storage caches are adjusted and reset. The core APIs will not change, since they are standard third-party `com.atproto` query APIs from ATProtocol. @@ -54,6 +55,24 @@ Two core standard query APIs are supported to balance convenience and trust. The _(work on this endpoint is in progress)_ +## Service proxying + +Clients can proxy atproto queries through their own PDS with [Service Proxying](https://atproto.com/specs/xrpc#service-proxying), and this is supported by Slingshot. The Slingshot instance must be started the `--domain` argument specified. + +Service-proxied requests can specify a Slingshot instance via the `atproto-proxy` header: + +```http +GET /xrpc/com.bad-example.identity.resolveMiniDoc?identifier=bad-example.com +Host: +atproto-proxy: did:web:#slingshot +``` + +Where `` is the user's own PDS host, and `` is the domain that the slingshot instance is deployed at (eg. `slingshot.microcosm.blue`). See the [Service Proxying](https://atproto.com/specs/xrpc#service-proxying) docs for more. + +> [!tip] +> Service proxying is supported but completely optional. All APIs are directly accessible over the public internet, and GeoDNS helps route users to the closest instance to them for the lowest possible latency. (_note: deploying multiple slingshot instances with GeoDNS is still TODO_) + + ## Ergonomic APIs - Slingshot also offers variants of the `getRecord` endpoints that accept a full `at-uri` as a parameter, to save clients from needing to parse and validate all parts of a record location. @@ -70,4 +89,5 @@ Slingshot excels when combined with _shallow indexing_ services, which offer fas - [🌌 Constellation](https://constellation.microcosm.blue/), a global backlink index (all social interactions in atproto are links!) - [🎇 Spacedust](https://spacedust.microcosm.blue/), a firehose of all social interactions -All microcosm projects are [open source](https://tangled.sh/@bad-example.com/microcosm-links). **You can help sustain Slingshot** and all of microcosm by becoming a [Github sponsor](https://github.com/sponsors/uniphil/) or a [Ko-fi supporter](https://ko-fi.com/bad_example)! +> [!success] +> All microcosm projects are [open source](https://tangled.sh/@bad-example.com/microcosm-links). **You can help sustain Slingshot** and all of microcosm by becoming a [Github sponsor](https://github.com/sponsors/uniphil/) or a [Ko-fi supporter](https://ko-fi.com/bad_example)! diff --git a/slingshot/src/main.rs b/slingshot/src/main.rs index c427e2a..a02d02e 100644 --- a/slingshot/src/main.rs +++ b/slingshot/src/main.rs @@ -30,7 +30,7 @@ struct Args { /// - an HTTPS certs will be automatically configured with Acme/letsencrypt /// - TODO: a rate-limiter will be installed #[arg(long)] - host: Option, + domain: Option, /// email address for letsencrypt contact /// /// recommended in production, i guess? @@ -104,7 +104,7 @@ async fn main() -> Result<(), String> { server_cache_handle, identity, repo, - args.host, + args.domain, args.acme_contact, args.certs, server_shutdown, diff --git a/slingshot/src/server.rs b/slingshot/src/server.rs index 478df5a..db198a4 100644 --- a/slingshot/src/server.rs +++ b/slingshot/src/server.rs @@ -227,8 +227,9 @@ struct Xrpc { enum ApiTags { /// Core ATProtocol-compatible APIs. /// - /// Upstream documentation is available at - /// https://docs.bsky.app/docs/category/http-reference + /// > [!tip] + /// > Upstream documentation is available at + /// > https://docs.bsky.app/docs/category/http-reference /// /// These queries are usually executed directly against the PDS containing /// the data being requested. Slingshot offers a caching view of the same @@ -241,12 +242,11 @@ enum ApiTags { /// more convenient [request parameters](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.repo.getUriRecord) /// or [response formats](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.identity.resolveMiniDoc). /// - /// At the moment, these are namespaced under the `com.bad-example.*` NSID - /// prefix, but as they stabilize they will likely be moved to either - /// `blue.microcosm.*` or a slingshot-instance-specific lexicon under its - /// `did:web` (ie., `blue.microcosm.slingshot.*`). Maybe one day they can - /// be promoted to the [Lexicon Community](https://discourse.lexicon.community/) - /// namespace. + /// > [!important] + /// > At the moment, these are namespaced under the `com.bad-example.*` NSID + /// > prefix, but as they stabilize they may be migrated to an org namespace + /// > like `blue.microcosm.*`. Support for asliasing to `com.bad-example.*` + /// > will be maintained as long as it's in use. #[oai(rename = "slingshot-specific queries")] Custom, } @@ -257,8 +257,9 @@ impl Xrpc { /// /// Get a single record from a repository. Does not require auth. /// - /// See also the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-repo-get-record) - /// that this endpoint aims to be compatible with. + /// > [!tip] + /// > See also the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-repo-get-record) + /// > that this endpoint aims to be compatible with. #[oai( path = "/com.atproto.repo.getRecord", method = "get", @@ -279,9 +280,9 @@ impl Xrpc { /// /// If not specified, then return the most recent version. /// - /// If specified and a newer version of the record exists, returns 404 not - /// found. That is: slingshot only retains the most recent version of a - /// record. (TODO: verify bsky behaviour for mismatched/old CID) + /// If a stale `CID` is specified and a newer version of the record + /// exists, Slingshot returns a `NotFound` error. That is: Slingshot + /// only retains the most recent version of a record. Query(cid): Query>, ) -> GetRecordResponse { self.get_record_impl(repo, collection, rkey, cid).await @@ -308,9 +309,10 @@ impl Xrpc { /// /// If not specified, then return the most recent version. /// - /// If specified and a newer version of the record exists, returns 404 not - /// found. That is: slingshot only retains the most recent version of a - /// record. + /// > [!tip] + /// > If specified and a newer version of the record exists, returns 404 not + /// > found. That is: slingshot only retains the most recent version of a + /// > record. Query(cid): Query>, ) -> GetRecordResponse { let bad_at_uri = || { @@ -354,12 +356,14 @@ impl Xrpc { /// Resolves an atproto [`handle`](https://atproto.com/guides/glossary#handle) /// (hostname) to a [`DID`](https://atproto.com/guides/glossary#did-decentralized-id). /// - /// Compatibility note: **Slingshot will _always_ bi-directionally verify - /// against the DID document**, which is optional according to the - /// authoritative lexicon. + /// > [!tip] + /// > Compatibility note: Slingshot will **always bi-directionally verify + /// > against the DID document**, which is optional according to the + /// > authoritative lexicon. /// - /// See the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle) - /// that this endpoint aims to be compatible with. + /// > [!tip] + /// > See the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle) + /// > that this endpoint aims to be compatible with. #[oai( path = "/com.atproto.identity.resolveHandle", method = "get", @@ -662,13 +666,13 @@ struct AppViewDoc { /// /// - 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: &str) -> impl Endpoint + use<> { +fn get_did_doc(domain: &str) -> impl Endpoint + use<> { let doc = poem::web::Json(AppViewDoc { - id: format!("did:web:{host}"), + id: format!("did:web:{domain}"), service: [AppViewService { id: "#slingshot".to_string(), r#type: "SlingshotRecordProxy".to_string(), - service_endpoint: format!("https://{host}"), + service_endpoint: format!("https://{domain}"), }], }); make_sync(move |_| doc.clone()) @@ -678,7 +682,7 @@ pub async fn serve( cache: HybridCache, identity: Identity, repo: Repo, - host: Option, + domain: Option, acme_contact: Option, certs: Option, shutdown: CancellationToken, @@ -693,7 +697,7 @@ pub async fn serve( "Slingshot", env!("CARGO_PKG_VERSION"), ) - .server(if let Some(ref h) = host { + .server(if let Some(ref h) = domain { format!("https://{h}") } else { "http://localhost:3000".to_string() @@ -714,16 +718,16 @@ pub async fn serve( .nest("/openapi", api_service.spec_endpoint()) .nest("/xrpc/", api_service); - if let Some(host) = host { + if let Some(domain) = domain { rustls::crypto::aws_lc_rs::default_provider() .install_default() .expect("alskfjalksdjf"); - app = app.at("/.well-known/did.json", get_did_doc(&host)); + app = app.at("/.well-known/did.json", get_did_doc(&domain)); let mut auto_cert = AutoCert::builder() .directory_url(LETS_ENCRYPT_PRODUCTION) - .domain(&host); + .domain(&domain); if let Some(contact) = acme_contact { auto_cert = auto_cert.contact(contact); }