Audit trail · Compliance · .NET-native
Every change. Every value. Every time.
Reldavi records who changed what in your database, what it looked like before, and exactly when — without adding a millisecond to the request that made the change. Install a NuGet package, add three lines, and your audit trail starts filling itself.
No credit card. 100,000 events a month on the free plan, forever.
Built for the questions auditors actually ask
dotnet add package to your first recorded change.
The problem
Your database knows what changed. It just will not tell you.
A row says status = "Shipped". It does not say who shipped it, when, from which IP, or that it was "Cancelled" ninety seconds earlier. When a customer disputes a charge, when a regulator asks for a change history, or when something is wrong and nobody knows who touched it, that missing context is the whole problem.
So teams build it themselves: a trigger here, an UpdatedBy column there, a JSON blob in a side table nobody indexes. It works until the first audit, and then somebody spends a quarter making it presentable.
Triggers are invisible
Database triggers run where your application cannot see them, cannot mask PII, and cannot record who the user was.
Side tables grow teeth
An audit table in your transactional database competes with production traffic for exactly the resource you cannot spare.
Logs are not evidence
Application logs get sampled, rotated and truncated. An auditor asks for seven years; your retention policy says fourteen days.
The product
A time machine for your data.
Every recorded change is one click from the before-and-after that produced it.
Read a change the way you read a pull request.
Property by property, old value against new, colour-coded and legible at a glance. No JSON blob to squint at, no log line to reconstruct. The properties you never changed are not there, because a diff that lists fifty unchanged fields buries the one that moved.
- — Only what actually changed — writing a value back unchanged records nothing.
- — Masked fields show their projection, never the original.
- — Truncated change sets say so in the record rather than silently dropping fields.
The full history of one record, in order.
Open any invoice, order or account and see everything that ever happened to it: who created it, every edit since, and who deleted it. Unbounded in time — the question "what happened to this record" does not come with a date range attached.
- — One record's entire life, however far back your retention goes.
- — Jump from any timeline entry straight into its diff.
- — Shareable links, so a finding travels with its evidence.
Find the change, not the haystack.
Filter by resource, actor, action, correlation id or free text, across a window of any size. Results come back from a columnar store built for exactly this shape of question, not from a table fighting your production workload.
- — Filter by who, what, when, which project and which request.
- — Turkish-aware case-insensitive search — “İstanbul” matches “istanbul”.
- — Cursor pagination, so page fifty costs the same as page one.
Integration
Three lines. Then stop thinking about it.
The .NET SDK hooks EF Core's save pipeline. You annotate what matters, and every insert, update and delete records itself from then on.
Install the package
One dotnet add package. It multi-targets .NET 8 and .NET 10, so you do not have to upgrade first.
Wire it up
Register the client, attach the interceptor to your DbContext, add the middleware that supplies the current user.
Mark what is sensitive
Attributes decide what is recorded, what is ignored and what is masked before it ever leaves your process.
That is the whole integration. No schema migration, no trigger, no change to your business logic.
// 1 · Install
dotnet add package Reldavi.EntityFrameworkCore
dotnet add package Reldavi.Client
// 2 · Program.cs
builder.Services.AddReldaviClient(options =>
{
options.Endpoint = new Uri("https://ingest.reldavi.com");
options.ApiKey = builder.Configuration["Reldavi:ApiKey"]!;
});
builder.Services.AddReldaviEntityFrameworkCore(options =>
options.MaskHashSecret = builder.Configuration["Reldavi:MaskSecret"]);
builder.Services.AddDbContext<ShopDbContext>((sp, options) => options
.UseNpgsql(connectionString)
.UseReldavi(sp));
// 3 · After authentication, so changes are attributed to a real user
app.UseAuthentication();
app.UseReldavi();[AuditResource("shop.order")]
public sealed class Order
{
public int Id { get; set; }
[AuditLabel] // shown in the timeline
public string Reference { get; set; } = "";
public string Status { get; set; } = "Pending";
[AuditMask(MaskStrategy.PreserveLast, 4)] // ************4242
public string CardNumber { get; set; } = "";
[AuditMask(MaskStrategy.Hash)] // searchable, not reversible
public string NationalId { get; set; } = "";
[AuditIgnore] // never recorded
public string InternalNotes { get; set; } = "";
}
// Nothing else to call. SaveChanges records itself.
order.Status = "Shipped";
await db.SaveChangesAsync();// Node.js
audit.capture({
action: 'updated',
resourceType: 'shop.order',
resourceId: order.id,
actor: { id: user.id, displayName: user.name },
changes: mask.applyMasks(diff(before, after), {
cardNumber: mask.preserveLast(4),
}),
});
# Python
audit.capture(AuditEvent(
action="updated",
resource=Resource(type="shop.order", id=order.id),
changes=masking.apply_masks(diff(before, after), {
"card_number": masking.preserve_last(4),
}),
))
// Go
client.Capture(reldavi.Event{
Action: reldavi.ActionUpdated,
Resource: reldavi.Resource{Type: "shop.order", ID: order.ID},
Changes: reldavi.Diff(before, after),
})Performance
Auditing that cannot slow you down.
The capture path does no network calls, takes no locks and never awaits. When the buffer fills, it sheds load and tells you — because an audit pipeline having a bad day must never become your checkout having a bad day.
No I/O on your thread
Capture writes into an in-memory buffer and returns. Every network call happens on a background dispatcher.
Fails open, loudly
A capture error is logged and swallowed, never raised into your transaction. Dropped events are counted so you can alert on them.
Rollback leaves no trace
Events are published only after your transaction commits. A failed save records nothing — as it should.
Batched and compressed
Events ship in batches, gzipped. Audit JSON is highly repetitive and compresses by roughly an order of magnitude.
Capturing one event costs the calling thread about two microseconds at the 99th percentile — the call your code makes inside its own transaction, not the delivery, which happens later on a background thread. The harness that measures it ships in the repository as build/loadtest.sh, so you can run it on your own hardware rather than taking the figure from a marketing page. It also fails the build if the number moves by an order of magnitude, which is the regression worth catching: somebody making capture synchronous.
Zero-PII
Sensitive values never leave your servers.
Masking happens inside your process, before anything is buffered. We cannot leak what we were never sent.
Mark a property [AuditMask] and Reldavi records a projection instead of the value: the last four digits of a card, a keyed hash of a national ID that stays searchable without being reversible, a redacted password. The original is never written to the buffer, never serialized onto the wire, and never reaches our storage.
Compliance
Which obligation are you answering?
Five rules that turn a change history from good practice into something somebody can fine you for. All four work today, including the agent-specific record the AI Act asks for.
KVKK
Processing records, retention periods and destruction. Masking happens at source, and the data can stay in Türkiye or on your own servers.
GDPR
Article 30 records of processing and Article 17 erasure, with data minimisation by design — the SDK records the properties you mark and nothing else.
EU AI Act, Article 12
In force since 2 August 2026: high-risk AI systems must record events automatically, and Article 26 makes deployers keep those logs for at least six months. Events carry a first-class agent record — model, version, tool call, whose authority, and the whole prompt rather than four kilobytes of it.
GoBD (Germany)
German tax law has a word for what this is: Revisionssicherheit. Records must be complete, traceable to who made them, unalterable once written, and readable for ten years — and an auditor may ask for them in a machine-readable export. Sealed daily roots make "unalterable" something somebody can check rather than something a vendor asserts, and the export is CSV or NDJSON with the hashes beside it.
SOC 2 and ISO 27001
Change history with actor attribution, records that nothing edits, defined retention and evidence you can export.
Language-agnostic
Your .NET service is not your only service.
The ingestion contract is a published JSON Schema. Every client implements the same one, so a Go worker and a Python job land in the same timeline as your ASP.NET API.
.NET
Automatic capture through an EF Core interceptor. Nothing to call by hand.
Node.js
Zero dependencies, no build step. diff() helper included.
Python
No runtime dependencies — the client speaks HTTP over the standard library.
Go
Standard library only. A buffered channel and one background dispatcher.
Every SDK guarantees the same thing: capture never blocks, never throws, masks before buffering, and drains on shutdown. All four are MIT licensed, so the capture layer is yours whether or not the platform is.
Four implementations of one wire format drift in the way nobody notices — a field spelled differently in one of them, a timestamp without its offset, a body encoded as Latin-1 — and the place it surfaces is your integration, on the day you are wiring up a second service. So every build runs all four SDKs against a real ingestion endpoint over a real socket, posts the same event from each, and reads it back through the real query API. One expected row, four languages. A language whose toolchain is missing is reported as unchecked, never as passing.
Deployment
Our servers or yours. Same product.
Reldavi Cloud
We run it. You get an endpoint and a dashboard, and we handle scaling, upgrades and backups.
- — Up in minutes
- — Managed retention and archival
- — Regional data residency
Self-hosted
One compose file brings up the whole stack on your infrastructure. Nothing calls home.
- — Flat price, unlimited events
- — Your network, your backups
- — Air-gapped friendly — no CDN, no external fonts
Pricing
Priced per event, not per person.
Audit logging is infrastructure, so it should not cost more because your team grew. Every plan includes unlimited users, the full dashboard and every SDK.
Free
$0/month
100,000 events a month
Team
$49/month
2,000,000 events a month
Business
$199/month
10,000,000 events a month
Enterprise
$899+/month
50,000,000+ events a month
Start recording today. Answer the audit next year.
Free for 100,000 events a month, forever. No credit card, no sales call.