Cloudsteading
Dashboard

Recall

Local-first spaced-repetition flashcards with an optional self-hosted sync relay

Recall is a self-hosted Anki alternative built on Cloudflare (R2, Workers). Free tier eligible within limits. Inspect the source and license in the linked repository.

Source & license

Upstream license: MIT

License TL;DR

You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.

Explain MIT in plain English →

Summary of the main license. Separate packages and assets can have different terms.

Inspect repository ↗Read this project’s actual license ↗

Repository owner

@Madlezz

See the upstream repository for the original creator and contributors.

Maintain this project? Maintainer verification →

Cloudflare hosting

Free tier eligible within limits

The browser build can be statically hosted; the optional Cloudflare Worker/R2 sync relay can fit small Free request/CPU and standard R2 allowances. Local study does not need that relay. Additional folder-sync providers, if chosen, have separate limits and fees.

Hosting requirements
  • The diagram distinguishes local browser storage from optional cloud synchronization; a configured R2 relay is not the application database.
  • Keep relay CPU under 10ms/request and standard R2 under 10GB-month, 1M Class A and 10M Class B/month. No sync or native-client runtime was tested.
  • Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested.
Check current pricing ↗
Sources checked 01/10/2026

Repository snapshot: 9e1b513. Hosting eligibility reflects the deployment documentation and listed assumptions.

  • anki ↗

    The web build is a Vite SPA that can be served by any static host (Nginx, GitHub Pages, Cloudflare Pages, etc.).

  • workers ↗

    name = "recall-sync-relay" main = "src/worker.ts" compatibility_date = "2024-09-01" [vars] ENVIRONMENT = "production" [[r2_buckets]] binding = "RECALL_SYNC" bucket_name = "recall-sync-blobs"

  • r2 ↗

    name = "recall-sync-relay" main = "src/worker.ts" compatibility_date = "2024-09-01" [vars] ENVIRONMENT = "production" [[r2_buckets]] binding = "RECALL_SYNC" bucket_name = "recall-sync-blobs"

  • free-tier-eligible ↗

    name = "recall-sync-relay" main = "src/worker.ts" compatibility_date = "2024-09-01" [vars] ENVIRONMENT = "production" [[r2_buckets]] binding = "RECALL_SYNC" bucket_name = "recall-sync-blobs"

  • free-tier-eligible ↗

    | **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation |

  • free-tier-eligible ↗

    | Storage | 10 GB-month / month |

  • free-tier-eligible ↗

    | Class A Operations | 1 million requests / month |

  • free-tier-eligible ↗

    | Class B Operations | 10 million requests / month |

  • MIT ↗

    MIT License Copyright (c) 2026 Madlezz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION

  • architecture ↗

    name = "recall-sync-relay" main = "src/worker.ts" compatibility_date = "2024-09-01" [vars] ENVIRONMENT = "production" [[r2_buckets]] binding = "RECALL_SYNC" bucket_name = "recall-sync-blobs"

  • architecture ↗

    your machine (SQLite desktop / IndexedDB browser) - JSON / `.recall` export - **Optional E2E cloud sync** - AES-256-GCM + PBKDF2; you deploy `sync-relay/` (Cloudflare Worker + R2) yourself - no maintainer-funded public relay - Optional folder sync (Dropbox, Drive, etc.) ### Customization - **6 accent colors** - zinc, blue, green, rose, amber, violet - **Dyslexia-friendly font** - **Dark / Light / High-contrast** - **English + Bahasa Indonesia** --- ## Quick Start ### Prerequisites - **Node.js** 22+ (dev pin: `.node-version` → 24.18.0) and **pnpm** 10+ - **Rust** stable (`rustup install stable`) for desktop builds - Platform libraries - see [CONTRIBUTING.md](CONTRIBUTING.md#prerequisites) ### Run ```bash pnpm install pnpm tauri dev # Desktop (Tauri) # or pnpm dev

  • architecture ↗

    name = "recall-sync-relay" main = "src/worker.ts" compatibility_date = "2024-09-01" [vars] ENVIRONMENT = "production" [[r2_buckets]] binding = "RECALL_SYNC" bucket_name = "recall-sync-blobs"

  • architecture ↗

    name = "recall-sync-relay" main = "src/worker.ts" compatibility_date = "2024-09-01" [vars] ENVIRONMENT = "production" [[r2_buckets]] binding = "RECALL_SYNC" bucket_name = "recall-sync-blobs"

Upstream screenshot · Madlezz/Recall repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.

What it can replace

Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.

Anki logoAnki ↗

