Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Codefied-CodePix/Karokar-backend/llms.txt

Use this file to discover all available pages before exploring further.

KaroKar operates in a regulated, trust-sensitive environment where corporate organizations, vehicle vendors, and employees exchange legal and financial obligations. Every business-critical action — booking approvals, vehicle suspensions, employee assignments, organization onboarding — must be traceable, attributable, and permanently recorded. The Audit domain fulfills this requirement by subscribing to domain events emitted by all other domains and automatically creating structured, immutable audit records. Business domains never create audit records directly; they simply emit events and the Audit domain takes care of the rest.

Core Principle

As established in ADR-008, the platform must always be able to answer five questions about any business action:

Who?

Actor — which user, admin, or system process performed the action (actorId)

What?

Action — which operation was performed (e.g., Booking Approved, Vehicle Suspended)

Which?

Entity — the specific record that was affected (entityType + entityId)

When?

Time — the exact UTC timestamp of the action (timestamp)

Whose tenant?

Organization — which organization performed the action (organizationId), required for multi-tenant investigations

AuditRecord Entity

Every audit record is stored as a single, structured database row. Records are indexed by organization + timestamp, entity type + ID, and actor ID to support fast investigation queries.
FieldTypeDescription
iduuidUnique audit record identifier
eventTypestringThe domain event that triggered this record (e.g., VehicleSuspended)
entityTypestringThe kind of entity affected (e.g., Vehicle, Booking)
entityIduuidThe specific entity that was affected
actorIduuid | nullUser who performed the action; null for system-generated events
organizationIduuidThe tenant organization context
actionstringHuman-readable description of what happened
timestamptimestamptzUTC timestamp of the event
metadatajsonbStructured payload with event-specific details (e.g., before/after state)

Example Audit Record

{
  "id": "a1b2c3d4-...",
  "eventType": "VehicleSuspended",
  "entityType": "Vehicle",
  "entityId": "vehicle_123",
  "actorId": "user_456",
  "organizationId": "vendor_789",
  "action": "Vehicle suspended",
  "timestamp": "2026-06-02T10:00:00Z",
  "metadata": {
    "reason": "INSURANCE_EXPIRED",
    "before": { "status": "ACTIVE" },
    "after":  { "status": "SUSPENDED" }
  }
}

How Audit Records Are Generated

The Audit domain uses NestJS event listeners to subscribe to domain events. Business domains emit events — the Audit domain converts them into records automatically through mapDomainEventToAuditRecord(). Booking events are captured by BookingAuditListener:
@Injectable()
export class BookingAuditListener {
  constructor(private readonly auditService: AuditService) {}

  @OnEvent('booking.*')
  async handleBookingEvent(event: DomainEvent): Promise<void> {
    await this.auditService.createRecord(mapDomainEventToAuditRecord(event));
  }
}
Fleet/vehicle events are captured by FleetAuditListener:
@Injectable()
export class FleetAuditListener {
  constructor(private readonly auditService: AuditService) {}

  @OnEvent('vehicle.*')
  async handleVehicleEvent(event: DomainEvent): Promise<void> {
    await this.auditService.createRecord(mapDomainEventToAuditRecord(event));
  }
}
Assignment events are captured by AssignmentAuditListener:
@Injectable()
export class AssignmentAuditListener {
  constructor(private readonly auditService: AuditService) {}

  @OnEvent('assignment.*')
  async handleAssignmentEvent(event: DomainEvent): Promise<void> {
    await this.auditService.createRecord(mapDomainEventToAuditRecord(event));
  }
}
Organization events are captured by OrganizationAuditListener:
@Injectable()
export class OrganizationAuditListener {
  constructor(private readonly auditService: AuditService) {}

  @OnEvent('organization.*')
  async handleOrganizationEvent(event: DomainEvent): Promise<void> {
    await this.auditService.createRecord(mapDomainEventToAuditRecord(event));
  }
}
Verification events are captured by VerificationAuditListener:
@Injectable()
export class VerificationAuditListener {
  constructor(private readonly auditService: AuditService) {}

