Skip to content

HackHumanityOrg/pulse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

314 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fmt Clippy Lint Tests

Pulse

Pulse is a community moderation platform that unifies data from Discourse forums, Telegram channels, and on-chain NEAR activity, then routes it through AI-assisted workflows for human review. The repository contains every service, shared package, and deployment artifact needed to run the stack yourself.

Highlights

  • Real-time ingestion from Discourse, Telegram, and NEAR balance activity
  • AI-powered content rating queue with structured moderation outcomes
  • Next.js 16 dashboard built on React Server Components and Better Auth
  • Shared PostgreSQL schema with generated TypeScript and Rust types
  • Docker, Docker Compose, and Railway deployment paths for every long-running service

Architecture

┌──────────────────────────────────────────────────────────────────────────────────────┐
│                         PostgreSQL Database                                          │
│                    (Single Source of Truth)                                          │
└──────────────────────────────────────────────────────────────────────────────────────┘
           ↑             ↑              ↑             ↑                   ↑
    ┌──────┴──────┐ ┌────┴────┐  ┌──────┴─────┐ ┌─────┴──────┐  ┌─────────┴─────────┐
    │   Web App   │ │The Brain│  │ Discourse  │ │ Telegram   │  │ NEAR Balance Sync │
    │  (Next.js)  │ │(Node.js)│  │  Listener  │ │  Listener  │  │     (Rust)        │
    └─────────────┘ └─────────┘  └────────────┘ └────────────┘  └───────────────────┘

Repository Layout

Path Description
apps/web Next.js moderation dashboard with Better Auth integration
apps/the-brain Node.js worker that processes the content rating queue with AI providers
apps/discourse-listener Rust service that ingests topics, posts, likes, and revisions from Discourse
apps/telegram-listener Rust MTProto listener for Telegram channels and reactions
apps/near-balance-listener Rust worker that snapshots NEAR account balances via FastNEAR
packages/shared Shared TypeScript helpers, database types, feature flags, and AI model registry
packages/better-auth-near Better Auth extension that adds NEAR wallet authentication
migrations / seeds Kysely migrations and optional seed scripts
docker-compose.yml End-to-end local container stack

Getting Started

Prerequisites

  • Node.js 20 or newer
  • pnpm 10.x
  • Rust stable toolchain
  • PostgreSQL 15+ (local or remote)
  • Docker (optional, for containerized workflows)

Local Development

pnpm install
cp .env.example .env
pnpm db:migrate:latest
pnpm db:generate
pnpm dev

Useful follow-up commands:

pnpm the-brain:dev
pnpm discourse-listener:dev
pnpm telegram-listener:dev
pnpm near-balance-listener:dev
pnpm db:seed:run   # optional demo/bootstrap data

Environment variable details live in docs/environment.md.

Deployment

This monorepo ships as independently deployable services plus a shared PostgreSQL database.

Component Role Public ingress Main requirements
web Next.js moderation UI Yes DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, FLAGS_SECRET, GitHub OAuth
the-brain AI rating worker with /health endpoint No DATABASE_URL, OPENROUTER_API_KEY, PORT or HEALTH_PORT
discourse-listener Discourse ingestion worker No DATABASE_URL, DISCOURSE_BASE_URL
telegram-listener Telegram ingestion worker No DATABASE_URL, Telegram credentials, durable session storage or TELEGRAM_SESSION_DATA
near-balance-listener NEAR balance worker No DATABASE_URL, optional FASTNEAR_API_KEY
db-migrate One-off schema migration job No Run before any schema-dependent rollout
db-seed Optional bootstrap/demo data job No Non-production only unless you intentionally want seed data

Docker

Use plain Docker when you want to run each container explicitly instead of relying on docker-compose.yml.

Build the images:

docker build -f migrations/Dockerfile -t pulse/db-migrate .
docker build -f apps/web/Dockerfile -t pulse/web .
docker build -f apps/the-brain/Dockerfile -t pulse/the-brain .
docker build -f apps/discourse-listener/Dockerfile -t pulse/discourse-listener .
docker build -f apps/telegram-listener/Dockerfile -t pulse/telegram-listener .
docker build -f apps/near-balance-listener/Dockerfile -t pulse/near-balance-listener .

Create a network and start PostgreSQL:

docker network create pulse-local
docker volume create pulse-postgres

docker run -d \
  --name pulse-db \
  --network pulse-local \
  -p 55432:5432 \
  -e POSTGRES_DB=codeofconduct \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -v pulse-postgres:/var/lib/postgresql/data \
  postgres:16-alpine

