DRN.Framework.Utils 0.9.7

Prefix Reserved
dotnet add package DRN.Framework.Utils --version 0.9.7
                    
NuGet\Install-Package DRN.Framework.Utils -Version 0.9.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DRN.Framework.Utils" Version="0.9.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DRN.Framework.Utils" Version="0.9.7" />
                    
Directory.Packages.props
<PackageReference Include="DRN.Framework.Utils" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DRN.Framework.Utils --version 0.9.7
                    
#r "nuget: DRN.Framework.Utils, 0.9.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DRN.Framework.Utils@0.9.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DRN.Framework.Utils&version=0.9.7
                    
Install as a Cake Addin
#tool nuget:?package=DRN.Framework.Utils&version=0.9.7
                    
Install as a Cake Tool

master develop Quality Gate Status

Security Rating Maintainability Rating Reliability Rating Vulnerabilities Bugs Lines of Code Coverage

DRN.Framework.Utils

Core utilities package providing attribute-based dependency injection, configuration management, scoped logging, ambient context, and essential extensions.

TL;DR

  • Attribute DI[Scoped<T>], [Singleton<T>], [Transient<T>] for zero-config service registration
  • ConfigurationIAppSettings with typed access, [Config("Section")] bindings
  • App data rootsIAppData resolves temp/data paths with traversal-safe child paths
  • Scoped LoggingIScopedLog aggregates structured logs per request
  • Scoped Cancellation — Explicit root cancel-all plus stable keyed groups with optional type ownership
  • AES-256 single block — Explicit runtime-intrinsic and portable paths with automatic fallback
  • Validators — Reusable payload validators such as JpegValidator
  • Monotonic Pagination — Cursor-based pagination leveraging entity ID temporal ordering
  • Bit PackingNumberBuilder for compact custom data structures
  • Ambient ContextScopeContext.UserId and ScopeContext.Settings within initialized DRN Hosting request scopes
  • Auto-RegistrationAddServicesWithAttributes() scans and registers public, concrete attributed services
  • SourceKnownEntityId utilities — generation, validation, secure/plain conversion, auto-detecting Parse

Table of Contents


QuickStart: Beginner

Register and use a service with attribute-based DI:

// 1. Define your service with DI attribute
public interface IGreetingService { string Greet(string name); }

[Scoped<IGreetingService>]
public class GreetingService : IGreetingService
{
    public string Greet(string name) => $"Hello, {name}!";
}

// 2. Register public, concrete attributed services in Startup
services.AddServicesWithAttributes();

// 3. Inject and use
public class HomeController(IGreetingService greetingService) : Controller
{
    public IActionResult Index() => Ok(greetingService.Greet("World"));
}

QuickStart: Advanced

Complete example with configuration binding, scoped logging, and ambient context:

// Bind configuration section to strongly-typed class
[Config]
public class PaymentSettings
{
    public string ApiKey { get; set; } = "";
    public int TimeoutSeconds { get; set; } = 30;
}

// Service using scoped logging and settings
[Scoped<IPaymentService>]
public class PaymentService(IAppSettings settings, IScopedLog log, PaymentSettings config) : IPaymentService
{
    public async Task<PaymentResult> ProcessAsync(decimal amount)
    {
        // Track execution time
        using var duration = log.Measure("PaymentProcessing");
        
        // Add structured context
        log.Add("Amount", amount);
        log.AddToActions("Processing payment");
        
        // Access ambient data inside an initialized DRN Hosting request scope
        var userId = ScopeContext.UserId;
        
        // Use typed configuration
        if (config.TimeoutSeconds < 10)
            throw ExceptionFor.Configuration("Timeout too short");
        
        return new PaymentResult(Success: true);
    }
}

Setup

If you are using DRN.Framework.Hosting (inheriting from DrnProgramBase), this package is automatically registered and validated.

For manual registration (e.g. Console Apps, Workers):

// Registers attributes, HybridCache, and TimeProvider
builder.Services.AddDrnUtils();

AddDrnUtils() does not create an ambient ScopeContext; console and worker code should use injected services.

HybridCache Registration

AddDrnUtils() registers Microsoft's HybridCache with default in-memory caching. To configure distributed caching (e.g., Redis), add your IDistributedCache registration before calling AddDrnUtils():

// Optional: Add distributed cache backend
builder.Services.AddStackExchangeRedisCache(options => 
{
    options.Configuration = "localhost:6379";
});

// HybridCache will use the distributed cache if available
builder.Services.AddDrnUtils();

For DRN Hosting rate limiting, use HybridCache to cache tenant plan, feature flag, or quota policy data. Do not treat HybridCache / IDistributedCache as an atomic distributed rate-limit counter by itself; hard multi-instance quotas need a backend designed for atomic operations, such as Redis with server-side Lua scripts, or enforcement at an API gateway/CDN/WAF layer.

Dependency Injection

Attribute-Based Registration

Reduce configuration boilerplate by using attributes directly on services. AddServicesWithAttributes() scans the calling assembly by default; pass an Assembly argument to scan a specific target assembly.

Attribute Lifetime Usage
[Singleton<T>] Singleton [Singleton<IMyService>] public class MyService : IMyService
[Scoped<T>] Scoped [Scoped<IMyService>] public class MyService : IMyService
[Transient<T>] Transient [Transient<IMyService>] public class MyService : IMyService
[SingletonWithKey<T>] Singleton (Keyed) [SingletonWithKey<IMyService>("key")]
[ScopedWithKey<T>] Scoped (Keyed) [ScopedWithKey<IMyService>("key")]
[TransientWithKey<T>] Transient (Keyed) [TransientWithKey<IMyService>("key")]
[HostedService] Singleton [HostedService] public class MyWorker : BackgroundService
[Config] Singleton [Config("Section")] public class MySettings
[ConfigRoot] Singleton [ConfigRoot] public class RootSettings

[Singleton<T>], [Scoped<T>], [Transient<T>], and their keyed variants accept an optional tryAdd parameter (default: true). When true, TryAdd is used so existing registrations are not overwritten. Set it to false to allow multiple implementations of the same service type.

Assembly scan metadata is cached, while registration modules and startup-validation state remain isolated to each service collection and provider. Repeating registration for the same assembly on one service collection is idempotent.

Hosted Services

Use [HostedService] to register IHostedService/BackgroundService implementations without manual AddHostedService<T>() calls. The class must implement IHostedService; otherwise the attribute is silently ignored.

[HostedService]
public class MyBackgroundWorker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // Do periodic work
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
}

Validation & Testing

DrnProgramBase automatically runs this validation at startup.

  • Validation: Ensure all registrations are resolvable via ValidateServicesAddedByAttributesAsync().
// In Program.cs
await app.Services.ValidateServicesAddedByAttributesAsync();

In integration tests with DRN.Framework.Testing:

[Theory, DataInline]
public async Task Validate_Dependencies(DrnTestContext context)
{
    context.ServiceCollection.AddServicesWithAttributes(); // Register local assembly
    await context.ValidateServicesAsync(); // Verifies attribute-registered services can be resolved
}

Scoped Cancellation

ICancellationUtils owns a root and keyed child scopes within the current DI service scope.

Intent API Effect
Cancel all scoped work cancellation.Root.Cancel() or cancellation.Root.Merge(token) Reaches every existing and later-created child.
Cancel a component or workflow GetOrCreateScope(key).Cancel() or .Merge(token) Affects only that group.
Cancel one operation A local linked CancellationTokenSource Affects only caller-owned work.
public sealed class CheckoutWorkflow(ICancellationUtils cancellation)
{
    private static readonly CancellationScopeKey ScopeKey =
        CancellationScopeKey.For<CheckoutWorkflow>();

    public async Task RunAsync(
        CancellationToken workflowLifetimeToken,
        CancellationToken operationToken)
    {
        var scope = cancellation.GetOrCreateScope(ScopeKey);

        using var operationSource = CancellationTokenSource
            .CreateLinkedTokenSource(scope.Token, workflowLifetimeToken, operationToken);

        await SomeAsyncOp(operationSource.Token);
    }

    public void CancelWorkflow() => cancellation.GetOrCreateScope(ScopeKey).Cancel();

    public void CancelEverything() => cancellation.Root.Cancel();
}

The same key returns the same scope and token. Root cancellation reaches every child, while child cancellation does not affect the root or other groups. Canceled scopes cannot be reset.

Keys can be type-owned or ownerless. Prefer CancellationScopeKey.For<T>() for a compile-time type or For(Type) for a runtime type. Add a name with For<T>(name) or For(Type, name) when one type owns multiple intentional groups. Use CancellationScopeKey.For(name) only when different types intentionally share one group.

Names use ordinal, case-sensitive equality and must be non-null developer-defined constants of at most 128 characters (empty string and whitespace are permitted). Keys are opaque and factory-created; the default value is invalid.

Ownerless keys share one ordinal-name namespace within the current ICancellationUtils service scope. Although empty and whitespace names are valid, prefer qualified, centrally defined names such as "MyPackage.CheckoutShutdown", because unrelated callers using the same ownerless name receive the same scope and can cancel each other's work.

