Files
stack/docs/4-api/3-activity-logging
Jason Woltje 12abdfe81d feat(#93): implement agent spawn via federation
Implements FED-010: Agent Spawn via Federation feature that enables
spawning and managing Claude agents on remote federated Mosaic Stack
instances via COMMAND message type.

Features:
- Federation agent command types (spawn, status, kill)
- FederationAgentService for handling agent operations
- Integration with orchestrator's agent spawner/lifecycle services
- API endpoints for spawning, querying status, and killing agents
- Full command routing through federation COMMAND infrastructure
- Comprehensive test coverage (12/12 tests passing)

Architecture:
- Hub → Spoke: Spawn agents on remote instances
- Command flow: FederationController → FederationAgentService →
  CommandService → Remote Orchestrator
- Response handling: Remote orchestrator returns agent status/results
- Security: Connection validation, signature verification

Files created:
- apps/api/src/federation/types/federation-agent.types.ts
- apps/api/src/federation/federation-agent.service.ts
- apps/api/src/federation/federation-agent.service.spec.ts

Files modified:
- apps/api/src/federation/command.service.ts (agent command routing)
- apps/api/src/federation/federation.controller.ts (agent endpoints)
- apps/api/src/federation/federation.module.ts (service registration)
- apps/orchestrator/src/api/agents/agents.controller.ts (status endpoint)
- apps/orchestrator/src/api/agents/agents.module.ts (lifecycle integration)

Testing:
- 12/12 tests passing for FederationAgentService
- All command service tests passing
- TypeScript compilation successful
- Linting passed

Refs #93

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 14:37:06 -06:00
..

Activity Logging API

The Activity Logging API provides comprehensive audit trail and activity tracking functionality for the Mosaic Stack platform. It logs user actions, workspace changes, task/event modifications, and authentication events.

Overview

Activity logs are automatically created for:

  • CRUD Operations: Task, event, project, and workspace modifications
  • Authentication Events: Login, logout, password resets
  • User Actions: Task assignments, workspace member changes
  • System Events: Configuration updates, permission changes

All activity logs are workspace-scoped and support multi-tenant isolation through Row-Level Security (RLS).

Endpoints

List Activity Logs

GET /api/activity

Get a paginated list of activity logs with optional filters.

Query Parameters:

Parameter Type Required Description
workspaceId UUID Yes Workspace to filter by
userId UUID No Filter by user who performed the action
action ActivityAction No Filter by action type (CREATED, UPDATED, etc.)
entityType EntityType No Filter by entity type (TASK, EVENT, etc.)
entityId UUID No Filter by specific entity
startDate ISO 8601 No Filter activities after this date
endDate ISO 8601 No Filter activities before this date
page Number No Page number (default: 1)
limit Number No Items per page (default: 50, max: 100)

Response:

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "workspaceId": "660e8400-e29b-41d4-a716-446655440001",
      "userId": "770e8400-e29b-41d4-a716-446655440002",
      "action": "CREATED",
      "entityType": "TASK",
      "entityId": "880e8400-e29b-41d4-a716-446655440003",
      "details": {
        "title": "New Task",
        "status": "NOT_STARTED"
      },
      "ipAddress": "192.168.1.1",
      "userAgent": "Mozilla/5.0...",
      "createdAt": "2024-01-28T12:00:00Z",
      "user": {
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "John Doe",
        "email": "john@example.com"
      }
    }
  ],
  "meta": {
    "total": 150,
    "page": 1,
    "limit": 50,
    "totalPages": 3
  }
}

Example Requests:

# Get all activities in a workspace
GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001

# Get activities for a specific user
GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&userId=770e8400-e29b-41d4-a716-446655440002

# Get task creation events
GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&action=CREATED&entityType=TASK

# Get activities in date range
GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&startDate=2024-01-01&endDate=2024-01-31

# Paginate results
GET /api/activity?workspaceId=660e8400-e29b-41d4-a716-446655440001&page=2&limit=25

Get Single Activity Log

GET /api/activity/:id

Retrieve a single activity log entry by ID.

Path Parameters:

Parameter Type Required Description
id UUID Yes Activity log ID

Query Parameters:

Parameter Type Required Description
workspaceId UUID Yes Workspace ID (for multi-tenant isolation)

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "workspaceId": "660e8400-e29b-41d4-a716-446655440001",
  "userId": "770e8400-e29b-41d4-a716-446655440002",
  "action": "UPDATED",
  "entityType": "TASK",
  "entityId": "880e8400-e29b-41d4-a716-446655440003",
  "details": {
    "changes": {
      "status": "IN_PROGRESS"
    }
  },
  "ipAddress": "192.168.1.1",
  "userAgent": "Mozilla/5.0...",
  "createdAt": "2024-01-28T12:00:00Z",
  "user": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "name": "John Doe",
    "email": "john@example.com"
  }
}

Example Request:

GET /api/activity/550e8400-e29b-41d4-a716-446655440000?workspaceId=660e8400-e29b-41d4-a716-446655440001

Get Entity Audit Trail

GET /api/activity/audit/:entityType/:entityId

Retrieve complete audit trail for a specific entity (task, event, project, etc.).

Path Parameters:

Parameter Type Required Description
entityType EntityType Yes Type of entity (TASK, EVENT, PROJECT, WORKSPACE, USER)
entityId UUID Yes Entity ID

Query Parameters:

Parameter Type Required Description
workspaceId UUID Yes Workspace ID (for multi-tenant isolation)

Response:

Returns array of activity logs in chronological order (oldest first).

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "workspaceId": "660e8400-e29b-41d4-a716-446655440001",
    "userId": "770e8400-e29b-41d4-a716-446655440002",
    "action": "CREATED",
    "entityType": "TASK",
    "entityId": "880e8400-e29b-41d4-a716-446655440003",
    "details": {
      "title": "New Task"
    },
    "createdAt": "2024-01-28T10:00:00Z",
    "user": {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "name": "John Doe",
      "email": "john@example.com"
    }
  },
  {
    "id": "660e8400-e29b-41d4-a716-446655440004",
    "workspaceId": "660e8400-e29b-41d4-a716-446655440001",
    "userId": "770e8400-e29b-41d4-a716-446655440005",
    "action": "UPDATED",
    "entityType": "TASK",
    "entityId": "880e8400-e29b-41d4-a716-446655440003",
    "details": {
      "changes": {
        "status": "IN_PROGRESS"
      }
    },
    "createdAt": "2024-01-28T12:00:00Z",
    "user": {
      "id": "770e8400-e29b-41d4-a716-446655440005",
      "name": "Jane Smith",
      "email": "jane@example.com"
    }
  }
]

Example Requests:

# Get audit trail for a task
GET /api/activity/audit/TASK/880e8400-e29b-41d4-a716-446655440003?workspaceId=660e8400-e29b-41d4-a716-446655440001

# Get audit trail for a project
GET /api/activity/audit/PROJECT/990e8400-e29b-41d4-a716-446655440006?workspaceId=660e8400-e29b-41d4-a716-446655440001

# Get audit trail for a workspace
GET /api/activity/audit/WORKSPACE/660e8400-e29b-41d4-a716-446655440001?workspaceId=660e8400-e29b-41d4-a716-446655440001

Enums

ActivityAction

Actions that can be logged:

  • CREATED - Entity was created
  • UPDATED - Entity was updated
  • DELETED - Entity was deleted
  • COMPLETED - Task or project was completed
  • ASSIGNED - Task was assigned to a user
  • COMMENTED - Comment was added (future use)
  • LOGIN - User logged in
  • LOGOUT - User logged out
  • PASSWORD_RESET - Password was reset
  • EMAIL_VERIFIED - Email was verified

EntityType

Types of entities that can be tracked:

  • TASK - Task entity
  • EVENT - Calendar event
  • PROJECT - Project
  • WORKSPACE - Workspace
  • USER - User profile

Automatic Logging

The Activity Logging system includes an interceptor that automatically logs:

  • POST requestsCREATED action
  • PATCH/PUT requestsUPDATED action
  • DELETE requestsDELETED action

The interceptor extracts:

  • User information from the authenticated session
  • Workspace context from request
  • IP address and user agent from HTTP headers
  • Entity ID from route parameters or response

Manual Logging

For custom logging scenarios, use the ActivityService helper methods:

import { ActivityService } from "@/activity/activity.service";

@Injectable()
export class TaskService {
  constructor(private activityService: ActivityService) {}

