Skip to content

Repository files navigation

n-dashboard

A personal single-page dashboard for a trusted LAN. Every widget is a TypeScript function on disk; the server runs it on a schedule, on an MQTT message or on a webhook, validates what it returns, and pushes the result to every open tab.

The board: eight widgets on live data — BTC, disk usage, endpoint health, a temperature chart, candlesticks, npm downloads, a market-cap table and Hacker News

Every card above is one file. Writing one looks like this — the editor knows the whole ctx API, because it is served the same ctx.d.ts the runtime uses:

Monaco completing ctx. in the admin panel: the list of ctx methods, filtered to secret, with its signature and doc comment

  • Widgets are functions. No plugin format, no config-driven renderer path. Summing a field, mixing two APIs, remembering what you already saw — all the same amount of work, because it is all just code.
  • Typed, and checked in the browser. Monaco runs the real TypeScript service against the same ctx.d.ts the server validates with, so a wrong return type is a red squiggle rather than a blank card at 3am.
  • Nothing silently stale. A failing widget keeps its last good value, dimmed, with the error on hover and a visible backoff.

Quick start

Needs Node 26+ — the server runs its TypeScript directly, no build step and no transpiler, which is also what gives widget stack traces honest line numbers. npm too; Docker only for the container or the dev MQTT broker.

git clone <this repo> && cd n-dashboard
npm install
npm run dev

Open http://localhost:5273. The first run copies the seventeen examples from examples/ into data/widgets/, disabled — enable the ones you want under /admin. Eight of them need no key and no connection, so crypto-price, weather-hourly, hn-top, npm-downloads, endpoint-health, btc-candles, api-sum and disk-usage all work on a fresh clone.

script what it does
npm run dev server on :8080 + Vite on :5273, proxying /api
npm run build builds the web bundle into dist/web
npm start serves dist/web and the API from :8080
npm test Vitest
npm run typecheck both projects

Or run the container

For the always-on install there is a prebuilt image, linux/amd64 and linux/arm64 so a Pi or an ARM NAS needs no local build:

docker run -d --name n-dashboard -p 8080:8080 \
  -v n-dashboard-data:/app/data \
  ghcr.io/devapro/n-dashboard:latest

:latest tracks the newest release tag and :edge tracks main, deliberately — a box on your LAN should not pick up a mid-refactor commit on restart. HOST and PORT are already set in the image; add -e ADMIN_PASSWORD=… to put a login on /admin.

To build it from source instead, with an optional MQTT broker alongside:

cd docker
docker compose up -d --build            # then http://localhost:8080
docker compose --profile broker up -d   # only if you have no broker of your own

data/ holds everything mutable — widgets, layout, secrets, history — so back up that one volume and you have backed up the lot.

Configuration is a .env in the repository root — copy .env.example. Set ADMIN_PASSWORD to put a login on /admin; the board itself stays readable by design. See Configuration and deployment for the rest.

Widgets in one minute

A widget is a folder under data/widgets/<id>/ whose widget.ts default-exports one function:

import type { Ctx, StatData } from '@n-dashboard/widget'

export default async function (ctx: Ctx): Promise<StatData> {
  const r = await ctx.fetch('https://api.example.com/price').then((r) => r.json())
  return { value: r.usd, unit: 'USD', delta: r.change24h }
}

It returns data matching its kind — stat, line-chart, candlestick, gauge, list, alert, table, bar-chart or markdown — and if it does not, the run is recorded as an error and the card keeps its previous value. Triggers are a clock, an MQTT topic, a webhook, or several at once.

Documentation

Writing a widget the folder layout, every kind, triggers, the full ctx, recorded history, isolation
Connections, webhooks and secrets MQTT, Google Calendar (iCal and OAuth), webhook tokens, the secret store
Configuration and deployment environment variables, the admin login, Docker, the GitHub Pages project page
How it fits together the repo map, the design decisions and why they went that way, the test suite
src/shared/ctx.d.ts the widget API itself — the same file the editor and the validator use
CLAUDE.md invariants that are easy to break and library gotchas that cost real time

Not included

Deliberately: multiple dashboards, user accounts and roles (the /admin login is one shared password, not a user model), IMAP, widget import/export, alert notifications (alert widgets display state only), long-retention downsampling, and phone-optimised layouts.

License

MIT — do what you like with it.

About

A self-hosted dashboard where every widget is a TypeScript function on disk — the server runs it on a clock, an MQTT message or a webhook, validates what it returns, and pushes the result to every open tab.

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages