# Configuration

The engine config file engine.json, every key and its default, plus the environment variables, paths and role names BasaltDB uses.

The engine reads one JSON file, `engine.json`. `basalt init` writes it and updates it each time you run init. You rarely edit it by hand. This page lists every key, so you know what a value does when you see it.

## Where the files live

| What                                | Linux                                       | macOS                                             |
| ----------------------------------- | ------------------------------------------- | ------------------------------------------------- |
| Engine config (default)             | `/etc/basalt/engine.json`                   | `/Library/Application Support/Basalt/engine.json` |
| Agent sockets and the person socket | `/run/basalt/`                              | `/Library/Application Support/Basalt/run/`        |
| State directory (`stateDir`)        | `/var/lib/basalt`                           | `/Library/Application Support/Basalt/state`       |
| The agent's connection string       | `<config dir>/agents/NAME.url`, mode `0600` | `<config dir>/agents/NAME.url`, mode `0600`       |

If you run init with `--config`, `--socket-dir` or `--credential-file`, the engine runs as you in the foreground. Init writes the config and the credential file itself, under your config directory, and prints the one `basalt engine --config ...` command. Without them, init offers to install the engine as a background service. The connection string with the agent's password goes into the credential file, never into `engine.json`.

## An example

```json
{
  "socketDir": "/run/basalt",
  "mode": "SERVICE",
  "personUids": [501],
  "pagesUrl": "http://localhost:8787",
  "limits": { "statementTimeoutMs": 5000, "readRowLimit": 1000 },
  "agents": [
    {
      "name": "demo_bot",
      "osUser": "imantumorang",
      "credentialFile": "/etc/basalt/agents/demo_bot.url",
      "initArgs": ["--agent=demo_bot", "--write=basalt_demo.leads,basalt_demo.lead_notes"]
    }
  ]
}
```

## Top-level keys

