Skip to content

Architecture overview

Pullminder is a Turborepo pnpm monorepo with five apps:

AppStackPurpose
apiGo, chi routerREST API and webhook handler
dashboardReact 19, Vite, TanStack Router/Query, Tailwind CSS 4Web UI for teams
cliGo, CobraOffline analysis and platform commands
marketingAstroPublic website at pullminder.com
docsAstro StarlightDocumentation site

The API server is a single Go binary built around the chi router. Business logic lives in internal/ packages:

PackageResponsibility
apiHTTP handlers and middleware
authGitHub OAuth, sessions, CSRF
billingSubscription management, Viva Payments
configEnvironment and feature flags
cryptoAES-256-GCM field encryption, HMAC signing
evaluationRule engine, risk scoring
githubGitHub API client, webhook parsing
notifyEmail and Slack notifications
observeLogging, tracing, metrics
queueAsynq task definitions and handlers
registryRule pack fetching and caching
riskRisk model and score calculation
sessionSession store (PostgreSQL-backed)
storeDatabase queries via pgx

PostgreSQL 16.8 is the primary data store. The schema is managed through 78+ sequential migrations that run automatically when the API server starts. All queries use the pgx driver directly — there is no ORM.

Redis 7.4 powers the task queue via Asynq. Four task types flow through the queue:

TaskDescription
analyze_prFetch diff, run analyzers, post results
send_notificationDeliver email or Slack message
fetch_coveragePull coverage data from CI provider
baseline_scanFull-repo scan on initial install

Queue priorities control worker allocation:

PriorityWorkers
critical3
default6
low1

The dashboard is a React 19 SPA using TanStack Router for file-based routing and TanStack Query for server state. Styling uses Tailwind CSS 4. Charts are rendered with Recharts.

The CLI is a standalone Go binary built with Cobra. It runs the same analyzers as the platform but operates entirely offline against local diffs. Platform commands (sync, login) connect to the API.

The API and worker run as Docker containers on Hetzner behind a Caddy reverse proxy. The dashboard, marketing site, and docs deploy to Cloudflare Pages with automatic builds from the main branch.

  • Logging — structured JSON via slog
  • Error tracking — GlitchTip (Sentry-compatible)
  • Tracing — OpenTelemetry
  • Metrics — Prometheus /metrics endpoint (internal, token-authenticated)
  • Field encryption — AES-256-GCM with rotatable keys for sensitive data at rest
  • Badge signing — HMAC to prevent badge URL tampering
  • CSRF — double-submit cookie pattern
  • Sessions — HttpOnly, Secure, SameSite cookies backed by PostgreSQL
GitHub webhook
|
v
API server ──> PostgreSQL
|
v
Redis queue
|
v
Worker
|
├── analyze PR
├── post comment on GitHub
└── set commit status
|
v
Dashboard reads from PostgreSQL
  1. A pull request is opened or updated on GitHub.
  2. GitHub sends a webhook to the API server.
  3. The API validates the webhook signature, persists the event, and enqueues an analyze_pr task.
  4. The worker picks up the task, fetches the diff from GitHub, runs it through the rule engine, calculates a risk score, and stores the results.
  5. The worker posts a comment on the PR and sets a commit status check.
  6. The dashboard queries the API (backed by PostgreSQL) to display results to the team.