# Vectrum
[vectrum.cc](https://vectrum.cc) · A self-hosted, multi-user GitHub built with Next.js. Repositories are real bare
git repositories on disk, and you push to them with plain git over **SSH** or
**HTTPS**.
## Features
- **Accounts**: sign up, sign in, profiles, password change (scrypt-hashed passwords, database-backed sessions)
- **Repositories**: public or private, rename, delete, optional README on creation
- **Collaborators**: give other users push access to a repository
- **Git over SSH**: built-in SSH server (default port 2222). Add public keys under *Settings → SSH keys*
- **Git over HTTPS**: smart HTTP protocol (v0 and v2). Authenticate with API tokens
- **Container registry**: every repository is also a Docker/OCI registry, like ghcr.io. `docker push <host>/<owner>/<repo>` (or `<host>/<owner>/<repo>/<name>` for more images per repository) with the same visibility, collaborators and API tokens as the code. Multi-platform images, cross-repository blob mounts, chunked uploads and the referrers API (signatures, SBOMs) are supported. Images are listed under the repository's *Packages* tab, where writers can delete versions
- **API tokens**: read-only or read & write, for all repositories or selected ones, with expiry. Only a hash is stored
- **Web editor**: CodeMirror with syntax highlighting and markdown preview. Create, edit, rename, upload and delete files, committing to the current branch or a new one
- **Code browsing**: file tree, highlighting (Shiki), rendered READMEs (sanitized HTML), raw files, image previews
- **History**: commit log per branch or file, commit diffs, branches overview, recent-activity dashboard
- **Issues as a Linear-style project tracker**, per repository:
- Workflow statuses (Backlog → Todo → In Progress → In Review → Done / Canceled), priorities, assignees, labels, estimates and due dates
- List view grouped by status, priority or assignee, and a kanban board with drag and drop
- Filters (assignee, priority, label, "My issues"), scopes (All / Active / Backlog) and orderings, kept in the URL so views can be shared
- Quick-create dialog and keyboard shortcuts: `C` new issue, `J`/`K` move, `Enter` open, `S`/`P`/`A`/`L` set status, priority, assignee, labels
- Inline editing of every property, with an activity feed of changes next to the markdown comments
- Anyone who can read a repository can open issues and comment, but only collaborators and the owner can change an issue (title, description, status and all other properties). Only an issue's creator and the repository owner can delete it. Labels are managed under *Settings → Labels* or created from the label menu
- Light and dark mode
## Getting started
Requires Node.js 22.5+ (for the built-in `node:sqlite`) and `git` on the `PATH`.
```bash
bun install
bun dev
```
Open http://localhost:3000, sign up, and create a repository.
### Pushing over SSH
1. Add your public key (`~/.ssh/id_ed25519.pub`) under **Settings → SSH keys**.
2. Check the connection: `ssh -T -p 2222 git@localhost`
3. Push:
```bash
git remote add origin ssh://git@localhost:2222/<you>/<repo>.git
git push -u origin main
```
### Pushing over HTTPS
Generate a token under **Settings → API tokens** and use it as the password
when git asks (any username works):
```bash
git clone http://localhost:3000/<you>/<repo>.git
# Username: <you> Password: vec_…
```
Anonymous users can clone public repositories without a token.
### Pushing container images
Log in with an API token as the password (read & write to push), then tag and
push images under a repository's name. Image names are lowercase:
```bash
docker login localhost:3000 -u <you> # Password: vec_…
docker build -t localhost:3000/<you>/<repo>:latest .
docker push localhost:3000/<you>/<repo>:latest
docker push localhost:3000/<you>/<repo>/worker:1.0 # more images in one repository
```
Anyone who can read the repository can pull; anonymous users can pull images of
public repositories. The repository has to exist before you push. Docker only
talks plain HTTP to `localhost`; anywhere else, serve Vectrum over HTTPS (or add
the host to Docker's `insecure-registries`). A reverse proxy in front of
Vectrum must pass large request bodies through (e.g. nginx `client_max_body_size 0;`).
## Configuration
| Variable | Default | Purpose |
| ----------------- | ----------- | --------------------------------------------------------- |
| `DATA_ROOT` | `./data` | Where repositories, the database and the SSH host key live |
| `SSH_PORT` | `2222` | Port of the built-in SSH server |
| `SSH_LISTEN_HOST` | `0.0.0.0` | Interface the SSH server binds to |
| `SSH_HOST` | request host | Host name shown in SSH clone URLs, e.g. `vectrum.cc` |
| `SSH_DISABLED` | — | Set to `1` to not start the SSH server |
Data layout:
```
data/
app.db users, sessions, keys, tokens, repos, issues (SQLite)
ssh_host_ed25519_key generated on first start
repos/<owner>/<repo>.git bare git repositories
registry/blobs/sha256/… container image layers, shared by all repositories
registry/uploads/ image uploads in progress
```
Image manifests and tags are stored in the database. A blob is deleted once no
manifest of any repository references it (blobs pushed in the last 24 hours are
kept so pushes in progress don't lose their layers).
## Security notes
- Serve the web UI over HTTPS if it's reachable beyond localhost. Session cookies become `Secure` when `X-Forwarded-Proto: https` is set.
- Private repositories show a 404 page to anyone without access, so their existence isn't revealed.
- Raw files are served with `nosniff` and a sandboxing CSP, so repository HTML/SVG can't run scripts in the app's origin.
## Layout
- `lib/db.ts`: SQLite schema and connection
- `lib/users.ts`, `lib/auth.ts`: accounts, password hashing, sessions
- `lib/repos.ts`: repository records, permissions (`repoAccess`), collaborators
- `lib/issues.ts`, `lib/issue-meta.ts`: issue tracker data and its shared vocabulary (statuses, priorities); UI in `components/issues/`
- `lib/keys.ts`, `lib/tokens.ts`: SSH keys and API tokens
- `lib/git.ts`, `lib/git-write.ts`: reading from and committing to bare repositories
- `lib/git-http.ts`, `lib/ssh-server.ts`: git transports (started from `instrumentation.ts`)
- `lib/registry.ts`, `lib/registry-http.ts`: container registry storage and the OCI distribution API, served under `/v2`
- `app/actions/*`: server actions, each re-checking the session and permissions