  @OnEvent('verification.*')
  async handleVerificationEvent(event: DomainEvent): Promise<void> {
    await this.auditService.createRecord(mapDomainEventToAuditRecord(event));
  }
}
The wildcard subscriptions (booking.*, vehicle.*, assignment.*, organization.*, verification.*) mean that every new domain event added to those channels is automatically audited — no additional listener code is required.
Business domains must never call AuditService.createRecord() directly. The correct pattern is: domain emits an event → Audit domain listener handles it → audit record is created. This keeps audit creation decoupled from business logic and guarantees consistency.

AuditService Query Methods

AuditService exposes three query methods for investigation workflows:
// Retrieve the full history of a specific entity
findByEntity(entityType: string, entityId: string): Promise<AuditRecord[]>

// Retrieve all actions performed by a specific user
findByActor(actorId: string): Promise<AuditRecord[]>

// Retrieve all actions within a specific organization (tenant)
findByOrganization(organizationId: string): Promise<AuditRecord[]>
These power investigation queries such as:
  • “Show the complete lifecycle of Booking #XYZ”findByEntity('Booking', bookingId)
  • “What did this vendor admin do last week?”findByActor(userId)
  • “All audit events for this corporate organization”findByOrganization(orgId)

Audit Event Sources by Domain

All major business domains are audit producers. The following events are audited in Phase 01:
EventTriggered When
BookingRequestedCorporate submits a booking request to the vendor
BookingApprovedVendor approves the booking
BookingRejectedVendor rejects the booking
BookingCancelledBooking cancelled before vehicle handover
BookingCompletedVehicle returned; booking reached planned conclusion
BookingTerminatedBooking ended prematurely after becoming operational
EventTriggered When
AssignmentCreatedAn assignment is created for an employee
AssignmentAcceptedEmployee accepts the vehicle handover
AssignmentRejectedEmployee declines the assignment
AssignmentClosedAssignment force-closed by a booking termination
EventTriggered When
VehicleCreatedA new vehicle is registered on the platform
VehicleActivatedVehicle status transitions from PENDING to ACTIVE
VehicleSuspendedVehicle is suspended (insurance, registration, admin, etc.)
VehicleMaintenanceScheduledA maintenance window is created for a vehicle
VehicleMaintenanceCompletedMaintenance is marked complete and availability released
EventTriggered When
VerificationRequestedA verification workflow is initiated
VerificationApprovedVerification is approved
VerificationRejectedVerification is rejected
VerificationExpiredA verification document or record has expired
VerificationRevokedAn approved verification is revoked
EventTriggered When
OrganizationCreatedA new vendor or corporate organization is registered
OrganizationApprovedAn organization is approved and onboarded
OrganizationSuspendedAn organization is suspended from the platform

System Actor

Some audit events are generated by automated platform processes rather than a human user — for example, when a verification document expires or when availability is released on booking completion. For these events, actorId is set to null (the column is typed uuid | null and is nullable in the database):
{
  "eventType": "VerificationExpired",
  "entityType": "Verification",
  "entityId": "verif_abc",
  "actorId": null,
  "action": "Verification expired automatically",
  "timestamp": "2026-08-01T00:00:00Z"
}
System-generated audit records carry the same compliance weight as user-initiated ones. Automated actions are fully traceable and immutable.

Immutability

Audit records are strictly read-only once created. The system deliberately does not expose any endpoint or service method to edit or delete an audit record.
Audit records must never be edited or deleted. The AuditService exposes only createRecord and read methods — there is no updateRecord or deleteRecord. Any requirement to “correct” an audit entry must be addressed by creating a new, compensating audit record rather than mutating history.

Retention Policy

Phase 01 retention policy: All audit records are retained indefinitely. No deletion, archival, or expiry is applied. Future phases may introduce cold-storage archival and regulatory retention rules, but audit history will never be silently removed.

Change Tracking

For update operations, the metadata field stores a change set showing the state before and after the action:
{
  "metadata": {
    "before": { "status": "ACTIVE" },
    "after":  { "status": "SUSPENDED" },
    "reason": "INSURANCE_EXPIRED"
  }
}
This enables precise reconstruction of what changed at any point in the entity’s history, without requiring a separate versioning table.

Build docs developers (and LLMs) love