| 1 | # openhub |
| 2 | |
| 3 | Minimal self-hosted git hosting with SSH key authentication. No passwords, no OAuth — just Ed25519 keys. |
| 4 | |
| 5 | ## Features |
| 6 | |
| 7 | - **SSH key auth** — signup, login, and push all use your existing SSH key |
| 8 | - **Git over SSH and HTTPS** — clone, fetch, and push with either protocol |
| 9 | - **Interactive SSH signup** — `ssh` in with an unregistered key to create an account |
| 10 | - **SSH repo management** — create, list, and delete repos over SSH |
| 11 | - **Web browsing** — browse users, repos, file trees, file contents, and commit logs |
| 12 | - **Invite-only** — signups require a single-use invite code |
| 13 | - **Quotas** — per-user repo limits and per-repo disk limits via a pre-receive hook |
| 14 | - **Single binary** — one Rust binary runs both the HTTP and SSH servers |
| 15 | |
| 16 | ## Quick Start |
| 17 | |
| 18 | ### Install the CLI |
| 19 | |
| 20 | ```sh |
| 21 | curl -fsSL https://your-host/install.sh | sh |
| 22 | ``` |
| 23 | |
| 24 | This installs the `openhub` CLI, configures SSH (`~/.ssh/config`), and sets up the git credential helper. |
| 25 | |
| 26 | ### Create an account |
| 27 | |
| 28 | ```sh |
| 29 | # Interactive signup over SSH (easiest) |
| 30 | ssh git@your-host |
| 31 | |
| 32 | # Or via CLI |
| 33 | openhub register |
| 34 | ``` |
| 35 | |
| 36 | ### Use it |
| 37 | |
| 38 | ```sh |
| 39 | openhub login |
| 40 | openhub repo new my-project |
| 41 | openhub clone you/my-project |
| 42 | cd my-project |
| 43 | # ... make changes ... |
| 44 | git push |
| 45 | ``` |
| 46 | |
| 47 | ### SSH commands |
| 48 | |
| 49 | ```sh |
| 50 | # Clone over SSH |
| 51 | git clone git@your-host:user/repo.git |
| 52 | |
| 53 | # Manage repos |
| 54 | ssh git@your-host repo new my-project |
| 55 | ssh git@your-host repo list |
| 56 | ssh git@your-host repo delete my-project |
| 57 | ``` |
| 58 | |
| 59 | ## Architecture |
| 60 | |
| 61 | ``` |
| 62 | server/ Axum HTTP server + russh SSH server |
| 63 | cli/ openhub CLI (register, login, repo management, clone) |
| 64 | hook/ git pre-receive hook for repo size enforcement |
| 65 | scripts/ install.sh, git-credential-openhub |
| 66 | migrations/ SQLite schema |
| 67 | deploy/ systemd unit, setup script |
| 68 | ``` |
| 69 | |
| 70 | The server runs two listeners via `tokio::select!`: |
| 71 | |
| 72 | - **HTTP** (default `:8080`) — API, git smart HTTP backend, web UI |
| 73 | - **SSH** (default `:22`) — git transport, repo commands, interactive signup |
| 74 | |
| 75 | Git protocol is handled by the system `git-http-backend` (HTTP) and `git-upload-pack`/`git-receive-pack` (SSH). The Rust server handles auth, routing, and quotas. |
| 76 | |
| 77 | ## Configuration |
| 78 | |
| 79 | All config via environment variables: |
| 80 | |
| 81 | | Variable | Default | Description | |
| 82 | |---|---|---| |
| 83 | | `OPENHUB_SITE_NAME` | `openhub` | Site name shown in UI and page titles | |
| 84 | | `OPENHUB_BIND` | `0.0.0.0:8080` | HTTP listen address | |
| 85 | | `OPENHUB_SSH_BIND` | `0.0.0.0:22` | SSH listen address | |
| 86 | | `OPENHUB_REPOS_ROOT` | `/srv/openhub/repos` | Bare git repos directory | |
| 87 | | `OPENHUB_DB_URL` | `sqlite:/srv/openhub/openhub.db` | SQLite database URL | |
| 88 | | `OPENHUB_SSH_HOST_KEY` | `/srv/openhub/ssh_host_ed25519_key` | SSH host key path (auto-generated) | |
| 89 | | `OPENHUB_ADMIN_KEY` | *(none)* | Bearer token for creating invites | |
| 90 | | `OPENHUB_MAX_REPOS_PER_USER` | `25` | Max repos per user | |
| 91 | | `OPENHUB_MAX_REPO_BYTES` | `52428800` | Max repo size in bytes (50 MiB) | |
| 92 | | `OPENHUB_MAX_PUSH_BYTES` | `10485760` | Max push request body (10 MiB) | |
| 93 | | `OPENHUB_HOOK_PATH` | `/usr/local/bin/git-quota-hook` | Path to pre-receive hook binary | |
| 94 | | `OPENHUB_GIT_HTTP_BACKEND` | `git-http-backend` | Path to git HTTP backend | |
| 95 | |
| 96 | ## Deployment |
| 97 | |
| 98 | ### Prerequisites |
| 99 | |
| 100 | - Rust toolchain |
| 101 | - `git`, `sqlite3`, `git-http-backend` (from `git-core`) |
| 102 | |
| 103 | ### Setup |
| 104 | |
| 105 | ```sh |
| 106 | # Run the setup script (creates user, disk image, builds, installs) |
| 107 | sudo deploy/setup.sh |
| 108 | |
| 109 | # Create an admin key for generating invites |
| 110 | echo 'OPENHUB_ADMIN_KEY=your-secret-key' >> /srv/openhub/openhub.env |
| 111 | |
| 112 | # Start |
| 113 | sudo systemctl start openhub |
| 114 | ``` |
| 115 | |
| 116 | ### Docker |
| 117 | |
| 118 | ```sh |
| 119 | docker compose up -d |
| 120 | ``` |
| 121 | |
| 122 | ### Generate invites |
| 123 | |
| 124 | ```sh |
| 125 | curl -X POST https://your-host/api/admin/invites \ |
| 126 | -H "Authorization: Bearer $OPENHUB_ADMIN_KEY" |
| 127 | ``` |
| 128 | |
| 129 | ## API |
| 130 | |
| 131 | | Method | Path | Auth | Description | |
| 132 | |---|---|---|---| |
| 133 | | `POST` | `/api/signup` | - | Create account with SSH key + invite | |
| 134 | | `POST` | `/api/login/challenge` | - | Request login challenge nonce | |
| 135 | | `POST` | `/api/login/verify` | - | Verify signature, get bearer token | |
| 136 | | `POST` | `/api/repos` | Bearer | Create a repository | |
| 137 | | `GET` | `/api/repos/:user` | - | List user's repositories | |
| 138 | | `DELETE` | `/api/repos/:user/:repo` | Bearer | Delete a repository | |
| 139 | | `GET` | `/api/stats` | - | User/repo counts and recent repos | |
| 140 | | `POST` | `/api/admin/invites` | Admin | Generate invite code | |
| 141 | |
| 142 | ## Building |
| 143 | |
| 144 | ```sh |
| 145 | # SQLite DB needed for sqlx compile-time checks |
| 146 | sqlite3 /tmp/openhub_build.db < migrations/0001_init.sql |
| 147 | DATABASE_URL=sqlite:/tmp/openhub_build.db cargo build --release |
| 148 | ``` |
| 149 | |
| 150 | ## License |
| 151 | |
| 152 | MIT |