Audit Logging
Security event audit trail for accountability, forensics, and compliance.
Overview
NIN Connect records security-relevant events in the auditLogs database table. Every log entry captures who performed an action, what they did, and contextual metadata like IP address and user agent.
Audit logs are fire-and-forget. They never block request processing and never cause a route handler to fail if the log write encounters an error. This ensures audit logging is invisible to end-users while still capturing events reliably.
Schema
| Column | Type | Description |
|---|---|---|
id | UUID | Auto-generated primary key |
action | string | Event name (e.g. api_key.created) |
actor | string | Developer ID or system identifier |
target | string (nullable) | Resource affected (key ID, plugin ID, etc.) |
metadata | JSON (nullable) | Additional context (IP, user agent, request details) |
createdAt | timestamp | When the event occurred |
Logged Events
| Event | Action | Actor | Target | When |
|---|---|---|---|---|
| Developer login | developer.login | Developer ID | — | Firebase auth sync |
| API key created | api_key.created | Developer ID | Key ID | Dashboard key creation |
| API key deleted | api_key.deleted | Developer ID | Key ID | Dashboard key deletion |
| Plugin created | plugin.created | Developer ID | Plugin ID | Dashboard plugin creation |
| Plugin updated | plugin.updated | Developer ID | Plugin ID | Dashboard plugin update |
| Plugin deleted | plugin.deleted | Developer ID | Plugin ID | Dashboard plugin deletion |
Usage
The audit helper is in lib/security/audit.ts:
logAudit({
action: "api_key.created",
actor: admin.id,
target: newKey.id,
metadata: {
...getRequestMeta(req),
keyName: parsed.data.name,
},
});getRequestMeta(req) extracts the client IP and user agent from the request headers.
Querying Audit Logs
Audit logs are stored in PostgreSQL and can be queried directly:
-- all actions by a specific developer
SELECT * FROM audit_logs WHERE actor = 'dev_uuid' ORDER BY created_at DESC;
-- all API key events in the last 24 hours
SELECT * FROM audit_logs
WHERE action LIKE 'api_key.%'
AND created_at > NOW() - INTERVAL '24 hours';
-- login events with IP addresses
SELECT action, actor, metadata->>'ip' AS ip, created_at
FROM audit_logs
WHERE action = 'developer.login'
ORDER BY created_at DESC;Extending Audit Coverage
To add audit logging to a new endpoint:
- Import
logAuditandgetRequestMetafromlib/security/audit - Choose a descriptive
actionname following theresource.verbpattern - Call
logAudit()after the operation succeeds (do notawaitit)
import { logAudit, getRequestMeta } from "@/lib/security/audit";
// after the operation succeeds
logAudit({
action: "resource.updated",
actor: admin.id,
target: resourceId,
metadata: getRequestMeta(req),
});Retention
Audit logs accumulate over time. For production deployments, consider:
- Setting up a cron job or scheduled task to archive or delete logs older than your retention period
- Exporting logs to an external SIEM or log management service for long-term storage and alerting
- Adding indexes on
actionandactorcolumns if query performance degrades