Solana Onchain & Landscape Intelligence Signal. Detects emerging Solana ecosystem narratives by fusing social sentiment, developer activity, onchain metrics, and market signals into daily intelligence reports.
- Monorepo: pnpm workspaces —
packages/agent,packages/web,shared - Agent: TypeScript pipeline orchestration, Claude Haiku 4.5 (via OpenRouter) for LLM analysis, GLM fallback chain
- Web: Next.js 15 + Tailwind v4, 7-section landing page (hero → pipeline → report → narratives → ideas → methodology → CTA) + 8-section report dashboard (header → nav → metrics → diff → narratives → ideas → sources) + archive/compare pages
- Agent Daemon: Persistent VPS heartbeat — runs pipeline daily at 08:00 UTC, commits & pushes reports, lock file + state persistence
- Reports: Git-committed JSON/MD artifacts in
reports/, served to web via Docker volume mount (no rebuild needed) - API Guard: Route-level rate limiting (in-memory sliding window) + optional x402 micropayments for paid tier bypass
- TypeScript orchestration — direct function calls, no LLM needed for pipeline control
- Claude Haiku 4.5 (OpenRouter) = analyst — narrative clustering, build idea generation (GLM-4.7 → GLM-4.7-flash fallback)
- Narrative history tracking — fuzzy matching (Jaccard similarity) across reports, stage transitions, report diffing
- Post-pipeline alerting — Telegram/Discord notifications for stage transitions, new narratives, anomaly spikes
- Discovery (optional) — dynamic repo discovery via GitHub Search API
- Collect — parallel signal collection from all layers (3 base + optional Layer 0)
- Deltas — calculate changes from previous report
- Score — z-score anomaly detection
- Cluster — LLM narrative clustering with previous report context
- Ideas — LLM build idea generation
- Diff — compute report diff (new/removed narratives, stage transitions)
- Output — write JSON + Markdown reports
- Alerts — send notifications via configured channel
- Layer 0 (SOCIAL, opt-in): LunarCrush v4 — interactions, sentiment, galaxy score, social dominance; X/Twitter v2 — KOL timeline signals (Mert, Toly, Akshay, Messari, a16z, Solana Devs)
- Layer 1 (LEADING): GitHub API — stars, commits, forks, new repos
- Layer 2 (COINCIDENT): DeFi Llama + Helius — TVL, volumes, program activity
- Layer 3 (CONFIRMING): CoinGecko — prices, volumes, categories
pnpm dev # Start web dev server (port 3001)
pnpm agent # Run analysis pipeline (one-shot)
pnpm heartbeat # Start persistent heartbeat daemon (daily at HEARTBEAT_HOUR UTC)
pnpm test:run # Run all tests (~286 tests across agent + web)
pnpm typecheck # TypeScript check
pnpm build # Build all packages
pnpm deploy:agent # Bundle heartbeat.ts and SCP to VPS
pnpm eval:llm # LLM side-by-side eval (--date YYYY-MM-DD --models a,b)
pnpm eval:calibration # Confidence calibration analysisAll secrets in ~/Documents/secret/.env. Required:
OPENROUTER_API_KEY— LLM analysis via OpenRouter (narrative clustering + idea generation)GITHUB_TOKEN— GitHub APISOLIS_HELIUS_API_KEY— Onchain data
Optional config (all have sensible defaults in config.ts):
COLLECTION_PERIOD_DAYS(14) — lookback period for signal collectionLLM_TOP_REPOS(30),LLM_TOP_PROGRAMS(15),LLM_TOP_TOKENS(20) — signal condensation limitsGITHUB_THROTTLE_MS(500),COINGECKO_THROTTLE_MS(2000),HELIUS_THROTTLE_MS(300) — API rate limitingCOINGECKO_MAX_PAGES(2),DEFILLAMA_MIN_TVL(100000) — data filtersCACHE_ENABLED(true) — enable filesystem signal cachingCACHE_TTL_HOURS(20) — cache entry TTL (20h ensures daily runs get fresh data)HELIUS_PROGRAMS_PATH— override built-in program list with JSON fileOPENROUTER_FALLBACK_MODELS(z-ai/glm-4.7,z-ai/glm-4.7-flash) — comma-separated fallback model chain for 5xx errorsANOMALY_THRESHOLD(2.0) — z-score threshold for anomaly detectionALERTS_ENABLED(false) — enable post-pipeline notificationsALERT_CHANNEL(telegram) —telegramordiscordTELEGRAM_BOT_TOKEN,TELEGRAM_CHAT_ID— Telegram bot configDISCORD_WEBHOOK_URL— Discord webhook configALERT_ANOMALY_THRESHOLD(3.0) — z-score threshold for anomaly spike alertsENABLE_X_SIGNALS(false) — enable X/Twitter KOL signal collection via X API v2X_BEARER_TOKEN— X API v2 Bearer token (required when X signals enabled)X_THROTTLE_MS(1000) — delay between X API requestsX_KOL_HANDLES(mert,toly,akshaybd,MessariCrypto,a16zcrypto,solana_devs) — KOL handles to trackX_TWEETS_PER_KOL(5) — recent tweets to fetch per KOL (5-100)LLM_TOP_X_TOPICS(20) — max X topics sent to LLM for clusteringENABLE_SOCIAL_SIGNALS(false) — enable Layer 0 social signal collection via LunarCrushLUNARCRUSH_API_KEY— LunarCrush v4 Bearer token (required when social signals enabled)LUNARCRUSH_THROTTLE_MS(1000) — delay between LunarCrush API requestsLLM_TOP_SOCIAL_COINS(20) — max social coins sent to LLM for clusteringAPI_RATE_LIMIT(30) — default requests per window per IP (web API routes)API_RATE_WINDOW_MS(3600000) — rate limit window duration in ms (default: 1 hour)ENABLE_X402(false) — enable x402 micropayment paid tier (returns 402 instead of 429)X402_RECEIVER_ADDRESS— Solana wallet for USDC payments (required when x402 enabled)X402_PRICE_CENTS(1) — price per API request in USD centsX402_FACILITATOR_URL(https://x402.org/facilitator) — x402 payment verification endpointHEARTBEAT_HOUR(8) — target UTC hour for daily pipeline run (heartbeat daemon only)GIT_PUSH_ENABLED(true) — push report commits to remote (heartbeat daemon only)REPORTS_DIR— reports directory path. Required on VPS — esbuild bundle resolvesimport.meta.dirnameto bundle location, causing default path to resolve to/reports(root). Set to/home/solis/solis/reportsin.agent-envDATA_DIR(process.cwd()) — directory for subscriber data (web container uses Docker volume mount)QUERY_API_MODEL(anthropic/claude-haiku-4.5) — model for custom query API via OpenRouterRESEND_API_KEY— Resend email API key (required for email digest)DIGEST_FROM_EMAIL(digest@solis.rectorspace.com) — from address for digest emailsDIGEST_UNSUBSCRIBE_SECRET— HMAC key for stateless unsubscribe token verificationDIGEST_API_SECRET— shared secret for digest trigger API authentication
- VPS:
solis.rectorspace.com→151.245.137.75:8001(reclabs3, migrated Feb 28 2026) - User:
solis(SSH alias:ssh solis) - Docker:
ghcr.io/rector-labs/solis:latestviadocker-compose.yml(name: solis) - CI/CD: Push to
main(web/shared/Docker changes) → GHCR → VPS auto-deploy - Agent: systemd service
solis-heartbeat.service— auto-restart, survives reboots, runs pipeline daily, commits & pushes reports to git - Agent deploy:
pnpm deploy:agent— esbuild bundle → SCP to VPS, thenssh reclabs3 'systemctl restart solis-heartbeat' - Agent env:
/home/solis/solis/.agent-env— OPENROUTER_API_KEY, GITHUB_TOKEN, SOLIS_HELIUS_API_KEY, NODE_ENV=production, GIT_PUSH_ENABLED=true, HEARTBEAT_HOUR=8, REPORTS_DIR=/home/solis/solis/reports - Agent logs:
ssh solis 'journalctl -u solis-heartbeat -f' - Report generation: VPS heartbeat daemon at 08:00 UTC (GitHub Actions
generate-report.ymlkept as manual emergency fallback) - Reports volume: Docker volume mount (
/home/solis/solis/reports → /app/reports:ro) — new reports appear instantly without rebuild - Data volume: Docker volume mount (
/home/solis/solis/data → /app/data:rw) — subscriber storage, read-write - Deploy triggers: Only
packages/web/**,shared/**,Dockerfile,docker-compose.yml— agent/report changes don't trigger deploy - Cleanup: Deploy workflow prunes old images aggressively
- SSH aliases:
solis(solis user),reclabs3(root) — both at151.245.137.75 - DNS: Cloudflare (rectorspace.com zone). All A records Proxied (orange cloud). SSH uses IP directly, unaffected by proxy setting
- ISP note: Biznet (home ISP) no longer blocks VPS IP (confirmed Apr 2026) — direct SSH works
shared/src/types.ts— Type contract between agent and web (Narrative, ReportDiff, FortnightlyReport, SocialSignals, XSignals, QueryRequest/Response, Subscriber)packages/agent/src/tools/lunarcrush.ts— LunarCrush v4 API collector (Layer 0 social signals)packages/agent/src/tools/twitter.ts— X/Twitter v2 API collector (Layer 0 social signals)packages/agent/src/config.ts— Environment validation (envalid, 20+ vars)packages/agent/src/index.ts— Pipeline entry point (9 phases), exportsrunPipeline()for heartbeatpackages/agent/src/heartbeat.ts— Persistent daemon: lock file, state persistence, smart scheduling, git commit/push, graceful shutdownpackages/agent/src/utils/history.ts— Narrative matching, history population, report diffingpackages/agent/src/cache/store.ts— Filesystem signal cache with TTL-based expirypackages/agent/src/cache/star-history.ts— Star snapshot recording + 7d/30d delta enrichmentpackages/agent/src/eval/llm-compare.ts— LLM side-by-side eval CLI (Jaccard overlap, stage agreement, Brier)packages/agent/src/eval/calibration.ts— Confidence calibration analysis (persistence, Brier score)packages/agent/src/output/alerts.ts— Alert detection, formatting, Telegram/Discord dispatchpackages/agent/src/output/markdown.ts— Markdown report with "What Changed" diff sectionpackages/web/src/app/page.tsx— Homepage: 7-section landing page with container breakout strategypackages/web/src/components/hero-section.tsx— Client: animated gradient, glassmorphic card, floating tags, live stats tickerpackages/web/src/components/signal-pipeline.tsx— Server: 4-step horizontal/vertical timeline with layer colorspackages/web/src/components/report-summary-card.tsx— Server: glassmorphic report stats cardpackages/web/src/components/featured-narratives.tsx— Server: top 4 narratives by confidencepackages/web/src/components/build-ideas-highlight.tsx— Server: top 3 build ideaspackages/web/src/components/methodology-trust.tsx— Server: 2x2 trust/differentiator cardspackages/web/src/app/feed.xml/route.ts— RSS 2.0 feed route handler (revalidates hourly, XML string templating, no deps)packages/web/src/app/pricing/page.tsx— Server: API docs + pricing tiers + x402 integration guidepackages/web/src/components/charts/confidence-chart.tsx— Client: Recharts bar chart — confidence distribution across narrativespackages/web/src/components/charts/timeline-chart.tsx— Client: Recharts line chart — confidence over time per narrativepackages/web/src/components/charts/signal-radar.tsx— Client: Recharts radar chart — signal layer coverage per narrativepackages/web/src/components/charts/meta-trends.tsx— Client: Recharts multi-line chart — pipeline meta trends across reportspackages/web/src/app/trends/page.tsx— Server: pipeline trends page with MetaTrendsChart + aggregate statspackages/web/src/lib/openrouter.ts— Web-side OpenRouter client for custom query API (standalone from agent)packages/web/src/app/api/query/route.ts— POST: x402-gated LLM query with report context injection ($0.05/call)packages/web/src/lib/graph.ts— Force graph data builder (narratives, repos, tokens, protocols → nodes/links)packages/web/src/components/knowledge-graph.tsx— Client: react-force-graph-2d with dynamic import, hover tooltips, click-to-navigatepackages/web/src/app/brain/page.tsx— Server: knowledge graph page using latest report datapackages/web/src/lib/subscribers.ts— JSON file subscriber store with atomic writes, HMAC unsubscribe tokenspackages/web/src/app/api/subscribe/route.ts— POST: subscribe (rate-limited), DELETE: unsubscribe (HMAC-verified)packages/web/src/components/subscribe-form.tsx— Client: email subscribe form with loading/success/error statespackages/web/src/lib/digest-template.ts— HTML email template for daily intelligence digestpackages/web/src/lib/digest.ts— Resend email sender for digest deliverypackages/web/src/app/api/digest/route.ts— POST: digest trigger (shared secret protected, called by heartbeat)packages/web/src/components/open-source-cta.tsx— Server: full-bleed GitHub CTA with trust badges + RSS link + subscribe formpackages/web/src/components/scroll-indicator.tsx— Client: bouncing chevron, hides on scrollpackages/web/src/app/report/[date]/page.tsx— Report page: 8-section intelligence dashboard with section anchors, prev/next nav, stage groupingpackages/web/src/components/report-header.tsx— Server: glassmorphic header with breadcrumb, prev/next chevron nav, export buttons, timestamppackages/web/src/components/report-nav.tsx— Client: sticky section pills with IntersectionObserver scroll tracking, mounted SSR guardpackages/web/src/components/report-metrics.tsx— Server: 4-stat glassmorphic cards with color-coded top borders (purple/green/blue/orange)packages/web/src/components/report-diff.tsx— Server: "What Changed" section — stage transitions, new/faded signals, confidence shifts (≥10pt threshold)packages/web/src/components/narrative-stage-group.tsx— Server: stage-grouped narratives (EARLY→MAINSTREAM) with colored banners, confidence-sortedpackages/web/src/components/build-ideas-filter.tsx— Client: difficulty filter pills (All/Beginner/Intermediate/Advanced) with count badges + filtered gridpackages/web/src/components/data-sources-card.tsx— Server: visual source cards with status dots, layer badges (color-coded), LLM footerpackages/web/src/lib/temporal.ts— Date utilities (freshness, countdown, relative labels)packages/web/src/components/countdown-timer.tsx— Live countdown to next 08:00 UTC reportpackages/web/src/components/report-timestamp.tsx— Freshness-coded relative timestampspackages/web/src/lib/rate-limit.ts— In-memory sliding window rate limiterpackages/web/src/lib/x402.ts— x402 payment protocol (402 responses, proof verification)packages/web/src/lib/api-guard.ts— Composable guard combining rate limiting + x402packages/web/src/app/layout.tsx— App shell (shared header/footer, max-w-6xl container)Dockerfile— Multi-stage build (deps → build → standalone)docker-compose.yml— Production container config (port 8001, reports volume ro, data volume rw)
- 2-space indent, TypeScript strict mode
- Agent tools follow MCP pattern (zod schema + async handler)
- Tests: vitest, 80%+ coverage on agent/analysis
- One commit per feature/fix
- Client components use
mountedstate guard for SSR hydration safety - Full-bleed sections use
-mx-4 -mt-8container breakout on page.tsx (avoids layout.tsx changes) - Tailwind v4 dynamic class safety: color-dependent classes use static config maps (not template literals)
- Alert errors never crash the pipeline (graceful failure)
- API routes use route-level
apiGuard()— not Next.js middleware (routes use Node.js APIs) - x402 is fully opt-in — rate limiting is always-on, payment bypass requires
ENABLE_X402=true - apiGuard supports per-route
priceCentsoverride for different x402 pricing (e.g., query API at $0.05) - Subscriber data uses atomic writes (tmp+rename) for crash safety
- Digest trigger requires
x-digest-secretheader matchingDIGEST_API_SECRETenv var