---
title: Configuration
description: The settings that matter, the ones that will bite you, and the four keys you must not lose.
---

Veefore is configured entirely through the environment. No runtime configuration
file, no settings table that can disagree with the process that booted.
Configuration is validated at startup, and an invalid value stops the process
rather than degrading it.

The complete list is [Configuration reference](/reference/configuration),
generated from `.env.example`. This page covers the ones with consequences.

## The four keys

:::danger Lose these and you lose data
| Key | Protects | Losing it means |
| --- | --- | --- |
| `VEEFORE_DATA_ENCRYPTION_KEY` | Sealed credential material | Every credential becomes unreadable |
| `VEEFORE_SIGNING_PRIVATE_KEY` | Agent passports | Every issued passport stops verifying |
| `VEEFORE_SESSION_SECRET` | Human sessions | Everyone is logged out. Recoverable |
| `VEEFORE_AUDIT_CHAIN_SECRET` | The audit hash chain | Integrity can no longer be verified |

The first, second and fourth are **not recoverable** by re-deriving. Back them
up where you back up your other root secrets, and inject them from a secret
manager rather than a file.
:::

`.env.example` ships with development keys so the stack boots out of the box.
They are shared with everyone who has ever cloned the repository, so they are
not secret by definition. Replace all four before anything that matters.

### Rotating them

| Key | Rotation |
| --- | --- |
| `VEEFORE_SESSION_SECRET` | Safe. Everyone re-authenticates |
| `VEEFORE_SIGNING_PRIVATE_KEY` | Invalidates issued passports. Issue new ones, let partners pick up the new published key, then retire the old |
| `VEEFORE_DATA_ENCRYPTION_KEY` | Requires re-sealing every credential. Plan as a migration |
| `VEEFORE_AUDIT_CHAIN_SECRET` | Breaks verification of the existing chain. Export and archive first |

The last two are not config changes. Treat them as projects.

## Database

```ini
DATABASE_URL=postgres://veefore:veefore@127.0.0.1:55432/veefore
DATABASE_SSL=true
DATABASE_POOL_MAX=20
DATABASE_STATEMENT_TIMEOUT_MS=15000
DATABASE_ENABLE_RLS=false
PGLITE_DATA_DIR=memory://
```

:::warning `PGLITE_DATA_DIR` is per-process
The embedded PGlite driver is genuinely useful for isolated tests. It is the
wrong choice for running the stack, because each process gets its own private
copy of the database — the control plane, the gateway and the worker would see
none of each other's writes.

The symptom is confusing rather than obviously broken: an agent you just created
does not exist. Set `DATABASE_URL` for anything multi-process.
:::

`DATABASE_ENABLE_RLS=true` turns on PostgreSQL row-level security as a second,
independent isolation layer under the application-level tenant scoping. Enable
it in production. See [Tenant isolation](/secure/tenant-isolation).

`DATABASE_POOL_MAX` is per process instance. Three control plane instances at 20
each is 60 connections, plus the gateway, plus the worker. Size PostgreSQL's
`max_connections` for the total, not for one process.

## Egress

```ini
GATEWAY_ALLOW_REAL_EGRESS=false
GATEWAY_EGRESS_TIMEOUT_MS=15000
```

`false` is the default, and with it the tool runtime produces deterministic
simulated responses — every one carrying `simulated: true`. Nothing is ever
presented as output from a real system when it is not.

:::danger `GATEWAY_ALLOW_REAL_EGRESS=true` means real outbound calls
Including webhook delivery. Turn it on deliberately, per environment, once the
destination allowlists on your tools are correct.

With it off, webhook deliveries are recorded as **failed with an explicit
reason** rather than reported as delivered — a webhook claimed as delivered when
nothing was sent would be a lie in the one record you would later rely on.
:::

## Sessions and tokens

```ini
SESSION_TTL_SECONDS=43200          # 12 hours
SESSION_IDLE_TIMEOUT_SECONDS=3600  # 1 hour idle
STEP_UP_TTL_SECONDS=300            # 5 minutes
AGENT_TOKEN_TTL_SECONDS=900        # 15 minutes
PASSPORT_TTL_SECONDS=2592000       # 30 days
MAX_LOGIN_ATTEMPTS=10
LOGIN_LOCKOUT_SECONDS=900
```

`STEP_UP_TTL_SECONDS` is the window in which a step-up authentication stays
valid. Five minutes is already generous: step-up exists so an unattended browser
session cannot revoke an agent, and a long window reintroduces exactly that.
Do not raise it for convenience.

`AGENT_TOKEN_TTL_SECONDS` is short because there is no refresh token — an agent
re-signs with its key. Lowering it costs a few extra signatures; raising it
extends how long a stolen token is useful.

## CORS

```ini
CORS_ALLOWED_ORIGINS=https://console.example.com
```

Exact origins, comma-separated. If you change the console's port or host and
forget this, the browser refuses every request and the console appears
completely broken with no useful error.

## Credentials

```ini
CREDENTIAL_BROKER_PROVIDER=local
CREDENTIAL_DEFAULT_TTL_SECONDS=600
```

`local` seals material in PostgreSQL with `VEEFORE_DATA_ENCRYPTION_KEY`. For
production, point it at your secret manager so material lives where the rest of
your secrets live and inherits that system's rotation, auditing and access
control.

## Observability

```ini
OTEL_ENABLED=true
OTEL_SERVICE_NAME=veefore
METRICS_ENABLED=true
LOG_LEVEL=info
```

See [Observability](/operate/observability).

## The console's build-time values

```ini
VITE_API_BASE_URL=https://veefore.example.com
VITE_GATEWAY_BASE_URL=https://gateway.veefore.example.com
```

:::warning These are baked in at build time
`VITE_*` values are compiled into the bundle. Changing them requires a rebuild
and redeploy of the console — restarting a server does nothing.

If the console is pointing at the wrong API after a config change, that is why.
:::

## Loading order

1. Real environment variables win.
2. Then the repository root `.env`.

That order holds for every process, including the console's Vite config, which
reads the root `.env` so ports stay consistent across the stack.

:::warning Ambient variables leak between runs
A shell with `VEEFORE_AGENT_ID` exported from an earlier session will quietly
make a test authenticate as the wrong agent. Shell environments persist across
commands.

If something is behaving as though it is a different agent or a different
organization, `env | grep Veefore` before anything else.
:::

## Checking it

```bash
curl -s http://localhost:4000/v1/meta/info
```

No authentication. Returns the version, the environment, both public URLs, and
the published capability and exclusion lists. The fastest way to confirm a
deployment is configured the way you think it is.

```bash
veefore doctor --strict
```

Audits the posture, including TLS, MFA enforcement, local file permissions and
network restrictions.

## Related

:::cards

:::card Configuration reference | /reference/configuration
Every variable. Generated from `.env.example`.
:::

:::card Secrets | /secure/secrets
Where secrets live and what stops them surfacing.
:::

:::card Database | /operate/database
Migrations, pooling, RLS, backups.
:::

:::card Deployment | /operate/deployment
Injecting configuration in containers.
:::

:::
