Themia.Audit
0.23.1
See the version list below for details.
dotnet add package Themia.Audit --version 0.23.1
NuGet\Install-Package Themia.Audit -Version 0.23.1
<PackageReference Include="Themia.Audit" Version="0.23.1" />
<PackageVersion Include="Themia.Audit" Version="0.23.1" />
<PackageReference Include="Themia.Audit" />
paket add Themia.Audit --version 0.23.1
#r "nuget: Themia.Audit, 0.23.1"
#:package Themia.Audit@0.23.1
#addin nuget:?package=Themia.Audit&version=0.23.1
#tool nuget:?package=Themia.Audit&version=0.23.1
Themia.Audit
Framework-neutral, append-only audit event log: a record model with validation and unconditional redaction, a Dapper store behind a per-engine dialect strategy, and the FluentMigrator schema.
net8.0;net10.0. No Themia.Framework.* dependency — asserted by a test, not left to convention.
What this package is for
Two kinds of event share one table and one write path:
- Activity — business events an application chooses to record.
- Authentication / user lifecycle — login, logout, refresh, lockout and credential changes,
recorded for the application by
Themia.Modules.Auditwithout it writing any code.
An entity change log — per-row CREATE/UPDATE/DELETE with field-level old→new values — is a
different shape and is not in this package. It is planned for 0.24.0.
Install
dotnet add package Themia.Audit
dotnet add package Themia.Audit.PostgreSql # or .SqlServer / .MySql
services.AddThemiaAudit(o =>
{
o.ConnectionString = cfg.GetConnectionString("Default")!;
o.Engine = AuditEngine.Postgres;
});
services.AddThemiaAuditPostgreSql();
AddThemiaAudit runs the schema migration by default, so this package works without the module
layer. Pass runMigration: false to defer it. Calling it with runMigration: true and an unset
Engine or empty ConnectionString throws immediately and names the missing setting — a requested
migration that silently does not run would surface much later as a missing-table error at the first
write.
Recording an event
await recorder.RecordAsync(
new AuditEntry
{
EventType = "PROPOSAL_ACCEPTED",
Category = AuditCategory.Activity,
Outcome = AuditOutcome.Success,
ActorId = currentUser.Id,
EntityType = nameof(Proposal),
EntityId = proposal.Id.ToString(),
},
payload: new { proposal.Status, proposal.Amount });
You pass an object, never a JSON string. The recorder serializes it and redacts it, so data is
well-formed JSON by construction and the redactor always has something it can walk. AuditEntry.Data
is internal init precisely so this cannot be bypassed.
Redaction
Unconditional, on the single write path — not an option and not a helper you can forget. Property
names matching the deny-list have their values replaced with [redacted] at any depth, including
inside nested objects and arrays. The property name is kept: that a password field was present is
itself audit-relevant.
password, passwordhash, passwordsalt, secret, token, refreshtoken, accesstoken,
apikey, api_key, authorization, otp, pin, cvv, creditcard, card_number, privatekey, ssn
AuditRedactionOptions.AddPattern adds to that list and cannot remove from it. Use
IsSensitive(name) to test membership; there is deliberately no public collection to enumerate,
because no shape of one was safe to expose.
An adopter who genuinely needs a default gone supplies their own IAuditRedactor — a conspicuous act
rather than a config line.
Storage
One table, themia_audit_events, unqualified and identical on every engine. Never
InSchema(...): FluentMigrator drops it on MySQL, where "schema" and "database" are the same concept,
so a qualified name means something different per engine — which is how two Themia modules once ended
up with colliding table names there.
data is nvarchar(max)/text on every engine, never jsonb or JSON. Those types validate on
insert, and an adopter-supplied payload must never be able to fail the transaction it describes on
some engines but not others.
id is a bigint identity clustered key; event_uid is the public Guid. A random Guid clustered
key page-splits an append-only table on every insert.
Bounded columns have constants on AuditEntry (MaxEventTypeLength and siblings) bound to the
migration. Fields an adopter names — EventType, ActorId, EntityType, EntityId — are
rejected when over-length rather than truncated, because truncating them merges two distinct
events into one. Fields the framework captures — UserAgent, IpAddress — are truncated, because a
clipped user-agent is still the same event.
Reads are not tenant-filtered
IAuditStore.QueryAsync applies no tenant predicate unless AuditQuery carries one. This package
is framework-neutral and cannot see ITenantContext.
If you are multi-tenant, use ITenantAuditReader from Themia.Modules.Audit, which pre-seeds the
ambient tenant and cannot be asked for another tenant's rows.
Tenant semantics
TenantId is nullable and null means host-level, not "unknown". A failed login for an
identifier matching no user genuinely has no tenant, and is exactly the row a security review wants.
Nothing in this package invents a tenant.
Retention
IAuditStore.PurgeAsync(olderThan, …) deletes in one statement. There is no scheduled job: the
default is keep-forever, and silently deleting an audit trail is worse than a growing table. On a
table that has never been purged the first call can be very large — purge in date slices rather than
in one go.
Related
Themia.Audit.AspNetCore— mountable read-only dashboard.Themia.Modules.Audit— tenant resolution, transaction enlistment, and automatic auditing of Identity.
| 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 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 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
- Dapper (>= 2.1.79)
- FluentMigrator (>= 8.0.1)
- FluentMigrator.Runner.MySql (>= 8.0.1)
- FluentMigrator.Runner.Postgres (>= 8.0.1)
- FluentMigrator.Runner.SqlServer (>= 8.0.1)
- Microsoft.Data.SqlClient (>= 6.1.5)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.1)
- Microsoft.IdentityModel.Logging (>= 8.19.1)
- Microsoft.IdentityModel.Protocols (>= 8.19.1)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.19.1)
- Microsoft.IdentityModel.Tokens (>= 8.19.1)
- MySqlConnector (>= 2.6.0)
- Npgsql (>= 10.0.3)
- System.IdentityModel.Tokens.Jwt (>= 8.19.1)
- Themia.Data.Migrations (>= 0.23.1)
-
net8.0
- Dapper (>= 2.1.79)
- FluentMigrator (>= 8.0.1)
- FluentMigrator.Runner.MySql (>= 8.0.1)
- FluentMigrator.Runner.Postgres (>= 8.0.1)
- FluentMigrator.Runner.SqlServer (>= 8.0.1)
- Microsoft.Data.SqlClient (>= 6.1.5)
- Microsoft.Extensions.DependencyInjection (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.1)
- Microsoft.IdentityModel.Logging (>= 8.19.1)
- Microsoft.IdentityModel.Protocols (>= 8.19.1)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.19.1)
- Microsoft.IdentityModel.Tokens (>= 8.19.1)
- MySqlConnector (>= 2.6.0)
- Npgsql (>= 10.0.3)
- System.IdentityModel.Tokens.Jwt (>= 8.19.1)
- Themia.Data.Migrations (>= 0.23.1)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Themia.Audit:
| Package | Downloads |
|---|---|
|
Themia.Audit.PostgreSql
PostgreSQL dialect (Npgsql) for Themia.Audit. |
|
|
Themia.Modules.Audit
Framework-side audit module: transaction enlistment over Themia.Audit, ambient-tenant resolution, a tenant-scoped read surface, and the IAuditLogService adapter. |
|
|
Themia.Audit.AspNetCore
Themia.Audit ASP.NET Core dashboard — a mountable, self-rendered, read-only /audit UI (list + detail) over IAuditStore, with a fail-closed Authorize hook. |
|
|
Themia.Audit.SqlServer
SQL Server dialect (Microsoft.Data.SqlClient) for Themia.Audit. |
|
|
Themia.Audit.MySql
MySQL dialect (MySqlConnector) for Themia.Audit. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.42.1 | 0 | 10/27/2026 |
| 0.42.0 | 0 | 10/27/2026 |
| 0.41.0 | 73 | 10/25/2026 |
| 0.40.0 | 74 | 10/25/2026 |
| 0.39.0 | 71 | 10/25/2026 |
| 0.38.0 | 71 | 10/24/2026 |
| 0.37.0 | 96 | 10/24/2026 |
| 0.36.0 | 106 | 10/23/2026 |
| 0.35.3 | 97 | 10/22/2026 |
| 0.35.2 | 146 | 10/20/2026 |
| 0.35.1 | 188 | 10/17/2026 |
| 0.35.0 | 147 | 10/17/2026 |
| 0.34.0 | 153 | 10/17/2026 |
| 0.33.0 | 163 | 10/17/2026 |
| 0.32.0 | 161 | 10/15/2026 |
| 0.31.3 | 163 | 10/14/2026 |
| 0.31.2 | 156 | 10/13/2026 |
| 0.31.1 | 152 | 10/13/2026 |
| 0.31.0 | 166 | 10/13/2026 |
| 0.23.1 | 192 | 9/7/2026 |