dek/octuna - git - Webernet Explorer _X
git 🏠 Home 👤 Sign up Self hosted git, retro shell
★ otgit, code hosting from the year 2000 ★
📚 dek / octuna public

Image Sharing

📃 5 commits  ·  📄 48 files  ·  📅 created 2026-05-04
JavaScript 71.2% Markdown 10.9% CSS 10.1% HTML 6.4% JSON 1.3%
📁 Files
TypeName
📄 .gitignore
📄 AGENTS.md
📄 README.md
📄 cmdkey
📄 config.example.json
📄 git
📁 lib/
📄 package.json
📁 public/
📁 routes/
📁 scripts/
📄 server.js
📄 setup.js
📁 views/

otgit

Self-hosted git host with a 1999 Microsoft-era UI. Reuses Octuna's CSS shell.

!otgit

What it does

  • Host git repositories on your own server
  • Browse repos, files, commits, and diffs in the browser
  • Push and pull over HTTPS smart protocol with basic auth
  • Render Markdown READMEs (safe subset, no raw HTML)
  • Render unified diffs with red/green hunks like GitHub
  • Per-user accounts, public/private repos, admin web UI

Why

Modern feature set, retro skin. Lightweight, low RAM, low code, secure.

No framework, no database, two npm dependencies maximum (currently zero

runtime deps beyond Node itself). Designed to be portable, easy to read,

and easy to host on any small VPS that has Node and the git binary.

Hard limits

  • Server target under 60 MB resident, capped at 64 MB heap
  • No source file over 400 lines
  • Total CSS under 1000 lines
  • Every tunable comes from config.json via config.get(). No magic

numbers in route files

Stack

  • Raw Node http (no Express, Koa, Fastify)
  • git binary spawned for all repo operations
  • Bare repos at data/repos/<owner>/<name>.git
  • JSON state with atomic writes (users.json, repos.json)
  • scrypt password hashing, HMAC-signed session cookies
  • git http-backend CGI bridge for smart HTTP push/pull

Layout

server.js              boot, route registration, listen
setup.js               creates admin, generates secrets
config.json            runtime tunables (generated by setup)
config.example.json
lib/
  config.js            layered config + setMany validate
  store.js             atomic JSON store
  respond.js           json/html/text + security headers + readJson
  router.js            :param pattern matcher
  log.js               leveled logger
  validate.js          name and path safety
  auth.js              scrypt + HMAC sessions
  limit.js             rate limit factory
  data.js              users.json + repos.json singletons
  git.js               spawn wrappers for every git op
  http-backend.js      smart HTTP CGI bridge
  render.js            templates, escape, sidebar, tree, blob, diff
  render-md.js         markdown safe subset
  render-diff.js       unified diff to HTML
routes/
  static.js            cached public assets
  auth.js              signup, login, logout, me, password
  repo-write.js        create, settings, rename, delete
  smart-http.js        info/refs, upload-pack, receive-pack
  pages.js             home, signup, new repo
  admin.js             admin settings page (full config control)
  repo.js              repo home, tree, blob, history, commit
  user.js              user profile
views/                 HTML templates
public/                style.css, pages.css, app.js, logo.png
scripts/
  smoke.js             end-to-end smoke test
data/
  users.json
  repos.json
  repos/<owner>/<name>.git/

Setup

git clone https://git.dek.cx/dek/octuna.git otgit
cd otgit
node setup.js
npm start

setup.js prompts for admin username, password, public URL, and port,

then writes config.json and creates the admin user.

Reverse proxy (nginx)

server {
  listen 80;
  server_name git.example.com;
  client_max_body_size 500M;
  location / {
    proxy_pass http://127.0.0.1:3031;
    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;
    proxy_request_buffering off;
    proxy_buffering off;
    proxy_read_timeout 300s;
  }
}

Then certbot --nginx -d git.example.com.

Pushing code

git remote add origin https://git.example.com/<user>/<repo>.git
git push -u origin main

Use the account password as the git basic auth password. Public repos

clone without auth.

Admin

The admin user (set by setup.js) sees an "Admin settings" link in

the sidebar. The page lets you edit any non-secret value in

config.json from the browser, with restart-required fields flagged.

v1 scope

In: signup, login, password change, repo CRUD, file tree, blob viewer

with line numbers, commit history, single-commit diff with hunks,

README render, push/pull, admin settings, per-repo settings.

Out (post-v1): issues, pull requests, stars, forks, webhooks, CI,

releases, wikis, organizations, SSH transport, federation.

License

MIT.

Done 🔒 Internet