Do not derive keys from request data, user input, instance IDs, or operation IDs because these values represent individual work rather than shared component or workflow lifetimes. ICancellationUtils owns returned scopes; callers own and dispose local linked sources.

For root-wide migration, replace cancellation.Cancel(), Merge(token), Token, and IsCancellationRequested with their cancellation.Root equivalents.

Module Registration & Startup Actions

Services can require complex registration logic or post-startup actions. Attributes inheriting from ServiceRegistrationAttribute handle this.

Example: DrnContext<T> (in DRN.Framework.EntityFramework) is decorated with [DrnContextServiceRegistration], which:

  1. Registers the DbContext.
  2. Runs startup migration handling; Development auto-migration occurs when DrnDevelopmentSettings:AutoMigrateDevelopment is enabled (default: true).
// The base class DrnContext handles the registration attributes.
// You just inherit from it, and your context is auto-registered with migration support.
public class MyDbContext : DrnContext<MyDbContext> { }

Configuration

IAppSettings

Access configuration using strongly-typed environment checks and utility methods.

public class MyService(IAppSettings settings)
{
    public void DoWork()
    {
        if (settings.IsDevelopmentEnvironment) { /* dev-only logic */ }
        if (settings.IsStagingEnvironment) { /* staging-only logic */ }
        
        var conn = settings.GetRequiredConnectionString("Default");
        var value = settings.GetValue<int>("MySettings:Timeout", 30);
        var debugSummary = settings.GetDebugView().ToSummary(); // best-effort key-name redaction
    }
}

GetDebugView(includeRawValues: true) only includes raw values in Development. Summaries apply best-effort key-name redaction, not a complete security boundary; review them before logging or exposure. Child keys remain listed even when a provider also defines a scalar value for the parent section, and summary paths use the value provider's key casing. Object-based configuration helpers serialize through the framework JSON defaults and therefore use camelCase keys; explicit key/value configuration preserves the key text supplied by the caller.

Configuration Attributes ([Config])

Bind classes directly to configuration sections. These are registered as Singletons.

[Config("PaymentSettings")] // Binds to "PaymentSettings" section
public class PaymentOptions 
{ 
    public string ApiKey { get; set; }
}

[Config] // Binds to "FeatureFlags" section (class name)
public class FeatureFlags { ... }

[ConfigRoot] // Binds to root configuration
public class RootSettings { ... }

Configuration Sources

