Class: Audit
Defined in: audit/src/index.ts:710
Constructors
Constructor
> new Audit(store, redact?, tenancyActive?, options?): Audit
Defined in: audit/src/index.ts:720
Parameters
store
redact?
AuditRedactor = defaultAuditRedactor
Scrubs each payload before it is stored. Default masks common secret keys.
tenancyActive?
() => boolean
Whether the host app is multi-tenant, i.e. whether @basaltkit/tenancy is registered. auditPlugin wires this to the container's 'tenancy:active' metadata marker; it is a signal, never an import — @basaltkit/audit is a generic package and must not depend on tenancy.
Defaults to false: a hand-built new Audit(store) behaves like a single-tenant app, which is the only thing it can safely assume.
options?
AuditOptions = {}
Returns
Audit
Methods
record()
> record(event, payload?): Promise<AuditEntry>
Defined in: audit/src/index.ts:755
Manual entry — for actions no hook covers.
Parameters
event
string
payload?
unknown
Returns
Promise<AuditEntry>
systemTrail()
> systemTrail(query?): Promise<AuditEntry[]>
Defined in: audit/src/index.ts:825
SYSTEM-ONLY escape hatch: reads across ALL tenants (or whatever query.tenantId explicitly pins), bypassing the tenant auto-scoping that trail enforces.
This exists for trusted platform/admin tooling only. NEVER call it with, or forward into it, client-controlled input — doing so re-opens the cross-tenant data-exposure that trail closes.
Parameters
query?
AuditQuery = {}
Returns
Promise<AuditEntry[]>
trail()
> trail(query?): Promise<AuditEntry[]>
Defined in: audit/src/index.ts:785
Reads the audit trail — the everyday read.
Tenant scoping (PII F2), applied only where a tenant dimension exists:
- When a tenant is present in the ambient context, the read is FORCED to that tenant. Any caller-supplied
query.tenantIdis ignored/overridden (the context tenant is spread LAST so it always wins), so a tenant-facing handler that forwards client input — e.g.trail({ tenantId: req.query.tenantId })— can never widen the scope and read another tenant's trail. - With no tenant in context, an explicit single-tenant read (
trail({ tenantId })) is honoured. - With no tenant in context and no explicit
tenantId, the behavior depends on whether the app is multi-tenant at all:- Tenancy registered (
@basaltkit/tenancypresent): the read is REFUSED. Returning every tenant's records must be a deliberate, system-only act via systemTrail, never the silent default. - No tenancy (single-tenant/non-SaaS app): there is no tenant dimension to scope to, so this is simply "read the trail" and returns the rows.
@basaltkit/auditis a general-purpose package; it must work without the opt-in SaaS layer.
- Tenancy registered (
Parameters
query?
AuditQuery = {}
Returns
Promise<AuditEntry[]>
verify()
> verify(options?): Promise<AuditVerifyResult>
Defined in: audit/src/index.ts:848
Verifies one hash chain: recomputes every entry's hash and checks seq continuity and the prevHash links. Tenant scoping mirrors trail: inside a tenant context the context tenant is forced; otherwise tenantId picks the chain, and omitting it verifies the system chain.
Rows of the tenant outside the chain are checked too: those written before the chain began (see legacyUntil) are counted as unchained; any other is listed in unverified and makes the result ok: false — trail() would serve it as history. Truncating the tail of a chain leaves no gap: pass the head recorded elsewhere as expectedHead to catch that.
Parameters
options?
AuditVerifyOptions = {}
Returns
Promise<AuditVerifyResult>
verifyAll()
> verifyAll(options?): Promise<AuditVerifyAllResult>
Defined in: audit/src/index.ts:864
SYSTEM-ONLY: verifies every chain in the store (each tenant plus the system chain). Like systemTrail, for trusted tooling (basalt audit:verify --all).
Inside a tenant context it is scoped like verify: only that tenant's chain is verified (and reported), so tenant-facing code cannot enumerate other tenants' ids and heads through it.
Parameters
options?
Returns
Promise<AuditVerifyAllResult>