What happened on the site, kept whole: one Event per request worth
keeping, with everything the request said about itself and everything
Cloudflare said about where it came from. The archive writes each one as
a row of events (the owner, 2026-10-03: "anywhere in the app where we
are dropping data we should plug", and of visitors, "Everything, linked").
Nothing here is shown on the site. It is for the owner's admin backend, which reads the archive's tables.
COLUMNS and values are the table's shape: the archive's migration
spells the same columns out, and a test there holds the two together.
The cookie that says when this browser was last counted as a visit, in Unix milliseconds.
18pub const VISIT: &str = "lmjtfy_visit";
A visit ends after this long with no page viewed.
What Cloudflare says of where a request came from (request.cf), and
the address it came from. All of it may be missing: a dev server has
none.
27#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] 28#[serde(default)] 29pub struct Origin { 30 pub ip: String, 31 pub country: String, 32 pub region: String, 33 pub city: String, 34 pub postcode: String, 35 pub timezone: String, 36 pub latitude: Option<f64>, 37 pub longitude: Option<f64>,
The network's number, and whose it is: the visitor's ISP.
The Cloudflare data centre that took the request.
One thing that happened.
view, answer, gate, vote, more, card, fetch, moved,
live, left, and from the page itself read (how long a page was
looked at, and how far down) and out (a link followed off the site).
The query as it came, the question in a shared link included.
59 pub query: String,
The question typed, asked or voted on, cleaned.
61 pub input: String,
What came of it: how an ask ended, which way a vote went, clone or
pull, shared for a page opened with a question in its link.
64 pub detail: String,
The response's status, or 0 when the event is not a response.
66 pub status: f64,
Requests sent to Jev or the LLM for it, and requests answered from what was kept.
Whether an LLM was needed, 1 or 0.
72 pub llm: f64,
How long it took or lasted, in milliseconds.
74 pub took_ms: f64,
1 on the first page this browser was ever given, its first of the UTC day, and its first in half an hour.
The page that linked here, whole.
81 pub referrer: String,
A campaign the link named (utm_source or ref).
83 pub source: String,
browser, git, bot or other, and for a browser its family,
operating system and mobile or desktop: read from agent, for
grouping.
The browser's id (the lmjtfy_browser cookie), which ties one
browser's events together.
The screen and the window, 1920x1080, as the page says them.
A value of one column.
117impl Event {
The columns of events, after id and at_ms, in the order of
values.
120 pub const COLUMNS: [&str; 41] = [ 121 "what", "method", "host", "path", "query", "input", "detail", "status", "sent", "kept", "llm", "took_ms", "first", "daily", "session", "referrer", 122 "source", "client", "family", "os", "device", "language", "agent", "browser", "ip", "country", "region", "city", "postcode", "timezone", "latitude", 123 "longitude", "asn", "network", "colo", "protocol", "medium", "campaign", "screen", "viewport", "scroll", 124 ];
126 pub fn values(&self) -> Vec<Cell> { 127 let text = |value: &str| Cell::Text(value.to_owned()); 128 let number = |value: Option<f64>| value.map_or(Cell::Null, Cell::Number); 129 let origin = &self.origin; 130 vec![ 131 text(&self.what), 132 text(&self.method), 133 text(&self.host), 134 text(&self.path), 135 text(&self.query), 136 text(&self.input), 137 text(&self.detail), 138 Cell::Number(self.status), 139 Cell::Number(self.sent), 140 Cell::Number(self.kept), 141 Cell::Number(self.llm), 142 Cell::Number(self.took_ms), 143 Cell::Number(self.first), 144 Cell::Number(self.daily), 145 Cell::Number(self.session), 146 text(&self.referrer), 147 text(&self.source), 148 text(&self.client), 149 text(&self.family), 150 text(&self.os), 151 text(&self.device), 152 text(&self.language), 153 text(&self.agent), 154 text(&self.browser), 155 text(&origin.ip), 156 text(&origin.country), 157 text(&origin.region), 158 text(&origin.city), 159 text(&origin.postcode), 160 text(&origin.timezone), 161 number(origin.latitude), 162 number(origin.longitude), 163 number(origin.asn.map(f64::from)), 164 text(&origin.network), 165 text(&origin.colo), 166 text(&format!("{} {}", origin.protocol, origin.tls).trim().to_owned()), 167 text(&self.medium), 168 text(&self.campaign), 169 text(&self.screen), 170 text(&self.viewport), 171 Cell::Number(self.scroll), 172 ] 173 }
What a request says of itself, before it is known what came of it.
browser is the id in its cookie, if it has one.
177 pub fn from(method: &Method, uri: &Uri, headers: &HeaderMap, browser: Option<String>, origin: Origin) -> Event { 178 let agent = text(headers, header::USER_AGENT.as_str()); 179 let lower = agent.to_ascii_lowercase(); 180 let client = client(&lower, headers); 181 let person = client == "browser"; 182 let query = uri.query().unwrap_or_default(); 183 Event { 184 method: method.as_str().to_owned(), 185 host: cut(text(headers, header::HOST.as_str()), 200), 186 path: cut(uri.path(), 500), 187 query: cut(query, 2000), 188 referrer: cut(text(headers, header::REFERER.as_str()), 1000), 189 source: tag(query, "utm_source").or_else(|| tag(query, "ref")).unwrap_or_default(), 190 medium: tag(query, "utm_medium").unwrap_or_default(), 191 campaign: tag(query, "utm_campaign").unwrap_or_default(), 192 client: client.to_owned(), 193 family: if person { family(&lower) } else { "" }.to_owned(), 194 os: if person { os(&lower) } else { "" }.to_owned(), 195 device: if person { device(&lower, headers) } else { "" }.to_owned(), 196 language: cut(text(headers, header::ACCEPT_LANGUAGE.as_str()), 200), 197 agent: cut(agent, 500), 198 browser: browser.unwrap_or_default(), 199 origin, 200 ..Event::default() 201 } 202 }
What a GET is, if it is worth keeping: not the site's own scripts
and fonts, or git's first request. A POST is named by the handler
that knows what came of it.
207 pub fn got(mut self) -> Option<Event> { 208 if self.method != "GET" { 209 return None; 210 } 211 self.what = match self.path.as_str() { 212 "/datastar.js" | "/emoji.woff2" | "/live.js" | "/rules.svg" | "/favicon.ico" => return None, 213 // The socket's own event is written by the archive, which keeps 214 // it to say how long the page stayed. 215 "/live" => return None, 216 path if path.ends_with("/info/refs") || path.starts_with("/icons/") => return None, 217 "/feed" => "more", 218 "/card.png" => "card", 219 _ => "view", 220 } 221 .to_owned(); 222 if self.what == "view" && self.query.split('&').any(|pair| pair.starts_with("q=") && pair.len() > 2) { 223 self.detail = "shared".into(); 224 } 225 Some(self) 226 }
The question it was about.
Counts a browser's page view as a visit: whether it is the browser's first page ever (it came with no cookie of the site's), its first today, and its first in half an hour. Returns the cookie that says it was counted now. Only a browser's page is a visit.
248 pub fn visit(&mut self, headers: &HeaderMap, now_ms: f64) -> Option<String> { 249 if self.what != "view" || self.client != "browser" { 250 return None; 251 } 252 let cookies = text(headers, header::COOKIE.as_str()); 253 let last = cookies.split(';').filter_map(|pair| pair.trim().strip_prefix(VISIT)?.strip_prefix('=')?.parse::<f64>().ok()).find(|last| last.is_finite() && *last <= now_ms); 254 let flag = |is: bool| if is { 1.0 } else { 0.0 }; 255 self.first = flag(cookies.trim().is_empty()); 256 self.daily = flag(last.is_none_or(|last| (last / DAY_MS).floor() < (now_ms / DAY_MS).floor())); 257 self.session = flag(last.is_none_or(|last| now_ms - last >= SESSION_MS)); 258 Some(format!("{VISIT}={now_ms:.0}; Path=/; Max-Age=31536000; Secure; HttpOnly; SameSite=Lax")) 259 } 260}
What kind of client asked, from what it says of itself.
271fn client(agent: &str, headers: &HeaderMap) -> &'static str { 272 const BOTS: [&str; 10] = ["bot", "crawler", "spider", "preview", "facebookexternalhit", "slurp", "curl/", "python", "wget", "headless"]; 273 if agent.starts_with("git/") || agent.starts_with("cargo") { 274 "git" 275 } else if BOTS.iter().any(|mark| agent.contains(mark)) { 276 "bot" 277 } else if headers.contains_key("sec-fetch-mode") || agent.starts_with("mozilla/") { 278 "browser" 279 } else { 280 "other" 281 } 282}
The browser's family. The order matters: Edge and Opera say Chrome too, and Chrome says Safari.
The operating system. iPhones say "like Mac OS X" and Android says Linux, so they are looked for first.
One of a link's campaign tags (utm_source, utm_medium, ...), if it
has it.
What the page says of itself when it is left or a link off the site is
followed (page.js, posted to /seen). Every part is the page's word
and is checked before it is kept.
Milliseconds it was in view since it last said.
335impl Event {
The event of a page's report: read, or out when it names a link.
The request is the report's own (POST /seen); the page it is about
is in the report.
339 pub fn seen(mut self, seen: &Seen) -> Event { 340 // A size is two numbers and an `x`, and nothing else. 341 let size = |text: &str| { 342 let fits = text.len() <= 11 && text.split_once('x').is_some_and(|(wide, high)| [wide, high].iter().all(|n| !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit()))); 343 if fits { text.to_owned() } else { String::new() } 344 }; 345 let within = |n: f64, most: f64| if n.is_finite() { n.clamp(0.0, most) } else { 0.0 }; 346 self.what = if seen.out.is_empty() { "read" } else { "out" }.to_owned(); 347 self.detail = cut(&seen.out, 500); 348 if seen.path.starts_with('/') { 349 self.path = cut(&seen.path, 500); 350 self.query = cut(seen.query.trim_start_matches('?'), 2000); 351 } 352 // A page cannot have been looked at for longer than a day. 353 self.took_ms = within(seen.ms, 86_400_000.0); 354 self.scroll = within(seen.scroll, 1.0); 355 self.screen = size(&seen.screen); 356 self.viewport = size(&seen.viewport); 357 self 358 } 359}
361#[cfg(test)] 362mod tests { 363 use super::*; 364 365 fn headers(pairs: &[(&str, &str)]) -> HeaderMap { 366 let mut headers = HeaderMap::new(); 367 for (name, value) in pairs { 368 headers.insert(http::HeaderName::from_bytes(name.as_bytes()).unwrap(), value.parse().unwrap()); 369 } 370 headers 371 } 372 373 const CHROME: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"; 374 375 fn event(method: Method, uri: &str, from: &[(&str, &str)]) -> Event { 376 Event::from(&method, &uri.parse().unwrap(), &headers(from), None, Origin::default()) 377 } 378 379 #[test] 380 fn a_get_is_named_for_what_it_is() { 381 let what = |uri: &str| event(Method::GET, uri, &[]).got().map(|event| event.what); 382 assert_eq!(what("/").as_deref(), Some("view")); 383 assert_eq!(what("/lmjtfy.git/packages/rules/").as_deref(), Some("view")); 384 assert_eq!(what("/feed?ms=1&q=x").as_deref(), Some("more")); 385 assert_eq!(what("/card.png?q=x").as_deref(), Some("card")); 386 // The site's own parts, the socket and git's first request are not 387 // named here. 388 assert_eq!(what("/live"), None); 389 assert_eq!(what("/emoji.woff2"), None); 390 assert_eq!(what("/icons/file-rust.png"), None); 391 assert_eq!(what("/lmjtfy.git/info/refs?service=git-upload-pack"), None); 392 // A POST is its handler's to name. 393 assert_eq!(event(Method::POST, "/ask", &[]).got(), None); 394 } 395 396 #[test] 397 fn nothing_the_request_said_is_dropped() { 398 let from = [ 399 ("referer", "https://discord.com/channels/1/2?x=y"), 400 ("host", "lmjtfy.fun"), 401 ("user-agent", CHROME), 402 ("accept-language", "en-GB,en;q=0.9"), 403 ]; 404 let uri: Uri = "/?q=Is+my+boss+a+lizard%3F&utm_source=discord&utm_medium=chat&utm_campaign=launch".parse().unwrap(); 405 let origin = Origin { ip: "203.0.113.7".into(), country: "GB".into(), asn: Some(2856), network: "British Telecommunications PLC".into(), ..Origin::default() }; 406 let event = Event::from(&Method::GET, &uri, &headers(&from), Some("0b5f2a1e".into()), origin).got().unwrap(); 407 assert_eq!((event.what.as_str(), event.detail.as_str(), event.path.as_str()), ("view", "shared", "/")); 408 assert_eq!(event.query, "q=Is+my+boss+a+lizard%3F&utm_source=discord&utm_medium=chat&utm_campaign=launch"); 409 assert_eq!(event.referrer, "https://discord.com/channels/1/2?x=y"); 410 assert_eq!((event.source.as_str(), event.language.as_str(), event.agent.as_str()), ("discord", "en-GB,en;q=0.9", CHROME)); 411 assert_eq!((event.medium.as_str(), event.campaign.as_str()), ("chat", "launch")); 412 assert_eq!((event.client.as_str(), event.family.as_str(), event.os.as_str(), event.device.as_str()), ("browser", "Chrome", "Windows", "desktop")); 413 assert_eq!((event.browser.as_str(), event.origin.ip.as_str(), event.origin.asn), ("0b5f2a1e", "203.0.113.7", Some(2856))); 414 } 415 416 #[test] 417 fn every_column_has_its_value_and_an_event_survives_the_wire() { 418 let event = Event { what: "answer".into(), origin: Origin { asn: Some(7), latitude: Some(1.5), ..Origin::default() }, ..Event::default() }; 419 let values = event.values(); 420 assert_eq!(values.len(), Event::COLUMNS.len()); 421 let at = |name: &str| values[Event::COLUMNS.iter().position(|column| *column == name).unwrap()].clone(); 422 assert_eq!(at("what"), Cell::Text("answer".into())); 423 assert_eq!(at("asn"), Cell::Number(7.0)); 424 assert_eq!(at("latitude"), Cell::Number(1.5)); 425 assert_eq!(at("longitude"), Cell::Null); 426 let wire = serde_json::to_string(&event).unwrap(); 427 assert_eq!(serde_json::from_str::<Event>(&wire).unwrap(), event); 428 } 429 430 #[test] 431 fn a_pages_report_is_checked_before_it_is_kept() { 432 let report = event(Method::POST, "/seen", &[("user-agent", CHROME)]); 433 let read = report.clone().seen(&Seen { path: "/rules".into(), query: "?answerable=no".into(), ms: 4200.0, scroll: 0.5, screen: "1920x1080".into(), viewport: "1200x800".into(), out: String::new() }); 434 assert_eq!((read.what.as_str(), read.path.as_str(), read.query.as_str()), ("read", "/rules", "answerable=no")); 435 assert_eq!((read.took_ms, read.scroll, read.screen.as_str(), read.viewport.as_str()), (4200.0, 0.5, "1920x1080", "1200x800")); 436 // A link followed off the site. 437 let out = report.clone().seen(&Seen { path: "/".into(), out: "https://docs.typesafe.ai/".into(), ..Seen::default() }); 438 assert_eq!((out.what.as_str(), out.detail.as_str()), ("out", "https://docs.typesafe.ai/")); 439 // What a page makes up is cut down to what it could be. 440 let odd = report.seen(&Seen { path: "elsewhere".into(), ms: f64::INFINITY, scroll: 7.0, screen: "<b>".into(), viewport: "9999999x9999999".into(), ..Seen::default() }); 441 assert_eq!((odd.path.as_str(), odd.took_ms, odd.scroll, odd.screen.as_str(), odd.viewport.as_str()), ("/seen", 0.0, 1.0, "", "")); 442 assert_eq!(serde_json::from_str::<Seen>("{\"ms\": 5}").unwrap().ms, 5.0); 443 } 444 445 #[test] 446 fn clients_are_classed() { 447 let class = |agent: &str| { 448 let event = event(Method::GET, "/", &[("user-agent", agent)]); 449 (event.client, event.family, event.os, event.device) 450 }; 451 let is = |client: &str, family: &str, os: &str, device: &str| (client.to_owned(), family.to_owned(), os.to_owned(), device.to_owned()); 452 assert_eq!(class("git/2.55.0"), is("git", "", "", "")); 453 assert_eq!(class("Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)"), is("bot", "", "", "")); 454 assert_eq!(class("curl/8.22.0"), is("bot", "", "", "")); 455 assert_eq!(class(""), is("other", "", "", "")); 456 assert_eq!(class(CHROME), is("browser", "Chrome", "Windows", "desktop")); 457 assert_eq!(class("Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 Chrome/140.0 Safari/537.36 Edg/140.0"), is("browser", "Edge", "Windows", "desktop")); 458 assert_eq!(class("Mozilla/5.0 (iPhone; CPU iPhone OS 19_0 like Mac OS X) AppleWebKit/605.1.15 Version/19.0 Mobile/15E148 Safari/604.1"), is("browser", "Safari", "iOS", "mobile")); 459 assert_eq!(class("Mozilla/5.0 (Android 16; Mobile; rv:143.0) Gecko/143.0 Firefox/143.0"), is("browser", "Firefox", "Android", "mobile")); 460 assert_eq!(class("Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0"), is("browser", "Firefox", "Linux", "desktop")); 461 } 462 463 #[test] 464 fn a_visit_is_counted_from_when_the_browser_was_last_counted() { 465 let noon = 20_000.0 * DAY_MS + DAY_MS / 2.0; 466 let visit = |cookie: Option<String>, now: f64| { 467 let mut pairs = vec![("user-agent", CHROME.to_owned())]; 468 pairs.extend(cookie.map(|cookie| ("cookie", cookie))); 469 let pairs: Vec<(&str, &str)> = pairs.iter().map(|(name, value)| (*name, value.as_str())).collect(); 470 let mut event = event(Method::GET, "/", &pairs).named("view"); 471 let cookie = event.visit(&headers(&pairs), now); 472 ((event.first, event.daily, event.session), cookie) 473 }; 474 // No cookie at all: a new browser, today's first page, a new visit. 475 let (flags, cookie) = visit(None, noon); 476 assert_eq!(flags, (1.0, 1.0, 1.0)); 477 let cookie = cookie.unwrap(); 478 assert!(cookie.starts_with(&format!("lmjtfy_visit={noon:.0}; ")), "{cookie}"); 479 let last = |ms: f64| Some(format!("lmjtfy_browser=x; lmjtfy_visit={ms:.0}")); 480 // A minute on: none of them. An hour on: a new visit, the same day. 481 assert_eq!(visit(last(noon), noon + 60_000.0).0, (0.0, 0.0, 0.0)); 482 assert_eq!(visit(last(noon), noon + 3_600_000.0).0, (0.0, 0.0, 1.0)); 483 // Past midnight UTC: a new day too. 484 assert_eq!(visit(last(noon), noon + DAY_MS).0, (0.0, 1.0, 1.0)); 485 // A cookie from before this one existed: known, not yet counted. 486 assert_eq!(visit(Some("lmjtfy_browser=x".into()), noon).0, (0.0, 1.0, 1.0)); 487 // A time the page made up, or one from the future, is no time. 488 assert_eq!(visit(Some("lmjtfy_visit=soon".into()), noon).0, (0.0, 1.0, 1.0)); 489 assert_eq!(visit(last(noon + DAY_MS), noon).0, (0.0, 1.0, 1.0)); 490 // Only a browser's page view is a visit. 491 let mut bot = event(Method::GET, "/", &[("user-agent", "curl/8")]).named("view"); 492 assert_eq!(bot.visit(&HeaderMap::new(), noon), None); 493 let mut vote = event(Method::POST, "/rate", &[("user-agent", CHROME)]).named("vote"); 494 assert_eq!(vote.visit(&HeaderMap::new(), noon), None); 495 } 496}