Wait for PostgreSQL and run migrations:

until docker exec pulse-db pg_isready -U postgres -d codeofconduct; do sleep 1; done

docker run --rm \
  --name pulse-migrate \
  --network pulse-local \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  pulse/db-migrate

Optional seed step:

docker run --rm \
  --name pulse-seed \
  --network pulse-local \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  pulse/db-migrate \
  npx kysely seed run

Start the main services:

docker run -d \
  --name pulse-web \
  --network pulse-local \
  -p 3000:3000 \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  -e DEV_AUTH_BYPASS=false \
  -e BETTER_AUTH_SECRET=... \
  -e BETTER_AUTH_URL=https://pulse.example.com \
  -e NEXT_PUBLIC_APP_URL=https://pulse.example.com \
  -e FLAGS_SECRET=... \
  -e GITHUB_CLIENT_ID=... \
  -e GITHUB_CLIENT_SECRET=... \
  pulse/web

docker run -d \
  --name pulse-the-brain \
  --network pulse-local \
  -p 3001:3001 \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  -e OPENROUTER_API_KEY=... \
  -e PORT=3001 \
  pulse/the-brain

docker run -d \
  --name pulse-discourse-listener \
  --network pulse-local \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  -e DISCOURSE_BASE_URL=https://gov.near.org \
  -e RUST_LOG=info \
  pulse/discourse-listener

For a local smoke test without wiring OAuth first, add -e DEV_AUTH_BYPASS=true -e IS_LOCAL=true -e DOCKER_ENV=development to the pulse-web container. Keep those values disabled in any public environment.

Optional workers:

docker run -d \
  --name pulse-near-balance-listener \
  --network pulse-local \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  -e FASTNEAR_API_KEY="${FASTNEAR_API_KEY:-}" \
  -e RUST_LOG=info \
  pulse/near-balance-listener

Telegram requires credentials plus durable session storage:

docker volume create pulse-telegram-session

docker run -d \
  --name pulse-telegram-listener \
  --network pulse-local \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  -e TELEGRAM_API_ID=... \
  -e TELEGRAM_API_HASH=... \
  -e TELEGRAM_CHANNELS=@nearprotocol \
  -e TELEGRAM_SESSION_FILE=/var/lib/telegram/telegram_session.bin \
  -v pulse-telegram-session:/var/lib/telegram \
  pulse/telegram-listener

Useful checks:

docker ps --filter name=pulse-
curl http://127.0.0.1:3000/healthz
curl http://127.0.0.1:3001/health
docker logs -f pulse-the-brain

Cleanup:

docker rm -f \
  pulse-web \
  pulse-the-brain \
  pulse-discourse-listener \
  pulse-telegram-listener \
  pulse-near-balance-listener \
  pulse-db \
  2>/dev/null || true

docker network rm pulse-local 2>/dev/null || true
docker volume rm pulse-postgres pulse-telegram-session 2>/dev/null || true

Docker Compose

The repo includes docker-compose.yml for a local multi-container stack:

  • db, db-migrate, web, the-brain, and discourse-listener start by default
  • telegram-listener is behind the telegram profile
  • near-balance-listener is behind the near profile
  • db-seed is behind the seed profile and stays optional
  • PostgreSQL data and Telegram session state use named volumes

Build the images:

docker compose build

Start the default stack:

docker compose up -d

Enable optional services only when you need them:

docker compose --profile seed run --rm db-seed
docker compose --profile near up -d near-balance-listener
docker compose --profile telegram up -d telegram-listener

Useful checks:

docker compose ps
docker compose logs -f web the-brain discourse-listener
curl http://127.0.0.1:3000/healthz

Stop and clean up:

docker compose --profile seed --profile near --profile telegram down
docker compose --profile seed --profile near --profile telegram down -v

Notes for this compose stack:

  • The containers always use the internal Postgres hostname db; they do not inherit your host-local DATABASE_URL.
  • PostgreSQL is exposed to the host on postgresql://postgres:postgres@127.0.0.1:55432/codeofconduct by default.
  • db-seed is explicit on purpose. Production deployments should not seed automatically on every rollout.
  • telegram-listener stores session state in the telegram_session volume at /var/lib/telegram/telegram_session.bin.
  • The local compose stack sets DEV_AUTH_BYPASS=true, IS_LOCAL=true, and DOCKER_ENV=development for web so containerized smoke tests do not require OAuth setup.
  • the-brain stays on the internal compose network; use docker compose ps to inspect its health status, or run docker compose exec the-brain node -e "fetch('http://127.0.0.1:3001/health',{redirect:'manual'}).then(async (response) => { process.stdout.write(await response.text()); process.exit(response.status === 200 ? 0 : 1) }).catch(() => process.exit(1))" if you want the raw JSON body.
  • The default compose stack uses a placeholder OPENROUTER_API_KEY so the-brain can boot for smoke tests. Set a real key before you expect rating jobs to succeed.