The framework automatically loads configuration in this order:

  1. appsettings.json
  2. appsettings.{Environment}.json
  3. User Secrets when the application assembly is available
  4. Environment variables (ASPNETCORE_, DOTNET_, then unprefixed)
  5. Mounted Settings:
    • /appconfig/key-per-file-settings/*
    • /appconfig/json-settings/*.json
  6. Command-line arguments

Environment is required and must be Development, Staging, or Production. DRN validates the value used to select appsettings.{Environment}.json; define it in appsettings.json, environment variables, mounted settings, or command-line arguments, and do not override it in environment-specific JSON or user secrets.

Override the mount directory by registering IMountedSettingsConventionsOverride.

IAppSettings Troubleshooting

Symptom Cause Solution
ConfigurationException on startup Missing or invalid required configuration Inspect the reported key and correct its source value
Environment setting is missing Required Environment key not configured Set Environment to Development, Staging, or Production in appsettings.json, environment variables, mounted settings, or command-line arguments
GetRequiredConnectionString throws Connection string not found Verify key exists under ConnectionStrings section
IsDevelopmentEnvironment always false Resolved Environment is not Development Set the Environment configuration key to Development in an applicable source
Mounted settings not loading Wrong mount path Verify files exist at /appconfig/json-settings/ or override via IMountedSettingsConventionsOverride
Environment variables not binding Wrong naming format Use __ (double underscore) for nested keys: MySection__MyKey

App Data Settings

DrnAppDataSettings controls required temp/data roots. Overrides use process environment variables because roots resolve before DRN configuration.

Environment variable Purpose
DrnAppDataSettings__TempPath Overrides the temp base; the resolved temp path is <TempPath>/<EntryAssemblyNameNormalized>.
DrnAppDataSettings__DataPath Overrides the resolved data root as <DataPath>; the resolved temp path is <DataPath>/Temp/<EntryAssemblyNameNormalized> when temp is unset.

Set DrnAppDataSettings:RequireTemp or DrnAppDataSettings:RequireData to fail startup when the resolved path is not valid.

DrnAppFeatures

Feature flags and runtime knobs bound from the DrnAppFeatures configuration section via [Config].

{
  "DrnAppFeatures": {
    "SeedData": false,
    "SeedKey": "Peace at home! Peace in the world! - Mustafa Kemal Atatürk (1931)",
    "DisableRequestBuffering": false,
    "MaxRequestBufferingSize": 0,
    "DrnRateLimit": {
      "Disabled": false,
      "TokenLimit": 100,
      "ReplenishmentSeconds": 60,
      "TokensPerPeriod": 100,
      "PreAuthTokenLimit": 1000,
      "PreAuthReplenishmentSeconds": 60,
      "PreAuthTokensPerPeriod": 1000,
      "PostAuthTokenLimit": 0,
      "PostAuthReplenishmentSeconds": 0,
      "PostAuthTokensPerPeriod": 0
    }
  }
}

DrnRateLimit is the configuration key; application code reads the same settings through IAppSettings.Features.RateLimit. Shared values apply to both DRN Hosting rate limiting phases. Phase-specific values set to 0 inherit the shared value; positive phase-specific values override it. Treat these values as global defaults; tenant plan, feature-flag, and account-specific quotas belong in DRN Hosting rate-limit rules. See the Hosting README rate limiting settings for operational guidance, endpoint metadata behavior, and production scaling notes.

Nested option objects must be validated explicitly before relying on child data annotations for startup safety. DrnAppFeatures validates DrnRateLimit as part of root validation because plain Validator.TryValidateObject does not recursively walk nested objects by itself.

DrnAppFeatures.SeedKey feeds AppSecuritySettings. AppSecuritySettings derives AppHashKey, AppEncryptionKey, AppKey, and AppSeed through BLAKE3 derive-key mode with distinct DRN Framework context strings. AppHashKey and AppEncryptionKey remain Base64Url-encoded 32-byte values, AppKey remains an 8-character public discriminator, and AppSeed remains a signed 64-bit seed value. Changing SeedKey changes app-specific names, rate-limit keyed hash outputs, Development default Nexus key material, and seed-dependent operations.

Property Type Default Description
ApplicationStartedBy string? null Identifies which test started the application (set automatically by DrnTestContext).
SeedData bool false Enables data seeding on startup.
SeedKey string "Peace at home!…" Secret key for seed operations. Enforced [SecureKey(MinLength = 58)].
InternalRequestHttpVersion string "1.1" HTTP version used by IInternalRequest.
InternalRequestProtocol string "http" Protocol scheme used by IInternalRequest (e.g., http, https).
UseMonotonicDateTimeProvider bool false Reserved experimental flag for monotonic time-provider behavior data; it is not wired as a provider switch.
DisableRequestBuffering bool false Disables request body buffering entirely. Use for high-throughput services (e.g., file upload endpoints).
MaxRequestBufferingSize int 0 (→ 30,000) Maximum request body size to buffer in bytes. Values below 10,000 are ignored; 0 uses the 30,000-byte default.
DrnRateLimit.Disabled bool false Disables both pre-auth and post-auth DRN Hosting rate limiting layers.
DrnRateLimit.PartitionLogMode RateLimitPartitionLogMode KeyedHash Controls rejected IP/partition logging. KeyedHash logs deterministic keyed hashes for correlation; PlainText logs raw values and should be limited to controlled development or dedicated audit sinks.
DrnRateLimit.TokenLimit int 100 Token bucket burst capacity. Must be positive.
DrnRateLimit.ReplenishmentSeconds int 60 Token replenishment period in seconds. Must be positive.
DrnRateLimit.TokensPerPeriod int 100 Tokens added per replenishment period. Must be positive.
DrnRateLimit.PreAuthTokenLimit int 1000 Coarse pre-auth burst capacity for shared B2B NAT/VPN/CDN egress addresses. 0 inherits TokenLimit.
DrnRateLimit.PreAuthReplenishmentSeconds int 60 Pre-auth replenishment period. 0 inherits ReplenishmentSeconds.
DrnRateLimit.PreAuthTokensPerPeriod int 1000 Pre-auth tokens per period. 0 inherits TokensPerPeriod.
DrnRateLimit.PostAuthTokenLimit int 0 Optional post-auth burst capacity. 0 inherits TokenLimit.
DrnRateLimit.PostAuthReplenishmentSeconds int 0 Optional post-auth replenishment period. 0 inherits ReplenishmentSeconds.
DrnRateLimit.PostAuthTokensPerPeriod int 0 Optional post-auth tokens per period. 0 inherits TokensPerPeriod.

Request buffering and rate limiting settings are consumed by DRN.Framework.Hosting. See the Hosting README for middleware details.

NexusAppSettings and Nexus Keys

NexusAppSettings provides Nexus routing, source-known ID generator identity, secure/plain ID mode, and the Nexus key ring used by SourceKnownEntityIdUtils.

{
  "NexusAppSettings": {
    "NexusAddress": "localhost:5988",
    "AppId": 5,
    "AppInstanceId": 12,
    "UseSecureSourceKnownIds": true,
    "Keys": [
      {
        "KeyMaterial": "0123456789abcdef0123456789abcdef",
        "Format": "Utf8",
        "Default": true
      }
    ]
  }
}

Keys must contain exactly one default key. Generation always uses the default key. Parsing tries the default key first and then the remaining configured keys, so old IDs remain parseable during key rotation while the previous key stays in the key ring.

ByteEncoding Requirement
Utf8 Default when omitted. KeyMaterial must be exactly 32 UTF-8 bytes. ASCII 32-character values satisfy this; non-ASCII values are valid only when the UTF-8 byte count is exactly 32.
Hex KeyMaterial must hex-decode to exactly 32 bytes, normally 64 hex characters. A 32-character hex string is rejected because it decodes to 16 bytes.
Base64 KeyMaterial must Base64-decode to exactly 32 bytes.
Base64UrlEncoded KeyMaterial must Base64Url-decode to exactly 32 bytes. This is the format used by Development default key-material generation.

Invalid user-provided keys are not hashed, stretched, truncated, repaired, or treated as another format. Startup validation rejects malformed encodings, empty keys, wrong decoded lengths, and raw values that are not exactly 32 UTF-8 bytes. Exception messages avoid including the secret key value.

When no default Nexus key is configured in the Development environment, AppSettings derives deterministic 32-byte key material from AppSecuritySettings context-derived values with BLAKE3 derive-key mode. DrnAppFeatures.SeedKey feeds AppSecuritySettings. The generated key material is not random, is stored in memory as Format = Base64UrlEncoded, and then goes through the same BLAKE3 derive-key separation as configured keys.

Logging (IScopedLog)

IScopedLog aggregates structured operational data, metrics, checkpoints, and exceptions for a logical scope. In DRN Hosting request scopes, Hosting enriches and emits that aggregate as a single log entry.

Core Features

  • Contextual: DRN Hosting request scopes add TraceId, UserId, and RequestPath; custom ScopeData remains separate unless added explicitly.
  • Aggregation: Groups all actions, metrics, and exceptions into a single structured log entry.
  • Performance Tracking: Built-in measurement for code block durations and execution counts.
  • Exception Recording: AddException records exception details without changing control flow; callers remain responsible for recovery, rethrowing, and excluding sensitive data.

API Usage

public class OrderService(IScopedLog logger)
{
    public void ProcessOrder(int orderId)
    {
        // 1. Measure execution time and count
        using var _ = logger.Measure("ProcessOrder"); 
        
        // 2. Add structured data (Key-Value)
        logger.Add("OrderId", orderId); 
        logger.AddIfNotNullOrEmpty("Referrer", "PartnerA");

        // 3. Track execution checkpoints
        logger.AddToActions("Validating order"); 
        
        try 
        {
            // ... logic ...
            // 4. Flatten and add complex objects that are safe to log
            logger.AddProperties("User", new { Name = "John", Role = "Admin" });
        }
        catch(Exception ex)
        {
            // 5. Record the exception, then preserve failure semantics
            logger.AddException(ex, "Failed to process order");
            throw;
        }
    }
}

HTTP Client Factories (IExternalRequest, IInternalRequest)

Wrappers around Flurl for HTTP clients with standardized JSON conventions and HTTP version policy configuration. Retries/circuit breakers are not configured by this package.

External Requests

Use IExternalRequest for standard external API calls. It pre-configures DefaultJsonSerializer and enforces HTTP version policies.

public class PaymentService(IExternalRequest request)
{
    public async Task Process()
    {
        // Enforces exact HTTP version for better compatibility with modern APIs
        var response = await request.For("https://api.example.com", HttpVersion.Version11)
            .AppendPathSegment("v1/charges")
            .PostJsonAsync(new { Amount = 1000 })
            .FromJsonAsync<ExternalApiResponse>();
    }
}

Internal Requests (Service Mesh)

Use IInternalRequest for Service-to-Service communication in Kubernetes. It's designed to work with Linkerd, supporting automatic protocol switching (HTTP/HTTPS) based on infrastructure settings.

Instead of using IInternalRequest directly in business logic, wrap it in a typed request factory for better maintainability and configuration encapsulation.

// 1. Definition (External Factory Wrapper)
public interface INexusRequest { IFlurlRequest For(string path); }

[Singleton<INexusRequest>]
public class NexusRequest(IInternalRequest request, IAppSettings settings) : INexusRequest
{
    private readonly string _nexusAddress = settings.NexusAppSettings.NexusAddress;
    public IFlurlRequest For(string path) => request.For(_nexusAddress).AppendPathSegment(path);
}

// 2. Client Usage
public class NexusClient(INexusRequest request) : INexusClient
{
    public async Task<HttpResponse<string>> GetStatusAsync() =>
        await request.For("status").GetAsync().ToStringAsync();
}

Buffered response converters (ToStringAsync, ToBytesAsync, and FromJsonAsync) capture HttpStatus and Payload, then dispose the response even if reading or deserialization fails. Call converters as extension methods on Task<IFlurlResponse> or IFlurlResponse; HttpResponse models the converted result and no longer exposes static conversion entry points.

HttpResponse.StatusClass classifies the status as Informational, Success, Redirection, ClientError, ServerError, or Unknown; IsSuccessStatusCode is true only for 2xx. Flurl normally follows redirects, so a 3xx snapshot represents a redirect that remained final. Use AllowAnyHttpStatus() when the throwing converters should return non-success responses for direct inspection.

Use the TryToStringAsync, TryToBytesAsync, or TryFromJsonAsync<T> counterparts when transport, timeout, response-read, or deserialization failures must be inspected without catching exceptions:

var result = await request.For("status").GetAsync().TryFromJsonAsync<StatusResponse>();
if (result.Failure is { } failure)
{
    // Apply failure policy here; Payload may be unavailable.
    logger.LogWarning("HTTP conversion failed: {Kind}", failure.Kind);
}

if (result.StatusClass == HttpStatusClass.ClientError)
{
    // Handle 4xx. HttpStatus and a successfully converted Payload remain available.
}
else if (result.StatusClass == HttpStatusClass.ServerError)
{
    // Handle 5xx according to the caller's retry policy.
}

HTTP error statuses and processing failures are independent. For example, a 422 with readable JSON has StatusClass.ClientError and no Failure, while malformed JSON returned with 200 has StatusClass.Success, IsSuccess == false, and Failure.Kind == Deserialization. Try converters propagate cancellation. HttpFailure.Message and HttpFailure.Exception are available for local diagnostics, may contain request details, and must be redacted before logging or exposure; both are ignored by System.Text.Json serialization.

Call result.ThrowIfFailure() after inspection to rethrow a captured transport, timeout, response-read, or deserialization exception with its original stack preserved. The method does not throw for a 3xx, 4xx, or 5xx response without a processing failure; inspect StatusClass or IsSuccessStatusCode when status enforcement is required.

Use using for streaming responses so the payload and response are released together:

using var response = await request.For("export").GetAsync().ToStreamAsync();
await response.Payload!.CopyToAsync(destination);

HttpResponse<T>.Dispose() is idempotent and disposes any IDisposable payload. TryToStreamAsync() transfers the same ownership to HttpCallResult<T>; dispose that result after consuming its stream payload.

Scope & Ambient Context (ScopeContext)

ScopeContext provides ambient access to request-scoped data after a DRN Hosting request scope has been initialized. Use injected services during startup, in background work, and outside request scopes.

  • Contextual Identity: Access UserId, TraceId, and Authenticated status within an initialized request scope.
  • Static Accessors: Provides request-scope access to IAppSettings, IScopedLog, and IServiceProvider.
  • RBAC Helpers: Built-in support for role and claim checks.
  • Test Initialization: ScopeContext.InitializeForTest(...) resets the async-local scope before seeding test services, user, log, and trace data.

IScopedUser exposes authenticated identity and claim state. Use GetClaimParameter<TValue> for typed claims; ScopeContext.GetClaimParameter<TValue> provides ambient access to the same contract.

ScopeData is separate caller-owned ambient storage and is not automatically copied into IScopedLog. Use SetFlag and typed SetParameter values for validated application data.

var currentUserId = ScopeContext.UserId;
var traceId = ScopeContext.TraceId;
var settings = ScopeContext.Settings; // Static IAppSettings access
var logger = ScopeContext.Log; // Static IScopedLog access

if (ScopeContext.IsUserInRole("Admin")) { ... }

var tenantId = ScopeContext.GetClaimParameter<Guid>("tenant-id");
ScopeContext.Data.SetFlag("show-preview", true);
ScopeContext.Data.SetParameter("page-size", 50);

Data Utilities

App Data Roots (IAppData)

IAppData exposes validated temp/data roots. Normal startup recreates temp; DRN test contexts preserve sibling test data.

public class ExportService(IAppData appData)
{
    public string GetExportPath(string fileName) =>
        appData.Temp.GetPath("exports", fileName);
}

Use AppDataPathResult.GetPath(...) for traversal-safe child paths.

Encodings (EncodingExtensions)

Unified API for binary-to-text encodings and model serialization-encoding.

  • Encodings: Base64, Base64Url (Safe for URLs), Hex, and Utf8.
  • Integrated: model.Encode(ByteEncoding.Hex) and hexString.Decode<TModel>(ByteEncoding.Hex).

AES-256 Single-Block Encryption (Aes256)

Aes256 accepts and returns one Vector128<byte> block. It exposes explicit x86/ARM runtime-intrinsic and portable .NET AES paths; the default Encrypt and Decrypt methods select runtime intrinsics when available and otherwise use the portable provider. Construction therefore remains portable, while explicit runtime-intrinsic methods throw PlatformNotSupportedException on unsupported hosts.

Methods Implementation
Encrypt / Decrypt Runtime intrinsics with automatic portable fallback
EncryptRuntimeIntrinsics / DecryptRuntimeIntrinsics Explicit x86 AES-NI or ARM AES intrinsics
EncryptWithFramework / DecryptWithFramework Explicit cross-platform .NET AES provider

A live instance supports concurrent calls. Intrinsic operations read pre-expanded round keys without locks or per-call allocation. On .NET 10.0.10, framework-provider operations are also lock-free because each call creates its own cipher state without mutating the configured key. Reverify this framework-provider assumption when changing the target runtime. Dispose the instance after all callers finish to clear the intrinsic schedules and dispose portable key state.

Aes256 is a deterministic, single-block ECB primitive with no authentication. Do not compose it into multi-block ECB encryption.

using var aes = new Aes256(key);
Vector128<byte> ciphertext = aes.Encrypt(plaintext);
Vector128<byte> recovered = aes.Decrypt(ciphertext);

Vector128<byte> portableCiphertext = aes.EncryptWithFramework(plaintext);
if (Aes256.IsSupported)
{
    Vector128<byte> intrinsicPlaintext = aes.DecryptRuntimeIntrinsics(portableCiphertext);
}

Hashing (HashExtensions)

High-performance hashing extensions supporting modern and legacy algorithms.

  • Blake3: Default modern cryptographic hash (fast and secure).
  • XxHash3: Non-cryptographic hashing for performance-critical scenarios (IDs, Cache keys).
  • Security: Keyed hashing support (HashWithKey) for integrity protection.
  • Streams: Stream overloads hash files and large payloads without first materializing them as BinaryData; prefer these overloads for file and upload hashing.

JSON & Document Utilities

  • Safe JSON Merge Patch: JsonMergePatch.SafeApplyMergePatch(target, patch) implements RFC 7396 processing semantics without mutating either input. Semantic no-ops reuse the target; changed object targets are cloned once and merged without repeated subtree cloning. MergeResult is a readonly record struct, Json is nullable for a root-level JSON null result, and Changed reports only actual document changes.
  • In-Place JSON Merge Patch: JsonMergePatch.ApplyMergePatchInPlace(ref target, patch) and ApplyMergePatchInPlace(targetObject, patchObject) perform full RFC 7396 merge patch operations directly in-place, preserving existing nested object references and updating the ref target reference if the root type changes.
  • Resource Safety: All merge methods validate the complete patch depth before applying changes. The repository unit suite includes every RFC 7396 Appendix A example.
  • Query String Serialization: QueryParameterSerializer flattens complex nested objects/arrays into clean query strings for API clients.

Serialization & Streams

  • Unified Extensions: model.Serialize(method) supports both JSON and Query String formats.
  • Safe Stream Consumption: ToBinaryDataAsync and ToArrayAsync extensions with MaxSizeGuard to prevent memory exhaustion from untrusted streams.

Programmatic Validation

Extensions for programmatic validation using System.ComponentModel.DataAnnotations.

  • Contextual: Integrates with DRN.Framework.SharedKernel.ValidationException for standardized error reporting across layers.

Entity Creation-Date Filters (IEntityDateTimeUtils)

IEntityDateTimeUtils filters SourceKnownEntity.Id by its 250ms Source-Known ID creation tick without requiring database timestamp columns. Each date boundary maps to minimum and maximum scalar long ID bounds for efficient query evaluation.

public class OrderService(IEntityDateTimeUtils dateTimeUtils)
{
    public IQueryable<Order> GetOrdersInDateRange(IQueryable<Order> query, DateTimeOffset start, DateTimeOffset end)
    {
        return dateTimeUtils.CreatedBetween(query, start, end, inclusive: true);
    }
}
Filter Inclusive boundary Exclusive boundary
CreatedAfter Id >= tick.Min Id > tick.Max
CreatedBefore Id <= tick.Max Id < tick.Min
CreatedBetween Id >= begin.Min && Id <= end.Max Id > begin.Max && Id < end.Min
CreatedOutside Id <= begin.Max \|\| Id >= end.Min Id < begin.Min \|\| Id > end.Max

Pagination

The framework provides IPaginationUtils for cursor-based pagination ordered by the temporal sequence of SourceKnownEntityId.

public class OrderDto(Order order) : Dto(order)
{
    public bool Active { get; } = order.Active;
}

public class OrderService(IPaginationUtils pagination, OrderDbContext dbContext)
{
    public async Task<PaginationResultModel<OrderDto>> GetRecentOrdersAsync(PaginationRequest request)
    {
        var query = dbContext.Orders.Where(x => x.Active);
        var result = await pagination.GetResultAsync(query, request);
        return result.ToModel(order => new OrderDto(order));
    }
}

Bit Packing

For scenarios requiring custom ID generation or compact binary data structures, use NumberBuilder and NumberParser. NumberBuilder<TNumber> is a ref struct; NumberParser is a value-type parser for low-allocation bit manipulation.

// Use NumberBuilder to pack data into a long
var builder = NumberBuilder.GetLong();
builder.TryAddNibble(0x05);  // Add 4 bits
builder.TryAddUShort(65535); // Add 16 bits
long packedValue = builder.GetValue();

// Use NumberParser to unpack
var parser = NumberParser.Get(packedValue);
byte nibble = parser.ReadNibble();
ushort value = parser.ReadUShort();
// Multi-format serialization
var json = model.Serialize(SerializationMethod.SystemTextJson);
var query = model.Serialize(SerializationMethod.QueryString);

// Data Integrity
var hash = data.Hash(HashAlgorithm.Blake3);
var fileHash = fileStream.Hash(HashAlgorithm.Sha256);

// Secure stream conversion
var bytes = await requestStream.ToBinaryDataAsync(maxSize: 1024 * 1024);

Validators

Reusable validators live under DRN.Framework.Utils.Validators.

using DRN.Framework.Utils.Validators;

var validation = await JpegValidator.ValidateAsync(requestStream, maxLength: 1024 * 1024);
if (!validation.IsValid)
{
    var message = validation.ErrorReason switch
    {
        JpegValidationErrorReason.MaxLengthExceeded => "Profile picture exceeds the maximum allowed size.",
        JpegValidationErrorReason.InvalidMaxLength => "Profile picture maximum size must be zero or greater.",
        _ => "Profile picture must be a valid JPEG image."
    };
    throw ExceptionFor.Validation(message);
}

var imageBytes = validation.ImageData;

JpegValidator performs structural JPEG checks for markers, segment bounds, frame metadata, scan metadata, scan data presence, and optional maximum byte length. JpegValidationResult.ErrorReason distinguishes MaxLengthExceeded, InvalidMaxLength, and InvalidJpeg failures. Use ValidateAsync when validating an upload stream and keeping the validated bytes for persistence.

Diagnostics

Development Status

Track database migration status and pending model changes in real-time during development.

public class StartupService(DevelopmentStatus status, IScopedLog log)
{
    public void CheckStatus()
    {
        if (status.HasPendingChanges)
        {
            log.AddToActions("Warning: Pending database changes detected");
            foreach (var model in status.Models)
            {
                 model.LogChanges(log, "Development");
            }
        }
    }
}

Time & Async

High-Performance Time (TimeStampManager)

For systems requiring frequent timestamp lookups (like ID generation or rate limiting), TimeStampManager provides a cached UTC timestamp updated periodically (default 10ms) to reduce DateTimeOffset.UtcNow overhead.

long precisionTicks = TimeStampManager.CurrentTimestamp(EpochTimeUtils.DefaultEpoch);
DateTimeOffset now = TimeStampManager.UtcNow; // Cached UTC time truncated to 250ms precision

Async-Safe Timer (RecurringAction)

An atomic timer implementation that prevents overlapping executions if one cycle takes longer than the period.

using var worker = new RecurringAction(async () => {
    await DoHeavyWork();
}, period: 1000, start: true);

worker.Stop();
worker.Start(); // Resume after stopping

Stop() prevents an active callback from rescheduling the timer after it completes. The callback itself is allowed to finish.

ID Generation & Validation

SourceKnownEntity ID's provide reversible, type-safe, and integrity-checked identifiers.

ID generation is automatically handled by DrnContext when SourceKnownEntities are saved.

Generation Modes

The Generate method dispatches to secure or plain generation based on the UseSecureSourceKnownIds flag in NexusAppSettings (defaults to true). Explicit GenerateSecure and GeneratePlain methods are also available to bypass the flag.

Method Behavior
Generate Dispatches to secure or plain based on UseSecureSourceKnownIds
GenerateSecure AES-256-ECB encrypted — full 16-byte GUID is a ciphertext block
GeneratePlain Plaintext with visible 8D8D version/variant markers
ToSecure Converts a plain ID to its secure form (idempotent)
ToPlain Converts a secure ID to its plain form (idempotent)

Secure variant encrypts the entire 16-byte GUID with Aes256 as a pseudo-random permutation (PRP). For a single 128-bit block, ECB is mathematically identical to CBC with a zero IV — no nonce required, no nonce-reuse vulnerability. Key separation ensures BLAKE3 keyed MAC (integrity) and AES-256 (confidentiality) use cryptographically independent keys derived from the same decoded NexusKey material.

Generation uses the default NexusKey. Parse uses a default-first key-ring fallback, so IDs generated before key rotation can still be parsed while the previous key remains configured.

SourceKnownEntityIdUtils is a singleton and safely reuses each key-ring entry's Aes256 instance across concurrent calls. It uses lock-free runtime intrinsics when available and automatically falls back to the lock-free .NET 10.0.10 framework provider, preserving the encrypted ID format on hosts without AES intrinsics. Reverify this framework-provider concurrency assumption when changing the target runtime.

// Generate with flag-based dispatch (secure by default)
var entityId = sourceKnownEntityIdUtils.Generate<User>(id);

// Explicitly secure
var secureId = sourceKnownEntityIdUtils.GenerateSecure<User>(id);

// Explicitly plain (visible markers for debugging/development)
var plainId = sourceKnownEntityIdUtils.GeneratePlain<User>(id);

// Convert between secure and plain forms (idempotent)
var convertedSecureId = sourceKnownEntityIdUtils.ToSecure(plainEntityId);
var convertedPlainId = sourceKnownEntityIdUtils.ToPlain(secureEntityId);
Parse & Validation

Parse accepts secure and plaintext IDs and verifies their integrity.

Add rate limiting to endpoints that accept SourceKnownEntityId from untrusted sources to prevent brute-force attacks.

Users can validate incoming IDs (e.g., from APIs) using multiple approaches depending on the context:

1. Injectable Utility (Recommended for Service Layer)

var sourceKnownId = sourceKnownEntityIdUtils.Validate<User>(externalGuidId);

2. SourceKnownRepository (Recommended for Data Access)

// Method on SourceKnownRepository<TEntity>
var sourceKnownId = userRepository.GetEntityId(externalGuidId); 

3. SourceKnownEntity (Recommended for Domain Logic)

// Helper on SourceKnownEntity base class
var sourceKnownId = userInstance.GetEntityId<User>(externalGuidId);
GUID Byte Layout

The plaintext form of a SourceKnownEntityId (SKEID) packs identity, integrity, time-addressing, and UUID V8 compatibility (RFC 9562 §5.8) into a single 128-bit GUID. Secure IDs are opaque ciphertext and are not guaranteed to retain UUID version or variant bits.

Byte(s) Purpose
0 Epoch index (8 bits; current releases support epoch 0)
1–4 SKID upper half (32 bits, sign-toggled)
5 SKID low byte 0 (MSB of SKID lower half / timestamp LSB)
6 Version marker (0x8D — UUID V8, RFC 9562 §5.8)
7 Entity type (8 bits — up to 256 entity types)
8 Variant marker (0x8D — RFC 4122 compatible)
9–11 SKID low bytes (remaining 24 bits)
12–15 BLAKE3 keyed MAC (32 bits — integrity verification)
Epoch & Time Addressing

SourceKnownEntityIds use epoch-based time addressing for monotonic ordering. Current releases support the first epoch, which starts on 2025-01-01 and spans approximately 68 years ($2^{31}$ seconds total coverage, split across two halves).

Property Value
Epoch start 2025-01-01
Supported duration ~68 years ($2^{31}$ seconds)
Supported epoch 0

Current releases support epoch 0 only; generation rejects timestamps outside its supported range.

Time

TimeProvider singleton is registered by default to TimeProvider.System for testable time entry. See Time & Async for high-performance alternatives.

Concurrency

Lock-Free Atomic Utilities (LockUtils)

LockUtils provides static helpers for lock-free atomic operations built on Interlocked. Use these primitives to coordinate concurrent access without OS-level locks.

Method Purpose
TryClaimLock(ref int) Atomically claims a lock (0 → 1). Returns true if successful.
TryClaimScope(ref int) Returns a disposable LockScope that auto-releases on dispose.
ReleaseLock(ref int) Unconditionally releases a lock (→ 0).
TrySetIfEqual<T>(ref T?, T, T?) Atomic CAS for reference types; sets value if current is the same reference as comparand.
TrySetIfNull<T>(ref T?, T) Sets value only if current is null.
TrySetIfNotEqual<T>(ref T?, T, T?) Sets value only if current is not the same reference as comparand (retry loop).
TrySetIfNotNull<T>(ref T?, T) Sets value only if current is not null.

TrySetIfNotEqual and TrySetIfNotNull use bounded retries (maxRetries, default 100). A false result can mean either that the comparison condition was not met or that retries were exhausted.

// Disposable lock scope (preferred) — auto-releases on dispose
private int _lock;

using var scope = LockUtils.TryClaimScope(ref _lock);
if (scope.Acquired) { /* critical section */ }

