Monitor the current thing. Your dashboard for the internet. Build a deck, pack it with columns, refresh on demand. Each column is a plugin: X, Reddit, Hacker News, Lobsters, Stack Overflow, DEV.to, npm + PyPI + crates.io packages, Hugging Face (models / datasets / spaces), arXiv (CS / stat / math.OC papers), GitHub (trending / issues / PRs / stars / forks / backlinks / search / releases / commits / Actions / Discussions), Farcaster, Mastodon, YouTube, RSS, Google News, Substack, LinkedIn, Facebook, Instagram, Apple + Google Play reviews, on-chain wallet activity, Polymarket prediction markets, CoinGecko crypto trending + prices, DeFiLlama TVL leaderboard, Dexscreener DEX pair search + watchlist, and the six biggest Chinese platforms (Weibo / Zhihu / Douyin / Bilibili / Toutiao / Baidu).
- You name a deck. Minitor packs it with whatever you're watching.
- 47 column types out of the box — social feeds, news, GitHub (including commits, CI runs, and Discussions), Hugging Face, arXiv, DEV.to, Product Hunt, npm + PyPI + crates.io packages, app reviews, on-chain transactions, prediction markets, CoinGecko prices, DeFiLlama TVL, Dexscreener DEX pairs, Chinese hot boards.
- Refresh per column or auto-fetch on creation. Load more pages 10 at a time.
- Shape the signal per column — highlight items with alert keywords, or filter the feed to "show only" / "hide" by keyword so a firehose column shows just what matters.
- ⌘K command palette over every deck, column, and action. Drag to reorder.
- Local-first by default — embedded PGlite, no Postgres install needed.
The recommended path: one command, ./minitor. First column up in under a minute, zero infra.
Prereqs — Node 20+. That's it. PGlite is bundled (real Postgres compiled to WASM), so no Docker, no hosted database, no setup.
git clone https://github.com/aeonfun/minitor.git && cd minitor
./minitorThe launcher checks Node, picks the right package manager (npm / pnpm / yarn / bun based on lockfile), installs deps, copies .env.example → .env.local if missing, runs DB migrations against PGlite, and starts the dev server at http://localhost:3000. Re-running it just starts the server.
For Grok / X / News / Web / Farcaster columns, paste your xAI API key into XAI_API_KEY in .env.local. Keyless columns (Reddit, HN, Lobsters, Stack Overflow, DEV.to, Product Hunt, npm, PyPI, crates.io, Hugging Face, arXiv, Mastodon, RSS, Google News, GitHub, China Hot, YouTube channel/playlist, app reviews, wallet transactions, Polymarket, CoinGecko, DeFiLlama, Dexscreener) work out of the box with no keys.
Other launcher subcommands:
./minitor build # production build
./minitor start # production server (after build)
./minitor migrate # run DB migrations only
./minitor doctor # print environment diagnostics
./minitor reset # wipe the local PGlite data dir
./minitor help # full usage| Category | Columns |
|---|---|
| Social — X / Reddit / HN / Farcaster / Mastodon (6) | x-search, x-trending, reddit, hacker-news, farcaster, mastodon |
| GitHub (11) | github-trending, github-releases, github-issues, github-prs, github-commits, github-stars, github-forks, github-search, github-backlinks, github-actions, github-discussions |
| News & web (10) | google-news, news-search, rss, lobsters, stack-overflow, devto, npm, pypi, crates, producthunt |
| AI / ML (2) | huggingface (trending models, datasets, spaces), arxiv (CS / stat / math.OC papers) |
| Long-form & video (3) | substack, youtube, linkedin |
| Mention monitors (2) | facebook, instagram |
| Apps & on-chain (7) | apple-reviews, play-reviews, wallet-tx, polymarket, coingecko, defillama, dexscreener |
| China hot boards (6) | weibo-hot, zhihu-hot, douyin-hot, bilibili-hot, toutiao, baidu-hot |
Full plugin manifest: lib/columns/plugins/manifest.ts. Add a new source by copying lib/columns/plugins/_template/ — see lib/columns/README.md for the full contract.
Keys: XAI_API_KEY for x-*, news-search, linkedin, facebook, instagram (and Substack's keyword-only mode). Optional NEYNAR_API_KEY for Farcaster (demo-key fallback works), optional YOUTUBE_API_KEY for YouTube search (channel / playlist Atom feeds are keyless), optional GITHUB_TOKEN for every github-* column (60 req/hr keyless → 5000 req/hr with a token; github-discussions follows the same rule), optional COINGECKO_DEMO_API_KEY for higher CoinGecko rate limits (keyless mode works fine for low-traffic decks). Everything else runs without keys.
| Category | Highlights |
|---|---|
| Launcher (6) | ./minitor (dev), ./minitor build, ./minitor start, ./minitor migrate, ./minitor doctor, ./minitor reset — auto-detects npm / pnpm / yarn / bun, idempotent re-runs |
| Plugin system (4) | 3-file folders (plugin.ts, client.tsx, server.ts), Zod-validated configs, init-time parity check across 3 registries, copy-paste _template/ to add a source |
| Pagination (3) | Cursor-based { items, nextCursor? } everywhere, 10 items per page, Load more + End of results states; slice helper at lib/columns/paginate.ts for non-cursor sources |
| Database (4) | PGlite default (zero install), node-postgres for self-hosted, @neondatabase/serverless HTTP driver for Neon, runtime selector by DATABASE_URL |
| UI (7) | ⌘K command palette, @dnd-kit drag-reorder, conic-gradient refresh beam (CSS-only), live-ticking timestamps (1Hz useSyncExternalStore), onboarding flow, missing-key dimming on Add column, full-column loading skeleton |
| State (3) | zustand + server-action writes, optimistic mutations, no localStorage (every device sees the same state) |
| Deck portability (5) | Export (copy JSON), import (paste JSON), share link (#deck=… URL fragment), starter templates, and version history — auto-captured snapshots (last 5 per deck) restorable as a new deck from the deck menu |
- Founder dashboard — your X mentions, Hacker News, Product Hunt RSS, GitHub stars on your repo, App Store reviews, Substack analytics, all on one screen
- Crypto desk — wallet activity across 9 chains, X trending in crypto, Reddit r/cryptocurrency, news search for protocol names
- Open-source maintainer — GitHub trending in your language, new issues / PRs across watched repos, new stargazers + forks, backlinks from HN / Reddit / news for your repo URL
- Journalist — Google News + xAI news search on a topic, paired with X trending and Reddit threads, plus Substack publications you trust
- PM / marketing — Apple + Google Play reviews on your app, Twitter mentions, LinkedIn / Facebook / Instagram mentions, Substack write-ups
- China-watcher — Weibo, Zhihu, Douyin, Bilibili, Toutiao, and Baidu hot boards in one column stack
- Personal radar — RSS for the blogs that matter, Farcaster for the casts that matter, no algorithmic feed in sight
- Next.js 16 (App Router, Turbopack) + React 19 + TypeScript
- Tailwind v4 + shadcn/ui (
base-novastyle on@base-ui/react) - Drizzle ORM with three runtime drivers (PGlite default / node-postgres /
@neondatabase/serverless) - xAI Agent Tools API (
/v1/responseswithx_search+web_search) — Grok-powered search backbone - zustand for client state with server-action writes
- @dnd-kit for sortable decks + columns
- sonner toasts, cmdk command palette, Instrument Serif + Geist fonts
Set DATABASE_URL in .env.local:
| URL form | Driver picked |
|---|---|
unset / pglite: / file: |
PGlite, persisted to .minitor/pgdata/ (default) |
memory: |
PGlite, ephemeral in-memory — nothing persists (used by the test suite) |
postgres://user:pass@localhost/minitor |
node-postgres |
postgresql://…@…neon.tech/…?sslmode=require |
@neondatabase/serverless HTTP |
./minitor migrate and the runtime client honor the same selector (lib/db/client.ts:resolveDatabaseConfig).
Minitor ships as a single Docker image (Dockerfile, output: "standalone") that any container host builds straight from source. It runs migrations on boot, then serves on $PORT (default 3000). Pair it with a Postgres database.
Heads up — Minitor has no per-user auth. Decks and columns are global, so a public URL is a shared, editable dashboard.
MINITOR_PASSWORDputs the whole app behind a single-password login page (a signed session cookie,/login, "Log out" in Settings). In a hosted deployment it is required — the image fails closed and serves a lock screen until you set it, so an instance is never accidentally public.
Local / VPS — one command:
MINITOR_PASSWORD=changeme XAI_API_KEY=xai-… docker compose up --buildBrings up Postgres + Minitor, migrates automatically, serves at http://localhost:3000. Data persists in the minitor-pgdata volume.
Railway (or Render / Fly) — one click:
- New project → Deploy from repo. Railway auto-detects the
Dockerfile(railway.jsonpins the builder + health check). - Add a Postgres plugin. Railway exposes its connection string as
DATABASE_URL— reference it on the app service. - Set
MINITOR_PASSWORD(required — the app won't serve without it) and any API keys as service variables, then deploy.
Hosting env vars:
| Var | Purpose |
|---|---|
DATABASE_URL |
postgres://… (compose/Railway) or a Neon URL. Required in hosted mode — memory:/PGlite is ephemeral. |
MINITOR_PASSWORD |
The login password. Gates the whole app behind a /login page (signed session cookie). Required when hosted — unset in hosted mode fails closed with a lock screen. Unset locally = open (dev default). |
MINITOR_HOSTED |
Baked to 1 in the image. Disables the in-app key editor; keys come from env vars. |
XAI_API_KEY, GITHUB_TOKEN, … |
Column API keys, read from the host environment. |
The image bakes MINITOR_HOSTED=1, so the Settings dialog is read-only and setEnvKeys is refused server-side — provide keys as environment variables instead. GET /api/health is an unauthenticated liveness probe (excluded from the password gate).
| Plugin contract | The 3-file plugin pattern + how to add a new column type |
lib/columns/types.ts |
ColumnUI, ColumnServer, PluginMeta, FeedItem, PageResult, Capabilities |
lib/columns/plugins/manifest.ts |
Canonical plugin id list — single source of truth |
lib/columns/paginate.ts |
Slice-based cursor pagination helper for non-cursor sources |
lib/db/client.ts |
Multi-driver Drizzle client (PGlite / pg / Neon HTTP) |
scripts/db-migrate.mjs |
The migration runner used by ./minitor migrate |
- Vercel or any Node host works. Set
DATABASE_URLandXAI_API_KEYas env vars. @neondatabase/serverlessuses the HTTP driver (no websockets in serverless), so Neon's pooled connection string is required.app/api/columns/[type]/route.tsisdynamic = "force-dynamic"andmaxDuration = 60for Grok latency.
.env.localis gitignored — only.env.exampleships.- Server actions call xAI / Neynar / GitHub server-side; API keys never reach the client.
- The add-column dialog asks the server which env keys are present (boolean only, values never leave the server) to grey out plugins you can't use.
- No auth layer: every browser hitting your deployment sees the same decks. To partition per user, add a
user_idcolumn to each table and filter in the server actions.
MIT.

