Ruvio.AspNetCore.DataProtection 0.1.0

dotnet add package Ruvio.AspNetCore.DataProtection --version 0.1.0
                    
NuGet\Install-Package Ruvio.AspNetCore.DataProtection -Version 0.1.0
                    
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="Ruvio.AspNetCore.DataProtection" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ruvio.AspNetCore.DataProtection" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Ruvio.AspNetCore.DataProtection" />
                    
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 Ruvio.AspNetCore.DataProtection --version 0.1.0
                    
#r "nuget: Ruvio.AspNetCore.DataProtection, 0.1.0"
                    
#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 Ruvio.AspNetCore.DataProtection@0.1.0
                    
#: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=Ruvio.AspNetCore.DataProtection&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Ruvio.AspNetCore.DataProtection&version=0.1.0
                    
Install as a Cake Tool

Ruvio.AspNetCore.DataProtection

An append-only IXmlRepository for ASP.NET Core Data Protection key rings. Sharing the key ring and application name lets instances decrypt each other's authentication/session cookies, antiforgery tokens and other protected payloads. This is durable key material, not an evictable cache or distributed session store.

Setup

Register an IRuvioClient separately, then:

using Microsoft.AspNetCore.DataProtection;
using Ruvio.AspNetCore.DataProtection;

builder.Services.AddDataProtection()
    .SetApplicationName("orders-production")
    .PersistKeysToRuvio(o => o.Key = "orders:production:data-protection:v1")
    .ProtectKeysWithCertificate(certificate);

certificate must come from the application's secure certificate configuration, have an accessible private key, and be usable by every reader of this key ring. Other framework IXmlEncryptor implementations also work. Encryptor configuration is checked after options configuration, so method ordering does not disable the requirement. Keep old decryption certificates available when rotating certificates. The package does not provision certificates, export secrets or install an exporter.

For a separate durable Ruvio connection, use a client factory, for example after registering a keyed IRuvioClient named "key-ring":

builder.Services.AddDataProtection()
    .SetApplicationName("orders-production")
    .PersistKeysToRuvio(
        services => services.GetRequiredKeyedService<Ruvio.Client.IRuvioClient>("key-ring"),
        o => o.Key = "orders:production:data-protection:v1")
    .ProtectKeysWithCertificate(certificate);

The application/DI container owns the selected client; the repository never disposes it or changes its database. Register persistence once per Data Protection configuration. SetApplicationName provides cryptographic application isolation; use separate keys/deployments for separate trust boundaries as well.

Encryption is required by default. Setting RequireEncryption = false is an explicit opt-out for isolated development, not a production recommendation. Unencrypted elements marked requiresEncryption are refused by the repository when the requirement is enabled. This does not encrypt arbitrary XML supplied directly to IXmlRepository; use the framework key manager to create keys.

Durability and failure contract

Use persistent, backed-up, non-evicting storage and restricted access. Do not point production key rings at a memory-only deployment or an eviction-prone cache. The provider cannot inspect or guarantee the deployment's durability, replication or failover policy. A separate durable connection is recommended when ordinary caches use different retention/eviction settings.

There is no TTL, key pruning, automatic retry or fallback to an ephemeral local ring. An existing key with a TTL is rejected on reads and writes. Keep old keys and revocation records: removing them can make existing cookies/tokens permanently unreadable or remove revocation information. Monitor storage capacity and back up both the ring and the decryption certificates. Persistence acknowledgment alone does not turn asynchronous replication into zero-loss failover.

Each XML element is stored in a single hash, under the SHA-256 digest of its serialized bytes. Distinct key/revocation elements are append-only even if their friendlyName values match. Repeating identical serialized content is a no-op; friendlyName is advisory and is not a storage identity. Concurrent writers do not replace the whole ring or lose each other's appends.

A read is PTTL plus HGETALL. A write is PTTL, HEXISTS, HLEN, and HSET. The ring must not have a TTL. These checks are separate commands. The length check happens before the write. These operations occur when Data Protection loads/updates its ring, not for every Protect/Unprotect call; the framework owns its own in-process key-ring caching and refresh behavior. No package-owned polling loop, per-request middleware or background task is added.

IXmlRepository is synchronous. Calls use a thread-pool dispatch to avoid blocking on client continuations captured by a caller's synchronization context. OperationTimeout defaults to 10 seconds and cancels the command. A timed-out write is still an unknown outcome, not proof that it did not persist.

Defaults: 128 elements and 64 KiB per XML element (8 MiB payload budget). Options allow up to 4096 elements and 1 MiB per element while retaining the 8 MiB total budget. Limits deliberately fail loudly instead of pruning keys or returning an empty ring. Account for both key rotation and revocation records when choosing limits. The private namespace must not contain unrelated values.

XML parsing prohibits DTDs/external entity resolution and limits document size. Corrupt XML, wrong server types, capacity errors and transport failures propagate to Data Protection; none are treated as a successful empty read.

Session integration

This package protects the session cookie, while Ruvio.Extensions.Caching stores the session contents:

builder.Services.AddRuvioDistributedCache(o => o.InstanceName = "orders-session-v1");
builder.Services.AddSession(o => o.IdleTimeout = TimeSpan.FromMinutes(20));
// Configure AddDataProtection().PersistKeysToRuvio(...).ProtectKeysWithCertificate(...)
// as above, with a consistent application name on all instances.

var app = builder.Build();
app.UseRouting();
app.UseSession();
app.MapGet("/visits", async (HttpContext context) =>
{
    await context.Session.LoadAsync(context.RequestAborted);
    var visits = (context.Session.GetInt32("visits") ?? 0) + 1;
    context.Session.SetInt32("visits", visits);
    return visits;
});

Import Ruvio.Extensions.Caching for AddRuvioDistributedCache. Preserve normal cookie consent, HTTPS, authentication and authorization configuration in your application. Session remains ASP.NET Core's non-locking, last-writer-wins model; the counter above is illustrative, not a concurrent atomic counter. Use native Ruvio coordination commands for that requirement.

Development

RUVIO_TEST_ADDR=127.0.0.1:16382 dotnet test integrations/dotnet/Ruvio.AspNetCore.DataProtection.Tests -c Release
dotnet pack integrations/dotnet/Ruvio.AspNetCore.DataProtection -c Release -o dist

Live cases cover concurrent appends, certificate-encrypted cross-instance protection, revocations, app isolation, and real ASP.NET Core Session with a shared cookie across two HTTP hosts. Tests require an isolated server and delete their own key-ring keys; unset RUVIO_TEST_ADDR explicitly skips live cases.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 86 10/2/2026