Files
Andy Dixon d7318acede Write full documentation with screenshots
README covers what CodeNavigator is, screenshots of each view, quick
start, configuration, keyboard shortcuts and links to docs/: a city
guide (what every visual means, getting around), architecture (indexer,
sessions, GitHub, rendering passes, routing), the HTTP API, GitHub App
setup including the alert permissions, deployment (Docker, systemd,
nginx, limits, security notes) and development (tests, layout,
conventions, design notes).
2026-08-09 10:15:59 +01:00

5.8 KiB
Raw Permalink Blame History

HTTP API

All endpoints are under /api/ and return JSON unless noted. Everything else is the static frontend. Every response carries Cache-Control: no-store, X-Content-Type-Options: nosniff and CORS headers (Access-Control-Allow-Origin from CORS_ORIGIN). OPTIONS on any path returns 204. Errors look like {"error": "message"}.

Sessions and ownership

The first POST /api/jobs (or GitHub sign-in) sets an HttpOnly, SameSite=Lax cookie called codenav_session (Secure when served over HTTPS or behind a proxy sending X-Forwarded-Proto: https). Jobs, snapshots and GitHub sign-ins belong to that session. Any other session gets 404 for them, exactly as if they didn't exist. IDs are random (crypto/rand).

Health

GET /api/health

{ "ok": true, "version": "0.1.0", "localFolders": false }

localFolders is true only when the server runs with SHOW_LOCAL=true.

Jobs (indexing)

POST /api/jobs

{ "kind": "github", "url": "https://github.com/owner/repo", "name": "repo" }
{ "kind": "local", "name": "my-folder" }
Status Meaning
201 {"jobId": "job-…"}; GitHub jobs start cloning immediately
400 Invalid body, unknown kind, missing or invalid GitHub URL (https://github.com/owner/repo only)
403 kind: local while local folders are disabled

GitHub clones are shallow (--depth=1), time out after 10 minutes, ignore the host's git config and credential helpers, and authenticate with the visitor's GitHub token when they're signed in.

POST /api/jobs/{id}/files?path=relative/path

Local jobs only. The request body is the raw file content (at most 64 MB). path must be relative with no .. segments. Returns {"ok": true}, 400 for unsafe paths, 404 for unknown or non-local jobs.

POST /api/jobs/{id}/commit

Local jobs only. Starts indexing the uploaded files. Returns 202 {"ok": true}.

GET /api/jobs/{id}

{
  "id": "job-…", "sourceKind": "github", "name": "prometheus",
  "phase": "parsing", "completed": 420, "total": 1676, "message": "Parsing 421 of 1676",
  "snapshotId": null, "error": null, "warnings": [],
  "authRequired": "signin"
}

phase is receiving, cloning, queued, scanning, parsing, linking, ready or error. authRequired only appears on failed clones that signing in (signin) or granting the GitHub App access (install) could fix.

GET /api/jobs/{id}/events

Server-sent events. Each change to the job is sent as:

event: progress
data: {…same shape as GET /api/jobs/{id}…}

A : keep-alive comment is sent every 15 seconds. The stream ends when the job has a snapshotId or an error, or when the client disconnects. At most two jobs index at once; others report queued.

Snapshots

GET /api/snapshots/{id}

{ "id": "snapshot-…", "name": "prometheus", "source": "github", "fileCount": 1676,
  "totalLines": 568224, "definitions": 13717, "references": 790, "edges": 790, "warnings": [] }

GET /api/snapshots/{id}/scene

Everything the frontend needs to lay out the map and city:

{
  "id": "snapshot-…", "name": "prometheus", "source": "github",
  "files": [{ "id": 1, "path": "cmd/prometheus/main.go", "name": "main.go", "directory": "cmd/prometheus",
              "extension": "go", "language": "Go", "layer": "application", "lines": 2296,
              "bytes": 91234, "complexity": 90, "preview": "// first 60 lines…", "symbolCount": 55 }],
  "edges": [{ "from": 1, "to": 2, "kind": "import", "confidence": "known" }],
  "totalLines": 568224, "definitions": 13717, "references": 790
}

After a scene is delivered the server drops its copy of the previews. layer is one of application, library, test, generated, vendor, platform, docs. confidence is known (resolved path) or inferred (matched by module name).

GET /api/snapshots/{id}/search?q=text

Case-insensitive substring match on file paths and symbol names (at least 2 characters, at most 180 hits):

[{ "entityId": 1, "path": "cmd/prometheus/main.go", "name": "main", "line": 120, "kind": "definition", "preview": "func main() {" }]

GET /api/snapshots/{id}/entities/{fileId}

The full file record, including symbols: [{name, kind, line, signature}].

GET /api/snapshots/{id}/entities/{fileId}/source?start=0&limit=600

{ "start": 0, "lines": ["package main", "…"], "totalLines": 2296 }

limit is clamped to 1–4096. Once every line of a file has been delivered the server deletes it from disk, and the workspace is removed once all files are delivered.

GET /api/snapshots/{id}/alerts

Open security alerts for a GitHub snapshot, fetched with the viewer's own token:

{
  "available": true,
  "codeScanning": { "status": "ok", "alerts": [
    { "path": "web/app.js", "title": "Cross-site scripting", "severity": "critical", "url": "https://github.com/…", "line": 7 } ] },
  "dependabot": { "status": "forbidden", "alerts": [] }
}

When alerts can't be fetched at all: {"available": false, "reason": "not-github" | "signin"}. Per-set status is ok, forbidden (permission not granted or feature disabled) or unavailable. Severities are normalised to critical, high, medium or low. Dependabot alerts point at the manifest that declares the vulnerable package.

GitHub sign-in

Endpoint Purpose
GET /api/github/status {"configured", "connected", "login", "installUrl"} for the current session
GET /api/github/login Redirects to GitHub with a single-use state bound to the session (10 minutes)
GET /api/github/callback Exchanges the code, stores the user token in memory, redirects to /?github=connected (or denied/error)
POST /api/github/logout Forgets the session's token

Login, callback and logout return 404 when no GitHub App is configured.