  async createTask(data, userId, workspaceId) {
    const task = await this.prisma.task.create({ data });

    // Log task creation
    await this.activityService.logTaskCreated(workspaceId, userId, task.id, { title: task.title });

    return task;
  }
}

Available Helper Methods

Task Activities

  • logTaskCreated(workspaceId, userId, taskId, details?)
  • logTaskUpdated(workspaceId, userId, taskId, details?)
  • logTaskDeleted(workspaceId, userId, taskId, details?)
  • logTaskCompleted(workspaceId, userId, taskId, details?)
  • logTaskAssigned(workspaceId, userId, taskId, assigneeId)

Event Activities

  • logEventCreated(workspaceId, userId, eventId, details?)
  • logEventUpdated(workspaceId, userId, eventId, details?)
  • logEventDeleted(workspaceId, userId, eventId, details?)

Project Activities

  • logProjectCreated(workspaceId, userId, projectId, details?)
  • logProjectUpdated(workspaceId, userId, projectId, details?)
  • logProjectDeleted(workspaceId, userId, projectId, details?)

Workspace Activities

  • logWorkspaceCreated(workspaceId, userId, details?)
  • logWorkspaceUpdated(workspaceId, userId, details?)
  • logWorkspaceMemberAdded(workspaceId, userId, memberId, role)
  • logWorkspaceMemberRemoved(workspaceId, userId, memberId)

User Activities

  • logUserUpdated(workspaceId, userId, details?)

Security & Privacy

Multi-Tenant Isolation

All activity logs are scoped to workspaces using Row-Level Security (RLS). Users can only access activity logs for workspaces they belong to.

Data Retention

Activity logs are retained indefinitely by default. Consider implementing a retention policy based on:

  • Compliance requirements
  • Storage constraints
  • Business needs

Sensitive Data

Activity logs should NOT contain:

  • Passwords or authentication tokens
  • Credit card information
  • Personal health information
  • Other sensitive PII

Store only metadata needed for audit purposes. Use the details field for non-sensitive context.


Best Practices

1. Use Descriptive Details

Include enough context to understand what changed:

// Good
await activityService.logTaskUpdated(workspaceId, userId, taskId, {
  changes: {
    status: { from: "NOT_STARTED", to: "IN_PROGRESS" },
    assignee: { from: null, to: "user-456" },
  },
});

// Less useful
await activityService.logTaskUpdated(workspaceId, userId, taskId);

2. Log Business-Critical Actions

Prioritize logging actions that:

  • Change permissions or access control
  • Delete data
  • Modify billing or subscription
  • Export data
  • Change security settings

3. Query Efficiently

Use appropriate filters to reduce data transfer:

// Efficient - filters at database level
const activities = await fetch("/api/activity?workspaceId=xxx&entityType=TASK&page=1&limit=50");

// Inefficient - transfers all data then filters
const activities = await fetch("/api/activity?workspaceId=xxx");
const taskActivities = activities.filter((a) => a.entityType === "TASK");

4. Display User-Friendly Activity Feeds

Transform activity logs into human-readable messages:

function formatActivityMessage(activity: ActivityLog) {
  const { user, action, entityType, details } = activity;

  switch (action) {
    case "CREATED":
      return `${user.name} created ${entityType.toLowerCase()} "${details.title}"`;
    case "UPDATED":
      return `${user.name} updated ${entityType.toLowerCase()}`;
    case "DELETED":
      return `${user.name} deleted ${entityType.toLowerCase()}`;
    default:
      return `${user.name} performed ${action}`;
  }
}

Error Handling

Activity logging failures should NOT block the primary operation. The interceptor and service methods handle errors gracefully:

try {
  await activityService.logActivity(data);
} catch (error) {
  // Log error but don't throw
  logger.error("Failed to log activity", error);
}

If activity logging is critical for compliance, implement synchronous validation before the operation completes.


Performance Considerations

Indexing

The following indexes optimize common queries:

  • workspaceId - Filter by workspace
  • workspaceId + createdAt - Recent activities per workspace
  • entityType + entityId - Audit trail queries
  • userId - User activity history
  • action - Filter by action type

Pagination

Always use pagination for activity queries. Default limit is 50 items, maximum is 100.

Background Processing

For high-volume systems, consider:

  • Async activity logging with message queues
  • Batch inserts for multiple activities
  • Separate read replicas for reporting