# Example: Configuration

The configuration document records how a console application is configured
across environments: the settings it retrieves from the Environment API, the
`DatabaseConfig` registrations that produce its connection strings, the
`App.config` surface, and the log destinations. Doc gen produces one per
application at `docs/applications/<app-slug>/configuration.md`.

## What reviewers should check

- The page states that connection strings are resolved from the Environment API
  through each `DatabaseConfig`'s environment config key rather than stored in
  `<connectionStrings>`, and the `App.config` section that follows does not
  contradict it with a literal connection string.
- Every Environment API row names the retrieval method (`GetConfigSetting` or
  `GetSecureConfigSetting`) and the consuming class, and `Secure` is `Yes` for
  every value retrieved securely.
- The database configuration table's `Config name` values match the Autofac
  registration names used to resolve `IUnitOfWork`, and each row's environment
  config key is the key the instance name is resolved from.
- `App.config` subsections with no entries are omitted rather than left empty;
  `ApplicationHost` appears in `appSettings` because the Environment API host is
  resolved from it.
- The log destination table maps `file0`-`file4` to what this application
  actually writes, marking unused ids "Not used" instead of inventing content.
- The environment variance table contains only verified values and uses
  `Unknown` rather than a guess; a row repeated identically across DEV, TEST,
  and PROD should be checked against the environment data, not assumed.
- Inherited framework statements carry `†` so a reviewer can tell application
  behaviour from framework behaviour.

## Example

The document below is a complete, correct `console-configuration.md` for a
fictional .NET Framework console application.

[View rendered example](rendered/applications/saif-policy-renewalnotice/configuration.md) — the same document as a real page, so you can compare rendering as well as source.

````markdown
---
title: Configuration
summary: Environment API settings, database configurations, and app.config surface.
status: draft
owner: Policy Systems Team
last_reviewed: 2026-09-10
---

# Configuration

`SAIF.Policy.RenewalNotice` carries almost no configuration of its own. The
`App.config` deployed with the executable names the Environment API host, the
Oracle managed driver, and the log4net appender; everything that varies by
environment — the database instance behind `policyDb`, the Agency API base
address and key, the notice window, the print vendor drop path, and the summary
mail recipients — is retrieved at run time from the Environment API for the track
the executable is running on. The same binaries therefore run in DEV, TEST, and
PROD without a config transform.

Configuration is retrieved at runtime from the Environment API through `IEnvironmentService`.
Connection strings are **not** stored in `<connectionStrings>`; they are resolved from the database instance name returned for each `DatabaseConfig`'s environment config key.†

## Environment API settings

| Key | Retrieved by | Consuming class | Secure | Varies by environment | Purpose |
| --- | --- | --- | --- | --- | --- |
| `policy.db.instance` | `GetConfigSetting` | `Program` | No | Yes | Oracle instance name behind the `policyDb` `DatabaseConfig` registration. |
| `policy.db.password` | `GetSecureConfigSetting` | `Program` | Yes | Yes | Password for the `POLICY_APP` database user used by the `policyDb` unit of work. |
| `agency.api.baseUrl` | `GetConfigSetting` | `RenewalNoticeApplication` | No | Yes | Base address of `SAIF.Agency.Api`, used to construct the `SAIF.Agency.Api.Client` agency client. |
| `agency.api.apiKey` | `GetSecureConfigSetting` | `RenewalNoticeApplication` | Yes | Yes | API key sent by the agency client on every agency and agent request. |
| `renewalnotice.noticeWindowDays` | `GetConfigSetting` | `NoticeGenerator` | No | No | Default lead time in days applied to the notice window when the notice template does not override it. |
| `renewalnotice.printQueuePath` | `GetConfigSetting` | `NoticeQueueWriter` | No | Yes | UNC path of the print vendor drop folder that generated notice documents are written to. |
| `renewalnotice.notificationRecipients` | `GetConfigSetting` | `RenewalNoticeApplication` | No | Yes | Semicolon-separated addresses for the run summary mail sent by `Finish`. |
| `renewalnotice.healthProbeAgencyNumber` | `GetConfigSetting` | `RenewalNoticeApplication` | No | No | Agency number used for the Agency API health probe in `Initialize`. |

## Database configurations

Each entry is a `DatabaseConfig` passed to `RegisterCommonDependencies` in `RegisterAppDependencies`. `Config name` is the Autofac registration name used to resolve the corresponding `IUnitOfWork`.