// One-time initialization guard
private MyService? _instance;
var service = new MyService();
LockUtils.TrySetIfNull(ref _instance, service);

Extensions

Comprehensive set of extensions for standard .NET types and reflection.

Reflection & MethodUtils

Reflection helpers with built-in caching for generic and non-generic method invocation.

  • Invoke: instance.InvokeMethod("Name", args) and type.InvokeStaticMethod("Name", args).
  • Generics: instance.InvokeGenericMethod("Name", typeArgs, args) with static and uncached variations.
  • Caching: Caches generic and non-generic method lookups.

Service Collection

Advanced DI container manipulation for testing and modularity.

  • Querying: sc.GetAllAssignableTo<TService>() retrieves all descriptors matching a type.
  • Replacement: ReplaceScoped, ReplaceSingleton, and ReplaceInstance for mocking/overriding dependencies in integration tests.

String & Binary Extensions

  • Parsing: string.Parse<T>() and string.TryParse<T>(out result) using the modern IParsable<T> interface.
  • Binary: ToStream() and ToByteArray() shortcuts with UTF8 default.
  • FileSystem: GetLines() for IFileInfo with efficient physical path reading.

Casing and safe path helpers live in DRN.Framework.SharedKernel.Extensions.

Type & Assembly Extensions

  • Discovery: assembly.GetSubTypes(typeof(T)) and assembly.GetTypesAssignableTo(to).
  • Instantiation: assembly.CreateSubTypes<T>() automatically discovers and instantiates classes with parameterless constructors.
  • Metadata: type.GetAssemblyName() returns a clean assembly name.

