BestaBoard is a personal project — a Go backend and React frontend that drives a physical flip-dot display with rotating, data-driven content.
See the virtual board ↓Renders the current time and date in the Vestaboard's native character set, with per-user timezone configuration.
Pulls today's forecast from Open-Meteo for a configured lat/lng and renders high/low temperatures, conditions, and precipitation chance.
User-submitted messages up to 135 characters with a configurable display duration. Notes expire and are dismissed automatically.
A fixed text slot in the rotation — useful for persistent announcements or any content that doesn't change between cycles.
Planned additions include calendar events, sports scores, transit alerts, and a webhook endpoint for pushing arbitrary content from external services.
Pause, resume, or skip the rotation cycle. Force-pin any mode to hold it indefinitely, then release to hand control back to the scheduler.
Rotation interval, default note duration, weather location, temperature units, and timezone are all runtime-configurable without redeploying.
All state mutations are exposed as Bearer-authenticated HTTP endpoints via a Chi router — cleanly separated from UI concerns and independently scriptable.
A central hub fans out scheduler state changes to all connected clients over SSE — no polling, no websocket overhead.
Session-based authentication with hashed credentials. Multiple users can be provisioned, each with their own bearer token for API access.
The server is written in Go and uses Chi for routing. Handlers are thin — they validate input and delegate to an internal scheduler and store, keeping HTTP concerns separate from business logic.
Each widget (clock, weather, notes, static) implements a common Mode interface. The scheduler holds a prioritized list and calls Render() on the active mode each tick, producing a 22×6 character matrix that gets pushed to the Vestaboard Subscription API.
The UI is built with TanStack Start (SSR), TanStack Router for file-based routing, and TanStack Query for server state. Tailwind CSS 4 and shadcn/ui handle styling.
A central event hub fans out scheduler state changes to all connected clients over Server-Sent Events. The frontend subscribes once on mount — no polling, no websocket overhead.
The app runs in a Docker container on Fly.io with a GitHub Actions workflow that deploys on every push to main. Persistent state (notes, preferences, users) lives in a Neon serverless Postgres database.
BestaBoard is a personal controller for a Vestaboard — a split-flap display updated via a character-matrix API (layout depends on model). It rotates modes on a schedule and runs self-hosted instead of using Vestaboard’s first-party app for orchestration.
Backend: Go. A scheduler calls Render() on each pluggable mode; modes share one interface. Output goes to the Vestaboard API. Weather uses Open-Meteo.
Frontend: React with TanStack Start, Router, and Query. Board state streams over SSE from the Go server. Sessions and hashed credentials live in Neon Postgres.
Infra: Docker on Fly.io. GitHub Actions deploys production and demo on every push to main.