Vectrum
Sign In
91 lines · 6.1 KB
# Installation

Requires Node.js 22+ (24.7+ for post-quantum SSH key exchange) and `git` on the `PATH`.

```bash
bun install   # also downloads the PocketBase server into apps/web/bin/
bun dev
```

Open http://localhost:3000, sign up, and create a repository.

The web app is `apps/web`; paths below are relative to it. Commands run from the repository root or from `apps/web`.

The data lives in [PocketBase](https://pocketbase.io). Vectrum starts `bin/pocketbase` itself on `127.0.0.1:8090`, with its data in `data/pb_data` and the schema from `pb_migrations/`; its superuser login is generated into `data/pocketbase-superuser.json` (also good for the PocketBase dashboard at http://127.0.0.1:8090/_/). Run `bun run pocketbase:install` if the binary is missing. To use a PocketBase server of your own instead, set `PB_URL` and the superuser credentials; it needs the migrations in `pb_migrations/` (`pocketbase serve --migrationsDir pb_migrations`).

## Running with Docker

```bash
docker compose up -d --build
```

The image runs the web UI on port 3000 and the SSH server on port 2222, with all data in the `vectrum-data` volume (mounted at `/data`). Settings go under `environment:` in `docker-compose.yml` (see [Configuration](#configuration)). To close registration, set `SIGNUPS_DISABLED: "1"` before you deploy, and create accounts from the command line instead:

```bash
docker compose exec -it vectrum node scripts/create-user.mjs <username> <email> [name]
```

It asks for the password (or reads `VECTRUM_PASSWORD`). Outside Docker, run `node scripts/create-user.mjs` the same way from `apps/web` while Vectrum runs. Clone URLs show `SSH_PORT`, so publish SSH on the same port on the host, and set `SSH_HOST` to your domain. Put a TLS reverse proxy in front of port 3000 if the server is reachable from the internet, and set `PUBLIC_URL` to its address.

To build the image without Compose, run `docker build -f apps/web/Dockerfile -t vectrum .` from the repository root.

## Configuration

| Variable          | Default     | Purpose                                                   |
| ----------------- | ----------- | --------------------------------------------------------- |
| `PUBLIC_URL`      | request host | Canonical address, e.g. `https://vectrum.cc`. Used for clone URLs; `https` also makes session cookies `Secure`. Set it in production |
| `SECURE_COOKIES`  | —           | Set to `1` to mark session cookies `Secure` without `PUBLIC_URL` |
| `DATA_ROOT`       | `./data`    | Where repositories, the database and the SSH host key live |
| `PB_PORT`         | `8090`      | Port of the PocketBase server Vectrum runs                |
| `PB_URL`          | —           | Use this PocketBase server instead of running one          |
| `PB_SUPERUSER_EMAIL`, `PB_SUPERUSER_PASSWORD` | generated | Superuser login; required with `PB_URL` |
| `PB_BINARY`       | `bin/pocketbase` | PocketBase executable to run                         |
| `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                    |
| `SIGNUPS_DISABLED` | —          | Set to `1` to close registration (add users with `scripts/create-user.mjs`) |
| `WEBHOOKS_ALLOW_PRIVATE` | —    | Set to `1` to let webhooks reach private and local addresses (a trusted, single-team server) |
| `SMTP_HOST`       | —           | SMTP server to send email through (see [Email](#email)); without it, Vectrum sends none |
| `SMTP_PORT`       | `587`       | Its port                                                  |
| `SMTP_SECURE`     | on for port 465 | `1` for TLS from the start (port 465); otherwise the connection upgrades with STARTTLS when the server offers it |
| `SMTP_USER`, `SMTP_PASSWORD` | — | Login for the SMTP server, if it needs one              |
| `MAIL_FROM`       | `SMTP_USER` | Sender, e.g. `Vectrum <noreply@vectrum.cc>`               |

### Email

With `SMTP_HOST` set, Vectrum sends email: a link to confirm the address after signing up or changing it, a link to reset a forgotten password (*Forgot your password?* on the sign-in page), organization invitations, being added as a collaborator, and notices when an account's password or email address changes. Notifications only go to confirmed addresses. Set `PUBLIC_URL` too, as the links in emails point there.

Outside Docker, put the settings in `apps/web/.env` (see `apps/web/.env.example`), which Next.js reads on start:

```bash
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=vectrum@example.com
SMTP_PASSWORD=…
MAIL_FROM="Vectrum <vectrum@example.com>"
PUBLIC_URL=https://vectrum.example.com
```

With Docker Compose, add them under `environment:`, or put them in a `.env` file next to `docker-compose.yml` and list it under `env_file:`. Failures to send are logged.

Data layout:

```
data/
  pb_data/                      PocketBase: users, keys, tokens, repos, issues, releases, workflow runs and logs
  pocketbase-superuser.json     the generated PocketBase superuser login
  ssh_host_ed25519_key          generated on first start
  hooks/                        server-side git hooks for every push, rewritten on start, and the socket they ask the server on
  repos/<owner>/<repo>.git      bare git repositories
  wikis/<repo>.git              each repository's wiki, named by its record id
  registry.db                   container image manifests, tags and blob records (SQLite)
  registry/blobs/sha256/…       container image layers, shared by all repositories
  registry/uploads/             image uploads in progress
  releases/<repo>/<release>/…   release files, named by their record id
```

The registry keeps its records in SQLite rather than PocketBase so that storing a
blob and pruning unused ones never interleave. 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).