📄 octuna / README.md
| 1 | # otgit |
| 2 | |
| 3 | Self-hosted git host with a 1999 Microsoft-era UI. Reuses Octuna's CSS shell. |
| 4 | |
| 5 |  |
| 6 | |
| 7 | ## What it does |
| 8 | |
| 9 | - Host git repositories on your own server |
| 10 | - Browse repos, files, commits, and diffs in the browser |
| 11 | - Push and pull over HTTPS smart protocol with basic auth |
| 12 | - Render Markdown READMEs (safe subset, no raw HTML) |
| 13 | - Render unified diffs with red/green hunks like GitHub |
| 14 | - Per-user accounts, public/private repos, admin web UI |
| 15 | |
| 16 | ## Why |
| 17 | |
| 18 | Modern feature set, retro skin. Lightweight, low RAM, low code, secure. |
| 19 | No framework, no database, two npm dependencies maximum (currently zero |
| 20 | runtime deps beyond Node itself). Designed to be portable, easy to read, |
| 21 | and easy to host on any small VPS that has Node and the `git` binary. |
| 22 | |
| 23 | ## Hard limits |
| 24 | |
| 25 | - Server target under 60 MB resident, capped at 64 MB heap |
| 26 | - No source file over 400 lines |
| 27 | - Total CSS under 1000 lines |
| 28 | - Every tunable comes from `config.json` via `config.get()`. No magic |
| 29 | numbers in route files |
| 30 | |
| 31 | ## Stack |
| 32 | |
| 33 | - Raw Node `http` (no Express, Koa, Fastify) |
| 34 | - `git` binary spawned for all repo operations |
| 35 | - Bare repos at `data/repos/<owner>/<name>.git` |
| 36 | - JSON state with atomic writes (`users.json`, `repos.json`) |
| 37 | - scrypt password hashing, HMAC-signed session cookies |
| 38 | - `git http-backend` CGI bridge for smart HTTP push/pull |
| 39 | |
| 40 | ## Layout |
| 41 | |
| 42 | ``` |
| 43 | server.js boot, route registration, listen |
| 44 | setup.js creates admin, generates secrets |
| 45 | config.json runtime tunables (generated by setup) |
| 46 | config.example.json |
| 47 | lib/ |
| 48 | config.js layered config + setMany validate |
| 49 | store.js atomic JSON store |
| 50 | respond.js json/html/text + security headers + readJson |
| 51 | router.js :param pattern matcher |
| 52 | log.js leveled logger |
| 53 | validate.js name and path safety |
| 54 | auth.js scrypt + HMAC sessions |
| 55 | limit.js rate limit factory |
| 56 | data.js users.json + repos.json singletons |
| 57 | git.js spawn wrappers for every git op |
| 58 | http-backend.js smart HTTP CGI bridge |
| 59 | render.js templates, escape, sidebar, tree, blob, diff |
| 60 | render-md.js markdown safe subset |
| 61 | render-diff.js unified diff to HTML |
| 62 | routes/ |
| 63 | static.js cached public assets |
| 64 | auth.js signup, login, logout, me, password |
| 65 | repo-write.js create, settings, rename, delete |
| 66 | smart-http.js info/refs, upload-pack, receive-pack |
| 67 | pages.js home, signup, new repo |
| 68 | admin.js admin settings page (full config control) |
| 69 | repo.js repo home, tree, blob, history, commit |
| 70 | user.js user profile |
| 71 | views/ HTML templates |
| 72 | public/ style.css, pages.css, app.js, logo.png |
| 73 | scripts/ |
| 74 | smoke.js end-to-end smoke test |
| 75 | data/ |
| 76 | users.json |
| 77 | repos.json |
| 78 | repos/<owner>/<name>.git/ |
| 79 | ``` |
| 80 | |
| 81 | ## Setup |
| 82 | |
| 83 | ``` |
| 84 | git clone https://git.dek.cx/dek/octuna.git otgit |
| 85 | cd otgit |
| 86 | node setup.js |
| 87 | npm start |
| 88 | ``` |
| 89 | |
| 90 | `setup.js` prompts for admin username, password, public URL, and port, |
| 91 | then writes `config.json` and creates the admin user. |
| 92 | |
| 93 | ## Reverse proxy (nginx) |
| 94 | |
| 95 | ``` |
| 96 | server { |
| 97 | listen 80; |
| 98 | server_name git.example.com; |
| 99 | client_max_body_size 500M; |
| 100 | location / { |
| 101 | proxy_pass http://127.0.0.1:3031; |
| 102 | proxy_http_version 1.1; |
| 103 | proxy_set_header Host $host; |
| 104 | proxy_set_header X-Real-IP $remote_addr; |
| 105 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| 106 | proxy_set_header X-Forwarded-Proto $scheme; |
| 107 | proxy_request_buffering off; |
| 108 | proxy_buffering off; |
| 109 | proxy_read_timeout 300s; |
| 110 | } |
| 111 | } |
| 112 | ``` |
| 113 | |
| 114 | Then `certbot --nginx -d git.example.com`. |
| 115 | |
| 116 | ## Pushing code |
| 117 | |
| 118 | ``` |
| 119 | git remote add origin https://git.example.com/<user>/<repo>.git |
| 120 | git push -u origin main |
| 121 | ``` |
| 122 | |
| 123 | Use the account password as the git basic auth password. Public repos |
| 124 | clone without auth. |
| 125 | |
| 126 | ## Admin |
| 127 | |
| 128 | The admin user (set by `setup.js`) sees an "Admin settings" link in |
| 129 | the sidebar. The page lets you edit any non-secret value in |
| 130 | `config.json` from the browser, with restart-required fields flagged. |
| 131 | |
| 132 | ## v1 scope |
| 133 | |
| 134 | In: signup, login, password change, repo CRUD, file tree, blob viewer |
| 135 | with line numbers, commit history, single-commit diff with hunks, |
| 136 | README render, push/pull, admin settings, per-repo settings. |
| 137 | |
| 138 | Out (post-v1): issues, pull requests, stars, forks, webhooks, CI, |
| 139 | releases, wikis, organizations, SSH transport, federation. |
| 140 | |
| 141 | ## License |
| 142 | |
| 143 | MIT. |
| 144 |