# Configuration

OpenConnector is configured with environment variables.

For local Node development, copy [`.env.example`](../.env.example) to `.env` in the repository root.
`npm run dev`, `npm start`, `npm run dev:api`, and `npm run runtime:data` load `.env` automatically
via Node's built-in env file loader. Variables already set in the shell take precedence. The file is
optional; without it the documented defaults apply.

| Variable                                 | Default                   | Purpose                                                                              |
| ---------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------ |
| `PORT`                                   | `3000`                    | Local HTTP server port.                                                              |
| `HOST`                                   | `127.0.0.1`               | Bind address. Docker image sets `0.0.0.0`.                                           |
| `OOMOL_CONNECT_ORIGIN`                   | `http://localhost:<PORT>` | Public origin used for OAuth redirect URLs.                                          |
| `OOMOL_CONNECT_DATA_DIR`                 | `./data`                  | Node data directory (SQLite file, transit files). Docker image sets `/app/data`.     |
| `OOMOL_CONNECT_DB_DRIVER`                | `sqlite`                  | Runtime store backend: `sqlite`, `postgres`, or `mysql`. Cloudflare D1 is unchanged. |
| `OOMOL_CONNECT_DATABASE_URL`             | unset                     | Required when `OOMOL_CONNECT_DB_DRIVER` is `postgres` or `mysql`.                    |
| `OOMOL_CONNECT_DB_POOL_MAX`              | `10`                      | Max connections in the PG/MySQL pool. Ignored for SQLite.                            |
| `OOMOL_CONNECT_ENCRYPTION_KEY`           | unset                     | Encrypts credentials, OAuth config, and completed idempotent Action responses.       |
| `OOMOL_CONNECT_NEW_ENCRYPTION_KEY`       | unset                     | New key used by `runtime:data rotate-key`.                                           |
| `OOMOL_CONNECT_ADMIN_TOKEN`              | unset                     | Requires bearer-token auth for local admin API, docs, and web console.               |
| `OOMOL_CONNECT_RUNTIME_TOKEN`            | unset                     | Optional bootstrap runtime bearer token for `/v1` and MCP callers.                   |
| `OOMOL_CONNECT_JWKS_URI`                 | unset                     | Node-only JWKS endpoint for validating runtime JWT access tokens.                    |
| `OOMOL_CONNECT_JWT_ISSUER`               | unset                     | Expected `iss` claim for runtime JWT access tokens.                                  |
| `OOMOL_CONNECT_JWT_AUDIENCE`             | unset                     | Expected API `aud` claim for runtime JWT access tokens.                              |
| `OOMOL_CONNECT_ALLOWED_ACTIONS`          | unset                     | Comma-separated executable action allowlist. Supports `service.*` and `*`.           |
| `OOMOL_CONNECT_BLOCKED_ACTIONS`          | unset                     | Comma-separated executable action denylist. Supports `service.*` and `*`.            |
| `OOMOL_CONNECT_ALLOWED_PROXIES`          | unset                     | Comma-separated provider proxy allowlist. Supports service names and `*`.            |
| `OOMOL_CONNECT_BLOCKED_PROXIES`          | unset                     | Comma-separated provider proxy denylist. Supports service names and `*`.             |
| `OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK`    | `false`                   | Allow self-hosted provider connections to target private networks. See below.        |
| `OOMOL_CONNECT_TRANSIT_FILE_TTL_SECONDS` | `86400`                   | Transit file lifetime before cleanup.                                                |
| `OOMOL_CONNECT_TRANSIT_FILE_MAX_BYTES`   | `104857600`               | Maximum transit file upload size.                                                    |
| `OOMOL_CONNECT_RUN_LIMIT`                | `5000`                    | Maximum number of recent action run audit records to retain.                         |

Example:

