samjeston / openhub Public
Sam Jeston Working vertical 9aa3c45 · History
150 lines· 9.3 KB ·Markdown
1# Openhub
2
3Openhub is a minimal, self-hostable git server with a familiar web interface. Push over SSH, browse code and history, and review changes with pull requests. That is the whole product: no issue tracker, no CI, no wiki.
4
5It ships as a single Rust binary with SQLite for storage, so a whole installation is one executable plus one data directory.
6
7![Repository page](docs/screenshots/repository.png)
8
9## Features
10
11- **Git over SSH**: a built-in SSH server authenticates `git push` and `git fetch` with the public keys users add in their settings. No system `sshd` configuration is needed.
12- **Read-only git over HTTP**: anonymous clones of public repositories, and password-authenticated clones of private ones.
13- **Code browsing**: file trees with the latest commit for each entry, syntax-highlighted files with linkable line numbers, rendered Markdown READMEs, raw files, and ZIP/tar.gz downloads.
14- **History**: paginated commit logs (per branch, tag or path), commit pages with diffs, branches with ahead/behind counts, and tags.
15- **Pull requests**: compare branches, open a pull request, discuss it in Markdown comments, and review the commits and the diff. Merge with a merge commit, a squash or a rebase, then delete the branch. Pull requests merged with a plain `git push` are detected automatically.
16- **Accounts and access**: public and private repositories, plus collaborators with write access. The first account created is the site administrator.
17- **Comfortable to use**: light and dark themes, works on phone-sized screens, and needs no JavaScript for core features.
18
19<table>
20 <tr>
21 <td><img src="docs/screenshots/pull-request.png" alt="Pull request conversation"></td>
22 <td><img src="docs/screenshots/diff-dark.png" alt="Diff view in the dark theme"></td>
23 </tr>
24</table>
25
26## Quick start
27
28### With Docker
29
30```sh
31docker compose up -d
32```
33
34Open <http://localhost:3000> and create an account; the first account becomes the administrator. Before deploying, edit `OPENHUB_PUBLIC_URL` in `docker-compose.yml` to the URL people will use.
35
36### From source
37
38You need Rust 1.88 or newer, a C compiler (to build the bundled libgit2 and SQLite), and `git` installed at runtime.
39
40```sh
41cargo build --release
42./target/release/openhub --public-url http://localhost:3000
43```
44
45By default the web interface listens on port `3000` and the SSH server on port `2222`. Everything is stored in `./data`.
46
47## Pushing your first repository
48
491. Sign in and open **Settings → SSH keys**. Paste your public key, for example the contents of `~/.ssh/id_ed25519.pub`.
502. Check that the server accepts the key:
51 ```sh
52 ssh -T -p 2222 git@your-host
53 # Hi alice! You've successfully authenticated, but Openhub does not provide shell access.
54 ```
553. Create a repository with **New**, then push to it:
56 ```sh
57 git remote add origin ssh://git@your-host:2222/alice/project.git
58 git push -u origin main
59 ```
60
61When you push a branch other than the default branch, the server prints a link for opening a pull request. Once a pull request exists, the link points to it instead.
62
63## Configuration
64
65Each option can be set as a command-line flag or as an environment variable.
66
67| Flag | Environment variable | Default | Description |
68| --- | --- | --- | --- |
69| `--data-dir` | `OPENHUB_DATA_DIR` | `./data` | Holds the database, repositories and SSH host key |
70| `--http-addr` | `OPENHUB_HTTP_ADDR` | `0.0.0.0:3000` | Listen address for the web interface |
71| `--ssh-addr` | `OPENHUB_SSH_ADDR` | `0.0.0.0:2222` | Listen address for the SSH server |
72| `--public-url` | `OPENHUB_PUBLIC_URL` | `http://localhost:3000` | External URL of the web interface, used for links and clone URLs |
73| `--ssh-host` | `OPENHUB_SSH_HOST` | host of the public URL | Host shown in SSH clone URLs |
74| `--ssh-port` | `OPENHUB_SSH_PORT` | the SSH listen port | Port shown in SSH clone URLs |
75| `--disable-registration` | `OPENHUB_DISABLE_REGISTRATION` | `false` | Turns off sign-ups (the first account can still be created) |
76| `--git-bin` | `OPENHUB_GIT_BIN` | `git` | Path to the `git` executable |
77
78Logging is controlled with `RUST_LOG`, for example `RUST_LOG=openhub=debug`.
79
80## Deployment notes
81
82- **HTTPS**: put Openhub behind a reverse proxy such as Caddy or nginx and set `OPENHUB_PUBLIC_URL=https://git.example.com`. Session cookies are marked `Secure` whenever the public URL uses HTTPS. SSH traffic goes straight to the SSH port, not through the proxy.
83- **Public URL must match**: form submissions are only accepted from the origin in `OPENHUB_PUBLIC_URL`, or from one matching the request's `Host` header. If sign-in or other forms fail with "Cross-origin request rejected", check that the public URL matches exactly what appears in the browser's address bar, including scheme and port.
84- **Using port 22 for SSH**: forward port 22 to Openhub's SSH port (for example `"22:2222"` in Docker) and set `OPENHUB_SSH_PORT=22`. Clone URLs then take the short form `[email protected]:alice/project.git`. If the host already runs its own `sshd` on port 22, keep Openhub on another port.
85- **Host key**: the SSH host key is created on first start at `<data-dir>/ssh_host_ed25519_key`. Keep it with your backups so clients don't get host-key warnings after a restore.
86- **Backups**: back up the whole data directory. To copy the database while the server is running, use `sqlite3 openhub.db ".backup backup.db"`; otherwise stop the server first.
87- **Registration**: once everyone has an account, set `OPENHUB_DISABLE_REGISTRATION=true`.
88
89## How it works
90
91```
92browser ──▶ web UI (axum + askama) ──┐
93git (HTTP) ──▶ smart HTTP, read-only ──┼──▶ bare repositories (libgit2 + git)
94git (SSH) ──▶ SSH server (russh) ──┘
95 │
96 └──▶ SQLite: users, SSH keys, repositories, pull requests
97```
98
99- Repositories are stored as bare git repositories named by their database ID (`repos/42.git`), so renaming a repository only changes the database.
100- Fetches and pushes run the standard `git upload-pack` and `git receive-pack`, so protocol support (including protocol v2) matches the installed git.
101- After each push Openhub corrects `HEAD` if the default branch is missing, tracks the new tips of open pull requests, and marks pull requests whose commits reached the base branch as merged.
102- Each pull request's head commit is kept at `refs/pull/<n>/head`, so its history survives after the branch is deleted. You can fetch it with `git fetch origin pull/<n>/head`. These refs cannot be pushed to.
103- Web merges happen in memory with libgit2. The base branch is only updated if it hasn't moved since the merge started.
104
105### Source layout
106
107| Path | Purpose |
108| --- | --- |
109| `src/main.rs` | Startup: configuration, database, web and SSH servers |
110| `src/ssh.rs` | SSH server, key authentication, git command dispatch |
111| `src/git.rs` | Repository access: refs, trees, history, diffs, merging |
112| `src/sync.rs` | Post-push bookkeeping for HEAD and pull requests |
113| `src/db.rs`, `migrations/` | SQLite schema and queries |
114| `src/web/` | HTTP handlers: accounts, code browsing, pull requests, smart HTTP |
115| `src/highlight.rs`, `src/markdown.rs` | Syntax highlighting and sanitised Markdown |
116| `templates/`, `static/` | HTML templates, stylesheet and script (compiled into the binary) |
117
118## Security
119
120- Passwords are hashed with Argon2id. Session tokens are random, and only their SHA-256 hashes are stored.
121- Cookies are `HttpOnly` and `SameSite=Lax`, and cross-origin form posts are rejected.
122- Pages are served with a strict Content-Security-Policy, and no inline scripts are used.
123- Markdown (READMEs, pull requests and comments) is sanitised with `ammonia`. Raw files are served with a sandboxing CSP so they can never run script on the Openhub origin.
124- The SSH server accepts only public-key authentication and runs only `git-upload-pack` and `git-receive-pack`, for repositories the key's owner can access. Shell sessions are refused.
125- Private repositories return 404 to anyone without access.
126
127Openhub doesn't yet rate-limit sign-in attempts. If the server is exposed to the internet, add rate limiting at your reverse proxy. [docs/security.md](docs/security.md) lists the known gaps and hardening advice, including what to change before running an instance with open registration.
128
129## Development
130
131```sh
132cargo test # unit tests
133cargo clippy --all-targets # lints (kept warning-free)
134scripts/smoke-test.sh # end-to-end: push, browse, pull requests, merges, permissions
135cargo run -- --data-dir ./data
136```
137
138The smoke test starts a throwaway instance on ports 38080 and 38022 (override with `HTTP_PORT` and `SSH_PORT`) and drives it with `curl`, `ssh`, `ssh-keygen` and `git`.
139
140How the system works, including the architecture, data model, permissions, git transport, pull request tracking, security model and operations, is documented in [`docs/`](docs/README.md). Coding conventions and known pitfalls for contributors are in [`CLAUDE.md`](CLAUDE.md), with template-specific notes in [`templates/CLAUDE.md`](templates/CLAUDE.md).
141
142## Out of scope
143
144Openhub deliberately covers only git hosting. It has no issues, CI, wikis, releases, forks, organisations or line-level review comments.
145
146## License
147
148MIT. See [LICENSE](LICENSE).
149
150Icons are from [Lucide](https://lucide.dev) (ISC License). Syntax definitions come from [syntect](https://github.com/trishume/syntect) and [two-face](https://github.com/CosmicHorrorDev/two-face).