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
···
### 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.
The core APIs will not change, since they are standard third-party `com.atproto` query APIs from ATProtocol.
···
_(work on this endpoint is in progress)_
## 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.
···
- [🌌 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)!
···
### Current status
+
> [!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.
···
_(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: <your pds>
+
atproto-proxy: did:web:<slingshot domain>#slingshot
+
```
+
+
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.
+
+
> [!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.
···
- [🌌 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
+
> [!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)!
+2 -2
slingshot/src/main.rs
···
/// - an HTTPS certs will be automatically configured with Acme/letsencrypt
/// - TODO: a rate-limiter will be installed
#[arg(long)]
-
host: Option<String>,
/// email address for letsencrypt contact
///
/// recommended in production, i guess?
···
server_cache_handle,
identity,
repo,
-
args.host,
args.acme_contact,
args.certs,
server_shutdown,
···
/// - an HTTPS certs will be automatically configured with Acme/letsencrypt
/// - TODO: a rate-limiter will be installed
#[arg(long)]
+
domain: Option<String>,
/// email address for letsencrypt contact
///
/// recommended in production, i guess?
···
server_cache_handle,
identity,
repo,
+
args.domain,
args.acme_contact,
args.certs,
server_shutdown,
+33 -29
slingshot/src/server.rs
···
enum ApiTags {
/// Core ATProtocol-compatible APIs.
///
-
/// 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
···
/// 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.
#[oai(rename = "slingshot-specific queries")]
Custom,
}
···
///
/// 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.
#[oai(
path = "/com.atproto.repo.getRecord",
method = "get",
···
///
/// 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)
Query(cid): Query<Option<String>>,
) -> GetRecordResponse {
self.get_record_impl(repo, collection, rkey, cid).await
···
///
/// 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.
Query(cid): Query<Option<String>>,
) -> GetRecordResponse {
let bad_at_uri = || {
···
/// 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.
///
-
/// 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",
···
///
/// - 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<> {
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())
···
cache: HybridCache<String, CachedRecord>,
identity: Identity,
repo: Repo,
-
host: Option<String>,
acme_contact: Option<String>,
certs: Option<PathBuf>,
shutdown: CancellationToken,
···
"Slingshot",
env!("CARGO_PKG_VERSION"),
)
-
.server(if let Some(ref h) = host {
format!("https://{h}")
} else {
"http://localhost:3000".to_string()
···
.nest("/openapi", api_service.spec_endpoint())
.nest("/xrpc/", api_service);
-
if let Some(host) = host {
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("alskfjalksdjf");
-
app = app.at("/.well-known/did.json", get_did_doc(&host));
let mut auto_cert = AutoCert::builder()
.directory_url(LETS_ENCRYPT_PRODUCTION)
-
.domain(&host);
if let Some(contact) = acme_contact {
auto_cert = auto_cert.contact(contact);
}
···
enum ApiTags {
/// Core ATProtocol-compatible APIs.
///
+
/// > [!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
···
/// 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).
///
+
/// > [!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,
}
···
///
/// Get a single record from a repository. Does not require auth.
///
+
/// > [!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",
···
///
/// If not specified, then return the most recent version.
///
+
/// 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<Option<String>>,
) -> GetRecordResponse {
self.get_record_impl(repo, collection, rkey, cid).await
···
///
/// If not specified, then return the most recent version.
///
+
/// > [!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<Option<String>>,
) -> GetRecordResponse {
let bad_at_uri = || {
···
/// Resolves an atproto [`handle`](https://atproto.com/guides/glossary#handle)
/// (hostname) to a [`DID`](https://atproto.com/guides/glossary#did-decentralized-id).
///
+
/// > [!tip]
+
/// > Compatibility note: Slingshot will **always bi-directionally verify
+
/// > against the DID document**, which is optional according to the
+
/// > authoritative lexicon.
///
+
/// > [!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",
···
///
/// - 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(domain: &str) -> impl Endpoint + use<> {
let doc = poem::web::Json(AppViewDoc {
+
id: format!("did:web:{domain}"),
service: [AppViewService {
id: "#slingshot".to_string(),
r#type: "SlingshotRecordProxy".to_string(),
+
service_endpoint: format!("https://{domain}"),
}],
});
make_sync(move |_| doc.clone())
···
cache: HybridCache<String, CachedRecord>,
identity: Identity,
repo: Repo,
+
domain: Option<String>,
acme_contact: Option<String>,
certs: Option<PathBuf>,
shutdown: CancellationToken,
···
"Slingshot",
env!("CARGO_PKG_VERSION"),
)
+
.server(if let Some(ref h) = domain {
format!("https://{h}")
} else {
"http://localhost:3000".to_string()
···
.nest("/openapi", api_service.spec_endpoint())
.nest("/xrpc/", api_service);
+
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(&domain));
let mut auto_cert = AutoCert::builder()
.directory_url(LETS_ENCRYPT_PRODUCTION)
+
.domain(&domain);
if let Some(contact) = acme_contact {
auto_cert = auto_cert.contact(contact);
}