Flashcard authoring and spaced-repetition study; no Anki add-on compatibility, deck migration guarantees or complete ecosystem parity.

See supporting source ↗
external SaaS target
varies
→ R2 + Workers

How it works

The shape of Recall on Cloudflare, and how it stacks up against the rented tools it replaces.

Architecture

Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.

View upstream source ↗
Public interface
Configured entry points1
recall-sync-relay
sync-relay/wrangler.toml
↓
App
recall-sync-relay
entry
Cloudflare Workers
Entrypoint: src/worker.ts
↓

Configuration and workflow sources

Reviewed commit 9e1b5131bfbf. Files were read as data; upstream applications and CI jobs were not executed.

Deployment configuration · 1 files
sync-relay/wrangler.toml ↗

Cloudflare Workers · compatibility 2024-09-01

recall-sync-relay · default

Entrypoint: src/worker.ts

  • RECALL_SYNC → R2

Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.

Runtime source · handlers, binding usage and workflow steps

Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.

sync-relay/src/worker.ts ↗
  • L54 · fetch handler exported · references RECALL_SYNC · calls corsHeaders, JSON.stringify, path.match, test, env.RECALL_SYNC.get, revisionOf, object.text, request.text, JSON.parse, env.RECALL_SYNC.head, stripEtagQuotes, request.headers.get, String, Number.parseInt, env.RECALL_SYNC.put, toISOString, env.RECALL_SYNC.delete, console.error
  • L38 · stripEtagQuotes calls (conditional paths may differ): value.trim, t.startsWith, stripEtagQuotes, t.slice, t.endsWith

Environment references: env.RECALL_SYNC

Build and deployment pipeline · 4 GitHub Actions workflows

Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.

CI · .github/workflows/ci.yml ↗

Triggers: push, pull_request

check · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271
  3. actions/setup-node@820762786026740c76f36085b0efc47a31fe5020actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
  4. Shell commandpnpm install --frozen-lockfile
  5. Lintpnpm lint
  6. Unit tests with coveragepnpm test:coverage
  7. Buildpnpm build

e2e · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271
  3. actions/setup-node@820762786026740c76f36085b0efc47a31fe5020actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
  4. Shell commandpnpm install --frozen-lockfile
  5. Install Playwright browsersnpx playwright install --with-deps chromium
  6. E2E testspnpm test:e2e

rust-audit · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. Install cargo-auditfor i in 1 2 3; do cargo install cargo-audit --locked && break echo "Attempt $i failed, retrying in 10s..." sleep 10 done
  3. Audit Rust dependenciescargo audit

rust-quality · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. Install Tauri system dependenciessudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
  3. Rust fmt checkcargo fmt --check
  4. Rust clippycargo clippy -- -D warnings
  5. Rust testscargo test
CodeQL · .github/workflows/codeql.yml ↗

Triggers: push, pull_request, schedule

Analyze JavaScript/TypeScript · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97agithub/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a
  3. github/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97agithub/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97a
  4. github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97agithub/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a

Analyze Rust · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97agithub/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a
  3. Install Tauri system dependenciessudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
  4. github/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97agithub/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97a
  5. github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97agithub/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a
Deploy PWA to GitHub Pages · .github/workflows/pages.yml ↗

Triggers: push, workflow_dispatch

build · no job dependencies declared

  1. actions/checkout@v7actions/checkout@v7
  2. pnpm/action-setup@v6.0.9pnpm/action-setup@v6.0.9
  3. actions/setup-node@v7actions/setup-node@v7
  4. Shell commandpnpm install --frozen-lockfile
  5. Build PWAnpx vite build --mode pages
  6. Setup Pagesactions/configure-pages@v6
  7. Upload artifactactions/upload-pages-artifact@v5

deploy · after build

  1. actions/deploy-pages@v5actions/deploy-pages@v5
Release · .github/workflows/release.yml ↗

Triggers: push

build-tauri · no job dependencies declared

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. Install Linux dependenciessudo apt-get update sudo apt-get install -y \ libwebkit2gtk-4.1-dev \ libappindicator3-dev \ librsvg2-dev \ patchelfCondition: matrix.platform == 'ubuntu-22.04'
  3. pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271
  4. actions/setup-node@820762786026740c76f36085b0efc47a31fe5020actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
  5. dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060cdtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c
  6. swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4
  7. Shell commandpnpm install --frozen-lockfile
  8. tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060ftauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f

