A SvelteKit dashboard that displays statistics from a Nostr relay/indexer backend. It now uses SvelteKit server routes so the upstream bearer token stays server-side.
Nostr Stats provides a comprehensive view of Nostr network activity, including event counts, user statistics, kind distribution, and time-series data visualization. The dashboard connects to a backend API to fetch and display real-time statistics about the Nostr protocol network.
Note: This frontend is designed to be paired with the Pensieve API backend, an archive-first Nostr indexer that provides the statistics endpoints consumed by this dashboard.
- Overview Statistics: Total events, pubkeys, kinds, and event time range
- Kind Distribution: Breakdown of events by kind with sortable tables
- Time Series Charts: Visual representation of activity over time
- User Statistics: Active users, new users, and user growth metrics
- Responsive Design: Modern UI built with Tailwind CSS
- Framework: SvelteKit 2 with Svelte 5
- Styling: Tailwind CSS 4
- Charts: Chart.js
- Linting/Formatting: Biome
- Package Manager: Bun
- Deployment: Vercel (server-side rendering via adapter-vercel)
- Language: TypeScript
# Install dependencies
bun installStart the development server:
bun run dev
# Or open in browser automatically
bun run dev -- --openThe app will be available at http://localhost:5173 (or the next available port).
Build for production:
bun run buildPreview the production build locally:
bun run preview# Development
bun run dev # Start dev server
bun run build # Build for production
bun run preview # Preview production build
# Code Quality
bun run check # Type checking and Svelte validation
bun run check:watch # Watch mode for type checking
bun run lint # Run linter
bun run lint:fix # Fix linting issues
bun run format # Format code
bun run biome:check # Check linting and formatting
bun run biome:fix # Fix linting and formatting issuesThe app uses the following environment variables (set via .env or build arguments):
PUBLIC_API_URL- Backend API URL (defaults tohttp://localhost:8080)API_TOKEN- Server-only bearer token for the upstream APIBASE_PATH- Optional subpath base if you mount the app below/
Create a .env file in the root directory:
PUBLIC_API_URL=http://localhost:8080
API_TOKEN=your-api-token-here
API_TOKENmust stay server-only. The repo includes a guard that failsdevandbuildif you accidentally defineVITE_API_TOKENorPUBLIC_API_TOKEN.
src/
├── lib/
│ ├── api.ts # API client and types for backend
│ ├── format.ts # Formatting utilities (numbers, dates, etc.)
│ ├── components/ # Reusable Svelte components
│ │ ├── Chart.svelte
│ │ ├── InfoTooltip.svelte
│ │ ├── KindRow.svelte
│ │ ├── LoadingSkeleton.svelte
│ │ ├── SortableTable.svelte
│ │ └── StatCard.svelte
│ └── assets/ # Static assets (favicon, etc.)
├── routes/
│ ├── +page.svelte # Main dashboard page
│ ├── +layout.svelte # App layout
│ ├── +layout.ts # Prerender config
│ └── layout.css # Global styles
└── app.html # HTML template
- Use TypeScript for all new code
- Follow existing patterns in the codebase
- Use Biome for formatting (not Prettier)
- Components use Svelte 5 runes syntax (
$state,$derived,$effect) - API types are defined in
src/lib/api.ts
This project is configured for server-side deployment using @sveltejs/adapter-vercel.
Set these environment variables in Vercel:
PUBLIC_API_URLAPI_TOKENBASE_PATH(optional)
The dashboard uses:
src/routes/+page.server.tsfor the initial server-rendered payloadsrc/routes/api/dashboard/+server.tsfor a single aggregated refresh endpointsrc/lib/server/dashboard.tsto fan out to the upstream Pensieve API in parallel
This keeps the bearer token off the client while also reducing the browser request waterfall.
See LICENSE file for details.
- AGENTS.md - Instructions for AI agents working on this codebase
- docs/feature-reqs.md - Feature requests for the backend API