README.md
·
220 строк
·
9.9 KB
| 1 | # Polar |
| 2 | |
| 3 | **Lightweight self-hosted Git hosting in a single Go binary.** |
| 4 | |
| 5 | Polar is a small, fast alternative to heavyweight Git servers: push over HTTP, browse code, file issues, open pull requests, and publish releases — all served from one static binary with an embedded SQLite database and no external runtime dependencies. |
| 6 | |
| 7 | ## Highlights |
| 8 | |
| 9 | - **One binary, zero dependencies** — `CGO_ENABLED=0` build, pure-Go SQLite (`modernc.org/sqlite`) in WAL mode, bare repositories stored on disk. |
| 10 | - **Git Smart HTTP** — anonymous read access for public repositories, token-authenticated push, per-user personal access tokens, and browser-assisted sign-in via the OAuth 2.0 Device Authorization Grant (RFC 8628). |
| 11 | - **Code browsing** — file tree, blob viewer with server-side syntax highlighting (Chroma), commit history, diffs, branches, tags, and zip archives. The **New file**, **New folder** and **Upload files** actions let you create or upload content right in the browser — changes are committed straight to the selected branch, no local checkout required. |
| 12 | - **Collaboration** — issues with comments and open/close state; pull requests with comments, approvals and change requests; forks with cross-fork pull requests; per-repository collaborators with read/write roles; and releases with changelogs. |
| 13 | - **Polished UI** — `html/template` + htmx, no SPA build step; Markdown rendered with Goldmark and sanitized with bluemonday; Hugeicons outline sprite; self-hosted Inter and JetBrains Mono fonts (Cyrillic subsets included). |
| 14 | - **Command palette** — press `Ctrl+K` / `Cmd+K` for quick navigation and actions. |
| 15 | - **Three themes** — Light (`#fff` background, `#111` foreground), Dark (`#111` background, `#fff` foreground), and System (follows the OS `prefers-color-scheme`). The choice is persisted in a `polar_theme` cookie so the server renders the correct `data-theme` on first paint — no flash of the wrong theme. |
| 16 | - **Bilingual interface** — Russian and English, with a configurable default language. |
| 17 | - **Practical security** — Argon2id password hashing, CSRF protection, rate-limited login and registration, session cookies, and revocable access tokens. |
| 18 | |
| 19 | ## Quick Start |
| 20 | |
| 21 | ### Build from source |
| 22 | |
| 23 | Requirements: Go (version per `go.mod`) and `git` available on the host. |
| 24 | |
| 25 | ```bash |
| 26 | CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o polar ./cmd/polar |
| 27 | ./polar |
| 28 | ``` |
| 29 | |
| 30 | Polar starts on `0.0.0.0:3000` and keeps its data in `./data`. Open http://localhost:3000 and register — **the first registered account becomes the administrator**. After that you can disable public registration or manage users from the admin panel. |
| 31 | |
| 32 | ### Docker |
| 33 | |
| 34 | ```bash |
| 35 | docker compose up -d |
| 36 | ``` |
| 37 | |
| 38 | Or without Compose: |
| 39 | |
| 40 | ```bash |
| 41 | docker build -t polar . |
| 42 | docker run -d --name polar -p 3000:3000 -v "$PWD/data:/app/data" polar |
| 43 | ``` |
| 44 | |
| 45 | ### systemd |
| 46 | |
| 47 | ```ini |
| 48 | # /etc/systemd/system/polar.service |
| 49 | [Unit] |
| 50 | Description=Polar Self-Hosted Git Server |
| 51 | After=network.target |
| 52 | |
| 53 | [Service] |
| 54 | Type=simple |
| 55 | User=polar |
| 56 | Group=polar |
| 57 | WorkingDirectory=/var/lib/polar |
| 58 | ExecStart=/usr/local/bin/polar -config /etc/polar/polar.toml |
| 59 | Environment=POLAR_STORAGE_DATA_DIR=/var/lib/polar/data |
| 60 | Restart=on-failure |
| 61 | |
| 62 | [Install] |
| 63 | WantedBy=multi-user.target |
| 64 | ``` |
| 65 | |
| 66 | ## Configuration |
| 67 | |
| 68 | Polar runs with sensible defaults and no config file. To customize, copy `polar.example.toml` to `polar.toml` (auto-detected in the working directory) or pass `-config /path/to/polar.toml`. Every setting can also be provided through environment variables, which take precedence over the file. |
| 69 | |
| 70 | ```toml |
| 71 | [server] |
| 72 | host = "0.0.0.0" |
| 73 | port = 3000 |
| 74 | base_url = "http://localhost:3000" |
| 75 | |
| 76 | [storage] |
| 77 | data_dir = "./data" |
| 78 | |
| 79 | [auth] |
| 80 | registration_enabled = true |
| 81 | |
| 82 | [landing] |
| 83 | enabled = true |
| 84 | default_lang = "en" # "en" or "ru" |
| 85 | ``` |
| 86 | |
| 87 | | Environment variable | Description | Default | |
| 88 | |---|---|---| |
| 89 | | `POLAR_SERVER_HOST` | HTTP listen host | `0.0.0.0` | |
| 90 | | `POLAR_SERVER_PORT` | HTTP listen port | `3000` | |
| 91 | | `POLAR_SERVER_BASE_URL` | Public URL used for clone links | `http://localhost:3000` | |
| 92 | | `POLAR_STORAGE_DATA_DIR` | Directory for the database and bare repositories | `./data` | |
| 93 | | `POLAR_AUTH_REGISTRATION_ENABLED` | Allow public registration | `true` | |
| 94 | | `POLAR_LANDING_ENABLED` | Serve the landing page at `/` | `true` | |
| 95 | | `POLAR_LANDING_DEFAULT_LANG` | Default UI language (`ru` or `en`) | `ru` | |
| 96 | | `POLAR_CONFIG` | Path to the configuration file | `polar.toml` if present | |
| 97 | |
| 98 | Short aliases are also accepted: `POLAR_HOST`, `POLAR_PORT`, `POLAR_BASE_URL`, `POLAR_DATA_DIR`. |
| 99 | |
| 100 | ## Git over HTTP |
| 101 | |
| 102 | Clone a public repository: |
| 103 | |
| 104 | ```bash |
| 105 | git clone http://localhost:3000/rin/demo.git |
| 106 | ``` |
| 107 | |
| 108 | Push using a personal access token (create one in **Settings → Access tokens**; it is shown once and looks like `polar_...`): |
| 109 | |
| 110 | ```bash |
| 111 | git clone http://localhost:3000/rin/demo.git |
| 112 | cd demo |
| 113 | git remote set-url origin http://rin:polar_XXXXXXXXXX@localhost:3000/rin/demo.git |
| 114 | git push |
| 115 | ``` |
| 116 | |
| 117 | When git asks for credentials, use your username as the login and the token as the password. Tokens can be revoked at any time from the settings page. |
| 118 | |
| 119 | ### Browser-assisted sign-in (device flow) |
| 120 | |
| 121 | Instead of pasting a token, let the bundled credential helper obtain one through your browser. Polar implements the OAuth 2.0 Device Authorization Grant (RFC 8628): |
| 122 | |
| 123 | 1. `git push` asks for credentials; the helper requests a device code from `POST /api/v1/device/code`. |
| 124 | 2. It opens `{base_url}/login/device?user_code=XXXX-XXXX` in your browser — sign in and approve (or deny) the request. |
| 125 | 3. The helper polls `POST /api/v1/device/token`, receives a personal access token, and caches it in `~/.polar/credentials.json`. |
| 126 | |
| 127 | Register the helper once, pointing the path at your Polar checkout: |
| 128 | |
| 129 | ```bash |
| 130 | # Linux / macOS |
| 131 | git config --global credential.helper '!sh /path/to/polar/scripts/git-credential-polar.sh' |
| 132 | |
| 133 | # Windows (PowerShell) |
| 134 | git config --global credential.helper "!powershell -NoProfile -ExecutionPolicy Bypass -File C:/path/to/polar/scripts/git-credential-polar.ps1" |
| 135 | ``` |
| 136 | |
| 137 | Device codes expire after 15 minutes, are single-use, and the issued token can be revoked like any other. Set `POLAR_URL` to override the server address detected from the git remote. |
| 138 | |
| 139 | ## Forks, Collaborators and Reviews |
| 140 | |
| 141 | **Forks.** The **Fork** button on any repository page copies the project to your account. Forking a fork re-roots the copy to the upstream repository, and every fork records its origin — the repository page shows a `forked from` line linking back. If you already have a fork, the button takes you to it instead of creating a duplicate. |
| 142 | |
| 143 | **Cross-fork pull requests.** Open a pull request from any branch of your fork into the base repository. The PR page marks the head repository, and merging a cross-fork pull request first fetches the head commits into the base repository, so no commits are lost. |
| 144 | |
| 145 | **Collaborators.** Repository owners and instance administrators manage access in **Settings → Collaborators**: add any registered account with a `read` or `write` role, change roles, or remove access at any time. |
| 146 | |
| 147 | **Reviews.** On an open pull request, reviewers submit an **Approve** or **Request changes** verdict — reviewing your own pull request is not allowed. Each reviewer has one current verdict, and the review panel lists them all. |
| 148 | |
| 149 | ## Themes |
| 150 | |
| 151 | Polar ships exactly three themes, selectable from the theme switcher (or the command palette): |
| 152 | |
| 153 | | Theme | Background | Foreground | |
| 154 | |---|---|---| |
| 155 | | Light | `#fff` | `#111` | |
| 156 | | Dark | `#111` | `#fff` | |
| 157 | | System | follows the OS `prefers-color-scheme` | — | |
| 158 | |
| 159 | The active theme is saved to `localStorage` and a `polar_theme` cookie, so the server renders `<html data-theme="...">` immediately and there is no flash of the wrong theme on load. |
| 160 | |
| 161 | ## Project Layout |
| 162 | |
| 163 | ``` |
| 164 | polar/ |
| 165 | ├── cmd/polar/main.go # Entry point, dependency wiring, graceful shutdown |
| 166 | ├── internal/ |
| 167 | │ ├── config/ # polar.toml + POLAR_* environment overrides |
| 168 | │ ├── db/ # SQLite connection and migrations |
| 169 | │ ├── auth/ # Sessions, CSRF, Argon2id, access tokens |
| 170 | │ ├── user/ # User accounts and profiles |
| 171 | │ ├── repo/ # Repositories, issues, pull requests, releases |
| 172 | │ ├── git/ # Git plumbing (bare repos, commits, diffs) |
| 173 | │ ├── i18n/ # Russian and English message catalogs |
| 174 | │ └── web/ |
| 175 | │ ├── handlers/ # HTTP and Git Smart HTTP handlers |
| 176 | │ ├── middleware/ # Auth, CSRF, rate limiting, i18n |
| 177 | │ ├── views/ # html/template views |
| 178 | │ └── static/ # go:embed assets (CSS, JS, fonts, icons) |
| 179 | ├── assets/input.css # Tailwind source for the generated stylesheet |
| 180 | ├── scripts/ # git credential helpers (device-flow sign-in) |
| 181 | ├── polar.example.toml |
| 182 | ├── Makefile |
| 183 | ├── Dockerfile |
| 184 | └── docker-compose.yml |
| 185 | ``` |
| 186 | |
| 187 | ## Development |
| 188 | |
| 189 | ```bash |
| 190 | make dev # run locally |
| 191 | make test # go test ./... |
| 192 | make lint # go vet ./... |
| 193 | make build # static binary with -trimpath -ldflags="-s -w" |
| 194 | make build-all # cross-compile Linux/macOS/Windows binaries into dist/ |
| 195 | make css # rebuild internal/web/static/css/app.css (requires the Tailwind CLI) |
| 196 | ``` |
| 197 | |
| 198 | All runtime assets are embedded with `go:embed` — the binary needs no external CDN or asset directory. See `THIRD_PARTY.md` for licenses of bundled assets (Hugeicons icons, Inter and JetBrains Mono fonts, htmx) and Go dependencies. |
| 199 | |
| 200 | ## Data and Backups |
| 201 | |
| 202 | ``` |
| 203 | data/ |
| 204 | ├── polar.db # SQLite database (WAL mode) |
| 205 | └── repos/ # Bare Git repositories, e.g. repos/rin/demo.git |
| 206 | ``` |
| 207 | |
| 208 | Safe backup while Polar is running: |
| 209 | |
| 210 | ```bash |
| 211 | sqlite3 data/polar.db "VACUUM INTO 'data/polar-backup.db';" |
| 212 | tar -czf polar-backup-$(date +%F).tar.gz data/polar-backup.db data/repos/ |
| 213 | rm data/polar-backup.db |
| 214 | ``` |
| 215 | |
| 216 | To restore, stop Polar, replace `data/polar.db` and `data/repos/` with the backup contents, and start it again. |
| 217 | |
| 218 | ## Health Check |
| 219 | |
| 220 | `GET /healthz` returns JSON with the database status — handy for Docker health checks and load balancers. |