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

3.4 KiB

Deployment

Docker

./build.sh [tag]            # default codenavigator:latest; runs go test inside the build
docker run -d --name codenavigator -p 127.0.0.1:4177:4177 \
  --env-file /etc/codenavigator/app.env \
  --memory 2g --cpus 4 --pids-limit 512 \
  codenavigator:latest

The image is a static binary on Alpine with git and CA certificates, running as a non-root user, with a HEALTHCHECK on /api/health. Workspaces live in the container's /tmp and disappear with it.

app.env (keep it chmod 600):

GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_APP_SLUG=
# SHOW_LOCAL=true         # only if visitors should be able to upload local folders

systemd

/etc/systemd/system/codenavigator.service:

[Unit]
Description=CodeNavigator (docker container)
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target

[Service]
Restart=always
RestartSec=5
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm -f codenavigator
ExecStart=/usr/bin/docker run --rm --name codenavigator \
  --env-file /etc/codenavigator/app.env \
  -p 127.0.0.1:4177:4177 \
  --memory 2g --cpus 4 --pids-limit 512 \
  codenavigator:latest
ExecStop=/usr/bin/docker stop -t 30 codenavigator

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now codenavigator

Redeploy: ./build.sh && sudo systemctl restart codenavigator.

nginx

server {
    listen 80;
    server_name codenav.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name codenav.example.com;

    ssl_certificate     /etc/letsencrypt/live/codenav.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/codenav.example.com/privkey.pem;

    # Local folder uploads send one file per request; the frontend skips files over 8 MB.
    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:4177;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # Indexing progress is a server-sent event stream: don't buffer it.
        proxy_buffering    off;
        proxy_read_timeout 300s;
    }
}

X-Forwarded-Proto: https makes the session cookie Secure. Certificates: certbot certonly --nginx -d codenav.example.com.

Operating limits

Limit Value
Concurrent indexing 2 jobs; others queue
Snapshots held 8 in total, 1 per session, least recently used evicted
Idle expiry 1 hour for snapshots, finished jobs and abandoned uploads
Clone timeout 10 minutes
File size files over 8 MB are skipped; uploads capped at 64 MB
GitHub tokens in memory, 8 hours

All state is in memory. A restart clears snapshots and signs everyone out.

Security notes

  • Local folders are off unless SHOW_LOCAL=true. With them on, anyone who can reach the site can upload files to the server's temporary workspace.
  • Anyone who can reach the site can clone public repositories through it. Put it behind authentication if that matters to you.
  • Visitors' GitHub tokens never reach the browser, git arguments or logs. Host git config and credential helpers are ignored, so the server's own credentials can't be used by a visitor.