checksums · after build-tauri

  1. actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
  2. Download release assetsmkdir -p assets gh release download ${{ github.ref_name }} -D assets -p '*'
  3. Generate SHA256SUMScd assets sha256sum * > SHA256SUMS cat SHA256SUMS
  4. Upload SHA256SUMSgh release upload ${{ github.ref_name }} assets/SHA256SUMS --clobber
  5. Update release notesgh api repos/{owner}/{repo}/releases/generate-notes \ -f tag_name=${{ github.ref_name }} \ --jq .body > notes.md gh release edit ${{ github.ref_name }} -F notes.md
package.json ↗
  • build: tsc -b && vite build
sync-relay/package.json ↗
  • deploy: wrangler deploy

Full upstream document by @Madlezz · README.md · snapshot 9e1b513

Recall logo

Recall

CI Release License: MIT Platform

Recall is FSRS-grade spaced repetition that makes you want to come back. No manual, no account, your data stays yours. Built-in streaks, XP, and focus rituals turn review into a habit - not a chore. Available as a desktop app (Windows / macOS / Linux) and a PWA on mobile.

Documentation

Doc What
Getting Started First run, study loop, import
Architecture App layers, DB, Tauri vs browser
Sync Optional E2E self-host relay + folder sync
Deployment Releases, PWA Pages, Worker
Card Formatting Markdown, LaTeX, code, cloze
i18n English + Bahasa Indonesia
Accessibility Keyboard + screen reader
Roadmap Planned work
Changelog Version history
Contributing Dev setup

Why Recall?

Snapshot comparison for orientation only. Third-party products change - verify Anki / RemNote / Mochi in their own docs before relying on a cell. Anki version pin: 26.05 (upstream release tag as of mid-2026).

Recall Anki RemNote Mochi
Built-in engagement Yes (XP, streaks, levels, achievements, focus timer, match game) Via community add-ons Some engagement features Minimal
No account, local-first Yes (SQLite desktop, IndexedDB browser) Local SQLite (+ optional AnkiWeb sync) Cloud-first product with offline use Local-first files; optional Pro sync
FSRS on by default Yes Legacy SM-2 by default; FSRS opt-in in deck options (Anki manual) Spaced repetition in a notes app (scheduler details product-defined; check in-app) Spaced repetition (commonly SM-2-style; check Mochi docs)
Open source Yes (MIT) Yes (AGPL) No (proprietary) App proprietary (some related OSS components)
Add-on ecosystem None (planned) Very large (long history) Extensions / plugins (product-defined) Limited vs Anki
Native desktop Yes (Tauri + Rust) Yes (Qt + Python) Yes (desktop clients) Yes (desktop clients)
Mobile Installable PWA AnkiMobile / AnkiDroid (separate apps) Mobile apps Mobile apps
Sync Optional E2E (AES-256-GCM) via self-hosted relay, or folder/file sync - no maintainer-hosted cloud AnkiWeb (not client-side E2E in the Anki sense); third-party options exist Hosted product sync Pro sync (see Mochi pricing/docs)
Stack React + TypeScript Python + Qt React-based web/desktop ClojureScript (historically)

Anki pioneered desktop SRS and still has the deepest add-on ecosystem - that is an honest concession. Recall's edge is the habit loop: engagement features are native, not bolted on. FSRS on by default, local-first, MIT, TypeScript-friendly contributions, optional self-hosted E2E sync.


Screenshots

Dashboard - due cards, daily goal, deck grid:

Dashboard

Deck browser:

Deck Browser

Deck detail - stats, tags, card list:

Deck Detail

Study - card front:

Study Mode

Study - answer revealed + FSRS interval preview:

Study - Answer Revealed

Stats - activity, retention, forecast:

Stats

Tags - hierarchical tree:

Tags

Card browser - search, filter, bulk actions:

Card Browser

Settings - theme, accent, language, TTS:

Settings

Recapture after UI changes: pnpm dev in one terminal, then node scripts/take-screenshots.js.


Features

Smart Study

  • FSRS scheduling - Again / Hard / Good / Easy
  • Cloze deletion - {{c1::hidden text}}
  • Rich cards - Markdown, LaTeX, syntax-highlighted code
  • Anki import - .apkg (review history + FSRS state)
  • CSV / Markdown / .recall import
  • Custom study - deck, count, tag filter, new-only
  • Card browser - search, filter, sort, bulk tag/delete/move
  • Tags - hierarchical tree, saved searches, autocomplete
  • FSRS interval preview on rating buttons
  • FSRS optimizer from review history
  • Keyboard-first: Space reveal, 1-4 rate, R review, Ctrl+N quick-add

