[READ-ONLY] Mirror of https://github.com/flo-bit/contrail. atproto backend in a bottle flo-bit.dev/contrail/
0

Configure Feed

Select the types of activity you want to include in your feed.

update namespace

Florian (Mar 18, 2026, 12:46 AM +0100) 6a2cdd15 e6906104

+40 -26
+1 -1
lexicons-generated/contrail/admin/getCursor.json lexicons-generated/com/example/admin/getCursor.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "contrail.admin.getCursor", 3 + "id": "com.example.admin.getCursor", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+1 -1
lexicons-generated/contrail/admin/getOverview.json lexicons-generated/com/example/admin/getOverview.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "contrail.admin.getOverview", 3 + "id": "com.example.admin.getOverview", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+1 -1
lexicons-generated/contrail/admin/reset.json lexicons-generated/com/example/admin/reset.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "contrail.admin.reset", 3 + "id": "com.example.admin.reset", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+1 -1
lexicons-generated/contrail/admin/sync.json lexicons-generated/com/example/admin/sync.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "contrail.admin.sync", 3 + "id": "com.example.admin.sync", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+1 -1
lexicons-generated/contrail/getProfile.json lexicons-generated/com/example/getProfile.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "contrail.getProfile", 3 + "id": "com.example.getProfile", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+20 -15
scripts/generate-lexicons.ts
··· 6 6 * - {nsid}.getUsers — query with limit/cursor 7 7 * - {nsid}.getStats — query returning collection stats 8 8 * 9 - * Plus admin endpoints: 10 - * - contrail.admin.getCursor 11 - * - contrail.admin.getOverview 12 - * - contrail.admin.discover 13 - * - contrail.admin.backfill 9 + * Plus namespaced endpoints: 10 + * - {namespace}.admin.getCursor 11 + * - {namespace}.admin.getOverview 12 + * - {namespace}.admin.sync 13 + * - {namespace}.admin.reset 14 + * - {namespace}.getProfile 14 15 * 15 16 * Usage: npx tsx scripts/generate-lexicons.ts 16 17 */ ··· 313 314 }; 314 315 } 315 316 317 + // --- Namespace --- 318 + 319 + const ns = config.namespace!; 320 + 316 321 // --- Admin endpoints --- 317 322 318 323 console.log("Generating admin endpoints..."); 319 324 320 - writeLexicon("contrail.admin.getCursor", { 325 + writeLexicon(`${ns}.admin.getCursor`, { 321 326 lexicon: 1, 322 - id: "contrail.admin.getCursor", 327 + id: `${ns}.admin.getCursor`, 323 328 defs: { 324 329 main: { 325 330 type: "query", ··· 339 344 }, 340 345 }); 341 346 342 - writeLexicon("contrail.admin.getOverview", { 347 + writeLexicon(`${ns}.admin.getOverview`, { 343 348 lexicon: 1, 344 - id: "contrail.admin.getOverview", 349 + id: `${ns}.admin.getOverview`, 345 350 defs: { 346 351 main: { 347 352 type: "query", ··· 373 378 }, 374 379 }); 375 380 376 - writeLexicon("contrail.admin.sync", { 381 + writeLexicon(`${ns}.admin.sync`, { 377 382 lexicon: 1, 378 - id: "contrail.admin.sync", 383 + id: `${ns}.admin.sync`, 379 384 defs: { 380 385 main: { 381 386 type: "query", ··· 408 413 }, 409 414 }); 410 415 411 - writeLexicon("contrail.admin.reset", { 416 + writeLexicon(`${ns}.admin.reset`, { 412 417 lexicon: 1, 413 - id: "contrail.admin.reset", 418 + id: `${ns}.admin.reset`, 414 419 defs: { 415 420 main: { 416 421 type: "query", ··· 431 436 432 437 // --- getProfile endpoint --- 433 438 434 - writeLexicon("contrail.getProfile", { 439 + writeLexicon(`${ns}.getProfile`, { 435 440 lexicon: 1, 436 - id: "contrail.getProfile", 441 + id: `${ns}.getProfile`, 437 442 defs: { 438 443 main: { 439 444 type: "query",
+4 -1
scripts/sync.ts
··· 5 5 * Usage: npx tsx scripts/sync.ts [base_url] [admin_secret] 6 6 */ 7 7 8 + import { config } from "../src/config"; 9 + 8 10 async function main() { 9 11 const base = process.argv[2] || "http://localhost:8787"; 10 12 const secret = process.argv[3]; 13 + const ns = config.namespace; 11 14 12 15 const headers: Record<string, string> = {}; 13 16 if (secret) { ··· 20 23 let lastRemaining = -1; 21 24 22 25 while (true) { 23 - const res = await fetch(`${base}/xrpc/contrail.admin.sync`, { headers }); 26 + const res = await fetch(`${base}/xrpc/${ns}.admin.sync`, { headers }); 24 27 const result = (await res.json()) as { 25 28 discovered: number; 26 29 backfilled: number;
+1
src/config.ts
··· 1 1 import type { ContrailConfig } from "./core/types"; 2 2 3 3 export const config: ContrailConfig = { 4 + namespace: "rsvp.atmo", 4 5 collections: { 5 6 "community.lexicon.calendar.event": { 6 7 relations: {
+6 -4
src/core/router/admin.ts
··· 24 24 await next(); 25 25 }; 26 26 27 - app.get("/xrpc/contrail.admin.getCursor", async (c) => { 27 + const ns = config.namespace; 28 + 29 + app.get(`/xrpc/${ns}.admin.getCursor`, async (c) => { 28 30 const cursor = await getLastCursor(db); 29 31 if (cursor === null) return c.json({ cursor: null }); 30 32 ··· 36 38 }); 37 39 }); 38 40 39 - app.get("/xrpc/contrail.admin.getOverview", async (c) => { 41 + app.get(`/xrpc/${ns}.admin.getOverview`, async (c) => { 40 42 const result = await db 41 43 .prepare( 42 44 "SELECT collection, COUNT(*) as records, COUNT(DISTINCT did) as unique_users FROM records GROUP BY collection" ··· 50 52 }); 51 53 }); 52 54 53 - app.get("/xrpc/contrail.admin.sync", requireAdmin, async (c) => { 55 + app.get(`/xrpc/${ns}.admin.sync`, requireAdmin, async (c) => { 54 56 const deadline = Date.now() + 25_000; 55 57 const concurrency = parseIntParam(c.req.query("concurrency"), 25) ?? 25; 56 58 ··· 124 126 }); 125 127 }); 126 128 127 - app.get("/xrpc/contrail.admin.reset", requireAdmin, async (c) => { 129 + app.get(`/xrpc/${ns}.admin.reset`, requireAdmin, async (c) => { 128 130 const tables = ["records", "counts", "backfills", "discovery", "cursor", "identities"]; 129 131 await db.batch(tables.map((t) => db.prepare(`DELETE FROM ${t}`))); 130 132 return c.json({ ok: true });
+3 -1
src/core/router/index.ts
··· 19 19 app.get("/health", (c) => c.json({ status: "ok" })); 20 20 app.get("/xrpc/_health", (c) => c.json({ status: "ok" })); 21 21 22 - app.get("/xrpc/contrail.getProfile", async (c) => { 22 + const ns = config.namespace; 23 + 24 + app.get(`/xrpc/${ns}.getProfile`, async (c) => { 23 25 const actor = c.req.query("actor"); 24 26 if (!actor) return c.json({ error: "actor parameter required" }, 400); 25 27
+1
src/core/types.ts
··· 51 51 ]; 52 52 53 53 export interface ContrailConfig { 54 + namespace: string; 54 55 collections: Record<string, CollectionConfig>; 55 56 profiles?: string[]; 56 57 relays?: string[];