# loc-via-git Tiny API that clones a Git repo and counts its non-empty lines of code. ## Features - Plain text and JSON endpoints - Optional API key enforcement - Per-repo in-memory caching - Extension-based language breakdown - Generic project metadata files are ignored during counting - Basic rate limiting - Bounded concurrent scans so the host does not get hammered - Host allowlist and scan/file size limits, with a capped temporary filesystem in Docker - SSH key file support for private repos - Docker Compose deployment ## Endpoints - `GET /loc.txt?repo=&ssh_key=&ref=&api_key=` - Returns the line count as plain text. - `GET /loc?repo=&ssh_key=&ref=&api_key=` - Returns JSON metadata, including the resolved commit SHA and a language breakdown by files and non-empty lines. - `GET /loc/diff?repo=&base=&head=&ssh_key=` - Returns both snapshots and their total/per-language LOC delta. - `GET /health` - Health plus queue/cache stats. - `GET /metrics` - Prometheus-compatible request, cache, scan, file, and line counters. Protected by the API key when one is configured. ## Auth No auth by default. If `API_KEY` is set, send it as either: - `x-api-key: ...` - `Authorization: Bearer ...` - `api_key=...` query param The `api_key` query param is unsafe and discouraged because it can leak through logs, browser history, analytics, caches, and referrers. Prefer `x-api-key` or `Authorization` whenever possible. ## SSH keys SSH keys live inside the Docker volume mounted at `/app/keys`, not in the repo or a host bind mount. You have two options: - Provide a key pair yourself inside the `ssh_keys` Docker volume - Let the service generate the default key by setting `GENERATE_SSH_KEY_IF_MISSING=true` Example: ```bash curl "http://localhost:3000/loc?repo=ssh://git@example.com/org/repo.git&ssh_key=loc_via_git_ed25519" ``` Fetch the public key to add it on the Git host: ```bash curl http://localhost:3000/ssh/public-key ``` You can also request a specific key: ```bash curl "http://localhost:3000/ssh/public-key?ssh_key=loc_via_git_ed25519" ``` ## Cleanup and caching - Every clone happens in a temporary directory and is deleted in a `finally` block after the scan finishes or fails. - The service also sweeps stale temp directories in case a process dies mid-scan. - Cache entries live in memory only and expire after `CACHE_TTL_MINUTES`. ## Operations - Requests emit structured JSON logs with an opaque request ID, route, status, and duration. Query strings and API keys are never logged. - The service handles `SIGINT` and `SIGTERM`: it stops accepting new connections, clears its cleanup timer, and exits after active connections drain (or 10 seconds). - `/metrics` is compatible with Prometheus scraping. `/health` remains unauthenticated for container health checks. ## Counting rules - Only text files are counted. - Empty lines are ignored. - Generic project metadata, generated/minified files, and common build/vendor directories are skipped. - Scans are rejected when they exceed the configured file, per-file, or total scanned-byte limits. ## Repository access `ALLOWED_GIT_HOSTS` is optional. Leave it unset or empty to allow every remote host, or use a comma-separated allowlist such as `gitea.reversed.dev,github.com`. Local paths, `file://` URLs, non-SSH/HTTPS protocols, and HTTPS URLs containing credentials are always rejected. ## Configuration Copy `.env.example` to `.env` and adjust: ```env PORT=3000 API_KEY= ALLOWED_GIT_HOSTS= CACHE_TTL_MINUTES=5 CACHE_SWEEP_INTERVAL_MINUTES=5 RATE_LIMIT_WINDOW_MINUTES=5 RATE_LIMIT_MAX=30 MAX_CONCURRENT_SCANS=4 MAX_FILES_PER_SCAN=20000 MAX_FILE_SIZE_MB=5 MAX_SCAN_SIZE_MB=100 CLONE_TIMEOUT_SECONDS=45 DEFAULT_SSH_KEY_NAME=loc_via_git_ed25519 GENERATE_SSH_KEY_IF_MISSING=false TRUST_PROXY=false SSH_KEYS_DIR=/app/keys TMP_DIR=/tmp/loc-via-git ``` ## Local run ```bash npm install npm run build npm start ``` ## Docker compose ```bash docker compose up --build ``` To copy an existing key pair into the Docker volume: ```bash docker cp ./id_ed25519 loc-via-git-api-1:/app/keys/loc_via_git_ed25519 docker cp ./id_ed25519.pub loc-via-git-api-1:/app/keys/loc_via_git_ed25519.pub ``` ## Smoke test ```bash npm run smoke ```