Flurl & HTTP Diagnostics

  • Logging: PrepareScopeLogForFlurlExceptionAsync() adds Flurl failure diagnostics to IScopedLog, and DRN Hosting applies it to unhandled Flurl exceptions. Captured request and response data is not automatically redacted; catch sensitive failures before they reach Hosting, or use the Try* converters and log only sanitized fields.
  • Status Codes: GetGatewayStatusCode() preserves 4xx, 503, and 504 statuses and maps other statuses to 502.
  • Testing: ClearFilteredSetups() utility for complex test scenarios.

Object & Dictionary Extensions

  • Deep Discovery: instance.GetGroupedPropertiesOfSubtype(type) recursively finds properties matching a base type across complex object graphs.
  • Dictionary Utility: Extensions for IDictionary to handle null-safe value retrieval and manipulation.
  • Bit Manipulation: GetBitPositions() for long values and bitmask generators for signed/unsigned lengths.
// Discovery and Instantiation
var implementations = typeof(IMyInterface).Assembly.CreateSubTypes<IMyInterface>();

// Modern Parsing
int value = "123".Parse<int>();

// Binary shortcuts
using var body = "payload".ToStream();

Suggested Consumer Global Usings

global using DRN.Framework.SharedKernel;
global using DRN.Framework.SharedKernel.Extensions;
global using DRN.Framework.Utils.DependencyInjection;

For complete examples, see Sample.Hosted.


Documented with the assistance of DiSC OS


Semper Progressivus: Always Progressive

Commit Info

Author: Duran Serkan KILIÇ
Date: 2026-07-29 21:49:21 +0300
Hash: ba240f925c31f3f7a9d1d5f2bece8a68b4de8c12

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on DRN.Framework.Utils:

Package Downloads
DRN.Framework.EntityFramework

DRN.Framework.EntityFramework provides DrnContext with conventions to develop rapid and effective domain models. ## Commit Info Author: Duran Serkan KILIÇ Date: 2026-07-29 21:49:21 +0300 Hash: ba240f925c31f3f7a9d1d5f2bece8a68b4de8c12

DRN.Framework.Hosting

