# Optional Lunelet registration

Registration tells Lunelet's creator which websites have chosen to use the shelf.
It is optional, free, and works for hosted or self-hosted copies. Declining never
changes the shelf's features. This guide is not permission to register a website.

## Ask the owner first

Ask: “May I register this website with Lunelet? It will share the website address
and chosen style with Lunelet's creator, along with registration and confirmation
dates. It won't share your books or track visitors. A small opt-in file will be
public on your website. You can withdraw later.”

Wait for an explicit yes. Silence, installing the shelf, accepting general terms,
or a request to update books is not consent. Never preselect registration or make
it a condition of installation. Record yes, declined, or not asked in the
project's non-public LUNELET.md so later agents preserve the choice without
repeatedly asking. If consent is unclear, do not register.

## What is recorded

Lunelet stores one record per exact HTTPS website origin: its address, chosen
design, initial registration date, last confirmation date and consent version.
The list is private to Lunelet's creator. An origin means scheme + hostname,
without a page path, query or fragment. Separate subdomains are separate sites;
use the site's canonical address to avoid counting www and non-www twice.

No names, email addresses, books, notes, visitor IDs, cookies, page-view counts,
IP addresses or user-agent strings are stored in the registry. Hosting providers
still process the ordinary requests needed to deliver and verify it. The public
opt-in file reveals that this website chose to register, including its style.

“Last confirmed” means an agent explicitly reconfirmed the registration. It does
not mean someone visited, read a book or was online. Unregistered copies are
invisible. This is an owner-approved registry, not a verified count of every
installed or still-active shelf. The check verifies the consent file, not the
page's JavaScript or reading feed. No recurring visitor-side reporting is added.

## Register after the shelf is live

After consent, create this small file in the site's public directory so it is
served at `https://THE-ACTUAL-SITE/.well-known/lunelet.json`. Replace the example
address with the site's actual canonical HTTPS origin and the design with the
reader's selected ID. Do not replace an existing file without inspecting it.
Keep it separate from reading.json. This example is not a registration target:

```json
{
  "schemaVersion": 1,
  "site": "https://your-site.example",
  "registered": true,
  "design": "loom",
  "consentVersion": "2026-09-15"
}
```

Publish through the owner's usual authorised workflow, preferably with the
installation if consent was already obtained. Confirm the live shelf works and
the consent file returns `200` and `Content-Type: application/json` from the exact
hostname. Lunelet does not follow redirects, so use the canonical host. The file
must be at the root path above; sites where you only control a subdirectory
cannot currently register, but can still use the full shelf normally.

Then make this agent-side request, substituting the actual website address:

```sh
curl --fail-with-body --silent --show-error --max-time 20 \
  https://lunelet.com/api/registrations \
  --json '{"site":"https://your-site.example","consent":true}'
```

This API requires no secret. Ownership is demonstrated by publishing the opt-in
file on the website. Lunelet checks that file on the public internet, but never
fetches the shelf's book feed. Send only `site` and `consent`; extra fields are
rejected. Do not call the API from the widget, a visitor's browser, a periodic
task or every book update.

A successful response has `registered: true`, the canonical `site`, `design`,
`registeredAt`, `lastConfirmedAt` and `consentVersion`. Record this successful
receipt and the consent-file path in non-public LUNELET.md. The dates come from
the server; never invent them. A failed request does not undo the installation:
say the shelf is live but registration remains pending. There is no public list
or GET endpoint. The owner's existing private access key is unrelated to this API
and must never be sent to Lunelet.

Repeating the request updates the same site's style/confirmation date and keeps
its original registration date. If a network failure leaves the result unclear,
one later retry is safe; do not send repeated automatic retries. The distribution
mirror https://lunelet.pages.dev/api/registrations uses the same registry.

## Change the style or confirm again

Only when the owner asks to refresh their registration, or changes the style of
an opted-in installation: keep the recorded consent decision, update the consent
file's design if needed, publish and repeat the POST. Changing books alone does
not send anything to the registry. Never re-register a declined or withdrawn site
without fresh explicit consent.

## Withdraw registration

When the owner asks to withdraw, change `registered` to `false` in the existing
consent file, keep its actual origin, design and consent version, and publish.
Then request removal:

```sh
curl --fail-with-body --silent --show-error --max-time 20 \
  --request DELETE https://lunelet.com/api/registrations \
  --json '{"site":"https://your-site.example","consent":false}'
```

A successful response has `registered: false`. The site's registry row is
deleted; the shelf and books stay untouched. Once removal is confirmed, the owner
may remove the consent file. Record “withdrawn” in LUNELET.md. Removing the file
or the embed alone does not notify the registry. Temporary DNS/hosting failures
never count as a request to withdraw. If the domain is no longer under the
owner's control, contact the creator through https://lilygeidelberg.com/ for help.
