Node SDK · Behaviors worth knowing
Empty action is a no-op
Events with no action set are dropped at send time. Useful when you want the middleware to attach an event to every request and let handlers conditionally opt out — set action only when something audit-worthy happens.
app.get("/api/secrets/:id", (req, res) => {
const s = vault.get(req.params.id);
if (!s) {
res.status(404).send("not found");
return; // action never set → event silently dropped
}
req.event!.action = "secret.read";
req.event!.target = { type: "secret", id: s.id };
res.json(s);
});
record() is non-blocking
record() enqueues the event on the in-memory buffer and returns immediately — the background loop handles the HTTP call on the next flush. Don't await audit acks in critical paths; awaiting buys you nothing because the network call hasn't started yet.
const start = performance.now();
rec.record(e);
console.log(performance.now() - start); // sub-millisecond — no network call
Caller-set fields take precedence
The middleware auto-populates result from the response status. If you set result yourself, that wins — useful for anti-enumeration handlers that return the same HTTP status for different audit outcomes.
app.get("/api/secrets/:id", (req, res) => {
const me = actor(req)!;
const id = req.params.id;
req.event!.action = "secret.reveal";
req.event!.target = { type: "secret", id };
const s = vault.get(id);
if (!s) {
res.status(404).send("not found"); // genuinely doesn't exist
return; // middleware records result: { status: "error", code: 404 }
}
if (s.tenantId !== me.tenantId) {
// Return 404 instead of 403 so attackers can't probe for
// secret IDs in other tenants. The user-facing response can't
// tell the two cases apart — but the audit log should.
req.event!.result = { status: "denied", code: 403, message: "cross-tenant access" };
res.status(404).send("not found");
return;
}
res.json({ value: s.value });
});