Stay Motivated

  • XP & levels - Curious Mind → Legend
  • Achievements - streaks, volume, accuracy, time-based
  • Daily goal + confetti on completion
  • Session summaries
  • Onboarding gallery - 6 starter decks

Study Tools

  • Focus timer - 15 / 25 / 45m
  • Ambient soundscapes - Rain, Cafe, Lofi (synthesized, no assets)
  • Match game
  • Review calendar heatmap
  • Sound effects + optional TTS
  • Swipe gestures on mobile
  • Voice input in card editor (Web Speech API)
  • Command palette - Ctrl+K

Analytics

  • Review volume, rating distribution, time-of-day
  • Deck health - retention, leeches, overdue
  • Activity heatmap, retention curve, 30-day workload forecast

Privacy First

  • No account, no telemetry by default
  • Offline on your machine (SQLite desktop / IndexedDB browser)
  • JSON / .recall export
  • Optional E2E cloud sync - AES-256-GCM + PBKDF2; you deploy sync-relay/ (Cloudflare Worker + R2) yourself - no maintainer-funded public relay
  • Optional folder sync (Dropbox, Drive, etc.)

Customization

  • 6 accent colors - zinc, blue, green, rose, amber, violet
  • Dyslexia-friendly font
  • Dark / Light / High-contrast
  • English + Bahasa Indonesia

Quick Start

Prerequisites

  • Node.js 22+ (dev pin: .node-version → 24.18.0) and pnpm 10+
  • Rust stable (rustup install stable) for desktop builds
  • Platform libraries - see CONTRIBUTING.md

Run

pnpm install
pnpm tauri dev # Desktop (Tauri)
# or
pnpm dev # Browser preview (no Rust)

PWA

Installable PWA: https://madlezz.github.io/Recall

Swipe left / right / up / down to rate after reveal.

Testing

pnpm test # Unit tests (806 as of 2026-07-18; count drifts)
pnpm lint # ESLint
pnpm build # tsc + Vite production build
pnpm test:e2e # Playwright (starts `pnpm dev` via config)

Download

Pre-built binaries on Releases (latest: v1.2.0):

Platform File
Windows .msi
macOS (Apple Silicon) .dmg
macOS (Intel) .dmg
Linux .AppImage

Or build from source - Quick Start.


Security

Local-first: card data stays on device unless you enable sync.

  • CI audits: cargo audit, Dependabot, CodeQL on push/PR
  • Disclosure: SECURITY.md

Sync codes are E2E key material - never put them in exports or screenshots.


Keyboard Shortcuts

Keys Action
Space Reveal answer
1-4 Again / Hard / Good / Easy
R Start review
B Bury card
S Snooze card
Ctrl+N Quick-add (in-app)
Ctrl+Shift+N Quick-add (global, desktop)
Ctrl+Z Undo last review
Ctrl+K Command palette
T Toggle TTS
? All shortcuts

Contributing

See CONTRIBUTING.md.


Tech

What With
Desktop Tauri 2 + Rust (rusqlite)
Mobile PWA (vite-plugin-pwa)
UI React 19 + TypeScript (strict)
Styling Tailwind CSS + shadcn/ui primitives
Browser storage Dexie
Desktop storage SQLite via Tauri
State Zustand
Algorithm FSRS (ts-fsrs)
Sync AES-256-GCM + PBKDF2; self-hosted Worker relay or folder sync
i18n react-i18next
Icons Lucide

License

MIT © Madlezz

Frequently asked about Recall

What is Recall?+

Recall is a self-hosted Anki alternative built on the Cloudflare developer platform. Local-first spaced-repetition flashcards with an optional self-hosted sync relay

What does Recall replace?+

Recall is listed as an alternative to Anki. Compare the features and tradeoffs before migrating.

What Cloudflare primitives does Recall use?+

Recall is built on R2, Workers.

How much does Recall cost to run?+

The browser build can be statically hosted; the optional Cloudflare Worker/R2 sync relay can fit small Free request/CPU and standard R2 allowances. Local study does not need that relay. Additional folder-sync providers, if chosen, have separate limits and fees. The diagram distinguishes local browser storage from optional cloud synchronization; a configured R2 relay is not the application database. Keep relay CPU under 10ms/request and standard R2 under 10GB-month, 1M Class A and 10M Class B/month. No sync or native-client runtime was tested. Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested. Check current Cloudflare pricing before deploying.

Is Recall open source?+

The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/Madlezz/Recall/9e1b5131bfbfe61aa3183edc0ac90dda66d13ac7/LICENSE. Source code and contributor credit are available at https://github.com/Madlezz/Recall.

Discussion · 0

sign in to comment →
No comments yet — be the first.