Recommended Deployment Sequence

  1. Provision PostgreSQL and set DATABASE_URL for every service.
  2. Run db-migrate before starting any schema-dependent service.
  3. Seed only when bootstrapping demo data or an intentionally preloaded environment.
  4. Deploy web and verify /healthz.
  5. Deploy the-brain and verify /health.
  6. Deploy only the listener services you actually need.
  7. Validate logs, OAuth callback URLs, Telegram session persistence, and database connectivity after each rollout.

Minimum Runtime Configuration

  • web: DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, FLAGS_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
  • the-brain: DATABASE_URL and OPENROUTER_API_KEY
  • discourse-listener: DATABASE_URL, DISCOURSE_BASE_URL
  • telegram-listener: DATABASE_URL, TELEGRAM_API_ID, TELEGRAM_API_HASH, TELEGRAM_CHANNELS, and either TELEGRAM_SESSION_DATA or a persistent TELEGRAM_SESSION_FILE
  • near-balance-listener: DATABASE_URL, optional FASTNEAR_API_KEY

Telegram Session Bootstrap and Rotation

telegram-listener can authenticate in two ways:

  • Persistent disk via TELEGRAM_SESSION_FILE
  • Base64 session injection via TELEGRAM_SESSION_DATA

For a deployment with durable storage, bootstrap the session once interactively:

docker volume create pulse-telegram-session

docker run --rm -it \
  --name pulse-telegram-auth \
  --network pulse-local \
  -e DATABASE_URL=postgresql://postgres:postgres@pulse-db:5432/codeofconduct \
  -e TELEGRAM_API_ID=... \
  -e TELEGRAM_API_HASH=... \
  -e TELEGRAM_CHANNELS=@nearprotocol \
  -e TELEGRAM_ALLOW_INTERACTIVE_LOGIN=true \
  -e TELEGRAM_SESSION_FILE=/var/lib/telegram/telegram_session.bin \
  -v pulse-telegram-session:/var/lib/telegram \
  pulse/telegram-listener

For an ephemeral platform such as Railway, encode the session file and store it as TELEGRAM_SESSION_DATA:

base64 < telegram_session.bin | tr -d '\n'

Then set:

  • TELEGRAM_SESSION_DATA=<single-line-base64-value>
  • TELEGRAM_SESSION_FILE=/var/lib/telegram/telegram_session.bin

If you need to rotate a session, stop the worker, repeat the same bootstrap flow, then restart the long-running deployment with the updated file or base64 blob.

Railway

Pulse already includes Railway manifests for the long-running services:

Use one Railway service per component. Keep the full repo context so the workspace builds can access shared packages from the monorepo root.

Recommended Railway setup:

  1. Create or attach a PostgreSQL service.
  2. Deploy web from apps/web/railway.toml and verify /healthz.
  3. Deploy the-brain from apps/the-brain/railway.toml and verify /health.
  4. Deploy the Rust listeners from their service-specific manifests as background workers.
  5. Run migrations separately before shipping schema-dependent changes. The simplest path is a one-off service or command based on migrations/Dockerfile that runs npx kysely migrate latest.

Railway-specific notes:

  • Set DEV_AUTH_BYPASS=false in any public environment.
  • web health check path is /healthz, not /api/health.
  • the-brain binds its health server on port 3001.
  • The Rust listeners are worker processes; they should rely on restart-on-failure, logs, and metrics rather than path-based HTTP health checks.
  • For Telegram on Railway, prefer TELEGRAM_SESSION_DATA unless you attach durable storage.

Operational Notes

  • web exposes a dependency-free /healthz endpoint. Treat it as a liveness check, not a full readiness probe.
  • the-brain exposes /health and /stats; /health includes database connectivity and queue backlog state.
  • In Docker Compose, the-brain is intentionally not published on a host port; use container health or docker compose exec to query /health.
  • db-seed is intentionally not part of the default rollout path.
  • DEV_AUTH_BYPASS must remain false anywhere the app is reachable from the internet.

Deployment Artifacts in This Repo

Validation

pnpm format:check
pnpm fmt:rust:check
pnpm lint
pnpm clippy
pnpm test
pnpm build

Additional Docs


Created by Hack Humanity • Copyright © 2026 • MIT License

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages