Schrollar: architecture and design

A technical report on how Schrollar is built, run and grown, as of 7 November 2026, app build 17.

note What Schrollar is. Lessons come in boxes: short, layered explanations (Hook → Why → How → Code → Try) of one idea, from Dijkstra's algorithm to how a tokenizer works to why a minor chord sounds sad. You scroll a feed of boxes, go as deep as you like, keep the ones worth remembering, and the app brings them back for review right before you would forget them.

The system in one picture

flowchart LR
  subgraph Devices
    W[Website / PWA]
    N[iOS and Android apps]
  end
  subgraph Edge["Cloudflare edge"]
    CW[Worker: app, content,<br/>link previews, API proxy]
    R2C[(R2: signed content)]
  end
  subgraph Fly["Fly.io"]
    API[API ×2<br/>accounts · sync · sharing]
    JOBS[Worker process<br/>jobs]
  end
  PG[(Postgres<br/>system of record)]
  LAKE[(R2: Parquet lake)]
  W & N -->|HTTPS| CW
  CW --> R2C
  CW -->|/api| API
  API --> PG
  JOBS --> PG
  JOBS -->|nightly export| LAKE
  CLI[Content build<br/>validate · compile · sign] -->|publish| CW

Schrollar is an offline-first client (one React codebase shipped as a website and as iOS and Android apps) that keeps everything a learner does in an on-device database and syncs it in the background through an outbox. Lessons are signed, versioned data published to object storage and served from the edge, so new content never needs an app release. A Cloudflare Worker fronts everything; the API is a TypeScript service on Fly.io backed by Postgres; background jobs compute learner models, send reminders, enforce retention and export pseudonymous analytics to a Parquet lake. Every decision the app makes for a learner is logged with its alternatives, so rules can later be replaced by learned policies and measured.

Contents

${sch.contents()}

Prefer to browse by section? Architecture · Features · Operations · Scaling Plan · Roadmap · Decisions · Glossary

Key numbers

Content162 boxes and 21 daily puzzles across 8 worlds and 40 topics
Interactives50+ hands-on playables, from Fenwick trees to FFTs to tokenizers
First download (web)133 KB of JavaScript, gzipped; everything else loads on demand
Content deliveryabout 55 ms to first byte from the edge cache
API2 app machines with rolling deploys, plus a separate jobs machine
PlatformsWeb (installable PWA), iOS, Android, from one codebase
ReleasesAPI, website and content deploy from CI on every merge; content ships without app releases

How this report is built

This report is itself a small SilverBullet application: diagrams are Mermaid blocks rendered by a code widget, and the contents tables and page-to-page navigation are Space Lua queries over each page's frontmatter. See Library/Schrollar/Publishing.