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

note boxes in docs!

Changed files
+57 -33
slingshot
+22 -2
slingshot/api-description.md
··· 16 16 17 17 ### Current status 18 18 19 - 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. 19 + > [!important] 20 + > 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. 20 21 21 22 The core APIs will not change, since they are standard third-party `com.atproto` query APIs from ATProtocol. 22 23 ··· 54 55 _(work on this endpoint is in progress)_ 55 56 56 57 58 + ## Service proxying 59 + 60 + 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. 61 + 62 + Service-proxied requests can specify a Slingshot instance via the `atproto-proxy` header: 63 + 64 + ```http 65 + GET /xrpc/com.bad-example.identity.resolveMiniDoc?identifier=bad-example.com 66 + Host: <your pds> 67 + atproto-proxy: did:web:<slingshot domain>#slingshot 68 + ``` 69 + 70 + Where `<your pds>` is the user's own PDS host, and `<slingshot domain>` 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. 71 + 72 + > [!tip] 73 + > 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_) 74 + 75 + 57 76 ## Ergonomic APIs 58 77 59 78 - 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 89 - [🌌 Constellation](https://constellation.microcosm.blue/), a global backlink index (all social interactions in atproto are links!) 71 90 - [🎇 Spacedust](https://spacedust.microcosm.blue/), a firehose of all social interactions 72 91 73 - 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)! 92 + > [!success] 93 + > 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)!
+2 -2
slingshot/src/main.rs
··· 30 30 /// - an HTTPS certs will be automatically configured with Acme/letsencrypt 31 31 /// - TODO: a rate-limiter will be installed 32 32 #[arg(long)] 33 - host: Option<String>, 33 + domain: Option<String>, 34 34 /// email address for letsencrypt contact 35 35 /// 36 36 /// recommended in production, i guess? ··· 104 104 server_cache_handle, 105 105 identity, 106 106 repo, 107 - args.host, 107 + args.domain, 108 108 args.acme_contact, 109 109 args.certs, 110 110 server_shutdown,
+33 -29
slingshot/src/server.rs
··· 227 227 enum ApiTags { 228 228 /// Core ATProtocol-compatible APIs. 229 229 /// 230 - /// Upstream documentation is available at 231 - /// https://docs.bsky.app/docs/category/http-reference 230 + /// > [!tip] 231 + /// > Upstream documentation is available at 232 + /// > https://docs.bsky.app/docs/category/http-reference 232 233 /// 233 234 /// These queries are usually executed directly against the PDS containing 234 235 /// the data being requested. Slingshot offers a caching view of the same ··· 241 242 /// more convenient [request parameters](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.repo.getUriRecord) 242 243 /// or [response formats](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.identity.resolveMiniDoc). 243 244 /// 244 - /// At the moment, these are namespaced under the `com.bad-example.*` NSID 245 - /// prefix, but as they stabilize they will likely be moved to either 246 - /// `blue.microcosm.*` or a slingshot-instance-specific lexicon under its 247 - /// `did:web` (ie., `blue.microcosm.slingshot.*`). Maybe one day they can 248 - /// be promoted to the [Lexicon Community](https://discourse.lexicon.community/) 249 - /// namespace. 245 + /// > [!important] 246 + /// > At the moment, these are namespaced under the `com.bad-example.*` NSID 247 + /// > prefix, but as they stabilize they may be migrated to an org namespace 248 + /// > like `blue.microcosm.*`. Support for asliasing to `com.bad-example.*` 249 + /// > will be maintained as long as it's in use. 250 250 #[oai(rename = "slingshot-specific queries")] 251 251 Custom, 252 252 } ··· 257 257 /// 258 258 /// Get a single record from a repository. Does not require auth. 259 259 /// 260 - /// See also the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-repo-get-record) 261 - /// that this endpoint aims to be compatible with. 260 + /// > [!tip] 261 + /// > See also the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-repo-get-record) 262 + /// > that this endpoint aims to be compatible with. 262 263 #[oai( 263 264 path = "/com.atproto.repo.getRecord", 264 265 method = "get", ··· 279 280 /// 280 281 /// If not specified, then return the most recent version. 281 282 /// 282 - /// If specified and a newer version of the record exists, returns 404 not 283 - /// found. That is: slingshot only retains the most recent version of a 284 - /// record. (TODO: verify bsky behaviour for mismatched/old CID) 283 + /// If a stale `CID` is specified and a newer version of the record 284 + /// exists, Slingshot returns a `NotFound` error. That is: Slingshot 285 + /// only retains the most recent version of a record. 285 286 Query(cid): Query<Option<String>>, 286 287 ) -> GetRecordResponse { 287 288 self.get_record_impl(repo, collection, rkey, cid).await ··· 308 309 /// 309 310 /// If not specified, then return the most recent version. 310 311 /// 311 - /// If specified and a newer version of the record exists, returns 404 not 312 - /// found. That is: slingshot only retains the most recent version of a 313 - /// record. 312 + /// > [!tip] 313 + /// > If specified and a newer version of the record exists, returns 404 not 314 + /// > found. That is: slingshot only retains the most recent version of a 315 + /// > record. 314 316 Query(cid): Query<Option<String>>, 315 317 ) -> GetRecordResponse { 316 318 let bad_at_uri = || { ··· 354 356 /// Resolves an atproto [`handle`](https://atproto.com/guides/glossary#handle) 355 357 /// (hostname) to a [`DID`](https://atproto.com/guides/glossary#did-decentralized-id). 356 358 /// 357 - /// Compatibility note: **Slingshot will _always_ bi-directionally verify 358 - /// against the DID document**, which is optional according to the 359 - /// authoritative lexicon. 359 + /// > [!tip] 360 + /// > Compatibility note: Slingshot will **always bi-directionally verify 361 + /// > against the DID document**, which is optional according to the 362 + /// > authoritative lexicon. 360 363 /// 361 - /// See the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle) 362 - /// that this endpoint aims to be compatible with. 364 + /// > [!tip] 365 + /// > See the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle) 366 + /// > that this endpoint aims to be compatible with. 363 367 #[oai( 364 368 path = "/com.atproto.identity.resolveHandle", 365 369 method = "get", ··· 662 666 /// 663 667 /// - PDS proxying offers a level of client IP anonymity from slingshot 664 668 /// - slingshot *may* implement more generous per-user rate-limits for proxied requests in the future 665 - fn get_did_doc(host: &str) -> impl Endpoint + use<> { 669 + fn get_did_doc(domain: &str) -> impl Endpoint + use<> { 666 670 let doc = poem::web::Json(AppViewDoc { 667 - id: format!("did:web:{host}"), 671 + id: format!("did:web:{domain}"), 668 672 service: [AppViewService { 669 673 id: "#slingshot".to_string(), 670 674 r#type: "SlingshotRecordProxy".to_string(), 671 - service_endpoint: format!("https://{host}"), 675 + service_endpoint: format!("https://{domain}"), 672 676 }], 673 677 }); 674 678 make_sync(move |_| doc.clone()) ··· 678 682 cache: HybridCache<String, CachedRecord>, 679 683 identity: Identity, 680 684 repo: Repo, 681 - host: Option<String>, 685 + domain: Option<String>, 682 686 acme_contact: Option<String>, 683 687 certs: Option<PathBuf>, 684 688 shutdown: CancellationToken, ··· 693 697 "Slingshot", 694 698 env!("CARGO_PKG_VERSION"), 695 699 ) 696 - .server(if let Some(ref h) = host { 700 + .server(if let Some(ref h) = domain { 697 701 format!("https://{h}") 698 702 } else { 699 703 "http://localhost:3000".to_string() ··· 714 718 .nest("/openapi", api_service.spec_endpoint()) 715 719 .nest("/xrpc/", api_service); 716 720 717 - if let Some(host) = host { 721 + if let Some(domain) = domain { 718 722 rustls::crypto::aws_lc_rs::default_provider() 719 723 .install_default() 720 724 .expect("alskfjalksdjf"); 721 725 722 - app = app.at("/.well-known/did.json", get_did_doc(&host)); 726 + app = app.at("/.well-known/did.json", get_did_doc(&domain)); 723 727 724 728 let mut auto_cert = AutoCert::builder() 725 729 .directory_url(LETS_ENCRYPT_PRODUCTION) 726 - .domain(&host); 730 + .domain(&domain); 727 731 if let Some(contact) = acme_contact { 728 732 auto_cert = auto_cert.contact(contact); 729 733 }