Monorepo scaffold for a local codebase explorer with:
backend/: RustaxumAPI for directory browsing, AI context assembly, repo indexing, and hybrid search.frontend/: Next.js app router UI for tree navigation, file viewing, indexing controls, search, "ask with selected files", and profile management.- Postgres-backed git repository snapshots that import tracked text files, store derived language analysis, and expose the stored tree/file contents through the UI and API.
.
├── backend
│ ├── Cargo.toml
│ ├── migrations/
│ ├── src/lib.rs
│ ├── src/main.rs
│ └── tests/api.rs
└── frontend
├── app/
├── components/
├── lib/
└── test/
The backend serves on http://127.0.0.1:8787 by default.
Core endpoints:
GET /healthGET /api/tree?path=<relative_dir>GET /api/file?path=<relative_file>POST /api/askwith body:question: stringpaths: string[]
Indexed search and indexing endpoints (require DATABASE_URL):
POST /api/indexwith body{}GET /api/index/statusGET /api/search?query=<text>&path=<relative_prefix>&limit=<n>GET /api/search/hybrid?query=<text>&path=<relative_prefix>&limit=<n>
Stored git repository endpoints (require DATABASE_URL):
POST /api/git/repositories/importwith body{ "source": "<relative_repo_path_or_remote_url>" }GET /api/git/repositoriesGET /api/git/repositories/{id}/tree?path=<relative_dir>GET /api/git/repositories/{id}/file?path=<relative_file>
Authentication:
GET /healthis public.- All
/api/*routes require credentials by default (EXPLORER_AUTH_DISABLED=false). - Send credentials using
X-API-Key: <key>(preferred) orAuthorization: Bearer <key>. - Read endpoints accept read/admin keys; admin endpoints (
POST /api/index,POST/PUT /api/profiles*) require the admin key. - Frontend profile writes are sent through same-origin proxy routes (
/api/internal/profiles*) that attach the admin key server-side.
Common auth/abuse status codes:
401missing or invalid credentials403read key provided for an admin-only endpoint413request body exceeds size limit429request rate limit exceeded
Feature toggle:
HYBRID_SEARCH_ENABLED=falsedisablesGET /api/search/hybrid, which then returns404with"hybrid search is disabled".
Path traversal is blocked (.. and absolute paths are rejected), and all reads are restricted to EXPLORER_ROOT.
Local git repository imports are restricted to repositories that resolve within EXPLORER_ROOT.
file:// remote imports are also restricted to EXPLORER_ROOT; http(s), ssh, git, and scp-style remotes are treated as external sources.
GET /api/search and GET /api/search/hybrid return 409 until at least one successful index exists.
Indexing defaults:
- Hidden files and sensitive files are excluded from indexing by default.
- Set
EXPLORER_INDEX_INCLUDE_SENSITIVE_FILES=trueonly when you intentionally need to include them.
docker run --name sherlock-postgres \
-e POSTGRES_DB=sherlock \
-e POSTGRES_USER=sherlock \
-e POSTGRES_PASSWORD=sherlock \
-p 5432:5432 \
-d pgvector/pgvector:pg17cd backend
DATABASE_URL=postgres://sherlock:sherlock@127.0.0.1:5432/sherlock \
EXPLORER_ROOT=.. \
EXPLORER_READ_API_KEY=dev-read-key \
EXPLORER_ADMIN_API_KEY=dev-admin-key \
OPENAI_API_KEY=your_key_here \
cargo runOptional env vars:
HOST(default:127.0.0.1)PORT(default:8787)EXPLORER_ROOT(default: current directory)EXPLORER_AUTH_DISABLED(default:false; settrueonly for explicit local/dev opt-out)EXPLORER_READ_API_KEY(required when auth is enabled; grants read scope)EXPLORER_ADMIN_API_KEY(required when auth is enabled; grants admin scope)EXPLORER_ALLOWED_ORIGINS(comma-separated CORS allowlist; defaults to localhost frontend origins)EXPLORER_INDEX_INCLUDE_SENSITIVE_FILES(default:false; includes hidden/sensitive files whentrue)DATABASE_URL(required for/api/index*and/api/search*)HYBRID_SEARCH_ENABLED(default:true; set tofalseto disable/api/search/hybrid)EMBEDDING_PROVIDER(default:openai;mockis available for local/testing)EMBEDDING_MODEL(default:text-embedding-3-small)OPENAI_API_KEY(required whenEMBEDDING_PROVIDER=openai)
cd frontend
npm install
SESSION_SECRET=replace-with-at-least-32-characters \
LOGIN_USERNAME=admin \
LOGIN_PASSWORD_HASH='<bcrypt hash for your password>' \
EXPLORER_BACKEND_API_BASE=http://127.0.0.1:8787 \
EXPLORER_READ_API_KEY=dev-read-key \
EXPLORER_ADMIN_API_KEY=dev-admin-key \
npm run devOpen http://127.0.0.1:3000.
Generate a bcrypt password hash from the frontend/ directory with:
node -e "const bcrypt=require('bcryptjs'); console.log(bcrypt.hashSync('change-me', 10));"Use the UI Start/Reindex button or call the API:
curl -X POST http://127.0.0.1:8787/api/index \
-H 'x-api-key: your_admin_api_key' \
-H 'content-type: application/json' \
-d '{}'Poll status:
curl http://127.0.0.1:8787/api/index/status \
-H 'x-api-key: your_read_api_key'Use the Explorer UI "Repository Archive" section or call the API:
curl -X POST http://127.0.0.1:8787/api/git/repositories/import \
-H 'x-api-key: your_admin_api_key' \
-H 'content-type: application/json' \
-d '{"source":"."}'Remote example:
curl -X POST http://127.0.0.1:8787/api/git/repositories/import \
-H 'x-api-key: your_admin_api_key' \
-H 'content-type: application/json' \
-d '{"source":"https://github.com/example/project.git"}'List stored repositories:
curl http://127.0.0.1:8787/api/git/repositories \
-H 'x-api-key: your_read_api_key'Each stored repository record includes whether the source was local or remote, branch/head metadata, dirty-state detection, tracked-vs-stored file counts, and a language breakdown for the imported text files.
Build and run the stack:
docker compose up --build -dStop the stack:
docker compose downServices:
- Frontend:
http://localhost:3000 - Backend API:
http://localhost:8787 - Postgres+pgvector:
localhost:5432
Required environment:
EXPLORER_READ_API_KEY=your_read_api_key \
EXPLORER_ADMIN_API_KEY=your_admin_api_key \
SESSION_SECRET=replace-with-at-least-32-characters \
LOGIN_USERNAME=your_login_username \
LOGIN_PASSWORD_HASH='<bcrypt hash for your password>' \
EXPLORER_BACKEND_API_BASE=http://backend:8787 \
OPENAI_API_KEY=your_key_here \
EMBEDDING_PROVIDER=openai \
docker compose up --build -dNotes:
- Backend container reads files from a read-only bind mount of the repo at
/workspace. - Backend root path is controlled by
EXPLORER_ROOT(default in compose:/workspace). - Backend bind address is configurable with
HOST(default:127.0.0.1; compose uses0.0.0.0). - Browser clients no longer receive backend API keys; frontend reads and writes go through same-origin Next.js proxy routes.
Run all tests from repo root:
./scripts/test-all.shOr run package-level commands directly:
cargo test --manifest-path backend/Cargo.toml
npm run test --prefix frontendLint checks:
cargo fmt --manifest-path backend/Cargo.toml --all -- --check
cargo clippy --manifest-path backend/Cargo.toml --all-targets -- -D warnings
npm --prefix frontend run lintGenerate coverage reports for both backend and frontend:
./scripts/test-all.sh --coverageRun full-stack integration checks (requires running backend, frontend, and postgres services):
./scripts/run-integration-tests.shCoverage artifact paths:
- Backend LCOV:
backend/target/llvm-cov/lcov.info - Frontend reports:
frontend/coverage/
CI:
- GitHub Actions workflow at
.github/workflows/lint.ymlruns backend and frontend lint jobs on every push and pull request. - GitHub Actions workflow at
.github/workflows/tests.ymlruns backend tests (with pgvector service), frontend tests, a Docker Compose full-stack integration suite, and a separate coverage job. - GitHub Actions workflow at
.github/workflows/audit.ymlruns dependency audits (cargo auditandnpm audit) on push/pull request and on manual dispatch.