OpenCodeReview Manager is a local-first, single-process application: one Python process serves the REST API, the SSE event streams, the MCP endpoint, the built React frontend, and runs the queue + webhook workers in-process. SQLite (WAL) is the only datastore.
┌────────────────────────────────────────────────────────────────────┐
│ Browser (React SPA, served from FastAPI static mount) │
│ REST /api/v1 · SSE /api/v1/jobs/{id}/events │
└──────────────┬─────────────────────────────────────────────────────┘
│
┌──────────────▼─────────────────────────────────────────────────────┐
│ FastAPI app (app/main.py) │
│ api/v1/ ── thin routes │
│ mcp/ ── MCP Streamable HTTP at /mcp (thin wrappers) │
│ services/ ── business logic (shared by REST, MCP, workers) │
│ queue/ ── QueueService (state machine) + QueueWorker + Runner │
│ webhooks/ ── WebhookService + delivery worker │
│ git/ · ocr/ ── GitService · OCRAdapter (the only integration pts) │
│ db/ ── SQLAlchemy 2 async models + Alembic migrations (SQLite WAL)│
└──────────────┬─────────────────────────────────────────────────────┘
│ argv arrays only (never a shell)
┌──────────────▼─────────────────────────────────────────────────────┐
│ git (worktrees, refs) · ocr CLI (reviews, sessions) · OS keyring │
└────────────────────────────────────────────────────────────────────┘
| Module | Responsibility |
|---|---|
core/config.py |
Settings (pydantic-settings, OCR_CC_* env prefix), data-dir layout, defaults |
core/secrets.py |
SecretStore abstraction: keyring (WinCred/Keychain/SecretService), env: references, in-memory for tests. The DB only ever stores references |
core/security.py |
Path normalization/allowlist, ref validation, additional-args parsing, env redaction, CSRF token |
core/logging.py |
structlog JSON logging + redact_text credential scrubber |
db/ |
SQLAlchemy 2 async models (SPEC §4 entities), session factory, Alembic runner |
git/service.py |
GitService: repo validation, folder scan (depth-limited, symlink-safe), for-each-ref branch cache, fetch/prune, worktree add/remove/prune, ref→SHA resolution. argv arrays only, --end-of-options for refs |
ocr/adapter.py |
OCRAdapter: binary detection, version + capability probe from --help, command generation, per-job HOME/config isolation, env construction (OCR_LLM_*), process start/cancel (process-tree kill), JSON result parse, JSONL session tail, ocr llm test, preview |
queue/ |
QueueService (durable queue + state machine, SPEC §13), QueueWorker (polling loop, concurrency limits via DB-level capacity check, workspace locks, stale-job reaper — scans every 60s for stuck running/preparing jobs), runner (job lifecycle: worktree → OCR process → parse → findings, with exception safety net + prepare timeout), recovery (interrupted jobs + orphan worktrees at startup), bus (in-process SSE fan-out) |
services/ |
Business logic shared by REST, MCP, and workers: folders, projects, providers (incl. model discovery + connection test), profiles, jobs (incl. exports), findings, settings, diagnostics |
webhooks/ |
WebhookService: HMAC-SHA256 signing, SSRF guards, retry/backoff, delivery log, replay; delivery worker polls next_attempt_at |
mcp/ |
MCP Streamable HTTP server: tools/resources/prompts (SPEC §17), thin wrappers over services/ |
api/v1/ |
All REST routes (SPEC §19) + SSE endpoint with Last-Event-ID resume |
main.py |
Lifespan: migrations → engine → workers → recovery; MCP mount; SPA static serving |
The frontend (frontend/src/) is a React 18 + strict-TS SPA: api/ (typed
fetch client + TanStack Query hooks), types/ (API contracts), layouts/
(app shell), pages/ (all SPEC §33 screens), features/ (complex widgets
like FindingCard), components/ui/ (custom design system), styles/
(design tokens, light/dark via data-theme).
- Submit (REST
POST /jobsor MCPocr_submit_review) →JobServiceresolves the profile/provider/model, resolves refs to SHAs viaGitService, builds the OCR command viaOCRAdapter, and persists an immutable configuration snapshot + generated command on the job row. - Queue —
QueueServiceenqueues with priority + manual position; per-project/provider/global concurrency and per-project workspace locks gate dispatch. Invalid state transitions are rejected. - Run — the runner creates a detached worktree (range/commit jobs) or
takes the workspace lock (workspace jobs), writes a per-job HOME with an
OCR
config.json(no secrets), previews the review inventory, spawnsocr review --format json --audience humanas an argv array, and persists both OCR progress output and normalized session JSONL events. - Events — persisted to
job_eventsand fanned out on the in-process bus; the SSE endpoint replays persisted events fromLast-Event-IDthen streams live ones, closing on terminal states. - Result — stdout JSON is parsed into
Findingrows (severity/category passed through, never invented;thinkingstored but never returned by default), warnings derivecompleted_with_warnings, and terminal-state webhooks are dispatched. - Consume — findings/warnings/logs/session/exports via REST or MCP resources; exports and previews pass through the same redactor as logs.
- Git — range/commit jobs run in detached worktrees under
<data_dir>/worktrees/<project>/<job>; the user's repo is never mutated (no destructive git commands anywhere). Workspace jobs run on the real path under a per-project exclusive lock so uncommitted changes are preserved and never raced. Orphan worktrees are pruned at startup. - OCR config — every job gets its own HOME at
<data_dir>/jobs/<job>/homewith a job-specificconfig.json; jobs never share or mutate a global OCR config, so concurrent jobs can use different providers/models without races. LLM settings flow viaOCR_LLM_*environment variables, not config edits. - Processes — OCR runs as a subprocess tree; cancellation kills the whole tree (POSIX process groups / Windows job objects), with a grace period before SIGKILL.
- Credentials — stored only in the OS keyring (or as
env:references); the DB holds opaque references. Redaction is applied to logs, command previews, metadata, exports, webhook payloads, and the diagnostics bundle.
- Binding — defaults to
127.0.0.1; CORS origins are restricted to the app origin. - CSRF — double-submit cookie (
ocrcc_csrf+X-OCR-CSRFheader) on every state-changing request. - Paths — project/folder paths are normalized, symlink-resolved, and can
be locked to allowlisted roots (
OCR_CC_PATH_RESTRICTIONS_ENABLED). - Refs/args — git refs validated and passed after
--end-of-options; expert additional-arguments are parsed into an argv array, shell metacharacters are rejected, and control-plane-owned flags are refused. - Webhooks — HTTPS required by default, private-network targets blocked unless explicitly allowed, HMAC-SHA256 signatures, bounded response reads.
- Reasoning — raw model reasoning (
thinking) is stored for opt-in inspection but excluded from every API response and export unless explicitly requested (SPEC §38.15). - Errors — a consistent
{error: {code, message, detail, next_action}}envelope; internal details are redacted before leaving the process.