⚠️ Not intended for production use - This is purely a learning project built to explore webhook integration, external API consumption, background task processing, database persistence, authentication, containerization, Github Actions CI/CD, E2E testing, async Python, and LLM inference APIs.
Automated code review powered by LLMs, delivered as a GitHub PR comment.
When a pull request is opened, Prism receives a webhook from GitHub, fetches the diff, runs it through a large language model, and posts structured feedback as a comment — no manual action required.
GitHub PR opened
│
▼
POST /webhook ──► Verify HMAC-SHA256 signature
│
▼
Background task queued ──► Return 200 immediately
│
▼
Fetch PR diff (GitHub API)
│
▼
Send diff to LLM (OpenRouter)
│
▼
Store structured feedback (PostgreSQL/Supabase)
│
▼
Post formatted comment to PR
Feedback is categorized across four dimensions — bugs, security, performance, and style — grouped by file and formatted as markdown tables in the PR comment.
| Layer | Choice | Why |
|---|---|---|
| Framework | FastAPI | Async-native; handles concurrent outbound HTTP calls without blocking |
| Database | PostgreSQL (Supabase) + SQLAlchemy + asyncpg | Async ORM stack; JSONB for flexible feedback storage |
| Validation | Pydantic / pydantic-settings | Type-safe config; fails loudly on missing env vars |
| LLM Proxy | OpenRouter | Single API key, swap models without code changes |
| Containerization | Docker | Reproducible builds; non-root user; layer-cached dependency installs |
| CI | GitHub Actions + Ruff + Pytest | Lint and test on every push and PR |
| Hosting | Render | Pulls Dockerfile, manages TLS, provides public URL |
Webhook security via HMAC-SHA256 — Every incoming request is verified against GitHub's signature header using hmac.compare_digest (constant-time comparison to prevent timing attacks). Signature is verified against the raw request bytes before JSON parsing.
Idempotency via gh_delivery_id — GitHub retries failed deliveries. A UNIQUE constraint on the delivery ID column in PostgreSQL ensures each event is processed exactly once, even if two requests race simultaneously.
Immediate response, background processing — The /webhook endpoint returns 200 OK before any analysis work begins. GitHub has delivery timeouts; doing the work inline risks missed deliveries. FastAPI's BackgroundTasks handles the processing after the response is sent.
Structured error handling — Three distinct failure modes are handled separately: analysis failure (stores status=error), database failure (best-effort error record), and comment posting failure (preserves status=completed, records the comment error). Analysis data is never lost due to a comment delivery failure.
expire_on_commit=False — Prevents SQLAlchemy from attempting implicit lazy-loads after commit in an async context, where there is no implicit async context to run them in.
Authentication for retrieval endpoints uses an X-API-Key header. The webhook endpoint uses HMAC verification instead — GitHub controls the headers it sends.
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/webhook |
HMAC-SHA256 | Receive GitHub webhook events |
GET |
/analyses |
API Key | List analyses with optional filtering and pagination |
GET |
/analyses/{id} |
API Key | Retrieve a single analysis result |
Query parameters for GET /analyses:
repo_full_name— filter by repository (e.g.owner/repo)pr_number— filter by PR numberlimit/offset— pagination (default: 20/0, ordered bycreated_atdesc)
Prerequisites: Docker, or Python 3.12+ with a PostgreSQL instance.
git clone https://github.com/your-username/prism-backend
cd prism-backend
cp .env.example .env
# Fill in DATABASE_URL, WEBHOOK_SECRET, PRISM_API_KEY, OPENROUTER_API_KEYWith Docker:
docker build -t prism .
docker run --env-file .env -p 8000:8000 prismWithout Docker:
pip install -r requirements.txt
uvicorn app.main:app --reloadTo receive webhooks locally, use ngrok or smee.io to tunnel to localhost:8000/webhook.
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string (postgresql+asyncpg://...) |
WEBHOOK_SECRET |
Secret set in GitHub webhook configuration |
PRISM_API_KEY |
Key required to call the retrieval endpoints |
OPENROUTER_API_KEY |
API key for OpenRouter |
GITHUB_TOKEN |
Personal access token for fetching diffs and posting comments |
- No task queue — Background tasks run in-process. If the server restarts mid-analysis, the task is lost. A production system would use ARQ or Celery with a Redis broker.
- No rate limiting — A high volume of webhook deliveries would each trigger an LLM call.
slowapior a reverse proxy rule would address this. - Single-tenant — One GitHub account, one API key. No multi-user or multi-repo registration.
GitHub Actions runs on every push to main and every PR targeting main:
- Ruff — linting and formatting checks
- Pytest — test suite with required environment variables injected from repository secrets
Both jobs must pass for a PR to be mergeable.