DRN.Framework.Hosting ## Commit Info Author: Duran Serkan KILIÇ Date: 2026-07-29 21:49:21 +0300 Hash: ba240f925c31f3f7a9d1d5f2bece8a68b4de8c12

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.7 39 7/29/2026
0.9.6 131 7/15/2026
0.9.6-preview004 131 7/7/2026
0.9.6-preview003 130 7/1/2026
0.9.6-preview002 134 6/29/2026
0.9.6-preview001 124 6/28/2026
0.9.5 149 6/14/2026
0.9.5-preview011 132 6/14/2026
0.9.5-preview010 126 6/14/2026
0.9.5-preview009 136 6/14/2026
0.9.5-preview008 133 6/14/2026
0.9.5-preview007 124 6/13/2026
0.9.5-preview006 131 6/7/2026
0.9.5-preview005 135 6/7/2026
0.9.5-preview004 127 6/7/2026
0.9.5-preview003 130 6/6/2026
0.9.5-preview002 134 6/2/2026
0.9.5-preview001 135 5/31/2026
0.9.4 157 5/13/2026
0.9.3 146 4/25/2026
Loading failed

Not every version includes changes, features or bug fixes. This project can increment version to keep consistency with other DRN.Framework projects.

## Version 0.9.7

### Breaking Changes

*   **CancellationScopeKey Relocation**: Moved `CancellationScopeKey` to `DRN.Framework.SharedKernel.Cancellation`. Callers should update namespace imports from `DRN.Framework.Utils.Cancellation` to `DRN.Framework.SharedKernel.Cancellation`.
*   **JSON Merge Patch API Split And Result Type**: Removed the `changeOriginal` Boolean from `JsonMergePatch.SafeApplyMergePatch`; the method now always leaves both inputs unchanged and reuses the target only for semantic no-ops. Use the new `ApplyMergePatchInPlace` method when in-place mutation is required. `MergeResult` changed from a record class to a readonly record struct, and its `Json` value is nullable because `System.Text.Json` represents root-level JSON `null` as a null `JsonNode`.
*   **Opaque Cancellation Scope Keys**: Removed the public `CancellationScopeKey.OwnerType` and `Name` identity accessors and the custom identity-revealing `ToString()` output. Continue creating type-owned keys with `For<T>()`, `For(Type)`, `For<T>(name)`, or `For(Type, name)` instead of inspecting their identity.
*   **HTTP Response Snapshots**: Removed `HttpResponse.Response` and changed public wrapper construction to accept an HTTP status. Use `HttpStatus` and `Payload`; dispose streaming wrappers after use.
*   **HTTP Response Conversion API**: Removed static conversion methods from `HttpResponse`. Call `ToStringAsync`, `ToBytesAsync`, `ToStreamAsync`, `FromJsonAsync<T>`, and their non-throwing `Try*` counterparts as extension methods on `Task<IFlurlResponse>` or `IFlurlResponse`.
*   **HTTP Response Ownership**: `HttpResponse<TResult>` is now sealed and implements `IDisposable`. Disposing it also disposes an `IDisposable` payload.
*   **Scope Data Authentication Separation**: `ScopeData` no longer stores role checks or parses claim strings. Its `Roles`, `IsRoleExists`, `SetParameterAsRole`, `SetParameterAsFlag`, and string-parsing `SetParameter` members were removed. Use `IScopedUser.IsInRole` / `ScopeContext.IsUserInRole` for roles, `IScopedUser.GetClaimParameter<TValue>` / `ScopeContext.GetClaimParameter<TValue>` for claims, and the typed `ScopeData.SetFlag` / `SetParameter` methods for caller-owned ambient values.

### New Features

*   **In-Place JSON Merge Patching**: Added `JsonMergePatch.ApplyMergePatchInPlace` overloads (`ref JsonNode? target` and `JsonObject target`) for executing full RFC 7396 merge patches in-place while preserving nested object references and updating root node references when necessary.
*   **Ownerless Named Cancellation Keys**: Support creating named cancellation keys without an owner type via `CancellationScopeKey.For(name)` for intentional cross-type groups. Ownerless keys share one ordinal-name namespace within the current cancellation service scope, so qualified, centrally defined names are recommended. Empty and whitespace names are valid, but the key name must not be null.
*   **AES-256 Single-Block Implementations**: Added `Aes256`, a disposable `Vector128<byte>` AES-256 ECB primitive with explicit x86/ARM runtime-intrinsic methods, explicit portable .NET AES methods, and default methods that use runtime intrinsics with automatic portable fallback. One live instance supports concurrent operations and clears or disposes its key state. Source-known ID encryption reuses the fallback methods without changing the encrypted ID format or requiring AES-intrinsic hardware.

### Changed

*   **JSON Merge Patch Allocation Model**: Safe changed-object merges clone the target once and mutate that detached tree without repeated nested subtree clones; semantic no-ops return the original target without a result allocation. The object-only in-place API avoids target-tree cloning for disjoint inputs and preserves existing nested object references.
*   **JSON Merge Patch Standard Reference**: Current APIs and documentation target RFC 7396, which obsoletes RFC 7386; historical release-note blocks retain their originally published wording.
*   **Configuration Debug View Security Guidance**: Package documentation now clarifies that `ConfigurationDebugView` summary redaction is best-effort, key-name-based redaction rather than a security boundary; protect debug summaries as potentially sensitive data.
*   **Inspectable HTTP Call Results**: Added HTTP status-class inspection and non-throwing `TryToStringAsync`, `TryToBytesAsync`, `TryToStreamAsync`, and `TryFromJsonAsync<T>` converters. Try results distinguish redirects, client errors, server errors, transport/timeouts, response-read failures, and JSON deserialization failures while preserving cancellation and response ownership semantics. `ThrowIfFailure()` rethrows captured processing failures with their original stack after inspection without treating HTTP error statuses as exceptions. Raw diagnostic exception messages and exception objects are excluded from System.Text.Json serialization.
*   **JIT-Safe Assembly Scanning**: Split `AddServicesWithAttributes` into a parameterless convenience overload (protected against JIT compiler inlining with `[MethodImpl(MethodImplOptions.NoInlining)]`) and an explicit `Assembly` overload. This prevents tail-call or inlining optimizations from unexpectedly altering assembly scanning results.
*   **Claim Lookup Allocation**: `ClaimGroup.GetValue`, `ClaimExists`, and `FindClaim` now traverse the frozen claim set directly instead of constructing LINQ iterators for single-result lookups.
*   **Scoped-User Typed Claims**: `IScopedUser` now exposes typed claim parsing through `GetClaimParameter<TValue>`, and `ScopeContext` delegates to that contract. `ScopedUser` resolves and parses each existing claim on demand without a separate parsed-value cache; missing claims return the supplied typed default unchanged so issuer, target-type, and default choices stay call-local.
*   **Scoped Cancellation Guidance**: Package guidance now distinguishes type-owned shared groups from caller-owned operation cancellation and documents type-only keys with optional names.

### Bug Fixes

*   **NumberParser Reset Type Safety**: `ResetToParse` now rejects values whose signedness does not match the parser instead of resetting the cursor while silently retaining the previous parsed value.
*   **Object Configuration Stream Reusability**: `ObjectToJsonConfigurationProvider.Load()` now serializes its source object into a fresh stream on each configuration build, allowing reusable configuration sources (such as test context dynamic options) to be built multiple times without throwing stream-disposed `ArgumentException` errors.
*   **Bounded Stream Materialization Defaults**: `StreamExtensions.ToArrayAsync`/`ToBinaryDataAsync` and `JpegValidator.Validate`/`ValidateAsync` methods now default to 10MB bounds (`DefaultMaxStreamSize` / `DefaultMaxJpegSize`) rather than `long.MaxValue`, preventing unconstrained stream materialization into memory when callers omit explicit bounds.
*   **Dependency Injection Provider Isolation**: Attribute registration now caches only immutable assembly scan metadata. Each service collection owns its `DrnServiceContainer` and module state, and startup validation no longer shares resolved service-type state across providers.
*   **LockUtils Reference Identity**: `TrySetIfNotEqual` now uses reference identity consistently with its `Interlocked.CompareExchange` CAS operation, so distinct value-equal records and classes no longer block an otherwise valid update.
*   **Entity Date Filter Tick Boundaries**: Date filters now apply inclusive and exclusive boundaries to the full 250ms Source-Known ID tick, preventing nonzero app, instance, and sequence payloads from being incorrectly included or excluded at range edges.
*   **Ambient Claim Isolation**: `ScopeContext` claim helpers now resolve every typed lookup independently by claim type, issuer, and requested target type. Issuer, type, and default-value choices can no longer contaminate later reads that use the same claim name.
*   **Multi-Identity Authentication**: `ScopedUser.Authenticated` now matches ASP.NET Core authorization by accepting any authenticated identity, treats an empty principal as anonymous, selects an authenticated primary identity, and excludes unauthenticated identities from ambient claims.
*   **Recurring Action Stop Semantics**: `RecurringAction.Stop()` now prevents an active callback from rescheduling the timer after the callback completes. A later `Start()` resumes scheduling normally.
*   **Object Reflection Depth Enforcement**: Fixed `GetGroupedPropertiesOfSubtype` recursion depth limit check to correctly increment depth levels along nested property traversal chains. Invalid (non-positive) recursion limits now trigger an `ArgumentOutOfRangeException`.
*   **JSON Merge Patch RFC, Change Tracking, And Depth Enforcement**: Object patches now merge recursively against empty objects when targets are missing or non-object, omitting null members per RFC 7396. Root-level JSON null is supported, no-op deletions and equivalent replacements no longer report `Changed`, and the complete patch depth is validated before applying changes. Unit coverage includes all 15 RFC 7396 Appendix A examples.
*   **Width-Aware Signed NumberBuilder Initialization**: Signed integer builders now initialize and reset the sign bit at the selected numeric width, preserving negative defaults when building 32-bit values.
*   **HTTP Response Disposal**: Converters now dispose responses when payload reading fails. Streaming conversion also disposes the response if stream retrieval fails.
*   **Claim Parameter Fallback**: `IScopedUser.GetClaimParameter` and `ScopedUser.GetClaimParameter` now return the caller-provided `defaultValue` fallback when claim value parsing fails.
*   **Scope Data Stored Null Handling**: `ScopeData.GetParameter<TValue>` now returns `null` for explicitly stored null entries when `TValue` is a nullable type, returning `defaultValue` only for missing keys, incompatible stored types, or non-nullable target types.