| Key             | Type    | Default                        | What it does                                                                                                                                                               |
| --------------- | ------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `socketDir`     | string  | the path in the table above    | Holds one socket per agent, `<socketDir>/<name>.sock`. Required.                                                                                                           |
| `agents`        | array   |                                | One entry per agent. At least one. See [Agent keys](#agent-keys).                                                                                                          |
| `limits`        | object  | see below                      | The read row limit and the time limit.                                                                                                                                     |
| `stateDir`      | string  | the path in the table above    | Where the engine keeps each agent's last verified ledger chain head (`<stateDir>/<agent>.head.json`, mode `0600`) It verifies from that head on start and every hour.      |
| `pagesUrl`      | string  | `http://localhost:8787`        | The base URL of the approval, undo and passkey pages. The page origin, and so the passkey's origin, is this URL. The older key `approvalPage` is still read, origin only.  |
| `pagesAddr`     | string  | `127.0.0.1:<port of pagesUrl>` | Where the engine serves those pages on loopback. `"off"` turns them off. A non-loopback address is refused. If the port is taken the engine refuses to start and names it. |
| `linkBudget`    | integer | `1800`                         | The longest long link, in characters, that the engine hands an agent as the way to open a page. Short links are never measured.                                            |
| `operatedBy`    | string  | `PERSON`                       | `PERSON` or `AGENT`: who runs machine steps on the engine's machine. See [A bot on its own computer](/guides/bot-own-computer/).                                           |
| `mode`          | string  | `SAME_USER`                    | `SERVICE` when init installed the background service, else `SAME_USER`. Only `SERVICE` opens the person socket.                                                            |
| `personUids`    | array   | none                           | OS user ids that may use the person socket. Init records yours. An empty list admits nobody.                                                                               |
| `directAddr`    | string  | off                            | A loopback address that a tunnel of yours forwards to, for the direct transport. `"off"` or empty is off. Only with the portal as `pagesUrl`.                              |
| `vault`         | string  | `OFF`                          | `ON` or `OFF`. Whether the engine dials out to the Vault for portal short links.                                                                                           |
| `vaultUrl`      | string  | the Vault                      | Overrides the Vault's address, for development and tests only: a `ws://` URL on a loopback address.                                                                        |
| `devLocalPages` | boolean | `false`                        | Lets an agent-operated engine use local pages, for development only. Every `personMessage` then carries a warning.                                                         |
| `pii`           | object  | see below                      | Sizes the classification dictionary. See [Column classes](#column-classes).                                                                                                |

With `operatedBy: AGENT`, `pagesUrl` is the hosted portal, because the loopback pages would belong to the bot's machine.

### Limits

| Key                  | Default | What it does                                                                                                                                                                                    |
| -------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `statementTimeoutMs` | `5000`  | The time limit on a `query`. The agent role has its own limit, `--statement-timeout` on `basalt init` (30 seconds by default), which also covers `preview_revert`. Between 1 ms and 10 minutes. |
| `readRowLimit`       | `1000`  | The most rows a `query` may return. Between 1 and 100,000. A query over it is `QUERY_TIMEOUT`.                                                                                                  |

Agents never set limits. The numbers in context: [Limits and defaults](/reference/limits/).

### Column classes

| Key                     | Default | What it does                                                                                                                   |
| ----------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `pii.maxMemoryMB`       | `512`   | The memory budget of the classification dictionary. The engine sets `GOMEMLIMIT` to twice this unless the environment sets it. |
| `pii.scanRowsPerSecond` | `20000` | How fast the engine scans your tables to build the classification dictionary.                                                  |

`basalt service install` writes the service's memory limits from `maxMemoryMB` at install time. After raising it, run `basalt service install` again with the same flags so the unit is rewritten.

## Agent keys

Each entry of `agents` has:

| Key              | What it does                                                                                                                                             |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | The agent's name: lowercase letters, digits and `_`. Its role is `<prefix>_agent_<name>`. `person` is reserved.                                          |
| `osUser`         | The OS user the agent runs as, the only one allowed on its socket. Defaults to you at init.                                                              |
| `credentialFile` | The file that holds the agent's connection string. Mode `0600`, owned by the engine's user.                                                              |
| `initArgs`       | The flags `basalt init` was run with, saved as strings. Every "run init again" step uses exactly these, plus the one change it is for.                   |
| `setup`          | A pending setup from your laptop: `setupId`, `kind`, `guardVersion`, `expiresAt`, `code` and `fingerprint`. Removed once the setup runs. Nothing secret. |

`initArgs` saves a flag only when its value is not the default, except `--agent` and `--os-user`. It never holds the admin connection string, `--database-url` or a one-shot flag such as `--yes`, `--dry-run` or `--rotate-password`. A value that is a connection URL or holds `password=` is refused.

## Environment variables

| Variable                    | Used by                                           | What it holds                                                                                                 |
| --------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `BASALT_ADMIN_DATABASE_URL` | `init`, `uninstall`, `passkey remove`, `classify` | Your admin connection string. Without it these commands ask at a hidden prompt. It is never written anywhere. |
| `BASALT_DATABASE_URL`       | `log`, `approve`, `revert`, `passkey`, `upgrade`  | An agent connection string, in same-user mode. A flag always wins over it.                                    |

`BASALT_ADMIN_DATABASE_URL` must be a `postgres://` or `postgresql://` URL. A keyword and value string is refused before init connects. `basalt init --remote` ignores it, on purpose.

## Names in your database

| What                     | Name                                                                               |
| ------------------------ | ---------------------------------------------------------------------------------- |
| Prefix                   | `basalt`, set with `--prefix`.                                                     |
| The guard's schema       | `basalt_meta`, with its owner role `<prefix>_guard`.                               |
| An agent's role          | `<prefix>_agent_<name>`. Through Supabase's pooler, `<prefix>_agent_<name>.<ref>`. |
| The group of agent roles | `<prefix>_agents`.                                                                 |

An agent role must be a plain role. Init refuses an existing role that is a superuser, can create roles or databases, bypasses row-level security, belongs to any role other than `<prefix>_agents`, or owns objects in the database. A role name is at most 63 bytes.

## The connection string init builds

Init builds the agent's connection string from yours. It keeps the scheme, hosts, ports and database, swaps in the agent's user and password, and keeps only these parameters: `host`, `port`, `dbname`, `sslmode`, `sslrootcert`, `sslcrl`, `sslcertmode`, `sslnegotiation`, `sslsni`, `channel_binding`, `require_auth`, `target_session_attrs`, `connect_timeout`, `application_name`, `min_protocol_version`, `max_protocol_version` and `default_query_exec_mode`. It drops every other parameter, among them `user`, `password`, `passfile`, `service`, `servicefile` and your TLS client identity (`sslcert`, `sslkey`, `sslpassword`). `sslrootcert` and `sslcrl` are file paths. After `basalt service install`, the engine's OS user must be able to read them.

## Check what is set

Run `basalt status --agent NAME`. It prints the config path, whether the engine runs, the socket and state directories, the pages URL, `operatedBy` and each problem with its fix. It never prints the credential file's contents. See [CLI](/reference/cli/#basalt-status).