```bash
OOMOL_CONNECT_DATA_DIR="$PWD/data" \
OOMOL_CONNECT_ENCRYPTION_KEY="replace-with-a-long-random-secret" \
OOMOL_CONNECT_ADMIN_TOKEN="replace-with-an-admin-token" \
OOMOL_CONNECT_ALLOWED_ACTIONS="hackernews.*,github.get_current_user" \
OOMOL_CONNECT_ALLOWED_PROXIES="github" \
npm run dev
```

Create persistent runtime tokens from the web console Access tab or `POST /api/runtime-tokens`.
Only token hashes are stored in the runtime database. `OOMOL_CONNECT_RUNTIME_TOKEN` remains available for
bootstrap scripts and backward compatibility.

## Self-hosted database backends

By default the Node server uses SQLite at `$OOMOL_CONNECT_DATA_DIR/connect.sqlite`. To use PostgreSQL
or MySQL instead:

```bash
# PostgreSQL
OOMOL_CONNECT_DB_DRIVER=postgres \
OOMOL_CONNECT_DATABASE_URL="postgres://user:pass@host:5432/connect" \
npm run dev

# MySQL 8+
OOMOL_CONNECT_DB_DRIVER=mysql \
OOMOL_CONNECT_DATABASE_URL="mysql://user:pass@host:3306/connect" \
npm run dev
```

Schema migrations run automatically on every startup. The flow is identical for SQLite,
PostgreSQL, and MySQL — only the SQL dialect differs.

1. **Bootstrap table** — On startup the server creates a `runtime_migrations (name, applied_at)`
   bookkeeping table with `CREATE TABLE IF NOT EXISTS`.
2. **Diff applied vs files** — It lists every `NNNN_*.sql` file under `drizzle/<driver>/` and skips
   any whose name is already recorded in `runtime_migrations`.
3. **Apply in order** — Each pending file runs inside a single transaction; on success its name is
   inserted into `runtime_migrations`. If any statement fails, the whole file rolls back and the
   server fails to start — partial migrations never reach the bookkeeping table.

### First run on an empty database

A fresh database has all six runtime tables created by the baseline `drizzle/<driver>/0000_initial.sql`
(`connections`, `oauth_client_configs`, `oauth_states`, `runtime_tokens`, `runs`, `idempotency_records`)
plus their indexes and CHECK constraints. No manual setup is required.

### Schema changes

Runtime schema is migration-only. To evolve it:

```bash
# 1. Edit src/server/storage/drizzle/schema/<driver>.ts (sqlite/postgres/mysql)
# 2. Regenerate the SQL files for every driver:
npm run drizzle:generate
# 3. Commit the new drizzle/<driver>/NNNN_*.sql file(s)
# 4. Restart — the new migration is applied automatically on next boot
```

Editing an already-applied migration file has no effect (the recorded name causes it to be skipped).
Always add a new `NNNN_*.sql` rather than rewriting history.

### Legacy SQLite adoption

Self-hosted SQLite databases created by older OpenConnector releases (the `node:sqlite` era with
`migrations/0001_*.sql` through `0006_*.sql`) are detected on first boot of the Drizzle backend. The
server inserts a synthetic `0000_initial.sql` row into `runtime_migrations` so the new baseline does
not collide with the existing tables, while any future `0001_*.sql` Drizzle migration still applies
normally. The original data is preserved.

### Per-driver notes

- **PostgreSQL / MySQL** — migrations run on a dedicated connection held for the entire run, so
  `BEGIN` / statements / `COMMIT` share one session. The runtime pool is opened only after
  migrations succeed.
- **MySQL 8.0.16+** is required because the `idempotency_records` CHECK constraints are enforced
  from that version onward.
- The Cloudflare Workers path uses D1 and is **not** affected by these variables or files.

Optional Compose overlay for a local Postgres sidecar:

```bash
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up
```

## JWT access tokens

The Node server can validate JWT access tokens issued by an existing identity provider for `/v1/*`
and `/mcp`. Configure all three settings together:

```bash
OOMOL_CONNECT_JWKS_URI="https://idp.example.com/oauth2/jwks" \
OOMOL_CONNECT_JWT_ISSUER="https://idp.example.com" \
OOMOL_CONNECT_JWT_AUDIENCE="https://connect-api.example.com" \
npm run dev
```

