Expand description
Runlight: privacy friendly web analytics that lives inside your app.
This crate is the Rust version of Runlight, a port of the TypeScript SDK that answers every request the way it does and uses the same tables, so either one can read the other’s database.
Re-exports§
pub use routes::Routes;pub use routes::RoutesOptions;pub use routes::TokenOption;pub use runlight::AppMailer;pub use runlight::AppSend;pub use runlight::Runlight;pub use runlight::RunlightOptions;pub use runlight::SiteOptions;pub use runlight::TrustProxy;
Modules§
- accounts
- Accounts: who may sign in, two-factor, invites, the throttle on sign-in tries, and the pages they use.
- adapters
- The routes as a tower
Service, for hyper, axum, and most Rust HTTP stacks (node.ts’s adapter, for Rust): the request read into arunlight::http::Request, with its body capped, and the answer written back as anhttp::Response. - assets
- The dashboard, its locales and map, the tracker, and the element picker: the TypeScript SDK’s built files, copied into assets/ by scripts/rust-assets.mts, so this crate serves the very same bytes.
- assistant
- The dashboard’s assistant: questions about the stats, answered by a model the owner chooses, through the same read-only tools as the MCP server. The model runs on the server, so the key never reaches a browser, and each tool reads the API with the asking person’s own access.
- brand
- The Runlight mark.
- connect
- Connecting another Runlight to this one (a hub) without copying a token: this server registers itself with the install’s OAuth server, sends the owner to that install’s consent page, and on the way back swaps the code for a manage token, limited there to the one site the owner picked.
- data
- Data every implementation reads the same: known traffic sources and AI agents.
- funnels
- Funnels: steps a visit is expected to take in order, checked as the dashboard sends them.
- geo
- Where a visitor is, from the headers a hosting platform adds or a lookup of the app’s choosing. Only the country, region, and city are kept.
- goals
- Goals: what is worth counting, checked as the dashboard sends them, and the click rules the tracker applies.
- hash
- Hashes and random ids.
- http
- The Fetch API’s types, as the routes and everything that calls out use
them:
Request,Response,Headers,URL,URLSearchParams, and an injectedfetch. - icon
- A site’s icon, for the dashboard header: the best icon its home page links to, or /favicon.ico. Fetched from the site’s own configured origin (never from request input), cached in memory for a day.
- importers
- Imports from other tools: short links with their clicks from other shorteners, and visit history from Umami or a CSV file (importers/index.ts).
- intl
- The pieces of JavaScript’s Intl the email reports use, for the dashboard’s languages (en, de, es, fr, and pt), written out so they read as Node’s do: Intl.NumberFormat for counts, percents, one decimal place, and money, Intl.DateTimeFormat for a month and year or a short day, and Intl.DisplayNames for region names. Region names and each currency’s symbol come from assets/intl.json, which scripts/rust-intl.mts writes from Node’s own ICU.
- journeys
- Journeys: the paths visits take through a site, page by page. Each visit’s pages are read in order, a page seen twice in a row (a refresh) counts once, and the path is cut to a number of steps, from a start page and to an end page when those are chosen. The answer lines the paths up in columns, one per step, with the flows between them.
- js
- What the port needs of JavaScript’s own behaviour, so that every value
the SDK writes, compares, or counts is written, compared, and counted the
same way here: numbers as
Number.prototype.toStringprints them,JSON.stringifyandJSON.parse(objects keep JavaScript’s key order), string lengths and cuts in UTF-16 code units, the characters\smatches,Date’s calendar arithmetic, and howString(),Number(), and truthiness read a JSON value. - limit
- The tracker’s rate limit, and turns taken one at a time per key.
- links
- Short links: create, change, delete, and import, with the rules every route shares.
- Mail: the services Runlight sends reports through (Amazon SES, SMTP, the HTTP ones, and a webhook), and the sealing of the keys kept for them.
- mcp
- The MCP server at {base}/mcp: Streamable HTTP without sessions or a stream, JSON-RPC in and JSON out. Every tool is a read of the HTTP API, made with the caller’s own credentials, so the MCP server can see exactly what the token can and nothing more.
- messages
- The dashboard’s translations, for text the server writes (email reports). Same keys, same placeholders, so every language stays in one place.
- mmdb
- A reader for MaxMind DB files (the MMDB format that MaxMind’s GeoLite2
and DB-IP’s free databases use), so location needs no other crate. It
answers what the TypeScript server’s mmdb-lib answers: the record for an
address as a JSON value, or
Nonewhen the address is not in the database. - oauth
- OAuth for the MCP server, so apps that connect only through OAuth (the Claude and ChatGPT web connectors) can reach it. Runlight is both the resource and the authorization server:
- payload
- What the tracker sends, after validation. Anything malformed is dropped.
- query
- Report queries: which dimensions exist, where each lives, and how filters are read from a URL. Shared by every store.
- reports
- Email reports: the period one covers, and the subject, HTML, and text of one site’s report in a language.
- routes
- The dashboard, its HTTP API, the tracker endpoint, and the pages around
them (routes.ts), framework-free:
Routes::handle(Request) -> Response. - runlight
- The Runlight: sites, the tracker’s hits, short links, AI agent fetches, and the scheduled upkeep (runlight.ts).
- safefetch
- Fetches from addresses that other people’s input names, such as the icon
links on a site’s home page or a link domain, and only from the public
internet. Only https is fetched, never a private, loopback, link-local,
or metadata address, and redirects are followed by hand under the same
rules. The name is resolved and every address it gives is checked before
each hop, and the request is pinned to the checked addresses (the
Fetcher’s
resolve), so a name that answers differently a moment later gets nowhere. - sources
- Where a visit came from: the page’s own tags, the referrer, and the channel they add up to.
- store
- Runlight’s tables: the database driver a store needs (
Db), the rows it keeps, andSqlStore, the same tables and statements as the TypeScript SDK’s store on SQLite, Postgres, and MySQL or MariaDB. The drivers over sqlx are the runlight-sqlx crate. - time
- Dates in a site’s timezone. Ranges are computed here as epoch milliseconds so the database only ever compares integers.
- ua
- User agents: AI agents, bots, and the browser, system, and device a person uses.
- version
- The SDK’s version and the HTTP API’s, as the TypeScript SDK’s version.ts has them.
- zip
- A ZIP file of text files, stored without compression, and CSV rows.
Macros§
- arr
- Builds a JSON array:
arr![1, "two"]. - obj
- Builds a JSON object in the order its keys are written, as a JavaScript
object literal is:
obj! { "ok" => true, "count" => 3 }. - params
- Builds a parameter list:
params![site, from, to].
Enums§
- Error
- An error from Runlight: a database that failed, or something refused with a code the dashboard says in its own words.