---
title: Console host bootstrap
summary: IHostBuilderExtensions/ConsoleBase/ConsoleAsyncBase compose and run console applications.
status: draft
owner: Framework.NCore maintainers
last_reviewed: 2026-10-02
library: SAIF.NetCore.ServiceExtensions
reference_type: package
operation_id: OP-18
trigger: A console application calls BuildConsoleApp<T>() or BuildSaifConsoleApp<T>() and then runs the resulting host.
---

# Console host bootstrap

This operation composes a generic .NET host for console applications — configuration precedence, console logging, and (for the SAIF variant) environment client/DB connectivity — then runs the hosted console service through its `Initialize`/`Execute`/`Finish` lifecycle.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-18 |
| **Trigger** | A console application calls `BuildConsoleApp<TConsole>()` or `BuildSaifConsoleApp<TConsole>()`, then runs the built host. |
| **Inputs** | `TConsole` — the hosted console type (implements `IHostedService`, typically via `ConsoleBase`/`ConsoleAsyncBase`); command-line `args`. |
| **Outputs** | A configured `IHost` whose `IHostedService` executes the console application's logic; the process exit code. |
| **Primary components** | `IHostBuilderExtensions.BuildConsoleApp`, `.BuildSaifConsoleApp`; `ConsoleBase`; `ConsoleAsyncBase` |
| **Entry point** | `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IHostBuilderExtensions.cs` (L24–63) |

## Steps

1. `BuildConsoleApp`: configuration sources added in order — `appsettings.json` (optional), environment variables, command line (if `args` supplied) — later sources override earlier ones ([BR-SE-20](../business-rules.md#br-se-20-console-configuration-source-precedence)) Register Singleton `IHostedService → TConsole`; configure logging with `AddConfiguration("Logging")` and `AddConsole`.
2. `BuildSaifConsoleApp` additionally delegates to [Register secure environment client](register-secure-environment-client.md) via `RegisterSecureEnvironmentClient(typeof(TConsole))` ([BR-SE-03](../business-rules.md#br-se-03-secure-mode-system-hash-is-computed-from-the-caller's-assembly)), delegates to [Register DB connection client](register-db-connection-client.md) via `RegisterDBConnectionClient()` ([BR-SE-04](../business-rules.md#br-se-04-db-connection-credentials-are-fetched-as-a-secure%2C-hash-gated-setting)), and registers Singleton `IConsoleLogService → ConsoleLogService`.

   > **Origin**: `ConsoleLogService`'s console-logging behavior is implemented in [`Configure console logger`](../../saif-netcore-logging/operations/op-03-configure-console-logger.md) and [`Write console log entry`](../../saif-netcore-logging/operations/op-09-write-console-log-entry.md) (`SAIF.NetCore.Logging`); this step only registers the singleton type — construction and logging calls happen later, at first resolution and at each log call.
3. `ConsoleAsyncBase`'s constructor captures `StartTime = DateTime.Now` and writes a header via `IConsoleLogService`; `Dispose` writes a footer and disposes the log service.
   - `EndTime` is a get-only property that returns `DateTime.Now` on every read rather than a timestamp captured once at run completion; `RunningTime` (`EndTime - StartTime`) is therefore the elapsed time since construction at the moment it is read, not a value frozen at a prior completion event.
4. `ConsoleBase.StartAsync` returns `Task.Run(() => Run(), cancellationToken)`, scheduling `Run` on the thread pool with the cancellation token forwarded to `Task.Run` itself (not observed inside `Run`). `Run` calls `Initialize` → `Execute` → `Finish` sequentially with no `try`/`finally`, then sets `Environment.ExitCode = ExitCode` ([BR-SE-21](../business-rules.md#br-se-21-console-process-exit-code-reflects-consolebase.exitcode)). `ConsoleBase.Stop()` is an empty no-op; it does not write the footer — footer logging and log-service disposal happen only via `ConsoleAsyncBase.Dispose()`.

## Process flow

This diagram shows host construction and the `Run()` lifecycle through assignment of `Environment.ExitCode`; it does not depict process termination, which is a separate host-lifetime concern outside this operation's scope.

```mermaid
flowchart TD
    Start([BuildConsoleApp / BuildSaifConsoleApp called]) --> S1[Add configuration sources in precedence order]
    S1 --> S2[Register IHostedService and console logging]
    S2 --> S3{BuildSaifConsoleApp variant?}
    S3 -->|Yes| S3a["Register secure environment client +<br>DB connection client + IConsoleLogService"]
    S3 -->|No| S4[Host built]
    S3a --> S4
    S4 --> S5[StartAsync runs Initialize then Execute then Finish]
    S5 --> S6["Environment.ExitCode = ExitCode<br>(host lifetime/process exit are separate)"]
    S6 --> EndOk([Run completes with exit status assigned])
```

## Failure and edge-case behavior
- `ConsoleAsyncBase.EndTime` recomputes `DateTime.Now` on every property read instead of returning a captured completion time; `RunningTime` read from `WriteFooter()` during `Dispose()` reflects the elapsed time up to that read, which is effectively the time of disposal, not a separately captured "run ended" instant.
- `Run()`'s three steps are not wrapped in `try`/`finally`: if `Initialize()` or `Execute()` throws, `Finish()` does not run and `Environment.ExitCode` is never assigned from `ExitCode` — the exception instead propagates out through the `Task` returned by `StartAsync`. The final process exit code reflects `ConsoleBase.ExitCode` only when `Run()` completes all three steps without an unhandled exception.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.

## Sources
- `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IHostBuilderExtensions.cs`
- `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/ConsoleBase.cs`
- `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/ConsoleAsyncBase.cs`
