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.

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. 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

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). To close registration, set SIGNUPS_DISABLED: "1" before you deploy, and create accounts from the command line instead:

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

VariableDefaultPurpose
PUBLIC_URLrequest hostCanonical 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./dataWhere repositories, the database and the SSH host key live
PB_PORT8090Port of the PocketBase server Vectrum runs
PB_URL—Use this PocketBase server instead of running one
PB_SUPERUSER_EMAIL, PB_SUPERUSER_PASSWORDgeneratedSuperuser login; required with PB_URL
PB_BINARYbin/pocketbasePocketBase executable to run
SSH_PORT2222Port of the built-in SSH server
SSH_LISTEN_HOST0.0.0.0Interface the SSH server binds to
SSH_HOSTrequest hostHost 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); without it, Vectrum sends none
SMTP_PORT587Its port
SMTP_SECUREon for port 4651 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_FROMSMTP_USERSender, 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:

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).