Blocks are just TypeScript
Named, typed inputs and outputs on a plain async function. If you need a switch statement, you write a switch statement — no expression language, no palette of “switch” and “change” nodes.
open source · bun · home assistant
Flowbun is a flow-based runtime in the spirit of Node-RED — except blocks are plain async TypeScript, wiring is diffable JSON on disk, and every wire is checked by tsc before anything runs.
Named, typed inputs and outputs on a plain async function. If you need a switch statement, you write a switch statement — no expression language, no palette of “switch” and “change” nodes.
Every flow compiles through a generated assertion file with realtsc. A wire carrying the wrong shape fails the reload — and the old flow keeps running untouched.
Only @hass/* blocks can touch Home Assistant, and each flow runs in its own process. A crash in one flow never takes down another — or your house.
Blocks live in data/blocks/, one TypeScript module each. Wiring lives beside them as plain JSON — diffable, greppable, editable in your IDE. The runtime only ever reads it, and every write from any tool is auto-committed to its own git history.
State is three-scope SQLite, so a debounce timer survives reloads, restarts, and kill -9.
import { defineBlock } from "flowbun";
export default defineBlock({
name: "debounce",
config: { ms: 30_000 },
inputs: { signal: {} as { state: string; at: number } },
outputs: { stable: {} as { state: string } },
async process({ signal }, ctx) {
const last = await ctx.state.block.get<number>("lastAt");
if (last !== undefined && signal.at - last < ctx.config.ms) return;
await ctx.state.block.set("lastAt", signal.at);
return { stable: { state: signal.state } };
},
});A browser canvas, Monaco with live typecheck errors on save, structured logs filtered per node, and an embedded Claude agent that edits flows through the exact same audited path you do. All of it is a plain websocket client of the same on-disk files — anything the editor can do, a curl script can do too.
