Looks Good To Meow — AI code review and CI/CD security, before bad code merges and before bad pipelines run.
LGTM has two product surfaces that share infrastructure:
- PR Review — open a PR, get an inline review from six specialist agents (security, bugs, performance, readability, best practices, documentation) plus a synthesized verdict, in about 30 seconds.
- LGTM Security — continuous CI/CD configuration scanning. Catches hardcoded secrets, the
pull_request_targetsupply-chain RCE pattern, privileged containers, shell injection from PR titles, unpinned actions, and 12 more well-known classes of CI/CD risk. Three enforcement gates: inline PR comments, GitHub Check Runs that block the merge, and a runtime Action that halts CI jobs at step 1 before any other step runs.
- Web app —
http://localhost:5173 - API —
http://localhost:3000 - LGTM Security tab —
/dashboard/security - Runtime watchdog Action —
tarinagarwal/security-watchdog-action - Hackathon writeup — see HACKATHON.md
.
├── server/ Express API + BullMQ workers + agent orchestration
│ ├── src/agents/ 6 review agents + ci-security agent + tree-sitter context
│ ├── src/security/ Pure-function rule library (16 detectors, no LLM)
│ ├── src/jobs/ review.job, context.job, security.job, security-cron
│ ├── src/models/ Mongoose schemas (User, Repo, Review, SecurityMonitor, …)
│ └── src/__tests__/ 123 vitest tests, runs against in-memory Mongo
├── client/ React 19 + Vite + Tailwind v4 + custom claymorphism
│ └── src/pages/ Dashboard, Repos, Reviews, LgtmSecurity*, Settings, …
├── lgtm-cli/ `lgtm` CLI — login, repo, review (SSE-streamed), config
├── lgtm-action/ GitHub Action published as v1 — halts CI on block findings
├── security/ STEP-0 scope decisions doc
├── content/ Marketing content
├── theme/ Design tokens
└── HACKATHON.md Story behind the build
Server — Node 20 / TypeScript / Express. MongoDB (Atlas in prod, in-memory for tests) for everything durable. Redis for BullMQ queues + the runtime halt-decision cache. Three queues: context (tree-sitter parsing + dependency graphs + PageRank), review (the 6-agent review pipeline), security (CI/CD scans). Workers can run inline with the API or split into a separate process.
Rule library — server/src/security/rules/. Pure functions, no I/O, no LLM. The same library is consumed by both the PR-review path (agents/review/ci-security.ts) and the monitor worker (jobs/security.job.ts). 16 detectors covering hardcoded secrets, workflow YAML supply-chain patterns, Dockerfile misconfigurations, lockfile-only edits, and outbound-network risks.
Runtime watchdog Action — composite GitHub Action bundled with @vercel/ncc. Customers add it as the first step of their CI jobs. It calls GET /pipeline/decision?repo=...&sha=... against the LGTM API, and on halt: true calls core.setFailed so the job exits non-zero before any other step runs. Soft-fails on network errors so an LGTM outage never breaks customer CI.
Client — React 19 + Vite + Tailwind v4. Custom claymorphism design system; we built our own Button, Select, Modal, SearchInput primitives because native browser controls would've broken the visual coherence.
CLI — lgtm commands: login, whoami, repo connect/list/index, config set-key, review (with SSE streaming so you see each agent finish in real time).
- 6 specialist agents run in parallel (security / bugs / performance / readability / best-practices / documentation), each with structured output schemas
- A synthesizer agent combines them into one PR review with verdict (
approve/request_changes/comment), confidence score, severity counts, top actions, and inline file/line comments - Auto-generated changelog entry per PR
- Tree-sitter indexing across 12 languages (C, C++, C#, Go, Java, JavaScript, TypeScript, Kotlin, PHP, Python, Ruby, Rust)
- BYO API key — OpenAI, Gemini, or Anthropic. Per-user, per-repo provider/model overrides
- Real-time agent progress streamed to the dashboard and the CLI
The 16 detectors:
| Rule | Severity | Default action |
|---|---|---|
secrets.hardcoded |
critical | block |
workflow.privileged-container |
critical | block |
workflow.untrusted-input-shell-injection |
critical | block |
workflow.pull-request-target-with-head-checkout |
critical | block |
workflow.self-hosted-runner-on-public-repo |
high | block |
workflow.unpinned-action-checkout |
high | warn |
workflow.unpinned-third-party-action |
high | warn |
workflow.permissions-write-all |
high | warn |
workflow.missing-job-permissions |
medium | warn |
workflow.trigger-weakening |
medium | warn |
workflow.external-reusable-workflow |
medium | warn |
dockerfile.privileged-flag |
high | warn |
dockerfile.user-root-final |
high | warn |
dockerfile.add-from-url |
medium | warn |
deps.lockfile-hash-mismatch |
high | warn |
network.unallowlisted-outbound |
medium | warn |
Each rule's action is tunable per-repo (block / warn / off). Allowlists for trusted action sources, internal mirror domains, and runner labels suppress findings on patterns the customer has explicitly approved.
Three enforcement gates:
- Inline PR review — when a PR touches CI/CD config, ci-security findings appear in the same review as the regular code-review comments
- Merge block via Check Run — block-action findings post a failed GitHub Check Run; branch protection respects it
- Runtime halt via Action — the
lgtm-action/watchdog runs as the first step of customer CI jobs and exits non-zero on block findings, before checkout, before tests, before deploys
Everything is recorded in an immutable SecurityAuditLog. The audit-log fields are immutable at the schema level (Mongoose pre("save") and pre("updateOne") hooks reject mutations); resolution fields (resolvedAt, resolution, resolvedBy) stay mutable so users can mark fixed / muted / false-positive.
Per-rule false-positive rates surface in the policy editor — surfaced after the user has resolved at least 5 findings on that rule, so we don't show a misleading "0%" before there's data.
You'll need: Node 20+, Docker (for Redis), MongoDB Atlas (or any Mongo URI), Cloudflare Tunnel or ngrok (so GitHub webhooks can reach you).
# 1. Redis in Docker
docker run -d --restart unless-stopped --name lgtm-redis -p 6380:6379 redis:7-alpine
# 2. Server
cd server
cp .env.example .env # fill in MONGODB_URI, JWT_SECRET, GITHUB_APP_*, REDIS_URL, OPENAI/GEMINI key
npm install --legacy-peer-deps
npm run dev # starts API + workers on :3000
# 3. Client
cd ../client
npm install
npm run dev # vite on :5173
# 4. Cloudflare tunnel for GitHub webhooks
cloudflared tunnel --url http://localhost:3000
# → copy the https://*.trycloudflare.com URL into your GitHub App's Webhook URL
# → also into server/.env as WEBHOOK_BASE_URL
# 5. CLI (optional)
cd ../lgtm-cli
npm install
npm run build
LGTM_API_URL=http://localhost:3000 node bin/lgtm.js loginFor a real demo: install the tarin-lgtm GitHub App on a test repo, log into the dashboard, click Repos → Connect, then Security → Enroll that repo. The enrollment fires an immediate backfill scan against the default branch.
cd server
npm run test # 123 vitest tests across 13 filesTest breakdown:
security.rules.test.ts— 27 unit tests, one good + one bad case per ruleci-security.agent.test.ts— agent wrapper smoke testssecurity.api.test.ts— enrollment + audit log + filter APIsecurity.worker.test.ts— monitor worker e2e with mocked GitHubsecurity.worker.alerts.test.ts— alert wiring (in-app + email + dedup)security.ruleStats.test.ts— per-rule false-positive aggregationpipeline.api.test.ts+apiToken.api.test.ts— runtime halt endpoint + token authemail.service.test.ts— nodemailer transport, escape XSS, soft-failhealth.api.test.ts+healthScore.*.test.ts— repo health score signals
Tests use mongodb-memory-server so they run without external Mongo. GitHub fetches and email sends are vi.mock'd.
| Layer | Choice |
|---|---|
| Server runtime | Node 20, TypeScript, Express, ts-node-dev for HMR |
| Persistence | MongoDB (Mongoose), Redis (ioredis + BullMQ) |
| AI providers | OpenAI, Google Gemini, Anthropic — pluggable per user |
| Code parsing | tree-sitter — 12 language grammars + per-language tag queries |
| nodemailer (SMTP) | |
| Errors | Sentry (server + client) |
| Billing | Dodo Payments (Pro tier) |
| Frontend | React 19, Vite, Tailwind v4, custom claymorphism |
| Tests | Vitest 3, mongodb-memory-server, supertest |
| GitHub Action | Composite action + bundled JS (@vercel/ncc), Node 20 runtime |
A push to a connected repo's default branch fires a push webhook, which enqueues a context-index job (tree-sitter parses the diff + updates the call graph) and, if the push touched CI files and the repo is enrolled in Security, a security scan job. A PR open/sync fires a pull_request webhook, which enqueues a review job that runs all six specialist agents plus the ci-security agent in parallel, runs the synthesizer, posts the GitHub PR review, and writes findings to the immutable audit log. Both review and monitor scans cache a halt decision in Redis at pipeline:decision:{repoId}:{sha} with 24h TTL — that's the cache the runtime watchdog Action reads to decide whether to halt CI. Socket.IO pushes scan progress to the dashboard so users see banners and posture refresh in real time without polling.
UNLICENSED for now. Public source for hackathon visibility; we'll pick a real license before any production deploy.