# NETMapper configuration reference (netmapper.nx1xlab.dev/docs/configuration)

All NETMapper settings come from environment variables. With Docker Compose
they live in a `.env` file next to `docker-compose.yml`; on bare metal they
live in `/etc/netmapper/.env`. Values marked required have no default and
`docker compose` refuses to start without them. Everything else can stay as
it is.

## Required: the release to run

These three come straight from the GitHub release notes of the version you
want to run. The digest is what actually pins the image: a tag can be
repointed upstream, a digest cannot. Upgrading means replacing all three.

| Variable | What it does | Default |
|---|---|---|
| `NETMAPPER_VERSION` | Image tag of the release to run, for example `0.1.0`. | none, required |
| `NETMAPPER_BACKEND_DIGEST` | `sha256:...` digest of the backend image for that release. | none, required |
| `NETMAPPER_FRONTEND_DIGEST` | `sha256:...` digest of the frontend image for that release. | none, required |

## Required: secrets

| Variable | What it does | Default |
|---|---|---|
| `POSTGRES_PASSWORD` | Password for the in-stack PostgreSQL role. Compose builds `DATABASE_URL` from it, so you never construct the URL by hand. | none, required |
| `NETMAPPER_JWT_SECRET` | HS256 signing key for issued access tokens. The production startup gate refuses to boot on a secret shorter than 32 bytes or left at the development placeholder. Rotating it invalidates every outstanding token and forces all users to log in again. | none, required |
| `NETMAPPER_ENCRYPTION_KEY` | urlsafe-base64 Fernet key that encrypts switch SSH passwords, notification-channel secrets and AI provider keys at rest. | none, required |

Generate them, for example:

```bash
python3 -c "import secrets; print(secrets.token_urlsafe(24))"   # POSTGRES_PASSWORD
python3 -c "import secrets; print(secrets.token_urlsafe(48))"   # NETMAPPER_JWT_SECRET
python3 -c "import base64, os; print(base64.urlsafe_b64encode(os.urandom(32)).decode())"
```

**Losing `NETMAPPER_ENCRYPTION_KEY` makes every stored secret
unrecoverable.** There is no key escrow and no recovery code. Back it up
separately from the database dump, in a password manager or a secret store,
and never commit it to git. A database backup on its own does not restore
your switch passwords.

## Database

| Variable | What it does | Default |
|---|---|---|
| `DATABASE_URL` | SQLAlchemy URL, for example `postgresql+psycopg://user:pass@host:5432/netmapper`. Compose derives it from the `POSTGRES_*` values; set it by hand only on bare metal or against an external database. | derived by Compose |
| `POSTGRES_USER` | PostgreSQL role name. | `netmapper` |
| `POSTGRES_DB` | Database name. | `netmapper` |
| `NETMAPPER_ALLOW_SQLITE` | Permits a SQLite `DATABASE_URL`. Development only. | `false` |

## Network

| Variable | What it does | Default |
|---|---|---|
| `NETMAPPER_HTTP_PORT` | Host port for the app over plain HTTP. With the HTTPS overlay this port is not published, because nginx takes 80 and 443 instead. | `80` |
| `NETMAPPER_COOKIE_SECURE` | Marks the auth cookies Secure. Set it to `false` only when users reach the app over plain `http://` with no TLS in front: browsers refuse Secure cookies on plain HTTP, so login would fail. | `true` |
| `DOMAIN_NAME` | Hostname used by `docker-compose.https.yml` for the Let's Encrypt certificate and the nginx server name. Only for a host that must be reachable from outside; prefer a firewall and a zero-trust proxy in front of plain port 80. | empty |
| `VITE_API_URL` | Base URL the React app uses to reach the API. The release frontend proxies the API itself, so this matters mainly for a source build. | unset |

## Optional tuning

Everything here has a working default. All of these ship commented out in
`.env.example`.

| Variable | What it does | Default |
|---|---|---|
| `NETMAPPER_ENV` | Set to `production` to enable the startup secret gate, which refuses to boot on weak or placeholder secrets. `development` relaxes it for a source build. | `production` in the release compose file |
| `NETMAPPER_JWT_EXPIRE_MINUTES` | Lifetime of an issued access token, in minutes. | `60` |
| `NETMAPPER_LOGIN_RATELIMIT` | Per-IP rate limit on the login endpoints, in slowapi syntax. Rejections carry a `Retry-After` header. | `5/minute` |
| `NETMAPPER_SCHEDULER_ENABLED` | Enables the APScheduler jobs behind scheduled scans and the daily maintenance work. Manual scans do not need it. | `true` in the release compose file |
| `NETMAPPER_AUDIT_RETENTION_DAYS` | How long audit log rows are kept. A prune job runs daily at 03:30 local time, and admins can prune on demand from the audit log UI. | `90` |
| `NETMAPPER_SCAN_COOLDOWN_SECONDS` | Minimum gap between scans of the same switch. | `30` |
| `NETMAPPER_SCAN_USER_HOURLY_LIMIT` | Maximum scans one user may trigger per hour. | `30` |
| `LOG_LEVEL` | Backend log verbosity. Raise it to `DEBUG` while reproducing a problem, then put it back. | `INFO` |

### Email alerts (SMTP)

Set these to let alert rules deliver over email, then restart the backend and
add a channel of type `email` under Settings, Alerting, Notification
Channels. The "Send test" button verifies delivery before you attach the
channel to a rule.

| Variable | What it does | Default |
|---|---|---|
| `SMTP_HOST` | SMTP server hostname. | empty |
| `SMTP_PORT` | SMTP port. | `587` |
| `SMTP_USER` | SMTP username. | empty |
| `SMTP_PASSWORD` | SMTP password. Encrypted at rest when stored on a channel. | empty |
| `SMTP_FROM` | Envelope and header sender address. | empty |
| `SMTP_TLS` | Use STARTTLS. | `true` |

## Telemetry and crash reporting

| Variable | What it does | Default |
|---|---|---|
| `NETMAPPER_TELEMETRY_ENABLED` | Anonymous daily usage ping. On by default. Set it to `false` to opt out; the environment variable always wins over the in-app switch, which then shows as locked. | `true` |
| `NETMAPPER_TELEMETRY_URL` | Where the daily ping goes. Point it at an endpoint you control to keep the aggregate for yourself. | `https://telemetry.nx1xlab.dev/v1/ping` |
| `NETMAPPER_SENTRY_DSN` | Crash reporting to a Sentry project you own. Unset means no crash reporting at all. Request and response bodies, environment variables, switch credentials, switch IPs, AI keys and notification secrets are scrubbed before anything is sent. | unset, off |

The ping carries a random install id, version numbers, OS family, bucketed
counts, and the kinds of scanners and AI providers in use. Nothing about your
network: no IPs, MACs, hostnames, switch names, credentials or scan output.
The full field list and the opt-out in the UI are on the
[security and privacy page](/docs/security).
