# Config and Server

Configuration is plain Idris data. Apps can build it directly, derive it from
environment variables in app code, and attach it to the app runner.

## 1. AppConfig

```idris
record AppConfig where
  constructor MkAppConfig
  mode : EnvMode
  port : Int
  database : DatabaseConfig
  session : SessionConfig
  logging : LoggingConfig
  maxRequestBytes : Int
```

Use `defaultConfig` locally and `productionConfig` in production:

```idris
appConfig : AppConfig
appConfig =
  withLogLevel Info
    (withMaxRequestBytes 131072
      (withPort 8082
        (productionConfig sessionSecret databaseUrl)))
```

Common modifiers:

- `withMode`
- `withPort`
- `withDatabaseUrl`
- `withDatabaseStatementTimeout`
- `withDatabaseTransactionTimeout`
- `withSessionSecret`
- `withSecureSessionCookies`
- `withLogLevel`
- `withMaxRequestBytes`

Database timeouts are passed through to the PostgreSQL runtime helper:

```idris
config : AppConfig
config =
  withDatabaseTransactionTimeout 5000
    (withDatabaseStatementTimeout 2000 defaultConfig)
```

## 2. Environment Modes

`EnvMode` is one of:

- `Development`
- `Test`
- `Production`

Use `isDevelopment`, `isTest`, and `isProduction` in app code when behavior
really needs to vary by environment.

## 3. Sessions

`SessionConfig` controls the signed session cookie:

```idris
secureSessionConfig : String -> SessionConfig
secureSessionConfig secret =
  MkSessionConfig "iwf_session" secret True True "Lax"
```

Production should use secure, HTTP-only cookies and a real secret:

```idris
config : AppConfig
config = productionConfig sessionSecret databaseUrl
```

`validateProductionConfig` reports common mistakes such as development mode,
the default session secret, or insecure cookies.

## 4. App Startup

Run the app through `app` and `runApp`:

```idris
main : IO ()
main =
  runApp
    (app "My App" routes
      |> withAppConfig appConfig)
```

`runApp`:

- mounts default framework assets
- uses configured logging
- enforces `maxRequestBytes` with `413 Payload Too Large`
- rejects malformed request lines and headers with `400 Bad Request`
- listens on `127.0.0.1:<port>`
- handles accepted clients concurrently
- can be paired with advanced supervisor/test shutdown hooks
- dispatches requests through the route registry
- closes each response connection explicitly; streaming is not part of the
  built-in server contract yet

For small demos, use the default app config:

```idris
main : IO ()
main =
  runApp (app "My App" routes)
```

Common app runner modifiers:

- `withAppConfig`
- `withPort`
- `withProduction`
- `withDatabaseFromEnv`
- `withSessionSecretFromEnv`
- `withAuth`
- `withAutoRefresh`

## 5. Graceful Shutdown

Most apps can let the process supervisor stop the app process. Advanced
embedding and test harnesses can use the lower-level server control API when
they need to stop the listener and wait for accepted requests to finish.

Shutdown closes the listening socket, stops accepting new clients, closes each
accepted connection after its response, and waits until active handlers drain.
Those hooks live behind the explicit `Iwf.Server.Advanced` import.

## 6. Built-In Server Contract

The built-in server is intentionally small and non-streaming:

- each accepted TCP connection is read once with `recv maxRequestBytes`
- the request payload is held in memory while it is parsed and dispatched
- request bodies larger than `maxRequestBytes` return `413 Payload Too Large`
- malformed request lines or headers return `400 Bad Request`
- each handler returns a complete `Response` value before bytes are written
- responses are rendered with `Content-Length`
- `HEAD` responses keep headers but render an empty body
- every accepted connection is closed after one response
- HTTP keep-alive, chunked request bodies, request streaming, and response
  streaming are not part of the built-in server contract

## 7. Logging

`LoggingConfig` contains a level and structured flag. By default, the server
emits colorized request/response logs with timing metadata through `Iwf.Log`.
Set `structured = True` if you want the older single-line key/value format.

```idris
logging : LoggingConfig
logging = MkLoggingConfig Info False
```

Diagnostics from framework subsystems use `Iwf.Diagnostic`, with areas for
routes, forms, auth, migration, and database typegen.

## Next

Read [Routing](routing.md).
