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 });
});