A home system, built for a hackathon

An afternoon on paper, invented for one person

Lanternina invents an afternoon for an adolescent, has a parent approve it, and then runs it in a real room — on paper, on e-paper displays, and on physical buttons. It reads what comes back in handwriting, decides what happens next from that, and stops at an hour the parent set.

A mini-PC in a house drives two e-paper displays, an inkjet and a flatbed scanner. Every language and vision call goes to Microsoft Foundry in the EU. No model runs on the device, and there is no offline mode. The house holds the clock and the sealing keys, and it is the only thing that starts work.

A 7.5-inch e-paper display in a 3D printed frame, showing an engraving.
One of two displays. The frame is 3D printed and the unit was assembled by hand; the picture on it was generated an hour earlier from a theme the parent wrote.

Four decisions worth arguing about

One prompt draws the whole sheet

There is no layout engine. An image model is given every string that has already passed the safety gate, told to letter exactly those characters, and returns a finished A4 page. The design work is the prompt, and the page format lives in one Markdown file rather than in code.

A page is read against its own blank

No QR code, no ArUco markers, no declared grid — nothing printed on the paper that is there for a machine. Reading is handing a model two images, the blank and what came back off the glass, and asking what is different. An earlier version had all three, and they were deleted.

The cloud cannot start anything

The dashboard is inert by construction: approving writes a row and does nothing else. The house asks, on its own clock, and the answer arrives inside the reply. There is no inbound path to a device in a room, which is also why there is nothing on the home network to attack.

The guarantees are types, not conventions

An agent has nowhere to record that its own output is acceptable, becauseProposal has no status field. Only the safety gate can mint the type a user-facing payload requires. Each boundary test was mutation-checked: the violation was injected and the test watched to fail.

What comes out, and what goes back

The page below was drawn whole by an image model, from words that had already been screened. Somebody writes on it and puts it back on the scanner glass; the reading decides the next moments, which are written while the sheet is still on the table.

A printed page, blank.
Handed over. Unedited model output, 19–33 s to draw.
The same page, filled in by hand.
Came back. Filled in here by a simulated hand, so a whole run can be exercised with no person in it.

The reader's vocabulary is deliberately poor: a page came back with marks orblank, and nothing else. Not a count and not a fraction, because a count of somebody's marks is one step from a score. The reading is then gone — the type refuses to be pickled, copied or cached.

Measured on the deployment

Dates are attached because prompts change and the numbers move with them. Everything here was measured against the real services, not estimated.

Devising a whole afternoon68–157 sn=22, Sep 2026
Rereading it with a second model14.4–22.8 sSep 2026
Drawing one page19–33 sAug 2026
Ink on a drawn page0.5–2.7 %Aug 2026
Reading a returned page4.4–5.5 sAug 2026
Button press to reading on the display≈ 35 s19 Aug 2026
Printed ruler, measured with a ruler50 mm asked, 50 mm got4 Aug 2026

The last row is the one that is not about speed. The print chain does not rescale, and that was checked by hand — a chain that silently rescales would make every physical dimension on every page wrong in a way no test would notice.

The stack

In the house

Raspberry Pi Compute Module 5 on a PoE base board, Raspberry Pi OS. Two 7.5" e-paper displays on XIAO ESP32-S3, running a patched TRMNL firmware against our own BYOS server. Epson ET-2870 over IPP and eSCL, driverless. Frames 3D printed, units assembled by hand.

In the cloud

Microsoft Foundry in the EU for every language, vision and image call, behind a single router module that is the only place allowed to import a model SDK. Azure AI Content Safety on everything generated and on every inbound image. Container Apps for the API, Cosmos DB, Blob Storage, Static Web Apps. Infrastructure as Bicep.

How it ships

A push to main publishes the API through a user-assigned managed identity with a federated credential, so there is no password anywhere in the pipeline — measured end to end at 1 m 35 s. The image is built on the runner rather than with az acr build, because the latter needs rights that would let the identity reconfigure the registry.

An e-paper panel, an ESP32 board and a battery, before assembly.
What goes into a frame: panel, XIAO ESP32-S3, a 2000 mAh cell, and the ribbon between them.

What it does not do

Engagement optimisation is easy to add to a system like this and would do harm, so it is a written rule rather than a matter of judgement. tests/test_boundaries.py refuses the vocabulary in the stored shape, in the panel, in the browser and in the prompts.

  • No streak, no daily goal, nothing withheld as a reason to come back.
  • No notification triggered by absence, and no measure of time spent.
  • A profile is kept, and it reaches the two models that write and run an afternoon — and no display, no printed sheet and no page of the panel. A review gate enforces that, not the hope that a prompt was polite.
  • No face or person detection, no emotion or attention inference, no biometrics.

Why each boundary exists, and what it costs · every prompt, whole, as the model receives it

This site sets no cookies, collects no statistics, and loads nothing from anybody else's server.