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

Compare changes

Choose any two refs to compare.

Changed files
+352 -12
constellation
src
bin
server
templates
lexicons
blue.microcosm
com.bad-example
spacedust
ufos
+2 -2
Cargo.lock
··· 1796 1796 [[package]] 1797 1797 name = "fjall" 1798 1798 version = "2.11.2" 1799 - source = "git+https://github.com/fjall-rs/fjall.git#42d811f7c8cc9004407d520d37d2a1d8d246c03d" 1799 + source = "git+https://github.com/fjall-rs/fjall.git?rev=fb229572bb7d1d6966a596994dc1708e47ec57d8#fb229572bb7d1d6966a596994dc1708e47ec57d8" 1800 1800 dependencies = [ 1801 1801 "byteorder", 1802 1802 "byteview", ··· 6049 6049 "clap", 6050 6050 "dropshot", 6051 6051 "env_logger", 6052 - "fjall 2.11.2 (git+https://github.com/fjall-rs/fjall.git)", 6052 + "fjall 2.11.2 (git+https://github.com/fjall-rs/fjall.git?rev=fb229572bb7d1d6966a596994dc1708e47ec57d8)", 6053 6053 "getrandom 0.3.3", 6054 6054 "http", 6055 6055 "jetstream",
+19 -3
constellation/src/bin/main.rs
··· 26 26 #[arg(long)] 27 27 #[clap(default_value = "0.0.0.0:6789")] 28 28 bind: SocketAddr, 29 + /// optionally disable the metrics server 30 + #[arg(long)] 31 + #[clap(default_value_t = false)] 32 + collect_metrics: bool, 29 33 /// metrics server's listen address 30 34 #[arg(long)] 31 35 #[clap(default_value = "0.0.0.0:8765")] ··· 92 96 let bind = args.bind; 93 97 let metrics_bind = args.bind_metrics; 94 98 99 + let collect_metrics = args.collect_metrics; 95 100 let stay_alive = CancellationToken::new(); 96 101 97 102 match args.backend { ··· 102 107 stream, 103 108 bind, 104 109 metrics_bind, 110 + collect_metrics, 105 111 stay_alive, 106 112 ), 107 113 #[cfg(feature = "rocks")] ··· 136 142 stream, 137 143 bind, 138 144 metrics_bind, 145 + collect_metrics, 139 146 stay_alive, 140 147 ); 141 148 eprintln!("run finished: {r:?}"); ··· 147 154 } 148 155 } 149 156 157 + #[allow(clippy::too_many_lines)] 158 + #[allow(clippy::too_many_arguments)] 150 159 fn run( 151 160 mut storage: impl LinkStorage, 152 161 fixture: Option<PathBuf>, ··· 154 163 stream: String, 155 164 bind: SocketAddr, 156 165 metrics_bind: SocketAddr, 166 + collect_metrics: bool, 157 167 stay_alive: CancellationToken, 158 168 ) -> Result<()> { 159 169 ctrlc::set_handler({ ··· 198 208 .build() 199 209 .expect("axum startup") 200 210 .block_on(async { 201 - install_metrics_server(metrics_bind)?; 211 + // Install metrics server only if requested 212 + if collect_metrics { 213 + install_metrics_server(metrics_bind)?; 214 + } 202 215 serve(readable, bind, staying_alive).await 203 216 }) 204 217 .unwrap(); ··· 206 219 } 207 220 }); 208 221 209 - s.spawn(move || { // monitor thread 222 + // only spawn monitoring thread if the metrics server is running 223 + if collect_metrics { 224 + s.spawn(move || { // monitor thread 210 225 let stay_alive = stay_alive.clone(); 211 226 let check_alive = stay_alive.clone(); 212 227 ··· 258 273 } 259 274 } 260 275 stay_alive.drop_guard(); 261 - }); 276 + }); 277 + } 262 278 }); 263 279 264 280 println!("byeeee");
+2 -2
constellation/src/server/mod.rs
··· 25 25 26 26 use acceptable::{acceptable, ExtractAccept}; 27 27 28 - const DEFAULT_CURSOR_LIMIT: u64 = 16; 29 - const DEFAULT_CURSOR_LIMIT_MAX: u64 = 100; 28 + const DEFAULT_CURSOR_LIMIT: u64 = 100; 29 + const DEFAULT_CURSOR_LIMIT_MAX: u64 = 1000; 30 30 31 31 fn get_default_cursor_limit() -> u64 { 32 32 DEFAULT_CURSOR_LIMIT
+1 -1
constellation/templates/dids.html.j2
··· 27 27 {% for did in linking_dids %} 28 28 <pre style="display: block; margin: 1em 2em" class="code"><strong>DID</strong>: {{ did.0 }} 29 29 -> see <a href="/links/all?target={{ did.0|urlencode }}">links to this DID</a> 30 - -> browse <a href="https://pdsls.dev/at://{{ did.0|urlencode }}">this DID record</a></pre> 30 + -> browse <a href="https://pdsls.dev/at://{{ did.0 }}">this DID record</a></pre> 31 31 {% endfor %} 32 32 33 33 {% if let Some(c) = cursor %}
+95
lexicons/blue.microcosm/links/getBacklinks.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "blue.microcosm.links.getBacklinks", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "a list of records linking to any record, identity, or uri", 8 + "parameters": { 9 + "type": "params", 10 + "required": [ 11 + "subject", 12 + "source" 13 + ], 14 + "properties": { 15 + "subject": { 16 + "type": "string", 17 + "format": "uri", 18 + "description": "the target being linked to (at-uri, did, or uri)" 19 + }, 20 + "source": { 21 + "type": "string", 22 + "description": "collection and path specification (e.g., 'app.bsky.feed.like:subject.uri')" 23 + }, 24 + "did": { 25 + "type": "array", 26 + "description": "filter links to those from specific users", 27 + "items": { 28 + "type": "string", 29 + "format": "did" 30 + } 31 + }, 32 + "limit": { 33 + "type": "integer", 34 + "minimum": 1, 35 + "maximum": 100, 36 + "default": 16, 37 + "description": "number of results to return" 38 + } 39 + } 40 + }, 41 + "output": { 42 + "encoding": "application/json", 43 + "schema": { 44 + "type": "object", 45 + "required": [ 46 + "total", 47 + "records" 48 + ], 49 + "properties": { 50 + "total": { 51 + "type": "integer", 52 + "description": "total number of matching links" 53 + }, 54 + "records": { 55 + "type": "array", 56 + "items": { 57 + "type": "ref", 58 + "ref": "#linkRecord" 59 + } 60 + }, 61 + "cursor": { 62 + "type": "string", 63 + "description": "pagination cursor" 64 + } 65 + } 66 + } 67 + } 68 + }, 69 + "linkRecord": { 70 + "type": "object", 71 + "required": [ 72 + "did", 73 + "collection", 74 + "rkey" 75 + ], 76 + "properties": { 77 + "did": { 78 + "type": "string", 79 + "format": "did", 80 + "description": "the DID of the linking record's repository" 81 + }, 82 + "collection": { 83 + "type": "string", 84 + "format": "nsid", 85 + "description": "the collection of the linking record" 86 + }, 87 + "rkey": { 88 + "type": "string", 89 + "format": "record-key", 90 + "description": "the record key of the linking record" 91 + } 92 + } 93 + } 94 + } 95 + }
+99
lexicons/blue.microcosm/links/getManyToManyCounts.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "blue.microcosm.links.getManyToManyCounts", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "count many-to-many relationships with secondary link paths", 8 + "parameters": { 9 + "type": "params", 10 + "required": [ 11 + "subject", 12 + "source", 13 + "pathToOther" 14 + ], 15 + "properties": { 16 + "subject": { 17 + "type": "string", 18 + "format": "uri", 19 + "description": "the primary target being linked to (at-uri, did, or uri)" 20 + }, 21 + "source": { 22 + "type": "string", 23 + "description": "collection and path specification for the primary link" 24 + }, 25 + "pathToOther": { 26 + "type": "string", 27 + "description": "path to the secondary link in the many-to-many record (e.g., 'otherThing.uri')" 28 + }, 29 + "did": { 30 + "type": "array", 31 + "description": "filter links to those from specific users", 32 + "items": { 33 + "type": "string", 34 + "format": "did" 35 + } 36 + }, 37 + "otherSubject": { 38 + "type": "array", 39 + "description": "filter secondary links to specific subjects", 40 + "items": { 41 + "type": "string" 42 + } 43 + }, 44 + "limit": { 45 + "type": "integer", 46 + "minimum": 1, 47 + "maximum": 100, 48 + "default": 16, 49 + "description": "number of results to return" 50 + } 51 + } 52 + }, 53 + "output": { 54 + "encoding": "application/json", 55 + "schema": { 56 + "type": "object", 57 + "required": [ 58 + "counts_by_other_subject" 59 + ], 60 + "properties": { 61 + "counts_by_other_subject": { 62 + "type": "array", 63 + "items": { 64 + "type": "ref", 65 + "ref": "#countBySubject" 66 + } 67 + }, 68 + "cursor": { 69 + "type": "string", 70 + "description": "pagination cursor" 71 + } 72 + } 73 + } 74 + } 75 + }, 76 + "countBySubject": { 77 + "type": "object", 78 + "required": [ 79 + "subject", 80 + "total", 81 + "distinct" 82 + ], 83 + "properties": { 84 + "subject": { 85 + "type": "string", 86 + "description": "the secondary subject being counted" 87 + }, 88 + "total": { 89 + "type": "integer", 90 + "description": "total number of links to this subject" 91 + }, 92 + "distinct": { 93 + "type": "integer", 94 + "description": "number of distinct DIDs linking to this subject" 95 + } 96 + } 97 + } 98 + } 99 + }
+56
lexicons/com.bad-example/identity/resolveMiniDoc.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.bad-example.identity.resolveMiniDoc", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "like com.atproto.identity.resolveIdentity but instead of the full didDoc it returns an atproto-relevant subset", 8 + "parameters": { 9 + "type": "params", 10 + "required": [ 11 + "identifier" 12 + ], 13 + "properties": { 14 + "identifier": { 15 + "type": "string", 16 + "format": "at-identifier", 17 + "description": "handle or DID to resolve" 18 + } 19 + } 20 + }, 21 + "output": { 22 + "encoding": "application/json", 23 + "schema": { 24 + "type": "object", 25 + "required": [ 26 + "did", 27 + "handle", 28 + "pds", 29 + "signing_key" 30 + ], 31 + "properties": { 32 + "did": { 33 + "type": "string", 34 + "format": "did", 35 + "description": "DID, bi-directionally verified if a handle was provided in the query" 36 + }, 37 + "handle": { 38 + "type": "string", 39 + "format": "handle", 40 + "description": "the validated handle of the account or 'handle.invalid' if the handle did not bi-directionally match the DID document" 41 + }, 42 + "pds": { 43 + "type": "string", 44 + "format": "uri", 45 + "description": "the identity's PDS URL" 46 + }, 47 + "signing_key": { 48 + "type": "string", 49 + "description": "the atproto signing key publicKeyMultibase" 50 + } 51 + } 52 + } 53 + } 54 + } 55 + } 56 + }
+54
lexicons/com.bad-example/repo/getUriRecord.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.bad-example.repo.getUriRecord", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "ergonomic complement to com.atproto.repo.getRecord which accepts an at-uri instead of individual repo/collection/rkey params", 8 + "parameters": { 9 + "type": "params", 10 + "required": [ 11 + "at_uri" 12 + ], 13 + "properties": { 14 + "at_uri": { 15 + "type": "string", 16 + "format": "at-uri", 17 + "description": "the at-uri of the record (identifier can be a DID or handle)" 18 + }, 19 + "cid": { 20 + "type": "string", 21 + "format": "cid", 22 + "description": "optional CID of the version of the record. if not specified, return the most recent version. if specified and a newer version exists, returns 404." 23 + } 24 + } 25 + }, 26 + "output": { 27 + "encoding": "application/json", 28 + "schema": { 29 + "type": "object", 30 + "required": [ 31 + "uri", 32 + "value" 33 + ], 34 + "properties": { 35 + "uri": { 36 + "type": "string", 37 + "format": "at-uri", 38 + "description": "at-uri for this record" 39 + }, 40 + "cid": { 41 + "type": "string", 42 + "format": "cid", 43 + "description": "CID for this exact version of the record" 44 + }, 45 + "value": { 46 + "type": "unknown", 47 + "description": "the record itself" 48 + } 49 + } 50 + } 51 + } 52 + } 53 + } 54 + }
+2
spacedust/src/error.rs
··· 30 30 TooManySourcesWanted, 31 31 #[error("more wantedSubjectDids were requested than allowed (max 10,000)")] 32 32 TooManyDidsWanted, 33 + #[error("more wantedSubjectPrefixes were requested than allowed (max 100)")] 34 + TooManySubjectPrefixesWanted, 33 35 #[error("more wantedSubjects were requested than allowed (max 50,000)")] 34 36 TooManySubjectsWanted, 35 37 }
+11 -2
spacedust/src/server.rs
··· 227 227 #[serde(default)] 228 228 pub wanted_subjects: HashSet<String>, 229 229 #[serde(default)] 230 + pub wanted_subject_prefixes: HashSet<String>, 231 + #[serde(default)] 230 232 pub wanted_subject_dids: HashSet<String>, 231 233 #[serde(default)] 232 234 pub wanted_sources: HashSet<String>, ··· 241 243 /// 242 244 /// The at-uri must be url-encoded 243 245 /// 244 - /// Pass this parameter multiple times to specify multiple collections, like 246 + /// Pass this parameter multiple times to specify multiple subjects, like 245 247 /// `wantedSubjects=[...]&wantedSubjects=[...]` 246 248 pub wanted_subjects: String, 249 + /// One or more at-uri, URI, or DID prefixes to receive links about 250 + /// 251 + /// The uri must be url-encoded 252 + /// 253 + /// Pass this parameter multiple times to specify multiple prefixes, like 254 + /// `wantedSubjectPrefixes=[...]&wantedSubjectPrefixes=[...]` 255 + pub wanted_subject_prefixes: String, 247 256 /// One or more DIDs to receive links about 248 257 /// 249 - /// Pass this parameter multiple times to specify multiple collections 258 + /// Pass this parameter multiple times to specify multiple subjects 250 259 pub wanted_subject_dids: String, 251 260 /// One or more link sources to receive links about 252 261 ///
+10 -1
spacedust/src/subscriber.rs
··· 124 124 let query = &self.query; 125 125 126 126 // subject + subject DIDs are logical OR 127 - if !(query.wanted_subjects.is_empty() && query.wanted_subject_dids.is_empty() 127 + if !(query.wanted_subjects.is_empty() 128 + && query.wanted_subject_prefixes.is_empty() 129 + && query.wanted_subject_dids.is_empty() 128 130 || query.wanted_subjects.contains(&properties.subject) 131 + || query 132 + .wanted_subject_prefixes 133 + .iter() 134 + .any(|p| properties.subject.starts_with(p)) 129 135 || properties 130 136 .subject_did 131 137 .as_ref() ··· 154 160 } 155 161 if opts.wanted_subject_dids.len() > 10_000 { 156 162 return Err(SubscriberUpdateError::TooManyDidsWanted); 163 + } 164 + if opts.wanted_subject_prefixes.len() > 100 { 165 + return Err(SubscriberUpdateError::TooManySubjectPrefixesWanted); 157 166 } 158 167 if opts.wanted_subjects.len() > 50_000 { 159 168 return Err(SubscriberUpdateError::TooManySubjectsWanted);
+1 -1
ufos/Cargo.toml
··· 13 13 clap = { version = "4.5.31", features = ["derive"] } 14 14 dropshot = "0.16.0" 15 15 env_logger = "0.11.7" 16 - fjall = { git = "https://github.com/fjall-rs/fjall.git", features = ["lz4"] } 16 + fjall = { git = "https://github.com/fjall-rs/fjall.git", rev = "fb229572bb7d1d6966a596994dc1708e47ec57d8", features = ["lz4"] } 17 17 getrandom = "0.3.3" 18 18 http = "1.3.1" 19 19 jetstream = { path = "../jetstream", features = ["metrics"] }