···1818metrics = "0.24.2"
1919metrics-exporter-prometheus = { version = "0.17.1", features = ["http-listener"] }
2020poem = { version = "3.1.12", features = ["acme"] }
2121-poem-openapi = { version = "5.1.16", features = ["scalar", "stoplight-elements"] }
2121+poem-openapi = { version = "5.1.16", features = ["scalar"] }
2222reqwest = { version = "0.12.22", features = ["json"] }
2323rustls = "0.23.31"
2424serde = { version = "1.0.219", features = ["derive"] }
+73
slingshot/api-description.md
···11+_A [gravitational slingshot](https://en.wikipedia.org/wiki/Gravity_assist) makes use of the gravity and relative movements of celestial bodies to accelerate a spacecraft and change its trajectory._
22+33+44+# Slingshot: edge record cache
55+66+Applications in [ATProtocol](https://atproto.com/) store data in users' own [PDS](https://atproto.com/guides/self-hosting) (Personal Data Server), which are distributed across thousands of independently-run servers all over the world. Trying to access this data poses challenges for client applications:
77+88+- A PDS might be far away with long network latency
99+- or may be on an unreliable connection
1010+- or overloaded when you need it, or offline, or…
1111+1212+Large projects like [Bluesky](https://bsky.app/) control their performance and reliability by syncing all app-relevant data from PDSs into first-party databases. But for new apps, building out this additional data infrastructure adds significant effort and complexity up front.
1313+1414+**Slingshot is a fast, eager, production-grade cache of data in the [ATmosphere](https://atproto.com/)**, offering performance and reliability without custom infrastructure.
1515+1616+1717+### Current status
1818+1919+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.
2020+2121+The core APIs will not change, since they are standard third-party `com.atproto` query APIs from ATProtocol.
2222+2323+2424+## Eager caching
2525+2626+In many cases, Slingshot can cache the data you need *before* first request!
2727+2828+Slingshot subscribes to the global [Firehose](https://atproto.com/specs/sync#firehose) of data updates. It keeps a short-term rolling indexed window of *all* data, and automatically promotes content likely to be requested to its longer-term main cache. _(automatic promotion is still a work in progress)_
2929+3030+When there is a cache miss, Slingshot can often still accelerate record fetching, since it keeps a large cache of resolved identities: it can usually request from the correct PDS without extra lookups.
3131+3232+3333+## Precise invalidation
3434+3535+The fireshose includes **update** and **delete** events, which Slingshot uses to ensure stale and deleted data is removed within a very short window. Additonally, identity and account-level events can trigger rapid cleanup of data for deactivated and deleted accounts. _(some of this is still a work in progress)_
3636+3737+3838+## Low-trust
3939+4040+The "AT" in ATProtocol [stands for _Authenticated Transfer_](https://atproto.com/guides/glossary#at-protocol): all data is cryptographically signed, which makes it possible to broadcast data through third parties and trust that it's real _without_ having to directly contact the originating server.
4141+4242+Two core standard query APIs are supported to balance convenience and trust. They both fetch [records](https://atproto.com/guides/glossary#record):
4343+4444+### [`com.atproto.repo.getRecord`](#tag/comatproto-queries/GET/xrpc/com.atproto.repo.getRecord)
4545+4646+- convenient `JSON` response format
4747+- cannot be proven authentic
4848+4949+### [`com.atproto.sync.getRecord`](#tag/comatproto-queries/GET/xrpc/com.atproto.sync.getRecord)
5050+5151+- [`DAG-CBOR`](https://atproto.com/specs/data-model)-encoded response requires extra libraries to decode, but
5252+- includes a cryptographic proof of authenticity!
5353+5454+_(work on this endpoint is in progress)_
5555+5656+5757+## Ergonomic APIs
5858+5959+- 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.
6060+6161+- Bi-directionally verifying identity endpoints, so you can directly exchange atproto [`handle`](https://atproto.com/guides/glossary#handle)s for [`DID`](https://atproto.com/guides/glossary#did-decentralized-id)s without extra steps, plus a convenient [Mini-Doc](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.identity.resolveMiniDoc) verified identity summary.
6262+6363+6464+## Part of microcosm
6565+6666+[Microcosm](https://www.microcosm.blue/) is a collection of services and independent community-run infrastructure for ATProtocol.
6767+6868+Slingshot excels when combined with _shallow indexing_ services, which offer fast queries of global data relationships but with only references to the data records. Microcosm has a few!
6969+7070+- [🌌 Constellation](https://constellation.microcosm.blue/), a global backlink index (all social interactions in atproto are links!)
7171+- [🎇 Spacedust](https://spacedust.microcosm.blue/), a firehose of all social interactions
7272+7373+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)!
+56-7
slingshot/src/server.rs
···2222 middleware::{Cors, Tracing},
2323};
2424use poem_openapi::{
2525- ApiResponse, Object, OpenApi, OpenApiService, param::Query, payload::Json, types::Example,
2525+ ApiResponse, ContactObject, ExternalDocumentObject, Object, OpenApi, OpenApiService, Tags,
2626+ param::Query, payload::Json, types::Example,
2627};
27282829fn example_handle() -> String {
···187188 repo: Arc<Repo>,
188189}
189190191191+#[derive(Tags)]
192192+enum ApiTags {
193193+ /// Core ATProtocol-compatible APIs.
194194+ ///
195195+ /// Upstream documentation is available at
196196+ /// https://docs.bsky.app/docs/category/http-reference
197197+ ///
198198+ /// These queries are usually executed directly against the PDS containing
199199+ /// the data being requested. Slingshot offers a caching view of the same
200200+ /// contents with better expected performance and reliability.
201201+ #[oai(rename = "com.atproto.* queries")]
202202+ ComAtproto,
203203+ /// Additional and improved APIs.
204204+ ///
205205+ /// These APIs offer small tweaks to the core ATProtocol APIs, with more
206206+ /// more convenient [request parameters](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.repo.getUriRecord)
207207+ /// or [response formats](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.identity.resolveMiniDoc).
208208+ ///
209209+ /// At the moment, these are namespaced under the `com.bad-example.*` NSID
210210+ /// prefix, but as they stabilize they will likely be moved to either
211211+ /// `blue.microcosm.*` or a slingshot-instance-specific lexicon under its
212212+ /// `did:web` (ie., `blue.microcosm.slingshot.*`). Maybe one day they can
213213+ /// be promoted to the [Lexicon Community](https://discourse.lexicon.community/)
214214+ /// namespace.
215215+ #[oai(rename = "slingshot-specific queries")]
216216+ Custom,
217217+}
218218+190219#[OpenApi]
191220impl Xrpc {
192221 /// com.atproto.repo.getRecord
···195224 ///
196225 /// See also the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)
197226 /// that this endpoint aims to be compatible with.
198198- #[oai(path = "/com.atproto.repo.getRecord", method = "get")]
227227+ #[oai(
228228+ path = "/com.atproto.repo.getRecord",
229229+ method = "get",
230230+ tag = "ApiTags::ComAtproto"
231231+ )]
199232 async fn get_record(
200233 &self,
201234 /// The DID or handle of the repo
···222255 /// com.bad-example.repo.getUriRecord
223256 ///
224257 /// Ergonomic complement to [`com.atproto.repo.getRecord`](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)
225225- /// which accepts an at-uri instead of individual repo/collection/rkey params
226226- #[oai(path = "/com.bad-example.repo.getUriRecord", method = "get")]
258258+ /// which accepts an `at-uri` instead of individual repo/collection/rkey params
259259+ #[oai(
260260+ path = "/com.bad-example.repo.getUriRecord",
261261+ method = "get",
262262+ tag = "ApiTags::Custom"
263263+ )]
227264 async fn get_uri_record(
228265 &self,
229266 /// The at-uri of the record
···281318 ///
282319 /// Like [com.atproto.identity.resolveIdentity](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-identity)
283320 /// but instead of the full `didDoc` it returns an atproto-relevant subset.
284284- #[oai(path = "/com.bad-example.identity.resolveMiniDoc", method = "get")]
321321+ #[oai(
322322+ path = "/com.bad-example.identity.resolveMiniDoc",
323323+ method = "get",
324324+ tag = "ApiTags::Custom"
325325+ )]
285326 async fn resolve_mini_id(
286327 &self,
287328 /// Handle or DID to resolve
···562603 } else {
563604 "http://localhost:3000".to_string()
564605 })
565565- .url_prefix("/xrpc");
606606+ .url_prefix("/xrpc")
607607+ .contact(
608608+ ContactObject::new()
609609+ .name("@microcosm.blue")
610610+ .url("https://bsky.app/profile/microcosm.blue"),
611611+ )
612612+ .description(include_str!("../api-description.md"))
613613+ .external_document(ExternalDocumentObject::new(
614614+ "https://microcosm.blue/slingshot",
615615+ ));
566616567617 let mut app = Route::new()
568618 .nest("/", api_service.scalar())
569569- .nest("/se", api_service.stoplight_elements())
570619 .nest("/openapi.json", api_service.spec_endpoint())
571620 .nest("/xrpc/", api_service);
572621