Skip to content

Security

A compliance tool has to survive its own audit.

Here is precisely what Reldavi stores, what it refuses to store, and how the boundaries are enforced. If any of this does not satisfy your reviewer, tell us what is missing.

Masking happens before anything is buffered

This is the most important boundary in the product. A property marked [AuditMask] is projected inside your process, on your servers, before the event reaches the in-memory buffer. The original value is never queued, never serialized, never transmitted and never stored. We cannot disclose what we were never given.

IN YOUR APPLICATION WHAT WE RECEIVE 4242 4242 4242 4242 ************4242 12345678901 sha256:9f2b1c74a0e8… emre@contoso.com e***@contoso.com P@ssw0rd! ***

Redact

The value is replaced entirely. Nothing about the input survives.

Preserve last / first

Keeps the digits a human needs to recognise a record — ************4242 — and nothing more.

Keyed hash

HMAC-SHA256 with a secret that stays on your servers. Identical inputs hash identically, so the field stays searchable without being reversible.

Email

Keeps the domain and one character: e***@contoso.com.

Length

Records only that the value changed, and how long it was.

The keyed hash matters more than it looks. An unkeyed digest of a national ID or a card number is reversible by brute force in seconds, because the input space is tiny. Keyed with a secret we never see, the digest is a pseudonym instead.

Tenant isolation is structural, not procedural

The tenant comes from your credential

It is resolved from the API key at the edge and never read from the payload. A producer that named someone else's tenant could write into their history, so producers are not allowed to name one.

Every query carries it

The tenant predicate is added by the query builder, not by each handler. A test asserts that no generated statement can leave without it.

Storage is partitioned by tenant

Which is also why erasing one customer's data is a metadata operation that completes in milliseconds rather than a table rewrite.

And a second boundary inside it

An account can be narrowed further, to particular projects or resource types, so that "the support team can read order history and cannot read payroll" is a sentence you can actually say. That scope is put on the session at sign-in and applied on every route, including exports.

Your infrastructure Your app EF Core interceptor Buffer In memory, bounded Ingestion Batched, gzipped Event store Columnar, compressed Masking happens here Nothing here is on your request path

Retention is a contract, not a setting we forgot

Each customer has their own window. A bank buying seven years and a startup buying thirty days live in the same deployment without either affecting the other. Expired data is dropped per tenant on a schedule, and if a tenant is owed an archive that cannot be written, the data is kept rather than deleted — losing evidence somebody paid to keep is the worse failure.

Nothing edits history

There is no API that modifies a recorded event. The only write path is ingestion, and the only delete path is retention.

Every day is sealed, and the seals are chained

Each day closes with a Merkle root over every event in it, and each seal carries the hash of the day before. Altering one record a year ago means recomputing every seal since — including the ones you have already been shown. You can verify any day yourself from the dashboard, and a single event can prove it belongs to its sealed day with a proof of a few dozen bytes.

Retries do not duplicate

Event ids are generated by the client as UUIDv7 and used as the idempotency key, so a batch resent after a timeout collapses instead of doubling.

Exports are exact

Report exports deduplicate strictly, because an export is the artefact an auditor relies on.

One button produces the whole file

A date range in, an archive out: the records as CSV and JSON, the Merkle root sealed for every day in the range, the destruction certificates for anything since removed, a manifest carrying the SHA-256 of every file, and a page explaining how to check all of it. Assembling that by hand is where a week goes.

Rollbacks leave nothing

Events are emitted only after your transaction commits.

What this gives your compliance programme

KVKK

Personal data can be masked or pseudonymised at source, so it never enters the audit store. Erasure requests are satisfied by dropping a tenant partition. Data can stay in Türkiye, or on your own servers. Every destruction issues a numbered certificate carrying the Merkle roots of the days it destroyed — because proving destruction is a documentary duty, and the guidance treats a missing document as evidence it never happened.

GDPR

Article 30 records of processing, Article 17 erasure, and data-minimisation by design — the SDK records the properties you mark and nothing else. A data processing agreement is available.

GoBD

German tax law requires Revisionssicherheit: records complete, traceable to whoever made them, unalterable once written, retained ten years, and producible as a machine-readable export on request. The sealed daily roots are what turns "unalterable" from an assertion into something an auditor can recompute, and the export is CSV or NDJSON with the checkpoint hashes beside it. Retention is set per resource type, so ten years for the records the tax office asks about does not mean ten years for session logs.

SOC 2

Change history with actor attribution, immutable records, defined retention and exportable evidence covers a large part of the Change Management and Logging criteria.

ISO 27001

Supports A.8.15 logging and A.8.16 monitoring with per-record attribution and tamper-evident history.

EU AI Act

Article 12 has required high-risk AI systems to record events automatically since 2 August 2026, and Article 26 requires deployers to keep those logs for at least six months. Agent events carry their own block — which model, which version, which tool call, on whose authority, and whether a person approved it — and the whole prompt and result rather than a clipped sample. The day's Merkle root covers all of it, so a record whose model was edited afterwards no longer verifies.

How the platform itself is built

API key secrets are never stored

Only a SHA-256 digest of a 256-bit random secret. Passwords use PBKDF2 with the ASP.NET Core hasher.

Transport is TLS only

Payloads are gzipped, authenticated with a bearer token and rate limited per tenant.

The dashboard ships a strict CSP

No bundler, no CDN, no external fonts — so self covers the whole application and there is no inline script to whitelist.

Exports are injection-safe

CSV fields that a spreadsheet would execute are neutralised. A display name is attacker-controlled, and an audit report should not run code on the auditor's machine.

Dependencies fail the build

A published advisory on any package stops the build. Warnings are errors.

Need a security review pack?

Architecture documentation, our data processing agreement and a sub-processor list are available on request.