Documentation
Not everyone who needs the trail should see all of it.
Usually the question that decides whether a company centralises its change history at all. At forty people, "everyone sees everything" means the support desk can read payroll.
Who read the trail
Your audit trail is the most sensitive dataset you keep here — it is every change, including the ones involving people’s data. So every search, every record opened in full and every export is itself recorded, and the record is yours to read at Settings → Who read the trail.
| Exports are the row that matters | A search shows fifty rows on a screen to somebody already signed in. An export puts an unbounded number of them in a file that leaves the platform, with the row count recorded, and that is what an investigation is actually about. |
| Navigation is not access | Opening the plan page is not a read of anybody’s trail. A log full of page views is a log nobody reads, which would make the whole thing decorative. |
| An attempt is not an access | A refused request is not recorded here. Failed probes belong in your own security telemetry, not in the same list as real reads. |
| The filter is kept | What was asked for, not just that something was. "Exported one order" and "exported the whole of last year" are different events and should not look identical. |
Kept separately from your own events, so it never inflates what you are metered on and never turns "what did our application do" into a question you have to filter our noise out of. Reads through the query API are recorded against the API key that made them.
Narrowing an account
A dashboard account can be limited to particular projects, particular resource types, or both. Everything it is not allowed to read is simply not there: not in the log, not on the overview, not in the dropdowns, not on a pasted permalink, and not in an export.
| The scope is on the session | It is decided when the person signs in and never read from a request. A limit the browser can influence is not a limit. |
| Asking for more is refused, not widened | A filter naming a resource type outside the scope returns nothing and says so. Quietly rewriting it to something allowed would answer a question nobody asked; quietly dropping it would widen the read. |
| It holds on every route | The log, the permalink, the overview tiles, the report builder and the export are all the same boundary. A limit that holds on the list and not on the export is a filter, and a filter is not a boundary. |
| The dropdowns shrink too | A type the account may not read is not offered. The list of names is itself information: hr.payslip in a dropdown tells you the company runs payroll through this system. |
| Empty means everything | An account with no limits set behaves exactly as every account did before this existed, so turning it on changes nothing for the people who do not need it. |
reldavi viewer --email dispatch@acme.test --types shop.order,shop.shipment
It takes effect the next time they sign in, because the scope is written onto the session. Narrowed accounts still appear in Who read the trail exactly like any other: a smaller window is still a window, and the reads through it are still reads.
Embedding the trail in your product
Your customers can read their own audit trail inside your product, without you building a second dashboard and without them having an account with us. Mint a scoped token on your server and put it in an iframe.
dotnet add package Reldavi.Embedding
// On your server, never in the browser.
var token = EmbedToken.Issue(
apiKey,
new EmbedScope(tenantSlug, ResourceType: "shop.order", ResourceId: order.Id.ToString()),
TimeSpan.FromMinutes(5));
<iframe src="https://app.reldavi.com/embed?t=@token" title="Activity"></iframe>
| The token is the entire boundary | It names one tenant and, optionally, one resource type, one record or one actor. A scope narrows; it can never be talked into widening. |
| No secret is exchanged | The signature is HMAC-SHA256 keyed on the SHA-256 of your API key’s secret segment — which is exactly what we already store for that key. You mint from the key in your hand; we verify from the digest in our database. |
| Fifteen minutes at most, five by default | The token rides in a URL, so it ends up in browser history, Referer headers and whatever logs sit in between. A leaked one should be worthless by the time anybody finds it. |
| Framing is opt-in per origin | frame-ancestors is built from the origins you list in Settings → Embedded viewer. List nothing and the viewer refuses every frame, including yours. |
| Revocation works | Revoke the key and every token it ever signed stops immediately. |
scope.IsNarrowed is there to check before you hand one out.Single sign-on
Any OpenID Connect provider — Entra ID, Okta, Google Workspace, Keycloak. Set four values and a second button appears on the sign-in page, labelled with whatever your company actually calls that login.
SingleSignOn__Enabled=true
SingleSignOn__Authority=https://login.microsoftonline.com/<tenant>/v2.0
SingleSignOn__ClientId=<client id>
SingleSignOn__ClientSecret=<client secret>
SingleSignOn__ButtonLabel=Sign in with Entra ID
| Register the redirect URIs | https://your-dashboard/signin-sso and https://your-dashboard/signout-sso. The authorisation code flow with PKCE; the implicit flow is not offered, because it puts a token in a URL and URLs end up in browser history. |
| The provider authenticates, we authorise | What comes back is one thing: an email address that has proved it is itself. Which tenant that person belongs to, what role they hold and what they may read all come from our database, never from the token. |
| Accounts are not created on the way in | Somebody who signs in successfully and has no account here is refused. The provider cannot know which tenant a stranger belongs to, and a wrong guess would hand somebody another company's audit history — so anybody with a mailbox in that directory, including the contractor nobody remembered to remove, would otherwise get in. |
| Deactivation works through every door | An account disabled here is refused at the SSO callback too, not only at the password form. |
Passwords keep working alongside it, which matters on the day the provider is the thing that is down. Turn it off by removing the configuration; nothing else changes.
What to read next
The schema, the SDK sources and the full architecture notes are open. If something is unclear, that is a bug in our documentation. Get in touch.