## Version 0.9.6

### Breaking Changes

*   **AppSecuritySettings BLAKE3 Derivation**: `AppHashKey`, `AppEncryptionKey`, `AppKey`, and `AppSeed` are now derived from `DrnAppFeatures.SeedKey` through BLAKE3 derive-key mode with distinct DRN Framework context strings. This replaces the previous custom SHA/BLAKE3 hash chains and changes app-specific names, rate-limit redaction hashes, Development default Nexus key material, and seed-dependent operations.
*   **NexusKey BLAKE3 Derivation**: `NexusKey` now derives both `MacKey` and `EncryptionKey` from decoded 32-byte key material through BLAKE3 derive-key mode with distinct DRN Framework context strings. This replaces the previous custom hash-chain derivation and changes generated secure IDs; existing IDs may require migration, regeneration, or an explicit compatibility strategy.
*   **Development Nexus Key Material Derivation**: When `Development` has no explicit default Nexus key, `AppSettings` now derives deterministic 32-byte Base64Url key material with BLAKE3 derive-key mode from `AppSecuritySettings` context-derived values instead of the previous custom hash-chain. Development-generated secure IDs may require migration, regeneration, or an explicit compatibility key.
*   **Legacy Nexus Key Configuration**: `AppSettings` now rejects legacy `NexusAppSettings:MacKeys` configuration before Development key auto-generation, preventing old key material from being silently ignored. Migrate `MacKeys[*].Key` to `Keys[*].KeyMaterial` and move matching `Format` and `Default` values to `Keys[*].Format` and `Keys[*].Default`.
*   **Casing And Path Extension Relocation**: Casing and safe path helpers moved from Utils to `DRN.Framework.SharedKernel.Extensions`.
*   **Settings Classes Sealed**: `AppSettings`, `NexusAppSettings`, and `NexusKey` are now sealed.
*   **Explicit Cancellation Root**: Removed the bare `ICancellationUtils.Token`, `IsCancellationRequested`, `Merge`, and `Cancel` members. Migrate root-wide calls to `cancellation.Root.Token`, `cancellation.Root.IsCancellationRequested`, `cancellation.Root.Merge(token)`, and `cancellation.Root.Cancel()`. Use `GetOrCreateScope(key)` for component or workflow groups and a caller-owned linked token source for instance-specific or operation-specific isolation.

### New Features

*   **App Data Roots**: Added `IAppData` and related types for validated temp/data roots, startup temp cleanup, and safe child paths.
*   **Cancellation Scopes**: Added typed child scopes through `CancellationScopeKey`. The same key resolves to the same scope and token, while root cancellation reaches every existing and later-created child.

### Bug Fixes

*   **AppSettings Nexus Key Validation**: Configured `NexusAppSettings:Keys` entries are now validated before default-key inspection, so null key entries report the intended configuration error instead of a null-reference failure.
*   **Settings Disposal Idempotency**: Settings now ignore repeated disposal and dispose owned key material once.
*   **Scoped Cancellation Composition**: Root and child scopes keep the same token for their lifetime, repeated merges do not duplicate registrations, and existing consumers observe later external or manual cancellation.
*   **Cancellation Lifecycle**: Reentrant or repeated cancellation and disposal are safe, caller-owned token sources remain untouched, and merged-token resources are released promptly.

## Version 0.9.5

### New Features

*   **Rate Limiting Settings**: Added validated `DrnAppFeatures:DrnRateLimit` knobs for DRN Hosting rate limiting (`Disabled`, partition log mode, shared token limit, replenishment period, tokens per period, B2B-friendly pre-auth defaults, and optional pre-auth/post-auth overrides), exposed in code as `IAppSettings.Features.RateLimit`.
*   **Test Scope Initialization**: `ScopeContext.InitializeForTest(...)` is now public and resets the current async-local scope before initialization, preventing stale test scope data from leaking between helper calls.
*   **Stream Hashing Support**: Added stream and file hashing overloads in `HashExtensions` (supporting Blake3, XxHash3, Sha256, Sha512, and keyed Blake3/XxHash3 algorithms) to hash files and large payloads without first materializing them as `BinaryData`.
*   **JPEG Payload Validation**: Added public `DRN.Framework.Utils.Validators.JpegValidator` with explicit `JpegValidationResult` and `JpegValidationErrorReason` support for structural, stream-based, and size-bounded JPEG byte validation before persisting uploaded image payloads.
*   **Path Security Extensions**: Added `PathExtensions.NormalizeDirectoryPath` (full-path resolution with trailing-separator cleanup) and `IsPathWithinDirectory` (segment-aware containment check using OS-correct path comparison) for safe path validation in file-serving and manifest processing.
*   **ScopedLog.CopyFrom**: New method on `IScopedLog` for merging log data, exception, and warning state from one scoped log into another with defensive value cloning for mutable collection types.
*   **Configuration Debug View Redaction**: `ConfigurationDebugView` now redacts sensitive configuration values (connection strings, passwords, secrets, tokens, API keys, credentials) by default. A new `GetDebugView(bool includeRawValues)` overload on `IAppSettings` allows opt-in to raw values, but raw inclusion is only permitted in the Development environment.
*   **Strict Nexus MAC Key Formats**: `NexusMacKey` now records a `ByteEncoding` `Format` (`Utf8`, `Hex`, `Base64`, or `Base64UrlEncoded`) and accepts only values that resolve directly to exactly 32 bytes. Development auto-generation remains deterministic from `SeedKey` and uses the framework `Hash()` default Base64Url output.
*   **SourceKnownEntityId Key-Ring Fallback**: `SourceKnownEntityIdUtils` generates with the default Nexus MAC key and parses with a default-first key ring so IDs generated before key rotation remain parseable while old keys stay configured.

### Bug Fixes

*   **Configuration Debug View**: Continues traversing child configuration keys when a higher-priority provider defines a scalar value for the parent section, and renders entries with the value provider's key casing so CI/environment overrides do not hide or rename lower-provider child entries in debug summaries.
*   **Prototype Recreation Gate**: `DevelopmentStatus` now enables prototype database recreation only in Development, and honors applied migrations: empty databases can still be recreated for prototyping, while databases with applied migrations require `UsePrototypeModeWhenMigrationExists`.

## Version 0.9.4

Dependencies upgraded to dotnet 10.0.8

## Version 0.9.3

Dependencies upgraded to dotnet 10.0.7

## Version 0.9.2

Dependencies upgraded to dotnet 10.0.6

## Version 0.9.0

My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals and is proud to inherit his spiritual legacy: 'I am not leaving behind any definitive text, any dogma, any frozen, rigid rule as my spiritual legacy. My spiritual wealth is science and reason. Those who wish to embrace me after my death will become my spiritual heirs if they accept the guidance of reason and science on this fundamental axis.'

### Breaking Changes

*   **Binary-Incompatible SKEID byte layout** (`SourceKnownEntityIdUtils`): UUID layout migrated to RFC 9562 big-endian; MAC relocated to contiguous bytes 12–15; epoch at byte 0; upper-half MSB sign-toggled for lexicographic sort correctness; lower half split across byte 5 and bytes 9–11.
*   **Timestamp precision: seconds → 250ms ticks** (`EpochTimeUtils`, `TimeStampManager`, `SourceKnownIdUtils`): `ConvertToSeconds` renamed to `ConvertToTicks` (250ms units). `TimeStampManager.UtcNow` truncates to nearest 250ms boundary. Epoch-range guard uses `MaxEpochTicks` (2³³ − 1).
*   **`ToUnsecure` → `ToPlain`** / **`GenerateUnsecure` → `GeneratePlain`** (`SourceKnownEntityIdUtils`).
*   **`NumberBuilder.GetLong` / `NumberParser.Get(long)` residue default**: 31 → 32 bits.
*   **Capacity rebalanced** (`SourceKnownIdUtils`): AppId 6→7 bits (max 127), AppInstanceId 5→6 bits (max 63), Sequence 21→18 bits (262,143/tick). `MaxAllowedDriftSeconds` const: 3s → 5s.

