work

personal projects. my day job is consumer real‑estate software at luxury presence, which mostly is not mine to publish, so what follows is what i build on my own time. each featured project has a page with screenshots, the architecture, and the decisions behind it.

background browser automation for agents, inside the chrome you already use

an mcp server that drives the user's own signed-in chrome through a manifest v3 extension and the devtools protocol. tabs open unselected and nothing on screen moves while an agent works.

node, chrome mv3, chrome devtools protocol 1.3, mcp over stdio, named pipes

case study →

a sync server that cannot read the data

a self-hosted, end-to-end encrypted sync backend for local-first apps. the rust server orders ciphertext and pokes subscribers. the typescript client does every merge. there are no accounts, identity is 12 words.

rust, axum, sqlite, ed25519, xchacha20-poly1305, typescript, dexie, server-sent events

the shoal protocol docs page case study →

deliver text into the focused windows app without destroying the clipboard

a rust crate for the last step of every dictation tool: putting text where the caret is. it restores the clipboard only after the target has actually read the new text, keeps transcripts out of clipboard history, releases held modifiers, and refuses elevated targets honestly.

rust, win32 through the windows crate, no other dependencies

case study →

push-to-talk dictation for windows in about 1,500 lines

hold a key, speak, release, and the text lands wherever you were typing. no window and no webview: one toml file, a tray icon, and a bubble that follows the microphone level without taking focus. transcription runs locally.

rust, win32, cpal, rubato, transcribe-cpp, win-text-inject

the level bubble, a dark pill with five vertical bars case study →

voice, video, screen share, and chat for a group of friends, with no service in the middle

one windows app that contains its own server. whoever launches it first on a machine is the host, friends reach it over tailscale funnel, and there are no accounts. livekit carries the media.

electron 43, react 19, express 5, node:sqlite, livekit, tailscale funnel, electron-builder

a voice room with participants and a shared screen case study →

excalidraw with a self-hosted encrypted cloud

excalidraw as a package, drawings in indexeddb, and every change pushed as an end-to-end encrypted op to a shoal server. a new device restores the whole library from 12 words. the static app holds no data and no secrets.

react 19, typescript, vite, @excalidraw/excalidraw, dexie, shoal-client, vercel

a hand-drawn architecture diagram open in scalidraw case study →

spaced repetition in the browser, fsrs-5, no account

flashcards scheduled by fsrs-5. decks, cards, review history, and stats live in indexeddb, so there is no server and no network dependency after first load. sync to a shoal server is optional.

react 19, react router 7, typescript, vite, tailwind 4, dexie, ts-fsrs, shoal-client

a flashcard with the answer revealed and four rating buttons showing the next interval for each case study →

local-first, no backend, no account

habits, completions, streaks, and heatmaps. everything persists to indexeddb and every derived view is computed at read time, so the data never leaves the browser and the app keeps working with the network off.

react 19, react router 7, typescript, vite, tailwind 4, dexie, date-fns, shoal-client

the overview page with a 20-week heatmap, current streak, best streak and total per habit case study →

also

tuna

offline-first android music player where playing is caching

every track you play is cached in full, so offline playback needs no download step. eviction is lru with a hard protected set: liked songs, playlists, and the current queue are never touched, and the storage cap is a slider. endless radio queue and android auto.

kotlin, jetpack compose, media3, room, hilt

camelo

self-hosted discord bot with a local llm dj

music player with a natural-language dj that takes requests in english and portuguese, a price watcher with drop alerts and screenshots, and reminders. all ai runs locally through ollama, so the bot needs no cloud keys and conversation stays on the box. deploys as a hardened docker container.

typescript, discord.js, sqlite, ollama, docker

ash lumen

a monochromatic design system for code-adjacent interfaces

hierarchy through luminance instead of hue. tokens are authored once in dtcg json and compiled to css custom properties, so light and dark resolve from a single source with no dark: prefixes anywhere. semantic color is reserved for meaning, and even the diagnostics are desaturated so a green tag never shouts louder than a headline. ships with a matching vs code theme and the shiki grammars to go with it.

astro, tailwind 4, dtcg tokens, shiki

this site

the design system, eating its own cooking

edfl.dev is built entirely on ash lumen, which is the point: a design system nobody uses is a mood board. the token pipeline that themes the blog is the same one published in the package, so anything that breaks here breaks in public.

astro 6, tailwind 4, vercel