An AOT (Ahead-of-Time) enabled ASP.NET 10 Web API, designed to serve as a robust backend for an application requiring efficient and secure booking management with a PostgreSQL database. The service is designed on a minimalist clean architecture emphasizing performance and maintainability without ceremony.
It is multi-tenant: many businesses share one PostgreSQL database, each seeing only its own data, with isolation enforced by the database itself.
- AOT Compilation: Leverages .NET 10's Native AOT with aggressive trimming.
- Build-time OpenAPI: Compile-time generated OpenAPI spec with zero runtime overhead.
- Source Generators: Extensive use of compile-time code generation:
- ServiceScan for automatic DI registration
- LoggerMessage for high-performance logging
- Json serializer for serialization
- OptionsValidator for compile-time options validation
- Dapper.AOT for ORM mapping/SQL queries
- Custom infrastructure query builder helper
- IAsyncEnumerable Streaming: Unbuffered streaming for collection endpoints with async iteration
- Channels: Async pipelines for immediate response with deferred processing.
- Background Services: In-process email processing on the API; cross-tenant scheduled jobs (reminder delivery, refresh-token flush) run in a dedicated
workerrun-mode container that holds only theBYPASSRLSbatch credential. - Activity Tracking: Historic audit logging.
- Problem Details: Structured error responses using RFC 7807 (ProblemDetails) standard with detailed validation errors.
- CLEF structured logging via NLog: Compact Log Event Format for efficient json log sink.
- Container Security: Runs rootless in tiny Alpine Linux images (~17MB image) with immutable filesystem.
- Password handling: Email-verified user accounts with BCrypt password hashing and timing attack mitigation.
- PostgreSQL Backend: Reliable data storage in Npgsql 18 container with schema bootstrapping.
- JWT Authentication: Secure API authentication with Microsoft.AspNetCore.Authentication.JwtBearer.
- Refresh Token Rotation: Opaque refresh tokens with 240-bit cryptographic entropy, family-based tracking for reuse detection, and automatic revocation on compromise.
- HttpOnly Cookies: Secure refresh token storage with
HttpOnly,Secure,SameSite=Strict, and path-scoped attributes to prevent XSS and CSRF attacks. - Token Management: SHAKE128 hashing for database storage, transactional token rotation, and automated daily cleanup of expired tokens.
- Role-based Authorization: Fine-grained access control using ASP.NET Core's built-in authorization policies.
- Multi-Tenancy: One shared PostgreSQL database isolated by
FORCE ROW LEVEL SECURITYplus one login role per tenant (t_<id>). The RLS policy keys on the connected role (current_user), so isolation is DB-enforced and unforgeable — no per-request session variable. The API resolves each tenant from its subdomain (<slug>.booqr.dk); the API image'sadminrun-mode provisions tenants, roles, and migrations. Per-tenant role passwords are HMAC-derived; cross-tenant scheduled jobs run in a separateworkercontainer as a dedicatedBYPASSRLSrole, decoupled from the tenant master secret so the internet-facing API never carries the batch credential. - Docker Compose: Wraps the service with HAProxy gateway in the front, PostgreSQL in the back, a long-running internal-only
workercontainer for cross-tenant scheduled jobs, an internal-onlyadmincontainer for provisioning/migrations, and efficient UNIX sockets for inter-container communication.
bigintid truncation on bookings/calendar: Thebookings.idandcalendar.idcolumns arebigintin the database (seeMigrations/0001_baseline.sql— "bigint promotion"), but the C# domain represents entity ids asintvia the sharedAudit.Id/IId(andint-keyed shared request/route typesByIdRequest,{id:int}).BookingRepository.Addnarrows the returned identity withConvert.ToInt32, which throwsOverflowExceptiononce an identity value exceedsint.MaxValue(~2.1 billion). A full fix requires threadinglongthrough the shared id types and the booking/calendar endpoints; it is deferred because those types are shared by every entity and widening them changes the wire contract forint-keyed entities too.
The Klinkby.Booqr solution is organized into several projects, each responsible for a distinct aspect of the application:
- Core: Defines core domain models, interfaces, and common utilities used across all layers.
- Application: Contains the application's business logic and orchestrates operations between the API and infrastructure layers.
- Infrastructure: Manages data access (e.g., interaction with PostgreSQL) and external service integrations.
Includes
Klinkby.Booqr.Infrastructure.Generatorsfor code generation. - Api: The entry point of the application, containing middleware configuration and all things HTTP. Run-mode is chosen by a leading arg: the web/API host (default), the control-plane admin CLI (
Admin/,admin --migrate|--provision|--deprovision|--rotate), and the scheduledworker(Worker/). Theadminandworkermodes run as the same image in separate internal containers;adminholds the elevated provisioning credentials and never starts the web pipeline. - Tests: Contains unit and integration tests for various components of the solution, including
Klinkby.Booqr.Api.Tests,Klinkby.Booqr.Application.Tests, andKlinkby.Booqr.Infrastructure.Testsusing test containers to spin up a live PostgreSQL instance.
- dotnet 10
- Npgsql for PostgreSQL access
- Dapper + AOT for high-performance data access
- BCrypt.Net for secure password hashing
- NLog for structured logging
Copyright (C) 2026 Mads Klinkby (https://www.kli.dk)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.