# runlight v0.1.0 - Table of Contents

> Privacy friendly web analytics that lives inside your Elixir app. Mount a Plug, add a script tag, read your stats at /runlight.

## Pages

- [Runlight for Elixir](readme.md)

## Modules

- [Runlight](Runlight.md): Privacy friendly web analytics that lives inside your Elixir app: the same
library as `@runlight/sdk`, the same tables, and the same answers, so an
Elixir app and a Node or PHP one can share a database.
- [Runlight.Accounts](Runlight.Accounts.md): Accounts: who may sign in, their password hashes, two-factor, invites, and
the signed cookie that keeps them signed in (the SDK's accounts/auth.ts).
An app turns them on with `Runlight.routes(rl, accounts: true)`.
- [Runlight.Accounts.Web](Runlight.Accounts.Web.md): Accounts on the web: sign-in, the code step, invites, first-run setup, and
the Account and People APIs, under the base path the routes answer at (the
SDK's accounts/web.ts). `Runlight.routes(rl, accounts: true)` makes one.

- [Runlight.Db](Runlight.Db.md): The little the store needs from a database: its dialect, and statements
with `?` placeholders that answer rows. `Runlight.Db.Ecto` runs them through
an app's own Ecto repo; anything else that implements the callbacks below
can stand in.
- [Runlight.Db.Ecto](Runlight.Db.Ecto.md): Runlight's statements through an app's own Ecto repo, on SQLite
(`ecto_sqlite3`), Postgres (`postgrex`), or MySQL 8.4 and MariaDB 11.4
and later (`myxql`). The repo's adapter picks the dialect.
- [Runlight.Fetch](Runlight.Fetch.md): Outgoing requests, the stand-in for JavaScript's fetch(). Everything that
calls another server (mail services, importers, connected installs, the
assistant's providers, site icons) goes through the instance's fetcher, so
tests can pass a fake.
- [Runlight.Geo](Runlight.Geo.md): Where a visitor is, from a hosting platform's headers or a database lookup.
- [Runlight.Http.Headers](Runlight.Http.Headers.md): A request's or a response's headers: a list of `{name, value}` pairs with
lowercase names, read and set as the Fetch API's `Headers` are.
- [Runlight.Http.Request](Runlight.Http.Request.md): A request, shaped like the Fetch API's Request so the routes read as the
TypeScript SDK's do: an absolute URL, a method, headers, and the body as
text. `Runlight.Plug` makes one from a `Plug.Conn`; a test or another
server can make one with `new/2`.
- [Runlight.Http.Response](Runlight.Http.Response.md): An answer, shaped like the Fetch API's Response: a status, headers (a list
of lowercase names and values, with one entry per Set-Cookie), and the
body as a binary.

- [Runlight.Importers](Runlight.Importers.md): Short links and their history from other shorteners: Umami, Dub, Bitly,
Short.io, and Rebrandly (the SDK's importers). The dashboard drives an
import a step at a time; each step fetches the next few links, writes each
with its history, and reports progress, with a cursor to carry on from.
Credentials come with every step and are never stored.

- [Runlight.Importers.Visits](Runlight.Importers.Visits.md): Visit history from Umami or a CSV file: pageviews and custom events with
where each visit came from, its place, and its device, written as imported
visits so the dashboard's history does not start the day Runlight was
installed (the SDK's importers/visits.ts and csvvisits.ts).
- [Runlight.JS.Object](Runlight.JS.Object.md): A JSON object that keeps its keys in the order they were set, as a
JavaScript object does, so answers serialise byte for byte as the
TypeScript library's. Rows from the store and the bodies of the API's
answers are these. It implements `Access`, so `row["name"]` reads a key.
- [Runlight.Links](Runlight.Links.md): Short links: create, change, delete, and import, with the rules every route
shares (the SDK's links.ts). A link is a JavaScript object: id, site,
domain ("" for the app's own), slug, name, url, createdAt, and updatedAt.

- [Runlight.Mmdb](Runlight.Mmdb.md): A reader for MaxMind DB files (the MMDB format that MaxMind's GeoLite2 and
DB-IP's free databases use), in plain Elixir so location needs no library.
It answers what the TypeScript server's mmdb-lib answers: the record for an
address, maps with string keys, or nil when the address is not in the
database.
- [Runlight.Plug](Runlight.Plug.md): The dashboard, its API, and the tracker as a Plug: the SDK's
`rl.routes()`, the same answers byte for byte, so the dashboard, the MCP
server, and every port's tools work against an Elixir app as they do
against a Node one.
- [Runlight.Plug.LinkDomains](Runlight.Plug.LinkDomains.md): For an endpoint (before the router): a request on a link domain added in
Settings, such as go.example.com, is answered with its redirect or a 404;
every other request carries on.
- [Runlight.Plug.Links](Runlight.Plug.Links.md): Short links on the app's own domain: forward the instance's link path
(default "/go") here, and `/go/{slug}` redirects to the link's
destination, with the click recorded. A request on a link domain is
answered as that domain's links first.
- [Runlight.Plug.Observer](Runlight.Plug.Observer.md): Records page requests from known AI agents, which run no JavaScript, so
the tracker cannot see them. Put it in a pipeline. It answers a request on
a link domain with its redirect or a 404 and never stops any other
request.
- [Runlight.Routes](Runlight.Routes.md): The dashboard, its JSON API, the tracker's script and endpoint, short
links' API, OAuth, and the MCP server, under one base path (the SDK's
routes.ts). Every answer is the SDK's byte for byte.
- [Runlight.Safefetch](Runlight.Safefetch.md): 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 (the SDK's safefetch.ts). Only https is fetched, never a private,
loopback, link-local, or metadata address, and redirects are followed by
hand under the same rules.
- [Runlight.SearchParams](Runlight.SearchParams.md): A URL's query as the WHATWG `URLSearchParams` reads it: an ordered list of
`{name, value}` pairs, where a name may repeat.
- [Runlight.Store](Runlight.Store.md): Runlight's tables in a SQL database: SQLite, Postgres, or MySQL and
MariaDB. The same tables, columns, and statements as the TypeScript SDK's
SqlStore, so one database can be served by any implementation.
- [Runlight.Url](Runlight.Url.md): A URL parsed as the WHATWG URL standard parses one, as `new URL()` does in
JavaScript, so every port reads a request's address the same way.

- Exceptions
  - [Runlight.AccountError](Runlight.AccountError.md): A problem with an account change, to show the person making it, with a code and params.
  - [Runlight.AssistantError](Runlight.AssistantError.md): What went wrong with the assistant, as a code the dashboard says in its own words.
  - [Runlight.ConnectError](Runlight.ConnectError.md): Why connecting another install failed, as a code the dashboard says in its own words.
  - [Runlight.FunnelError](Runlight.FunnelError.md): Why a funnel was refused, as a code the dashboard says in its own words.
  - [Runlight.GoalError](Runlight.GoalError.md): Why a goal was refused, as a code the dashboard says in its own words.
  - [Runlight.ImportError](Runlight.ImportError.md): Why an import stopped, as a code the dashboard says in its own words.
  - [Runlight.LinkError](Runlight.LinkError.md): A link that cannot be made. `code` and `params` let the dashboard say it in its own language.
  - [Runlight.MailError](Runlight.MailError.md): A mail problem to show the person setting it up. `code` and `params` let the
dashboard say it in its own language; a service's own words travel in
`params["detail"]`.

  - [Runlight.RangeError](Runlight.RangeError.md): JavaScript's RangeError, for the few refusals that carry no code of their own, such as an unknown link.
  - [Runlight.SettingsError](Runlight.SettingsError.md): A setting refused, such as a site's domain or the assistant's service, with a code the dashboard says in its own words.

