Hardware-as-a-Service
Submit a binary. It is checked, flashed to a real board on the bench, and its serial output streams back — from the browser, a curl, the Python SDK, or your editor. No order, no wait, no drivers.
Live demo
A precompiled ESP32-C3 program is checked against the board profile, flashed to one of the C3s on the bench, and its serial output is streamed back here as the board prints it: the chip it is, the serial number burned into its eFuse, its flash and its radio — evidence a simulator could not fake. No account needed — one demo per visitor every five minutes, so the bench stays free for everyone.
checked against esp32c3-mini-v1 · flashed to a real ESP32-C3 SuperMini · serial streamed as it prints
System architecture
Jobs are coordinated in the cloud, while the device agent keeps the physical board behind the lab boundary.
Dashboard and API client
Requests · live output
Control plane on Cloudflare Workers
Auth · validation · queue
Postgres
Users · jobs · logs
Object storage
R2 · local fallback
Python service inside the homelab
Outbound socket · isolation
A real board on the bench
Native firmware · serial output
A .bin built for the board's profile, from the dashboard, the API, the Python SDK or the MCP server. It is checked against that profile — size, image format, chip — before it goes anywhere near a board.
curl -F firmware=@blink.bin \ -H "Authorization: Bearer $PAT" \ https://haas-lab.com/api/v1/jobs
The bench takes the job, flashes the binary to the board its profile names, and captures everything that board prints. If every board is busy, you see your place in the queue, live.
QUEUED -> FLASHING -> RUNNING -> COMPLETED profile esp32c3-mini-v1 queue position 2, live
Serial output streams to your browser as it happens and is kept with the job. Fetch the log from CI and fail the build on what the board said.
GET /api/v1/jobs/{id}/stream
-> live serial output
GET /api/v1/jobs/{id}/serial
-> the whole logThe same API the dashboard uses. Personal access tokens come from the dashboard; the Python SDK and the MCP server speak it for you.
# Flash a binary and read back what the board printed curl -X POST \ -H "Authorization: Bearer $PAT" \ -F firmware=@blink.bin \ https://haas-lab.com/api/v1/jobs curl -H "Authorization: Bearer $PAT" \ https://haas-lab.com/api/v1/jobs/<job-id>/serial
from haaslab import Client
client = Client(token="pat_...")
job = client.jobs.create("blink.bin")
job = client.jobs.wait(job.id, timeout=120)
print(client.jobs.serial(job.id))Skip the dev board, USB cables, and driver nightmares. We own the hardware; you own the firmware.
Every binary is checked against the board's profile before it reaches the bench, and a job can only ever replace the application image — never the bootloader, never the partition table.
One curl command in your GitHub Actions workflow. Get serial output as a build artifact, fail the build on assertion errors.
Sign in with Google or GitHub, or try the demo without an account. The bench is six ESP32-C3 boards today, and the public profile catalogue at/api/v1/device-profiles already describes the other targets the platform is built for. Nothing ships to you and nothing needs installing.