AI-powered code review platform that automatically reviews pull requests, detects anti-patterns, security issues, and code quality problems using OpenCode.
OpenDiff is a SaaS platform that integrates with GitHub to provide:
- Automated Code Reviews - AI analyzes pull requests for anti-patterns, security vulnerabilities, performance issues, and bugs
- Intelligent Triage - Automatically fixes detected issues and commits changes back to the PR
- Team Collaboration - Organization-based access with role management and seat-based subscriptions
- Custom Skills - Define custom review instructions that the AI agent follows when reviewing your code
- Custom Review Rules - Configure per-repository review guidelines and coding standards
| Package | Description | Tech Stack |
|---|---|---|
bff |
Backend API with auth, billing, and data management | Hono, Prisma, PostgreSQL, Cloudflare R2 |
app |
Main console dashboard and management UI | React 19, Vite, Tailwind CSS v4 |
site |
Marketing website and landing pages | React 19, Vite, Tailwind CSS v4 |
review-agent |
GitHub webhook handler and AI code reviewer | Hono, OpenCode SDK, Octokit |
prompts |
Shared AI prompt templates with variable substitution | Bun |
components |
Shared UI component library | Radix UI, shadcn, CVA |
shared |
Shared business logic, hooks, and services | React Query, React Router |
github |
GitHub App auth and API utilities | Octokit |
vscode-extension |
VS Code extension for local code review | VS Code API |
assets |
Shared static assets (fonts, icons) | - |
Content sources used by the website:
- Blog articles:
packages/articles/README.md - Changelog entries:
packages/changelog/README.md
- Bun v1.1.29+
- PostgreSQL 15+
- GitHub App credentials
- Anthropic API Key
# Clone the repository
git clone https://github.com/antiptrn/opendiff.git
cd opendiff
# Install dependencies
bun installThe app and website use Helvetica Now Display for headings and Helvetica Now Text for everything else. These are licensed fonts from Monotype and are not checked into the repository.
If you have a license, put these files in packages/assets/public/fonts/:
HelveticaNowText-Regular.woff2HelveticaNowText-Medium.woff2HelveticaNowText-Bold.woff2HelveticaNowDisplay-Regular.woff2HelveticaNowDisplay-Medium.woff2HelveticaNowDisplay-Bold.woff2
Without them, the UI falls back to Helvetica Neue, Helvetica or Arial.
For local development, each package requires its own environment configuration. Copy the example files and configure:
# Server
cp apps/bff/.env.example apps/bff/.env
# App (Console)
cp apps/app/.env.example apps/app/.env
# Website
cp apps/site/.env.example apps/site/.env
# Review Agent
cp apps/review-agent/.env.example apps/review-agent/.env# Database
DATABASE_URL=postgresql://user:password@localhost:5432/opendiff
# OAuth Providers
GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx
MICROSOFT_CLIENT_ID=xxx
MICROSOFT_CLIENT_SECRET=xxx
# Email (Resend) — sends email sign-in codes/links and org invites.
# Without RESEND_API_KEY, codes and links are printed to the server log instead.
RESEND_API_KEY=re_xxx
FROM_EMAIL="Contrast <noreply@opendiff.dev>"
# GitHub App credentials (same values as review agent — used for fetching PR metadata)
GITHUB_APP_ID=xxx
GITHUB_PRIVATE_KEY_PATH=/path/to/private-key.pem
# URLs
FRONTEND_URL=http://localhost:5174
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:5174
PORT=3001
# Redis (recommended for shared rate limiting)
REDIS_URL=redis://localhost:6379
# Optional: in production, keep false unless your proxy headers are trusted
RATE_LIMIT_TRUST_PROXY_HEADERS=false
# Optional: cooldown after Redis failures before retrying Redis (milliseconds)
REDIS_RATE_LIMIT_COOLDOWN_MS=10000
# Payment Provider ("polar" or "stripe")
PAYMENT_PROVIDER=polar
# Polar (when PAYMENT_PROVIDER=polar)
POLAR_ACCESS_TOKEN=polar_oat_xxx
POLAR_WEBHOOK_SECRET=polar_whs_xxx
POLAR_SERVER=sandbox
POLAR_ORGANIZATION_ID=xxx
POLAR_PRO_MONTHLY_PRODUCT_ID=xxx
POLAR_PRO_YEARLY_PRODUCT_ID=xxx
POLAR_ULTRA_MONTHLY_PRODUCT_ID=xxx
POLAR_ULTRA_YEARLY_PRODUCT_ID=xxx
POLAR_SELF_SUFFICIENT_MONTHLY_PRODUCT_ID=xxx
POLAR_SELF_SUFFICIENT_YEARLY_PRODUCT_ID=xxx
# Stripe (when PAYMENT_PROVIDER=stripe)
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_PRO_MONTHLY_PRICE_ID=price_xxx
STRIPE_PRO_YEARLY_PRICE_ID=price_xxx
STRIPE_ULTRA_MONTHLY_PRICE_ID=price_xxx
STRIPE_ULTRA_YEARLY_PRICE_ID=price_xxx
STRIPE_SELF_SUFFICIENT_MONTHLY_PRICE_ID=price_xxx
STRIPE_SELF_SUFFICIENT_YEARLY_PRICE_ID=price_xxx
# Review Agent Integration
REVIEW_AGENT_API_KEY=xxx
REVIEW_AGENT_WEBHOOK_URL=http://localhost:3000
# Cloudflare R2 Storage
R2_ACCOUNT_ID=xxx
R2_ACCESS_KEY_ID=xxx
R2_SECRET_ACCESS_KEY=xxx
R2_BUCKET_NAME=opendiff
R2_PUBLIC_URL=https://cdn.example.comRate limiting notes:
- Redis-backed rate limits are enabled when
REDIS_URLis configured. - In production, requests should come through Cloudflare (or another trusted proxy) so
CF-Connecting-IPis available. - If you do not fully trust proxy headers, keep
RATE_LIMIT_TRUST_PROXY_HEADERS=false.
VITE_WEBSITE_URL=http://localhost:5173
# Dev server host allowlist (comma-separated)
VITE_ALLOWED_HOSTS=localhost,127.0.0.1,app.opendiff.dev,.opendiff.dev
# Optional: shared cookie domain for cross-subdomain theme sync
# Example: opendiff.dev
VITE_THEME_COOKIE_DOMAIN=
# Product/Price IDs (from Polar or Stripe dashboard)
VITE_PRO_MONTHLY_PRODUCT_ID=xxx
VITE_PRO_YEARLY_PRODUCT_ID=xxx
VITE_ULTRA_MONTHLY_PRODUCT_ID=xxx
VITE_ULTRA_YEARLY_PRODUCT_ID=xxx
VITE_SELF_SUFFICIENT_MONTHLY_PRODUCT_ID=xxx
VITE_SELF_SUFFICIENT_YEARLY_PRODUCT_ID=xxxVITE_API_URL=http://localhost:3001
VITE_APP_URL=http://localhost:5174
# Dev server host allowlist (comma-separated)
VITE_ALLOWED_HOSTS=localhost,127.0.0.1,opendiff.dev,.opendiff.dev
# Product/Price IDs (from Polar or Stripe dashboard)
VITE_SELF_SUFFICIENT_MONTHLY_PRICE_ID=xxx
VITE_SELF_SUFFICIENT_YEARLY_PRICE_ID=xxx
VITE_PRO_MONTHLY_PRICE_ID=xxx
VITE_PRO_YEARLY_PRICE_ID=xxx
VITE_ULTRA_MONTHLY_PRICE_ID=xxx
VITE_ULTRA_YEARLY_PRICE_ID=xxxIf you use the included deploy/preview workflows, configure secrets on GitHub using environments:
-
Repo -> Settings -> Environments -> production -> SecretsDEPLOY_DIR(path to the trusted clone on your runner)GH_APP_PRIVATE_KEY_PATH(absolute path to your GitHub App PEM file on the runner)SLACK_WEBHOOK_URL(optional)
-
Repo -> Settings -> Environments -> preview -> SecretsDEPLOY_DIRPREVIEWS_BASE_DIRINGRESS_DIRPREVIEW_DB_PASSWORDPREVIEW_GITHUB_CLIENT_IDPREVIEW_GITHUB_CLIENT_SECRETSLACK_WEBHOOK_URL(optional)
Preview behavior is controlled by repo-level Actions variables (non-secret):
Repo -> Settings -> Secrets and variables -> Actions -> VariablesPREVIEW_DOMAIN,PREVIEW_*_SUBDOMAIN_PREFIX,PREVIEW_DB_*,PREVIEW_OAUTH_CALLBACK_BASE_URL,PREVIEW_BOT_USERNAME
# GitHub Webhook Secret
GITHUB_WEBHOOK_SECRET=xxx
# GitHub App Authentication (recommended)
GITHUB_APP_ID=xxx
GITHUB_PRIVATE_KEY_PATH=/path/to/private-key.pem
# Or: Personal Access Token (alternative)
# GITHUB_TOKEN=ghp_xxx
# AI provider credentials used by OpenCode
ANTHROPIC_API_KEY=sk-ant-xxx
# Bot configuration
BOT_USERNAME=opendiff-bot
BOT_TEAMS=
# Server Integration
SETTINGS_API_URL=http://localhost:3001
REVIEW_AGENT_API_KEY=xxx
# Review queue controls
REVIEW_QUEUE_CONCURRENCY=1
REVIEW_QUEUE_MAX_SIZE=100
REVIEW_QUEUE_MAX_ATTEMPTS=2
REVIEW_QUEUE_RETRY_DELAY_MS=15000
PORT=3000cd apps/bff
# Generate Prisma client
bunx prisma generate
# Run migrations
bunx prisma migrate dev# Start all services concurrently
bun run start:dev
# Or start individually (in separate terminals)
bun run dev:server # Backend API on http://localhost:3001
bun run dev:app # Console dashboard on http://localhost:5174
bun run dev:site # Marketing site on http://localhost:5173
bun run dev:agent # Review agent on http://localhost:3000From the monorepo root:
# Development
bun run dev:server # Start server in watch mode
bun run dev:app # Start console app dev server
bun run dev:site # Start site dev server
bun run dev:agent # Start review agent in watch mode
bun run start:dev # Start all services concurrently
# Building
bun run build # Build site for production
bun run build:app # Build console app for production
bun run build:agent # Build review agent for production
bun run start:prod # Build all and start in production mode
# Code Quality
bun run lint # Lint all packages
bun run lint:fix # Auto-fix linting issues
bun run format # Format all code
bun run check # Run all checks
# Testing
bun run test # Run tests for all packages
# Process Management
bun run start:dev:nohup # Start all services in background (dev)
bun run start:prod:nohup # Start all services in background (prod)
bun run stop # Stop all background servicesThe project uses Biome for linting and formatting:
# Check for issues
bun run lint
# Auto-fix issues
bun run lint:fix
# Format code
bun run format# Run all tests
bun run test
# Run tests for specific package
cd apps/app && bun run test
cd apps/site && bun run test
cd packages/components && bun run test
cd packages/shared && bun run test
The AI reviewer analyzes code for:
| Category | Examples |
|---|---|
| Security | SQL injection, XSS, hardcoded secrets, insecure crypto |
| Anti-patterns | God objects, tight coupling, magic numbers, copy-paste code |
| Performance | N+1 queries, memory leaks, blocking operations |
| Style | Naming conventions, error handling, missing types |
| Bug Risks | Off-by-one errors, null safety, race conditions |
Skills are user-defined instructions that the AI agent follows during code reviews. Each skill includes:
- Name - Unique identifier (e.g.,
security-review,api-standards) - Description - Short description for skill routing
- Content - Detailed instructions for the agent
Skills are automatically applied based on the repository configuration and team membership.
- Critical - Must be fixed before merging
- Warning - Should be addressed
- Suggestion - Optional improvements
| Tier | Token Quota | Features |
|---|---|---|
| Self-sufficient | Unlimited (BYOK) | Bring Your Own Key — use your selected model provider API key |
| Pro | 2.5M tokens/mo | Automated PR reviews, auto-fix, custom rules, skills |
| Ultra | 8M tokens/mo | Everything in Pro with higher token quota |
cd apps/app
bun run build
# Deploy dist/ to your static hostcd apps/site
bun run build
# Deploy dist/ to your static hostDeploy as a standard Node.js/Bun application with PostgreSQL database.
For Docker-based production deploys, set this environment variable on the deploy runner:
export GITHUB_PRIVATE_KEY_PATH=/absolute/path/to/private-key.pemdocker-compose.prod.yml mounts this path into both bff and agent containers.
The site's "Pull requests merged with Contrast" counter reads GET /api/public/stats. New merges are recorded automatically from the review agent's pull_request webhooks. To count PRs merged before this was deployed, run once on the server after the migration:
docker compose -f docker-compose.prod.yml run --rm bff bun apps/bff/scripts/backfill-merged-prs.tscd apps/review-agent
# Build image
docker build -t review-agent .
# Run with docker-compose
docker-compose up -dAll authenticated endpoints require:
Authorization: Bearer <token>headerX-Organization-Id: <org-id>header (for org-scoped requests)
GET /api/repos- List available repositoriesGET /api/org/repos- List organization repositoriesPUT /api/settings/:owner/:repo- Update repository settings
GET /api/reviews- List reviews for organizationGET /api/stats- Get review statisticsPOST /api/reviews- Record a completed review (internal)
GET /api/skills- List user's skills (paginated, searchable)POST /api/skills- Create a new skillPUT /api/skills/:id- Update a skillDELETE /api/skills/:id- Delete a skill
GET /api/subscription/status- Check subscription statusPOST /api/subscription/create- Create new subscriptionGET /api/billing- Get billing information
GET /api/organization- Get organization detailsGET /api/organization/members- List organization membersPOST /api/organization/invites- Create inviteDELETE /api/organization/invites/:id- Revoke invite
opendiff/
├── packages/
│ ├── app/ # Console Dashboard
│ │ └── src/
│ │ ├── features/ # Feature modules (admin, billing, dashboard, etc.)
│ │ ├── components/ # App-specific components
│ │ └── main.tsx # App entry point
│ │
│ ├── assets/ # Shared Static Assets
│ │ └── public/
│ │ ├── typefaces/ # Font files (Helvetica Now)
│ │ ├── icons/ # OAuth provider icons
│ │ └── icon.svg # Favicon
│ │
│ ├── bff/ # Backend API
│ │ ├── src/
│ │ │ ├── index.ts # Main server entry
│ │ │ ├── routes/ # API route handlers
│ │ │ ├── auth.ts # OAuth authentication
│ │ │ ├── db.ts # Prisma client
│ │ │ └── payments/ # Polar/Stripe integration
│ │ └── prisma/
│ │ └── schema.prisma # Database schema
│ │
│ ├── components/ # UI Component Library
│ │ └── src/
│ │ ├── components/ui/ # Primitives (Button, Dialog, etc.)
│ │ ├── hooks/ # Shared React hooks
│ │ ├── utils/ # Utility functions
│ │ └── constants/ # Shared constants
│ │
│ ├── prompts/ # Shared AI Prompt Templates
│ │ ├── src/
│ │ │ └── index.ts # loadPrompt() with {var} substitution
│ │ └── templates/
│ │ ├── generate-summary.md
│ │ ├── review.md
│ │ ├── respond-to-comment.md
│ │ └── fix-issue.md
│ │
│ ├── review-agent/ # AI Code Reviewer
│ │ └── src/
│ │ ├── agent/ # OpenCode integration
│ │ │ ├── reviewer.ts # Code review agent
│ │ │ └── triage.ts # Auto-fix agent
│ │ ├── utils/
│ │ │ ├── git.ts # Git operations
│ │ │ └── skill-hydrator.ts # Skill materialization
│ │ └── webhook/ # GitHub webhook handlers
│ │
│ ├── shared/ # Shared Business Logic
│ │ └── src/
│ │ ├── auth/ # Authentication logic
│ │ ├── billing/ # Billing logic
│ │ ├── organizations/ # Organization management
│ │ ├── services/ # API services, query keys
│ │ └── navigation/ # Navigation utilities
│ │
│ ├── github/ # GitHub App Auth & API Utilities
│ │ └── src/
│ │ └── index.ts # JWT auth, installation tokens, types
│ │
│ ├── vscode-extension/ # VS Code Extension
│ │ └── src/
│ │ └── extension.ts
│ │
│ └── site/ # Marketing Website
│ └── src/
│ ├── features/ # Feature modules
│ └── components/ # Website components
│
├── scripts/ # Operations scripts
│ ├── start.sh # Start all services (dev or prod mode)
│ ├── stop.sh # Stop all running services by port
│ └── deploy-webhook.ts # Webhook listener for auto-deploy
├── biome.json # Linting configuration
└── package.json # Workspace configuration
- User - Multi-OAuth user accounts (GitHub, Google, Microsoft)
- Organization - Teams with subscription management
- OrganizationMember - User membership and seat allocation
- OrganizationInvite - Pending team invites
- RepositorySettings - Per-repo review configuration
- Review - Review history tracking
- ReviewComment - Individual review comments
- ReviewFix - Automated fix tracking
- Skill - User-defined review instructions
- SkillResource - Additional resources attached to skills
- Feedback - User feedback submissions
- Notification - In-app notifications
- AuditLog - Security audit trail
See apps/bff/prisma/schema.prisma for the complete schema.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run linting and tests (
bun run check && bun run test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the AGPL-3.0 License - see the LICENSE.md file for details.