The Audit Trail system provides comprehensive tracking of all critical actions in the TruthBounty API. It ensures traceability by logging:
- Claim submission, updates, and finalization
- Evidence/verification submissions and updates
- Reward calculations and distributions
- User actions and wallet linking
The audit system consists of:
Stores audit log records with:
- Action type (e.g.,
CLAIM_CREATED,EVIDENCE_SUBMITTED) - Entity type and ID being acted upon
- User ID who performed the action
- Before and after state snapshots
- Metadata (IP address, user agent, correlation ID)
- Timestamp
Core service providing:
log()- Record a new audit eventgetEntityAuditLogs()- Get all logs for a specific entitygetUserAuditLogs()- Get all actions by a usergetActionAuditLogs()- Get all logs for a specific action typegetAuditLogs()- Advanced filtering querygetChangeHistory()- Get complete change history for an entitygetAuditSummary()- Get summary statistics
Decorator to automatically log method calls:
@AuditLog({
actionType: AuditActionType.CLAIM_CREATED,
entityType: AuditEntityType.CLAIM,
entityIdPath: 'args.0.id',
captureAfterState: true,
})
async createClaim(data) { }Automatically processes decorated methods and logs results/errors.
REST endpoints for querying audit logs.
CLAIM_CREATED- New claim submittedCLAIM_UPDATED- Claim data modifiedCLAIM_RESOLVED- Claim verdict determinedCLAIM_FINALIZED- Claim locked/finalized
EVIDENCE_SUBMITTED- Evidence provided for claimEVIDENCE_UPDATED- Evidence version updatedEVIDENCE_FLAGGED- Evidence marked as problematicEVIDENCE_VERIFIED- Evidence verified/approvedVERIFICATION_COMPLETED- Verification process completed
REWARD_CALCULATED- Reward amount computedREWARD_DISTRIBUTED- Reward sent to userREWARD_CLAIMED- User claimed their reward
USER_CREATED- New user registeredUSER_UPDATED- User profile changedWALLET_LINKED- Wallet connected to accountVERIFICATION_INITIATED- Verification started
GET /audit?entityType=CLAIM&actionType=CLAIM_CREATED&userId=xxx&limit=50&offset=0
Query Parameters:
entityType- Filter by entity type (CLAIM, EVIDENCE, REWARD, USER, WALLET)actionType- Filter by action typeuserId- Filter by user IDlimit- Max results (default: 100, max: 500)offset- Pagination offset (default: 0)
Response:
{
"logs": [
{
"id": "uuid",
"actionType": "CLAIM_CREATED",
"entityType": "CLAIM",
"entityId": "claim-id",
"userId": "user-id",
"walletAddress": "0x...",
"description": "Claim created",
"beforeState": null,
"afterState": { "id": "claim-id", "verdict": true },
"metadata": { "method": "ClaimsService", "handler": "createClaim" },
"ipAddress": "192.168.1.1",
"userAgent": "Mozilla/5.0...",
"createdAt": "2026-03-28T12:00:00Z",
"correlationId": "corr-id"
}
],
"total": 150
}GET /audit/entity/CLAIM/claim-id-123
Returns all actions performed on a specific entity in chronological order.
GET /audit/user/user-id-123?limit=50&offset=0
Returns all actions performed by a specific user.
GET /audit/action/CLAIM_CREATED?limit=50
Returns all logs for a specific action type.
GET /audit/changes/CLAIM/claim-id-123
Returns a formatted change history showing what changed over time:
[
{
"timestamp": "2026-03-28T12:00:00Z",
"action": "CLAIM_CREATED",
"userId": "user-id",
"changes": {}
},
{
"timestamp": "2026-03-28T12:05:00Z",
"action": "CLAIM_RESOLVED",
"userId": "verifier-id",
"changes": {
"resolvedVerdict": { "before": null, "after": true },
"confidenceScore": { "before": 0, "after": 0.95 }
}
}
]GET /audit/summary?entityType=CLAIM&days=7
Returns a count of actions by type for the specified period:
{
"CLAIM_CREATED": 45,
"CLAIM_RESOLVED": 38,
"CLAIM_FINALIZED": 35,
"EVIDENCE_SUBMITTED": 92
}Use the @AuditLog() decorator on service methods:
@AuditLog({
actionType: AuditActionType.CLAIM_CREATED,
entityType: AuditEntityType.CLAIM,
descriptionTemplate: 'Claim created by user',
captureAfterState: true,
})
async createClaim(data: CreateClaimDto): Promise<Claim> {
// Your implementation
}Inject AuditTrailService and call log() directly:
constructor(private auditTrailService: AuditTrailService) {}
async resolveClaim(claimId: string, verdict: boolean) {
const claim = await this.claimRepo.findOne(claimId);
const beforeState = { ...claim };
// Update claim
claim.resolvedVerdict = verdict;
const updated = await this.claimRepo.save(claim);
// Log the action
await this.auditTrailService.log({
actionType: AuditActionType.CLAIM_RESOLVED,
entityType: AuditEntityType.CLAIM,
entityId: claimId,
userId: currentUserId,
description: `Claim resolved with verdict: ${verdict}`,
beforeState,
afterState: updated,
});
return updated;
}| Field | Type | Description |
|---|---|---|
id |
UUID | Unique audit log identifier |
actionType |
Enum | Type of action performed |
entityType |
Enum | Type of entity (CLAIM, EVIDENCE, etc.) |
entityId |
String | ID of the entity affected |
userId |
String | ID of user who performed action (nullable) |
walletAddress |
String | Wallet address if applicable |
description |
Text | Human-readable description |
beforeState |
JSON | Entity state before action |
afterState |
JSON | Entity state after action |
metadata |
JSON | Additional context (service, handler name, errors) |
ipAddress |
String | Client IP address |
userAgent |
String | Client user agent |
createdAt |
DateTime | Timestamp of action |
correlationId |
String | Request correlation ID for tracing |
Audit logs are stored indefinitely by default. For retention policies:
// Delete logs older than 90 days
await this.auditTrailService.deleteOldLogs(90);- Always include userId - Track who performed each action
- Capture state changes - Use
beforeStateandafterStatefor change tracking - Use correlation IDs - For tracing related actions across requests
- Include wallet address - For blockchain-related actions
- Add meaningful descriptions - For quick human review
- Set metadata - Include context like method/handler names
async resolveClaim(
claimId: string,
verdict: boolean,
confidenceScore: number,
userId: string,
): Promise<Claim> {
const claim = await this.claimRepo.findOne(claimId);
await this.auditTrailService.log({
actionType: AuditActionType.CLAIM_RESOLVED,
entityType: AuditEntityType.CLAIM,
entityId: claimId,
userId,
description: `Resolved with verdict: ${verdict}, confidence: ${confidenceScore}`,
beforeState: {
resolvedVerdict: claim.resolvedVerdict,
confidenceScore: claim.confidenceScore,
finalized: claim.finalized,
},
afterState: {
resolvedVerdict: verdict,
confidenceScore,
finalized: claim.finalized,
},
metadata: {
claimType: claim.claimType,
verificationMethod: 'automated',
},
});
claim.resolvedVerdict = verdict;
claim.confidenceScore = confidenceScore;
return this.claimRepo.save(claim);
}// Get all actions by a user
const { logs, total } = await this.auditTrailService.getUserAuditLogs(userId);
// Get all claim-related actions by the user
const { logs } = await this.auditTrailService.getAuditLogs(
AuditEntityType.CLAIM,
undefined,
userId,
100,
);
// Get change history for a claim
const history = await this.auditTrailService.getChangeHistory(
AuditEntityType.CLAIM,
claimId,
);The audit trail enables:
- Compliance reporting - Prove who did what and when
- Fraud detection - Identify suspicious patterns
- Performance analysis - Track action frequencies
- User behavior - Understand user workflows
- Change tracking - See all modifications to claims/evidence
- SLA audits - Verify timelines and SLAs
- Audit logs are immutable (no deletion of historical logs)
- Includes IP address and user agent for security analysis
- Correlation IDs enable request tracing
- All timestamps are critical for compliance
- Never log sensitive data (passwords, keys) in state fields
To apply the audit trail to your database:
npm run typeorm migration:generate -- src/migrations/AddAuditLog
npm run typeorm migration:runOr manually create the audit_logs table:
CREATE TABLE audit_logs (
id UUID PRIMARY KEY,
actionType VARCHAR NOT NULL,
entityType VARCHAR NOT NULL,
entityId VARCHAR NOT NULL,
userId VARCHAR,
walletAddress VARCHAR,
description TEXT,
beforeState JSON,
afterState JSON,
metadata JSON,
ipAddress VARCHAR,
userAgent VARCHAR,
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
correlationId VARCHAR,
INDEX (userId),
INDEX (entityType),
INDEX (actionType),
INDEX (createdAt),
INDEX (entityId),
INDEX (userId, createdAt),
INDEX (actionType, createdAt)
);