Tech Stack & Key Libraries
Technologies and libraries used in Courier MFT — .NET, PostgreSQL, Next.js, and more.
This section catalogs every technology choice in Courier, organized by layer. Each entry includes the specific package, its version constraint, what it's used for, and which section(s) it supports.
3.1 Runtime & Frameworks
| Technology | Version | Purpose | Section |
|---|---|---|---|
| .NET | 10 | Application runtime, API host, background services | All |
| ASP.NET Core | 10 | REST API framework, middleware pipeline, OpenAPI | 10, 12 |
| Entity Framework Core | 10 | ORM for PostgreSQL (query/change tracking only — not migrations) | 4, 13 |
| Npgsql.EntityFrameworkCore.PostgreSQL | Latest stable | EF Core PostgreSQL provider | 13 |
| EFCore.NamingConventions | Latest stable | Automatic snake_case mapping for EF Core | 13 |
| .NET Aspire | Latest stable | Service orchestration, health checks, telemetry | 14 |
| Next.js | Latest stable (React) | Frontend framework | 11 |
| PostgreSQL | 16+ | Primary database | 13 |
3.2 Job Scheduling & Workflow
| Package | Purpose | Section |
|---|---|---|
| Quartz.NET | Cron scheduling, one-shot scheduling, persistent job store (AdoJobStore) | 5 |
| Quartz.Serialization.SystemTextJson | Quartz serialization using System.Text.Json (preferred over Newtonsoft where possible) | 5 |
Quartz.NET is used solely as the scheduling trigger layer. It fires at the scheduled time and delegates to Courier's Job Engine for actual execution. Quartz tables (QRTZ_*) are managed via DbUp (Section 13.3.6).
3.3 File Transfer
| Package | Purpose | Section |
|---|---|---|
| SSH.NET | SFTP client — connections, uploads, downloads, directory operations, host key verification | 6 |
| FluentFTP | FTP/FTPS client — connections, uploads, downloads, TLS negotiation, passive mode | 6 |
Both libraries are wrapped behind the ITransferClient abstraction (Section 6.2). Application code never references SSH.NET or FluentFTP types directly outside the protocol adapter implementations.
3.4 Cryptography & Key Management
| Package | Purpose | Section |
|---|---|---|
| BouncyCastle (Portable.BouncyCastle) | PGP operations — encrypt, decrypt, sign, verify, key generation, key import/export, armor encoding, keyring parsing | 7 |
| System.Security.Cryptography (.NET) | AES-256-GCM encryption at rest, RSA/ECDSA key generation, SHA-256/384/512 hashing, CSPRNG | 7, 12 |
| Azure.Security.KeyVault.Keys | Master key (KEK) management — wrap/unwrap DEKs | 7, 12 |
| Azure.Security.KeyVault.Secrets | Application secrets (connection strings, Entra client secret) | 12 |
| Azure.Identity | DefaultAzureCredential for Key Vault authentication (Managed Identity in prod, Azure CLI in dev) | 12 |
FIPS note: When FIPS mode is enabled, internal cryptographic operations use .NET's System.Security.Cryptography APIs (backed by CNG on Windows, OpenSSL on Linux) restricted to FIPS-approved algorithms. Whether the underlying module runs in its FIPS-validated mode depends on OS/container configuration (Section 12.10). BouncyCastle (standard edition) is restricted to approved algorithms but is not a FIPS-validated module; a migration path to bcpg-fips-csharp exists if validated PGP operations are required.
3.5 Compression
| Package | Purpose | Section |
|---|---|---|
| SharpZipLib | ZIP (with AES-256 password support), GZIP, TAR, TAR.GZ — compress and decompress | 8 |
| SharpCompress | RAR decompression only (RAR creation is proprietary) | 8 |
| 7z CLI (system dependency) | 7z format — compress and decompress via Process wrapper | 8 |
All compression operations use streaming APIs and never load full files into memory. The ICompressionProvider abstraction (Section 8.1) wraps each library.
3.6 Authentication & Authorization
| Package | Purpose | Section |
|---|---|---|
| Microsoft.Identity.Web | Entra ID JWT bearer token validation, OIDC metadata, token claim extraction | 12 |
| Microsoft.AspNetCore.Authentication.JwtBearer | ASP.NET JWT authentication middleware | 12 |
Roles (Admin, Operator, Viewer) are defined as Entra ID App Roles and enforced via [Authorize(Roles = "...")] attributes (Section 12.2).
3.7 API & Validation
| Package | Purpose | Section |
|---|---|---|
| Swashbuckle.AspNetCore | OpenAPI/Swagger spec generation, Swagger UI (dev/staging only) | 10 |
| FluentValidation | Request body validation with auto-discovery | 10 |
| FluentValidation.AspNetCore | ASP.NET Core integration (validation filter) | 10 |
3.8 Database & Migrations
| Package | Purpose | Section |
|---|---|---|
| DbUp | Database migration runner — numbered SQL scripts, embedded resources, executed on API startup only (Section 13.1) | 13 |
| DbUp.PostgreSQL | PostgreSQL provider for DbUp | 13 |
| Npgsql | Low-level PostgreSQL driver (used by EF Core and DbUp) | 13 |
Migrations are raw SQL scripts managed via DbUp. EF Core is not used for migrations — it is used only for querying and change tracking against the schema that DbUp creates.
3.9 JSON Serialization
| Package | Purpose | Section |
|---|---|---|
| System.Text.Json | Primary serializer — API requests/responses, JSONB columns, configuration | All |
| Newtonsoft.Json | Required by Quartz.NET AdoJobStore serialization; used nowhere else | 5 |
Configuration:
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower));
});
Enums are serialized as snake_case strings ("retry_step", "sftp") rather than integers, for API readability. JSONB columns in PostgreSQL use the same serializer settings via EF Core's HasConversion or Npgsql's ConfigureJsonOptions.
3.10 Logging & Observability
| Package | Purpose | Section |
|---|---|---|
| Serilog | Structured logging framework | 12 |
| Serilog.Sinks.Console | Console output (all environments) | — |
| Serilog.Sinks.Seq | Seq sink for development — rich local log search and dashboards | — |
| Serilog.Sinks.ApplicationInsights | Azure Application Insights sink for staging/production | — |
| Serilog.Enrichers.Environment | Machine name, process ID enrichment | — |
| Serilog.Enrichers.Thread | Thread ID enrichment | — |
| Serilog.AspNetCore | ASP.NET Core request logging integration | — |
Configuration:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Quartz", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithThreadId()
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.Conditional(
_ => builder.Environment.IsDevelopment(),
config => config.Seq("http://localhost:5341"))
.WriteTo.Conditional(
_ => !builder.Environment.IsDevelopment(),
config => config.ApplicationInsights(
builder.Configuration["ApplicationInsights:ConnectionString"],
TelemetryConverter.Traces))
.CreateLogger();
Sensitive data redaction: Serilog destructuring policies are configured to redact passwords, passphrases, private key material, and bearer tokens from all log output (Section 12.8).
3.11 Testing
| Package | Purpose |
|---|---|
| xUnit | Test framework — test discovery, execution, parallelization |
| Shouldly | Assertion library — fluent, readable assertions with rich failure messages |
| NSubstitute | Mocking library — substitute interfaces and verify interactions |
| Microsoft.AspNetCore.Mvc.Testing | Integration test host — in-memory API server for endpoint testing |
| Testcontainers.PostgreSql | Disposable PostgreSQL containers for database integration tests |
| Bogus | Fake data generation for test fixtures |
Test project structure:
Courier.Tests.Unit/ Unit tests — pure logic, all dependencies mocked
Courier.Tests.Integration/ Integration tests — real database (Testcontainers), real HTTP
Courier.Tests.Architecture/ Architecture tests — enforce dependency rules, naming conventions
3.12 Infrastructure & Deployment
| Technology | Purpose | Section |
|---|---|---|
| Docker | Containerization — multi-stage builds for API and background services | 14 |
| Kubernetes / Azure Container Apps | Container orchestration (deployment target) | 14 |
| .NET Aspire | Local development orchestration, service discovery, health checks | 14 |
| Azure Database for PostgreSQL Flexible Server | Managed PostgreSQL with TDE, automated backups | 13, 14 |
| Azure Key Vault | Master key storage (FIPS 140-2 Level 2/3), application secrets | 7, 12 |
| Azure Application Insights | Production telemetry, distributed tracing, alerting | — |
| Azure Blob Storage | Cold storage for archived partition data | 13 |
3.13 Package Version Policy
Version constraints:
- .NET 10 and ASP.NET Core 10: Pinned to the major version. Minor/patch updates applied via regular dependency updates.
- EF Core: Matches the .NET major version (EF Core 10 with .NET 10).
- All NuGet packages: Use the latest stable release at project initialization. Pinned to major version with
~>range to allow patch updates. Breaking changes require an explicit upgrade decision. - PostgreSQL: Minimum version 16 for native partitioning performance improvements and JSONB enhancements.
Dependency audit: dotnet list package --vulnerable is run as part of the CI pipeline. Packages with known CVEs are flagged as build warnings (high severity) or build failures (critical severity).
3.14 Library Decision Summary
┌──────────────────┬──────────────────────────────────────────────┐
│ Concern │ Decision │
├──────────────────┼──────────────────────────────────────────────┤
│ Runtime │ .NET 10 │
│ Frontend │ Next.js (React) │
│ Database │ PostgreSQL 16+ │
│ ORM │ EF Core 10 (query only, not migrations) │
│ Migrations │ DbUp (raw SQL scripts) │
│ Scheduling │ Quartz.NET (AdoJobStore) │
│ SFTP │ SSH.NET │
│ FTP/FTPS │ FluentFTP │
│ PGP │ BouncyCastle (FIPS-approved algorithms only) │
│ Encryption │ .NET System.Security.Cryptography (CNG/OSSL) │
│ Key management │ Azure Key Vault │
│ Compression │ SharpZipLib + SharpCompress + 7z CLI │
│ Auth │ Entra ID via Microsoft.Identity.Web │
│ API docs │ Swashbuckle (OpenAPI/Swagger) │
│ Validation │ FluentValidation │
│ JSON (primary) │ System.Text.Json │
│ JSON (Quartz) │ Newtonsoft.Json │
│ Logging │ Serilog → Seq (dev) / App Insights (prod) │
│ Testing │ xUnit + Shouldly + NSubstitute │
│ Orchestration │ .NET Aspire (dev), Docker + K8s (prod) │
└──────────────────┴──────────────────────────────────────────────┘