| Config name | Type | Environment config key | Database user | Stateless | Server | Purpose |
| --- | --- | --- | --- | --- | --- | --- |
| `policyDb` | `OracleConfig` | `policy.db.instance` | `POLICY_APP` | No | Resolved per track from the Environment API | Reads `POLICY.POLICY_TERM` and `POLICY.NOTICE_TEMPLATE`, and writes `POLICY.RENEWAL_NOTICE` and `POLICY.RENEWAL_NOTICE_QUEUE` through the NHibernate unit of work. |

## app.config settings

### appSettings

| Key | Value | Read by | Purpose |
| --- | --- | --- | --- |
| `ApplicationHost` | `https://environmentapi.example.saif` | `EnvironmentServiceBase` | Environment API host resolution† |
| `ApplicationName` | `SAIF.Policy.RenewalNotice` | `ProgramRunnerBase` | Identifies the application to the Environment API and appears in the header, footer, and summary mail. |
| `AgencyApiTimeoutSeconds` | `30` | `RenewalNoticeApplication` | Request timeout applied to the agency client; a term whose agent lookup exceeds it is skipped and logged to `file2`. |
| `PrintFileExtension` | `.afp` | `NoticeQueueWriter` | Extension of the generated print document expected by the print vendor pickup job. |

### Custom configuration sections

| Section | Purpose |
| --- | --- |
| `oracle.manageddataaccess.client` | Oracle managed driver configuration |
| `hibernate-configuration` | NHibernate dialect (`Oracle10gDialect`), driver, and batch size used by the session factory built from the `policyDb` config |
| `log4net` | Rolling file appender for the application log that sits alongside the `file0`-`file2` destinations |

### Assembly binding redirects

| Assembly | Redirected to | Note |
| --- | --- | --- |
| Newtonsoft.Json | 13.0.0.0 | Aligns the version pulled in by `SAIF.Web.API.Clients` with the one the agency client contracts are compiled against |
| Oracle.ManagedDataAccess | 4.122.21.1 | Pins the managed driver version that `SAIF.Framework.Data.NHibernate` was built against |
| System.Runtime.CompilerServices.Unsafe | 6.0.0.0 | Required by the HTTP stack used underneath `SAIF.Web.API.Clients` |

## Log file destinations

Log outputs are resolved from **machine-level environment variables** `file0`–`file4`, scanned by `ConsoleLogService` at construction. Each variable's value is a log file path.†

| Environment variable | Log id | Content written by this application |
| --- | --- | --- |
| `file0` | `LogType.File0` | Run control log: header block, resolved arguments, notice window, per-100-notice progress lines, totals, and fatal errors |
| `file1` | `LogType.File1` | Notice detail: one line per generated notice with policy number, renewal effective date, agency number, servicing agent, and delivery channel; dry-run lines carry a `WHATIF` marker |
| `file2` | `LogType.File2` | Exception detail: one line per skipped or failed term with policy number, agency number, and the exception message |
| `file3` | `LogType.File3` | Not used |
| `file4` | `LogType.File4` | Not used |

## Environment variance

| Setting | DEV | TEST | PROD | Notes |
| --- | --- | --- | --- | --- |
| `policy.db.instance` | `POLICYDEV` | `POLICYTST` | `POLICYPRD` | Same `POLICY_APP` user in all three; only the instance differs. |
| `agency.api.baseUrl` | `https://agencyapi-dev.example.saif/` | `https://agencyapi-test.example.saif/` | `https://agencyapi.example.saif/` | DEV and TEST point at the corresponding `SAIF.Agency.Api` deployments. |
| `renewalnotice.noticeWindowDays` | `45` | `45` | `45` | Same in all tracks; the `NonRenewal` template overrides it with the statutory 60 days. |
| `renewalnotice.printQueuePath` | `\\printdev\drop\renewalnotice` | `\\printtest\drop\renewalnotice` | `\\printprd\drop\renewalnotice` | The DEV and TEST shares are drained by a cleanup job rather than by the print vendor. |
| `renewalnotice.notificationRecipients` | `policysystems-dev@example.saif` | `policysystems-test@example.saif` | `batchops@example.saif;policysystems-oncall@example.saif` | Only PROD notifies operations. |
| `agency.api.apiKey` | Unknown | Unknown | Unknown | Retrieved with `GetSecureConfigSetting`; values are not readable from the environment data. |

## Related documentation
- [Application index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Run lifecycle](run-lifecycle.md)
- [Arguments](arguments.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Data model](data-model.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

## Sources
- `src/SAIF.Policy.RenewalNotice/App.config`
- `src/SAIF.Policy.RenewalNotice/Program.cs`
- `src/SAIF.Policy.RenewalNotice/RenewalNoticeApplication.cs`
- `src/SAIF.Policy.RenewalNotice/NoticeQueueWriter.cs`
- `deploy/environment-settings/renewalnotice.settings.json`
````
