.NET API
IAuditLog
Tracon.Abstractions.dllThe audit trail log.
public interface IAuditLogRemarks
Section titled “Remarks”Writing happens in the store decorators, not in the endpoint layer: writing in the endpoint layer would miss a change made to the same store from another code path. The decorator is the single gate.
A write failure does not stop the operation — with eight named exceptions. For every audit write but those eight, a failed IAuditLog.WriteAsync is logged and the operation continues: observability does not break behaviour. The record can therefore be missing, which is what an audit trail proves and does not prove.
The eight FAIL-CLOSED operations write their entry BEFORE the change and do not apply
the change if the write fails: an approval decision; granting or revoking a skill
script; running a skill script; saving or deleting an inbound trigger; an automatic
canary rollback; a data subject erasure; and answering a workflow’s pending human
request (workflow.respond), which is written before the answer starts a run; and answering a tool’s
request for user input (tool_input.answer), which is written before the suspended tool call runs again. Each of these is irreversible or
security-bearing enough that a missing record is worse than a refused operation, so
a failed write surfaces to the caller as an error instead of a warning in a log.
That split is a published guarantee, not an implementation detail:
an implementation of this interface that swallows its own write errors turns eight
fail-closed operations into best-effort ones without anything else changing. Throw
on a write that did not happen. Both paths count a failure on the
tracon.audit.write_failures counter, tagged swallowed or
refused, so the difference stays visible in production.
There is a read endpoint only; there is no delete or edit endpoint, and there will not be one.
DI lifetime. Registered as a singleton with TryAdd;
a consumer’s own registration wins. An implementation must be safe under concurrent
calls and must not capture or depend on a scoped service.
Tenant behavior — EXPECTED tenant, with an AMBIENT fallback.
IAuditLog.WriteAsync is scoped by the entry’s own AuditEntry.TenantId
field — an explicit, EXPECTED tenant that is never inferred. IAuditLog.QueryAsync
and IAuditLog.VerifyChainAsync accept an explicit TenantId override
(AuditQuery.TenantId, AuditChainQuery.TenantId) and fall
back to the AMBIENT ITenantContext.TenantId ONLY when that override is
null or empty. A null TenantId is a
contract, not a convenience: it MUST resolve to the single ambient
tenant, never to “every tenant”. An implementation that treats a missing filter as “no
filter” returns every tenant’s records to whoever leaves the field unset — this is
exactly the shape AuditLogContract’s ambient-fallback scenarios (BL-046) guard
against; the built-in InMemoryAuditLog and SqlAuditLog both resolve the
fallback through an injected ITenantContext, the same pattern
IRunStore’s AMBIENT methods use.
Methods
Section titled “Methods”QueryAsync(AuditQuery, CancellationToken)
Section titled “ QueryAsync(AuditQuery, CancellationToken)”Reads the records through a filter.
ValueTask<IReadOnlyList<AuditEntry>> QueryAsync(AuditQuery query, CancellationToken cancellationToken = default)Parameters
Section titled “Parameters”query AuditQuery
The filter.
cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”ValueTask<IReadOnlyList<AuditEntry>>
The records, newest first.
VerifyChainAsync(AuditChainQuery, CancellationToken)
Section titled “ VerifyChainAsync(AuditChainQuery, CancellationToken)”Walks a tenant’s hash chain and reports whether it is intact.
ValueTask<AuditChainVerification> VerifyChainAsync(AuditChainQuery query, CancellationToken cancellationToken = default)Parameters
Section titled “Parameters”query AuditChainQuery
The scope: tenant and, optionally, a date range.
cancellationToken CancellationToken
The cancellation token.
Returns
Section titled “Returns”ValueTask<AuditChainVerification>
The verification result.
Remarks
Section titled “Remarks”A date range narrows which entries are walked; it does not weaken the check within that range. Because the entry immediately before the range’s start is not read, a break at the range’s own boundary cannot be judged and is not reported — an unbounded query is the only way to check a tenant’s whole history.
WriteAsync(AuditEntry, CancellationToken)
Section titled “ WriteAsync(AuditEntry, CancellationToken)”Writes an audit record.
ValueTask WriteAsync(AuditEntry entry, CancellationToken cancellationToken = default)Parameters
Section titled “Parameters”entry AuditEntry
The record to write.
cancellationToken CancellationToken
The cancellation token.