Audit Events Implementation Guide | Fluid Attacks Help

Audit Events

Audit events enable product analytics and security audits across all our software components. It provides a unified way to register business-relevant events performed by human actors.

Notes
Note
This is not a debugging or technical logging system — its purpose is to give visibility into meaningful events that matter to the business.

Instrumenting

Tracks exposes an HTTP API available at https://tracks.fluidattacks.com/.

For convenience, SDKs are available in multiple languages:
  1. Python: https://pypi.org/project/fluidattacks-tracks/.
  2. JavaScript / TypeScript: https://www.npmjs.com/package/@fluidattacks/tracks.
  3. Kotlin: https://central.sonatype.com/artifact/com.fluidattacks/tracks.
Each SDK’s main page includes a simple instrumentation example; you can follow it to get started.

As a developer, your task is to install the SDK and call it wherever business-relevant events occur. If you are unsure which events should be tracked, please consult the issue author, your manager, or a product team member.

You can also run Tracks locally to validate that events are being received correctly.

From the universe repository:

cd tracks && direnv allow && tracks-local


Then, run your component with the following environment variable:


This setup will send events to your local Tracks instance, allowing you to test and verify instrumentation before deploying.

🚨 Critical Requirements

Every audit event MUST include these metadata fields when applicable:

metadata: {
  organization: "organization-name",  group: "group-name",
  // ... additional context}

Event Structure

{
  action: "CREATE" | "READ" | "UPDATE" | "DELETE",                                                                               author: string,                    // Who performed the action
  date: Date,                                                                                                         
  mechanism: "API" | "WEB" | "SCHEDULER" | "TASK" | "MIGRATION" | "JIRA" | "FORCES" | "MELTS" | "RETRIEVES" | "FIXES",  metadata: Record<string, unknown>, // MUST include organization & group if applicable                                 
  object: string,                    // What was affected                                                               objectId: string                   // Unique identifier within object type scope                                     
}

Implementation Examples

TypeScript/JavaScript

import { Tracks } from "@fluidattacks/tracks";

const client = new Tracks();
client.event.create({
  action: "CREATE",
  author: "user@example.com",  
  date: new Date(),
  mechanism: "WEB",
  metadata: {
    organization: "org-name",    
    group: "group-name",    
    description: "New vulnerability created"  
  },
  object: "Vulnerability",  
  objectId: "vuln-id"
});

Python

from fluidattacks_tracks import Tracks from fluidattacks_tracks.resources.event import Event client = Tracks() client.event.create( Event( action="CREATE",
        author="user@example.com",  
        date=datetime.now(UTC),
        mechanism="WEB",
metadata={ organization: "org-name",
            group: "group-name",    
            description: "New vulnerability created"  
}, object="Vulnerability",
        objectId="vuln-id"
) )

Object Naming Convention

Tracks support any naming convention for the object field, providing flexibility for different use cases. However, Fluid Attacks applications follow two specific naming patterns to ensure consistency across the platform:

1. Instance-Based Events

For events that reference a specific instance of a platform entity, use the entity name and include the instance ID in objectId:

object: "Vulnerability"
objectId: "vuln-id"

object: "Group"objectId: "unittesting"

object: "Group.Findings"objectId: "unittesting"

object: "Organization"objectId: "okada"

object: "Organization.Analytics.AcceptedVulnerabilitiesBySeverity"objectId: "okada"

object: "Group.Integrations.Connect.Jira"objectId: "testgroup"

2. Action-Based Events

For events that represent platform actions without a specific instance, use descriptive action names:

object: "Login"objectId: "Microsoft"
object: "Autoenroll.Welcome"objectId: "unknown"
object: "UploadGroupFile"objectId: "repo_nickname/group_name"
object: "HelpDeskMenu"objectId: "unknown"

Naming Guidelines for Fluid Attacks Applications

  1. Use PascalCase for object names.
  2. Use dots (.) for hierarchical relationships.
  3. Be descriptive but concise.
  4. Include complements for action variants when needed.
  5. Ensure objectId is unique and meaningful for the context when applicable.
  6. Follow the two patterns above for consistency across the platform.
  7. objectId must be related to the instance that starts the event name (e.g., “Group.Findings” → group name, “Organization.Analytics” → organization name/ID).
  8. objectId may use generic values like “unknown” when no specific information is relevant.

Validation Checklist

Before creating an event, verify:
  1. organization field is included in metadata (when applicable)
  2. group field is included in metadata (when applicable)
  3. object follows the naming convention
  4. author is properly identified
  5. mechanism matches the context
  6. objectId is unique and meaningful
  7. Verify that no unauthorized information is captured

Common Patterns

Conditional Metadata

Use conditional inclusion when organization and group information may not always be available or applicable. This ensures metadata fields are only included when they have meaningful values.

const metadata: Record<string, unknown> = {};
if (groupName) metadata.group = groupName;
if (organizationName) metadata.organization = organizationName;

Best Practices

✅ Good Examples

// Includes all required context
metadata: {
  organization: "org-name",
  group: "group-name",  
  state: {
    source: "ANALYST",    
    status: "VULNERABLE",    
    severity: "HIGH"  }
}

// Conditional inclusion
const metadata: Record<string, unknown> = {};
if (groupName) metadata.group = groupName;
if (organizationName) metadata.organization = organizationName;

❌ Bad Examples

// Inconsistent naming
metadata: {
  org: "org-name",
  groupName: "group-name"
}

// Including undefined values
metadata: {
  group: groupName, // Could be undefined
  organization: organizationName // Could be undefined}

More about Tracks

  1. Introduction
  2. Swagger UI
Idea
Tip
Have an idea to simplify our architecture or noticed docs that could use some love? Don't hesitate to open an issue or submit improvements.