08

Tinkr

IoT Platform / Embedded

RoleSolo Engineer
TimelineJun 2025 – Ongoing
ClientPersonal Product
StatusBeta
Tinkr screenshot

Outcomes

Zero-install flashing

Firmware writes from a browser tab over Web Serial. No PlatformIO, no Arduino IDE, no USB driver, no port selection.

A live credential rotated without bricking a board

When the HMAC pepper had to change, dual-pepper verification re-hashed each board on its next contact instead of forcing a physical factory reset.

Overview

Tinkr is an ESP32 platform for people who do not want to install a toolchain. Firmware writes to the board from a browser tab over Web Serial, the board is claimed to an account in the same flow, and from then on it is driven from a phone.

Behind that: dual-slot OTA with an MD5 check before the switch, so a bad update rolls back instead of bricking a board that may be across the house. Board identity is an HMAC-SHA256 secret under a server-side pepper. Hardware profiles (tank, garden, camera) change what a board is without changing how it is provisioned, authenticated, or updated.

Challenge

Getting an ESP32 running normally means PlatformIO or the Arduino IDE, a USB driver that may not match your chip, and a serial port you have to guess. Every one of those is a place a beginner quits. Tinkr had to move all of it into a browser tab without giving up OTA updates, device identity, or the ability to recover a board that took a bad flash.

Tech Stack

ESP32C++PlatformIOWeb SerialNext.jsTypeScriptPrismaPostgresBetter AuthAblyVercel

Architecture

DEVICEBROWSERSERVERESP32 firmwaredual-slot OTA + MD5Web Serial flasherzero installDashboardNext.jsBoard authHMAC-SHA256 + pepperRealtimeAbly / MQTT
Flashing happens in the browser over Web Serial, so nothing is installed. Everything after that is the board proving who it is: an HMAC-SHA256 secret peppered server-side, verified before any command is honoured.

Key Features

01

Browser Flashing

Web Serial writes firmware straight from a tab. No IDE, no driver install, no port guessing. The board is claimed to an account in the same flow that flashes it, so provisioning and identity happen once.

02

Self-Healing OTA

Dual-slot partitions with an MD5 verification before the switch. A failed or corrupted update boots the previous slot rather than bricking a board that is no longer physically reachable.

03

Hardware Profiles

One firmware base with swappable profiles (tank, garden, camera). The profile decides what the pins mean; provisioning, authentication, and OTA stay identical across all of them.

Lessons & tradeoffs

The hard part of hobby electronics is not the electronics. It is the forty minutes of driver installation before the LED blinks. Tinkr moves flashing into a browser tab over Web Serial, so the thing standing between someone and a working board is a cable, not a toolchain.

Challenge

A device flashed in someone's living room is a device you may never physically touch again. A bad update is not a bug there, it is a brick.

Decision

I put OTA on dual-slot partitions with an MD5 check before the switch, so a failed update boots the previous slot instead of a broken one.

Lesson

When the hardware is out of reach, design the recovery path before the happy path. The update mechanism deserves more engineering than the feature it delivers.

Challenge

Board secrets are hashed under a server-side pepper, and that pepper is the HMAC key. Changing it invalidates every stored hash at once, which would have locked out every board already in the field.

Decision

I verified against the current pepper first and fell back to the retired one, re-hashing transparently whenever the old pepper was what matched, so boards migrated themselves on next contact.

Lesson

Rotating a credential that physical devices depend on is a migration, not a config change. Lazy migration on next contact beats a coordinated cutover you have no way to coordinate.

Challenge

Middleware gated the page routes but not the API routes, and each API route was expected to authorize itself. Two of them shipped without doing so.

Decision

I audited every route against one canonical gate, and deleted the endpoint that turned out to have no callers rather than fixing it.

Lesson

A convention that is not compiler-enforced is a convention that will eventually be broken. If every route has to remember to authorize itself, one of them will forget, and the only open question is which one.

Next ProjectForge BI