# Configuration

> How the template is configured: appsettings as development placeholders, environment-variable overrides in production, and the local port map.

## One rule: placeholders in, environment variables out

Configuration follows a single principle. The values committed to `appsettings.json` are
**development placeholders** that let you clone and run the stack with `docker compose up -d` and no
extra setup. Every secret or environment-specific value is **overridden by an environment variable**
in any real environment.

There is no Key Vault or Secrets Manager scaffolding to adopt. Point your platform's secret
mechanism (Docker secrets, Kubernetes `Secret`, your host's env panel) at environment variables and
you are done. Nothing in the repo needs to change to deploy.

This page covers the API and SPA. The marketing site has its own customization guide: see
[customising the landing site](/docs/landing-site).

## The double-underscore convention

.NET maps environment variables onto the configuration tree by replacing each `:` with `__` (two
underscores). A nested key in `appsettings.json` becomes a flat environment variable:

```json
{
  "ConnectionStrings": { "Postgres": "Host=localhost;..." },
  "Cors": { "AllowedOrigins": ["http://localhost:3003"] }
}
```

```sh
ConnectionStrings__Postgres="Host=db;Port=5432;Database=slicekit;Username=...;Password=..."
Cors__AllowedOrigins__0="https://app.example.com"
```

Array entries are addressed by index (`__0`, `__1`). This is the standard ASP.NET Core binding, so
anything you can put in `appsettings.json` you can override from the environment without touching
code. Strongly-typed settings are bound the same way, see [the settings pattern](/docs/settings-pattern).

## Common overrides

The values you will set in production, grouped by concern:

| Concern        | Variables                                                                 |
| -------------- | ------------------------------------------------------------------------- |
| Database       | `ConnectionStrings__Postgres`                                             |
| Cache/sessions | `ConnectionStrings__Redis`                                                |
| Messaging      | `RabbitMq__Host`, `RabbitMq__Username`, `RabbitMq__Password`              |
| File storage   | `Storage__Endpoint`, `Storage__AccessKey`, `Storage__SecretKey`, `Storage__Bucket` |
| CORS           | `Cors__AllowedOrigins__0`, `Cors__AllowedOrigins__1`, ...                 |
| OAuth          | `Authentication__Google__ClientId`, `Authentication__Google__ClientSecret` |
| Telemetry      | `OTEL_EXPORTER_OTLP_ENDPOINT` (the OTLP collector your traces and audit log flow to) |

Storage points at MinIO locally and any S3-compatible bucket in production (see
[file storage](/docs/file-storage)). OAuth client credentials are added per provider (see
[adding an OAuth provider](/docs/oauth-provider)). Telemetry exporters are plain OTLP, so they target
your collector (see [observability](/docs/observability)).

## The local port map

`docker compose up -d` brings up the full local stack. The dev servers and infrastructure use a
fixed set of ports:

| Service      | Port         | Notes                                          |
| ------------ | ------------ | ---------------------------------------------- |
| Frontend     | 3003         | Vite dev server; matches `Cors:AllowedOrigins` |
| API          | 5076 / 5077  | http / https from Kestrel                       |
| Postgres     | 5432         |                                                |
| Redis        | 6379         |                                                |
| RabbitMQ     | 5672 / 15672 | broker / management UI                          |
| Mailpit      | 1025 / 8025  | SMTP / web UI                                   |
| MinIO        | 9000 / 9001  | S3 API / console                                |
| Grafana      | 3010         | dashboards                                      |
| Prometheus   | 9090         |                                                |
| Loki         | 3100         | logs and the audit trail                        |
| Tempo        | 3200         | traces                                          |
| Alertmanager | 9093         |                                                |

The frontend dev server on `3003` is the origin allowed by `Cors:AllowedOrigins`, so the SPA and API
agree out of the box. If you change the frontend port, update that origin.

## Production

In production you run the same images with environment variables supplied by your platform:

```sh
docker compose -f docker-compose.prod.yml up -d --build
```

Terminate TLS at your proxy or load balancer and forward the scheme and client address; the API
trusts forwarded headers so cookies, redirects and audited IPs stay correct. See
[reverse proxy](/docs/reverse-proxy) and [deployment](/docs/deployment) for the full topology.

## Checklist

- Treat `appsettings.json` as placeholders; never commit a real secret.
- Override secrets and environment-specific values with environment variables, using `__` for `:`.
- Set `Cors__AllowedOrigins` to your real frontend origin(s).
- Point `OTEL_EXPORTER_OTLP_ENDPOINT` and `Storage__*` at your infrastructure.
- Run migrations as an explicit deploy step (see [deployment](/docs/deployment)).
