λx.xDocs← app

Self-Hosting

Vinculum runs on your own machine with three commands. Your data stays in a PostgreSQL database you own.

The three lines

bash
curl -fsSL https://vinculum.run/install/server | sh
vinculum init                 # guided setup, ~2 min
vinculum run                  # listens on 127.0.0.1:31415

The first line installs uv if you don't have it, then installs Vinculum as a uv tool. It downloads the package, checks its SHA-256 and stops if the check cannot run.

It installs into ~/.local/bin, which a brand-new account may not have on its PATH until you log in again. If the next line says vinculum: command not found, run source ~/.local/bin/env or open a new terminal, then carry on.

vinculum init is the setup wizard. It applies the schema and migrations, seeds a default project, picks your dashboard username and writes your config to ~/.config/vinculum/.env. The default database is a built-in PostgreSQL 16 with pgvector. There is no database to go find.

vinculum run starts the server and the dashboard on 127.0.0.1:31415. It also starts the built-in database. Open http://127.0.0.1:31415/login. On a single-user install it signs you straight in.

Requirements

  • A Linux or macOS shell with curl and sh for the installer.
  • Python 3.12 or newer. The package won’t install on anything older.
  • Port 31415 free, and about 1 GB of free disk. The doctor fails under 1 GB.

That is the whole list. You do not need Docker, Node, a repository checkout, a domain, a TLS certificate or a GitHub OAuth app. vinculum init generates the JWT secret, turns on single-user mode and binds the server to 127.0.0.1.

Check your install: vinculum doctor

vinculum doctor runs a set of independent checks and reports each one as pass, info, warn or fail, or says it couldn't check. A check that breaks reports a failure and the rest still run.

bash
vinculum doctor

Reading the result

In a terminal you get one line per check: a mark, the check name and a message. The marks are ✓ pass, · info, ! warn, ✗ fail and ? couldn't check. Set NO_COLOR and they become [PASS], [INFO], [WARN], [FAIL] and [CANT]. Every line that isn't a pass carries an arrow line under it with the fix. A summary closes the report: N passed, N info, N warnings, N failures, N couldn't check.

text
$ NO_COLOR=1 vinculum doctor
vinculum doctor
  [PASS] python_version         Python 3.12
  [PASS] dependencies           all 6 required packages importable
  ...
  [FAIL] migrations             2 migration(s) pending (at abc123, head is def456)
    → run `vinculum migrate` — this database is behind the code that is about to run against it. ...
  [WARN] port_available         port 31415 on 127.0.0.1 is in use (Address already in use)
    → stop the process holding 31415, or set VINCULUM_PORT to a free port

14 passed, 3 info, 1 warnings, 1 failures, 0 couldn't check

Info is advice, never a problem: a correct install can show several and still exit 0. The doctor exits 1 when any check warns or fails, and 2 when any check couldn't run, because a check that can't see is the one that hides a risk. When the output is piped, or you pass --json, it prints the same results as JSON instead of text.

What it checks

CheckIt fails or warns whenWhat to do
python_versionPython is older than 3.12, the version the package itself requires. Install Python 3.12 or newer and run again with that interpreter.
dependenciesA required package will not import. Reinstall Vinculum. The message names what is missing.
server_importableThe server module will not import. Reinstall Vinculum and read the dependencies line.
config_fileWarns when ~/.config/vinculum/.env is missing. Fails when the file names no database: neither VINCULUM_DATABASE_URL (your own Postgres) nor VINCULUM_EMBEDDED_PGDATA (the built-in one, which vinculum init sets up by default). Run vinculum init. For a file that names no database, run vinculum init --force to write it again.
database_urlVINCULUM_DATABASE_URL is unset or is not a postgres:// or postgresql:// URL. Run vinculum init, or export a URL like postgresql://user:pass@host:5432/vinculum.
postgres_reachableThe doctor cannot connect within 3 seconds. Check that Postgres is running and the URL's credentials are right.
postgres_versionThe server is older than PostgreSQL 15. Point VINCULUM_DATABASE_URL at PostgreSQL 15 or newer. 17 is the recommended version.
pgvectorThe vector extension is not installed in this database. If the server has pgvector, run CREATE EXTENSION vector; in the database. If it does not, install the pgvector package, then run vinculum doctor --enable-pgvector.
schemaOne of the four core tables is missing. Run vinculum migrate.
migrationsMigrations are pending, or the migration history has more than one head. Run vinculum migrate. The doctor's own hint says not to apply schema.sql to an existing install.
isolation_rolesOne of the six vinculum_* roles is missing. Background jobs fail without them. Run vinculum init again with a superuser or CREATEROLE connection. It is safe to repeat.
port_availableWarns when the port is in use. If Vinculum is already running, that is expected. Stop the other process, or set VINCULUM_PORT to a free port.
disk_spaceLess than 1 GB is free. Free some disk space.
voyage_api_keyInfo when no key is set. Fails when a key is set but looks malformed. The key is optional and enables semantic search. Set VINCULUM_VOYAGE_API_KEY or paste a key in Settings → Connections. Skip it if you don't want semantic search.
anthropic_api_keyInfo when VINCULUM_ANTHROPIC_API_KEY is unset. The key is optional and enables entry classification and thread titles. Set the key, or leave it.
cc_session_bindingInfo when no Claude Code MCP config is found. Warns when the config doesn't bind your sessions to Vinculum, or can't be parsed. Fails when it sets the session header on an http server, where it can't expand. On a machine without Claude Code, ignore it. Otherwise run vinculum config --client=claude-code --write -y.

Configuration and safety checks

