Actively developed, pre-1.0. This is a personal project built in the open, published so the work can be read and run. It is not a supported product.
Known gaps and caveats, stated up front:
- Exercised locally against the bundled synthetic fixtures; not run against a large real-world repository at scale.
- Findings are heuristic. Absence of a finding is not evidence of safety, and test presence is not measured coverage.
- The model stage requires Ollama; without it that stage degrades to a warning and deterministic results still render.
Issues and pull requests are welcome. If something breaks on first run, that is useful information — please open an issue rather than assuming it works for everyone else.
Understand a repository without executing it.
RepoLens is a local-first repository analysis platform for developers joining unfamiliar projects, reviewing portfolios, learning architecture, or auditing generated code. Import a public HTTPS Git repository or upload a ZIP and receive a deterministic, evidence-backed report spanning structure, languages, dependencies, entry points, architecture, tests, documentation, maintainability, security patterns, recommendations, and interview preparation.
RepoLens is an assistive static-analysis tool. Findings can be wrong, absence of findings does not prove security or correctness, test presence is not measured coverage, and imported commands are never assumed to work.
All captures below are real, taken from a local run against the bundled
synthetic fixture sample-python-api. No mocked interfaces.
Home. The three guarantees — static only, safe Git import, guarded ZIPs — are stated before anything is imported, not buried in documentation.
Overview. Note the labelling discipline: repository signals are "observed presence, not executed or measured coverage," architecture is marked "Inference," and the sidebar carries a persistent "No code executed" badge.
Security review — the finding is severity LOW, MEDIUM CONFIDENCE, the
value is redacted, and the evidence is tagged TEST FIXTURE CANDIDATE with a
stated limitation: "Test fixtures and examples can resemble credentials; no
provider received this value."
Worth noting: GitHub's own push protection flagged this repository's detector fixtures as live Slack and GitLab credentials and blocked the first push. RepoLens, pointed at comparable material, classifies it as a probable test fixture and redacts it rather than asserting a breach. That contrast is the whole design thesis — confidence labels and stated limitations beat a binary verdict.
Architecture. Every claim is tiered — directly observed, inferred, or model-assisted — and LLM output is fenced inside an "UNVERIFIED MODEL-GENERATED DRAFT" block that tells you to check it against the deterministic evidence below.
Study guide. "Explain the project without inventing its history" — it generates talking points and likely questions while explicitly declining to imply authorship.
- Safe shallow Git import through a DNS-pinning HTTPS proxy with hooks, prompts, redirects, credential helpers, LFS smudging, submodules, and non-HTTPS schemes disabled
- Guarded ZIP extraction with traversal, absolute-path, symlink, expanded-size, count, and compression-ratio checks
- File inventory, line counts, language distribution, classification, symbols, imports, dependencies, and likely entry points
- Evidence-backed maintainability, documentation, testing, security-pattern, and conservative secret findings, with comment and string awareness for Python and JavaScript/TypeScript and measured per-rule precision
- Git history signals for URL imports — churn, hotspots, knowledge concentration, and temporal coupling — with author identity hashed and never reported
- Ingestion of committed coverage artefacts (
coverage.xml,lcov.info,coverage-final.json) from the repository root or acoverage/directory, reported as the project's own prior measurement, never as RepoLens's - Redacted source explorer with cited-line navigation and symbol outlines
- Deterministic architecture components plus optional, schema-validated Ollama drafts that are explicitly marked unverified
- Persistent queued jobs, cancellation, lifecycle events, searchable findings, Markdown/JSON export, history, rename, and cascading deletion
- Immutable versioned migrations, service-heartbeat readiness, hash-locked Python installs, and digest-pinned production images
- Responsive, keyboard-accessible dark/light developer interface
RepoLens never imports repository modules, installs repository packages, runs hooks, executes builds or tests, renders imported HTML, or invokes a shell with repository values.
flowchart LR
UI[React + TypeScript UI] --> API[FastAPI /api/v1]
API --> Queue[(SQLite job queue)]
Queue --> Worker[Supervising worker]
Worker --> Child[Resource-limited child]
Child --> Import[Bounded Git and ZIP importers]
Child -->|internal-only network| Egress[Job-authorized pinning proxy]
Egress --> Internet[Repository or configured model host]
Import --> Scratch[Disposable bounded scratch space]
Child --> Pipeline[Deterministic pipeline]
Scratch --> Store[(Dedicated repository storage)]
Pipeline --> Evidence[(SQLite evidence model)]
Evidence --> Reports[Markdown and JSON reports]
Evidence --> Redact[Secret redaction]
Redact -. optional .-> Provider[Ollama or compatible endpoint]
The FastAPI service owns untrusted-input boundaries, analysis, persistence, provider calls, and report generation. The Vite application renders typed API data and never recreates findings. See architecture, methodology, and security model.
| View | Status | What it must show |
|---|---|---|
docs/images/dashboard.png |
TODO | The analysis list with at least two completed analyses and one warning state |
docs/images/finding-evidence.png |
TODO | A single finding expanded, with its path:line locus and the cited source line |
docs/images/source-viewer.png |
TODO | The redacted source viewer with a [REDACTED] span visible |
docs/images/progress.gif |
TODO | ~15s: upload a fixture ZIP, stages advancing, report opening |
- Python 3.12 (pinned in
.python-version) - Node.js 22 (pinned in
.nvmrc; CI uses the same version) - Git 2.30+
- Docker Desktop (optional)
- Ollama (optional; RepoLens remains useful offline)
Run make doctor to check all of the above before installing.
cp .env.example .env
make install
make migrateIn terminal one:
make apiIn terminal two:
make workerIn terminal three:
make webOpen http://localhost:5173, choose New analysis, and upload a ZIP of one of the bundled synthetic repositories. API documentation is at http://localhost:8000/docs.
The fixtures under sample_repositories/ are original and intentionally contain broad exception handling, duplication, and credential-shaped test data. They are safe demonstration inputs, not production examples.
make demoThis command invokes RepoLens's analyzer on sample-python-api; it does not execute the sample.
Ollama is optional. Install it separately, pull the configured model, and keep it listening at http://localhost:11434. The default is qwen2.5-coder:7b. Set REPOLENS_MODEL_NAME to a model already available locally. Health reports ready only when both the endpoint and configured model are available. When unavailable or malformed, the model stage becomes a warning while deterministic results and exports remain intact.
For an OpenAI-compatible endpoint set:
REPOLENS_MODEL_PROVIDER=openai-compatible
REPOLENS_MODEL_BASE_URL=https://your-endpoint.example
REPOLENS_MODEL_NAME=your-model
REPOLENS_MODEL_API_KEY=your-keyThe Settings page visibly reports when requests can leave the machine. Potential secrets are redacted before synthesis. API keys never appear in settings responses or reports. Model output is schema-validated but not treated as factual evidence; the UI marks it as an unverified draft and keeps deterministic evidence primary.
docker compose up --buildOpen http://localhost:5173. The API applies frozen Alembic migrations before accepting traffic, while /api/v1/readiness remains unavailable until the worker and egress heartbeats are fresh. Compose mounts only the dedicated repolens-data volume. Every service runs non-root with a read-only root, dropped capabilities, a PID ceiling, and CPU/memory limits. The worker additionally receives a size-bounded noexec scratch tmpfs and only an internal Docker network. A separate proxy authorizes the current Git host or configured model endpoint, pins repository DNS, and is the worker's only egress route. Only a validated bounded tree is published to persistent storage. No arbitrary host source is mounted. Compose uses REPOLENS_DOCKER_MODEL_BASE_URL (default http://host.docker.internal:11434) for host Ollama and supplies the Linux host-gateway mapping; direct development continues to use REPOLENS_MODEL_BASE_URL.
| Task | Command |
|---|---|
| Check the toolchain | make doctor |
| Install | make install (runs doctor first) |
| Install pre-commit hooks | make hooks |
| Regenerate reviewed Python locks | make lock |
| API with reload | make api |
| Analysis worker | make worker |
| Web UI | make web |
| Migrate database | make migrate |
| Import the bundled fixtures | make seed |
| Backend + frontend tests | make test |
| End-to-end tests | make e2e |
| Lint (ruff check, ruff format --check, eslint) | make lint |
| Apply formatting | make format |
| Python type check | make typecheck |
| CI's lint, type, test, coverage and advisory jobs, in CI's order | make ci |
| Production web build | make build |
| Analyze synthetic sample | make demo |
| Require provider health | make provider-health |
| Reset local data | make clean-data (requires confirmation) |
make ci runs CI's backend and frontend jobs plus the dependency-advisory steps, in CI's order. It does not run the four jobs that need Docker, a browser download or network access a local run should not assume: the gitleaks secret scan, CycloneDX SBOM generation, the Playwright end-to-end job (make e2e), and the docker job that boots Compose and curls health, readiness and the SPA. A green make ci predicts those seven steps, not the whole pipeline.
Formatting and linting are enforced by pre-commit and again in CI. Run make hooks once after installing so a commit cannot introduce an unformatted file; pre-commit run --all-files normalises an existing tree.
See development and troubleshooting.
| Variable | Default | Purpose |
|---|---|---|
REPOLENS_DATA_DIR |
.repolens-data |
Dedicated import/report storage |
REPOLENS_DATABASE_URL |
SQLite under data directory | Metadata and evidence database |
REPOLENS_MAX_REPOSITORY_BYTES |
50 MiB | Expanded authored-file limit |
REPOLENS_MAX_FILE_COUNT |
20,000 | Imported entry/file limit |
REPOLENS_MAX_FILE_BYTES |
1 MiB | Per-file analysis/display limit |
REPOLENS_MAX_ARCHIVE_BYTES |
25 MiB | ZIP upload limit |
REPOLENS_MAX_CLONE_STORAGE_BYTES |
128 MiB | Active clone storage ceiling |
REPOLENS_CLONE_TIMEOUT_SECONDS |
120 | Git subprocess deadline |
REPOLENS_SERVICE_HEARTBEAT_SECONDS |
5 | Worker/egress heartbeat interval |
REPOLENS_SERVICE_STALE_SECONDS |
20 | Readiness heartbeat expiry |
REPOLENS_REQUIRED_SERVICES |
empty; Compose sets worker,egress |
Services required by readiness |
REPOLENS_JOB_WALL_TIMEOUT_SECONDS |
300 | Child-process wall deadline |
REPOLENS_JOB_MEMORY_BYTES |
1 GiB | Linux child virtual-memory ceiling |
REPOLENS_JOB_CPU_SECONDS |
240 | POSIX child CPU-time ceiling |
REPOLENS_WORKER_SCRATCH_DIR |
unset | Optional separately bounded worker filesystem; Compose uses /work |
REPOLENS_EGRESS_PROXY_URL |
unset | Optional trusted pinning proxy; Compose injects its isolated egress service |
REPOLENS_ALLOWED_CORS_ORIGINS |
http://localhost:5173 |
Exact local UI origins |
REPOLENS_BIND_HOST |
127.0.0.1 |
Compose-only. Host interface the API and web ports publish on |
REPOLENS_GIT_HISTORY_DEPTH |
200 | Commits fetched for churn/hotspot/coupling analysis; 1 disables it |
REPOLENS_MAX_PATH_DEPTH |
64 | Maximum archive/clone path nesting |
REPOLENS_MAX_PATH_LENGTH |
1024 | Maximum archive member name length |
REPOLENS_MAX_MATCHES_PER_RULE_PER_FILE |
10 | Per-rule cap before a +N more rollup |
REPOLENS_MAX_FINDINGS_PER_FILE |
200 | Per-file finding budget |
REPOLENS_MAX_FINDINGS_PER_ANALYSIS |
5000 | Per-analysis finding budget |
REPOLENS_REPORT_FINDINGS_LIMIT |
200 | Findings embedded in the detail response |
REPOLENS_MODEL_CONTEXT_TOKENS |
8192 | Declared context window; sent to Ollama as num_ctx |
REPOLENS_MODEL_PROVIDER |
ollama |
ollama or openai-compatible |
REPOLENS_MODEL_BASE_URL |
http://localhost:11434 |
Provider endpoint |
REPOLENS_DOCKER_MODEL_BASE_URL |
http://host.docker.internal:11434 |
Compose-only provider endpoint |
REPOLENS_MODEL_NAME |
qwen2.5-coder:7b |
Configured model |
REPOLENS_MODEL_TIMEOUT_SECONDS |
30 | Per-attempt provider timeout |
REPOLENS_MODEL_RETRIES |
1 | Provider retries after the initial attempt |
REPOLENS_MODEL_MAX_RESPONSE_BYTES |
1 MiB | Streamed provider-response ceiling |
Safe text inventory and language detection cover Python, JavaScript, TypeScript, JSX, TSX, JSON, YAML, TOML, Markdown, reStructuredText, AsciiDoc, HTML, CSS, shell, Dockerfiles, and SQL. Only Python and JavaScript/TypeScript receive symbol/import extraction; see the exact support matrix.
RepoLens v1 has no authentication. Its trust boundary is a single local machine, and the shipped docker-compose.yml enforces that: both published ports bind 127.0.0.1 by default. Setting REPOLENS_BIND_HOST=0.0.0.0 exposes an API that permits full read, rename, cancel, source read-back and delete to anyone who can reach it. Do not do that without putting authentication in front of it. See security model and SECURITY.md.
- Version 1 is a local, single-user application and is not hardened for direct public-internet exposure.
- The bundled worker processes one job at a time. Queue claims take the SQLite write lock up front (
BEGIN IMMEDIATE) and are atomic, and the supervision loop survives a failed iteration, but concurrent workers are not covered by tests and SQLite remains intended for a local single-worker deployment. - Rule precision is measured against the committed corpus under
apps/api/tests/corpus/, not against a broad real-world sample. Comment and string awareness exists for Python and the JavaScript/TypeScript family only; findings in other languages say so and carry lower confidence. - Coverage is never measured. A committed
coverage.xml,lcov.info, orcoverage-final.jsonis read and reported as the project's own prior measurement, which may be stale. - Git history signals are bounded by the fetched commit window and are unavailable for ZIP uploads. Author identity is hashed and never reported.
- Multi-language complexity (
lizard) and Python security rules (bandit) are optional and are not in the production lockfile.summary.capabilitiesreports which actually ran. - Hard aggregate scratch-space enforcement is supplied by Compose's bounded
tmpfs. A localmake workerrun still has active clone monitoring and POSIX per-file limits, but its aggregate filesystem capacity depends on the configuredREPOLENS_WORKER_SCRATCH_DIRmount. - Direct host development uses an in-process address-pinning proxy but does not receive Compose's network-namespace egress isolation.
- Symbol extraction is AST-based for Python and conservative pattern-based for JavaScript/TypeScript.
.gitignoreis not interpreted in version 1; a fixed safety-oriented ignore set is used.- No vulnerability database runs by default, so dependency hygiene findings are never labeled as CVEs.
- Architecture and study-guide explanations are inferences, not original developer intent.
- Git network imports depend on local network availability and the system Git binary.
Read responsible analysis before acting on a report.
RepoLens has been reviewed twice, adversarially, and both reports are published unedited — including the parts that are unflattering.
- 2026-07-18 first-principles security review — a 55-row verification matrix, an honest "Conditional Pass", and an explicit list of features that were claimed but not implemented.
- 2026-07-24 full-stack audit — backend security, analysis-engine quality, frontend and design, and testing/CI/DX, plus a phased roadmap. Findings were reproduced by executing the shipped code against crafted hostile inputs, not by reading it.
The 2026-07-24 audit is the source of the Phase 0 and Phase 1 work in the current CHANGELOG. Findings that remain open are listed there rather than quietly dropped.
Six decisions that shaped the architecture, each with the alternatives that were rejected and what would change our minds:
| ADR | Decision |
|---|---|
| 0001 | A separate egress-proxy process rather than in-process DNS pinning |
| 0002 | SQLite and a polling worker rather than Redis or Celery |
| 0003 | Fork/exec per job rather than a thread pool |
| 0004 | Frozen migrations that refuse to guess at an unknown schema |
| 0005 | Model output is schema-validated but explicitly not evidence |
| 0006 | .gitignore is deliberately not interpreted |
- Server-sent live stage events and explicit job retry controls
- Deeper tree-sitter parsing and import graph cycles
- User-configurable safe ignore rules
- Optional maintained vulnerability scanner integration with explicit provenance
- Measured rule precision fixtures and a false-positive review workflow
Contributions are welcome; see CONTRIBUTING.md. RepoLens is available under the MIT License.




