# Install

Install basalt and guard the tables your agent may change on your own Postgres.

Use this page to put BasaltDB on a Postgres you own and choose the tables your agent may touch. To try it first on a sample database, use the [Quickstart](/start/quickstart/).

You need:

- macOS or Linux, with `sudo`.
- Go 1.26 or newer, and `git`.
- A Postgres you own: Supabase, RDS, or one you host yourself. You need its admin connection string, the one for the `postgres` user.
- A browser on the same machine, with a passkey (Touch ID, a security key, or a password manager that stores passkeys).
- An agent that supports MCP. See [Connect an agent](/start/connect-agent/).

The admin connection string is used once, by `basalt init`, typed into its hidden prompt. It is never stored. Do not export it or put it on a command line.

## 1. Build and install basalt

There is no release download yet. You build `basalt` from source. You need Go 1.26 or newer and `git`.

```sh
git clone https://github.com/BasaltDBHq/BasaltDB.git
cd BasaltDB
go build -o basalt ./cmd/basalt
sudo install -d -o root -m 0755 /usr/local/libexec
sudo install -o root -m 0755 basalt /usr/local/libexec/basalt
sudo mkdir -p /usr/local/bin
sudo ln -sf /usr/local/libexec/basalt /usr/local/bin/basalt
basalt version
```

`basalt version` prints the version.

The engine runs as its own OS user from the real file behind the link, `/usr/local/libexec/basalt`. Whoever can change that file can run code with the agent's credential. So `basalt init` and `basalt service install` check that the file and every directory above it belong to root and are not writable by group or others. If they do not, they stop before changing anything:

```text
basalt: /usr/local/bin is owned by uid 501, not root: the engine service runs /usr/local/bin/basalt as the engine's OS user, so only root may be able to change it or any directory above it. Install basalt in a root-owned directory and run that copy: ... (or run basalt init with --no-service)
```

That is why the commands above put the binary in `/usr/local/libexec` and only link it from `/usr/local/bin`. Do not install it in your home directory.

## 2. Get the admin connection string

**Supabase.** In the dashboard press **Connect** and copy the string that matches your network.

| String                                                                          | Use it                             |
| ------------------------------------------------------------------------------- | ---------------------------------- |
| Session pooler, port 5432 (`postgres.<ref>@aws-0-<region>.pooler.supabase.com`) | If unsure. Works on IPv4 and IPv6. |
| Direct connection (`db.<ref>.supabase.co`)                                      | Only if your network has IPv6.     |

Fill in `[PASSWORD]` with your database password. URL-encode characters such as `@`, `/`, `:` and `#`. Add `?sslmode=require` at the end.

**Other Postgres.** Use `postgresql://postgres:PASSWORD@HOST:5432/DBNAME?sslmode=require`. See [Hosts](/guides/hosts/) for RDS and self-hosted notes.

## 3. Guard your tables

Name the tables the agent may use. `--write` lists tables it may read and change. `--read` lists tables it may only read. Both take a comma-separated list of `schema.table`.

```sh
basalt init --agent my_bot \
  --write public.leads --read public.lead_notes
```

`my_bot` is the agent's name. Pick any name. Init asks, in order:

1. `Admin connection string (used once, never stored):` Paste the string from step 2. Nothing is echoed.
2. Only if a table has row-level security on and no policy for the agent: `... hides its rows with row-level security. Let my_bot see all <n> rows of this table? [y/N]`. Answer `y` to add a policy for the agent role only. With `n` the agent reads zero rows. Supabase can turn row-level security on for every new table. Init asks before it shows the SQL.
3. It prints the setup SQL, then `Run this SQL as postgres? [y/N]`. Type `y`.
4. `basalt: guard installed; agent role basalt_agent_my_bot`
5. It prints a `http://localhost:8787/passkeys/register#...` link. Open it, press the button, confirm with your passkey, copy the `basalt-passkey.1....` code the page shows, and paste it at `Paste the code the page shows:`. Then `basalt: passkey registered for localhost`.
6. `Install the engine as a background service? ... [Y/n]` Press Enter. Init runs one `sudo`, installs and starts the service, and checks that the engine answers.

Port 8787 must be free while init waits for the passkey, and afterwards for the engine. `lsof -nP -iTCP:8787 -sTCP:LISTEN` shows what holds it.

## 4. Check that it works

```sh
basalt status --agent my_bot
curl -s http://localhost:8787/healthz
```

`basalt status` reports the engine, the guard version and the passkey, and lists any problem with what to do about it. `curl` prints `ok`.

## 5. Connect your agent

Next: [Connect an agent](/start/connect-agent/). The agent starts `basalt mcp --agent my_bot`.

## If something is off

- `tables ... not found in database`: the tables in `--write` or `--read` do not exist in the database this connection string points at. Init checks first and changes nothing.
- The agent reads zero rows from a table that has rows: row-level security is on with no policy for the agent. Run step 3 again and answer `y` to the row-level security question, or add `--agent-rls=all-rows`.
- `/mcp` shows the server failed, or the connection closes at once: the engine admits only the OS user that ran `init`. Start the agent as that user.
- `guard schema vN is older than this build's vM`: run `basalt upgrade --agent my_bot`. See [Upgrade](/guides/upgrade/).
- No `sudo`, or you do not want a service: add `--no-service` to `basalt init`. It prints the one `basalt engine --config ...` command to run in the foreground as you. Writes are still guarded in Postgres, but your user can read the agent's credential file.

## Remove it

```sh
basalt uninstall
sudo basalt service uninstall
```

`basalt uninstall` asks for the admin connection string, then drops the `basalt_meta` schema, the guard's triggers and policies, and the `basalt_*` roles. `service uninstall` stops and removes the engine service. See [Remove BasaltDB](/guides/remove/).