`OOMOL_CONNECT_JWKS_URI` must be the direct HTTPS JWKS endpoint, not an OIDC discovery URL. Plain
HTTP is accepted only for loopback endpoints used during local development. `OOMOL_CONNECT_JWT_AUDIENCE`
should identify this API resource, not a web application's OIDC client. OpenConnector requires an
expiration claim and validates the JWT signature, issuer, audience, expiration, and not-before time.
Clients send the access token as `Authorization: Bearer <jwt>`.

JWT authentication is additive: the bootstrap runtime token and persistent `oct_...` tokens remain
valid when JWT verification is configured. For a JWT-only deployment, leave
`OOMOL_CONNECT_RUNTIME_TOKEN` unset and revoke any persistent runtime tokens. JWTs do not grant
access to the admin API, docs, or web console, so configure `OOMOL_CONNECT_ADMIN_TOKEN` separately
before exposing those surfaces.

OpenConnector acts only as a resource server. It does not implement OIDC discovery or login, accept
ID tokens as API credentials, or map JWT claims to action and proxy policy. JWT verification is
currently available only on the Node server, not Cloudflare Workers.

## Private network access

By default OpenConnector applies a public-only SSRF guard to every user-supplied
URL, including self-hosted provider instance URLs (for example the Dokploy
**Instance URL**). Connections may therefore only target public addresses, and
private targets are rejected during connection setup.

Some self-hosted services are only reachable over a LAN or an overlay network
such as Tailscale or NetBird. To allow those connections, set
`OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK=true`. When enabled, provider connections
that opt in (currently **Dokploy**) may target:

- RFC 1918 ranges: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`
- Carrier-grade NAT / shared address space `100.64.0.0/10` (Tailscale, NetBird)
- Private hostname suffixes: `.local`, `.internal`, `.home`, `.lan`

The following targets stay blocked even when the flag is enabled:

- Loopback and localhost (`127.0.0.0/8`, `localhost`, `.localhost`)
- Link-local and cloud metadata (`169.254.0.0/16`, `100.100.100.200/32`, and
  metadata hostnames such as `metadata.google.internal`)
- Reserved, multicast, and broadcast ranges, and all IPv6 targets

> **Enable this only on a single-tenant, self-hosted runtime that you operate.**
> On a shared or multi-tenant deployment, turning it on lets any connection
> owner reach the operator's internal network from the runtime's egress
> position, so leave it at the `false` default there.

## Cloudflare Workers

Cloudflare uses the same environment variable names for origin, static auth tokens, execution
policy, transit file limits, and data encryption. The JWT settings above, `PORT`, `HOST`, and
`OOMOL_CONNECT_DATA_DIR` are Node-only settings.

The Worker runtime also requires these bindings in `wrangler.local.jsonc`. Copy
`wrangler.example.jsonc` to `wrangler.local.jsonc` and fill in your own Cloudflare resource IDs
before running Wrangler commands.

- `DB`: D1 database for connections, OAuth config/state, runtime tokens, run logs, and idempotency
  claims and responses.
- `TRANSIT_FILES`: R2 bucket or Workers KV namespace for temporary transit files.
- `ASSETS`: Workers Static Assets binding for the web console.

R2 is the default transit-file backend. To use Workers KV, bind the KV namespace as
`TRANSIT_FILES` and set the Wrangler variable `TRANSIT_FILES_BACKEND` to `"kv"`. Configure exactly
one R2 bucket or KV namespace with that binding name. KV limits each file to 25 MiB, clamps the
transit-file TTL to a minimum of 60 seconds, and deletes expired files automatically.

Set secrets with Wrangler instead of committing them to config:

```bash
npx wrangler secret put OOMOL_CONNECT_ADMIN_TOKEN --config wrangler.local.jsonc
npx wrangler secret put OOMOL_CONNECT_ENCRYPTION_KEY --config wrangler.local.jsonc
```
