carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

LIVE https://todo-ocaml.bfj.oncarlos.com a24ca85

Checks

  • make test: 16 Alcotest tests pass (form parsing, Unicode trimming, escaping, and the request handler against an in-memory database).
  • Shared suite against dist/app --listen 127.0.0.1: 67 passed, 3 skipped (the two JS-only edit tests with JS off, and the screenshot with JS off).
  • scripts/boot-check.sh dist/app-linux-arm64: /healthz and /api/version 200, clean exit on SIGTERM.
  • Deployed with make deploy; /api/version reports a24ca85.
  • Shared suite against https://todo-ocaml.bfj.oncarlos.com: 67 passed, 3 skipped.

Review fixes (a24ca85)

  1. Unbounded request heads. bin/main.ml now runs cohttp-eio's per-connection loop itself instead of Cohttp_eio.Server.run (which hard-codes max_size:max_int). Each connection reads through a byte budget: 64 KiB for a request line plus headers, then 1 MiB + 64 KiB per request after that, with at most 256 connections. Checked locally: a 10 MB header line and 100,000 small headers both get 431 Request Header Fields Too Large, and the process stays at about 17 MB RSS.
  2. Edits lost on re-render. app.js keeps an open editor across a response: the same item stays in edit mode with its unsaved text, focus and selection, and losing focus during the swap doesn't count as a save. Checked in Chromium with a toggle response held back while another item was being edited.
  3. Unicode whitespace. Form.trim strips Unicode White_Space (plus U+FEFF) and is used for both adding and editing. A title of only non-breaking spaces adds nothing, and editing to one deletes the item.
  4. SQLite from source. tools/sqlite.sh downloads the SQLite 3.54.1 amalgamation once into .cache/src (checked against sqlite.org's SHA3-256) and compiles it into .cache/sqlite-<arch>. The sqlite3 opam package finds it through pkg-config. make clean-deps deletes those builds and keeps the tarball. The shipped binary contains SQLite 3.54.1 (source id 2026-10-16), not the arm64 root's 3.46.1.

Notes

  • The shipped binary is static glibc (aarch64), built in the emulated arm64 root with Debian's OCaml 5.3.0 as ocaml-system. The linker warns about getpwnam, gethostbyname and the like (pulled in by OCaml's unix library). The app never calls them.
  • make clean-deps && make build-arm64 took 20.5 minutes here: SQLite and every OCaml library compiled under qemu. After that, a rebuild of the app alone takes about 15 seconds.
  • docs-t1 has no pkg-config, so the x86 build unpacks Debian's pkgconf into .cache/ with apt-get download (see README).