Hosting
Lightkone is a static site (npm run build writes dist/), so any static
host works. The repository deploys to Cloudflare Pages: free for private
repositories, and in the same account as an R2 bucket for storage.
Cloudflare Pages from GitHub Actions
.github/workflows/deploy.yml publishes on every push to main once these
are set in the repository (Settings → Secrets and variables → Actions):
| Name | Kind | Value |
|---|---|---|
CLOUDFLARE_ACCOUNT_ID |
secret | Shown on the Cloudflare dashboard's account home, right sidebar. |
CLOUDFLARE_API_TOKEN |
secret | My Profile → API Tokens → Create Token → Custom token, permission Account · Cloudflare Pages · Edit, limited to your account. |
PAGES_DOMAIN |
variable (optional) | A custom domain, e.g. lightkone.lab.kausalflow.com. |
PAGES_PROJECT |
variable (optional) | Pages project name; defaults to lightkone. |
The first run creates the Pages project; the app is then at
https://<project>.pages.dev.
For a custom domain, set PAGES_DOMAIN, then point DNS at Pages:
add a CNAME from the domain to <project>.pages.dev (proxied, if the zone is
on Cloudflare). The deploy token only manages Pages, so it cannot add the record.
If storage is an S3/R2 bucket, add the site's address to the bucket's CORS rule (see STORAGE.md).
The PDF service
functions/api/pdf.ts is a Cloudflare Pages Function, deployed with the site
at /api/pdf. Given a DOI and a contact email it asks
Unpaywall for a legal open-access copy and passes the
PDF through, because publishers do not let browsers download from another
site. It fetches only URLs Unpaywall returns for that DOI. arXiv PDFs do not
need it. On other hosts, either run the same function elsewhere and set its URL
under Settings → PDFs, or go without DOI downloads.
The published-version service
functions/api/published.ts (at /api/published?arxiv=<ids>) tells Tidy
up which arXiv preprints have been published: first from the DOI and journal
reference authors added on arXiv, then from Semantic Scholar. Semantic
Scholar limits anonymous requests; for steadier results, request a free key at
https://www.semanticscholar.org/product/api and add it to the Pages project as
the secret S2_API_KEY.
Limits on the services
The services under /api/ (PDF, published versions, arXiv, equations, MCP
address) are for the site's own app. functions/api/_middleware.ts refuses
calls that a browser says come from another website, and limits each address
to a number of calls a minute per service (30 for PDFs, 120 for equations; see
LIMITS), answering 429 with Retry-After beyond that. Normal use stays far
below these.
Those counts are kept per Cloudflare server. For a hard limit across the whole
network, also add a rate limiting rule in the dashboard (the free plan
includes one): your domain's zone → Security → WAF → Rate limiting rules →
Create rule, with URI Path starts with /api/, counting by IP, for example
50 requests per 10 seconds, action Block for 10 seconds.
Other hosts
Copy dist/ to any static host (GitHub Pages, Netlify, S3 + CloudFront, an
internal nginx). Paths are relative, so it also works under a sub-path.