typoena.dev / about

How it's built

Typoena began with one constraint: a machine that only lets you write. The whole build is a set of answers to that — which hardware can hold an editor and nothing else, how to save your words without a cloud account, and how to make it boot fast enough that you stop thinking about it. This page is how it went.

The parts

Compute
ESP32-S3 · 8 MB PSRAM
Display
5.79″ e-ink · 792 × 272
Input
USB-host mechanical keyboard
Storage
SD card, on its own SPI bus
Sync
git over Wi-Fi · HTTPS + token
Editor
Vim keymap, written in Rust

One spike at a time

Almost nothing here was known to work in advance, so each hard part was built and proven on its own before it touched the editor: a blink test, then text on the panel, a USB keyboard, partial screen refresh, Wi-Fi with a validated TLS chain, and finally a git push over HTTPS. Only once a piece stood up alone did it join the rest. A few of those spikes nearly ended the project.

Problems worth telling

The card that wouldn't mount

The SD card refused to mount for days. The culprit turned out to be the card itself: a 133 GB SDXC card was the entire fault, and a plain 32 GB SDHC card mounted on the first try. Then a second problem surfaced — the e-ink panel and the card were sharing one SPI bus and stepping on each other. The fix was to stop sharing. The card got its own bus, the panel kept its own, and the arbitration trouble went away.

Git on a microcontroller

The plan was always to save your work as commits and push them somewhere real. The first attempt used a pure-Rust git library and hit a wall we couldn't get past. So we switched to libgit2 — the same C library desktop git leans on — compiled as an ESP-IDF component against mbedTLS and given its own 96 KB thread. After that the device could clone and push over HTTPS with a token, checking GitHub's certificate chain against CAs baked into the firmware.

The memory war

Pushing a toy repo was easy. Pushing a real one — 63,000 objects and 560 MB of history — took nine attempts against 8 MB of RAM. libgit2 memory-maps its pack-index files, and on a repo that size those maps alone overran the budget. A double-free in libgit2's TLS error path crashed the device mid-push. The pack builder wanted to inflate ten objects at once. Each fix bought back a few hundred kilobytes and exposed the next ceiling. On the ninth try the numbers finally lined up — tuned memory-map windows, a capped object cache, a patched TLS layer — and a real repository pushed from the device for the first time.

Seconds to a cursor

A writing machine has to feel instant, so cold-boot time got its own campaign. It started at five and a half seconds ending on a blank screen. The failure was quiet: the first screen draw was doing a slow full refresh where a faster partial one would do the same job. With that and a reshuffled startup, it's now a little over four seconds from power-on to a blinking cursor. The target for 1.0 is under three.

Where it is now

The editor grew up in versioned steps: cursor navigation, then a real Vim keymap with modes, registers, undo and redo, and dot-repeat; visual mode and ex commands; a fuzzy file palette across a whole notes folder; search with smartcase and accent folding, so /ete finds été; and git pull and push straight from the device.

The newest piece is a first-boot wizard, so you can set one up with no computer at all: pick your Wi-Fi on screen, sign in to GitHub by scanning a QR code, choose a repository, and start writing.

It runs today — hour-long writing sessions with no crash. Boot is still slower than we want and some edges are rough, but the loop it was built for works: open the lid, write, push, close the lid.

The whole thing is open source — read the code ↗