diff --git a/src/index.ts b/src/index.ts index 9902e0a..339c4f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -288,6 +288,20 @@ async function collections(url: URL, env: Env): Promise { return json({ collections: results }, 200, { "cache-control": "public, max-age=300" }); } +const PUBLIC_BASE = "https://pub-735e1688181b45e49c925eed81fe2da7.r2.dev"; + +/** which drained segments hold rows for a namespace, as public parquet URLs the page can hand to duckdb-wasm. + * app.bsky is private by policy, so it is refused here. */ +async function partitions(url: URL, env: Env): Promise { + const ns = url.searchParams.get("ns"); + if (ns === null || !/^[a-z0-9-]+\.[a-z0-9-]+$/i.test(ns)) return json({ error: "ns must be an authority like fm.teal" }, 400); + if (ns === "app.bsky") return json({ error: "app.bsky rows are not public; use the aggregates" }, 403); + const listed = await env.PUBLIC.list({ prefix: `events/ns=${ns}/`, limit: 1000 }); + const urls = listed.objects.map((o) => `${PUBLIC_BASE}/${o.key}`); + const bytes = listed.objects.reduce((a, o) => a + o.size, 0); + return json({ ns, files: urls, bytes, truncated: listed.truncated }, 200, { "cache-control": "public, max-age=120" }); +} + async function namespaces(env: Env): Promise { const { results } = await env.DB.prepare( "SELECT ns, SUM(count) AS count, COUNT(DISTINCT nsid) AS members FROM segment_collections GROUP BY ns ORDER BY count DESC", @@ -310,13 +324,15 @@ export default { return collections(url, env); case "/api/namespaces": return namespaces(env); + case "/api/partitions": + return partitions(url, env); case "/": return new Response(page, { headers: { "content-type": "text/html; charset=utf-8", "cache-control": "public, max-age=300" } }); case "/api": return json({ name: "strata", what: "per-segment, per-collection event counts of the stream.waow.tech archive", - endpoints: ["/api/progress", "/api/heat?step=N&top=N&group=namespaces|collections", "/api/heat?step=N&ns=", "/api/namespaces", "/api/collections?ns="], + endpoints: ["/api/progress", "/api/heat?step=N&top=N&group=namespaces|collections", "/api/heat?step=N&ns=", "/api/namespaces", "/api/collections?ns=", "/api/partitions?ns="], source: "https://tangled.org/zat.dev/strata", }); default: diff --git a/src/page.html b/src/page.html index e7798fe..67015a0 100644 --- a/src/page.html +++ b/src/page.html @@ -4,263 +4,285 @@ strata - - + + + + + +
-

strata

-

the shape of the stream.waow.tech archive, by lexicon — read from the sealed segments, not the process

+ ~  s t r a t a  ~ + · reading the archive +

the stream archive, cut across. every lexicon on the network is a layer: thickness is how much of it there is, shade is how it comes and goes.

+
+
+
—segments
+
—events
+
—namespaces
+
—collections
+
-
loading…
+
+ + + + + +
-
- - - - -
+
+ +
hover a layer to read it · click a namespace to split it into its collections
+
-
-
- - +
+ nothing in that column + fewermore +
-
fewermore
-
- x is archive position (segment index; each segment ≈ 3.2M events, ~270 MB). the dates under it are when - stream witnessed the events, not when they happened: the bootstrap era replayed years of history - inside a six-day window from 2026-07-28, so those columns are dense; segments after it are live-era, about - two minutes each. counts come from each segment's collection index; account/identity/sync markers carry no - collection and are excluded. "other" folds every collection outside the top rows. -
-
- +

columns are archive position: sealed segments in order, ≈3.2M events each. the dates are when stream witnessed the events, not when they happened — the bootstrap band replayed years of history inside six days from 2026-07-28, so it is dense; the live band is about two minutes per segment. account, identity and sync markers carry no collection and are not counted. "other" is every namespace below the ones shown.

+ + +
ask the tail
+
+
+ + pick one + +
+
+ +
every row this namespace ever wrote — rows(seq, time_us, did, kind, op, collection, rkey) — queried in your browser with duckdb, straight from the public archive. app.bsky is aggregates-only by policy.
+
+
+
+