Getting started
Pulse runs as one process: collector, Solana firehose, JSON API and dashboard on a single port. Everything has a default, so a fresh clone works with no accounts and no keys.
Requirements#
- Node 24 or newer. Pulse runs TypeScript directly through Node type stripping and reads
.envnatively. - No database to install. Without
DATABASE_URLthe archive lives in an embedded PGlite directory atdata/pglite. - Optional: Docker for a TimescaleDB archive, a Telegram bot for the newsletter, and a dedicated Solana RPC endpoint to keep the firehose steady.
Run from source#
This is the path in the repository README.
git clone https://github.com/nirholas/pulse.git
cd pulse
npm install
cp .env.example .env # optional, everything has a default
npm run build # builds the dashboard into dist/web
npm start # collector + firehose + API + dashboard on http://localhost:8787
Open http://localhost:8787. The dashboard fills in after the first collection cycle finishes. Until then the overview shows an empty state and the status pill reads "last cycle pending".
Run from npm#
The pulse-trenches package ships two commands: pulse (the app) and pulse-mcp (the MCP server). Because the package has two bins, tell npx which package to take them from with -p.
npx -y -p pulse-trenches pulse # start everything (same as: pulse start)
npx -y -p pulse-trenches pulse --help # list subcommands
Subcommands of pulse:
| Command | What it does |
|---|---|
pulse start | Collector, firehose, API and dashboard. The default when no subcommand is given. |
pulse collect | Run one collection cycle and exit. Add --jobs to also import wallet labels and score wallets. |
pulse report | Build the daily newsletter. Flags: --day YYYY-MM-DD, --no-send, --print. |
pulse migrate | Apply database migrations and exit. |
pulse mcp | Start the MCP server over stdio. Set PULSE_URL to the API. |
Confirm it is working#
The health route reports which database engine is in use, when the last collection cycle finished, and the live firehose state.
curl -s http://localhost:8787/api/health
{"ok":true,"db":"pglite","lastCycle":"2026-10-09T16:02:44.530Z","stream":null,"prices":{"solUsd":110.30668154929297,"ethUsd":2507.195}}
The response above came from a real run started with --no-stream, which is why stream is null. With the firehose on, it holds connection state and rolling counters. Then ask for the top tokens by volume:
curl -s "http://localhost:8787/api/tokens?limit=3&sort=volume"
Day-to-day commands#
| Command | What it does |
|---|---|
npm run dev | Collector and Vite dev server with hot reload (http://localhost:5173, API on 8787). |
npm run collect | One collection cycle, then exit (-- --jobs also imports wallets and scores them). |
npm run report -- --no-send --print | Build today's newsletter without sending it. |
npm run db:up | Start TimescaleDB with Docker Compose on host port 5544. |
npm run db:migrate | Apply migrations and exit. |
npm test, npm run typecheck | Tests and types. |
Switch to Postgres or TimescaleDB#
npm run db:up # Postgres on host port 5544
echo 'DATABASE_URL=postgres://pulse:pulse@localhost:5544/pulse' >> .env
npm start
Any Postgres works. On a TimescaleDB server Pulse converts the time-series tables to compressed hypertables automatically. See Data model and Deployment.
Next steps#
- Set a Telegram bot token and chat to receive the daily newsletter.
- Point your agent at the MCP server.
- Tune the environment variables, especially a dedicated
SOLANA_WS_URLSendpoint. - Read how statuses, classification and wallet scores are decided.