After the install checks, eight more look for risky setups you probably didn't choose, rather than broken ones.

CheckIt warns whenWhat to do
grunt_sandboxA runner runs grunt jobs with no sandbox, so model-written code runs on the host unconfined. Info when no runner is live.Set VINCULUM_SANDBOX=enforce on that runner and install a sandbox backend (bubblewrap on Linux).
relay_devicesThe relay is on and a paired device isn't key-pinned, or a browser approval has expired. Passes when the relay is off.Sign each device in again, which binds its key, and revoke expired approvals in Settings → Devices.
remote_relay_reachableRemote access is on, but the relay doesn't answer its health check, so a phone would fail to connect. Info when remote access is off or no relay address is set.Check the relay named in the message is running and reachable from this machine.
mcp_targetThe MCP URL your clients use points somewhere other than this server, such as vinculum.run, so writes go to the wrong place.Point the MCP URL at this instance, http://127.0.0.1:31415/mcp, in that client's config.
bind_exposureThe server listens on 0.0.0.0 or a public interface with no auth token in front of it.Set VINCULUM_HOST=127.0.0.1, or set VINCULUM_AUTH_TOKEN to make a public bind deliberate.
secret_exposureAn env file holding credentials can be read by other users on the box. It names the file and the keys, never the values.chmod 600 the file it names.
claude_hooksA Claude Code hook points at a Vinculum program that is missing or can't run, so it fails in every session, every time it fires. Info when no Vinculum hooks are set up.Reinstall the hooks: the runner re-registers its own, and the shell hooks come from hooks/install-hook.sh. Or remove the stale entry from ~/.claude/settings.json.
wsl_runner_keepaliveA runner lives in a WSL distro and nothing keeps that distro up, so Windows stops it, and the runner with it, once nothing is attached. Info on machines with no such distro.Register a keepalive task that runs as you at sign-in. The warning prints the exact schtasks line for your distro.
runner_config_privateThe runner's config.json, which holds its token, can be read by other users, so anyone on the machine can act as this runner. Only the file's mode is read, never its contents. Info on Windows.chmod 600 the file it names.
versionThis install is older than the published package. It's the only check that uses the network, and it says so when you're offline.Re-run the install line, curl -fsSL https://vinculum.run/install/server | sh; it upgrades in place.
data_pathNever: it's always info, one line saying where your data lives.Nothing.
backupsBackups are configured but stale. Info on a fresh install with none set up.Check the backup job: a backup that stopped silently is worse than none. With none set up, schedule one (pg_dump or WAL archiving) once this instance holds data you'd miss.

Info lines are not problems

A correct install shows several info lines: the two optional API keys, a machine with no Claude Code, where your data lives, and no backups yet. None of them changes the exit code. You can leave them.

Your own Postgres

You can skip the built-in database. Set VINCULUM_DATABASE_URL before init and Vinculum uses that instead. It needs PostgreSQL 15 or newer, and 17 is recommended. Local installs, Neon, Supabase and RDS all work.

bash
export VINCULUM_DATABASE_URL=postgresql://user:pass@host:5432/vinculum
vinculum init

Connect as a superuser or a CREATEROLE role for init. It creates six vinculum_* roles, and a least-privilege managed role cannot create them. Run vinculum doctor afterward. Its isolation_roles check names any that are missing.

pgvector is optional on a Postgres you bring. Without it, semantic search is off and everything else works.

Reverse proxy

On a single-user install the server binds 127.0.0.1 and you never need a proxy. If you put a proxy on the same machine to reach it over HTTPS, point it at localhost:31415.

Caddy

caddyfile
your-domain.example.com {
    reverse_proxy localhost:31415
}

nginx

Turn proxy buffering off. The dashboard gets live updates over a streaming connection, and a buffering proxy holds them back.

nginx
server {
    listen 443 ssl;
    server_name your-domain.example.com;

    location / {
        proxy_pass http://127.0.0.1:31415;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_buffering off;
    }
}

Upgrading

Run the installer again. It reinstalls the tool over the old one. Then apply any new migrations and run the doctor:

bash
curl -fsSL https://vinculum.run/install/server | sh
vinculum migrate
vinculum doctor

vinculum migrate --dry-run lists pending migrations without applying them. The doctor's migrations check tells you if you skipped a step.

Connecting a client

From any project directory, connect Claude Code:

bash
vinculum config --client=claude-code --write -y

For claude.ai, run vinculum setup. It prints your MCP URL, a connector config to paste into claude.ai and the project instructions text, then pings the server. Start vinculum run first, or pass --skip-verify.

You know it works when the server answers at http://127.0.0.1:31415/healthz.

Running agents on your own machine

Starting agent sessions from Vinculum takes a paired vinculum-runner on the machine where they should run. The runner reaches your server over outbound HTTPS, so it needs no inbound port and no database connection. Sign in on that machine to connect it. With a screen, install the Vinculum app and sign in. Over SSH, install from your own server:

bash
curl -fsSL https://<your-server>/install | bash
~/.local/bin/vinculum-runner login

login prints a code. Approve it from any browser.

Where your data lives

The built-in database stores its files under ~/.local/share/vinculum/pgdata and listens on a private UNIX socket, so it opens no TCP port. Your settings live in ~/.config/vinculum/.env.

It is ordinary PostgreSQL, and your data is ordinary Postgres data. Setting VINCULUM_DATABASE_URL later moves you to any other Postgres. The built-in cluster is then ignored and never deleted.

The data is yours. On a self-hosted install it sits in your Postgres. Vinculum is AGPL v3, so the code cannot be taken away. If the project stops, your graph does not.