# Tutelink, the `links` module (plan TL)

Short links on the short-link host (`HOST_INK`, `tutel.ink` in production) for products' messages. It lives in `app/Modules/Links/`.

| Table | What a row says | Platform or scoped |
|---|---|---|
| `link` (`lnk_…`, addressed by its code) | One short link: its code (7 characters from `Links::ALPHABET`, 31 with no look-alikes, never reused), who made it (a server, for an organisation, `api` or `notify`), where it goes (encrypted, since it may carry a signed token; its `host` and a cut-short `shown` path kept plainly), when it ends, whether and why it was turned off, its taps and its first and last. Audited, the destination and taps left out | Platform, naming its organisation and server |
| `link_tap_day` | A link's taps on one day. Nothing about who tapped is kept anywhere | Platform |

## Making one

`POST /v1/links` (`links` scope, `X-Organisation`) takes `url` and an optional `expires_in_days` (1 to 1,825); otherwise
`links.default_days` (365, a client able to differ). `Links::make` allows `https` only, with no user or password, to the calling
product's own domain or a subdomain of it, that organisation's verified addresses for the product (`organisation_domain`), or Atrium's
sign-in host; anything else is 422 `url` (TL-2). A server with no product (an outside client) gets none (TL-12). It answers 201 with
`code`, `short_url`, `state`, `taps` and `expires_at`, never the destination. Each link made is counted (`usage_event`, `links`/`link`).

`GET /v1/links/{code}` adds `taps_by_day` for the last 30 days; `DELETE /v1/links/{code}` turns it off at once. A server reads and
turns off only its own links, in the same organisation.

## The short-link host

`EnforceHost` hands every request on the short-link host to `InkController` before any session or route, so the host sets no cookie
and answers nothing else. A working code (read case-insensitively) redirects with `302`, `Cache-Control: no-store, private`,
`Referrer-Policy: no-referrer` and `X-Robots-Tag: noindex`, passing nothing on, so turning a link off works even for someone who tapped
it before (TL-7). Its tap is counted (`Links::tap`: the link's count, its first and last, and the day's count, in one transaction);
a `HEAD`, or an agent in `Links::PREVIEWS` (WhatsApp, iMessage's and others' link previews, search engines), isn't (TL-6).

Anything else gets `resources/views/ink.blade.php` (inline styles, no web fonts, for a phone on a poor connection): **never existed**
(404), **expired** or **turned off** (410). For a real code it names the organisation and product that sent it and when, never where it
went, and says to ask them for a new one (TL-8).

## Nuntius shortening

Where `notify.short_links` is on for the client (off by default), `Shortener` replaces each `https` link in a text that TL-2 allows
with a short one, before the text's segments are counted and only when that makes it shorter; a link the product may not use is sent
as it is; a code's template never (TL-9). One short link for each distinct address in a send: `Batches` shares it across a bulk send's
texts.

## Turning off, and forgetting

The Service turns a link off on the console with a reason (Input Fields `link_off_reason`); the product's server hears
`links.turned_off` by webhook. A product's own `DELETE` tells it nothing more. `links.keep_days` (180) after a link expired or was
turned off, the night's purge empties its destination (`OldLinks`): the code and its taps stay, so an old message still says the link
has expired (TL-10).

## The console

Links (`console.link.index`, `.show`, `.off`; `Console\LinkController`): every link found by its code or where it goes, by state; one
link's taps as 30 bars, its first and last tap and its end, and turning it off with a reason. Where a link goes shows as its host and
cut-short path, never the full address.

## Settings

`links.default_days` (365, a client able to differ: 1 to 1,825 days), `links.keep_days` (180: 30 to 3,650), with Nuntius's
`notify.short_links`.

## Not done, and why

- **A neutral short domain for outside clients** waits for the first outside client that sends links (TL-12).
- **The organisation's own view of its links** waits for a product that needs one; the product reads its links' taps through the API.
