Valtuutus.Core
0.8.0-beta
See the version list below for details.
dotnet add package Valtuutus.Core --version 0.8.0-beta
NuGet\Install-Package Valtuutus.Core -Version 0.8.0-beta
<PackageReference Include="Valtuutus.Core" Version="0.8.0-beta" />
<PackageVersion Include="Valtuutus.Core" Version="0.8.0-beta" />
<PackageReference Include="Valtuutus.Core" />
paket add Valtuutus.Core --version 0.8.0-beta
#r "nuget: Valtuutus.Core, 0.8.0-beta"
#:package Valtuutus.Core@0.8.0-beta
#addin nuget:?package=Valtuutus.Core&version=0.8.0-beta&prerelease
#tool nuget:?package=Valtuutus.Core&version=0.8.0-beta&prerelease
Valtuutus
A Google Zanzibar inspired authorization library in .NET
The implementation is inspired on permify and other ReBac open source projects.
<a href="https://bencher.dev/perf/valtuutus?key=true&reports_per_page=4&branches_per_page=8&testbeds_per_page=8&benchmarks_per_page=8&plots_per_page=8&reports_page=1&branches_page=1&testbeds_page=1&benchmarks_page=1&plots_page=1&report=f7c35e30-f513-402c-af44-56d8c876fff3&branches=9e4cbdcf-9fee-4cd3-ada1-62aefe433145&heads=5bdd1841-0a0f-4532-84b7-87ef3d065302&testbeds=072da3db-e609-4676-99a6-5b9262df6086&benchmarks=22f44ad6-7979-4757-aa00-3286de603788%2Cd0d39808-ab7a-42f9-95fb-9193853640f3%2Cd3f76a50-964b-4526-9c33-89a38c18f474%2Cb0c2d4d2-2cae-4dfd-89d2-35b49a00b23e&measures=b549a9dd-6ff0-4525-b90a-c9e3af815580&start_time=1714521600000&lower_boundary=false&upper_boundary=false&clear=true&lower_value=false&upper_value=false&x_axis=date_time&end_time=1796083200000&utm_medium=share&utm_source=bencher&utm_content=img&utm_campaign=perf%2Bimg&utm_term=valtuutus"><img src="https://api.bencher.dev/v0/projects/valtuutus/perf/img?branches=9e4cbdcf-9fee-4cd3-ada1-62aefe433145&heads=5bdd1841-0a0f-4532-84b7-87ef3d065302&testbeds=072da3db-e609-4676-99a6-5b9262df6086&benchmarks=22f44ad6-7979-4757-aa00-3286de603788%2Cd0d39808-ab7a-42f9-95fb-9193853640f3%2Cd3f76a50-964b-4526-9c33-89a38c18f474%2Cb0c2d4d2-2cae-4dfd-89d2-35b49a00b23e&measures=b549a9dd-6ff0-4525-b90a-c9e3af815580&start_time=1714521600000&end_time=1797083200000" title="valtuutus" alt="valtuutus - Bencher" /></a>
Functionality
The library is designed to be simple and easy to use. Each subset of functionality is divided in engines. The engines are:
- ICheckEngine: The engine that handles the answering of two questions:
Can entity U perform action Y in resource Z? For that, use theCheckfunction.What permissions entity U have in resource Z? For that, use theSubjectPermissionfunction.
- ILookupSubjectEngine: The engine that can answer:
Which subjects of type T have permission Y on entity:X?For that, use theLookupfunction. - ILookupEntityEngine: The engine that can answer:
Which resources of type T can entity:X have permission Y?For that, use theLookupEntityfunction. Supports scoped queries and cursor pagination — see below. - IDataWriterProvider: This is the provider that can write your relational or attribute data.
- IDbDataWriterProvider: Works similarly to
IDataWriterProvider, with the addition of accepting a connection and transaction as parameters. - Read here about how the relational data is stored.
- Read here for engine usage examples (Check, SubjectPermission, LookupSubject, LookupEntity).
LookupEntity — scoped queries and pagination
LookupEntity returns a LookupEntityPage:
LookupEntityPage page = await lookupEntityEngine.LookupEntity(
new LookupEntityRequest("task", "view", "user", "alice"),
cancellationToken);
// page.EntityIds — IReadOnlyList<string>
// page.ContinuationToken — null if no more pages
Scope — constrain results to a parent entity
Use EntityScope when you need to answer a scoped question like
"which tasks in project X can this user view?" — the same query you'd back a
GET /projects/{projectId}/tasks endpoint with.
Without scope, LookupEntity returns all tasks the user can view across the entire system.
With scope, results are limited to tasks that have the specified relation to the given parent entity —
so only tasks belonging to project-1 are considered.
var page = await lookupEntityEngine.LookupEntity(
new LookupEntityRequest("task", "view", "user", "alice")
{
Scope = new EntityScope(
Relation: "parent", // the relation on "task" that points to its parent
SubjectType: "project", // the parent entity type
SubjectId: "project-1" // the specific parent to scope to
)
},
cancellationToken);
Pagination
string? token = null;
do
{
var page = await lookupEntityEngine.LookupEntity(
new LookupEntityRequest("task", "view", "user", "alice")
{
Scope = new EntityScope("parent", "project", "project-1"),
PageSize = 50,
ContinuationToken = token
},
cancellationToken);
Process(page.EntityIds);
token = page.ContinuationToken;
} while (token is not null);
Documentation
| Guide | Description |
|---|---|
| Getting Started | End-to-end quickstart — install, configure, write data, check permissions |
| Modeling Authorization | Schema DSL walkthrough with the GitHub example |
| Schema Reference | Complete reference for every keyword, operator, and type in the DSL |
| Authorization Patterns | Ready-made patterns: RBAC, hierarchical RBAC, ABAC, multi-tenancy |
| Using the Engines | Code examples for Check, SubjectPermission, LookupSubject, LookupEntity, depth |
| Storing Data | Writing, deleting, snap tokens, source generator |
| Testing | Unit-testing your authorization model with the InMemory provider |
| Caching | Reducing database load with FusionCache |
| Telemetry | OpenTelemetry activity sources, emitted spans, and what to monitor |
Usage
Install the package from NuGet:
If using Postgres:
dotnet add package Valtuutus.Data.Postgres
If using SqlServer:
dotnet add package Valtuutus.Data.SqlServer
If you prefer using an InMemory provider:
dotnet add package Valtuutus.Data.InMemory
Adding to DI:
builder.Services.AddValtuutusCore(c =>
...
See examples of how to define your schema here.
If using Postgres:
builder.Services
.AddPostgres(_ => () => new NpgsqlConnection(builder.Configuration.GetConnectionString("PostgresDb")!));
If using SqlServer:
builder.Services
.AddSqlServer(_ => () => new SqlConnection(builder.Configuration.GetConnectionString("SqlServerDb")!));
If using InMemory:
builder.Services
.AddInMemory();
Database migrations
If you are using a DB provider to store your data, please look at the scripts that create the tables that Valtuutus require to function.
Schema and table name customization
Both relational providers accept an optional options object to customise the database schema and table names. Pass it as the second argument to AddPostgres or AddSqlServer:
// Postgres — defaults: schema="public", tables="transactions", "relation_tuples", "attributes"
builder.Services.AddValtuutusCore(/* schema */)
.AddPostgres(
_ => () => new NpgsqlConnection(connectionString),
new ValtuutusPostgresOptions(
schema: "authz",
transactionsTableName: "transactions",
relationsTableName: "relation_tuples",
attributesTableName: "attributes"));
// SQL Server — defaults: schema="dbo", same table names
builder.Services.AddValtuutusCore(/* schema */)
.AddSqlServer(
_ => () => new SqlConnection(connectionString),
new ValtuutusSqlServerOptions(
schema: "authz",
transactionsTableName: "transactions",
relationsTableName: "relation_tuples",
attributesTableName: "attributes"));
Make sure the migration script targets the same schema and table names you configure here.
ValtuutusPostgresOptions also exposes two Npgsql-specific properties for automatic prepared statements:
| Property | Default | Meaning |
|---|---|---|
MaxAutoPrepare |
64 |
Maximum number of statements Npgsql will auto-prepare |
AutoPrepareMinUsages |
2 |
Minimum executions before a statement is prepared |
These map directly to Npgsql's prepared statement feature and can improve performance for repeated queries under load.
Using query concurrent limiting
It is expected that you don't want to allow Valtuutus to expand queries while it has resources. The default limit is 5 concurrent queries for the same request. To change that, you can use the AddConcurrentQueryLimit method, for example:
builder.Services
.AddPostgres(_ => () => new NpgsqlConnection(builder.Configuration.GetConnectionString("PostgresDb")!)) // Replace this with any provider you want
.AddConcurrentQueryLimit(10);
Change your data provider according to your database.
Caching
Valtuutus supports caching the calls to the engines through the Valtuutus.Data.Caching package. To use it, install like:
dotnet add package Valtuutus.Data.Caching
In your DI setup, add the caching component:
builder.Services
.AddPostgres(_ => () => new NpgsqlConnection(builder.Configuration.GetConnectionString("PostgresDb")!)) // Replace this with any provider you want
.AddCaching(); // <-- This line
This packages requires that you set up the amazing FusionCache library. Click here for more information.
Telemetry
The library uses OpenTelemetry to provide telemetry data. To enable it, just add a source with the name "Valtuutus":
builder.Services
.AddOpenTelemetry()
.WithTracing(telemetry =>
{
telemetry
.AddSource("Valtuutus")
...
| Product | Versions 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 is compatible. 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 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.3)
- OneOf (>= 3.0.271)
- Valtuutus.Lang (>= 0.8.0-beta)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.3)
- OneOf (>= 3.0.271)
- Valtuutus.Lang (>= 0.8.0-beta)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.3)
- OneOf (>= 3.0.271)
- Valtuutus.Lang (>= 0.8.0-beta)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Valtuutus.Core:
| Package | Downloads |
|---|---|
|
Valtuutus.Data
Valtuutus data access abstractions; Valtuutus provides a developer-focused, modern library for creating ReBAC without complexity. |
|
|
Valtuutus.Data.Caching
Valtuutus relational caching abstractions; Valtuutus provides a developer-focused, modern library for creating ReBAC without complexity. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.9.0-beta | 124 | 4/20/2026 |
| 0.8.2-beta | 65 | 4/17/2026 |
| 0.8.1-beta | 60 | 4/17/2026 |
| 0.8.0-beta | 73 | 4/13/2026 |
| 0.7.3.1-beta | 1,137 | 4/3/2025 |
| 0.7.3-beta | 201 | 3/20/2025 |
| 0.7.2-beta | 131 | 12/30/2024 |
| 0.7.1-beta | 135 | 12/30/2024 |
| 0.7.0-beta | 122 | 11/19/2024 |
| 0.6.0-beta | 136 | 10/9/2024 |
| 0.5.0-beta | 144 | 8/21/2024 |
| 0.4.0-alpha | 109 | 7/26/2024 |
| 0.3.0-alpha | 138 | 4/4/2024 |
| 0.2.0-alpha | 142 | 3/27/2024 |
| 0.1.0-alpha | 135 | 3/25/2024 |
Version 0.8.0-beta:
BREAKING: Dropped netstandard2.0 — targets are net8.0, net9.0, net10.0
BREAKING: LookupEntity now returns LookupEntityPage instead of HashSet<string>
BREAKING: IDataReaderProvider methods gain a required EntityScope? scope parameter
BREAKING: SnapToken is now required (non-nullable) in data-layer filter types
NEW: net10.0 target framework support
NEW: LookupEntity parent-scoped queries via EntityScope
NEW: Cursor pagination for LookupEntity via PageSize + ContinuationToken
PERF: ~93% allocation reduction in LookupEntity engine
PERF: CheckEngine memoization, reachability pruning, and batched dispatch
PERF: Two-hop and TTU EXISTS JOIN eliminates extra DB round-trips
PERF: Native Npgsql hot-path readers for relation queries