~ober/jerboa-gitsite
Imported from ~/mine/jerboa-gitsite
about
# gitsite
A minimal SourceHut-style git forge built in Jerboa.
## Features
- **Git hosting**: Public, unlisted, and private repositories
- **Web UI**: Browse repositories, view commits, trees, and blobs
- **Git over HTTPS**: Clone and fetch via HTTP(S) with per-IP rate limiting
- **Git over SSH**: Push via SSH with public key authentication
- **CI/CD**: SourceHut-style build manifests with job queue and artifact collection
- **Minimal authentication**: Register/login with Argon2id password hashing
- **API token management**: Generate and revoke tokens from `/settings/tokens`
- **Security headers**: CSP, HSTS, X-Content-Type-Options, X-Frame-Options, etc. on every response
- **Session/token garbage collection**: Periodic sweep of expired records
## Quick Start
### Build Release Bundle (Production)
```bash
make binary
```
Creates `dist/gitsite` with native runtime libraries in `dist/lib/`.
The binary has these subcommands:
`serve`, `ssh-auth`, `buildd`, `add-user`, `migrate`.
No Jerboa install, SQLite3 library, or Scheme interpreter is required on the
target machine. Run the binary with `JERBOA_HOME` pointing at the bundled
`dist` directory so the packaged native runtime can be loaded.
### Create First User
```bash
dist/gitsite add-user <username> <email> <password>
```
### Run (Development)
```bash
make run
```
The server starts on `http://127.0.0.1:8080` with `mode development`,
registration open, and TLS disabled.
### Prerequisites for Development
- Jerboa (>= 0.2.0)
- Git (>= 2.28)
- OpenSSH (for SSH push support)
- sendmail or compatible MTA (for build notifications)
## Configuration
Edit `etc/gitsite.sexp`:
```scheme
((listen-port 8080)
(listen-address "127.0.0.1")
(var-root "/srv/gitsite/var")
(public-url "https://git.example.org")
(registration "open")
(session-secret "change-this-to-a-random-secret-in-production")
(tls-cert "/etc/ssl/private/fullchain.pem")
(tls-key "/etc/ssl/private/key.pem")
(builds-mode "async")
(buildd-workers 2)
(mode development))
```
- `listen-port`: HTTP/HTTPS port (default: 8080)
- `listen-address`: Bind address (default: 127.0.0.1)
- `var-root`: Data directory (default: var)
- `public-url`: Public URL for hooks and links
- `registration`: "open" or "closed" (default: closed)
- `session-secret`: Random secret used to sign session cookies — **must** be set
in production
- `tls-cert` / `tls-key`: PEM cert + key paths. When **both** are set the server
terminates TLS in-process (`httpsd-start`), sends
`Strict-Transport-Security`, and marks session cookies `Secure`. When absent,
plain HTTP is served (dev / behind-proxy mode).
- `builds-mode`: `"async"` (default) spawns a separate `gitsite buildd` process;
`"sync"` runs builds inline during push.
- `buildd-workers`: Number of concurrent build workers (default: 2).
- `mode`: `development` (default) or `production`. In `production`, `tls-cert`
and `tls-key` are required and `registration` is forced to `closed`.
## Deployment
### Production Setup
1. **Create dedicated user**:
```bash
sudo useradd -r -m -d /srv/gitsite -s /bin/bash gitsite
sudo useradd -r -m -d /srv/gitsite-build -s /bin/bash gitsite-build
```
2. **Install release bundle**:
```bash
sudo install -d -m 755 /usr/local/libexec/gitsite
sudo install -m 755 dist/gitsite /usr/local/libexec/gitsite/
sudo cp -R dist/lib /usr/local/libexec/gitsite/
sudo ln -sf /usr/local/libexec/gitsite/gitsite /usr/local/bin/gitsite
```
3. **Create directories**:
```bash
sudo mkdir -p /srv/gitsite/{var,etc}
sudo chown -R gitsite:gitsite /srv/gitsite
sudo chown gitsite-build:gitsite-build /srv/gitsite-build
```
4. **Configure**:
```bash
sudo -u gitsite cp etc/gitsite.sexp /srv/gitsite/etc/
# Edit /srv/gitsite/etc/gitsite.sexp for production
# Set tls-cert/tls-key + (mode production) to terminate TLS in-process,
# or leave TLS unset and terminate at a reverse proxy.
# Set session-secret to a long random string.
```
5. **Reverse proxy** (nginx example, only if not using in-process TLS):
```nginx
server {
listen 443 ssl http2;
server_name git.example.org;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
```
6. **Systemd service**:
```ini
[Unit]
Description=gitsite git forge
After=network.target
[Service]
Type=simple
User=gitsite
WorkingDirectory=/srv/gitsite
Environment=JERBOA_HOME=/usr/local/libexec/gitsite
Environment=JERBOA_GIT_LIB_PATH=/usr/local/libexec/gitsite/lib/libjerboa_git_shim.so
ExecStart=/usr/local/bin/gitsite serve
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
### SSH Push Setup
1. **Create SSH user**:
```bash
sudo useradd -r -m -d /srv/gitsite-ssh -s /usr/local/bin/gitsite ssh-auth gitsite-ssh
```
2. **Configure sshd** (add to /etc/ssh/sshd_config):
```
Match User gitsite-ssh
AuthorizedKeysFile /srv/gitsite/var/ssh/authorized_keys
ForceCommand /usr/local/bin/gitsite ssh-auth
PermitTTY no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
```
3. **Restart sshd**:
```bash
sudo systemctl restart sshd
```
4. **Add SSH keys** via web UI at `/settings/keys`
### Build Daemon
For asynchronous build execution:
```bash
sudo -u gitsite \
JERBOA_HOME=/usr/local/libexec/gitsite \
JERBOA_GIT_LIB_PATH=/usr/local/libexec/gitsite/lib/libjerboa_git_shim.so \
/usr/local/bin/gitsite buildd &
```
Or create a separate systemd service:
```ini
[Unit]
Description=gitsite build daemon
After=network.target gitsite.service
[Service]
Type=simple
User=gitsite
WorkingDirectory=/srv/gitsite
ExecStart=/usr/local/bin/gitsite buildd
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
### FreeBSD Jail Setup
Recommended layout on FreeBSD 14+ with a dedicated jail:
1. **Create the jail and ZFS dataset**:
```bash
sudo zfs create -o mountpoint=/jails/gitsite tank/jails/gitsite
# use bsdinstall or a template to populate the jail
```
2. **Install runtime dependencies inside the jail**:
```bash
pkg install git
```
3. **Create users**:
```bash
pw useradd -n gitsite -d /srv/gitsite -s /bin/sh -m
pw useradd -n gitsite-build -d /srv/gitsite-build -s /bin/sh -m
```
4. **Deploy the binary and service files**:
```bash
install -m 755 dist/gitsite /usr/local/bin/gitsite
install -m 555 files/rc.d/gitsite /usr/local/etc/rc.d/gitsite
install -m 644 files/newsyslog.d/gitsite /usr/local/etc/newsyslog.conf.d/gitsite
mkdir -p /srv/gitsite/{var,etc}
chown -R gitsite:gitsite /srv/gitsite
chown gitsite-build:gitsite-build /srv/gitsite-build
cp etc/gitsite.sexp /srv/gitsite/etc/
# edit /srv/gitsite/etc/gitsite.sexp: set session-secret, tls-cert/tls-key, mode production
```
5. **Configure sshd for SSH push** (add to `/etc/ssh/sshd_config` inside the jail):
```
Match User git
AuthorizedKeysFile /srv/gitsite/var/ssh/authorized_keys
ForceCommand /usr/local/bin/gitsite ssh-auth
PermitTTY no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
```
6. **Enable and start services**:
```bash
sysrc gitsite_enable=YES
service gitsite start
service sshd restart
```
7. **Log rotation**:
The included `files/newsyslog.d/gitsite` rotates `/srv/gitsite/var/gitsite.log`.
### Backup and Restore
All persistent state lives under `var-root` (`/srv/gitsite/var` by default):
- `gitsite.db` — SQLite database (users, repos, sessions, tokens, jobs)
- `repos/` — bare git repositories
- `builds/` — build logs
- `artifacts/` — build artifacts
- `spool/` — async build queue
Back up the entire `var` directory while the server and buildd are stopped:
```bash
service gitsite stop
service gitsite_buildd stop 2>/dev/null || true
tar czf gitsite-var-$(date +%Y%m%d).tar.gz -C /srv/gitsite var
service gitsite start
```
Restore by stopping services, extracting the archive, and fixing ownership:
```bash
service gitsite stop
tar xzf gitsite-var-YYYYMMDD.tar.gz -C /srv/gitsite
chown -R gitsite:gitsite /srv/gitsite/var
service gitsite start
```
### Upgrade Path
1. Build the new binary: `make binary`
2. Stop `gitsite serve` and `gitsite buildd`
3. Run migrations if the release notes require it: `gitsite migrate`
4. Install the new binary: `install -m 755 dist/gitsite /usr/local/bin/gitsite`
5. Start services again
Database migrations are idempotent; `gitsite migrate` only creates tables that do not already exist.
## Usage
### Create Repository
1. Register/login at `/register` or `/login`
2. Click "new repo" in the navigation
3. Fill in name, description, and visibility
4. Click "create"
### Clone Repository
**HTTPS** (read-only for public/unlisted):
```bash
git clone http://git.example.org/~username/repo.git
```
**SSH** (requires SSH key):
```bash
git clone git@git.example.org:~username/repo.git
```
### Push Changes
**SSH** (recommended):
```bash
git remote set-url origin git@git.example.org:~username/repo.git
git push origin master
```
### Build Manifest
Create `.build.yml` in your repository:
```yaml
image: alpine/latest
packages:
- build-base
tasks:
- build: |
cd repo
make
- test: |
cd repo
make test
artifacts:
- repo/dist/app.bin
```
Builds are triggered automatically on push or manually via the web UI.
## Architecture
- **Web server**: `(std net httpd)` with custom routing
- **Database**: SQLite via `(std db sqlite)` (jsqlite pure-Scheme implementation)
- **Authentication**: Argon2id password hashing, server-side session records
- **Git operations**: System `git` binary for wire protocol, libgit2 for browsing (via jerboa-git Rust shim)
- **Builds**: Async file-spool queue; `gitsite buildd` workers claim and run jobs out-of-process
- **SSH**: System OpenSSH with forced-command binary (`gitsite ssh-auth`)
- **TLS**: In-process termination via `httpsd-start` (optional, config-driven)
## Limitations
- No merge requests or code review
- No issue tracker
- No webhooks
- No LFS support
- No syntax highlighting
- Builds run as the `gitsite-build` user with no further sandboxing; runaway builds must be killed manually
- HTTPS push not supported (use SSH); the HTTP stack decodes request bodies as UTF-8 strings
- jsqlite keeps the database image in memory, so only one process may write to a given `gitsite.db` at a time. Run `gitsite add-user` and `gitsite migrate` while the server is stopped
- Rate limiting on `/git/*` is 30 requests per 60 seconds per IP (sliding window); tunable at build time
- No organizations or teams
## License
MIT
recent commits
- gitsite: serve usable styled layout 7dc0be1 Jaime Fournier
- gitsite: add FreeBSD buildd service 1b17fee Jaime Fournier
- gitsite: render page SXML once fbd5f25 Jaime Fournier
- gitsite: fix FreeBSD rc service management 5e5cbd5 Jaime Fournier
- gitsite: daemonize FreeBSD service b92b62d Jaime Fournier
- gitsite: set bundle environment in rc script 94915cd Jaime Fournier
- Merge main and reconcile repository policies c9e0097 user
- gitsite: use portable HTTP request body API 9a16735 Jaime Fournier
- gitsite: verify packaged lifecycle 0d0ae94 Jaime Fournier
- Document local mine repository authority 92b6833 Jaime Fournier