> [!WARNING]
> This is a binary-incompatible change. Entity IDs generated with v0.8.0 will not parse correctly in v0.9.0 — IDs must be regenerated. No migration tooling is provided; there are no expected production consumers with persisted v0.8.x entity IDs.

### New Features

*   **250ms timestamp precision**: New `TimeStampManager` constants: `PrecisionUnitInMs = 250`, `TicksPerPrecisionUnit = 2,500,000`. Epoch-half constants in `SourceKnownIdUtils`: `TicksPerHalf`, `MaxEpochTicks`. Correct sign-bit logic: first half → negative SKID, second half → positive SKID; monotonic ordering preserved.
*   **`NexusAppSettings` constructors**: Added `(byte appId, byte appInstanceId)` overload for programmatic instantiation.
*   **Throughput**: ~1,048,576 IDs/s per generator (262,143 × 4 ticks/s); up to ~8.6B IDs/s with 8,192 generators.

## Version 0.8.0

My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals, rooted in his timeless words that 'science is the truest guide in life.' In that spirit, and to honor the 14 March Scientists Day, this release is dedicated to the researchers working for the benefit of humanity, and to the rejection of my first academic paper :) ([JOSS #10176](https://github.com/openjournals/joss-reviews/issues/10176)).

### Breaking Changes

*   **SKEID Marker Migration (UUID V4 → V8)**: `SourceKnownEntityIdUtils` markers migrated from `0x4D` (UUID V4) to `0x8D` (UUID V8) for RFC 9562 §5.8 compliance.

> [!WARNING]
> This is a binary-incompatible change. Entity IDs generated with v0.7.0 (`0x4D8D` markers) will not parse correctly in v0.8.0. No migration tooling is provided as there are no production consumers with persisted v0.7.0 entity IDs.

### New Features

*   **Clock Drift Detection**: `TimeStampManager` now detects backward clock drift:
   *   Minor drift (<3 seconds): Cached timestamp is frozen (freeze-and-ride-through strategy). `UtcNowTicks` continues serving the previous higher value until the real clock catches up. No blocking or spin-wait.
   *   Critical drift (>=3 seconds): `ClockDriftException` is set and `ApplicationLifetime.RequestShutdown()` is called to initiate graceful shutdown. All subsequent `UtcNowTicks` / `UtcNow` calls throw `ClockDriftException`.
   *   New types: `ClockDriftException`, `ApplicationLifetime`.

## Version 0.7.0

My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals and honors 8 March, International Women's Day, a cause inseparable from his vision of equality. This release is dedicated to freedom of speech, democracy, women's rights, and Prof. Dr. Ümit Özdağ, a defender of Mustafa Kemal Atatürk’s enlightenment ideals.

> [!WARNING]
> Since v0.6.0 (released 10 November 2024), substantial changes have occurred. This release notes file has been reset to reflect the current state of the project as of 08 March 2026. Previous history has been archived to maintain a clean source of truth based on the current codebase.

### New Features

*   **Attribute-Based Dependency Injection**
   *   **Comprehensive Lifetimes**: `[Singleton]`, `[Scoped]`, `[Transient]`, `[HostedService]`, `[Config]`, `[ConfigRoot]`, and Keyed variants (`[SingletonWithKey]`, `[ScopedWithKey]`, `[TransientWithKey]`).
   *   **Registration**: `AddServicesWithAttributes()` auto-scans assemblies. `ValidateServicesAddedByAttributesAsync()` verifies resolution at startup.
   *   **Module Pattern**: `HasServiceCollectionModuleAttribute` for custom registration logic.
   *   **Test Helpers**: `ReplaceInstance`, `ReplaceScoped`, `ReplaceTransient`, `ReplaceSingleton` overrides for integration tests.
*   **Configuration System**
   *   **IAppSettings**: Strong-typed access to config with support for Connection Strings and Sections.
   *   **Environment Helpers**: `IsDevelopmentEnvironment` and `IsStagingEnvironment` properties for explicit environment checks.
   *   **[Config] Attribute**: Bind classes directly to config sections (e.g., `[Config("Payment")]`). Support for `[ConfigRoot]`.
   *   **Layered Sources**: Loads `appsettings`, `appsettings.{Env}`, User Secrets, Env Vars, and **Mounted Settings** (`/appconfig/json-settings/*.json`, `/appconfig/key-per-file-settings`).
   *   **Environment-Aware Auto-Migration**: `DrnDevelopmentSettings.AutoMigrateDevelopment` (default `true`) and `AutoMigrateStaging` (default `false`) replace the previous single `AutoMigrate` flag, enabling per-environment migration control.
*   **Ambient Context & Scoped Cancellation**
   *   **ScopeContext**: Centralized access to `UserId`, `TraceId`, `Authenticated` status, and ambient `IAppSettings`/`IScopedLog`. Built-in RBAC helpers.
   *   **ICancellationUtils**: Scoped cancellation management supporting token merging and lifecycle control.
*   **Scoped Logging & Diagnostics**
   *   **IScopedLog**: Request aggregation of actions, properties, and exceptions. `Measure()` for performance tracking and counting.
   *   **DevelopmentStatus**: Runtime tracking of DB model changes and migration status with environment-aware migration decisions (Development and Staging).
*   **Advanced Data & Bit Packing**
   *   **Bit Packing**: `NumberBuilder` and `NumberParser` (ref structs) for high-performance custom data structures and bit manipulation.
   *   **Monotonic Pagination**: `IPaginationUtils` for temporal cursor-based pagination leveraging entity IDs.
   *   **Cryptographic Helpers**: Unified `HashExtensions` (Blake3, XxHash3), `EncodingExtensions` (Base64, Base64Url, Hex), and `SafeApplyMergePatch` (RFC 7386).
*   **HTTP & Temporal IDs**
   *   **HTTP Request Wrappers**: `IInternalRequest`/`IExternalRequest` with standardized Flurl integration, HTTP version policy configuration, and enriched `HttpResponse<T>` diagnostics. Retries/circuit breakers are not configured by this package.
   *   **Temporal IDs**: `ISourceKnownIdUtils` and `ISourceKnownEntityIdUtils` providing globally sortable identifiers.
   *   **Secure Entity IDs**: AES-256-ECB single-block encrypted `SourceKnownEntityId` variants with flag-based dispatch via `UseSecureSourceKnownIds` (defaults to `true`).
       *   `GenerateSecure` / `GenerateUnsecure` explicit methods; `Parse` auto-detects encrypted and plaintext IDs.
       *   Post-quantum ready — AES-256 retains 128-bit security under Grover's algorithm.
   *   **Epoch-Based Time Addressing**: `SourceKnownEntityId` byte 5 reserved for epoch indexing, enabling ~34,842 monotonic time years starting from 2025-01-01. Each epoch spans ~136 years (2³¹ seconds × 2 epoch halves). The first epoch requires no configuration.
   *   **ISourceKnownEntityIdOperations Inheritance**: `ISourceKnownEntityIdUtils` now inherits `ISourceKnownEntityIdOperations` (SharedKernel), formalizing the core contract (`Generate`, `Parse`, `ToSecure`, `ToUnsecure`) for cross-layer use without Utils dependency.
   *   **Secure ↔ Unsecure Conversion**: `ToSecure` / `ToUnsecure` methods (with nullable overloads) on `SourceKnownEntityIdUtils` for idempotent conversion between encrypted and plaintext `SourceKnownEntityId` forms.
   *   **Named Constants for GUID Layout**: Replaced magic numbers in `SourceKnownEntityIdUtils` with named constants (`GuidLength`, `MacHashLength`, `MacHashFirstIndex`–`MacHashFourthIndex`) for improved readability and maintainability.
*   **Concurrency**
   *   **Lock-Free Atomics**: `LockUtils` static helpers (`TryClaimLock`, `TryClaimScope`, `ReleaseLock`, `TrySetIfEqual`, `TrySetIfNull`, `TrySetIfNotEqual`, `TrySetIfNotNull`) for lock-free coordination using `Interlocked`. Includes disposable `LockScope` ref struct for automatic lock release via `using`.
*   **Core Extensions & Time**
   *   **Reflection**: Optimized `MethodUtils` with caching, `CreateSubTypes`, and deep discovery (`GetGroupedPropertiesOfSubtype`).
   *   **Extensions**: Robust set of `string` (Casing, Parsing), `FileInfo` (Efficient line reading), `Stream` (Size guards), and `Dictionary` utilities.
   *   **High-Perf Time**: `TimeStampManager` (cached UTC seconds) and `RecurringAction` (async-safe timers).

---

Documented with the assistance of [DiSC OS](https://github.com/duranserkan/DRN-Project/blob/develop/.agent/rules/DiSCOS.md)

---
**Semper Progressivus: Always Progressive**
 
 
## Commit Info  
Author: Duran Serkan KILIÇ  
Date: 2026-07-29 21:49:21 +0300  
Hash: ba240f925c31f3f7a9d1d5f2bece8a68b4de8c12