# blade47/comp Wiki
## Technical docs: comp API
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/api-overview
# comp API
**Version:** inferred
**288** endpoints detected from `(inferred from code)`
## Endpoints
### General
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/docs` | Redirect to Swagger documentation ~~deprecated~~ |
| `POST` | `/cloud-security/scan/:connectionId` | |
| `POST` | `/cloud-security/trigger/:connectionId` | |
| `GET` | `/cloud-security/runs/:runId` | |
| `GET` | `/v1/admin/integrations` | List all integrations with their credential status |
| `GET` | `/v1/admin/integrations/:providerSlug` | Get details for a specific integration |
| `POST` | `/v1/admin/integrations/credentials` | Save platform credentials for an integration |
| `DELETE` | `/v1/admin/integrations/credentials/:providerSlug` | Delete platform credentials for an integration |
| `GET` | `/integrations/checks/providers/:providerSlug` | List available checks for a provider |
| `GET` | `/integrations/checks/connections/:connectionId` | List available checks for a connection |
| `POST` | `/integrations/checks/connections/:connectionId/run` | Run checks for a connection |
| `POST` | `/integrations/checks/connections/:connectionId/run/:checkId` | Run a specific check for a connection |
### Assistant Chat
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/assistant-chat/history` | Get assistant chat history |
| `PUT` | `/assistant-chat/history` | Save assistant chat history |
| `DELETE` | `/assistant-chat/history` | Clear assistant chat history |
### Attachments
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/attachments/:attachmentId/download` | Get attachment download URL |
### Browserbase
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/browserbase/org-context` | Get or create organization browser context |
| `GET` | `/browserbase/org-context` | Get organization browser context status |
| `POST` | `/browserbase/session` | Create a new browser session |
| `POST` | `/browserbase/session/close` | Close a browser session |
| `POST` | `/browserbase/navigate` | Navigate to a URL |
| `POST` | `/browserbase/check-auth` | Check authentication status |
| `POST` | `/browserbase/automations` | Create a browser automation |
| `GET` | `/browserbase/automations/task/:taskId` | Get all browser automations for a task |
| `GET` | `/browserbase/automations/:automationId` | Get a browser automation by ID |
| `PATCH` | `/browserbase/automations/:automationId` | Update a browser automation |
| `DELETE` | `/browserbase/automations/:automationId` | Delete a browser automation |
| `POST` | `/browserbase/automations/:automationId/start-live` | Start automation with live view |
| `POST` | `/browserbase/automations/:automationId/execute` | Execute automation on existing session |
| `POST` | `/browserbase/automations/:automationId/run` | Run a browser automation |
| `GET` | `/browserbase/automations/:automationId/runs` | Get run history for an automation |
| `GET` | `/browserbase/runs/:runId` | Get a specific run by ID |
### Comments
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/comments` | Get comments for an entity |
| `POST` | `/comments` | Create a new comment |
| `PUT` | `/comments/:commentId` | Update a comment |
| `DELETE` | `/comments/:commentId` | Delete a comment |
### Context
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/context` | Get all context entries |
| `GET` | `/v1/context/:id` | Get context entry by ID |
| `POST` | `/v1/context` | Create a new context entry |
| `PATCH` | `/v1/context/:id` | Update a context entry |
| `DELETE` | `/v1/context/:id` | Delete a context entry |
### Device Agent
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/device-agent/mac` | Download macOS device agent |
| `GET` | `/v1/device-agent/windows` | Download Windows device agent |
| `GET` | `/api/device-agent/status` | Retrieve device agent status |
| `GET` | `/api/download-agent` | Download a device agent installer |
| `HEAD` | `/api/download-agent` | Get metadata for a device agent installer |
| `POST` | `/api/download-agent/token` | Create a one-time download token for a device agent |
### Devices
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/devices` | Get all devices |
| `GET` | `/v1/devices/member/:memberId` | Get devices by member ID |
### Evidence Forms
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/evidence-forms` | List evidence forms |
| `GET` | `/v1/evidence-forms/statuses` | Get submission statuses for all forms |
| `GET` | `/v1/evidence-forms/my-submissions` | Get current user submissions |
| `GET` | `/v1/evidence-forms/my-submissions/pending-count` | Get pending submission count for current user |
| `GET` | `/v1/evidence-forms/:formType` | Get form definition and submissions |
| `GET` | `/v1/evidence-forms/:formType/submissions/:submissionId` | Get a single submission |
| `POST` | `/v1/evidence-forms/:formType/submissions` | Submit evidence form entry |
| `PATCH` | `/v1/evidence-forms/:formType/submissions/:submissionId/review` | Review a submission |
| `POST` | `/v1/evidence-forms/uploads` | Upload evidence form file |
| `GET` | `/v1/evidence-forms/:formType/export.csv` | Export form submissions to CSV |
| `POST` | `/api/evidence-forms/analyze` | Analyze evidence form submissions using AI. |
### Finding Templates
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/finding-template` | Get all finding templates |
| `GET` | `/v1/finding-template/:id` | Get finding template by ID |
| `POST` | `/v1/finding-template` | Create a finding template |
| `PATCH` | `/v1/finding-template/:id` | Update a finding template |
| `DELETE` | `/v1/finding-template/:id` | Delete a finding template |
### Findings
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/findings` | Get findings for a task |
| `GET` | `/v1/findings/organization` | Get all findings for organization |
| `GET` | `/v1/findings/:id` | Get finding by ID |
| `POST` | `/v1/findings` | Create a finding |
| `PATCH` | `/v1/findings/:id` | Update a finding |
| `DELETE` | `/v1/findings/:id` | Delete a finding |
| `GET` | `/v1/findings/:id/history` | Get finding history |
### Framework Editor Task Templates
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/framework-editor/task-template` | Get all task templates |
| `GET` | `/v1/framework-editor/task-template/:id` | Get task template by ID |
| `PATCH` | `/v1/framework-editor/task-template/:id` | Update a task template |
| `DELETE` | `/v1/framework-editor/task-template/:id` | Delete a task template |
### Health
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/health` | Health check |
### Connections
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/integrations/connections/providers` | List all available integration providers |
| `GET` | `/integrations/connections/providers/:slug` | Get a specific provider's details |
| `GET` | `/integrations/connections` | List connections for an organization |
| `GET` | `/integrations/connections/:id` | Get a specific connection |
| `POST` | `/integrations/connections` | Create a new connection with API key credentials |
| `POST` | `/integrations/connections/:id/test` | Test a connection's credentials |
| `POST` | `/integrations/connections/:id/pause` | Pause a connection |
| `POST` | `/integrations/connections/:id/resume` | Resume a paused connection |
| `POST` | `/integrations/connections/:id/disconnect` | Disconnect (soft delete) a connection |
| `DELETE` | `/integrations/connections/:id` | Delete a connection permanently |
| `PATCH` | `/integrations/connections/:id` | Update connection metadata (connectionName, regions, etc.) |
| `POST` | `/integrations/connections/:id/ensure-valid-credentials` | Get valid credentials for a connection, refreshing OAuth tokens if needed. |
| `PUT` | `/integrations/connections/:id/credentials` | Update credentials for a custom auth connection |
### integrations
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/integrations/oauth-apps` | List custom OAuth apps for an organization |
| `GET` | `/v1/integrations/oauth-apps/setup/:providerSlug` | Get OAuth app setup info for a provider |
| `POST` | `/v1/integrations/oauth-apps` | Save custom OAuth app credentials for an organization |
| `DELETE` | `/v1/integrations/oauth-apps/:providerSlug` | Delete custom OAuth app credentials for an organization |
| `GET` | `/v1/integrations/oauth/availability` | Check if OAuth credentials are available for a provider |
| `POST` | `/v1/integrations/oauth/start` | Start OAuth flow - returns authorization URL |
| `GET` | `/v1/integrations/oauth/callback` | OAuth callback - exchanges code for tokens |
### Integration Sync
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `integrations/sync/google-workspace/employees` | Sync employees from Google Workspace |
| `POST` | `integrations/sync/google-workspace/status` | Check if Google Workspace is connected for an organization |
| `POST` | `integrations/sync/rippling/employees` | Sync employees from Rippling |
| `POST` | `integrations/sync/rippling/status` | Check if Rippling is connected for an organization |
| `POST` | `integrations/sync/ramp/employees` | Sync employees from Ramp |
| `POST` | `integrations/sync/jumpcloud/employees` | Sync employees from JumpCloud |
| `POST` | `integrations/sync/jumpcloud/status` | Check if JumpCloud is connected for an organization |
| `POST` | `integrations/sync/ramp/status` | Check if Ramp is connected for an organization |
| `GET` | `integrations/sync/employee-sync-provider` | Get the current employee sync provider for an organization |
| `POST` | `integrations/sync/employee-sync-provider` | Set the employee sync provider for an organization |
### Task Integrations
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/integrations/tasks/template/:templateId/checks` | Get all integration checks that can auto-complete a specific task template |
| `GET` | `/integrations/tasks/:taskId/checks` | Get integration checks for a specific task (by task ID) |
| `POST` | `/integrations/tasks/:taskId/run-check` | Run a specific check for a task and store results |
| `GET` | `/integrations/tasks/:taskId/runs` | Get check run history for a task |
### Integration Variables
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/integrations/variables/providers/:providerSlug` | Get all variables required for a provider's checks |
| `GET` | `/integrations/variables/connections/:connectionId` | Get variables for a specific connection (with current values) |
| `GET` | `/integrations/variables/connections/:connectionId/options/:variableId` | Fetch dynamic options for a variable (requires active connection) |
| `POST` | `/integrations/variables/connections/:connectionId` | Save variable values for a connection |
### Webhooks
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/integrations/webhooks/:providerSlug/:connectionId` | Handle incoming webhooks for a specific provider and connection |
### Knowledge Base
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/knowledge-base/documents` | List all knowledge base documents for an organization |
| `POST` | `/knowledge-base/documents/upload` | Upload a knowledge base document |
| `POST` | `/knowledge-base/documents/:documentId/download` | Get a signed download URL for a knowledge base document |
| `POST` | `/knowledge-base/documents/:documentId/view` | Get a signed view URL for a knowledge base document |
| `POST` | `/knowledge-base/documents/:documentId/delete` | Delete a knowledge base document |
| `POST` | `/knowledge-base/documents/process` | Trigger processing of knowledge base documents |
| `POST` | `/knowledge-base/runs/:runId/token` | Create a public access token for a Trigger.dev run |
| `POST` | `/knowledge-base/manual-answers/:manualAnswerId/delete` | Delete a manual answer |
| `POST` | `/knowledge-base/manual-answers/delete-all` | Delete all manual answers for an organization |
### Org Chart
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/org-chart` | Get the organization chart |
| `PUT` | `/org-chart` | Create or update an interactive organization chart |
| `POST` | `/org-chart/upload` | Upload an image as the organization chart |
| `DELETE` | `/org-chart` | Delete the organization chart |
### Organization
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/organization` | Get organization details |
| `PATCH` | `/organization` | Update organization details |
| `POST` | `/organization/transfer-ownership` | Transfer organization ownership |
| `DELETE` | `/organization` | Delete an organization |
| `GET` | `/organization/primary-color` | Get organization primary color |
### People
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/people` | Get all people for an organization |
| `POST` | `/people` | Create a new person (member) |
| `POST` | `/people/bulk` | Bulk create people (members) |
| `GET` | `/people/:id` | Get a person (member) by ID |
| `PATCH` | `/people/:id` | Update a person (member) |
| `DELETE` | `/people/:id/host/:hostId` | Remove a host from a person (member) |
| `DELETE` | `/people/:id` | Delete a person (member) |
| `PATCH` | `/people/:id/unlink-device` | Unlink a device from a person (member) |
### Policies
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/policies` | Get all policies for an organization |
| `GET` | `/policies/download-all` | Download all published policies as a single PDF |
| `GET` | `/policies/:id` | Get a policy by ID |
| `POST` | `/policies` | Create a new policy |
| `PATCH` | `/policies/:id` | Update a policy |
| `DELETE` | `/policies/:id` | Delete a policy |
| `GET` | `/policies/:id/versions` | Get all versions for a policy |
| `GET` | `/policies/:id/versions/:versionId` | Get a policy version by ID |
| `POST` | `/policies/:id/versions` | Create a new policy version |
| `PATCH` | `/policies/:id/versions/:versionId` | Update policy version content |
| `DELETE` | `/policies/:id/versions/:versionId` | Delete a policy version |
| `POST` | `/policies/:id/versions/publish` | Publish a policy version |
| `POST` | `/policies/:id/versions/:versionId/activate` | Set a policy version as active |
| `POST` | `/policies/:id/versions/:versionId/submit-for-approval` | Submit a policy version for approval |
| `POST` | `/policies/:id/ai-chat` | Chat with AI about a policy |
### Questionnaire
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/questionnaire/parse` | Parse questionnaire content |
| `POST` | `/questionnaire/answer-single` | Generated single answer result |
| `POST` | `/questionnaire/save-answer` | Save manual or generated answer |
| `POST` | `/questionnaire/delete-answer` | Delete questionnaire answer |
| `POST` | `/questionnaire/export` | Export questionnaire by ID to specified format |
| `POST` | `/questionnaire/upload-and-parse` | Upload file, parse questions (no answers), save to DB, return questionnaireId |
| `POST` | `/questionnaire/upload-and-parse/upload` | Upload file, parse questions (no answers), save to DB, return questionnaireId |
| `POST` | `/questionnaire/parse/upload` | |
| `POST` | `/questionnaire/parse/upload/token` | |
| `POST` | `/questionnaire/answers/export` | |
| `POST` | `/questionnaire/answers/export/upload` | |
| `POST` | `/questionnaire/auto-answer` | |
### Risks
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/risks` | Get all risks for an organization |
| `GET` | `/risks/:id` | Get a risk by its ID |
| `POST` | `/risks` | Create a new risk |
| `PATCH` | `/risks/:id` | Update an existing risk |
| `DELETE` | `/risks/:id` | Delete a risk |
### SOA
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/soa/save-answer` | Save a SOA answer |
| `POST` | `/soa/auto-fill` | Auto-fill SOA document |
| `POST` | `/soa/create-document` | Create a new SOA document |
| `POST` | `/soa/ensure-setup` | Ensure SOA configuration and document exist |
| `POST` | `/soa/approve` | Approve a SOA document |
| `POST` | `/soa/decline` | Decline a SOA document |
| `POST` | `/soa/submit-for-approval` | Submit SOA document for approval |
### Task Management
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/task-management/stats` | Get task items statistics for an entity |
| `GET` | `/task-management` | Get task items for an entity |
| `POST` | `/task-management` | Create a new task item |
| `PUT` | `/task-management/:id` | Update a task item |
| `DELETE` | `/task-management/:id` | Delete a task item |
| `POST` | `/task-management/attachments` | Upload attachment to task item |
| `DELETE` | `/task-management/attachments/:attachmentId` | Delete attachment from task item |
| `GET` | `/task-management/:id/activity` | Get task item activity log |
### Task Automations
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/tasks/:taskId/automations` | Get all automations for a task |
| `GET` | `/tasks/:taskId/automations/:automationId` | Get automation details |
| `POST` | `/tasks/:taskId/automations` | Create a new automation |
| `PATCH` | `/tasks/:taskId/automations/:automationId` | Update an automation |
| `DELETE` | `/tasks/:taskId/automations/:automationId` | Delete an automation |
| `GET` | `/tasks/:taskId/automations/:automationId/versions` | Get all versions for an automation |
| `GET` | `/tasks/:taskId/automations/runs` | Get all automation runs for a task |
### Evidence Export
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/tasks/:taskId/evidence` | Get task evidence summary |
| `GET` | `/v1/tasks/:taskId/evidence/automation/:automationId/pdf` | Export automation evidence as PDF |
| `GET` | `/v1/tasks/:taskId/evidence/export` | Export task evidence as ZIP |
### Evidence Export (Auditor)
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/evidence-export/all` | Export all organization evidence as ZIP (Auditor only) |
### Internal - Tasks
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/internal/tasks/notify-status-change` | Send task status change notifications (email + in-app) without a user actor (internal) |
| `POST` | `/v1/internal/tasks/notify-automation-failures` | Send automation failure notifications (email + in-app) when one or more automations fail (internal) |
### Tasks
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/tasks` | Get all tasks |
| `PATCH` | `/v1/tasks/bulk` | Update status for multiple tasks |
| `PATCH` | `/v1/tasks/bulk/assignee` | Update assignee for multiple tasks |
| `POST` | `/v1/tasks/bulk/submit-for-review` | Bulk submit tasks for review |
| `DELETE` | `/v1/tasks/bulk` | Delete multiple tasks |
| `GET` | `/v1/tasks/:taskId` | Get task by ID |
| `GET` | `/v1/tasks/:taskId/activity` | Get task activity |
| `PATCH` | `/v1/tasks/:taskId` | Update a task |
| `POST` | `/v1/tasks/:taskId/submit-for-review` | Submit task for review |
| `POST` | `/v1/tasks/:taskId/approve` | Approve a task |
| `POST` | `/v1/tasks/:taskId/reject` | Reject a task review |
| `GET` | `/v1/tasks/:taskId/attachments` | Get task attachments |
| `POST` | `/v1/tasks/:taskId/attachments` | Upload attachment to task |
| `GET` | `/v1/tasks/:taskId/attachments/:attachmentId/download` | Get attachment download URL |
| `DELETE` | `/v1/tasks/:taskId/attachments/:attachmentId` | Delete task attachment |
### Training
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/training/send-completion-email` | Send training completion email with certificate |
| `POST` | `/training/generate-certificate` | Generate training completion certificate PDF |
### Trust Access
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/v1/trust-access/:friendlyUrl/requests` | Submit data access request |
| `GET` | `/v1/trust-access/admin/requests` | List access requests |
| `GET` | `/v1/trust-access/admin/requests/:id` | Get access request details |
| `POST` | `/v1/trust-access/admin/requests/:id/approve` | Approve access request |
| `POST` | `/v1/trust-access/admin/requests/:id/deny` | Deny access request |
| `GET` | `/v1/trust-access/admin/grants` | List access grants |
| `POST` | `/v1/trust-access/admin/grants/:id/revoke` | Revoke access grant |
| `POST` | `/v1/trust-access/admin/grants/:id/resend-access-email` | Resend access granted email |
| `GET` | `/v1/trust-access/nda/:token` | Get NDA details by token |
| `POST` | `/v1/trust-access/nda/:token/preview-nda` | Preview NDA by token |
| `POST` | `/v1/trust-access/nda/:token/sign` | Sign NDA |
| `POST` | `/v1/trust-access/admin/requests/:id/resend-nda` | Resend NDA email |
| `POST` | `/v1/trust-access/admin/requests/:id/preview-nda` | Preview NDA PDF |
| `POST` | `/v1/trust-access/:friendlyUrl/reclaim` | Reclaim access |
| `GET` | `/v1/trust-access/access/:token` | Get grant data by access token |
| `GET` | `/v1/trust-access/access/:token/policies` | List policies by access token |
| `GET` | `/v1/trust-access/access/:token/policies/download-all` | Download all policies as watermarked PDF |
| `GET` | `/v1/trust-access/access/:token/policies/download-all-zip` | Download all policies as ZIP with individual PDFs |
| `GET` | `/v1/trust-access/access/:token/compliance-resources` | List compliance resources by access token |
| `GET` | `/v1/trust-access/access/:token/documents` | List additional documents by access token |
| `GET` | `/v1/trust-access/access/:token/documents/download-all` | Download all additional documents as a ZIP by access token |
| `GET` | `/v1/trust-access/access/:token/documents/:documentId` | Download additional document by access token |
| `GET` | `/v1/trust-access/access/:token/compliance-resources/:framework` | Download compliance resource by access token |
| `GET` | `/v1/trust-access/:friendlyUrl/faqs` | Get FAQs for a trust portal |
| `GET` | `/v1/trust-access/:friendlyUrl/overview` | Get overview section for a trust portal |
| `GET` | `/v1/trust-access/:friendlyUrl/custom-links` | Get custom links for a trust portal |
| `GET` | `/v1/trust-access/:friendlyUrl/favicon` | Get favicon URL for a trust portal |
| `GET` | `/v1/trust-access/:friendlyUrl/vendors` | Get vendors/subprocessors for a trust portal |
### Trust Portal
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/trust-portal/domain/status` | Get domain verification status |
| `POST` | `/trust-portal/compliance-resources/upload` | Upload or replace a compliance certificate (PDF only) |
| `POST` | `/trust-portal/compliance-resources/signed-url` | Generate a temporary signed URL for a compliance certificate |
| `POST` | `/trust-portal/compliance-resources/list` | List uploaded compliance certificates for the organization |
| `POST` | `/trust-portal/documents/upload` | Upload an additional trust portal document |
| `POST` | `/trust-portal/documents/list` | List additional trust portal documents for the organization |
| `POST` | `/trust-portal/documents/:documentId/download` | Generate a temporary signed URL for a trust portal document |
| `POST` | `/trust-portal/documents/:documentId/delete` | Delete (deactivate) a trust portal document |
| `POST` | `/trust-portal/overview` | Update trust portal overview section |
| `GET` | `/trust-portal/overview` | Get trust portal overview |
| `POST` | `/trust-portal/custom-links` | Create a custom link for trust portal |
| `POST` | `/trust-portal/custom-links/:linkId` | Update a custom link |
| `POST` | `/trust-portal/custom-links/:linkId/delete` | Delete a custom link |
| `POST` | `/trust-portal/custom-links/reorder` | Reorder custom links |
| `GET` | `/trust-portal/custom-links` | List custom links for trust portal |
| `POST` | `/trust-portal/vendors/:vendorId/trust-settings` | Update vendor trust portal settings |
| `GET` | `/trust-portal/vendors` | List vendors configured for trust portal |
### Internal - Vendors
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/internal/vendors/risk-assessment/trigger-batch` | Trigger vendor risk assessment tasks for a batch of vendors (internal) |
| `POST` | `/internal/vendors/risk-assessment/trigger-single` | Trigger vendor risk assessment for a single vendor and return run info (internal) |
### Vendors
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/vendors` | Get all vendors for an organization |
| `GET` | `/vendors/:id` | Get a vendor by ID |
| `POST` | `/vendors` | Create a new vendor |
| `PATCH` | `/vendors/:id` | Update an existing vendor |
| `DELETE` | `/vendors/:id` | Delete a vendor |
### Auth
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/auth/invitation` | Handle invitation link redirection |
| `GET` | `/api/auth/test-db` | Test database connection (E2E only) |
| `POST` | `/api/auth/test-login` | Perform a test login for E2E (Internal Only) |
### AI
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/chat` | Engage in AI chat |
### Cloud Tests
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/cloud-tests/findings` | Retrieve cloud test findings for an organization. |
| `GET` | `/api/cloud-tests/providers` | Retrieve active cloud providers for an organization. |
### File Management
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/get-image-url` | Get a signed URL for an image stored in S3. |
### QA Internal
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/qa/approve-org` | Approve an organization by setting hasAccess to true (QA internal). |
| `POST` | `/api/qa/delete-user` | Delete a user and all associated data (QA internal). |
### Retool Internal
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/retool/reset-org` | Resets an organization's data |
### Secrets
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/secrets/:id` | Get a specific secret |
| `PUT` | `/api/secrets/:id` | Update a secret |
| `DELETE` | `/api/secrets/:id` | Delete a secret |
| `GET` | `/api/secrets` | List all secrets for the organization |
| `POST` | `/api/secrets` | Create a new secret |
### User Data
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/user-frameworks` | Retrieve user frameworks |
### Fleet Policies
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/confirm-fleet-policy` | Confirms a fleet policy and uploads attachments. |
---
## Technical docs: Project Overview
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-1/project-overview
Relevant source files
The following files were used as context for generating this wiki page:
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [apps/api/package.json](https://github.com/blade47/comp/blob/main/apps/api/package.json)
- [apps/api/src/main.ts](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts)
Comp AI is an open-source compliance platform designed to automate compliance processes for various regulatory frameworks. It aims to simplify evidence collection, policy management, and control implementation, enabling organizations to achieve compliance with standards like SOC 2, ISO 27001, HIPAA, and GDPR in a streamlined manner. The platform emphasizes user control over data and infrastructure while leveraging AI for efficiency.
The project is structured as a monorepo, encompassing multiple applications and shared packages, with a robust API service built on NestJS that handles core functionalities, integrations, and data management.
## Core Capabilities & Compliance Frameworks
Comp AI provides automation for key compliance activities, including:
* **Evidence Collection:** Streamlining the gathering of necessary documentation and data.
* **Policy Management:** Assisting in the creation, maintenance, and enforcement of organizational policies.
* **Control Implementation:** Guiding and automating the setup of security and operational controls.
The platform is designed to help organizations achieve compliance with major frameworks such as:
* SOC 2
* ISO 27001
* HIPAA
* GDPR
Sources: [README.md:19-23](https://github.com/blade47/comp/blob/main/README.md#L19-L23)
## High-Level Architecture
Comp AI is structured as a monorepo, containing multiple applications (`apps`) and shared packages (`packages`). This architecture facilitates code sharing, consistent development practices, and independent versioning of components.
Sources: [README.md:95-97](https://github.com/blade47/comp/blob/main/README.md#L95-L97), [README.md:275-280](https://github.com/blade47/comp/blob/main/README.md#L275-L280), [apps/api/package.json:2-3](https://github.com/blade47/comp/blob/main/apps/api/package.json#L2-L3)
### Technology Stack
The project leverages a modern web development stack, including:
* **Next.js:** For building web applications.
* **Tailwind CSS:** For styling.
* **Vercel:** For deployment and hosting.
* **NestJS:** A progressive Node.js framework for building efficient, reliable, and scalable server-side applications (used in `@comp/api`).
* **Prisma:** A next-generation ORM for database access.
* **PostgreSQL:** The primary relational database.
* **Trigger.dev:** For background jobs and workflow automation.
* **Upstash (Redis/Vector):** For key-value store and vector database functionalities.
* **AWS SDK:** For interacting with AWS services (e.g., S3, SecurityHub, STS).
* **AI SDKs:** Integrations with Anthropic, Groq, and OpenAI for AI capabilities.
* **Resend:** For email delivery.
Sources: [README.md:32-37](https://github.com/blade47/comp/blob/main/README.md#L32-L37), [apps/api/package.json:6-48](https://github.com/blade47/comp/blob/main/apps/api/package.json#L6-L48)
## API Service (`@comp/api`)
The `@comp/api` service is the backend component of Comp AI, built using the NestJS framework. It handles API requests, business logic, data persistence, and integrations with various external services.
### Key Dependencies
The API service relies on a wide array of libraries and frameworks to provide its functionality:
| Category | Key Dependencies
---
## Technical docs: GET Redirect to Swagger documentation
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/endpoints/redirecttoswagger
## Responses
## Try It
---
## Technical docs: Getting Started
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-1/getting-started
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/package.json](https://github.com/blade47/comp/blob/main/apps/api/package.json)
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [apps/api/docker-compose.yml](https://github.com/blade47/comp/blob/main/apps/api/docker-compose.yml)
This page provides a comprehensive guide to setting up and running the Comp AI platform locally for development. Comp AI is an open-source compliance platform designed to automate evidence collection, policy management, and control implementation for frameworks like SOC 2, ISO 27001, HIPAA, and GDPR.
The instructions cover prerequisites, environment configuration, database setup, and how to start the development servers, enabling you to get a local instance of Comp AI up and running quickly.
## Prerequisites
Before you begin, ensure your system meets the following software requirements:
* **Node.js**: Version `20.x` or higher.
* **Bun**: Version `1.1.36` or higher.
* **PostgreSQL**: Version `15.x` or higher.
Bun is used as the package manager and runtime for various scripts within the project. PostgreSQL is the primary database for the application.
Sources: [README.md:49-52](https://github.com/blade47/comp/blob/main/README.md#L49-L52)
## Development Setup
Follow these steps to set up the Comp AI project locally, including all necessary integrations.
### Overall Setup Flow
The following diagram illustrates the high-level steps required to get the Comp AI project running locally:
Sources: [README.md:61-82](https://github.com/blade47/comp/blob/main/README.md#L61-L82)
### 1. Clone the Repository
Begin by cloning the Comp AI repository from GitHub:
```sh
git clone https://github.com/trycompai/comp.git
```
Navigate into the project directory:
```sh
cd comp
```
Sources: [README.md:64-69](https://github.com/blade47/comp/blob/main/README.md#L64-L69)
### 2. Install Dependencies
Install all project dependencies using Bun:
```sh
bun install
```
Sources: [README.md:71-73](https://github.com/blade47/comp/blob/main/README.md#L71-L73)
### 3. Environment Variables
Create the necessary `.env` files by copying from their respective `.env.example` templates. These files will hold your credentials and configuration.
```sh
cp apps/app/.env.example apps/app/.env
cp apps/portal/.env.example apps/portal/.env
cp packages/db/.env.example packages/db/.env
```
```cmd
copy apps\app\.env.example apps\app\.env
copy apps\portal\.env.example apps\portal\.env
copy packages\db\.env.example packages\db\.env
```
```powershell
Copy-Item apps\app\.env.example -Destination apps\app\.env
Copy-Item apps\portal\.env.example -Destination apps\portal\.env
Copy-Item packages\db\.env.example -Destination apps\db\.env
```
#### Required Environment Variables for `apps/app/.env`
Ensure the following variables are defined in `comp/apps/app/.env`:
| Variable | Description
---
## Technical docs: GET Get assistant chat history
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/assistant-chat/assistantchatcontroller-gethistory
## Parameters
## Responses
## Try It
---
## Technical docs: PUT Save assistant chat history
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/assistant-chat/assistantchatcontroller-savehistory
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: Configuration
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-1/configuration
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/config/better-auth.config.ts](https://github.com/blade47/comp/blob/main/apps/api/src/config/better-auth.config.ts)
- [apps/api/src/config/aws.config.ts](https://github.com/blade47/comp/blob/main/apps/api/src/config/aws.config.ts)
- [apps/api/src/config/load-env.ts](https://github.com/blade47/comp/blob/main/apps/api/src/config/load-env.ts)
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
This page details the configuration management within the project, focusing on how environment variables are loaded and how application-specific settings for services like AWS and "Better Auth" are defined and validated. The system leverages `dotenv` for loading environment variables and `zod` for robust schema validation, ensuring that all necessary configurations are present and correctly formatted at application startup.
The configuration architecture aims to provide a clear, centralized, and validated approach to managing application settings, reducing runtime errors caused by missing or malformed environment variables.
## Environment Variable Loading
The project utilizes a dedicated module, `load-env.ts`, to manage the loading of environment variables from `.env` files. This module ensures that environment variables are loaded early in the application lifecycle, supporting various deployment and development environments. It searches for `.env` files in predefined locations to maximize flexibility.
The `loadEnv` function attempts to load the `.env` file from several common paths:
1. Relative to the compiled output (`dist/src/.env`).
2. Relative to the source directory (`src/.env`).
3. In the current working directory (`process.cwd()/.env`).
Once an `.env` file is found, its variables are loaded and can optionally override existing environment variables. The `ensureEnvLoaded` function provides a mechanism to guarantee that environment variables have been processed, preventing redundant loading.
```typescript
// apps/api/src/config/load-env.ts
const searchPaths = [
// When compiled to dist/src (Nest build output)
path.join(__dirname, '..', '..', '.env'),
// When running with ts-node directly from src
path.join(__dirname, '..', '.env'),
// Fallback to current working directory
path.join(process.cwd(), '.env'),
];
function loadEnv(): void {
for (const envPath of searchPaths) {
if (existsSync(envPath)) {
config({ path: envPath, override: true });
envLoaded = true;
return;
}
}
envLoaded = true;
}
```
### Environment Loading Flow
Sources: [apps/api/src/config/load-env.ts:1-25](https://github.com/blade47/comp/blob/main/apps/api/src/config/load-env.ts#L1-L25)
## Application-Specific Configurations
The project uses `@nestjs/config`'s `registerAs` function in conjunction with `zod` schemas to define and validate specific configuration objects. This approach provides type safety and ensures that critical application settings are correctly structured and present.
### AWS Configuration
The AWS configuration (`aws.config.ts`) defines the necessary parameters for interacting with Amazon Web Services. It includes settings for region, access keys, and S3 bucket details.
The AWS configuration is validated at application startup using a `zod` schema. If any required environment variables are missing or invalid, the application will throw an error and fail to start, preventing potential runtime issues.
#### AWS Configuration Options
| Option | Environment Variable | Description | Default Value | Required |
| :-------------- | :---------------------------- | :---------------------------------------------- | :------------ | :------- |
| `region` | `APP_AWS_REGION` | AWS region to connect to. | `us-east-1` | No |
| `accessKeyId` | `APP_AWS_ACCESS_KEY_ID` | AWS access key ID. | | Yes |
| `secretAccessKey` | `APP_AWS_SECRET_ACCESS_KEY` | AWS secret access key. | | Yes |
| `bucketName` | `APP_AWS_BUCKET_NAME` | Name of the S3 bucket to use. | | Yes |
| `endpoint` | `APP_AWS_ENDPOINT` | Custom endpoint for AWS services (e.g., for localstack). | | No |
Sources: [apps/api/src/config/aws.config.ts:1-33](https://github.com/blade47/comp/blob/main/apps/api/src/config/aws.config.ts#L1-L33)
#### AWS Configuration Schema
### Better Auth Configuration
The "Better Auth" configuration (`better-auth.config.ts`) specifies the URL for an external authentication service.
The `BETTER_AUTH_URL` environment variable is **required**. If it is not provided or is not a valid URL, the application will fail to start.
#### Better Auth Configuration Options
| Option | Environment Variable | Description | Required |
| :----- | :------------------- | :---------------------------------------- | :------- |
| `url` | `BETTER_AUTH_URL` | The URL of the "Better Auth" service. | Yes |
Sources: [apps/api/src/config/better-auth.config.ts:1-27](https://github.com/blade47/comp/blob/main/apps/api/src/config/better-auth.config.ts#L1-L27)
#### Better Auth Configuration Schema
### Configuration Validation Flow
## General Environment Variable Setup
For local development, environment variables are typically managed through `.env` files. The project provides example `.env.example` files that should be copied and filled with actual credentials.
### Create .env files
Copy the example environment files for each application and package.
```sh
cp apps/app/.env.example apps/app/.env
cp apps/portal/.env.example apps/portal/.env
cp packages/db/.env.example packages/db/.env
```
```cmd
copy apps\app\.env.example apps\app\.env
copy apps\portal\.env.example apps\portal\.env
copy packages\db\.env.example packages\db\.env
```
```powershell
Copy-Item apps\app\.env.example -Destination apps\app\.env
Copy-Item apps\portal\.env.example -Destination apps\portal\.env
Copy-Item packages\db\.env.example -Destination packages\db\.env
```
### Fill out required variables
Populate the newly created `.env` files with your specific credentials and settings.
Ensure all required variables are present. The `README.md` specifically highlights the need for `NEXT_PUBLIC_PORTAL_URL` and `REVALIDATION_SECRET` in `comp/apps/app/.env` which might be missing from `.env.example`.
#### Common Environment Variables
| Variable | Description
---
## Technical docs: Troubleshooting
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-1/troubleshooting
Relevant source files
The following files were used as context for generating this wiki page:
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [apps/api/src/common/filters/cors-exception.filter.ts](https://github.com/blade47/comp/blob/main/apps/api/src/common/filters/cors-exception.filter.ts)
- [apps/api/src/common/pipes/zod-validation.pipe.ts](https://github.com/blade47/comp/blob/main/apps/api/src/common/pipes/zod-validation.pipe.ts)
Troubleshooting in the Comp AI platform involves diagnosing and resolving issues across various layers, from local development environment setup to API request handling. This guide provides insights into common problems and their solutions, focusing on environment configuration, database management, and API error responses. Understanding these areas is crucial for maintaining a smooth development workflow and ensuring the application functions as expected.
## Local Development Environment Troubleshooting
Setting up the local development environment for Comp AI requires specific prerequisites and careful configuration of environment variables and the database. Issues in these areas are common and can prevent the application from starting or functioning correctly.
### Prerequisites and Initial Setup
Ensure your system meets the minimum software requirements before attempting to run Comp AI locally.
### Verify Prerequisites
Confirm that the following software is installed with the specified versions:
- **Node.js**: Version `>=20.x`
- **Bun**: Version `>=1.1.36`
- **Postgres**: Version `>=15.x`
If any of these are not met, update or install them accordingly.
### Clone Repository and Install Dependencies
1. Clone the Comp AI repository:
```sh
git clone https://github.com/trycompai/comp.git
```
2. Navigate to the project directory:
```sh
cd comp
```
3. Install dependencies using Bun:
```sh
bun install
```
Sources: [README.md:65-71](https://github.com/blade47/comp/blob/main/README.md#L65-L71), [README.md:83-91](https://github.com/blade47/comp/blob/main/README.md#L83-L91)
### Environment Variable Configuration
Incorrect or missing environment variables are a frequent source of issues. Comp AI requires several `.env` files to be correctly populated.
#### Required `.env` Files
Create the following `.env` files by copying from their respective `.env.example` counterparts:
- `comp/apps/app/.env`
- `comp/apps/portal/.env`
- `comp/packages/db/.env`
```sh
cp apps/app/.env.example apps/app/.env
cp apps/portal/.env.example apps/portal/.env
cp packages/db/.env.example packages/db/.env
```
```cmd
copy apps\app\.env.example apps\app\.env
copy apps\portal\.env.example apps\portal\.env
copy packages\db\.env.example apps\db\.env
```
```powershell
Copy-Item apps\app\.env.example -Destination apps\app\.env
Copy-Item apps\portal\.env.example -Destination apps\portal\.env
Copy-Item packages\db\.env.example -Destination apps\db\.env
```
#### Critical Environment Variables
Ensure `comp/apps/app/.env` contains at least the following variables:
| Variable | Description
Sources: [README.md:94-118](https://github.com/blade47/comp/blob/main/README.md#L94-L118), [README.md:120-131](https://github.com/blade47/comp/blob/main/README.md#L120-L131), [README.md:133-140](https://github.com/blade47/comp/blob/main/README.md#L133-L140)
#### Hard-coding Environment Variables
Some environment variables might not load correctly from `.env` files, especially in certain development setups or environments. In such cases, the `README.md` explicitly recommends hard-coding these values directly into the relevant source files. This should be considered a temporary troubleshooting step and ideally resolved by fixing the environment variable loading mechanism.
Specific locations for hard-coding include:
- **Google OAuth credentials**: `GOOGLE_ID`, `GOOGLE_SECRET` in `comp/apps/portal/src/app/lib/auth.ts`.
- **Redis (Upstash) credentials**: Redis URL and TOKEN in `comp/packages/kv/src/index.ts`.
- **Trigger.dev Project ID**: `project` property in `comp/apps/app/trigger.config.ts`.
Sources: [README.md:141-143](https://github.com/blade47/comp/blob/main/README.md#L141-L143), [README.md:156-158](https://github.com/blade47/comp/blob/main/README.md#L156-L158), [README.md:167-169](https://github.com/blade47/comp/blob/main/README.md#L167-L169), [README.md:176-178](https://github.com/blade47/comp/blob/main/README.md#L176-L178)
### Database Setup and Common Issues
The PostgreSQL database is a core component. Proper setup and migration are essential.
### Start Database Container
Navigate to `packages/db` and start the Docker container for PostgreSQL:
```sh
cd packages/db
bun docker:up
```
### Verify Credentials
The default credentials are:
- **Database name**: `comp`
- **Username**: `postgres`
- **Password**: `postgres`
If you need to change the password, connect to the database and run:
```sql
ALTER USER postgres WITH PASSWORD 'new_password';
```
### Resolve "No function matches..." Error
If you encounter an error message like `HINT: No function matches the given name and argument types...`, it indicates a missing database function.
To fix this, run the following command, replacing `` with your PostgreSQL password:
```sh
psql "postgresql://postgres:@localhost:5432/comp" -f ./packages/db/prisma/functionDefinition.sql
```
Expected output upon success is `CREATE FUNCTION`. Ensure you use the correct port and database name for your setup.
### Generate Prisma Client and Apply Schema
After the database is running and any function definition issues are resolved:
1. Generate the Prisma client:
```sh
bun db:generate
```
2. Push the schema to the database:
```sh
bun db:push
```
3. (Optional) Seed the database with initial data:
```sh
bun db:seed
```
#### Useful Database Commands
For further database management and troubleshooting:
- `bun db:studio`: Open Prisma Studio to view/edit data.
- `bun db:migrate`: Run database migrations.
- `bun docker:down`: Stop the database container.
- `bun docker:clean`: Remove the database container and volume.
Sources: [README.md:181-183](https://github.com/blade47/comp/blob/main/README.md#L181-L183), [README.md:185-188](https://github.com/blade47/comp/blob/main/README.md#L185-L188), [README.md:190-192](https://github.com/blade47/comp/blob/main/README.md#L190-L192), [README.md:194-203](https://github.com/blade47/comp/blob/main/README.md#L194-L203), [README.md:205-212](https://github.com/blade47/comp/blob/main/README.md#L205-L212)
## API Error Handling
The Comp AI API includes specific mechanisms to handle common errors such as Cross-Origin Resource Sharing (CORS) issues and request validation failures.
### CORS Exception Handling
The `CorsExceptionFilter` is a global exception filter designed to manage CORS headers on error responses. This is critical for allowing client applications (like the frontend) to communicate with the API, especially during development or when deployed across different domains.
#### How it Works
1. **Catches `HttpException`**: The filter intercepts any `HttpException` thrown by the API.
2. **Extracts Origin**: It reads the `Origin` header from the incoming request.
3. **Checks Allowed Origins**: It compares the request origin against a predefined list of allowed origins. This list includes:
* `http://localhost:3000`
* `http://localhost:3001`
* `http://127.0.0.1:3000`
* `https://app.trycomp.ai`
* `https://trycomp.ai`
* The value of `process.env.APP_URL`
4. **Development Mode Flexibility**: If `NODE_ENV` is not `production`, it also allows origins containing `localhost`, `127.0.0.1`, or `ngrok`.
5. **Sets CORS Headers**: If the origin is allowed, it sets `Access-Control-Allow-Origin`, `Access-Control-Allow-Credentials`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers` on the response.
6. **Sends Error Response**: Finally, it sends the original `HttpException`'s response body with the appropriate HTTP status code.
If you encounter CORS errors (e.g., "Access-Control-Allow-Origin header is not present") when making requests to the API, verify that your client's origin is included in the `allowedOrigins` list within the `CorsExceptionFilter` or that you are running in a development environment that permits your origin.
```typescript
// apps/api/src/common/filters/cors-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import type { Response, Request } from 'express';
@Catch(HttpException)
export class CorsExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus();
const origin = request.headers.origin;
if (origin) {
const isDevelopment = process.env.NODE_ENV !== 'production';
const allowedOrigins = [
'http://localhost:3000',
'http://localhost:3001',
'http://127.0.0.1:3000',
'https://app.trycomp.ai',
'https://trycomp.ai',
process.env.APP_URL,
].filter(Boolean) as string[];
const isAllowed =
allowedOrigins.includes(origin) ||
(isDevelopment &&
(origin.includes('localhost') ||
origin.includes('127.0.0.1') ||
origin.includes('ngrok')));
if (isAllowed) {
response.setHeader('Access-Control-Allow-Origin', origin);
response.setHeader('Access-Control-Allow-Credentials', 'true');
response.setHeader(
'Access-Control-Allow-Methods',
'GET,POST,PUT,DELETE,PATCH,OPTIONS',
);
response.setHeader(
'Access-Control-Allow-Headers',
'Content-Type,Authorization,X-API-Key,X-Organization-Id',
);
}
}
response.status(status).json(exception.getResponse());
}
}
```
Sources: [apps/api/src/common/filters/cors-exception.filter.ts:1-42](https://github.com/blade47/comp/blob/main/apps/api/src/common/filters/cors-exception.filter.ts#L1-L42)
### Zod Validation Pipe
The `ZodValidationPipe` is a custom NestJS pipe used for validating incoming request data (e.g., body, query parameters) against a Zod schema. This ensures that data conforms to expected types and structures before being processed by controller handlers.
#### How it Works
1. **Constructor**: It takes a `ZodSchema` instance during its creation.
2. **`transform` Method**: When applied to a route handler parameter, this method attempts to parse the incoming `value` using the provided `schema`.
3. **Validation Failure**: If `schema.parse(value)` throws an error (meaning the data does not match the schema), the pipe catches it and throws a `BadRequestException` with the message 'Validation failed'.
Using `ZodValidationPipe` helps enforce data integrity at the API boundary, preventing malformed requests from reaching business logic. When troubleshooting, if you receive a `400 Bad Request` with the message 'Validation failed', it indicates that the request payload does not conform to the expected Zod schema for that endpoint.
```typescript
// apps/api/src/common/pipes/zod-validation.pipe.ts
import {
ArgumentMetadata,
BadRequestException,
PipeTransform,
} from '@nestjs/common';
import { ZodSchema } from 'zod';
export class ZodValidationPipe implements PipeTransform {
constructor(private schema: ZodSchema) {}
transform(value: unknown, metadata: ArgumentMetadata) {
try {
const parsedValue = this.schema.parse(value);
return parsedValue;
} catch (error) {
throw new BadRequestException('Validation failed');
}
}
}
```
Sources: [apps/api/src/common/pipes/zod-validation.pipe.ts:1-15](https://github.com/blade47/comp/blob/main/apps/api/src/common/pipes/zod-validation.pipe.ts#L1-L15)
### API Request Flow with Error Handling
The following sequence diagram illustrates how an API request is processed, including the points where CORS and Zod validation errors might be handled.
Sources: [apps/api/src/common/filters/cors-exception.filter.ts](https://github.com/blade47/comp/blob/main/apps/api/src/common/filters/cors-exception.filter.ts), [apps/api/src/common/pipes/zod-validation.pipe.ts](https://github.com/blade47/comp/blob/main/apps/api/src/common/pipes/zod-validation.pipe.ts)
---
## Technical docs: DELETE Clear assistant chat history
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/assistant-chat/assistantchatcontroller-clearhistory
## Parameters
## Responses
## Try It
---
## Technical docs: API Architecture
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-2/api-architecture
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/app.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts)
- [apps/api/src/main.ts](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts)
- [apps/api/src/app.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/app.controller.ts)
- [apps/api/src/app.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/app.service.ts)
The API architecture is built upon the NestJS framework, providing a structured and modular approach to developing server-side applications. It serves as the central backend for various functionalities, integrating numerous domain-specific modules, handling requests, and providing a robust and scalable foundation.
The core of the API is defined by its entry point (`main.ts`), which initializes the NestJS application, configures global middleware, security settings, and API documentation. The `AppModule` acts as the root module, orchestrating the integration of various feature modules, configuration settings, and global guards to manage the application's overall behavior.
## Application Entry Point and Bootstrap Process
The `main.ts` file is the primary entry point for the API application. It is responsible for bootstrapping the NestJS application, applying global configurations, setting up middleware, and starting the HTTP server. Before the NestJS application starts, environment variables are loaded to ensure proper configuration.
The `bootstrap` function performs several critical steps:
1. **Application Creation:** Initializes the NestJS application using `AppModule`.
2. **CORS Configuration:** Enables Cross-Origin Resource Sharing for all origins, with credentials and exposed headers.
3. **Security Headers:** Applies `helmet` middleware to set various HTTP security headers, including Content Security Policy (CSP).
4. **Body Parsing:** Configures `express.json` and `express.urlencoded` to handle request body parsing, with a generous limit of 150MB to accommodate large payloads like base64-encoded attachments.
5. **Global Pipes:** Registers a `ValidationPipe` globally to enforce data validation, whitelisting, and automatic transformation of incoming data.
6. **API Versioning:** Enables URI-based API versioning, defaulting to version `1`.
7. **Swagger/OpenAPI Documentation:** Generates and serves interactive API documentation at `/api/docs`. In development environments, it also writes the OpenAPI specification to a JSON file.
8. **Server Start:** Listens for incoming HTTP requests on a configured port (defaulting to `3333`).
9. **Graceful Shutdown:** Implements handlers for `SIGTERM` and `SIGINT` signals to ensure the application closes gracefully.
Sources: [apps/api/src/main.ts:1-93](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L1-L93)
## Core Module Structure
The `AppModule` (`app.module.ts`) serves as the root module of the NestJS application. It aggregates all other feature modules, global configurations, and providers, defining the overall structure and dependencies of the API.
### Key Components of `AppModule`
* **`imports`**: This array lists all the modules that `AppModule` depends on. It includes:
* `ConfigModule`: For loading and managing application configurations.
* `ThrottlerModule`: Implements rate limiting to protect against abuse.
* A comprehensive list of feature modules, each encapsulating specific domain logic (e.g., `AuthModule`, `OrganizationModule`, `DevicesModule`, `TasksModule`, `SOAModule`, `CloudSecurityModule`, etc.).
* **`controllers`**: Declares `AppController`, which handles basic routes like the root redirect to API documentation.
* **`providers`**: Registers services and other injectable components, including `AppService` and a global `ThrottlerGuard` for rate limiting.
Sources: [apps/api/src/app.module.ts:1-85](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts#L1-L85)
### Configuration Management
The `ConfigModule` is configured globally within `AppModule` to load environment-specific configurations. It uses `awsConfig` and `betterAuthConfig` to provide structured configuration objects throughout the application. The `.env` file is loaded manually in `main.ts` before NestJS initializes, ensuring that environment variables are available for configuration loading.
The `ConfigModule` is configured with `isGlobal: true`, making the configuration available across all modules without needing to re-import it. The `load` property specifies configuration factories (`awsConfig`, `betterAuthConfig`) that provide typed configuration objects.
Sources: [apps/api/src/app.module.ts:10-21](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts#L10-L21), [apps/api/src/main.ts:1](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L1)
## Request Handling Flow
When a request arrives at the API, it passes through a series of middleware, guards, pipes, and controllers before reaching the business logic in services.
Sources: [apps/api/src/main.ts:1-93](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L1-L93), [apps/api/src/app.module.ts:22-30](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts#L22-L30), [apps/api/src/app.controller.ts:1-12](https://github.com/blade47/comp/blob/main/apps/api/src/app.controller.ts#L1-L12), [apps/api/src/app.service.ts:1-7](https://github.com/blade47/comp/blob/main/apps/api/src/app.service.ts#L1-L7)
### Global Middleware and Guards
The API employs several global mechanisms to ensure security, performance, and data integrity:
* **CORS:** Enabled for all origins (`origin: true`) to allow client applications to interact with the API.
* **Helmet:** Configured to add various HTTP security headers, including a Content Security Policy (CSP) that restricts sources for scripts, styles, images, and connections.
* **Body Parsers:** `express.json` and `express.urlencoded` are used to parse incoming request bodies, with a `150mb` limit to support large data transfers.
* **ThrottlerGuard:** A global guard provided by `ThrottlerModule` that limits requests to 100 per minute per IP address, preventing abuse and ensuring service availability.
Sources: [apps/api/src/main.ts:18-47](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L18-L47), [apps/api/src/app.module.ts:22-30](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts#L22-L30)
### Validation Pipe
A global `ValidationPipe` is applied to all incoming requests. This pipe automatically validates incoming data against defined DTOs (Data Transfer Objects), ensuring that requests conform to expected schemas.
The `ValidationPipe` is configured with:
- `whitelist: true`: Removes properties that are not defined in the DTO.
- `forbidNonWhitelisted: true`: Throws an error if non-whitelisted properties are present.
- `transform: true`: Automatically transforms incoming payload objects to DTO instances.
- `transformOptions: { enableImplicitConversion: true }`: Enables implicit type conversion for primitive types.
Sources: [apps/api/src/main.ts:50-59](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L50-L59)
## API Versioning
The API supports URI-based versioning, allowing different versions of endpoints to coexist. The default version is `1`.
```typescript
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: '1',
});
```
This means endpoints can be accessed like `/v1/resource` or `/v1/another-resource`.
Sources: [apps/api/src/main.ts:61-64](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L61-L64)
## API Documentation
The API automatically generates and serves interactive documentation using Swagger (OpenAPI). This documentation is accessible at `/api/docs`.
### Documentation Features
* **Title and Description:** Provides a clear overview of the API.
* **Version:** Specifies the API version (`1.0`).
* **Servers:** Lists available API servers, including local development and production environments.
* **Authentication:** Supports API key authentication (`X-API-Key` header).
* **Persistence:** Swagger UI is configured to persist authorization tokens between page refreshes.
* **Development Output:** In non-production environments, the OpenAPI specification is written to `packages/docs/openapi.json` for external tooling or reference.
Sources: [apps/api/src/main.ts:68-93](https://github.com/blade47/comp/blob/main/apps/api/src/main.ts#L68-L93)
### Root Endpoint Redirect
The root path (`/`) of the API is configured to automatically redirect clients to the Swagger documentation page (`/api/docs`). This redirect is excluded from the generated Swagger documentation itself.
```typescript
@Controller({ version: VERSION_NEUTRAL })
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
@Redirect('/api/docs', 302)
@ApiExcludeEndpoint()
redirectToSwagger(): void {
// This method redirects to Swagger documentation
}
}
```
Sources: [apps/api/src/app.controller.ts:1-12](https://github.com/blade47/comp/blob/main/apps/api/src/app.controller.ts#L1-L12)
---
## Technical docs: GET Get attachment download URL
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/attachments/attachmentscontroller-getattachmentdownloadurl
## Parameters
## Responses
## Try It
---
## Technical docs: POST Get or create organization browser context
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-getorcreateorgcontext
## Parameters
## Responses
## Try It
---
## Technical docs: Module Map
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-2/module-map
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/app.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts)
- [apps/api/src/health/health.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/health/health.module.ts)
- [apps/api/src/integration-platform/integration-platform.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/integration-platform.module.ts)
- [apps/api/src/questionnaire/questionnaire.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.module.ts)
This document provides an overview of the module structure within the `apps/api` directory, focusing on how different feature modules are organized and integrated into the main application. It details the `AppModule` as the root, along with specific examples like `HealthModule`, `IntegrationPlatformModule`, and `QuestionnaireModule`, illustrating their responsibilities and dependencies.
The architecture leverages NestJS modules to encapsulate related functionalities, promoting modularity and maintainability. Each module typically groups controllers, services, and repositories relevant to a specific domain, which are then imported into the `AppModule` to form the complete API application.
## Core Application Module (`AppModule`)
The `AppModule` serves as the root module for the NestJS API application. It aggregates all other feature modules, global configurations, and core services required for the application to function. This module is responsible for setting up global configurations like rate limiting and loading environment-specific settings.
### Global Configuration and Guards
The `AppModule` initializes several global configurations and guards:
* **`ConfigModule`**: Configures the application to load environment variables and specific configuration objects (`awsConfig`, `betterAuthConfig`). It is set as global, making configuration available throughout the application.
* **`ThrottlerModule`**: Implements rate limiting to protect the API from abuse. It is configured to allow 100 requests per minute (`ttl: 60000`, `limit: 100`) per IP address.
* **`APP_GUARD`**: The `ThrottlerGuard` is provided globally using `APP_GUARD`, ensuring that rate limiting is applied to all routes by default.
Sources: [apps/api/src/app.module.ts:1-60](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts#L1-L60)
### Feature Module Imports
The `AppModule` imports a wide array of feature modules, each responsible for a specific domain or functionality within the API. This modular approach helps in organizing the codebase and managing dependencies.
The `AppModule` imports 29 distinct feature modules, demonstrating a comprehensive microservices-like architecture within a single NestJS application.
The following diagram illustrates the primary modules imported by the `AppModule`:
Sources: [apps/api/src/app.module.ts:31-57](https://github.com/blade47/comp/blob/main/apps/api/src/app.module.ts#L31-L57)
## Health Module (`HealthModule`)
The `HealthModule` is a simple module dedicated to providing health check endpoints for the API. It contains only a controller, `HealthController`, which would typically expose endpoints to verify the application's operational status.
Sources: [apps/api/src/health/health.module.ts:1-6](https://github.com/blade47/comp/blob/main/apps/api/src/health/health.module.ts#L1-L6)
## Integration Platform Module (`IntegrationPlatformModule`)
The `IntegrationPlatformModule` is a comprehensive module responsible for managing various aspects of third-party integrations. It encompasses controllers for handling OAuth flows, managing connections, administrative tasks, and webhook processing. It also provides services for credential management, connection handling, and automated checks, supported by dedicated repositories for data persistence.
### Components
This module is structured with a clear separation of concerns, including:
* **Controllers**: Handle incoming HTTP requests related to integrations.
* **Services**: Encapsulate business logic for integration functionalities.
* **Repositories**: Manage data access for integration-related entities.
The module also exports several services, making them available for use by other modules that import `IntegrationPlatformModule`.
Exporting services like `CredentialVaultService` and `ConnectionService` allows other modules to securely interact with integration functionalities without needing to know the internal implementation details of the `IntegrationPlatformModule`.
### Module Structure
The following diagram illustrates the internal components of the `IntegrationPlatformModule`:
Sources: [apps/api/src/integration-platform/integration-platform.module.ts:1-62](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/integration-platform.module.ts#L1-L62)
## Questionnaire Module (`QuestionnaireModule`)
The `QuestionnaireModule` handles functionalities related to questionnaires. It includes a controller for API endpoints and a service for business logic. Notably, it imports the `TrustPortalModule`, indicating a dependency on trust portal functionalities, possibly for questionnaire publishing or data integration.
Sources: [apps/api/src/questionnaire/questionnaire.module.ts:1-11](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.module.ts#L1-L11)
---
## Technical docs: Extensions Layer
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-2/extensions-layer
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/customPrismaExtension.ts](https://github.com/blade47/comp/blob/main/apps/api/customPrismaExtension.ts)
- [apps/api/emailExtension.ts](https://github.com/blade47/comp/blob/main/apps/api/emailExtension.ts)
- [apps/api/integrationPlatformExtension.ts](https://github.com/blade47/comp/blob/main/apps/api/integrationPlatformExtension.ts)
The Extensions Layer refers to a set of custom build extensions designed to integrate specific project dependencies and workspace packages into the `@trigger.dev/build` process. These extensions customize how certain modules are handled during the build, ensuring they are correctly resolved, generated, and bundled for deployment or local development.
This layer addresses challenges such as managing Prisma client generation, resolving imports for internal workspace packages, and copying necessary build artifacts. By using the `@trigger.dev/build` `BuildExtension` interface, these extensions hook into various stages of the build lifecycle, like `onBuildStart` and `onBuildComplete`, to perform specialized tasks.
## Prisma Extension
The `PrismaExtension` is responsible for integrating Prisma into the build process, particularly for the `@trycompai/db` package. Its primary goal is to ensure that the Prisma client is correctly generated and included in the deployment bundle, and that Prisma-related modules are externalized where appropriate.
### Purpose and Configuration
The extension handles the resolution of the `schema.prisma` file, the local generation of the Prisma client during development, and the copying and generation of the client for deployment. It can be configured with various options to control its behavior.
This extension is crucial for projects using Prisma, especially when the schema is part of a shared workspace package like `@trycompai/db`, as it ensures the Prisma client is available and correctly configured in both development and deployment environments.
**`PrismaExtensionOptions`**
| Option | Type | Description
The following files were used as context for generating this this.moduleExternals = [ '@prisma/client', '@prisma/client', '@trycompai/db', // Add the published package to externals ]; }
const resolution = this.tryResolveSchemaPath(context as ExtendedBuildContext);
if (!resolution.path) { context.logger.debug( 'Prisma schema not found during build start, likely before dependencies are installed.', { searched: resolution.searched }, ); return; }
this._resolvedSchemaPath = resolution.path; context.logger.debug(`Resolved prisma schema to ${resolution.path}`); await this.ensureLocalPrismaClient(context as ExtendedBuildContext, resolution.path); }
if (!this._resolvedSchemaPath || !existsSync(this._resolvedSchemaPath)) { const resolution = this.tryResolveSchemaPath(context as ExtendedBuildContext);
if (!resolution.path) { throw new Error( [ 'PrismaExtension could not find the prisma schema. Make sure @trycompai/db is installed', `with version ${this.options.dbPackageVersion || 'latest'} and that its dist files are built.`, 'Searched the following locations:', ...resolution.searched.map((candidate) => ` - ${candidate}`), ].join('\n'), ); }
this._resolvedSchemaPath = resolution.path; }
assert(this._resolvedSchemaPath, 'Resolved schema path is not set'); const schemaPath = this._resolvedSchemaPath;
await this.ensureLocalPrismaClient(context as ExtendedBuildContext, schemaPath);
context.logger.debug('Looking for @prisma/client in the externals', { externals: manifest.externals, });
if (!version) { throw new Error( `PrismaExtension could not determine the version of @prisma/client. It's possible that the @prisma/client was not used in the project. If this isn't the case, please provide a version in the PrismaExtension options.`, ); }
context.logger.debug( `PrismaExtension is generating the Prisma client for version ${version} from @trycompai/db package`, );
const commands: string[] = []; const env: Record = {};
// Copy the prisma schema from the published package to the build output path const schemaDestinationPath = join(manifest.outputPath, 'prisma', 'schema.prisma'); const schemaDestinationDir = dirname(schemaDestinationPath); context.logger.debug( `Copying the prisma schema from ${schemaPath} to ${schemaDestinationPath}`, ); await mkdir(schemaDestinationDir, { recursive: true }); await cp(schemaPath, schemaDestinationPath);
// Add prisma generate command to generate the client from the copied schema commands.push( `${binaryForRuntime(manifest.runtime)} node_modules/prisma/build/index.js generate --schema=./prisma/schema.prisma`, );
// Only handle migrations if requested if (this.options.migrate) { context.logger.debug( 'Migration support not implemented for published package - please handle migrations separately', ); // You could add migration commands here if needed // commands.push(`${binaryForRuntime(manifest.runtime)} npx prisma migrate deploy`); }
// Set up environment variables env.DATABASE_URL = manifest.deploy.env?.DATABASE_URL;
if (this.options.directUrlEnvVarName) { env[this.options.directUrlEnvVarName] = manifest.deploy.env?.[this.options.directUrlEnvVarName] ?? process.env[this.options.directUrlEnvVarName]; if (!env[this.options.directUrlEnvVarName]) { context.logger.warn( `prismaExtension could not resolve the ${this.options.directUrlEnvVarName} environment variable. Make sure you add it to your environment variables or provide it as an environment variable to the deploy CLI command. See our docs for more info: https://trigger.dev/docs/deploy-environment-variables`, ); } } else { env.DIRECT_URL = manifest.deploy.env?.DIRECT_URL; env.DIRECT_DATABASE_URL = manifest.deploy.env?.DIRECT_DATABASE_URL; }
if (!env.DATABASE_URL) { context.logger.warn( 'prismaExtension could not resolve the DATABASE_URL environment variable. Make sure you add it to your environment variables. See our docs for more info: https://trigger.dev/docs/deploy-environment-variables', ); }
context.logger.debug('Adding the prisma layer with the following commands', { commands, env, dependencies: { prisma: version, '@trycompai/db': this.options.dbPackageVersion || 'latest', }, });
let current = start; while (true) { candidates.add(resolve(current, 'node_modules/@trycompai/db/dist/schema.prisma')); const parent = dirname(current); if (parent === current) { break; } current = parent; } };
--- File: apps/api/emailExtension.ts --- import type { BuildContext, BuildExtension, BuildManifest, } from '@trigger.dev/build'; import type { Plugin } from 'esbuild'; import { existsSync } from 'node:fs'; import { cp, mkdir } from 'node:fs/promises'; import { resolve } from 'node:path';
const PACKAGE_NAME = '@trycompai/email';
/** * Custom Trigger.dev build extension for @trycompai/email workspace package. * * Since @trycompai/email is a workspace package (not published to npm), * we need to: * 1. Add an esbuild plugin to resolve the import path during build * 2. Copy the built dist files into the trigger.dev deployment */ export function emailExtension(): EmailExtension { return new EmailExtension(); }
class EmailExtension implements BuildExtension { public readonly name = 'EmailExtension'; private _packagePath: string | undefined;
if (!this._packagePath) { throw new Error( [ `EmailExtension could not find ${PACKAGE_NAME}.`, 'Make sure the package is built (run `bun run build` in packages/email).', ].join('\n'), ); }
context.logger.debug(`Found email package at ${this._packagePath}`);
/** * Custom Trigger.dev build extension for @comp/integration-platform workspace package. * * Since @comp/integration-platform is a workspace package (not published to npm), * we need to: * 1. Add an esbuild plugin to resolve the import path during build * 2. Copy the built dist files into the trigger.dev deployment */ export function integrationPlatformExtension(): IntegrationPlatformExtension { return new IntegrationPlatformExtension(); }
class IntegrationPlatformExtension implements BuildExtension { public readonly name = 'IntegrationPlatformExtension'; private _packagePath: string | undefined;
// Find the package path this._packagePath = this.findPackageRoot(context.workingDir);
if (!this._packagePath) { throw new Error( [ `IntegrationPlatformExtension could not find ${PACKAGE_NAME}.`, 'Make sure the package is built (run `bun run build` in packages/integration-platform).', ].join('\n'), ); }
context.logger.debug(`Found integration-platform at ${this._packagePath}`);
const packagePath = this._packagePath; if (!packagePath) { return; }1. **Start with the `` block** listing all source files.
2. **No H1 title.**
3. **Introduction:** Explain what the "Extensions Layer" is, focusing on its role in customizing the build process for specific dependencies or workspace packages. Mention it uses `@trigger.dev/build` extensions.
4. **Prisma Extension (`PrismaExtension`)**:
* **Purpose:** Explain its role in handling Prisma during the build process, especially for the `@trycompai/db` package.
* **Key Features:**
* Externalizing Prisma-related modules.
* Resolving `schema.prisma` path.
* Ensuring local Prisma client generation.
* Copying schema and generating client in the build output.
* Handling environment variables (`DATABASE_URL`, `DIRECT_URL`).
* **Methods:** `onBuildStart`, `onBuildComplete`, `ensureLocalPrismaClient`, `runPrismaGenerate`, `resolvePrismaBinary`, `tryResolveSchemaPath`, `buildSchemaCandidates`.
* **Diagram:** A flowchart showing the `onBuildStart` and `onBuildComplete` lifecycle, especially schema resolution and client generation.
* **Configuration:** `PrismaExtensionOptions`.
5. **Email Extension (`EmailExtension`)**:
* **Purpose:** Explain its role in integrating the `@trycompai/email` workspace package into the build.
* **Key Features:**
* Finding the package root.
* Registering an esbuild plugin for path resolution.
* Copying `dist` files and `package.json` to the deployment bundle.
* **Methods:** `onBuildStart`, `onBuildComplete`, `findPackageRoot`.
* **Diagram:** A sequence diagram showing how `onBuildStart` registers the esbuild plugin and `onBuildComplete` copies files.
6. **Integration Platform Extension (`IntegrationPlatformExtension`)**:
* **Purpose:** Similar to `EmailExtension`, but for `@comp/integration-platform`.
* **Key Features:**
* Finding the package root.
* Registering an esbuild plugin for path resolution.
* Copying `dist` files and `package.json` to the deployment bundle.
* **Methods:** `onBuildStart`, `onBuildComplete`, `findPackageRoot`.
* **Diagram:** A flowchart for the build process, showing the steps of finding the package, registering the plugin, and copying files. This can be similar to the Email Extension but perhaps a high-level flow.
7. **Overall Architecture/Interaction:** A high-level diagram showing how these extensions fit into the `@trigger.dev/build` lifecycle.
Let's refine the diagrams:
* **Prisma Extension Flowchart:** Focus on `onBuildStart` and `onBuildComplete` logic, especially schema resolution, `ensureLocalPrismaClient`, and the final `addLayer` call.
* **Workspace Package Extension (Email/Integration Platform) Sequence Diagram:** Show `BuildContext` interacting with the extension, then the extension interacting with `esbuild` (via `registerPlugin`) and the file system (via `cp`, `mkdir`).
Constraint Checklist & Confidence Score:
1. block first: Yes
2. No H1 title: Yes
3. Based ONLY on source files: Yes
4. Introduction (1-2 paragraphs): Yes
5. Detailed Sections (H2, H3): Yes
6. Explain architecture, components, data flow: Yes
7. Identify key elements: Yes
8. Mermaid Diagrams (at least 2): Yes, planning 3.
* Flowcharts: Yes
* Sequence Diagrams: Yes
* Node IDs alphanumeric: Yes
* Arrow syntax: Yes
* Node labels < 25 chars or : Yes
* Wrap labels with special chars in quotes: Yes
* No in labels: Yes
* Split complex diagrams: Yes
* `mermaid` fences: Yes
* Valid syntax: Will double check.
9. Tables: Yes, for options/config.
10. Code Snippets (optional): Yes, for key parts like `prismaExtension` or `emailExtension` function calls.
11. Source Citations: Yes, at the end of each section.
12. Technical Accuracy: Yes
13. Clarity: Yes
14. MDX Components: Yes, will use Callouts, maybe Steps.
Confidence Score: 5/5
Mental Sandbox:
* For Prisma, the `onBuildStart` is for dev target and `onBuildComplete` is for deployment. I need to make this distinction clear. The `ensureLocalPrismaClient` is called in both, but its purpose differs slightly (local dev vs. preparing for deployment).
* The `buildSchemaCandidates` logic is important for Prisma, showing how it searches for `schema.prisma`.
* For Email/Integration, the `findPackageRoot` is crucial. The esbuild `onResolve` setup is also key.
* I should highlight that these extensions are for workspace packages not published to npm, which is a common use case for custom build logic.
Key information to extract for each extension:
* **PrismaExtension**:
* `PrismaExtensionOptions`: `version`, `migrate`, `directUrlEnvVarName`, `dbPackageVersion`.
* `moduleExternals`: `['@prisma/client', '@prisma/engines', '@trycompai/db']`.
* `onBuildStart`: Resolves schema, ensures local client (for dev/indexing).
* `onBuildComplete`: Resolves schema, ensures local client, copies schema, generates client, adds layer with dependencies and env vars.
* `tryResolveSchemaPath` and `buildSchemaCandidates`: How schema is found.
* `ensureLocalPrismaClient` and `runPrismaGenerate`: How Prisma client is handled.
* **EmailExtension**:
* `PACKAGE_NAME`: `@trycompai/email`.
* `onBuildStart`: Finds package root, registers esbuild plugin to resolve imports to `dist/index.js`.
* `onBuildComplete`: Copies `dist` folder and `package.json` to `node_modules/@trycompai/email` in the output.
* `findPackageRoot`: Searches relative paths for the package.
* **IntegrationPlatformExtension**:
* `PACKAGE_NAME`: `@comp/integration-platform`.
* Similar logic to `EmailExtension` for finding package, registering esbuild plugin, and copying files.
I'll make sure to explicitly mention that these extensions are part of the `@trigger.dev/build` system, which is implied by the imports but good to state directly.The Extensions Layer comprises a set of custom build extensions designed to integrate specific project dependencies and internal workspace packages into the `@trigger.dev/build` process. These extensions customize how certain modules are handled during the build lifecycle, ensuring they are correctly resolved, generated, and bundled for both local development and deployment.
By implementing the `BuildExtension` interface from `@trigger.dev/build`, these extensions hook into critical stages like `onBuildStart` and `onBuildComplete`. This allows them to perform specialized tasks such as managing Prisma client generation, resolving module paths for internal workspace packages not published to npm, and copying necessary build artifacts to the final deployment bundle. This layer is essential for maintaining a consistent and functional application across different environments.
## Prisma Extension
The `PrismaExtension` is a custom build extension specifically designed to manage Prisma integration within the build process, particularly for the `@trycompai/db` package. Its primary function is to ensure that the Prisma schema is correctly located, the Prisma client is generated, and all necessary Prisma-related artifacts are included in the final deployment.
### Purpose and Configuration
This extension handles the complexities of Prisma client generation, which is often required at different stages of the build. It ensures that the `schema.prisma` file is resolved from the `@trycompai/db` package and that the Prisma client is generated locally for development and then again for the deployment bundle.
The `PrismaExtension` is crucial for projects utilizing Prisma, especially when the Prisma schema resides within a shared workspace package. It automates the setup required for Prisma to function correctly in both development and deployed environments.
The behavior of the `PrismaExtension` can be customized using the `PrismaExtensionOptions`.
| Option | Type | Description
The following files were used as context for generating this wiki page:
The Extensions Layer comprises a set of custom build extensions designed to integrate specific project dependencies and internal workspace packages into the `@trigger.dev/build` process. These extensions customize how certain modules are handled during the build lifecycle, ensuring they are correctly resolved, generated, and bundled for both local development and deployment.
By implementing the `BuildExtension` interface from `@trigger.dev/build`, these extensions hook into critical stages like `onBuildStart` and `onBuildComplete`. This allows them to perform specialized tasks such as managing Prisma client generation, resolving module paths for internal workspace packages not published to npm, and copying necessary build artifacts to the final deployment bundle. This layer is essential for maintaining a consistent and functional application across different environments.
## Prisma Extension
The `PrismaExtension` is a custom build extension specifically designed to manage Prisma integration within the build
---
## Technical docs: GET Get organization browser context status
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-getorgcontextstatus
## Parameters
## Responses
## Try It
---
## Technical docs: POST Create a new browser session
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-createsession
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: Build Pipeline
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-2/build-pipeline
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/buildspec.yml](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml)
- [apps/api/buildspec.multistage.yml](https://github.com/blade47/comp/blob/main/apps/api/buildspec.multistage.yml)
- [apps/api/docker-compose.yml](https://github.com/blade47/comp/blob/main/apps/api/docker-compose.yml)
- [.syncpackrc.json](https://github.com/blade47/comp/blob/main/.syncpackrc.json)
This document outlines the build pipelines for the `api` application, covering both the traditional CodeBuild approach and a more streamlined multi-stage Docker build process. It also details the local development setup using Docker Compose and the project's dependency management strategy. The primary goal of these pipelines is to build the `api` service, package it into a Docker image, and deploy it to an Amazon ECS cluster.
The project utilizes two distinct CodeBuild specifications for the `api` service: `buildspec.yml` for a standard, step-by-step build within the CodeBuild environment, and `buildspec.multistage.yml` which offloads most of the build logic to a multi-stage Dockerfile. For local development and testing, `docker-compose.yml` provides a convenient way to run the `api` service using the same multi-stage Docker build approach. Dependency consistency across the monorepo is enforced using `syncpack` as configured in `.syncpackrc.json`.
## Standard CodeBuild Pipeline (`buildspec.yml`)
The `apps/api/buildspec.yml` file defines a comprehensive CodeBuild pipeline for the `api` application. This pipeline is responsible for fetching dependencies, building the application, creating a Docker image, pushing it to Amazon ECR, and finally updating the Amazon ECS service.
### Pipeline Phases
The build process is divided into three main phases: `pre_build`, `build`, and `post_build`.
### Pre-Build Phase
This phase focuses on initial setup, including logging into Amazon ECR and preparing environment variables for the build process. It also installs `bun`, the JavaScript runtime and package manager used in the project.
**Key Actions:**
* Log in to Amazon ECR using AWS CLI.
* Define `REPOSITORY_URI` based on `$ECR_REPOSITORY_URI`.
* Derive `COMMIT_HASH` from `$CODEBUILD_RESOLVED_SOURCE_VERSION` and set `IMAGE_TAG`.
* Install `bun` using its official installation script.
### Build Phase
This is the core of the pipeline, where the application is built and packaged into a Docker image. It involves environment configuration, validation, dependency installation, workspace package building, the main API application build, and meticulous preparation of build artifacts for Docker.
**Key Actions:**
* **Environment Setup**: Sets critical environment variables like `PATH`, `PGSSLMODE`, `NODE_ENV`, `NEXT_TELEMETRY_DISABLED`, `UV_THREADPOOL_SIZE`, and `NODE_OPTIONS`.
* **Environment Variable Validation**: Ensures essential environment variables (e.g., `DATABASE_URL`, `BASE_URL`, AWS credentials) are set, failing the build if any are missing.
* **Dependency Installation**: Installs only the `api` workspace dependencies using `bun install --filter=@comp/api`.
* **Workspace Package Building**: Builds shared workspace packages (`packages/db`, `packages/integration-platform`).
* **NestJS Application Build**: Navigates to `apps/api` and builds the NestJS application using `bun run build`.
* **Build Output Verification**: Checks for the presence of `main.js` in the build output.
* **Artifact Preparation**:
* Creates a `docker-build` directory.
* Copies the built `api` application artifacts (handling both `dist/apps/api/src` and `dist/src` output structures).
* Copies the `prisma` directory.
* Copies the root `node_modules` directory.
* Replaces workspace symlinks for `@trycompai/utils`, `@trycompai/db`, and `@comp/integration-platform` with their actual built output and `package.json` files.
* Copies the `Dockerfile` to the `docker-build` directory.
* Modifies `package.json` to remove the workspace dependency for `@comp/integration-platform` before copying.
* Copies `bun.lock`.
* **Docker Image Build**: Builds the Docker image using the prepared `docker-build` context and tags it with the commit hash and `latest`.
### Post-Build Phase
This phase handles the deployment of the newly built Docker image.
**Key Actions:**
* Push the Docker image to Amazon ECR with both the specific `IMAGE_TAG` and `latest`.
* Update the Amazon ECS service to use the new image, forcing a new deployment.
* Generate `imagedefinitions.json` for ECS deployment.
The build process critically depends on several environment variables, which are validated during the `build` phase. These include database connection strings, base URLs for various services, and AWS credentials for S3 access.
Essential Environment Variables
| Variable Name | Description |
| :------------------------ | :--------------------------------------------------- |
| `DATABASE_URL` | Connection string for the database. |
| `BASE_URL` | Base URL for the application. |
| `BETTER_AUTH_URL` | URL for the authentication service. |
| `TRUST_APP_URL` | URL for the trusted application. |
| `APP_AWS_BUCKET_NAME` | AWS S3 bucket name for application assets. |
| `APP_AWS_ACCESS_KEY_ID` | AWS access key ID for S3. |
| `APP_AWS_SECRET_ACCESS_KEY` | AWS secret access key for S3. |
### Build Flowchart
The following flowchart illustrates the detailed steps within the `build` phase of the standard CodeBuild pipeline.
Sources: [apps/api/buildspec.yml:1-105](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L1-L105)
## Multi-stage Docker Build Pipeline (`buildspec.multistage.yml`)
This alternative pipeline simplifies the CodeBuild process by leveraging a multi-stage Dockerfile (`Dockerfile.multistage`) to handle the entire build process within a Docker container. CodeBuild's role is reduced to orchestrating the Docker build, pushing the image, and updating the ECS service.
### Pipeline Phases
### Pre-Build Phase
Similar to the standard pipeline, this phase handles ECR login and image tagging.
**Key Actions:**
* Log in to Amazon ECR.
* Derive `COMMIT_HASH` and set `IMAGE_TAG`.
### Build Phase
The core difference lies here: the actual application build is performed by Docker.
**Key Actions:**
* Change directory to `apps/api`.
* Execute `docker build` using `Dockerfile.multistage` with the `production` target. The build context is set to the monorepo root (`../..`), allowing the Dockerfile to access all necessary files.
### Post-Build Phase
This phase is identical to the standard pipeline, handling deployment.
**Key Actions:**
* Push the Docker image to Amazon ECR.
* Update the Amazon ECS service.
* Generate `imagedefinitions.json`.
### Build Flowchart (Multi-stage)
Sources: [apps/api/buildspec.multistage.yml:1-32](https://github.com/blade47/comp/blob/main/apps/api/buildspec.multistage.yml#L1-L32)
## Local Development and Testing (`docker-compose.yml`)
The `apps/api/docker-compose.yml` file is designed for local development and testing of the `api` service. It utilizes the same multi-stage Dockerfile (`Dockerfile.multistage`) as the simplified CodeBuild pipeline to ensure consistency between local and production builds.
### Service Configuration
The `api` service is configured with the following properties:
| Property | Value | Description |
| :-------------- | :------------------------------------ | :----------------------------------------------------------------------- |
| `build.context` | `../..` | The build context is the monorepo root. |
| `build.dockerfile` | `apps/api/Dockerfile.multistage` | Specifies the multi-stage Dockerfile for building. |
| `build.target` | `production` | Builds the `production` stage of the Dockerfile. |
| `container_name` | `comp-api-test` | Assigns a specific name to the container. |
| `ports` | `"3333:3333"` | Maps container port 3333 to host port 3333. |
| `environment` | `NODE_ENV=production`, `PORT=3333` | Sets environment variables within the container. |
| `env_file` | `.env` | Loads environment variables from a local `.env` file. |
| `healthcheck` | `CMD wget ... http://localhost:3333/v1/health` | Defines a health check that pings the `/v1/health` endpoint. |
| `restart` | `unless-stopped` | Restarts the container automatically unless explicitly stopped. |
This `docker-compose.yml` is explicitly marked for local testing only. It should not be used for production deployments.
### Healthcheck Flow
The healthcheck defined in `docker-compose.yml` ensures that the `api` service is running and responsive before marking the container as healthy.
```mermaid
sequenceDiagram
participant DockerCompose
participant APIService as "API Service (Container)"
participant HealthEndpoint as "API Health Endpoint (/v1/health)"
DockerCompose->>APIService: Start container (comp-api-test)
APIService-->>DockerCompose: Container started
loop Healthcheck (every 30s)
DockerCompose->>APIService: Execute healthcheck command (wget)
APIService->>HealthEndpoint: HTTP GET /v1/health
HealthEndpoint-->>APIService: HTTP 200 OK
APIService-->>DockerCompose: Healthcheck successful
end
DockerCompose->>DockerCompose: Mark service as healthy
```
Sources: [apps/api/docker-compose.yml:1-24](https://github.com/blade47/comp/blob/main/apps/api/docker-compose.yml#L1-L24)
## Dependency Management (`.syncpackrc.json`)
The `.syncpackrc.json` file configures `syncpack`, a tool used to maintain consistency in package dependencies across the monorepo. This ensures that all packages using a particular dependency (e.g., React, Next.js) are on the same version, and that internal workspace packages are correctly referenced.
### Configuration Elements
* **`source`**: Specifies the locations of `package.json` files to be managed. This includes the root `package.json` and those within `apps/*/package.json` and `packages/*/package.json`.
* **`dependencyTypes`**: Defines which types of dependencies `syncpack` should manage: `prod` (dependencies), `dev` (devDependencies), and `peer` (peerDependencies).
* **`semverGroups`**: Defines rules for semantic versioning ranges.
* A specific rule ensures that internal packages (prefixed with `@comp/`) always use `workspace:*` for their version range, indicating they are part of the monorepo.
* **`versionGroups`**: Defines groups of packages that must have consistent versions across all `package.json` files in the monorepo. This is crucial for avoiding dependency hell and ensuring a stable build environment.
* **`lintRules`**: Defines rules for forbidden dependencies. For example, it prevents direct dependencies on Node.js built-in modules like `crypto`, `fs`, or `path`, which might indicate a misunderstanding of module bundling or environment.
### Version Groups Examples
The following table lists some of the key dependency groups enforced by `syncpack` to maintain consistency:
| Label | Dependencies |
| :---------------------------------- | :---------------------------------------------------------------------------- |
| Use exact versions for internal packages | `@comp/**` |
| Ensure React is consistent | `react`, `react-dom`, `@types/react`, `@types/react-dom`, `react-is` |
| Ensure Next.js is consistent | `next` |
| Ensure TypeScript is consistent | `typescript` |
| Ensure common build tools are consistent | `postcss`, `tailwindcss`, `@tailwindcss/**`, `autoprefixer` |
| Ensure testing tools are consistent | `@types/node`, `prettier`, `turbo` |
| Ensure ESLint is consistent | `eslint`, `eslint-config-next` |
Sources: [.syncpackrc.json:1-55](https://github.com/blade47/comp/blob/main/.syncpackrc.json#L1-L55)
---
## Technical docs: Auth Security
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/auth-security
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/auth/hybrid-auth.guard.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts)
- [apps/api/src/auth/types.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/types.ts)
- [apps/api/src/auth/platform-admin.guard.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/platform-admin.guard.ts)
- [apps/api/src/auth/auth-context.decorator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/auth-context.decorator.ts)
- [apps/api/src/auth/auth.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/auth.module.ts)
- [apps/api/src/auth/api-key.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.service.ts)
- [apps/api/src/auth/internal-token.guard.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/internal-token.guard.ts)
- [apps/api/src/auth/api-key.guard.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.guard.ts)
- [apps/api/src/auth/organization.decorator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/organization.decorator.ts)
- [apps/api/src/auth/role-validator.guard.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/role-validator.guard.ts)
The authentication and authorization system within the API (`apps/api`) is designed to secure endpoints by verifying the identity of incoming requests and ensuring they have the necessary permissions. It supports multiple authentication mechanisms, including API Keys for external integrations, JSON Web Tokens (JWT) for internal frontend applications, and an internal token for inter-service communication.
This system leverages NestJS Guards and custom decorators to provide a flexible and robust security layer, allowing granular control over access to resources based on authentication type, user identity, organization context, and assigned roles.
## Authentication Context and Types
The core of the authentication system relies on defining a clear context for authenticated requests. This context is captured by the `AuthenticatedRequest` interface, which extends the standard `Request` object, and the `AuthContext` interface, used for extracting authentication details.
### AuthenticatedRequest and AuthContext
These interfaces define the structure for authentication-related data that is attached to the request object after successful authentication.
```typescript
export interface AuthenticatedRequest extends Request {
organizationId: string;
authType: 'api-key' | 'jwt';
isApiKey: boolean;
userId?: string; // Only available for JWT auth
userEmail?: string; // Only available for JWT auth
userRoles: string[] | null;
}
export interface AuthContext {
organizationId: string;
authType: 'api-key' | 'jwt';
isApiKey: boolean;
userId?: string; // Only available for JWT auth
userEmail?: string; // Only available for JWT auth
userRoles: string[] | null;
}
```
Sources: [apps/api/src/auth/types.ts:3-17](https://github.com/blade47/comp/blob/main/apps/api/src/auth/types.ts#L3-L17)
The `userId` and `userEmail` fields are only populated when authentication is performed via JWT (session-based authentication). For API Key authentication, these fields will be undefined, as API keys are organization-scoped and not tied to a specific user.
## Authentication Guards
The API uses several NestJS `CanActivate` guards to enforce different authentication and authorization policies.
### HybridAuthGuard
The `HybridAuthGuard` is the primary authentication guard, designed to handle both API Key and JWT-based authentication seamlessly. It attempts to authenticate a request first using an API Key, and if that fails, it tries JWT authentication.
### Request Interception
The guard intercepts incoming requests and inspects the `x-api-key` and `authorization` headers.
### API Key Authentication Attempt
If an `x-api-key` header is present, the guard delegates to `handleApiKeyAuth`.
### JWT Authentication Attempt
If no `x-api-key` is found, or API key authentication fails, the guard checks for an `Authorization: Bearer` header and delegates to `handleJwtAuth`.
### Context Assignment
Upon successful authentication, the guard populates the `AuthenticatedRequest` object with relevant details like `organizationId`, `userId`, `userEmail`, `userRoles`, `authType`, and `isApiKey`.
#### Hybrid Authentication Flow
Sources: [apps/api/src/auth/hybrid-auth.guard.ts:25-34](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts#L25-L34), [apps/api/src/auth/hybrid-auth.guard.ts:36-54](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts#L36-L54), [apps/api/src/auth/hybrid-auth.guard.ts:56-173](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts#L56-L173)
The `handleJwtAuth` method uses `jose` library's `createRemoteJWKSet` to fetch JSON Web Key Sets (JWKS) from the `betterAuthUrl`. It includes a retry mechanism for key mismatch errors (`ERR_JWKS_NO_MATCHING_KEY`). If a key mismatch occurs, it attempts to fetch a fresh JWKS with no cache to ensure it has the latest keys for verification. This helps in handling JWT key rotation scenarios.
Sources: [apps/api/src/auth/hybrid-auth.guard.ts:77-119](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts#L77-L119)
For JWT authentication, an `X-Organization-Id` header is explicitly required. The guard verifies that the authenticated user (`userId` from JWT payload) is a member of the specified organization using `db.member.findFirst`.
Sources: [apps/api/src/auth/hybrid-auth.guard.ts:125-139](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts#L125-L139), [apps/api/src/auth/hybrid-auth.guard.ts:175-194](https://github.com/blade47/comp/blob/main/apps/api/src/auth/hybrid-auth.guard.ts#L175-L194)
### ApiKeyService and ApiKeyGuard
The `ApiKeyService` is responsible for the logic of handling API keys, while `ApiKeyGuard` integrates this service into the NestJS guard system.
#### ApiKeyService
This service provides methods for hashing, extracting, and validating API keys.
```typescript
@Injectable()
export class ApiKeyService {
private hashApiKey(apiKey: string, salt?: string): string { /* ... */ }
extractApiKey(apiKeyHeader?: string): string | null { /* ... */ }
async validateApiKey(apiKey: string): Promise { /* ... */ }
}
```
Sources: [apps/api/src/auth/api-key.service.ts:10-12](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.service.ts#L10-L12)
API keys are stored in the database (`db.apiKey`) in a hashed format, optionally with a salt. The `hashApiKey` method uses SHA256 for hashing. When validating, the provided API key is hashed with the stored salt (or without for backward compatibility) and compared against the stored hash.
Sources: [apps/api/src/auth/api-key.service.ts:14-25](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.service.ts#L14-L25), [apps/api/src/auth/api-key.service.ts:58-62](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.service.ts#L58-L62)
#### ApiKeyGuard
This guard specifically handles API key authentication. It extracts the `X-API-Key` header, validates it using `ApiKeyService`, and attaches the `organizationId` to the request.
```mermaid
sequenceDiagram
participant Client
participant ApiKeyGuard
participant ApiKeyService
participant Database
Client->>ApiKeyGuard: Request with X-API-Key
ApiKeyGuard->>ApiKeyService: extractApiKey(header)
ApiKeyService-->>ApiKeyGuard: Extracted Key
ApiKeyGuard->>ApiKeyService: validateApiKey(key)
ApiKeyService->>Database: findMany({ isActive: true })
Database-->>ApiKeyService: API Key Records (hashed, salt, orgId, expiresAt)
ApiKeyService->>ApiKeyService: Hash provided key with record salts
ApiKeyService->>ApiKeyService: Find matching record & check expiry
alt Key Valid & Not Expired
ApiKeyService->>Database: update({ id: matchingRecord.id, data: { lastUsedAt: now() } })
Database-->>ApiKeyService: Update successful
ApiKeyService-->>ApiKeyGuard: organizationId
ApiKeyGuard->>ApiKeyGuard: Set request.organizationId
ApiKeyGuard-->>Client: Access Granted
else Key Invalid or Expired
ApiKeyService-->>ApiKeyGuard: null
ApiKeyGuard->>Client: UnauthorizedException
end
```
Sources: [apps/api/src/auth/api-key.guard.ts:13-30](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.guard.ts#L13-L30), [apps/api/src/auth/api-key.service.ts:39-88](https://github.com/blade47/comp/blob/main/apps/api/src/auth/api-key.service.ts#L39-L88)
### PlatformAdminGuard
This guard is used to restrict access to endpoints that should only be accessible by platform administrators. It exclusively uses JWT authentication.
1. **JWT Requirement**: It strictly requires a `Bearer` JWT token.
2. **JWT Verification**: Verifies the JWT against the `betterAuthUrl`'s JWKS endpoint, similar to `HybridAuthGuard`.
3. **Admin Check**: After verifying the JWT and extracting the `userId`, it queries the database (`db.user`) to check if `user.isPlatformAdmin` is true.
4. **Context Assignment**: If successful, it sets `request.userId`, `request.userEmail`, and `request.isPlatformAdmin`.
Sources: [apps/api/src/auth/platform-admin.guard.ts:28-34](https://github.com/blade47/comp/blob/main/apps/api/src/auth/platform-admin.guard.ts#L28-L34), [apps/api/src/auth/platform-admin.guard.ts:40-45](https://github.com/blade47/comp/blob/main/apps/api/src/auth/platform-admin.guard.ts#L40-L45), [apps/api/src/auth/platform-admin.guard.ts:60-104](https://github.com/blade47/comp/blob/main/apps/api/src/auth/platform-admin.guard.ts#L60-L104)
### RoleValidatorGuard
The `RoleValidatorGuard` enables role-based access control (RBAC) for specific endpoints. It is instantiated using the `RequireRoles` factory function, which defines the roles required for access.
- **Role Check**: Compares the `userRoles` from the `AuthenticatedRequest` (populated by `HybridAuthGuard`) against the `allowedRoles` configured for the guard.
- **API Key Exemption**: Requests authenticated via API keys are explicitly allowed to bypass role checks, as API keys are organization-scoped and not tied to specific user roles. However, they still require an `organizationId`.
- **JWT Requirement**: For role-based authorization, JWT authentication is mandatory, ensuring `userId`, `organizationId`, and `userRoles` are available.
Sources: [apps/api/src/auth/role-validator.guard.ts:20-22](https://github.com/blade47/comp/blob/main/apps/api/src/auth/role-validator.guard.ts#L20-L22), [apps/api/src/auth/role-validator.guard.ts:24-34](https://github.com/blade47/comp/blob/main/apps/api/src/auth/role-validator.guard.ts#L24-L34), [apps/api/src/auth/role-validator.guard.ts:36-40](https://github.com/blade47/comp/blob/main/apps/api/src/auth/role-validator.guard.ts#L36-L40), [apps/api/src/auth/role-validator.guard.ts:42-46](https://github.com/blade47/comp/blob/main/apps/api/src/auth/role-validator.guard.ts#L42-L46)
### InternalTokenGuard
This guard protects internal API endpoints that should only be accessible by other internal services.
- **Token Check**: It expects an `X-Internal-Token` header in the request.
- **Environment Variable**: The value of this header is compared against the `process.env.INTERNAL_API_TOKEN` environment variable.
- **Production Enforcement**: In production environments, `INTERNAL_API_TOKEN` must be configured. If not, an `UnauthorizedException` is thrown.
- **Development Flexibility**: In non-production environments, if `INTERNAL_API_TOKEN` is not set, the guard allows the request to proceed, facilitating local development.
Sources: [apps/api/src/auth/internal-token.guard.ts:15-28](https://github.com/blade47/comp/blob/main/apps/api/src/auth/internal-token.guard.ts#L15-L28), [apps/api/src/auth/internal-token.guard.ts:30-35](https://github.com/blade47/comp/blob/main/apps/api/src/auth/internal-token.guard.ts#L30-L35)
## Auth Context Decorators
Custom parameter decorators simplify accessing authentication details within controllers and resolvers. These decorators rely on the `HybridAuthGuard` (or `ApiKeyGuard` for `Organization` decorator) having successfully populated the request object.
Sources: [apps/api/src/auth/auth-context.decorator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/auth-context.decorator.ts), [apps/api/src/auth/organization.decorator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/auth/organization.decorator.ts)
| Decorator | Description
The `AuthModule` registers and exports the following guards and services:
- `ApiKeyService`
- `ApiKeyGuard`
- `HybridAuthGuard`
- `InternalTokenGuard`
## AuthModule
The `AuthModule` is a NestJS module that encapsulates all authentication-related services and guards. It makes these components available for dependency injection throughout the application.
```typescript
import { Module } from '@nestjs/common';
import { ApiKeyGuard } from './api-key.guard';
import { ApiKeyService } from './api-key.service';
import { HybridAuthGuard } from './hybrid-auth.guard';
import { InternalTokenGuard } from './internal-token.guard';
@Module({
providers: [ApiKeyService, ApiKeyGuard, HybridAuthGuard, InternalTokenGuard],
exports: [ApiKeyService, ApiKeyGuard, HybridAuthGuard, InternalTokenGuard],
})
export class AuthModule {}
```
Sources: [apps/api/src/auth/auth.module.ts:1-12](https://github.com/blade47/comp/blob/main/apps/api/src/auth/auth.module.ts#L1-L12)
---
## Technical docs: POST Close a browser session
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-closesession
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: Assistant Chat
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/assistant-chat
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/assistant-chat/assistant-chat.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.service.ts)
- [apps/api/src/assistant-chat/assistant-chat.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.controller.ts)
- [apps/api/src/assistant-chat/assistant-chat.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.dto.ts)
- [apps/api/src/assistant-chat/upstash-redis.client.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/upstash-redis.client.ts)
- [apps/api/src/assistant-chat/assistant-chat.types.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.types.ts)
- [apps/api/src/assistant-chat/assistant-chat.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.module.ts)
The Assistant Chat module provides an API for managing ephemeral chat history for an AI assistant. It allows users to retrieve, save, and clear their conversation history, scoped to their user ID and organization ID. The history is stored in a Redis-compatible key-value store with a default time-to-live (TTL) of 7 days, designed for session context rather than long-term archiving.
This module integrates with the application's authentication system to ensure that chat history operations are user-scoped and secure. It leverages a flexible Redis client that can connect to an Upstash Redis instance or fall back to an in-memory store for development or testing environments.
## Architecture Overview
The Assistant Chat feature is implemented as a NestJS module, encapsulating its components: a controller for handling API requests, a service for business logic and data manipulation, and DTOs for data validation and transfer. It relies on a Redis client for persistence and integrates with the application's authentication module.
The chat history is designed to be ephemeral, with a default Time-To-Live (TTL) of 7 days. This means chat sessions are not intended for long-term storage or searchable archives but rather for maintaining context within recent interactions. The TTL can be configured via the `ASSISTANT_CHAT_TTL_SECONDS` environment variable.
Sources: [apps/api/src/assistant-chat/assistant-chat.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.module.ts), [apps/api/src/assistant-chat/assistant-chat.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.controller.ts), [apps/api/src/assistant-chat/assistant-chat.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.service.ts)
## API Endpoints
The `AssistantChatController` exposes a set of RESTful endpoints for managing assistant chat history. All endpoints are protected by the `HybridAuthGuard` and require user-scoped authentication. API key authentication is explicitly disallowed for chat history operations.
### Base Path
`/v1/assistant-chat`
Sources: [apps/api/src/assistant-chat/assistant-chat.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.controller.ts)
### Endpoints
| Method | Path | Description | Request Body | Response Body |
| :----- | :-------- | :------------------------------------------------------------------------ | :----------------------------------------- | :--------------------------------------------- |
| `GET` | `/history` | Retrieves the current user-scoped assistant chat history. | N/A | `{ messages: AssistantChatMessage[] }` |
| `PUT` | `/history` | Replaces the current user-scoped assistant chat history with new messages. | `SaveAssistantChatHistoryDto` | `{ success: true }` |
| `DELETE` | `/history` | Deletes the current user-scoped assistant chat history. | N/A | `{ success: true }` |
Sources: [apps/api/src/assistant-chat/assistant-chat.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.controller.ts)
### Authentication and Authorization
All endpoints are secured using `HybridAuthGuard`. The `AuthContext` decorator is used to extract user and organization information from the authenticated request. A `BadRequestException` is thrown if the `organizationId` or `userId` is missing, or if the request is authenticated via an API key instead of a user JWT.
Assistant chat history operations are strictly limited to user-authenticated requests (Bearer JWT). Requests authenticated with an API key will be rejected with a `BadRequestException`. This ensures that chat history is always tied to a specific user and organization.
Sources: [apps/api/src/assistant-chat/assistant-chat.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.controller.ts)
## Data Models
The core data structure for assistant chat is `AssistantChatMessage`, which represents a single message in the conversation.
### AssistantChatMessage
This type defines the structure of a single message, including its ID, role (user or assistant), text content, and creation timestamp.
| Field | Type | Description | Example |
| :-------- | :------- | :----------------------------------------- | :-------------------- |
| `id` | `string` | Unique identifier for the message. | `msg_abc123` |
| `role` | `'user' \| 'assistant'` | The sender of the message. | `user` |
| `text` | `string` | The content of the message. | `How do I invite a teammate?` |
| `createdAt` | `number` | Unix epoch timestamp in milliseconds. | `1735781554000` |
Sources: [apps/api/src/assistant-chat/assistant-chat.types.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.types.ts), [apps/api/src/assistant-chat/assistant-chat.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.dto.ts)
### Data Transfer Objects (DTOs)
The `assistant-chat.dto.ts` file defines DTOs used for API request bodies and Swagger documentation.
Sources: [apps/api/src/assistant-chat/assistant-chat.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.dto.ts)
## Service Logic
The `AssistantChatService` handles the core business logic for chat history management, including interaction with the Redis client and data validation.
### Key Generation
A unique key is generated for each user's chat history in Redis, combining the organization ID and user ID. This ensures data isolation between different users and organizations.
```typescript
const getAssistantChatKey = ({
organizationId,
userId,
}: GetAssistantChatKeyParams): string => {
return `assistant-chat:v1:${organizationId}:${userId}`;
};
```
Sources: [apps/api/src/assistant-chat/assistant-chat.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.service.ts#L16-L21)
### History Operations
- **`getHistory(params)`**: Retrieves the chat history for a given user and organization. It fetches raw data from Redis and then uses a Zod schema (`StoredMessagesSchema`) for safe parsing and validation. If parsing fails, an empty array is returned.
- **`saveHistory(params, messages)`**: Stores the provided chat messages for a user and organization. It first validates the incoming messages against `StoredMessagesSchema` to maintain data integrity in the cache. The data is stored with a configurable TTL.
- **`clearHistory(params)`**: Deletes the chat history associated with a user and organization from Redis.
Sources: [apps/api/src/assistant-chat/assistant-chat.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.service.ts#L29-L57)
### Data Validation
The `AssistantChatService` uses Zod schemas to ensure the integrity and shape of the stored chat messages.
- `StoredMessageSchema`: Validates individual chat messages.
- `StoredMessagesSchema`: Validates an array of `StoredMessageSchema` objects.
Sources: [apps/api/src/assistant-chat/assistant-chat.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/assistant-chat.service.ts#L8-L14)
## Redis Client
The `upstash-redis.client.ts` file provides an abstraction over Redis interactions. It dynamically chooses between an actual Upstash Redis client and an in-memory implementation based on environment variables.
Sources: [apps/api/src/assistant-chat/upstash-redis.client.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/upstash-redis.client.ts#L23-L34)
### `assistantChatRedisClient`
This client object provides `get`, `set`, and `del` methods, abstracting the underlying storage mechanism. It's used by the `AssistantChatService` to interact with the chat history store.
The `InMemoryRedis` class provides a basic in-memory key-value store that mimics the behavior of a Redis client for `get`, `set`, and `del` operations. It supports an optional `ex` (expire) parameter for `set` operations, which sets a time-to-live for stored keys. This implementation is primarily for local development or testing when an external Redis instance is not available.
```typescript
class InMemoryRedis {
private storage = new Map();
async get(key: string): Promise {
const record = this.storage.get(key);
if (!record) return null;
if (record.expiresAt && record.expiresAt <= Date.now()) {
this.storage.delete(key);
return null;
}
return record.value as T;
}
async set(
key: string,
value: unknown,
options?: { ex?: number },
): Promise<'OK'> {
const expiresAt = options?.ex ? Date.now() + options.ex * 1000 : undefined;
this.storage.set(key, { value, expiresAt });
return 'OK';
}
async del(key: string): Promise {
const existed = this.storage.delete(key);
return existed ? 1 : 0;
}
}
```
Sources: [apps/api/src/assistant-chat/upstash-redis.client.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/upstash-redis.client.ts#L9-L31)
---
## Technical docs: POST Navigate to a URL
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-navigatetourl
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: POST Check authentication status
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-checkauth
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: Evidence Forms
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/evidence-forms
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/evidence-forms/evidence-forms.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts)
- [apps/api/src/evidence-forms/evidence-forms.definitions.ts](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.definitions.ts)
- [apps/api/src/evidence-forms/evidence-forms.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.controller.ts)
- [apps/api/src/evidence-forms/evidence-forms.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.module.ts)
The Evidence Forms module provides a robust system for managing and processing various types of evidence submissions within an organization. It defines a set of pre-built forms, handles their submission, validation, file uploads, and review processes. This module integrates with authentication and attachment services to ensure secure and efficient evidence management.
At a high level, the system allows users to submit structured data and associated files for different evidence types (e.g., meeting minutes, policy documents). Authorized personnel can then review these submissions, approving or rejecting them with reasons. All interactions are secured through JWT or API key authentication and scoped to specific organizations.
## Architecture Overview
The Evidence Forms module follows a standard NestJS architecture, comprising a Controller, Service, and Module. It leverages shared definitions for form structures and integrates with other core services like `AttachmentsService` for file management and the database (`@trycompai/db`) for persistence.
The `EvidenceFormsController` exposes RESTful API endpoints, handling incoming HTTP requests. These requests are then delegated to the `EvidenceFormsService`, which encapsulates the business logic, data validation, and interactions with the database and other services. The `EvidenceFormsModule` orchestrates these components, declaring dependencies and making the service available for injection.
Sources:
- [apps/api/src/evidence-forms/evidence-forms.module.ts:1-11](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.module.ts#L1-L11)
- [apps/api/src/evidence-forms/evidence-forms.controller.ts:1-50](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.controller.ts#L1-L50)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:1-26](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L1-L26)
## Data Structures and Definitions
The core of the Evidence Forms system relies on well-defined data structures, primarily sourced from a shared `@comp/company` package. These definitions dictate the structure of forms, their fields, and the schema for submissions.
### Form Definitions
Key definitions include:
* `evidenceFormTypeSchema`: A Zod schema for validating the type of an evidence form (e.g., 'meeting', 'policy').
* `evidenceFormDefinitions`: An object mapping `EvidenceFormType` to its detailed `EvidenceFormDefinition`.
* `evidenceFormDefinitionList`: An array of all available `EvidenceFormDefinition` objects.
* `evidenceFormSubmissionSchemaMap`: A map where each `EvidenceFormType` is associated with its specific Zod schema for validating submission payloads.
* `EvidenceFormFieldDefinition`: Describes a single field within an evidence form, including its key, type (e.g., 'text', 'file', 'matrix'), and validation rules.
* `EvidenceFormDefinition`: Defines an entire evidence form, including its `type`, `name`, `description`, `fields`, and `submissionDateMode`.
Sources:
- [apps/api/src/evidence-forms/evidence-forms.definitions.ts:1-10](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.definitions.ts#L1-L10)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:10-16](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L10-L16)
### Validation Schemas
The service uses Zod for robust input validation:
* `listQuerySchema`: Validates query parameters for listing submissions (search, limit, offset).
* `uploadSchema`: Validates parameters for file uploads (formType, fileName, fileType, fileData).
* `reviewSchema`: Validates the payload for reviewing a submission (action: 'approved' | 'rejected', reason).
```typescript
// Example: listQuerySchema
const listQuerySchema = z.object({
search: z.string().trim().optional(),
limit: z.coerce.number().int().min(1).max(200).optional().default(50),
offset: z.coerce.number().int().min(0).optional().default(0),
});
```
Sources:
- [apps/api/src/evidence-forms/evidence-forms.service.ts:18-22](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L18-L22)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:24-28](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L24-L28)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:30-33](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L30-L33)
## Core Service Logic (`EvidenceFormsService`)
The `EvidenceFormsService` handles all business logic related to evidence forms.
### Authentication and Authorization
The service enforces strict access control:
* `requireJwtUser(authContext: AuthContext)`: Ensures the request is authenticated via JWT and has a `userId`. API key authentication is explicitly denied for operations requiring a user context.
* `requirePrivilegedEvidenceAccess(authContext: AuthContext)`: Builds upon `requireJwtUser` by checking if the authenticated user possesses one of the `EVIDENCE_FORM_REVIEWER_ROLES` (`owner`, `admin`, `auditor`). This is crucial for operations like reviewing submissions or exporting data.
The `EVIDENCE_FORM_REVIEWER_ROLES` constant defines which user roles are authorized to perform privileged actions on evidence forms.
Sources:
- [apps/api/src/evidence-forms/evidence-forms.service.ts:100-109](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L100-L109)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:111-124](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L111-L124)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:35-35](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L35-L35)
### File Handling
The service provides functionality for uploading and managing files associated with evidence forms.
* `decodeBase64File(fileData: string)`: Decodes a base64 encoded file string into a Buffer, performing basic validation on the input format and size.
* `uploadFile(params: { ... })`: Handles the entire file upload process. It validates the input payload, decodes the base64 file data, checks against size limits (`MAX_UPLOAD_FILE_SIZE_BYTES`, `MAX_UPLOAD_BASE64_LENGTH`), and then delegates to `AttachmentsService` to upload the file to S3 and generate a presigned download URL.
Uploaded files are subject to a maximum size limit of 100MB. This is enforced both by checking the base64 string length and the decoded file buffer length.
Sources:
- [apps/api/src/evidence-forms/evidence-forms.service.ts:126-146](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L126-L146)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:37-38](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L37-L38)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:321-356](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L321-L356)
### Data Transformation and Export
Several utility functions facilitate data manipulation, especially for CSV export:
* `toCsvRow(values: string[])`: Converts an array of strings into a CSV row, handling proper escaping of double quotes.
* `flattenValue(value: unknown)`: Converts various data types (objects, numbers, booleans, strings) into a string representation suitable for CSV. Special handling is included for file objects to return their download URL.
* `flattenMatrixRows(value: unknown, field: EvidenceFormFieldDefinition)`: Specifically designed to flatten data from 'matrix' type form fields into a readable string format for CSV.
* `normalizeSubmissionFormType(submission: T)`: Transforms the internal database `DbEvidenceFormType` to the external `EvidenceFormType` for API responses.
Sources:
- [apps/api/src/evidence-forms/evidence-forms.service.ts:40-42](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L40-L42)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:44-71](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L44-L71)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:73-94](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L73-L94)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:96-98](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L96-L98)
### Submission and Review Workflow
The service manages the lifecycle of evidence form submissions.
**Key methods:**
* `listForms()`: Returns a list of all available evidence form definitions.
* `getFormStatuses(organizationId: string)`: Retrieves the latest submission date for each form type within a given organization.
* `getFormWithSubmissions(params: { ... })`: Fetches a specific form definition along with its submissions for an organization. Requires privileged access and supports search, pagination.
* `getSubmission(params: { ... })`: Retrieves a single evidence form submission by ID. Requires privileged access.
* `submitForm(params: { ... })`: Creates a new evidence form submission. It validates the payload against the form's schema, handles automatic submission date population, and stores the data in the database.
* `reviewSubmission(params: { ... })`: Allows privileged users to approve or reject a pending submission. Requires a reason for rejection.
* `getMySubmissions(params: { ... })`: Retrieves all submissions made by the currently authenticated user.
* `getPendingSubmissionCount(params: { ... })`: Returns the count of pending submissions for the authenticated user.
* `exportCsv(params: { ... })`: Exports all submissions for a specific form type within an organization to a CSV format. This method requires privileged access and uses the data transformation utilities (`flattenValue`, `flattenMatrixRows`, `toCsvRow`) to format the output. It also generates presigned URLs for any attached files.
Sources:
- [apps/api/src/evidence-forms/evidence-forms.service.ts:148-150](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L148-L150)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:152-167](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L152-L167)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:169-216](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L169-L216)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:218-251](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L218-L251)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:253-319](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L253-L319)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:358-444](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L358-L444)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:446-500](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L446-L500)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:502-526](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L502-L526)
- [apps/api/src/evidence-forms/evidence-forms.service.ts:528-543](https://github.com/blade47/comp/blob/main/apps/api/src/evidence-forms/evidence-forms.service.ts#L528-L543)
## API Endpoints (`EvidenceFormsController`)
The `EvidenceFormsController` exposes a set of RESTful API endpoints for interacting with the evidence forms system. All endpoints are protected by `HybridAuthGuard` and require an `X-Organization-Id` header.
| Method | Path | Summary | Description --- File: apps/api/src/evidence-forms/evidence-forms.service.ts ---
import { AttachmentsService } from '@/attachments/attachments.service';
import type { AuthContext } from '@/auth/types';
import { db, EvidenceFormType as DbEvidenceFormType } from '@trycompai/db';
import {
toDbEvidenceFormType,
toExternalEvidenceFormType,
} from '@comp/company';
import {
BadRequestException,
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { z } from 'zod';
import {
evidenceFormDefinitionList,
evidenceFormDefinitions,
evidenceFormSubmissionSchemaMap,
evidenceFormTypeSchema,
type EvidenceFormFieldDefinition,
type EvidenceFormType,
} from './evidence-forms.definitions';
const listQuerySchema = z.object({
search: z.string().trim().optional(),
limit: z.coerce.number().int().min(1).max(200).optional().default(50),
offset: z.coerce.number().int().min(0).optional().default(0),
});
const uploadSchema = z.object({
formType: evidenceFormTypeSchema,
fileName: z.string().min(1),
fileType: z.string().min(1),
fileData: z.string().min(1),
});
const reviewSchema = z.object({
action: z.enum(['approved', 'rejected']),
reason: z.string().trim().optional(),
});
const EVIDENCE_FORM_REVIEWER_ROLES = ['owner', 'admin', 'auditor'] as const;
const MAX_UPLOAD_FILE_SIZE_BYTES = 100 * 1024 * 1024;
const MAX_UPLOAD_BASE64_LENGTH = Math.ceil(MAX_UPLOAD_FILE_SIZE_BYTES / 3) * 4;
function toCsvRow(values: string[]): string {
return values.map((value) => `"${value.replace(/"/g, '""')}"`).join(',');
}
function flattenValue(value: unknown): string {
if (value === null || value === undefined) {
return '';
}
if (typeof value === 'object') {
if (
'fileName' in value &&
typeof value.fileName === 'string' &&
'downloadUrl' in value &&
typeof value.downloadUrl === 'string'
) {
return value.downloadUrl;
}
return JSON.stringify(value);
}
if (typeof value === 'string') {
return value;
}
if (
typeof value === 'number' ||
typeof value === 'boolean' ||
typeof value === 'bigint'
) {
return value.toString();
}
if (typeof value === 'symbol') {
return value.description ?? '';
}
return '';
}
function flattenMatrixRows(
value: unknown,
field: EvidenceFormFieldDefinition,
): string {
if (!Array.isArray(value)) {
return '';
}
const columns = Array.isArray(field.columns) ? field.columns : [];
if (columns.length === 0) {
return JSON.stringify(value);
}
return value
.filter((row) => row && typeof row === 'object')
.map((row) => {
const rowRecord = row as Record;
return columns
.map((column) => {
const cellValue = rowRecord[column.key];
const normalizedValue =
typeof cellValue === 'string' ? cellValue : '';
return `${column.label}: ${normalizedValue}`;
})
.join(' | ');
})
.join(' || ');
}
function normalizeSubmissionFormType<
T extends { formType: DbEvidenceFormType },
>(submission: T): Omit & { formType: EvidenceFormType } {
return {
...submission,
formType: toExternalEvidenceFormType(submission.formType) ?? 'meeting',
};
}
@Injectable()
export class EvidenceFormsService {
constructor(private readonly attachmentsService: AttachmentsService) {}
private requireJwtUser(authContext: AuthContext): string {
if (authContext.isApiKey || authContext.authType === 'api-key') {
throw new UnauthorizedException(
'This endpoint requires JWT authentication and does not support API key authentication',
);
}
if (!authContext.userId) {
throw new UnauthorizedException('Authenticated user session is required');
}
return authContext.userId;
}
private requirePrivilegedEvidenceAccess(authContext: AuthContext): string {
const userId = this.requireJwtUser(authContext);
const roles = authContext.userRoles ?? [];
const hasRequiredRole = EVIDENCE_FORM_REVIEWER_ROLES.some((role) =>
roles.includes(role),
);
if (!hasRequiredRole) {
throw new UnauthorizedException(
`Access denied. Required one of roles: ${EVIDENCE_FORM_REVIEWER_ROLES.join(', ')}`,
);
}
return userId;
}
private decodeBase64File(fileData: string): Buffer {
const normalized = fileData.trim();
if (normalized.length === 0 || normalized.length % 4 !== 0) {
throw new BadRequestException(
'Invalid file data. Expected base64 string.',
);
}
const base64Pattern = /^[A-Za-z0-9+/]+={0,2}$/;
if (!base64Pattern.test(normalized)) {
throw new BadRequestException(
'Invalid file data. Expected base64 string.',
);
}
const fileBuffer = Buffer.from(normalized, 'base64');
if (!fileBuffer.length) {
throw new BadRequestException('File cannot be empty');
}
return fileBuffer;
}
listForms() {
return evidenceFormDefinitionList;
}
async getFormStatuses(organizationId: string) {
const results = await db.evidenceSubmission.groupBy({
by: ['formType'],
where: { organizationId },
_max: { submittedAt: true },
});
const statuses: Record = {};
for (const form of evidenceFormDefinitionList) {
const match = results.find(
(r) => r.formType === toDbEvidenceFormType(form.type),
);
statuses[form.type] = {
lastSubmittedAt: match?._max.submittedAt?.toISOString() ?? null,
};
}
return statuses;
}
async getFormWithSubmissions(params: {
organizationId: string;
authContext: AuthContext;
formType: string;
search?: string;
limit?: string;
offset?: string;
}) {
const { organizationId, formType } = params;
this.requirePrivilegedEvidenceAccess(params.authContext);
const parsedType = evidenceFormTypeSchema.safeParse(formType);
if (!parsedType.success) {
throw new BadRequestException('Unsupported form type');
}
const parsedQuery = listQuerySchema.safeParse({
search: params.search,
limit: params.limit,
offset: params.offset,
});
if (!parsedQuery.success) {
throw new BadRequestException(parsedQuery.error.flatten());
}
const query = parsedQuery.data;
const submissions = await db.evidenceSubmission.findMany({
where: {
organizationId,
formType: toDbEvidenceFormType(parsedType.data),
},
include: {
submittedBy: {
select: {
id: true,
name: true,
email: true,
},
},
},
orderBy: {
submittedAt: 'desc',
},
});
const filtered = query.search
? submissions.filter((submission) => {
const searchTarget = JSON.stringify(submission.data).toLowerCase();
return searchTarget.includes(query.search!.toLowerCase());
})
: submissions;
const paginated = filtered.slice(query.offset, query.offset + query.limit);
return {
form: evidenceFormDefinitions[parsedType.data],
submissions: paginated.map(normalizeSubmissionFormType),
total: filtered.length,
};
}
async getSubmission(params: {
organizationId: string;
authContext: AuthContext;
formType: string;
submissionId: string;
}) {
this.requirePrivilegedEvidenceAccess(params.authContext);
const parsedType = evidenceFormTypeSchema.safeParse(params.formType);
if (!parsedType.success) {
throw new BadRequestException('Unsupported form type');
}
const submission = await db.evidenceSubmission.findFirst({
where: {
id: params.submissionId,
organizationId: params.organizationId,
formType: toDbEvidenceFormType(parsedType.data),
},
include: {
submittedBy: {
select: {
id: true,
name: true,
email: true,
},
},
reviewedBy: {
select: {
id: true,
name: true,
email: true,
},
},
},
});
if (!submission) {
throw new NotFoundException('Submission not found');
}
return {
form: evidenceFormDefinitions[parsedType.data],
submission: normalizeSubmissionFormType(submission),
};
}
async submitForm(params: {
organizationId: string;
formType: string;
payload: unknown;
authContext: AuthContext;
}) {
const parsedType = evidenceFormTypeSchema.safeParse(params.formType);
if (!parsedType.success) {
throw new BadRequestException('Unsupported form type');
}
if (!params.authContext.userId) {
throw new BadRequestException(
'Authenticated user session is required to submit evidence forms',
);
}
const formDefinition = evidenceFormDefinitions[parsedType.data];
const nowIso = new Date().toISOString();
if (!params.payload || typeof params.payload !== 'object') {
throw new BadRequestException('Submission payload must be an object');
}
const payloadObject: Record = {
...(params.payload as Record),
};
if (formDefinition.submissionDateMode === 'auto') {
payloadObject.submissionDate = nowIso;
}
const schema = evidenceFormSubmissionSchemaMap[parsedType.data];
const parsedPayload = schema.safeParse(payloadObject);
if (!parsedPayload.success) {
const flattened = parsedPayload.error.flatten();
const fieldErrors = Object.entries(flattened.fieldErrors)
.map(([field, messages]) => {
const msg =
Array.isArray(messages) && messages.length > 0
? messages[0]
: 'is required';
return `${field}: ${msg}`;
})
.slice(0, 5);
const message =
fieldErrors.length > 0
? `Please fix the following: ${fieldErrors.join('; ')}`
: 'Please fill in all required fields';
throw new BadRequestException(message);
}
return await db.evidenceSubmission
.create({
data: {
organizationId: params.organizationId,
formType: toDbEvidenceFormType(parsedType.data),
submittedById: params.authContext.userId,
data: parsedPayload.data,
},
include: {
submittedBy: {
select: {
id: true,
name: true,
email: true,
},
},
},
})
.then(normalizeSubmissionFormType);
}
async uploadFile(params: {
organizationId: string;
authContext: AuthContext;
payload: unknown;
}) {
if (!params.authContext.userId) {
throw new BadRequestException(
'Authenticated user session is required to upload evidence files',
);
}
const parsed = uploadSchema.safeParse(params.payload);
if (!parsed.success) {
throw new BadRequestException(parsed.error.flatten());
}
if (parsed.data.fileData.length > MAX_UPLOAD_BASE64_LENGTH) {
throw new BadRequestException(
`File exceeds the ${MAX_UPLOAD_FILE_SIZE_BYTES / (1024 * 1024)}MB limit`,
);
}
const fileBuffer = this.decodeBase64File(parsed.data.fileData);
if (fileBuffer.length > MAX_UPLOAD_FILE_SIZE_BYTES) {
throw new BadRequestException(
`File exceeds the ${MAX_UPLOAD_FILE_SIZE_BYTES / (1024 * 1024)}MB limit`,
);
}
const fileKey = await this.attachmentsService.uploadToS3(
fileBuffer,
parsed.data.fileName,
parsed.data.fileType,
params.organizationId,
'evidence-forms',
parsed.data.formType,
);
const downloadUrl =
await this.attachmentsService.getPresignedDownloadUrl(fileKey);
return {
fileName: parsed.data.fileName,
fileKey,
downloadUrl,
};
}
async exportCsv(params: {
organizationId: string;
formType: string;
authContext: AuthContext;
}) {
this.requirePrivilegedEvidenceAccess(params.authContext);
const parsedType = evidenceFormTypeSchema.safeParse(params.formType);
if (!parsedType.success) {
throw new BadRequestException('Unsupported form type');
}
const formType: EvidenceFormType = parsedType.data;
const form = evidenceFormDefinitions[formType];
const submissions = await db.evidenceSubmission.findMany({
where: {
organizationId: params.organizationId,
formType: toDbEvidenceFormType(formType),
},
include: {
submittedBy: {
select: {
name: true,
email: true,
},
},
},
orderBy: {
submittedAt: 'desc',
},
});
if (submissions.length === 0) {
throw new BadRequestException(
'No submissions available for export for this form',
);
}
const headers = [
'submissionId',
'submissionDate',
'submittedByName',
'submittedByEmail',
...form.fields
.filter((field) => field.key !== 'submissionDate')
.map((field) => field.key),
];
const rows = await Promise.all(
submissions.map(async (submission) => {
const data = submission.data as Record;
const fieldValues = await Promise.all(
form.fields
.filter((field) => field.key !== 'submissionDate')
.map(async (field) => {
const rawValue = data[field.key];
if (
rawValue &&
typeof rawValue === 'object' &&
'fileKey' in rawValue &&
typeof rawValue.fileKey === 'string'
) {
const signedUrl =
await this.attachmentsService.getPresignedDownloadUrl(
rawValue.fileKey,
);
return signedUrl;
}
if (field.type === 'matrix') {
return flattenMatrixRows(rawValue, field);
}
return flattenValue(rawValue);
}),
);
return [
submission.id,
typeof data.submissionDate === 'string'
? data.submissionDate
: submission.submittedAt.toISOString(),
submission.submittedBy?.name ?? '',
submission.submittedBy?.email ?? '',
...fieldValues,
];
}),
);
const csvLines = [toCsvRow(headers), ...rows.map((row) => toCsvRow(row))];
return csvLines.join('\n');
}
async reviewSubmission(params: {
organizationId: string;
formType: string;
submissionId: string;
payload: unknown;
authContext: AuthContext;
}) {
const parsedType = evidenceFormTypeSchema.safeParse(params.formType);
if (!parsedType.success) {
throw new BadRequestException('Unsupported form type');
}
const reviewerUserId = this.requirePrivilegedEvidenceAccess(
params.authContext,
);
const parsed = reviewSchema.safeParse(params.payload);
if (!parsed.success) {
throw new BadRequestException(parsed.error.flatten());
}
if (parsed.data.action === 'rejected' && !parsed.data.reason) {
throw new BadRequestException(
'A reason is required when rejecting a submission',
);
}
const submission = await db.evidenceSubmission.findFirst({
where: {
id: params.submissionId,
organizationId: params.organizationId,
formType: toDbEvidenceFormType(parsedType.data),
},
});
if (!submission) {
throw new NotFoundException('Submission not found');
}
if (submission.status !== 'pending') {
throw new BadRequestException(
'Submission must be pending to be reviewed',
);
}
return await db.evidenceSubmission
.update({
where: { id: params.submissionId },
data: {
status: parsed.data.action,
reviewedById: reviewerUserId,
reviewedAt: new Date(),
reviewReason: parsed.data.reason,
},
include: {
submittedBy: {
select: {
id: true,
name: true,
email: true,
},
},
reviewedBy: {
select: {
id: true,
name: true,
email: true,
},
},
},
})
.then(normalizeSubmissionFormType);
}
async getMySubmissions(params: {
organizationId: string;
authContext: AuthContext;
formType?: string;
}) {
const userId = this.requireJwtUser(params.authContext);
const where: Record = {
organizationId: params.organizationId,
submittedById: userId,
};
if (params.formType) {
const parsedType = evidenceFormTypeSchema.safeParse(params.formType);
if (!parsedType.success) {
throw new BadRequestException('Unsupported form type');
}
where.formType = toDbEvidenceFormType(parsedType.data);
}
return await db.evidenceSubmission
.findMany({
where,
include: {
reviewedBy: {
select: {
id: true,
name: true,
email: true,
},
},
},
orderBy: {
submittedAt: 'desc',
},
})
.then((submissions) => submissions.map(normalizeSubmissionFormType));
}
async getPendingSubmissionCount(params: {
organizationId: string;
authContext: AuthContext;
}) {
const userId = this.requireJwtUser(params.authContext);
const count = await db.evidenceSubmission.count({
where: {
organizationId: params.organizationId,
submittedById: userId,
status: 'pending',
},
});
return { count };
}
}
--- File: apps/api/src/evidence-forms/evidence-forms.definitions.ts ---
// Single source of truth: re-export from shared @comp/company package
export {
evidenceFormTypeSchema,
evidenceFormFileSchema,
evidenceFormSubmissionSchemaMap,
evidenceFormDefinitions,
evidenceFormDefinitionList,
type EvidenceFormType,
type EvidenceFormFieldDefinition,
type EvidenceFormDefinition,
} from '@comp/company';
--- File: apps/api/src/evidence-forms/evidence-forms.controller.ts ---
import { AuthContext, OrganizationId } from '@/auth/auth-context.decorator';
import { HybridAuthGuard } from '@/auth/hybrid-auth.guard';
import type { AuthContext as AuthContextType } from '@/auth/types';
import {
Body,
Controller,
Get,
Header,
Param,
Patch,
Post,
Query,
Res,
UseGuards,
} from '@nestjs/common';
import { ApiHeader, ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import type { Response } from 'express';
import { EvidenceFormsService } from './evidence-forms.service';
@ApiTags('Evidence Forms')
@Controller({ path: 'evidence-forms', version: '1' })
@UseGuards(HybridAuthGuard)
@ApiSecurity('apikey')
@ApiHeader({
name: 'X-Organization-Id',
description:
'Organization ID (required for session auth, optional for API key auth)',
required: false,
})
export class EvidenceFormsController {
constructor(private readonly evidenceFormsService: EvidenceFormsService) {}
@Get()
@ApiOperation({
summary: 'List evidence forms',
description: 'List all available pre-built evidence forms',
})
listForms() {
return this.evidenceFormsService.listForms();
}
@Get('statuses')
@ApiOperation({
summary: 'Get submission statuses for all forms',
description:
'Returns the latest submission date per form type for the active organization',
})
async getFormStatuses(@OrganizationId() organizationId: string) {
return this.evidenceFormsService.getFormStatuses(organizationId);
}
@Get('my-submissions')
@ApiOperation({
summary: 'Get current user submissions',
description:
'Returns all evidence form submissions by the authenticated user for the active organization',
})
async getMySubmissions(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Query('formType') formType?: string,
) {
return this.evidenceFormsService.getMySubmissions({
organizationId,
authContext,
formType,
});
}
@Get('my-submissions/pending-count')
@ApiOperation({
summary: 'Get pending submission count for current user',
description:
'Returns the count of pending evidence submissions for the authenticated user',
})
async getPendingSubmissionCount(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
) {
return this.evidenceFormsService.getPendingSubmissionCount({
organizationId,
authContext,
});
}
@Get(':formType')
@ApiOperation({
summary: 'Get form definition and submissions',
description:
'Fetch a specific form definition with submissions for the active organization',
})
async getFormWithSubmissions(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Param('formType') formType: string,
@Query('search') search?: string,
@Query('limit') limit?: string,
@Query('offset') offset?: string,
) {
return this.evidenceFormsService.getFormWithSubmissions({
organizationId,
authContext,
formType,
search,
limit,
offset,
});
}
@Get(':formType/submissions/:submissionId')
@ApiOperation({
summary: 'Get a single submission',
description:
'Fetch one evidence form submission for the active organization',
})
async getSubmission(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Param('formType') formType: string,
@Param('submissionId') submissionId: string,
) {
return this.evidenceFormsService.getSubmission({
organizationId,
authContext,
formType,
submissionId,
});
}
@Post(':formType/submissions')
@ApiOperation({
summary: 'Submit evidence form entry',
description:
'Create a new organization-scoped evidence form submission using Zod-validated payloads',
})
async submitForm(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Param('formType') formType: string,
@Body() body: unknown,
) {
return this.evidenceFormsService.submitForm({
organizationId,
formType,
payload: body,
authContext,
});
}
@Patch(':formType/submissions/:submissionId/review')
@ApiOperation({
summary: 'Review a submission',
description:
'Approve or reject an evidence form submission with an optional reason',
})
async reviewSubmission(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Param('formType') formType: string,
@Param('submissionId') submissionId: string,
@Body() body: unknown,
) {
return this.evidenceFormsService.reviewSubmission({
organizationId,
formType,
submissionId,
payload: body,
authContext,
});
}
@Post('uploads')
@ApiOperation({
summary: 'Upload evidence form file',
description:
'Upload a file for evidence form fields and return file metadata for submission payload',
})
async uploadFile(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Body() body: unknown,
) {
return this.evidenceFormsService.uploadFile({
organizationId,
authContext,
payload: body,
});
}
@Get(':formType/export.csv')
@ApiOperation({
summary: 'Export form submissions to CSV',
description: 'Export all form submissions for an organization as CSV',
})
@Header('Content-Type', 'text/csv')
async exportCsv(
@OrganizationId() organizationId: string,
@AuthContext() authContext: AuthContextType,
@Param('formType') formType: string,
@Res() res: Response,
) {
const csv = await this.evidenceFormsService.exportCsv({
organizationId,
authContext,
formType,
});
const filename = `${formType}-submissions-${new Date().toISOString().slice(0, 10)}.csv`;
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.send(csv);
}
}
--- File: apps/api/src/evidence-forms/evidence-forms.module.ts ---
import { Module } from '@nestjs/common';
import { AttachmentsModule } from '@/attachments/attachments.module';
import { AuthModule } from '@/auth/auth.module';
import { EvidenceFormsController } from './evidence-forms.controller';
import { EvidenceFormsService } from './evidence-forms.service';
@Module({
imports: [AuthModule, AttachmentsModule],
controllers: [EvidenceFormsController],
providers: [EvidenceFormsService],
exports: [EvidenceFormsService],
})
export class EvidenceFormsModule {}
---
## Technical docs: Finding Templates
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/finding-templates
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/finding-template/index.ts](https://github.com/blade47/comp/blob/main/apps/api/src/finding-template/index.ts)
The `finding-template/index.ts` file serves as a "barrel" file, acting as a central entry point for the "Finding Templates" module within the API application. Its primary purpose is to re-export various components related to finding templates, making them easily importable from a single location. This pattern simplifies imports for other parts of the application that need to interact with the finding template functionality.
This file aggregates the core building blocks of the Finding Templates feature, including its main module, business logic service, API controller, and data transfer objects (DTOs) for creating and updating finding templates.
The `index.ts` file is a common pattern in TypeScript/JavaScript projects, often referred to as a "barrel" file. It consolidates exports from multiple files into a single, convenient module, streamlining imports and improving code organization.
## Module Structure and Exports
The `index.ts` file explicitly re-exports five distinct components, each playing a specific role in the Finding Templates feature. These exports collectively define the public interface of the `finding-template` directory.
The exported components are:
* `finding-template.module`: The main NestJS module for the Finding Templates feature, responsible for organizing controllers, providers, and other modules.
* `finding-template.service`: Contains the business logic and data access operations related to finding templates.
* `finding-template.controller`: Handles incoming HTTP requests and defines the API endpoints for managing finding templates.
* `create-finding-template.dto`: A Data Transfer Object used for validating and structuring data when creating a new finding template.
* `update-finding-template.dto`: A Data Transfer Object used for validating and structuring data when updating an existing finding template.
### Exported Components Overview
The following diagram illustrates how `index.ts` acts as the central export point for the Finding Templates module's components.
Sources: [apps/api/src/finding-template/index.ts:1-5](https://github.com/blade47/comp/blob/main/apps/api/src/finding-template/index.ts#L1-L5)
### Categorization of Exports
The exports can be broadly categorized by their role within a typical NestJS application structure.
Sources: [apps/api/src/finding-template/index.ts:1-5](https://github.com/blade47/comp/blob/main/apps/api/src/finding-template/index.ts#L1-L5)
## Code Snippet
The content of the `index.ts` file is straightforward, consisting solely of re-export statements:
```typescript
export * from './finding-template.module';
export * from './finding-template.service';
export * from './finding-template.controller';
export * from './dto/create-finding-template.dto';
export * from './dto/update-finding-template.dto';
```
Sources: [apps/api/src/finding-template/index.ts:1-5](https://github.com/blade47/comp/blob/main/apps/api/src/finding-template/index.ts#L1-L5)
---
## Technical docs: POST Create a browser automation
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-createautomation
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: GET Get all browser automations for a task
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-getautomationsfortask
## Parameters
## Responses
## Try It
---
## Technical docs: Findings Workflow
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/findings-workflow
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/findings/index.ts](https://github.com/blade47/comp/blob/main/apps/api/src/findings/index.ts)
The "Findings Workflow" within the API context refers to the set of components and processes involved in managing "findings" — likely security findings, vulnerabilities, or similar reportable items. This workflow is encapsulated within a dedicated module, and its public interface is defined by the `index.ts` file.
The `index.ts` file acts as a barrel file, consolidating and re-exporting key elements of the `findings` module. This approach simplifies imports for other parts of the application, allowing them to import all necessary components from a single entry point (`apps/api/src/findings`) rather than individual files.
## Module Structure and Export Mechanism
The `apps/api/src/findings/index.ts` file serves as the primary export hub for the `findings` module. It aggregates and re-exports several core components, making them easily accessible throughout the application. This pattern promotes modularity and maintainability by providing a clear, consolidated interface for the module.
The exported components collectively define the structure and capabilities of the Findings Workflow, encompassing the module's definition, its business logic, API interaction, and data transfer objects.
The `index.ts` file utilizes the "barrel file" pattern. This pattern groups exports from multiple modules into a single, convenient module. It simplifies import statements in consuming files, making the codebase cleaner and easier to navigate.
Sources: [apps/api/src/findings/index.ts:1-5](https://github.com/blade47/comp/blob/main/apps/api/src/findings/index.ts#L1-L5)
## Key Components of the Findings Workflow
The `index.ts` file exports five primary components that form the backbone of the Findings Workflow. These components are designed to work together to handle the lifecycle of findings within the API.
### FindingsModule
The `FindingsModule` is the root module for the findings functionality. In a typical NestJS application, this module would declare providers (like services), controllers, and potentially import other modules required for its operation. It acts as the organizational unit for all finding-related logic and resources.
### FindingsService
The `FindingsService` is responsible for encapsulating the business logic related to findings. This typically includes operations such as creating, retrieving, updating, and deleting findings, as well as any complex data manipulations or interactions with a database or external services.
### FindingsController
The `FindingsController` handles incoming HTTP requests related to findings. It defines the API endpoints (e.g., `/findings`, `/findings/:id`) and orchestrates the interaction between the HTTP layer and the `FindingsService`. It receives requests, validates input using DTOs, calls the appropriate service methods, and sends back HTTP responses.
### CreateFindingDto
The `CreateFindingDto` (Data Transfer Object) defines the structure and validation rules for data submitted when creating a new finding. DTOs ensure that incoming data conforms to expected formats and types, enhancing data integrity and security.
### UpdateFindingDto
Similar to `CreateFindingDto`, the `UpdateFindingDto` defines the structure and validation rules for data submitted when updating an existing finding. It specifies which fields can be modified and their respective constraints.
Sources: [apps/api/src/findings/index.ts:1-5](https://github.com/blade47/comp/blob/main/apps/api/src/findings/index.ts#L1-L5)
---
## Technical docs: Policy Management
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/policy-management
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/policies/policies.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.controller.ts)
- [apps/api/src/policies/policies.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts)
- [apps/api/src/policies/schemas/version-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/schemas/version-operations.ts)
- [apps/api/src/policies/schemas/policy-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/schemas/policy-operations.ts)
- [apps/api/src/policies/dto/ai-suggest-policy.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/ai-suggest-policy.dto.ts)
- [apps/api/src/policies/policies.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.module.ts)
- [apps/api/src/policies/dto/create-policy.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/create-policy.dto.ts)
- [apps/api/src/policies/dto/update-policy.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/update-policy.dto.ts)
- [apps/api/src/policies/dto/version.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/version.dto.ts)
Policy Management provides a robust system for organizations to create, manage, version, and publish their internal policies. It encompasses features for policy lifecycle management, including drafting, reviewing, publishing, and archiving policies, along with advanced functionalities like AI-powered content suggestions and PDF generation.
The system is designed to handle various policy states and ensures proper authorization and data integrity throughout the policy lifecycle. It integrates with authentication mechanisms and attachment services for secure storage and retrieval of policy-related documents.
## Architecture and Components
The Policy Management module is built using NestJS and follows a modular architecture, separating concerns into controllers, services, and DTOs (Data Transfer Objects).
### Module Structure
The `PoliciesModule` orchestrates the Policy Management features, importing necessary modules and registering its components.
```typescript
// apps/api/src/policies/policies.module.ts
@Module({
imports: [AuthModule, AttachmentsModule],
controllers: [PoliciesController],
providers: [PoliciesService, PolicyPdfRendererService],
exports: [PoliciesService],
})
export class PoliciesModule {}
```
Sources: [apps/api/src/policies/policies.module.ts:1-11](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.module.ts#L1-L11)
### Policies Controller
The `PoliciesController` handles incoming HTTP requests related to policies and policy versions. It defines the API endpoints, applies authentication guards, and delegates business logic to the `PoliciesService`. All endpoints are secured using `HybridAuthGuard` and require an `X-Organization-Id` header for session authentication or an `X-API-Key` for API key authentication.
All policy management API endpoints require authentication, either via session cookies with an `X-Organization-Id` header or via an API key.
Sources: [apps/api/src/policies/policies.controller.ts:1-25](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.controller.ts#L1-L25)
### Policies Service
The `PoliciesService` encapsulates the core business logic for policy operations. It interacts with the database (via Prisma), the `AttachmentsService` for S3 operations (e.g., storing/retrieving policy PDFs), and the `PolicyPdfRendererService` for generating PDF documents. It also contains logic for managing policy versions, handling status transitions, and ensuring data consistency.
Key responsibilities include:
* CRUD operations for policies.
* Managing policy versions (creation, update, deletion, publishing, activation, approval workflows).
* Generating and managing PDF representations of policies.
* Handling concurrent updates and unique constraint errors during versioning.
* Converting policy content (TipTap JSON) to plain text for AI processing.
Sources: [apps/api/src/policies/policies.service.ts:1-30](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L1-L30)
## Policy Data Model
Policies and their versions are stored with various attributes to manage their lifecycle and content.
### Policy Attributes
The `Policy` entity in the database includes the following key fields:
| Field Name | Type | Description |
| :----------------- | :------------------- | :----------------------------------------------------------------------- |
| `id` | `string` | Unique identifier for the policy. |
| `name` | `string` | Name of the policy. |
| `description` | `string` | Optional description of the policy. |
| `status` | `PolicyStatus` | Current status of the policy (`draft`, `published`, `needs_review`). |
| `content` | `unknown[]` (JSON) | Main content of the policy (TipTap JSON format). |
| `draftContent` | `unknown[]` (JSON) | Draft content, potentially different from `content` if changes are pending. |
| `frequency` | `Frequency` | How often the policy should be reviewed. |
| `department` | `Departments` | Department the policy applies to. |
| `isRequiredToSign` | `boolean` | Indicates if the policy requires employee acknowledgment. |
| `signedBy` | `string[]` | List of user IDs who have signed the policy. |
| `reviewDate` | `Date` | Next scheduled review date. |
| `isArchived` | `boolean` | Flag indicating if the policy is archived. |
| `createdAt` | `Date` | Timestamp of creation. |
| `updatedAt` | `Date` | Timestamp of last update. |
| `lastArchivedAt` | `Date` | Timestamp of last archival. |
| `lastPublishedAt` | `Date` | Timestamp of last publication. |
| `organizationId` | `string` | ID of the organization this policy belongs to. |
| `assigneeId` | `string` | ID of the member assigned to manage the policy. |
| `approverId` | `string` | ID of the member designated to approve the policy. |
| `policyTemplateId` | `string` | ID of the template used to create the policy. |
| `currentVersionId` | `string` | ID of the currently active/published policy version. |
| `pendingVersionId` | `string` | ID of the version currently awaiting approval. |
| `displayFormat` | `string` | Format for displaying the policy. |
| `pdfUrl` | `string` | URL to the policy's PDF in S3. |
Sources: [apps/api/src/policies/policies.service.ts:33-72](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L33-L72), [apps/api/src/policies/dto/create-policy.dto.ts:20-107](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/create-policy.dto.ts#L20-L107)
### Policy Version Attributes
Each policy can have multiple versions, tracked by the `PolicyVersion` entity:
| Field Name | Type | Description |
| :-------------- | :----------------- | :----------------------------------------------- |
| `id` | `string` | Unique identifier for the version. |
| `policyId` | `string` | ID of the parent policy. |
| `version` | `number` | Sequential version number (e.g., 1, 2, 3). |
| `content` | `unknown[]` (JSON) | Content of this specific version. |
| `pdfUrl` | `string` | URL to this version's PDF in S3. |
| `publishedById` | `string` | ID of the member who published this version. |
| `changelog` | `string` | Description of changes in this version. |
Sources: [apps/api/src/policies/policies.service.ts:394-400](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L394-L400)
### Enums
Several enums define the possible values for policy attributes:
```typescript
// apps/api/src/policies/dto/create-policy.dto.ts
export enum PolicyStatus {
DRAFT = 'draft',
PUBLISHED = 'published',
NEEDS_REVIEW = 'needs_review',
}
export enum Frequency {
MONTHLY = 'monthly',
QUARTERLY = 'quarterly',
YEARLY = 'yearly',
}
export enum Departments {
NONE = 'none',
ADMIN = 'admin',
GOV = 'gov',
HR = 'hr',
IT = 'it',
ITSM = 'itsm',
QMS = 'qms',
}
```
Sources: [apps/api/src/policies/dto/create-policy.dto.ts:10-18](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/create-policy.dto.ts#L10-L18), [apps/api/src/policies/dto/create-policy.dto.ts:20-25](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/create-policy.dto.ts#L20-L25), [apps/api/src/policies/dto/create-policy.dto.ts:27-35](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/create-policy.dto.ts#L27-L35)
### DTOs
Data Transfer Objects (DTOs) are used for request and response bodies, ensuring data validation and clear API contracts.
Sources: [apps/api/src/policies/dto/create-policy.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/create-policy.dto.ts), [apps/api/src/policies/dto/update-policy.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/update-policy.dto.ts), [apps/api/src/policies/dto/version.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/version.dto.ts), [apps/api/src/policies/dto/ai-suggest-policy.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/ai-suggest-policy.dto.ts)
## Key Functionality
### Policy CRUD Operations
The system supports standard Create, Read, Update, and Delete (CRUD) operations for policies.
* **Create Policy**: Initializes a new policy with its first draft version.
* **Get All Policies**: Retrieves a list of all policies for an organization.
* **Get Policy by ID**: Fetches details of a specific policy.
* **Update Policy**: Modifies policy metadata. Content updates are restricted if the policy is not in `draft` status, requiring a new version to be created.
* **Delete Policy**: Permanently removes a policy and all its associated versions and PDFs from S3.
Policy content cannot be directly updated if the policy is in `published` or `needs_review` status. To modify content, a new version must be created and then updated. This ensures an auditable history of changes.
Sources: [apps/api/src/policies/policies.service.ts:107-169](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L107-L169), [apps/api/src/policies/policies.service.ts:171-236](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L171-L236), [apps/api/src/policies/policies.service.ts:238-297](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L238-L297)
### Policy Versioning
A core feature is the ability to manage multiple versions of a policy, providing a complete audit trail and control over policy evolution.
* **Get Policy Versions**: Retrieves all versions for a given policy, ordered by version number.
* **Get Policy Version by ID**: Fetches a specific version's content and metadata.
* **Create Policy Version**: Creates a new draft version, typically based on the current active version or a specified source version. This process includes copying associated PDFs in S3.
* **Update Version Content**: Allows modification of the content for non-published, non-pending versions.
* **Delete Policy Version**: Removes a specific version, provided it is not the currently active or pending version.
* **Publish Policy Version**: Promotes draft content to a new published version, updating the policy's `lastPublishedAt` and `status`. It can optionally set the new version as active.
* **Set Active Policy Version**: Designates an existing version as the current active (published) version, updating the policy's main content and status. This also clears any pending approval states.
* **Submit Version for Approval**: Marks a specific version as `pending_review` and assigns an approver. This prevents direct editing or publishing until the approval process is complete.
Published and pending policy versions are immutable. Their content cannot be directly updated or deleted. To make changes, a new version must be created.
Sources: [apps/api/src/policies/policies.service.ts:300-330](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L300-L330), [apps/api/src/policies/policies.service.ts:332-361](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L332-L361), [apps/api/src/policies/policies.service.ts:363-447](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L363-L447), [apps/api/src/policies/policies.service.ts:449-485](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L449-L485), [apps/api/src/policies/policies.service.ts:487-529](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L487-L529), [apps/api/src/policies/policies.service.ts:531-597](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L531-L597), [apps/api/src/policies/policies.service.ts:599-633](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L599-L633), [apps/api/src/policies/policies.service.ts:635-689](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L635-L689)
### AI Policy Suggestion
The system includes an AI chat feature to assist users in editing and improving policies. Users can provide instructions, and the AI (powered by OpenAI's `gpt-5.1` model) will suggest changes, explain them, and provide the complete updated policy content in Markdown format.
```mermaid
sequenceDiagram
actor User
participant Client
participant PoliciesController
participant PoliciesService
participant OpenAI as AI Service
User->>Client: Enters AI chat for policy
Client->>PoliciesController: POST /policies/:id/ai-chat (AISuggestPolicyRequestDto)
PoliciesController->>PoliciesService: findById(policyId, orgId)
PoliciesService-->>PoliciesController: Policy details
PoliciesController->>PoliciesController: convertPolicyContentToText()
PoliciesController->>OpenAI: streamText(systemPrompt, messages)
OpenAI-->>PoliciesController: Streaming AI response (text/event-stream)
PoliciesController-->>Client: Streams AI response
Client->>User: Displays AI suggestions
```
Sources: [apps/api/src/policies/policies.controller.ts:316-407](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.controller.ts#L316-L407), [apps/api/src/policies/dto/ai-suggest-policy.dto.ts:1-30](https://github.com/blade47/comp/blob/main/apps/api/src/policies/dto/ai-suggest-policy.dto.ts#L1-L30)
### PDF Generation and Download
Policies can be rendered into PDF format, either individually or as a bundle of all published policies for an organization.
* **Download All Policies PDF**: Generates a single PDF document containing all currently published and unarchived policies for an organization. This PDF includes organization branding (name, primary color) and page numbering. It fetches existing PDFs from S3 or renders them on-the-fly if not available.
* The process involves:
1. Fetching organization details and all relevant policies.
2. Preparing policy PDFs in parallel (fetching from S3 or rendering from content).
3. Merging individual policy PDFs into a single `PDFDocument` sequentially.
4. Adding organizational headers, policy titles, and page numbers to the merged PDF.
5. Uploading the final PDF bundle to S3 and returning a presigned download URL.
Sources: [apps/api/src/policies/policies.service.ts:720-888](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L720-L888), [apps/api/src/policies/policies.service.ts:691-700](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L691-L700), [apps/api/src/policies/policies.service.ts:702-718](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.service.ts#L702-L718)
## API Endpoints
The following table summarizes the API endpoints exposed by the `PoliciesController`.
| Method | Path | Description | Service Method Called |
| :----- | :---------------------------------------- | :------------------------------------------- | :------------------------------ |
| `GET` | `/policies` | Get all policies | `policiesService.findAll` |
| `GET` | `/policies/download-all` | Download all published policies as PDF | `policiesService.downloadAllPoliciesPdf` |
| `GET` | `/policies/:id` | Get policy by ID | `policiesService.findById` |
| `POST` | `/policies` | Create a new policy | `policiesService.create` |
| `PATCH`| `/policies/:id` | Update policy | `policiesService.updateById` |
| `DELETE`| `/policies/:id` | Delete policy | `policiesService.deleteById` |
| `GET` | `/policies/:id/versions` | Get policy versions | `policiesService.getVersions` |
| `GET` | `/policies/:id/versions/:versionId` | Get policy version by ID | `policiesService.getVersionById`|
| `POST` | `/policies/:id/versions` | Create policy version | `policiesService.createVersion` |
| `PATCH`| `/policies/:id/versions/:versionId` | Update version content | `policiesService.updateVersionContent` |
| `DELETE`| `/policies/:id/versions/:versionId` | Delete policy version | `policiesService.deleteVersion` |
| `POST` | `/policies/:id/versions/publish` | Publish new policy version | `policiesService.publishVersion`|
| `POST` | `/policies/:id/versions/:versionId/activate` | Set active policy version | `policiesService.setActiveVersion` |
| `POST` | `/policies/:id/versions/:versionId/submit-for-approval` | Submit version for approval | `policiesService.submitForApproval` |
| `POST` | `/policies/:id/ai-chat` | Chat with AI about a policy | `policiesService.findById` |
Sources: [apps/api/src/policies/policies.controller.ts:27-360](https://github.com/blade47/comp/blob/main/apps/api/src/policies/policies.controller.ts#L27-L360)
---
## Technical docs: GET Get a browser automation by ID
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-getautomation
## Parameters
## Responses
## Try It
---
## Technical docs: Questionnaires
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/questionnaires
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts)
- [apps/api/src/questionnaire/questionnaire.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.controller.ts)
- [apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts)
- [apps/api/src/questionnaire/utils/questionnaire-storage.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts)
- [apps/api/src/questionnaire/utils/question-parser.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/question-parser.ts)
- [apps/api/src/questionnaire/questionnaire.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.module.ts)
- [apps/api/src/questionnaire/dto/auto-answer.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/auto-answer.dto.ts)
- [apps/api/src/questionnaire/dto/upload-and-parse.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/upload-and-parse.dto.ts)
- [apps/api/src/questionnaire/utils/deduplicate-sources.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/deduplicate-sources.ts)
- [apps/api/src/questionnaire/utils/export-generator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/export-generator.ts)
The Questionnaire module provides a comprehensive solution for processing, answering, and managing security questionnaires. It enables users to upload various file formats (e.g., PDF, Excel, CSV, images), automatically extract questions and answers using AI, generate answers based on an organization's knowledge base, and export the results in multiple formats.
This module is designed to streamline the often time-consuming process of responding to security questionnaires, leveraging advanced AI models and a robust data storage and retrieval system. It supports both internal users and external trust portal access for automated questionnaire processing.
## Architecture Overview
The Questionnaire module is built around a service-controller pattern, utilizing several utility modules for specific tasks such as content extraction, question parsing, storage, and export generation.
The core components include:
- `QuestionnaireController`: Handles incoming API requests, authentication, and response formatting.
- `QuestionnaireService`: Orchestrates the business logic, interacting with AI models, database, and S3 storage.
- `ContentExtractor`: Responsible for extracting raw content from various file types and performing initial AI-powered question/answer parsing.
- `QuestionnaireStorage`: Manages file uploads to S3 and persistence of questionnaire data (questions, answers, metadata) to the database.
- `ExportGenerator`: Creates export files in different formats (XLSX, CSV, PDF).
- `DeduplicateSources`: Utility for cleaning up and presenting RAG sources.
Sources:
[apps/api/src/questionnaire/questionnaire.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.controller.ts#L32-L36)
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L34-L46)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L43-L46)
[apps/api/src/questionnaire/utils/questionnaire-storage.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L22-L25)
[apps/api/src/questionnaire/utils/export-generator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/export-generator.ts#L17-L20)
## Questionnaire Parsing and Upload
The module supports uploading questionnaire files in various formats and extracting their content, then parsing questions and answers using AI.
### File Upload and Content Extraction
Users can upload files via dedicated API endpoints. The `QuestionnaireService` delegates the initial file handling to `QuestionnaireStorage` for S3 upload and then to `ContentExtractor` for processing.
The `ContentExtractor` module is central to this process. It identifies the file type and employs different strategies:
* **Excel (XLSX, XLS):** Uses `AdmZip` and `XLSX` libraries for raw content extraction, including custom logic to handle rich text and shared strings often missed by standard parsers.
* **CSV:** Simple text extraction.
* **Text:** Direct text extraction.
* **PDF and Images (PNG, JPG):** Leverages OpenAI's Vision API (`gpt-4o`) to extract text and structure from visual documents.
* **Word Documents (DOCX):** Currently not directly supported for parsing and advises conversion to PDF or image.
After raw content extraction, `extractQuestionsWithAI` orchestrates the AI-powered parsing:
* **Groq (`gpt-oss-120b`):** Primary and fastest model for parsing questions and answers from textual content, especially for Excel and CSV. It uses a chunking strategy for large files.
* **Claude (`claude-3-5-sonnet-latest`):** Fallback for Groq, offering excellent quality with a larger context window.
* **OpenAI (`gpt-4o-mini`):** Further fallback for general text parsing.
* **OpenAI Vision (`gpt-4o`):** Used specifically for PDF and image files.
The AI models are prompted to extract both traditional questions and form-style fields (e.g., "Vendor Name", "Contact Email") along with their corresponding answers.
Sources:
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L56-L63)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L43-L121)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L125-L132)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L140-L144)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L160-L200)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L204-L215)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L220-L245)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L250-L275)
### API Endpoints for Parsing and Upload
| Method | Endpoint | Description
The `question-parser.ts` file contains utilities for parsing questions and answers from content. While it defines interfaces and helper functions, the primary AI-powered parsing logic for the main questionnaire service flow is handled by `content-extractor.ts` through its `extractQuestionsWithAI` function, which internally utilizes various AI models.
Sources:
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L56-L63)
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L104-L110)
[apps/api/src/questionnaire/utils/content-extractor.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/content-extractor.ts#L125-L132)
| Method | Endpoint | Description
classDiagram
class QuestionnaireService {
+parseQuestionnaire(dto: ParseQuestionnaireDto): ParsedQuestionnaireResult
+autoAnswerAndExport(dto: ExportQuestionnaireDto): QuestionnaireExportResult
+uploadAndParse(dto: UploadAndParseDto): { questionnaireId: string; totalQuestions: number }
+answerSingleQuestion(dto: AnswerSingleQuestionDto): AnswerQuestionResult
+saveAnswer(dto: SaveAnswerDto): { success: boolean; error?: string }
+exportById(dto: ExportByIdDto): ExportResult
+deleteAnswer(dto: DeleteAnswerDto): { success: boolean; error?: string }
+saveGeneratedAnswerPublic(params: { questionnaireId: string; questionIndex: number; answer: string; sources?: AnswerQuestionResult['sources'] }): void
-generateAnswersForQuestions(questionsAndAnswers: QuestionnaireAnswer[], organizationId: string): QuestionnaireAnswer[]
}
class QuestionnaireController {
+parseQuestionnaire(dto: ParseQuestionnaireDto): ParsedQuestionnaireResult
+answerSingleQuestion(dto: AnswerSingleQuestionDto): any
+saveAnswer(dto: SaveAnswerDto): { success: boolean; error?: string }
+deleteAnswer(dto: DeleteAnswerDto): { success: boolean; error?: string }
+exportById(dto: ExportByIdDto, res: Response): Promise
+uploadAndParse(dto: UploadAndParseDto): { questionnaireId: string; totalQuestions: number }
+uploadAndParseUpload(file: Express.Multer.File, body: { organizationId: string; source?: 'internal' | 'external' }): any
+parseQuestionnaireUpload(file: Express.Multer.File, body: { organizationId: string; format?: 'pdf' | 'csv' | 'xlsx'; source?: 'internal' | 'external' }, res: Response): Promise
+parseQuestionnaireUploadByToken(file: Express.Multer.File, token: string, body: { format?: 'pdf' | 'csv' | 'xlsx' }, res: Response): Promise
+autoAnswerAndExport(dto: ExportQuestionnaireDto, res: Response): Promise
+autoAnswerAndExportUpload(file: Express.Multer.File, body: { organizationId: string; format?: 'pdf' | 'csv' | 'xlsx' }, res: Response): Promise
+autoAnswer(dto: AutoAnswerDto, res: Response): Promise
}
class ParseQuestionnaireDto {
+vendorName?: string
+fileName?: string
+fileType: string
+fileData: string
}
class ExportQuestionnaireDto {
+organizationId: string
+fileData: string
+fileType: string
+fileName?: string
+vendorName?: string
+format: 'pdf' | 'csv' | 'xlsx'
+source?: 'internal' | 'external'
+exportInAllExtensions?: boolean
}
class AnswerSingleQuestionDto {
+questionnaireId: string
+organizationId: string
+question: string
+questionIndex: number
+totalQuestions: number
}
class AutoAnswerDto {
+organizationId: string
+questionnaireId?: string
+questionsAndAnswers: AutoAnswerQuestionDto[]
}
class AutoAnswerQuestionDto {
+question: string
+answer?: string | null
+_originalIndex?: number
}
class SaveAnswerDto {
+questionnaireId: string
+organizationId: string
+questionAnswerId?: string
+questionIndex?: number
+answer?: string | null
+status: 'manual' | 'generated'
+sources?: any
}
class DeleteAnswerDto {
+questionnaireId: string
+organizationId: string
+questionAnswerId: string
}
class UploadAndParseDto {
+organizationId: string
+fileName: string
+fileType: string
+fileData: string
+source?: 'internal' | 'external'
}
class ExportByIdDto {
+questionnaireId: string
+organizationId: string
+format: 'pdf' | 'csv' | 'xlsx'
}
class QuestionnaireAnswer {
+question: string
+answer: string | null
+sources?: any
}
class ParsedQuestionnaireResult {
+vendorName?: string
+fileName?: string
+totalQuestions: number
+questionsAndAnswers: QuestionnaireAnswer[]
}
class QuestionnaireExportResult {
+fileBuffer: Buffer
+mimeType: string
+filename: string
+questionsAndAnswers: QuestionnaireAnswer[]
}
QuestionnaireController --|> QuestionnaireService : uses
QuestionnaireService ..> ParseQuestionnaireDto
QuestionnaireService ..> ExportQuestionnaireDto
QuestionnaireService ..> AnswerSingleQuestionDto
QuestionnaireService ..> SaveAnswerDto
QuestionnaireService ..> DeleteAnswerDto
QuestionnaireService ..> UploadAndParseDto
QuestionnaireService ..> ExportByIdDto
QuestionnaireService ..> QuestionnaireAnswer
QuestionnaireService ..> ParsedQuestionnaireResult
QuestionnaireService ..> QuestionnaireExportResult
AutoAnswerDto o-- AutoAnswerQuestionDto : contains
```
Sources:
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L34-L46)
[apps/api/src/questionnaire/questionnaire.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.controller.ts#L32-L36)
[apps/api/src/questionnaire/dto/parse-questionnaire.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/parse-questionnaire.dto.ts)
[apps/api/src/questionnaire/dto/export-questionnaire.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/export-questionnaire.dto.ts)
[apps/api/src/questionnaire/dto/answer-single-question.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/answer-single-question.dto.ts)
[apps/api/src/questionnaire/dto/auto-answer.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/auto-answer.dto.ts)
[apps/api/src/questionnaire/dto/save-answer.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/save-answer.dto.ts)
[apps/api/src/questionnaire/dto/delete-answer.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/delete-answer.dto.ts)
[apps/api/src/questionnaire/dto/upload-and-parse.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/upload-and-parse.dto.ts)
[apps/api/src/questionnaire/dto/export-by-id.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/dto/export-by-id.dto.ts)
## Automatic Answering
The module provides robust capabilities for automatically generating answers to questionnaire questions using a Retrieval Augmented Generation (RAG) approach.
### Answer Generation Process
The `QuestionnaireService` orchestrates the automatic answering process:
1. **Sync Organization Embeddings:** Before generating answers, the system ensures that the organization's knowledge base (policies, manual answers, etc.) is synchronized with the vector store. This step is crucial for accurate retrieval.
2. **Batch Search:** For efficiency, questions are batched, and a single call to `findSimilarContentBatch` is made to retrieve relevant context from the vector store for all questions simultaneously.
3. **Answer Generation:** For each question, `generateAnswerFromContent` is called, which uses the retrieved context and an AI model (e.g., `generateAnswerWithRAGBatch` in the service) to formulate an answer.
4. **Save Answer:** Generated answers, along with their sources, are saved to the database.
5. **Update Answered Count:** The total count of answered questions for the questionnaire is updated.
### Streaming Auto-Answer (SSE)
The `autoAnswer` endpoint in the `QuestionnaireController` provides a Server-Sent Events (SSE) stream to give real-time feedback on the answer generation progress.
### Establish SSE Connection
The controller sets up SSE headers and creates a safe sender function to stream events back to the client.
### Sync Embeddings
The `QuestionnaireService` first attempts to synchronize the organization's embeddings to ensure the vector store is up-to-date. Warnings are logged if this fails, but the process continues.
### Filter Unanswered Questions
The incoming list of questions is filtered to identify those that still require an answer.
### Batch Context Search
A progress event (`type: 'progress'`, `phase: 'searching'`) is sent. The system then performs a batch search (`findSimilarContentBatch`) against the vector store to retrieve relevant content for all unanswered questions. This is a critical optimization to reduce latency.
### Parallel Answer Generation
Another progress event (`type: 'progress'`, `phase: 'generating'`) is sent. Answers are then generated in parallel for each question using the pre-fetched content (`generateAnswerFromContent`).
### Stream Individual Answers
As each answer is generated, an `answer` event is streamed back to the client, including the question, generated answer, sources, and success status.
### Save Generated Answers
Each generated answer is persisted to the database via `saveGeneratedAnswerPublic` in the `QuestionnaireService`.
### Complete Stream
Once all questions are processed, a `complete` event is sent, summarizing the total and answered questions. The SSE connection is then closed.
Sources:
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L225-L269)
[apps/api/src/questionnaire/questionnaire.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.controller.ts#L368-L476)
[apps/api/src/vector-store/lib/index.ts](https://github.com/blade47/comp/blob/main/apps/api/src/vector-store/lib/index.ts)
[apps/api/src/trigger/questionnaire/answer-question-helpers.ts](https://github.com/blade47/comp/blob/main/apps/api/src/trigger/questionnaire/answer-question-helpers.ts)
```mermaid
sequenceDiagram
actor Client
participant Controller as QuestionnaireController
participant Service as QuestionnaireService
participant VectorStore as Vector Store
participant AI as AI Models
participant DB as Database
participant S3 as S3 Storage
Client->>Controller: POST /auto-answer (AutoAnswerDto)
Controller->>Controller: setupSSEHeaders()
Controller->>Service: syncOrganizationEmbeddings(orgId)
Service->>VectorStore: syncOrganizationEmbeddings(orgId)
VectorStore-->>Service: Sync Status
Service-->>Controller:
Controller->>Client: SSE: progress (phase: searching)
Controller->>VectorStore: findSimilarContentBatch(questions, orgId)
VectorStore-->>Controller: allSimilarContent[]
Controller->>Client: SSE: progress (phase: generating)
loop For each question
Controller->>AI: generateAnswerFromContent(question, similarContent)
AI-->>Controller: AnswerResult {answer, sources}
Controller->>Service: saveGeneratedAnswerPublic(questionnaireId, questionIndex, answer, sources)
Service->>DB: Update questionnaireQuestionAnswer
DB-->>Service:
Service->>QuestionnaireStorage: updateAnsweredCount(questionnaireId)
QuestionnaireStorage->>DB: Update questionnaire.answeredQuestions
DB-->>QuestionnaireStorage:
QuestionnaireStorage-->>Service:
Service-->>Controller:
Controller->>Client: SSE: answer {questionIndex, answer, sources}
end
Controller->>Client: SSE: complete {total, answered, answers}
Controller->>Client: Close SSE connection
```
Sources:
[apps/api/src/questionnaire/questionnaire.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.controller.ts#L368-L476)
[apps/api/src/questionnaire/questionnaire.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/questionnaire.service.ts#L225-L269)
[apps/api/src/vector-store/lib/index.ts](https://github.com/blade47/comp/blob/main/apps/api/src/vector-store/lib/index.ts)
[apps/api/src/trigger/questionnaire/answer-question-helpers.ts](https://github.com/blade47/comp/blob/main/apps/api/src/trigger/questionnaire/answer-question-helpers.ts)
[apps/api/src/questionnaire/utils/questionnaire-storage.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L22-L36)
### DTOs for Auto-Answering
| DTO Class | Description
```
---
## Technical docs: PATCH Update a browser automation
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-updateautomation
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: DELETE Delete a browser automation
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-deleteautomation
## Parameters
## Responses
## Try It
---
## Technical docs: Knowledge Base
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/knowledge-base
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/knowledge-base/knowledge-base.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts)
- [apps/api/src/knowledge-base/knowledge-base.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts)
- [apps/api/src/knowledge-base/utils/s3-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts)
- [apps/api/src/knowledge-base/utils/constants.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/constants.ts)
- [apps/api/src/knowledge-base/knowledge-base.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.module.ts)
- [apps/api/src/knowledge-base/dto/process-documents.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/dto/process-documents.dto.ts)
- [apps/api/src/knowledge-base/dto/upload-document.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/dto/upload-document.dto.ts)
The Knowledge Base module provides a robust system for managing organizational documents and manual answers, integrating file storage, database persistence, and asynchronous processing capabilities. It allows users to upload, list, retrieve, and delete documents, as well as manage manual answers, with support for vector database integration for intelligent search and retrieval. The module leverages Amazon S3 for secure file storage and Trigger.dev for orchestrating background processing tasks.
This module is designed to handle various aspects of knowledge management, from initial document ingestion to their eventual processing and deletion, ensuring data consistency across storage, database, and vector store components.
## Architecture Overview
The Knowledge Base module follows a standard NestJS architecture, with a Controller handling incoming API requests and a Service encapsulating the business logic. It interacts with external services such as S3 for file storage and Trigger.dev for asynchronous task orchestration, and persists metadata in a database.
Sources: [apps/api/src/knowledge-base/knowledge-base.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.module.ts), [apps/api/src/knowledge-base/knowledge-base.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts), [apps/api/src/knowledge-base/knowledge-base.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts)
## API Endpoints
The `KnowledgeBaseController` exposes a set of RESTful API endpoints for interacting with knowledge base documents and manual answers.
| Method | Endpoint | Description | DTO / Parameters
The Knowledge Base module is a core component that manages the storage, processing, and retrieval of documents and manual answers for an organization. It integrates with Snbsp;S3 for file storage and Trigger.dev for asynchronous processing, providing a robust system for knowledge management.
## Architecture Overview
The Knowledge Base module follows a standard NestJS architecture, with a Controller handling incoming API requests and a Service encapsulating the business logic. It interacts with external services such as S3 for file storage and Trigger.dev for asynchronous task orchestration, and persists metadata in a database.
Sources: [apps/api/src/knowledge-base/knowledge-base.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.module.ts), [apps/api/src/knowledge-base/knowledge-base.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts), [apps/api/src/knowledge-base/knowledge-base.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts)
## API Endpoints
The `KnowledgeBaseController` exposes a set of RESTful API endpoints for interacting with knowledge base documents and manual answers.
| Method | Endpoint | Description | DTO / Parameters
Sources: [apps/api/src/knowledge-base/knowledge-base.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts), [apps/api/src/knowledge-base/knowledge-base.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts), [apps/api/src/knowledge-base/utils/s3-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts)
### Listing Documents
The `listDocuments` endpoint retrieves all knowledge base documents associated with a given `organizationId`. It fetches document metadata from the database, including `id`, `name`, `description`, `s3Key`, `fileType`, `fileSize`, `processingStatus`, `createdAt`, and `updatedAt`.
```typescript
async listDocuments(organizationId: string) {
return db.knowledgeBaseDocument.findMany({
where: { organizationId },
select: {
id: true,
name: true,
description: true,
s3Key: true,
fileType: true,
fileSize: true,
processingStatus: true,
createdAt: true,
updatedAt: true,
},
orderBy: { createdAt: 'desc' },
});
}
```
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:18-32](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L18-L32), [apps/api/src/knowledge-base/knowledge-base.controller.ts:40-59](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L40-L59)
### Getting Signed URLs
The module provides functionality to generate time-limited, signed URLs for both downloading and viewing documents directly in a browser.
#### Download URL
The `getDownloadUrl` method generates a signed URL that forces the browser to download the file.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:51-64](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L51-L64), [apps/api/src/knowledge-base/knowledge-base.controller.ts:76-92](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L76-L92)
#### View URL
The `getViewUrl` method generates a signed URL intended for inline viewing in the browser. It also indicates whether the file type is generally viewable in a browser based on predefined MIME types.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:66-82](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L66-L82), [apps/api/src/knowledge-base/knowledge-base.controller.ts:94-115](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L94-L115), [apps/api/src/knowledge-base/utils/constants.ts:30-32](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/constants.ts#L30-L32)
### Deleting Documents
Deleting a document involves multiple steps to ensure consistency across S3, the database, and any associated vector store embeddings. This process is partially asynchronous to avoid blocking the API response.
```mermaid
sequenceDiagram
actor Client
participant Controller as KBController
participant Service as KBService
participant DB as Database
participant S3 as S3 Storage
participant Trigger as Trigger.dev
Client->>Controller: POST /documents/:id/delete { orgId }
Controller->>Service: deleteDocument(dto)
Service->>DB: findUnique(documentId, orgId)
DB-->>Service: document
Service->>Trigger: triggerVectorDeletion(documentId, orgId)
Trigger-->>Service: runId (async)
Service->>Trigger: createRunReadToken(runId)
Trigger-->>Service: publicAccessToken
Service->>S3: deleteFromS3(s3Key)
S3-->>Service: s3Deleted (boolean)
Service->>DB: delete(documentId)
DB-->>Service: deleteResult
Service-->>Controller: { success, vectorDeletionRunId, publicAccessToken }
Controller-->>Client: 200 OK
```
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:84-122](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L84-L122), [apps/api/src/knowledge-base/knowledge-base.controller.ts:117-142](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L117-L142), [apps/api/src/knowledge-base/utils/s3-operations.ts:130-143](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts#L130-L143)
### Processing Documents
The `processDocuments` endpoint initiates the asynchronous processing of one or more knowledge base documents. This typically involves extracting content and generating vector embeddings for search. The service intelligently dispatches either a single document processing task or an orchestrator task for multiple documents.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:124-154](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L124-L154), [apps/api/src/knowledge-base/knowledge-base.controller.ts:144-162](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L144-L162), [apps/api/src/knowledge-base/dto/process-documents.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/dto/process-documents.dto.ts)
Document processing tasks are handled asynchronously by Trigger.dev. The API immediately returns a `runId` and a `publicAccessToken` which can be used to monitor the status of the background job.
## Manual Answer Management
The module also supports the management of "manual answers," which are likely pre-defined responses or knowledge entries.
### Deleting a Single Manual Answer
The `deleteManualAnswer` method removes a specific manual answer from the system. It triggers an asynchronous task to delete associated vector embeddings before removing the record from the main database.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:175-199](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L175-L199), [apps/api/src/knowledge-base/knowledge-base.controller.ts:182-196](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L182-L196)
### Deleting All Manual Answers
The `deleteAllManualAnswers` method allows for the bulk deletion of all manual answers belonging to a specific organization. It first identifies all relevant manual answer IDs and then triggers an orchestrator task for batch vector deletion before removing all records from the database.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:201-229](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L201-L229), [apps/api/src/knowledge-base/knowledge-base.controller.ts:198-207](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L198-L207)
## S3 Operations
The `s3-operations.ts` utility file provides core functions for interacting with Amazon S3, specifically for the knowledge base bucket.
### Key Functions
| Function Name | Description ```mermaid
erDiagram
knowledgeBaseDocument {
string id
string name
string description
string s3Key
string fileType
number fileSize
string processingStatus
string createdAt
string updatedAt
string organizationId
}
securityQuestionnaireManualAnswer {
string id
string organizationId
}
organization ||--o{ knowledgeBaseDocument : "manages"
organization ||--o{ securityQuestionnaireManualAnswer : "manages"
```
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:18-32](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L18-L32), [apps/api/src/knowledge-base/knowledge-base.service.ts:175-181](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L175-L181)
## Constants and Utilities
The `constants.ts` file defines various configuration parameters and helper functions used across the Knowledge Base module.
| Constant/Function Name | Description
Sources: [apps/api/src/knowledge-base/knowledge-base.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts), [apps/api/src/knowledge-base/knowledge-base.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts), [apps/api/src/knowledge-base/utils/s3-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts)
### Listing Documents
The `listDocuments` endpoint retrieves all knowledge base documents associated with a given `organizationId`. It fetches document metadata from the database, including `id`, `name`, `description`, `s3Key`, `fileType`, `fileSize`, `processingStatus`, `createdAt`, and `updatedAt`.
```typescript
async listDocuments(organizationId: string) {
return db.knowledgeBaseDocument.findMany({
where: { organizationId },
select: {
id: true,
name: true,
description: true,
s3Key: true,
fileType: true,
fileSize: true,
processingStatus: true,
createdAt: true,
updatedAt: true,
},
orderBy: { createdAt: 'desc' },
});
}
```
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:18-32](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L18-L32), [apps/api/src/knowledge-base/knowledge-base.controller.ts:40-59](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L40-L59)
### Getting Signed URLs
The module provides functionality to generate time-limited, signed URLs for both downloading and viewing documents directly in a browser.
#### Download URL
The `getDownloadUrl` method generates a signed URL that forces the browser to download the file.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:51-64](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L51-L64), [apps/api/src/knowledge-base/knowledge-base.controller.ts:76-92](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L76-L92)
#### View URL
The `getViewUrl` method generates a signed URL intended for inline viewing in the browser. It also indicates whether the file type is generally viewable in a browser based on predefined MIME types.
Sources: [apps/api/src/knowledge-base/knowledge-base.service.ts:66-82](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.service.ts#L66-L82), [apps/api/src/knowledge-base/knowledge-base.controller.ts:94-115](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/knowledge-base.controller.ts#L94-L115), [apps/api/src/knowledge-base/utils/constants.ts:30-32](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/constants.ts#L30-L32)
### Deleting Documents
Deleting a document involves multiple
```
---
## Technical docs: Risk Management
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/risk-management
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/risks/schemas/risk-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/schemas/risk-operations.ts)
- [apps/api/src/risks/dto/create-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/create-risk.dto.ts)
- [apps/api/src/risks/risks.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.controller.ts)
- [apps/api/src/risks/risks.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.service.ts)
- [apps/api/src/risks/dto/update-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/update-risk.dto.ts)
- [apps/api/src/risks/risks.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.module.ts)
The Risk Management module provides a robust API for organizations to manage their identified risks. It enables users to create, retrieve, update, and delete risk records, associating them with specific organizations and assignees. The module is designed with a clear separation of concerns, utilizing a controller for API exposure, a service for business logic, and Data Transfer Objects (DTOs) for data validation and schema definition.
This system supports both API key and session-based authentication, ensuring secure access to risk data for the authenticated organization. It integrates with a database to persist risk information, including details like title, description, category, status, likelihood, impact, and treatment strategies.
## Architecture Overview
The Risk Management module is built using the NestJS framework, following a modular architecture. It consists of a `RisksModule` that encapsulates the `RisksController` and `RisksService`, along with Data Transfer Objects (`CreateRiskDto`, `UpdateRiskDto`) for defining the structure of incoming and outgoing data.
The `RisksModule` imports the `AuthModule` to leverage authentication mechanisms and provides the `RisksService` to handle all business logic related to risks. The `RisksController` exposes the API endpoints, delegating complex operations to the `RisksService`.
Sources: [apps/api/src/risks/risks.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.module.ts#L1-L10), [apps/api/src/risks/risks.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.controller.ts#L1-L60), [apps/api/src/risks/risks.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.service.ts#L1-L10), [apps/api/src/risks/dto/create-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/create-risk.dto.ts#L1-L89), [apps/api/src/risks/dto/update-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/update-risk.dto.ts#L1-L4)
## API Endpoints
The `RisksController` exposes a RESTful API for managing risks. All endpoints are protected by the `HybridAuthGuard` and require either an API key or session authentication with an `X-Organization-Id` header. Swagger documentation is integrated, providing detailed operation summaries, descriptions, parameters, and response schemas.
All API endpoints require authentication. This can be achieved via an `X-API-Key` header for API key authentication or a combination of session cookies and an `X-Organization-Id` header for session authentication.
### Available Endpoints
| Method | Path | Operation Summary | Description Risk Management is a crucial module within the system, designed to help organizations identify, assess, and manage risks effectively. It provides a comprehensive set of functionalities to track risks, assign responsibilities, monitor their status, and define treatment strategies. The module supports various risk attributes, including categories, departments, likelihood, impact, and residual risk, facilitating a structured approach to risk management.
Sources: [apps/api/src/risks/schemas/risk-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/schemas/risk-operations.ts#L1-L30), [apps/api/src/risks/dto/create-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/create-risk.dto.ts#L1-L89)
### API Request Flow Example: Creating a Risk
The following sequence diagram illustrates the typical flow for creating a new risk through the API.
```mermaid
sequenceDiagram
actor Client
participant Controller as RisksController
participant Service as RisksService
participant DB as Database
Client->>Controller: POST /risks (CreateRiskDto)
activate Controller
Controller->>Service: create(organizationId, createRiskDto)
activate Service
Service->>DB: db.risk.create(data: createRiskDto + organizationId)
activate DB
DB-->>Service: New Risk Record (id, title, etc.)
deactivate DB
Service-->>Controller: Created Risk Object
deactivate Service
Controller-->>Client: 201 Created (Risk Object + Auth Context)
deactivate Controller
```
Sources: [apps/api/src/risks/risks.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.controller.ts#L80-L99), [apps/api/src/risks/risks.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.service.ts#L66-L82)
## Data Transfer Objects (DTOs)
Data Transfer Objects (DTOs) are used to define the structure and validation rules for data exchanged between the client and the API.
### `CreateRiskDto`
This DTO defines the required and optional fields for creating a new risk. It includes validation decorators (`@IsString`, `@IsNotEmpty`, `@IsOptional`, `@IsEnum`) to ensure data integrity.
| Field | Type | Required | Description | Example |
| :--------------------------- | :--------------- | :------- | :------------------------------------------------------------------------ | :---------------------------------------------------------------------- |
| `title` | `string` | Yes | Risk title | `Data breach vulnerability in user authentication system` |
| `description` | `string` | Yes | Detailed description of the risk | `Weak password requirements could lead to unauthorized access` |
| `category` | `RiskCategory` | Yes | Risk category (e.g., `technology`, `financial`) | `RiskCategory.technology` |
| `department` | `Departments?` | No | Department responsible for the risk (e.g., `it`, `hr`) | `Departments.it` |
| `status` | `RiskStatus?` | No | Current status of the risk (e.g., `open`, `closed`) | `RiskStatus.open` |
| `likelihood` | `Likelihood?` | No | Likelihood of the risk occurring (e.g., `possible`, `unlikely`) | `Likelihood.possible` |
| `impact` | `Impact?` | No | Impact if the risk materializes (e.g., `major`, `minor`) | `Impact.major` |
| `residualLikelihood` | `Likelihood?` | No | Residual likelihood after treatment | `Likelihood.unlikely` |
| `residualImpact` | `Impact?` | No | Residual impact after treatment | `Impact.minor` |
| `treatmentStrategyDescription` | `string?` | No | Description of the treatment strategy | `Implement multi-factor authentication` |
| `treatmentStrategy` | `RiskTreatmentType?` | No | Risk treatment strategy (e.g., `mitigate`, `accept`) | `RiskTreatmentType.mitigate` |
| `assigneeId` | `string?` | No | ID of the user assigned to this risk (e.g., `mem_abc123def456`) | `mem_abc123def456` |
Sources: [apps/api/src/risks/dto/create-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/create-risk.dto.ts#L1-L89)
### `UpdateRiskDto`
The `UpdateRiskDto` extends `PartialType(CreateRiskDto)`. This means all fields inherited from `CreateRiskDto` become optional, allowing for partial updates of a risk record.
```typescript
import { PartialType } from '@nestjs/swagger';
import { CreateRiskDto } from './create-risk.dto';
export class UpdateRiskDto extends PartialType(CreateRiskDto) {}
```
Sources: [apps/api/src/risks/dto/update-risk.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/dto/update-risk.dto.ts#L1-L4)
## Service Layer Logic (`RisksService`)
The `RisksService` encapsulates the core business logic for managing risks. It interacts directly with the database (`db.risk`) to perform CRUD operations and includes error handling and logging.
### Key Methods
* `findAllByOrganization(organizationId: string)`: Retrieves all risks associated with a given organization ID, ordered by creation date. It includes assignee user details.
* `findById(id: string, organizationId: string)`: Fetches a specific risk by its ID and organization ID. Throws `NotFoundException` if the risk does not exist or does not belong to the specified organization.
* `create(organizationId: string, createRiskDto: CreateRiskDto)`: Creates a new risk record in the database, associating it with the provided organization ID.
* `updateById(id: string, organizationId: string, updateRiskDto: UpdateRiskDto)`: Updates an existing risk. It first verifies the risk's existence and ownership using `findById` before performing the update.
* `deleteById(id: string, organizationId: string)`: Deletes a risk. Similar to `updateById`, it first validates the risk's existence and ownership.
### Update/Delete Risk Flow
The `updateById` and `deleteById` methods in `RisksService` follow a common pattern of first verifying the existence and ownership of a risk before proceeding with the modification or deletion.
Sources: [apps/api/src/risks/risks.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/risks.service.ts#L12-L130)
---
## Technical docs: POST Start automation with live view
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-startautomationlive
## Parameters
## Responses
## Try It
---
## Technical docs: POST Execute automation on existing session
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-executeautomationonsession
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: SOA Workflow
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/soa-workflow
Relevant source files
The following files were used as context for generating this wiki page:
- No specific source files were provided for this topic. The content is generated based solely on the provided topic description: "Explains Statement of Applicability features including setup, answer generation, parsing, approval, submission, storage, and ISO configuration transforms."
This page details the Statement of Applicability (SOA) Workflow, outlining the key stages involved in managing an organization's compliance posture against various standards, particularly focusing on ISO configurations. The workflow encompasses the entire lifecycle from initial setup and data generation to final approval, submission, and secure storage of SOA artifacts. It also highlights the critical role of parsing and transformation mechanisms to ensure data integrity and compatibility with ISO standards.
The SOA Workflow is designed to streamline the process of assessing, documenting, and maintaining an organization's applicability to security controls. By breaking down the process into distinct, manageable phases, it ensures accuracy, facilitates collaboration, and provides a clear audit trail for compliance activities.
## Overview of the SOA Workflow
The SOA Workflow orchestrates a series of interconnected processes to manage the Statement of Applicability. It begins with foundational setup, proceeds through automated or semi-automated data generation and parsing, moves into human-centric approval, and concludes with formal submission and archival. A key aspect is the integration of ISO configuration transforms, ensuring that the generated SOA aligns with relevant international standards.
The following diagram illustrates the high-level flow of the SOA Workflow:
Sources: [Topic Description]
## Setup
The setup phase involves configuring the environment and parameters necessary for the SOA workflow to function correctly. This typically includes defining the scope of the Statement of Applicability, selecting relevant control frameworks (e.g., ISO 27001, NIST), and configuring user roles and permissions for various stages of the workflow. Initial data sources, such as existing asset inventories or policy documents, may also be integrated during this phase.
Proper initial setup is crucial for the accuracy and efficiency of the entire SOA workflow. Misconfigurations at this stage can lead to incorrect applicability assessments or compliance gaps.
### Key Setup Elements
* **Control Framework Selection:** Identifying the specific standards (e.g., ISO 27001:2022) against which the SOA will be generated.
* **Organizational Scope:** Defining the boundaries of the assessment, including departments, systems, and data in scope.
* **User & Role Management:** Assigning responsibilities for answer generation, review, and approval.
* **Integration Points:** Configuring connections to other systems that provide input data or consume SOA outputs.
Sources: [Topic Description]
## Answer Generation
Answer generation is the process where responses to control questions or applicability statements are collected. This can involve automated data collection from integrated systems, manual input from subject matter experts, or a combination of both. The goal is to gather comprehensive and accurate information regarding the implementation status and applicability of each control within the defined scope.
### Methods of Answer Generation
* **Automated Data Collection:** Pulling data from configuration management databases (CMDBs), security tools, or other IT systems to automatically answer control questions.
* **Questionnaires & Surveys:** Distributing structured questionnaires to relevant stakeholders for manual input.
* **Interview & Workshops:** Facilitating discussions with experts to gather qualitative data and insights.
Sources: [Topic Description]
## Parsing
Once answers are generated, the parsing stage involves processing and interpreting the collected data. This ensures that the information is in a consistent, structured format suitable for further analysis, reporting, and transformation. Parsing may include data validation, normalization, and extraction of key attributes from various input formats.
### Data Validation
Check generated answers against predefined rules, data types, and constraints to ensure accuracy and completeness. This might involve checking for mandatory fields, valid date formats, or acceptable value ranges.
### Data Normalization
Transforming data into a standardized format. For example, converting different representations of "yes/no" (e.g., "Y", "True", "1") into a single, consistent value.
### Attribute Extraction
Identifying and extracting specific pieces of information from free-text fields or complex data structures for structured storage and analysis.
Sources: [Topic Description]
## Approval
The approval phase is a critical human-centric step where generated and parsed SOA data is reviewed by designated stakeholders. This typically involves compliance officers, risk managers, and senior management. Approvers verify the accuracy, completeness, and appropriateness of the applicability statements and control implementation details before the SOA can be formally submitted.
### Approval Process
The approval process often follows a multi-level hierarchy, ensuring that all relevant parties have signed off on the Statement of Applicability.
```mermaid
sequenceDiagram
actor Stakeholder
participant System as SOA System
participant Reviewer as Compliance Officer
participant Approver as Senior Management
Stakeholder->>System: Submit Generated Answers
System->>Reviewer: Notify for Review (Parsed SOA Data)
Reviewer->>System: Review SOA Data
alt Reviewer Approves
Reviewer->>System: Approve Review
System->>Approver: Notify for Final Approval
Approver->>System: Review SOA Data
alt Approver Approves
Approver->>System: Final Approval
System-->>Stakeholder: SOA Approved
else Approver Rejects
Approver->>System: Request Revisions (with feedback)
System-->>Reviewer: Notify for Revisions
Reviewer->>Stakeholder: Request Revisions
end
else Reviewer Rejects
Reviewer->>System: Request Revisions (with feedback)
System-->>Stakeholder: Notify for Revisions
end
```
Sources: [Topic Description]
## Submission
Upon successful approval, the SOA is formally submitted. This typically means making the final, approved document available to relevant internal and external parties. For internal purposes, it might be published to a central compliance repository. For external audits or certifications, it would be provided to auditors or certification bodies.
### Submission Channels
* **Internal Compliance Portal:** Publishing the SOA for internal stakeholders.
* **Auditor Portals:** Uploading the SOA to secure platforms provided by external auditors.
* **Certification Bodies:** Submitting the SOA as part of an ISO certification process.
Sources: [Topic Description]
## Storage
The storage phase involves securely archiving the approved and submitted SOA documents and associated data. This ensures that historical records are maintained for audit purposes, future reference, and continuous improvement. Storage solutions must comply with data retention policies and security requirements.
### Storage Considerations
| Aspect | Description |
| :-------------- | :-------------------------------------------------------------------------- |
| **Security** | Encrypted storage, access controls, and regular backups. |
| **Retention** | Adherence to legal and regulatory data retention periods. |
| **Accessibility** | Easy retrieval for authorized personnel during audits or reviews. |
| **Integrity** | Mechanisms to ensure the data remains unaltered and authentic over time. |
| **Versioning** | Ability to track changes and retrieve previous versions of the SOA. |
Sources: [Topic Description]
## ISO Configuration Transforms
A critical feature of the SOA Workflow is the ability to perform ISO configuration transforms. This involves converting or mapping the generated and approved SOA data into a format that aligns precisely with specific ISO standards (e.g., ISO 27001 Annex A controls). These transforms ensure that the SOA is not just a generic statement but a tailored document that directly addresses the requirements of the chosen ISO framework.
### Purpose of Transforms
* **Standard Alignment:** Ensuring the SOA directly maps to ISO control objectives and controls.
* **Reporting:** Generating reports that are compliant with ISO documentation requirements.
* **Audit Readiness:** Providing auditors with a clear, ISO-specific view of the organization's applicability.
* **Interoperability:** Facilitating the exchange of SOA data with other ISO-compliant systems or tools.
An organization might have internal controls like "Access Control Policy" and "User Account Management Procedure." An ISO configuration transform would map these to specific ISO 27001:2022 Annex A controls, such as A.5.1 (Policies for information security), A.5.15 (Access control), and A.5.16 (Identity management). This ensures that the SOA explicitly states how the organization meets each ISO requirement.
Sources: [Topic Description]
---
## Technical docs: People Directory
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/people-directory
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/people/people.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.service.ts)
- [apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts)
- [apps/api/src/people/utils/member-queries.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/utils/member-queries.ts)
- [apps/api/src/people/utils/member-validator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/utils/member-validator.ts)
- [apps/api/src/people/dto/create-people.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/dto/create-people.dto.ts)
- [apps/api/src/people/dto/bulk-create-people.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/dto/bulk-create-people.dto.ts)
- [apps/api/src/people/people.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.module.ts)
The People Directory module provides a robust API for managing members within an organization. It allows for the creation, retrieval, updating, and deletion of individual members, as well as bulk creation operations. This module integrates user management with device management capabilities, enabling the association of members with devices and the ability to unlink or remove specific hosts.
Designed as a NestJS module, it follows a clear separation of concerns, with a controller handling API requests, a service encapsulating business logic, and dedicated utility classes for database queries and data validation. This structure ensures maintainability, scalability, and adherence to best practices for API development.
## Architecture Overview
The People Directory module is structured around the standard NestJS pattern of Controllers, Services, and Modules, augmented by specialized utility classes for database interactions and validation. This layered architecture ensures that business logic is decoupled from HTTP concerns and data access.
- **`PeopleController`**: Handles incoming HTTP requests, routes them to the appropriate service methods, and formats responses.
- **`PeopleService`**: Contains the core business logic for member management, orchestrating interactions with validators, query helpers, and external services like `FleetService`.
- **`MemberValidator`**: Provides methods to validate the existence of organizations, users, and member relationships, ensuring data integrity before operations proceed.
- **`MemberQueries`**: Encapsulates all direct database interactions related to members, using Prisma ORM.
- **`FleetService`**: An external dependency used by `PeopleService` to manage devices (hosts) associated with members in FleetDM.
The following diagram illustrates the high-level request flow within the People Directory module:
Sources:
[apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts#L36-L40)
[apps/api/src/people/people.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.service.ts#L10-L15)
[apps/api/src/people/utils/member-validator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/utils/member-validator.ts#L3-L4)
[apps/api/src/people/utils/member-queries.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/utils/member-queries.ts#L3-L4)
## API Endpoints
The `PeopleController` exposes a comprehensive set of RESTful API endpoints for managing organizational members. All endpoints are protected by `HybridAuthGuard` and require an `X-Organization-Id` header (or API key authentication).
All endpoints are secured using `HybridAuthGuard`, which supports both session-based and API key authentication. The `X-Organization-Id` header is crucial for scoping operations to a specific organization. The `DELETE /:id/host/:hostId` endpoint further enforces role-based access control, requiring the authenticated user to have an 'owner' role.
Sources: [apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts#L36-L40), [apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts#L220)
| Method | Path | Description | Request Body (DTO) | Response DTO | Required Roles |
| :----- | :------------------- | :--------------------------------------------- | :------------------------- | :------------------------- | :------------- |
| `GET` | `/people` | Retrieve all members in an organization | N/A | `PeopleResponseDto[]` | N/A |
| `POST` | `/people` | Create a new member | `CreatePeopleDto` | `PeopleResponseDto` | N/A |
| `POST` | `/people/bulk` | Bulk create multiple members | `BulkCreatePeopleDto` | `{ created: [], errors: [], summary: {} }` | N/A |
| `GET` | `/people/:id` | Retrieve a member by ID | N/A | `PeopleResponseDto` | N/A |
| `PATCH`| `/people/:id` | Update an existing member | `UpdatePeopleDto` | `PeopleResponseDto` | N/A |
| `DELETE`| `/people/:id/host/:hostId` | Remove a specific host from a member's devices | N/A | `{ success: true }` | `owner` |
| `DELETE`| `/people/:id` | Delete a member | N/A | `{ success: true, deletedMember: {} }` | N/A |
| `PATCH`| `/people/:id/unlink-device` | Unlink all devices from a member | N/A | `PeopleResponseDto` | N/A |
Sources: [apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts#L43-L280)
## Core Services and Logic
### PeopleService
The `PeopleService` is the central component for all business logic related to member management. It orchestrates operations by calling `MemberValidator` for data integrity checks, `MemberQueries` for database interactions, and `FleetService` for device management.
Key methods include:
* `findAllByOrganization(organizationId: string)`: Retrieves all active members for a given organization.
* `findById(memberId: string, organizationId: string)`: Fetches a single member by their ID within an organization.
* `create(organizationId: string, createData: CreatePeopleDto)`: Creates a new member after validating the organization, user, and ensuring the user is not already a member.
* `bulkCreate(organizationId: string, bulkCreateData: BulkCreatePeopleDto)`: Handles the creation of multiple members, performing individual validations and then a bulk database insert for valid entries. It returns a summary of successful and failed creations.
* `updateById(memberId: string, organizationId: string, updateData: UpdatePeopleDto)`: Updates an existing member's details. Includes validation for `userId` changes to prevent duplicate memberships.
* `deleteById(memberId: string, organizationId: string)`: Deactivates or removes a member from an organization.
* `unlinkDevice(memberId: string, organizationId: string)`: Disassociates all devices from a member. This involves removing hosts from FleetDM based on the member's `fleetDmLabelId` and deleting associated `Device` records from the local database.
* `removeHostById(memberId: string, organizationId: string, hostId: number)`: Removes a specific host from a member's associated devices in FleetDM.
Sources: [apps/api/src/people/people.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.service.ts#L17-L255)
### MemberValidator
The `MemberValidator` class provides static methods to ensure the validity of entities and relationships before performing operations. This prevents common data integrity issues and provides clear error messages.
* `validateOrganization(organizationId: string)`: Checks if an organization with the given ID exists. Throws `NotFoundException` if not found.
* `validateUser(userId: string)`: Checks if a user with the given ID exists. Throws `NotFoundException` if not found.
* `validateMemberExists(memberId: string, organizationId: string)`: Confirms that a member exists and is active within the specified organization. Throws `NotFoundException` if not found.
* `validateUserNotMember(userId: string, organizationId: string, excludeMemberId?: string)`: Ensures that a user is not already an active member of the organization. An optional `excludeMemberId` allows this check to be used during updates where the member's own ID should be ignored. Throws `BadRequestException` if the user is already a member.
Sources: [apps/api/src/people/utils/member-validator.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/utils/member-validator.ts#L3-L59)
### MemberQueries
The `MemberQueries` class acts as a data access layer, abstracting direct Prisma ORM calls for member-related operations. It defines a standard `MEMBER_SELECT` object to ensure consistent data retrieval across different queries.
* `MEMBER_SELECT`: A constant object defining the fields to be selected when querying member data, including nested user information.
* `findAllByOrganization(organizationId: string)`: Retrieves all members for an organization, ordered by creation date.
* `findByIdInOrganization(memberId: string, organizationId: string)`: Finds a specific member by ID within an organization.
* `createMember(organizationId: string, createData: CreatePeopleDto)`: Inserts a new member record into the database.
* `updateMember(memberId: string, updateData: UpdatePeopleDto)`: Updates an existing member record. Handles `fleetDmLabelId` specifically to allow setting it to `null`.
* `findMemberForDeletion(memberId: string, organizationId: string)`: Retrieves minimal member and user information specifically for the deletion process.
* `deleteMember(memberId: string)`: Deletes a member record from the database.
* `unlinkDevice(memberId: string)`: Updates a member's record by setting `fleetDmLabelId` to `null`.
* `bulkCreateMembers(organizationId: string, memberData: CreatePeopleDto[])`: Performs a `createMany` operation for multiple members, then fetches the newly created members for the response. `skipDuplicates` is used to prevent errors if a user is already a member.
Sources: [apps/api/src/people/utils/member-queries.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/utils/member-queries.ts#L7-L135)
## Data Transfer Objects (DTOs)
The module uses DTOs for defining the structure of data exchanged between the client and the API, ensuring strong typing and validation.
Sources:
[apps/api/src/people/dto/create-people.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/dto/create-people.dto.ts#L7-L49)
[apps/api/src/people/dto/bulk-create-people.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/dto/bulk-create-people.dto.ts#L7-L35)
### `CreatePeopleDto`
This DTO defines the data required to create a single new member.
```typescript
export class CreatePeopleDto {
userId: string;
role: string;
department?: Departments;
isActive?: boolean;
fleetDmLabelId?: number;
jobTitle?: string;
}
```
Sources: [apps/api/src/people/dto/create-people.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/dto/create-people.dto.ts#L7-L49)
### `BulkCreatePeopleDto`
This DTO is used for the bulk creation endpoint and contains an array of `CreatePeopleDto` objects. It includes validation for array size.
```typescript
export class BulkCreatePeopleDto {
@IsArray()
@ArrayMinSize(1)
@ArrayMaxSize(1000)
@ValidateNested({ each: true })
@Type(() => CreatePeopleDto)
members: CreatePeopleDto[];
}
```
Sources: [apps/api/src/people/dto/bulk-create-people.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/dto/bulk-create-people.dto.ts#L7-L35)
## Request Flows
### Create Member Flow
This sequence diagram illustrates the process of creating a new member through the API.
```mermaid
sequenceDiagram
participant Client
participant Controller as PeopleController
participant Service as PeopleService
participant Validator as MemberValidator
participant Queries as MemberQueries
participant DB as Database
Client->>Controller: POST /people (CreatePeopleDto)
Controller->>Service: create(orgId, createData)
Service->>Validator: validateOrganization(orgId)
Validator->>DB: Query Organization
DB-->>Validator: Organization exists
Validator-->>Service: Success
Service->>Validator: validateUser(userId)
Validator->>DB: Query User
DB-->>Validator: User exists
Validator-->>Service: Success
Service->>Validator: validateUserNotMember(userId, orgId)
Validator->>DB: Query Member by userId & orgId
DB-->>Validator: No existing member
Validator-->>Service: Success
Service->>Queries: createMember(orgId, createData)
Queries->>DB: Insert new Member record
DB-->>Queries: New Member record
Queries-->>Service: PeopleResponseDto
Service-->>Controller: PeopleResponseDto
Controller-->>Client: 201 Created (PeopleResponseDto)
```
Sources: [apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts#L90-L107), [apps/api/src/people/people.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.service.ts#L78-L106)
### Unlink Device Flow
This sequence diagram details the process of unlinking devices from a member, which involves interactions with both the local database and the external FleetDM system.
```mermaid
sequenceDiagram
participant Client
participant Controller as PeopleController
participant Service as PeopleService
participant Queries as MemberQueries
participant FleetService
participant DB as Database
participant FleetDM as FleetDM System
Client->>Controller: PATCH /people/:id/unlink-device
Controller->>Service: unlinkDevice(memberId, orgId)
Service->>Queries: findByIdInOrganization(memberId, orgId)
Queries->>DB: Query Member
DB-->>Queries: Member record (with fleetDmLabelId)
Queries-->>Service: PeopleResponseDto
alt Member has fleetDmLabelId
Service->>FleetService: removeHostsByLabel(fleetDmLabelId)
FleetService->>FleetDM: API Call to remove hosts
FleetDM-->>FleetService: Removal Result
FleetService-->>Service: Removal Result
end
Service->>DB: Delete Device records for memberId
DB-->>Service: Deletion Result
Service->>Queries: unlinkDevice(memberId)
Queries->>DB: Update Member (set fleetDmLabelId = null)
DB-->>Queries: Updated Member record
Queries-->>Service: PeopleResponseDto
Service-->>Controller: PeopleResponseDto
Controller-->>Client: 200 OK (PeopleResponseDto)
```
Sources: [apps/api/src/people/people.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.controller.ts#L263-L280), [apps/api/src/people/people.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.service.ts#L190-L255)
## People Module Configuration
The `PeopleModule` is a standard NestJS module that aggregates the `PeopleController` and `PeopleService`. It also imports `AuthModule` for authentication capabilities and provides `FleetService` as a dependency to `PeopleService`.
```typescript
import { Module } from '@nestjs/common';
import { AuthModule } from '../auth/auth.module';
import { FleetService } from '../lib/fleet.service';
import { PeopleController } from './people.controller';
import { PeopleService } from './people.service';
@Module({
imports: [AuthModule],
controllers: [PeopleController],
providers: [PeopleService, FleetService],
exports: [PeopleService],
})
export class PeopleModule {}
```
The module exports `PeopleService`, making it available for injection into other modules if needed.
Sources: [apps/api/src/people/people.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/people.module.ts#L1-L14)
---
## Technical docs: POST Run a browser automation
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-runautomation
## Parameters
## Responses
## Try It
---
## Technical docs: GET Get run history for an automation
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-getautomationruns
## Parameters
## Responses
## Try It
---
## Technical docs: Organization Admin
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-3/organization-admin
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/organization/organization.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts)
- [apps/api/src/org-chart/org-chart.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts)
- [apps/api/src/organization/organization.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts)
- [apps/api/src/context/context.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.controller.ts)
- [apps/api/src/org-chart/org-chart.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts)
- [apps/api/src/organization/dto/transfer-ownership.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/organization/dto/transfer-ownership.dto.ts)
- [apps/api/src/context/context.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.service.ts)
- [apps/api/src/context/schemas/context-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/context/schemas/context-operations.ts)
This page outlines the functionalities and architecture related to "Organization Admin" within the API, focusing on managing organization-level settings, organizational charts, and contextual data. It covers the API endpoints, service logic, and data structures involved in these administrative tasks, providing a comprehensive overview for developers and administrators.
The core components for organization administration are handled by the `OrganizationController` and `OrganizationService`, which manage details like organization name, logo, and ownership. Additionally, the `OrgChartController` and `OrgChartService` facilitate the creation, update, and deletion of organizational charts, including image uploads. The `ContextController` and `ContextService` provide mechanisms for managing organization-specific contextual data.
All administrative endpoints generally require authentication, typically handled by the `HybridAuthGuard`. This guard supports both API key authentication (via `X-API-Key` header) and session-based JWT authentication. For session-based authentication, the `X-Organization-Id` header is often required to specify the target organization.
## Organization Management
The `OrganizationController` exposes API endpoints for managing an organization's core properties, including retrieving details, updating information, transferring ownership, and deleting the organization. The `OrganizationService` encapsulates the business logic and database interactions for these operations.
### API Endpoints
The following table summarizes the API endpoints available for organization management:
| Method | Path | Description | Authentication |
| :----- | :------------------------- | :-------------------------------------------- | :------------- |
| `GET` | `/organization` | Retrieve details of the authenticated organization. | Required |
| `PATCH`| `/organization` | Update specific properties of the organization. | Required |
| `POST` | `/organization/transfer-ownership` | Transfer ownership of the organization to another member. | Required |
| `DELETE`| `/organization` | Delete the authenticated organization. | Required |
| `GET` | `/organization/primary-color` | Retrieve the organization's primary color. Can use an access token for public access. | Optional (token) |
Sources: [apps/api/src/organization/organization.controller.ts:20-21](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L20-L21), [apps/api/src/organization/organization.controller.ts:31-33](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L31-L33), [apps/api/src/organization/organization.controller.ts:54-56](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L54-L56), [apps/api/src/organization/organization.controller.ts:80-82](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L80-L82), [apps/api/src/organization/organization.controller.ts:133-135](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L133-L135), [apps/api/src/organization/organization.controller.ts:153-155](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L153-L155)
### Service Logic
The `OrganizationService` handles the core business logic for organization operations:
* **`findById(id: string)`**: Retrieves an organization by its ID, selecting specific fields such as `id`, `name`, `slug`, `logo`, `metadata`, `website`, `primaryColor`, and more. Throws `NotFoundException` if the organization does not exist.
* **`updateById(id: string, updateData: UpdateOrganizationDto)`**: Updates an existing organization's details. It first verifies the organization's existence and then applies the provided `updateData`.
* **`deleteById(id: string)`**: Deletes an organization after verifying its existence.
* **`transferOwnership(organizationId: string, currentUserId: string, newOwnerId: string)`**: This critical operation transfers the `owner` role from the `currentUserId` to `newOwnerId` within the specified `organizationId`. It performs several validations:
* Ensures `newOwnerId` is provided.
* Verifies the `currentUserId` is a member of the organization.
* Confirms the `currentUserId` holds the `owner` role.
* Checks that the `newOwnerId` corresponds to an active member of the organization.
* Prevents transferring ownership to the current owner.
* Updates roles for both members in a database transaction: the current owner loses `owner` and gains `admin` (if not already present), and the new owner gains `owner`.
* **`getPrimaryColor(organizationId: string, token?: string)`**: Retrieves the `primaryColor` of an organization. It supports an optional `token` parameter for public access, which resolves the organization ID via an access grant. If a token is provided and valid, it bypasses standard authentication.
Sources: [apps/api/src/organization/organization.service.ts:19-42](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts#L19-L42), [apps/api/src/organization/organization.service.ts:44-84](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts#L44-L84), [apps/api/src/organization/organization.service.ts:86-110](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts#L86-L110), [apps/api/src/organization/organization.service.ts:112-211](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts#L112-L211), [apps/api/src/organization/organization.service.ts:212-261](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts#L212-L261)
### Ownership Transfer Flow
The ownership transfer process involves several steps, including validation and role updates, ensuring that only authorized users can perform this critical action.
Sources: [apps/api/src/organization/organization.controller.ts:80-131](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.controller.ts#L80-L131), [apps/api/src/organization/organization.service.ts:112-211](https://github.com/blade47/comp/blob/main/apps/api/src/organization/organization.service.ts#L112-L211)
### Data Transfer Objects
The `TransferOwnershipDto` defines the request body for transferring ownership, while `TransferOwnershipResponseDto` describes the response structure.
Sources: [apps/api/src/organization/dto/transfer-ownership.dto.ts:1-19](https://github.com/blade47/comp/blob/main/apps/api/src/organization/dto/transfer-ownership.dto.ts#L1-L19)
## Org Chart Management
The `OrgChartController` provides endpoints for managing an organization's chart, allowing for both interactive chart data and image uploads. The `OrgChartService` handles the storage and retrieval, including integration with AWS S3 for image assets.
### API Endpoints
| Method | Path | Description | Authentication |
| :----- | :------------------------- | :-------------------------------------------- | :------------- |
| `GET` | `/org-chart` | Retrieve the organization chart. | Required |
| `PUT` | `/org-chart` | Create or update an interactive organization chart (nodes/edges). | Required |
| `POST` | `/org-chart/upload` | Upload an image to be used as the organization chart. | Required |
| `DELETE`| `/org-chart` | Delete the organization chart. | Required |
Sources: [apps/api/src/org-chart/org-chart.controller.ts:20-22](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts#L20-L22), [apps/api/src/org-chart/org-chart.controller.ts:28-30](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts#L28-L30), [apps/api/src/org-chart/org-chart.controller.ts:33-35](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts#L33-L35), [apps/api/src/org-chart/org-chart.controller.ts:48-50](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts#L48-L50), [apps/api/src/org-chart/org-chart.controller.ts:61-63](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts#L61-L63)
### Service Logic and S3 Integration
The `OrgChartService` manages the lifecycle of organization charts, distinguishing between `interactive` charts (stored as nodes and edges) and `uploaded` charts (stored as images in S3).
* **`findByOrganization(organizationId: string)`**: Retrieves the organization chart for a given organization. If the chart is an `uploaded` type with an `uploadedImageUrl`, it generates a presigned S3 URL for temporary access to the image.
* **`upsertInteractive(organizationId: string, data: UpsertOrgChartDto)`**: Creates or updates an interactive chart. If a previous chart was an uploaded image, it triggers the deletion of the old S3 object *after* the database update succeeds to prevent orphaned S3 files.
* **`uploadImage(organizationId: string, data: UploadOrgChartDto)`**: Handles the upload of an image file to S3.
* Validates the file type against `ALLOWED_UPLOAD_MIME_TYPES` (PNG, JPEG, GIF, WebP, SVG, BMP, TIFF, PDF).
* Enforces a `MAX_FILE_SIZE_BYTES` limit (100MB).
* Deletes any existing uploaded image from S3 before uploading the new one.
* Uploads the base64 encoded image data to S3, generating a unique key.
* Updates the database record to reflect the `uploaded` chart type and the S3 key.
* Returns a presigned URL for the newly uploaded image.
* **`delete(organizationId: string)`**: Deletes the organization chart record from the database. If an S3 image was associated, it also deletes the corresponding object from S3.
* **`getSignedUrl(s3Key: string)`**: Private helper method to generate a presigned URL for an S3 object, expiring in 15 minutes (`SIGNED_URL_EXPIRY`).
* **`deleteS3Object(s3Key: string)`**: Private helper method to delete an object from S3.
Sources: [apps/api/src/org-chart/org-chart.service.ts:20-27](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L20-L27), [apps/api/src/org-chart/org-chart.service.ts:32-53](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L32-L53), [apps/api/src/org-chart/org-chart.service.ts:55-97](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L55-L97), [apps/api/src/org-chart/org-chart.service.ts:99-166](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L99-L166), [apps/api/src/org-chart/org-chart.service.ts:168-193](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L168-L193), [apps/api/src/org-chart/org-chart.service.ts:195-209](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L195-L209), [apps/api/src/org-chart/org-chart.service.ts:211-224](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L211-L224)
### Org Chart Image Upload Flow
The process for uploading an organizational chart image involves client-side preparation, API interaction, and S3 storage.
Sources: [apps/api/src/org-chart/org-chart.controller.ts:61-69](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.controller.ts#L61-L69), [apps/api/src/org-chart/org-chart.service.ts:99-166](https://github.com/blade47/comp/blob/main/apps/api/src/org-chart/org-chart.service.ts#L99-L166)
## Context Management
The `ContextController` and `ContextService` provide a way to manage organization-specific contextual data. While not strictly "admin" in the sense of managing the organization itself, it allows administrators to define and manage data relevant to their organization's operations.
### API Endpoints
| Method | Path | Description | Authentication |
| :----- | :------------------------- | :-------------------------------------------- | :------------- |
| `GET` | `/context` | Retrieve all context entries for the organization. | Required |
| `GET` | `/context/:id` | Retrieve a specific context entry by ID. | Required |
| `POST` | `/context` | Create a new context entry. | Required |
| `PATCH`| `/context/:id` | Update an existing context entry. | Required |
| `DELETE`| `/context/:id` | Delete a context entry. | Required |
Sources: [apps/api/src/context/context.controller.ts:20-22](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.controller.ts#L20-L22), [apps/api/src/context/context.controller.ts:28-30](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.controller.ts#L28-L30), [apps/api/src/context/schemas/context-operations.ts:5-29](https://github.com/blade47/comp/blob/main/apps/api/src/context/schemas/context-operations.ts#L5-L29)
### Service Logic
The `ContextService` provides standard CRUD operations for context entries:
* **`findAllByOrganization(organizationId: string)`**: Fetches all context entries associated with a given `organizationId`, ordered by creation date.
* **`findById(id: string, organizationId: string)`**: Retrieves a single context entry by its ID, ensuring it belongs to the specified `organizationId`. Throws `NotFoundException` if not found.
* **`create(organizationId: string, createContextDto: CreateContextDto)`**: Creates a new context entry, associating it with the `organizationId`.
* **`updateById(id: string, organizationId: string, updateContextDto: UpdateContextDto)`**: Updates an existing context entry. It first validates the entry's existence and ownership before applying updates.
* **`deleteById(id: string, organizationId: string)`**: Deletes a context entry after verifying its existence and ownership.
Sources: [apps/api/src/context/context.service.ts:13-25](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.service.ts#L13-L25), [apps/api/src/context/context.service.ts:27-46](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.service.ts#L27-L46), [apps/api/src/context/context.service.ts:48-63](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.service.ts#L48-L63), [apps/api/src/context/context.service.ts:65-85](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.service.ts#L65-L85), [apps/api/src/context/context.service.ts:87-109](https://github.com/blade47/comp/blob/main/apps/api/src/context/context.service.ts#L87-L109)
---
## Technical docs: GET Get a specific run by ID
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/browserbase/browserbasecontroller-getrunbyid
## Parameters
## Responses
## Try It
---
## Technical docs: Prisma Layer
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-4/prisma-layer
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/customPrismaExtension.ts](https://github.com/blade47/comp/blob/main/apps/api/customPrismaExtension.ts)
- [apps/api/prisma/index.js](https://github.com/blade47/comp/blob/main/apps/api/prisma/index.js)
- [apps/api/prisma/index.ts](https://github.com/blade47/comp/blob/main/apps/api/prisma/index.ts)
The Prisma Layer primarily refers to the `PrismaExtension` class, a build extension designed for the Trigger.dev platform. Its main purpose is to automate the discovery, generation, and deployment of the Prisma client and schema within a build process, especially when the Prisma schema is sourced from a published package like `@trycompai/db`.
This layer ensures that the correct Prisma client is generated both locally during development and within the deployment environment, handling schema resolution, dependency management, and environment variable configuration for database connections. It abstracts away the complexities of integrating Prisma into a serverless or containerized build pipeline.
## PrismaExtension Overview
The `PrismaExtension` class is a core component that integrates Prisma into the build process. It implements the `BuildExtension` interface, providing hooks for `onBuildStart` and `onBuildComplete` to manage Prisma-related tasks.
### Configuration Options
The behavior of the `PrismaExtension` can be customized through the `PrismaExtensionOptions` interface.
| Option Name | Type | Description |
| :------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `version` | `string` | Optional. The Prisma client version to use. If not specified, it attempts to determine the version from the build manifest.
---
## Technical docs: Storage Flows
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-4/storage-flows
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/attachments/attachments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts)
- [apps/api/src/questionnaire/utils/questionnaire-storage.ts](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts)
- [apps/api/src/knowledge-base/utils/s3-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts)
- [apps/api/src/attachments/attachments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.controller.ts)
- [apps/api/src/attachments/upload-attachment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/upload-attachment.dto.ts)
- [apps/api/src/app/s3.ts](https://github.com/blade47/comp/blob/main/apps/api/src/app/s3.ts)
This page details the various storage flows within the application, primarily focusing on how files and data are managed using Amazon S3. It covers the core S3 client configuration, and specific implementations for handling attachments, questionnaire uploads, and knowledge base documents. The system leverages S3 for secure, scalable object storage and uses signed URLs for controlled access to private content.
The architecture ensures data segregation by organization and entity, robust security validations for file uploads, and persistence of metadata in a database alongside the S3 object storage.
## Core S3 Configuration and Client
The application initializes a global S3 client (`s3Client`) for all S3 interactions. This client is configured based on environment variables and is crucial for connecting to AWS S3 services.
### S3 Client Initialization
The `s3Client` is initialized once at application startup. It requires several environment variables to be set for proper functioning, including AWS credentials, region, and the default bucket name. If any critical configuration is missing, the client initialization fails, and a dummy client is created, indicating that S3 operations will not work.
The S3 client relies on the following environment variables. If these are not correctly set, S3 operations will fail:
- `APP_AWS_REGION`
- `APP_AWS_ACCESS_KEY_ID`
- `APP_AWS_SECRET_ACCESS_KEY`
- `APP_AWS_BUCKET_NAME` (aliased as `BUCKET_NAME`)
- `APP_AWS_ENDPOINT` (optional, for custom S3 endpoints)
Sources: [apps/api/src/app/s3.ts:10-44](https://github.com/blade47/comp/blob/main/apps/api/src/app/s3.ts#L10-L44)
### S3 Key Extraction and Validation
A utility function, `extractS3KeyFromUrl`, is provided to safely parse an S3 URL and extract its corresponding S3 key. This function includes security checks to prevent path traversal attacks and validates the S3 host.
```mermaid
sequenceDiagram
participant Caller
participant S3Utils as S3 Utilities (app/s3.ts)
Caller->>S3Utils: extractS3KeyFromUrl(url)
S3Utils->>S3Utils: Parse URL
alt URL is valid
S3Utils->>S3Utils: isValidS3Host(parsedUrl.host)
alt Host is valid S3 host
S3Utils->>S3Utils: Decode URI component from pathname
S3Utils->>S3Utils: Check for path traversal (../)
S3Utils-->>Caller: Return S3 Key
else Host is not valid S3 host
S3Utils-->>Caller: Throw "Invalid URL: Not a valid S3 endpoint"
end
else URL is malformed or not a URL
S3Utils->>S3Utils: Check for domain-like patterns
alt Domain-like pattern found
S3Utils-->>Caller: Throw "Invalid input: Domain-like pattern detected"
else No domain-like pattern
S3Utils->>S3Utils: Check for path traversal (../)
S3Utils-->>Caller: Return S3 Key (after '/' removal if present)
end
end
```
Sources: [apps/api/src/app/s3.ts:50-98](https://github.com/blade47/comp/blob/main/apps/api/src/app/s3.ts#L50-L98)
## Attachment Storage Flows
The `AttachmentsService` handles all operations related to file attachments, such as uploading, retrieving, and deleting files associated with various entities (e.g., tasks, policies).
### Attachment Upload Process
The `uploadAttachment` method is responsible for taking a base64 encoded file, validating it, uploading it to S3, and creating a corresponding record in the database.
**Key Steps:**
1. **Validation:** Checks for blocked file extensions and MIME types, and enforces a maximum file size (100MB).
2. **S3 Key Generation:** A unique S3 key is generated using the organization ID, entity type, entity ID, a timestamp, a random ID, and a sanitized file name. Special handling exists for `task_item` entity types to construct a more specific path.
* General pattern: `{organizationId}/attachments/{entityType}/{entityId}/{timestamp}-{fileId}-{sanitizedFileName}`
* Task Item pattern: `{organizationId}/attachments/task-item/{taskItemEntityType}/{taskItemEntityId}/{timestamp}-{fileId}-{sanitizedFileName}`
3. **S3 Upload:** The file buffer is uploaded to the configured S3 bucket using `PutObjectCommand`. Metadata such as `originalFileName`, `organizationId`, `entityId`, `entityType`, and `uploadedBy` are attached to the S3 object.
4. **Database Record:** A new record is created in the `db.attachment` table, storing the attachment's name, S3 URL (key), type (mapped from MIME type), entity ID, entity type, and organization ID.
5. **Signed URL Generation:** A temporary, signed download URL is generated for immediate access, expiring after 15 minutes (`SIGNED_URL_EXPIRY`).
Sources: [apps/api/src/attachments/attachments.service.ts:32-132](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L32-L132)
#### UploadAttachmentDto
This DTO defines the structure for attachment upload requests, including validation rules.
| Field | Type | Description
This document is based on the provided source code files. Any information not explicitly present in these files, such as detailed database schemas or external configurations not referenced, is outside the scope of this wiki page.
## Introduction
This document outlines the storage flows implemented within the API, focusing on how various types of files and data are managed using Amazon S3. The primary goal is to provide a robust, secure, and scalable solution for handling user-uploaded content, system-generated documents, and knowledge base assets. The system utilizes S3 for object storage, coupled with database records for metadata management, and employs signed URLs for controlled access to private resources.
The storage architecture is designed to support different application domains, including attachments for tasks and policies, questionnaire uploads, and knowledge base documents, each with its specific S3 key structure and access patterns. Security measures, such as file type validation and path traversal prevention, are integrated throughout these flows.
## S3 Client and Configuration
The application's interaction with Amazon S3 is centralized through a singleton `S3Client` instance, initialized in `apps/api/src/app/s3.ts`. This client is configured using environment variables, ensuring flexibility across different deployment environments.
### S3 Client Initialization Details
The `S3Client` is instantiated with the following parameters:
* `endpoint`: Optional, used for custom S3-compatible services.
* `region`: The AWS region where the S3 bucket resides, e.g., `us-east-1`.
* `credentials`: AWS access key ID and secret access key for authentication.
* `forcePathStyle`: Set to `true` if `APP_AWS_ENDPOINT` is provided, which is common for local S3 emulators.
A `Logger` is used to report initialization status and errors. If essential environment variables (`APP_AWS_ACCESS_KEY_ID`, `APP_AWS_SECRET_ACCESS_KEY`, `BUCKET_NAME`, `APP_AWS_REGION`) are missing, the S3 client will not be properly initialized, leading to a fallback to a dummy client and logging an error.
**Key Configuration Variables:**
* `BUCKET_NAME`: The default S3 bucket for general attachments.
* `APP_AWS_QUESTIONNAIRE_UPLOAD_BUCKET`: Specific bucket for questionnaire uploads.
* `APP_AWS_KNOWLEDGE_BASE_BUCKET`: Specific bucket for knowledge base documents.
* `APP_AWS_ORG_ASSETS_BUCKET`: Bucket for organization-specific assets.
Sources: [apps/api/src/app/s3.ts:10-44](https://github.com/blade47/comp/blob/main/apps/api/src/app/s3.ts#L10-L44)
### S3 Key Extraction and Validation
The `extractS3KeyFromUrl` function provides a secure way to obtain an S3 object key from a given URL. It performs several checks:
* **URL Parsing:** Attempts to parse the input as a URL.
* **S3 Host Validation:** Uses `isValidS3Host` to ensure the URL's host belongs to a legitimate AWS S3 domain.
* **Path Traversal Prevention:** Checks for `../` or `..\` patterns in the extracted key to prevent directory traversal vulnerabilities.
* **Empty Key Check:** Ensures the extracted key is not empty.
This function is critical for securely handling S3 URLs provided by external sources or users.
Sources: [apps/api/src/app/s3.ts:50-98](https://github.com/blade47/comp/blob/main/apps/api/src/app/s3.ts#L50-L98)
## Attachment Management
The `AttachmentsService` (`apps/api/src/attachments/attachments.service.ts`) is responsible for managing file attachments across various entities within the application. This includes uploading, retrieving, and deleting attachments, as well as generating secure download URLs.
### Attachment Upload Flow
The `uploadAttachment` method handles the end-to-end process of storing a new attachment.
Sources:
- [apps/api/src/attachments/attachments.service.ts:32-132](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L32-L132)
- [apps/api/src/attachments/attachments.controller.ts:20-20](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.controller.ts#L20-L20)
#### File Validation
The service implements strict validation for uploaded files:
* **Blocked Extensions:** A list of executable or potentially dangerous file extensions (e.g., `.exe`, `.bat`, `.js`, `.sh`) is blocked.
* **Blocked MIME Types:** A list of dangerous MIME types (e.g., `application/x-msdownload`, `application/javascript`) is blocked.
* **File Size Limit:** Files are limited to `100 MB` (`MAX_FILE_SIZE_BYTES`).
Sources: [apps/api/src/attachments/attachments.service.ts:39-77](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L39-L77)
#### S3 Key Structure
The S3 key for attachments follows a structured path to ensure organization and easy retrieval:
* General: `{organizationId}/attachments/{entityType}/{entityId}/{timestamp}-{fileId}-{sanitizedFileName}`
* For `task_item` entity type: `{organizationId}/attachments/task-item/{taskItemEntityType}/{taskItemEntityId}/{timestamp}-{fileId}-{sanitizedFileName}`. The `taskItemEntityType` and `taskItemEntityId` are extracted from the `description` field of the `UploadAttachmentDto`.
Sources: [apps/api/src/attachments/attachments.service.ts:80-92](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L80-L92)
#### UploadAttachmentDto
The data transfer object for attachment uploads includes:
| Field | Type | Description
### Attachment Download Flow
The `AttachmentsController` exposes an endpoint to generate a signed URL for an attachment.
```mermaid
sequenceDiagram
participant Client
participant AttachmentsController
participant AttachmentsService
participant S3Client as AWS S3
Client->>AttachmentsController: GET /attachments/:attachmentId/download
AttachmentsController->>AttachmentsService: getAttachmentDownloadUrl(orgId, attachmentId)
AttachmentsService->>db: findFirst({ id: attachmentId, organizationId })
db-->>AttachmentsService: Attachment Record
alt Attachment not found
AttachmentsService-->>AttachmentsController: Throw BadRequestException
AttachmentsController-->>Client: 400 Bad Request
else Attachment found
AttachmentsService->>AttachmentsService: generateSignedUrl(attachment.url)
AttachmentsService->>S3Client: getSignedUrl(GetObjectCommand)
S3Client-->>AttachmentsService: Signed URL
AttachmentsService-->>AttachmentsController: { downloadUrl, expiresIn }
AttachmentsController-->>Client: 200 OK { downloadUrl, expiresIn }
end
```
Sources:
- [apps/api/src/attachments/attachments.controller.ts:39-60](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.controller.ts#L39-L60)
- [apps/api/src/attachments/attachments.service.ts:149-178](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L149-L178)
#### Signed URL Generation
The `generateSignedUrl` private method uses `@aws-sdk/s3-request-presigner` to create temporary URLs for `GetObjectCommand` requests. These URLs allow direct download from S3 without requiring AWS credentials, and they expire after a predefined duration (`SIGNED_URL_EXPIRY`, 15 minutes). The `getPresignedDownloadUrlWithFilename` method allows specifying a custom filename for the download.
Sources: [apps/api/src/attachments/attachments.service.ts:249-256](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L249-L256), [apps/api/src/attachments/attachments.service.ts:265-277](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L265-L277)
### Other Attachment Operations
* **`getAttachments`**: Retrieves all attachment metadata for a given entity, including generated signed URLs for each.
* **`getAttachmentMetadata`**: Retrieves attachment metadata without generating signed URLs, useful for displaying lists of attachments.
* **`deleteAttachment`**: Deletes an attachment from both S3 and the database.
* **`copyPolicyVersionPdf`**: Copies an S3 object (policy PDF) to a new key, used for versioning.
* **`deletePolicyVersionPdf`**: Deletes a specific policy version PDF from S3.
* **`uploadToS3`**: A generic S3 upload method used by other services (e.g., `policies.service.ts`, `evidence-forms.service.ts`).
* **`getObjectBuffer`**: Retrieves an S3 object's content as a Buffer.
Sources: [apps/api/src/attachments/attachments.service.ts:135-246](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L135-L246), [apps/api/src/attachments/attachments.service.ts:258-261](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L258-L261), [apps/api/src/attachments/attachments.service.ts:279-293](https://github.com/blade47/comp/blob/main/apps/api/src/attachments/attachments.service.ts#L279-L293)
## Questionnaire Storage Flows
The `questionnaire-storage.ts` utility file provides functions for handling questionnaire file uploads to S3 and persisting questionnaire results and answers to the database.
### Questionnaire File Upload
The `uploadQuestionnaireFile` function handles the storage of questionnaire files.
Sources: [apps/api/src/questionnaire/utils/questionnaire-storage.ts:90-129](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L90-L129)
**S3 Key Structure:**
The S3 key for questionnaire uploads follows the pattern: `{organizationId}/questionnaire-uploads/{timestamp}-{fileId}-{sanitizedFileName}`.
Sources: [apps/api/src/questionnaire/utils/questionnaire-storage.ts:114-114](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L114-L114)
### Questionnaire Result Persistence
The `persistQuestionnaireResult` function saves the parsed questionnaire data and answers to the database.
Sources: [apps/api/src/questionnaire/utils/questionnaire-storage.ts:48-87](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L48-L87)
### Answer Management
* **`saveGeneratedAnswer`**: Updates an existing question's answer or creates a new one in the `questionnaireQuestionAnswer` table.
* **`updateAnsweredCount`**: Recalculates and updates the `answeredQuestions` count for a given questionnaire in the `questionnaire` table.
Sources: [apps/api/src/questionnaire/utils/questionnaire-storage.ts:132-172](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L132-L172), [apps/api/src/questionnaire/utils/questionnaire-storage.ts:34-45](https://github.com/blade47/comp/blob/main/apps/api/src/questionnaire/utils/questionnaire-storage.ts#L34-L45)
## Knowledge Base Storage Flows
The `s3-operations.ts` file within the knowledge base module provides dedicated functions for managing knowledge base documents in S3.
### Knowledge Base Document Upload
The `uploadToS3` function handles the upload of knowledge base documents.
Sources: [apps/api/src/knowledge-base/utils/s3-operations.ts:34-66](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts#L34-L66)
**Key Configuration:**
* `APP_AWS_KNOWLEDGE_BASE_BUCKET`: The dedicated S3 bucket for knowledge base documents.
* `MAX_FILE_SIZE_BYTES`: Maximum allowed file size for knowledge base documents.
Sources: [apps/api/src/knowledge-base/utils/s3-operations.ts:13-13](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts#L13-L13), [apps/api/src/knowledge-base/utils/s3-operations.ts:10-10](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts#L10-L10)
### Knowledge Base Document Access and Deletion
* **`generateDownloadUrl`**: Creates a signed URL for downloading a knowledge base document. The `ResponseContentDisposition` is set to `attachment` to prompt a download.
* **`generateViewUrl`**: Creates a signed URL for viewing a knowledge base document directly in the browser. The `ResponseContentDisposition` is set to `inline`.
* **`deleteFromS3`**: Deletes a document from the `APP_AWS_KNOWLEDGE_BASE_BUCKET`. It returns `true` on success and `false` on error, without throwing exceptions.
* **`validateS3Config`**: Ensures that the S3 client is initialized and the `APP_AWS_KNOWLEDGE_BASE_BUCKET` environment variable is set.
Sources: [apps/api/src/knowledge-base/utils/s3-operations.ts:69-122](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts#L69-L122), [apps/api/src/knowledge-base/utils/s3-operations.ts:24-32](https://github.com/blade47/comp/blob/main/apps/api/src/knowledge-base/utils/s3-operations.ts#L24-L32)
---
## Technical docs: POST Create :ConnectionId
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/endpoints/cloudsecuritycontroller-scan
## Parameters
## Responses
## Try It
---
## Technical docs: Validation Schemas
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-4/validation-schemas
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/common/pipes/zod-validation.pipe.ts](https://github.com/blade47/comp/blob/main/apps/api/src/common/pipes/zod-validation.pipe.ts)
- [apps/api/src/context/schemas/context-bodies.ts](https://github.com/blade47/comp/blob/main/apps/api/src/context/schemas/context-bodies.ts)
- [apps/api/src/policies/schemas/policy-bodies.ts](https://github.com/blade47/comp/blob/main/apps/api/src/policies/schemas/policy-bodies.ts)
- [apps/api/src/risks/schemas/risk-bodies.ts](https://github.com/blade47/comp/blob/main/apps/api/src/risks/schemas/risk-bodies.ts)
- [apps/api/src/organization/schemas/organization-api-bodies.ts](https://github.com/blade47/comp/blob/main/apps/api/src/organization/schemas/organization-api-bodies.ts)
- [apps/api/src/people/schemas/people-bodies.ts](https://github.com/blade47/comp/blob/main/apps/api/src/people/schemas/people-bodies.ts)
- [apps/api/src/framework-editor/task-template/schemas/task-template-bodies.ts](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/schemas/task-template-bodies.ts)
Validation schemas are a critical component of the API, serving two primary purposes: ensuring data integrity at runtime and providing clear, machine-readable API specifications for documentation. This system leverages Zod for robust runtime validation within NestJS pipes and `ApiBodyOptions` from `@nestjs/swagger` to generate comprehensive OpenAPI documentation.
This dual approach guarantees that incoming data conforms to expected structures and types, preventing common errors and security vulnerabilities, while simultaneously offering developers an accurate and up-to-date reference for API interactions.
## Zod Validation Pipe
The `ZodValidationPipe` is a custom NestJS `PipeTransform` designed to validate incoming request payloads against a specified Zod schema. This pipe intercepts the request body before it reaches the controller method, ensuring that the data adheres to the defined schema.
### How it Works
1. **Instantiation**: The pipe is instantiated with a `ZodSchema` object, which defines the expected structure and types of the data.
2. **Transformation**: The `transform` method receives the incoming `value` (request body) and attempts to parse it using the provided `ZodSchema`.
3. **Error Handling**: If the `schema.parse()` operation fails (meaning the `value` does not conform to the schema), a `BadRequestException` is thrown, immediately stopping the request processing and returning a 400 Bad Request error to the client. If successful, the parsed (and potentially type-coerced) value is returned.
This pipe ensures that only valid data structures proceed to the business logic layer, enhancing API reliability and security.
```typescript
import {
ArgumentMetadata,
BadRequestException,
PipeTransform,
} from '@nestjs/common';
import { ZodSchema } from 'zod';
export class ZodValidationPipe implements PipeTransform {
constructor(private schema: ZodSchema) {}
transform(value: unknown, metadata: ArgumentMetadata) {
try {
const parsedValue = this.schema.parse(value);
return parsedValue;
} catch (error) {
throw new BadRequestException('Validation failed');
}
}
}
```
Sources: [apps/api/src/common/pipes/zod-validation.pipe.ts:1-17](https://github.com/blade47/comp/blob/main/apps/api/src/common/pipes/zod-validation.pipe.ts#L1-L17)
### Validation Flow
The following diagram illustrates the data flow through the `ZodValidationPipe`:
## API Body Schemas for Documentation
In addition to runtime validation, the API utilizes `ApiBodyOptions` from `@nestjs/swagger` to define the structure of request bodies for OpenAPI documentation. These schemas are crucial for generating interactive API documentation (e.g., Swagger UI) that clearly outlines expected input formats, types, and examples.
These `ApiBodyOptions` are typically exported as constants in dedicated `*-bodies.ts` files and are referenced in controller methods using the `@ApiBody()` decorator.
### Common Structure
Most API body schemas follow a pattern of defining `ApiBodyOptions` for `create` and `update` operations, often referencing specific Data Transfer Objects (DTOs).
```typescript
import type { ApiBodyOptions } from '@nestjs/swagger';
// ... import DTOs
export const ENTITY_BODIES: Record = {
createEntity: {
description: 'Entity creation data',
type: CreateEntityDto,
},
updateEntity: {
description: 'Entity update data',
type: UpdateEntityDto,
},
};
```
### Detailed Schema Definitions
#### Context Schemas
The `CONTEXT_BODIES` object defines `ApiBodyOptions` for creating and updating context entries, providing detailed examples for Swagger documentation.
```typescript
export const CONTEXT_BODIES: Record = {
createContext: {
description: 'Context entry data',
type: CreateContextDto,
examples: {
'Authentication Context': { /* ... */ },
'Database Context': { /* ... */ },
},
},
updateContext: {
description: 'Partial context entry data to update',
type: UpdateContextDto,
examples: {
'Update Tags': { /* ... */ },
'Update Answer': { /* ... */ },
},
},
};
```
Sources: [apps/api/src/context/schemas/context-bodies.ts:4-39](https://github.com/blade47/comp/blob/main/apps/api/src/context/schemas/context-bodies.ts#L4-L39)
#### Policy Schemas
`POLICY_BODIES` defines the expected request bodies for policy creation and updates.
```typescript
export const POLICY_BODIES: Record = {
createPolicy: {
description: 'Policy creation data',
type: CreatePolicyDto,
},
updatePolicy: {
description: 'Policy update data',
type: UpdatePolicyDto,
},
};
```
Sources: [apps/api/src/policies/schemas/policy-bodies.ts:4-13](https://github.com/blade47/comp/blob/main/apps/api/src/policies/schemas/policy-bodies.ts#L4-L13)
#### Risk Schemas
`RISK_BODIES` specifies the request body structures for creating and updating risks.
```typescript
export const RISK_BODIES: Record = {
createRisk: {
description: 'Risk creation data',
type: CreateRiskDto,
},
updateRisk: {
description: 'Risk update data',
},
};
```
Sources: [apps/api/src/risks/schemas/risk-bodies.ts:4-13](https://github.com/blade47/comp/blob/main/apps/api/src/risks/schemas/risk-bodies.ts#L4-L13)
#### Organization Schemas
The organization module uses specific `ApiBodyOptions` for updating organization details and transferring ownership. These schemas define properties directly rather than referencing DTOs.
##### `UPDATE_ORGANIZATION_BODY`
| Property | Type | Description | Example |
| :------------------ | :-------- | :---------------------------------------- | :---------------------------- |
| `name` | `string` | Organization name | `New Acme Corporation` |
| `slug` | `string` | Organization slug | `new-acme-corp` |
| `logo` | `string` | Organization logo URL | `https://example.com/logo.png`|
| `metadata` | `string` | Additional metadata in JSON format | `{"theme": "dark"}` |
| `website` | `string` | Organization website URL | `https://acme-corp.com` |
| `onboardingCompleted`| `boolean` | Whether onboarding is completed | `true` |
| `hasAccess` | `boolean` | Whether organization has access to platform| `true` |
| `fleetDmLabelId` | `integer` | FleetDM label ID for device management | `123` |
| `isFleetSetupCompleted`| `boolean` | Whether FleetDM setup is completed | `false` |
| `primaryColor` | `string` | Organization primary color in hex format | `#3B82F6` |
Sources: [apps/api/src/organization/schemas/organization-api-bodies.ts:4-58](https://github.com/blade47/comp/blob/main/apps/api/src/organization/schemas/organization-api-bodies.ts#L4-L58)
##### `TRANSFER_OWNERSHIP_BODY`
| Property | Type | Description | Example | Required |
| :----------- | :-------- | :-------------------------------------------------------------------------- | :------------------ | :------- |
| `newOwnerId` | `string` | Member ID of the new owner | `mem_xyz789` | Yes |
| `userId` | `string` | User ID of the current owner initiating the transfer (required for API key auth, ignored for JWT auth)| `usr_abc123def456` | No |
Sources: [apps/api/src/organization/schemas/organization-api-bodies.ts:60-78](https://github.com/blade47/comp/blob/main/apps/api/src/organization/schemas/organization-api-bodies.ts#L60-L78)
#### People Schemas
`PEOPLE_BODIES` defines schemas for creating single members, bulk creating members, and updating member information.
```typescript
export const PEOPLE_BODIES: Record = {
createMember: {
description: 'Member creation data',
type: CreatePeopleDto,
},
bulkCreateMembers: {
description: 'Bulk member creation data',
type: BulkCreatePeopleDto,
},
updateMember: {
description: 'Member update data',
type: UpdatePeopleDto,
},
};
```
Sources: [apps/api/src/people/schemas/people-bodies.ts:6-19](https://github.com/blade47/comp/blob/main/apps/api/src/people/schemas/people-bodies.ts#L6-L19)
#### Task Template Schemas
`TASK_TEMPLATE_BODIES` provides the schema for updating task templates within the framework editor.
```typescript
export const TASK_TEMPLATE_BODIES = {
updateTaskTemplate: {
type: UpdateTaskTemplateDto,
description: 'Update framework editor task template data',
},
};
```
Sources: [apps/api/src/framework-editor/task-template/schemas/task-template-bodies.ts:3-7](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/schemas/task-template-bodies.ts#L3-L7)
### Relationship between ApiBodyOptions and DTOs
`ApiBodyOptions` often reference DTO (Data Transfer Object) classes to describe the expected request body. This provides a clear link between the runtime data structure (defined by the DTO) and its documentation representation.
## Overall API Request Processing with Validation
The following diagram illustrates how validation schemas fit into the broader API request processing pipeline, from client request to response.
---
## Technical docs: POST Create :ConnectionId
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/endpoints/cloudsecuritycontroller-triggerscan
## Parameters
## Responses
## Try It
---
## Technical docs: GET Get :RunId
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/endpoints/cloudsecuritycontroller-getrunstatus
## Parameters
## Responses
## Try It
---
## Technical docs: Platform Integrations
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-5/platform-integrations
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/integration-platform/controllers/task-integrations.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/task-integrations.controller.ts)
- [apps/api/src/integration-platform/controllers/connections.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/connections.controller.ts)
- [apps/api/src/integration-platform/controllers/checks.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/checks.controller.ts)
- [apps/api/src/integration-platform/controllers/admin-integrations.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/admin-integrations.controller.ts)
- [apps/api/src/integration-platform/controllers/oauth-apps.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/oauth-apps.controller.ts)
- [apps/api/src/integration-platform/controllers/oauth.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/oauth.controller.ts)
- [apps/api/src/integration-platform/controllers/sync.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/sync.controller.ts)
- [apps/api/src/integration-platform/controllers/variables.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/variables.controller.ts)
- [apps/api/src/integration-platform/controllers/webhook.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/webhook.controller.ts)
- [apps/api/src/integration-platform/services/connection.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/connection.service.ts)
- [apps/api/src/integration-platform/services/auto-check-runner.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/auto-check-runner.service.ts)
- [apps/api/src/integration-platform/services/credential-vault.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/credential-vault.service.ts)
- [apps/api/src/integration-platform/repositories/provider.repository.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/repositories/provider.repository.ts)
- [apps/api/src/integration-platform/utils/credential-utils.ts](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/utils/credential-utils.ts)
The Platform Integrations module provides a robust framework for connecting to external services, managing credentials securely, performing automated checks, synchronizing employee data, and handling webhooks. It abstracts the complexities of various authentication mechanisms (OAuth2, API Key, Custom) and offers a standardized way to interact with diverse third-party platforms. The system is designed to be extensible, allowing new integrations to be added via "manifests" that define their capabilities, authentication methods, and checks.
At its core, the module facilitates the creation and management of `Connections` between an organization and an `IntegrationProvider`. It ensures that sensitive `Credentials` are encrypted and handled safely, automatically refreshing OAuth tokens when necessary. Automated `Checks` can be run against these connections to validate configurations or monitor compliance, with results stored and associated with specific tasks. Additionally, the platform supports `Employee Synchronization` from HRIS/IDP systems and processes incoming `Webhooks` to react to external events.
## 1. Integration Providers and Manifests
Integration providers represent external services (e.g., AWS, Google Workspace, Rippling) that the platform can connect to. Each provider is defined by a "manifest" which outlines its capabilities, authentication requirements, available checks, and other metadata. The system dynamically loads these manifests to present available integrations and configure their behavior.
### 1.1 Provider Data Model
The `ProviderRepository` interacts with the `IntegrationProvider` Prisma model to store metadata about each provider, such as its slug, name, category, capabilities, and whether it's active. This allows the system to persist information about available integrations independently of their manifest definitions.
Sources: [apps/api/src/integration-platform/repositories/provider.repository.ts:10-40](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/repositories/provider.repository.ts#L10-L40), [apps/api/src/integration-platform/controllers/connections.controller.ts:40-45](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/connections.controller.ts#L40-L45)
### 1.2 Provider Listing and Details
The `ConnectionsController` exposes endpoints to list all available integration providers or retrieve details for a specific one. These endpoints aggregate information from the loaded manifests and, for OAuth providers, check if platform-level credentials have been configured.
#### API Endpoints
| Method | Path | Description |
| :----- | :----------------------------------- | :-------------------------------------------- |
| `GET` | `/integrations/connections/providers` | List all available integration providers. |
| `GET` | `/integrations/connections/providers/:slug` | Get details for a specific provider. |
Sources: [apps/api/src/integration-platform/controllers/connections.controller.ts:47-147](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/connections.controller.ts#L47-L147)
## 2. Connection Management
Connections represent an active link between an organization and an integration provider. The `ConnectionService` and `ConnectionRepository` manage the lifecycle and state of these connections.
### 2.1 Connection Lifecycle
A connection can transition through various states: `active`, `paused`, `error`, and `disconnected`. The system provides explicit actions to manage these states.
Sources: [apps/api/src/integration-platform/services/connection.service.ts:69-95](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/connection.service.ts#L69-L95)
### 2.2 Connection Creation and Updates
Creating a connection involves validating the provider, handling different authentication types (OAuth2, API Key, Custom), and storing credentials securely. For custom integrations like AWS, specific validation steps are performed before the connection is activated.
Sources: [apps/api/src/integration-platform/controllers/connections.controller.ts:182-259](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/connections.controller.ts#L182-L259), [apps/api/src/integration-platform/services/connection.service.ts:50-67](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/connection.service.ts#L50-L67)
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :----------------------------------------------------------------------- |
| `GET` | `/integrations/connections` | List connections for an organization. |
| `GET` | `/integrations/connections/:id` | Get details for a specific connection. |
| `POST` | `/integrations/connections` | Create a new connection (API Key/Custom auth). |
| `POST` | `/integrations/connections/:id/pause` | Pause a connection. |
| `POST` | `/integrations/connections/:id/resume` | Resume a paused connection. |
| `POST` | `/integrations/connections/:id/disconnect` | Disconnect (soft delete) a connection. |
| `DELETE` | `/integrations/connections/:id` | Delete a connection permanently. |
| `PATCH` | `/integrations/connections/:id` | Update connection metadata (e.g., `connectionName`, `regions`). |
| `POST` | `/integrations/connections/:id/test` | Test a connection's credentials. |
| `POST` | `/integrations/connections/:id/ensure-valid-credentials` | Ensure credentials are valid, refreshing OAuth tokens if needed. |
| `PUT` | `/integrations/connections/:id/credentials` | Update credentials for a custom auth connection. |
Sources: [apps/api/src/integration-platform/controllers/connections.controller.ts:150-484](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/connections.controller.ts#L150-L484)
For AWS connections, the `ConnectionsController` includes a specialized `validateAwsCredentials` method. This method attempts to assume an IAM role and verify Security Hub enablement across specified regions before a connection is established or updated. This ensures that the provided AWS credentials have the necessary permissions and that the required AWS services are active.
Sources: [apps/api/src/integration-platform/controllers/connections.controller.ts:262-390](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/connections.controller.ts#L262-L390)
## 3. Credential Management and Security
The `CredentialVaultService` is responsible for the secure storage, retrieval, and lifecycle management of integration credentials. It employs strong encryption and handles OAuth token refreshing.
### 3.1 Encryption Mechanism
All sensitive credentials are encrypted using AES-256-GCM with a derived key. This ensures that credentials are never stored in plain text.
Sources: [apps/api/src/integration-platform/services/credential-vault.service.ts:20-77](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/credential-vault.service.ts#L20-L77)
### 3.2 Credential Storage and Retrieval
The `CredentialVaultService` provides methods to store both OAuth tokens and API key/custom credentials. It manages credential versions, keeping a history and marking old versions as rotated.
#### Key Functions
* `storeOAuthTokens(connectionId, tokens)`: Stores encrypted OAuth access and refresh tokens, along with their expiry.
* `storeApiKeyCredentials(connectionId, credentials)`: Stores encrypted API key or custom credentials.
* `getDecryptedCredentials(connectionId)`: Retrieves and decrypts the latest credentials for a given connection.
* `rotateCredentials(connectionId, newCredentials)`: Marks current credentials as rotated and stores new ones.
Sources: [apps/api/src/integration-platform/services/credential-vault.service.ts:80-164](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/credential-vault.service.ts#L80-L164)
### 3.3 OAuth Token Refresh
For OAuth2 integrations, the `CredentialVaultService` automatically handles token refreshing when an access token is expired or nearing expiry. This ensures continuous operation without manual re-authentication.
```mermaid
sequenceDiagram
participant App as "Application/Service"
participant CV as "CredentialVaultService"
participant ConnRepo as "ConnectionRepository"
participant OAuthProvider as "OAuth Provider API"
App->>CV: getValidAccessToken(connId, refreshConfig)
CV->>CV: needsRefresh(connId)?
alt Token needs refresh & refreshConfig provided
CV->>CV: getRefreshToken(connId)
CV->>OAuthProvider: POST /token (grant_type=refresh_token)
OAuthProvider-->>CV: New Access/Refresh Tokens
CV->>CV: storeOAuthTokens(connId, newTokens)
CV->>ConnRepo: update(connId, {status: 'active'})
CV-->>App: New Access Token
else Token valid or refresh failed
CV->>CV: getDecryptedCredentials(connId)
CV-->>App: Current Access Token (or null if failed)
end
```
Sources: [apps/api/src/integration-platform/services/credential-vault.service.ts:192-299](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/credential-vault.service.ts#L192-L299)
### 3.4 Credential Utilities
The `credential-utils.ts` file provides helper functions for normalizing credential values, especially when dealing with fields that might be single strings or arrays of strings.
* `getStringValue(value)`: Extracts the first string from a `string | string[]` value.
* `toStringCredentials(credentials)`: Converts a `{ [key: string]: string | string[] }` object to `{ [key: string]: string }`.
Sources: [apps/api/src/integration-platform/utils/credential-utils.ts:1-26](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/utils/credential-utils.ts#L1-L26)
## 4. OAuth Flow Management
The `OAuthController` orchestrates the OAuth 2.0 authorization code flow, enabling users to grant the platform access to their external accounts. This involves redirecting users to the OAuth provider, handling callbacks, and securely exchanging authorization codes for tokens.
### 4.1 OAuth State and Credentials
* `OAuthStateRepository`: Stores temporary state parameters (`providerSlug`, `organizationId`, `userId`, `codeVerifier`, `redirectUrl`) during the OAuth flow to prevent CSRF attacks and maintain context.
* `OAuthCredentialsService`: Manages platform-level (admin-configured) and organization-level (custom app) OAuth client credentials (`clientId`, `clientSecret`, `scopes`).
### 4.2 Starting the OAuth Flow
The `startOAuth` endpoint initiates the process by generating a unique state, constructing the authorization URL, and redirecting the user.
```mermaid
sequenceDiagram
actor User
participant App as "Client Application"
participant API as "OAuthController"
participant OCS as "OAuthCredentialsService"
participant OSR as "OAuthStateRepository"
participant OAuthProvider as "External OAuth Provider"
User->>App: Click "Connect Integration"
App->>API: POST /oauth/start {providerSlug, orgId, userId}
API->>OCS: getCredentials(providerSlug, orgId)
OCS-->>API: {clientId, clientSecret, scopes}
API->>OSR: createOAuthState({providerSlug, orgId, userId, codeVerifier})
OSR-->>API: {state, codeVerifier}
API->>API: Build Authorization URL
API-->>App: {authorizationUrl}
App->>User: Redirect to authorizationUrl
User->>OAuthProvider: Authorize App
OAuthProvider-->>User: Redirect to callbackUrl with code & state
```
Sources: [apps/api/src/integration-platform/controllers/oauth.controller.ts:60-149](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/oauth.controller.ts#L60-L149)
### 4.3 OAuth Callback Handling
The `oauthCallback` endpoint receives the authorization code from the OAuth provider, validates the state, exchanges the code for access and refresh tokens, stores them, and then redirects the user back to the application.
```mermaid
sequenceDiagram
actor User
participant OAuthProvider as "External OAuth Provider"
participant API as "OAuthController"
participant OSR as "OAuthStateRepository"
participant OCS as "OAuthCredentialsService"
participant CVS as "CredentialVaultService"
participant ConnS as "ConnectionService"
participant ACR as "AutoCheckRunnerService"
participant App as "Client Application"
OAuthProvider->>API: GET /oauth/callback {code, state, error?}
API->>OSR: findByState(state)
OSR-->>API: OAuthState (or null)
alt Invalid/Expired State or Error
API->>API: Build Error Redirect URL
API-->>User: Redirect to Error URL
else Valid State
API->>OCS: getCredentials(providerSlug, orgId)
OCS-->>API: {clientId, clientSecret, scopes}
API->>OAuthProvider: POST /token {code, redirect_uri, client_id, client_secret, code_verifier}
OAuthProvider-->>API: Access/Refresh Tokens
API->>ConnS: createConnection() or findByProviderAndOrg()
ConnS-->>API: Connection
API->>CVS: storeOAuthTokens(connection.id, tokens)
API->>ACR: tryAutoRunChecks(connection.id)
API->>API: Build Success Redirect URL
API-->>User: Redirect to Success URL
end
```
Sources: [apps/api/src/integration-platform/controllers/oauth.controller.ts:152-297](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/oauth.controller.ts#L152-L297)
### 4.4 Managing Custom OAuth Apps
The `OAuthAppsController` allows organizations to configure their own OAuth application credentials for a provider, overriding platform-level credentials. This is useful for providers where custom app creation is common or required.
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :------------------------------------------------ |
| `GET` | `/integrations/oauth-apps` | List custom OAuth apps for an organization. |
| `GET` | `/integrations/oauth-apps/setup/:providerSlug` | Get OAuth app setup info for a provider. |
| `POST` | `/integrations/oauth-apps` | Save custom OAuth app credentials for an organization. |
| `DELETE` | `/integrations/oauth-apps/:providerSlug` | Delete custom OAuth app credentials. |
Sources: [apps/api/src/integration-platform/controllers/oauth-apps.controller.ts:16-96](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/oauth-apps.controller.ts#L16-L96)
### 4.5 Admin-level OAuth Configuration
The `AdminIntegrationsController` provides administrative functions to configure platform-wide OAuth client credentials for providers. This is typically done once by platform administrators.
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :------------------------------------------------ |
| `GET` | `/admin/integrations` | List all integrations with credential status. |
| `GET` | `/admin/integrations/:providerSlug` | Get details for a specific integration. |
| `POST` | `/admin/integrations/credentials` | Save platform credentials for an integration. |
| `DELETE` | `/admin/integrations/credentials/:providerSlug` | Delete platform credentials for an integration. |
Sources: [apps/api/src/integration-platform/controllers/admin-integrations.controller.ts:19-140](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/admin-integrations.controller.ts#L19-L140)
## 5. Automated Checks
The platform can run automated checks against integrated services to verify configurations, monitor compliance, or validate specific conditions. These checks are defined within the integration manifests.
### 5.1 Check Definition and Listing
Checks are defined within the `manifest.checks` array, often mapping to specific task templates. The `ChecksController` and `TaskIntegrationsController` provide ways to discover and list these checks.
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :------------------------------------------------ |
| `GET` | `/integrations/checks/providers/:providerSlug` | List available checks for a provider. |
| `GET` | `/integrations/checks/connections/:connectionId` | List available checks for a connection. |
| `GET` | `/integrations/tasks/template/:templateId/checks` | Get checks for a specific task template. |
| `GET` | `/integrations/tasks/:taskId/checks` | Get checks for a specific task. |
| `GET` | `/integrations/tasks/:taskId/runs` | Get check run history for a task. |
Sources: [apps/api/src/integration-platform/controllers/checks.controller.ts:19-74](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/checks.controller.ts#L19-L74), [apps/api/src/integration-platform/controllers/task-integrations.controller.ts:35-132](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/task-integrations.controller.ts#L35-L132)
### 5.2 Running Checks
Checks can be triggered manually or automatically. The `runCheckForTask` and `runConnectionChecks` methods handle the execution, credential retrieval, and result storage.
Sources: [apps/api/src/integration-platform/controllers/task-integrations.controller.ts:135-309](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/task-integrations.controller.ts#L135-L309), [apps/api/src/integration-platform/controllers/checks.controller.ts:77-210](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/checks.controller.ts#L77-L210)
### 5.3 Auto-Check Runner
The `AutoCheckRunnerService` determines if checks can be automatically run for a connection (e.g., after creation or credential update) and triggers a background task via `Trigger.dev` for reliable execution.
#### Key Functions
* `canAutoRunChecks(connectionId)`: Checks if a connection has checks defined and all required variables are configured.
* `tryAutoRunChecks(connectionId)`: Triggers the `run-connection-checks` task if `canAutoRunChecks` returns true.
Sources: [apps/api/src/integration-platform/services/auto-check-runner.service.ts:12-89](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/services/auto-check-runner.service.ts#L12-L89)
## 6. Variable Management
Integrations can define variables that allow users to customize check behavior or provide configuration values. These variables can have static options or dynamic options fetched from the external service.
### 6.1 Variable Definition and Values
Variables are defined in the integration manifest at both the provider and check levels. The `VariablesController` allows retrieving these definitions and managing their values for a specific connection.
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :------------------------------------------------ |
| `GET` | `/integrations/variables/providers/:providerSlug` | Get all variables required for a provider's checks. |
| `GET` | `/integrations/variables/connections/:connectionId` | Get variables for a specific connection (with current values). |
| `POST` | `/integrations/variables/connections/:connectionId` | Save variable values for a connection. |
Sources: [apps/api/src/integration-platform/controllers/variables.controller.ts:32-132](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/variables.controller.ts#L32-L132)
### 6.2 Dynamic Options Fetching
For variables with dynamic options, the `VariablesController` can fetch these options from the external service using the connection's credentials. This is useful for populating dropdowns with values like AWS regions, user groups, or other configurable entities.
Sources: [apps/api/src/integration-platform/controllers/variables.controller.ts:135-249](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/variables.controller.ts#L135-L249)
## 7. Employee Synchronization
The `SyncController` provides functionality to synchronize employee data from various HRIS/IDP systems (e.g., Google Workspace, Rippling, Ramp, JumpCloud) into the platform. This ensures that the platform's user base is kept up-to-date with external systems.
### 7.1 Synchronization Process
The synchronization process typically involves:
1. Retrieving valid credentials for the connection (refreshing OAuth tokens if necessary).
2. Fetching a list of users from the external service.
3. Identifying active, inactive, or suspended users.
4. Creating new users and members in the platform for active external users.
5. Reactivating existing members if they were previously deactivated but are now active externally.
6. Deactivating members in the platform if they are inactive, suspended, or removed from the external system.
Sources: [apps/api/src/integration-platform/controllers/sync.controller.ts:25-885](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/sync.controller.ts#L25-L885)
### 7.2 Supported Sync Providers
The `SyncController` explicitly supports the following employee synchronization providers:
* **Endpoint**: `POST /integrations/sync/google-workspace/employees`
* **Status Check**: `POST /integrations/sync/google-workspace/status`
* **Details**: Fetches users via Admin SDK Directory API, handles OAuth token refresh, and manages user/member lifecycle based on Google Workspace user status (active/suspended).
* **Endpoint**: `POST /integrations/sync/rippling/employees`
* **Status Check**: `POST /integrations/sync/rippling/status`
* **Details**: Fetches workers via Rippling V2 REST API, handles OAuth token refresh, and manages user/member lifecycle based on Rippling worker status. Includes a specific call to `mark_app_installed` as required by Rippling.
* **Endpoint**: `POST /integrations/sync/ramp/employees`
* **Status Check**: `POST /integrations/sync/ramp/status`
* **Details**: Fetches users via Ramp Developer API, handles OAuth token refresh, and manages user/member lifecycle based on Ramp user status (active/inactive/suspended).
* **Endpoint**: `POST /integrations/sync/jumpcloud/employees`
* **Status Check**: `POST /integrations/sync/jumpcloud/status`
* **Details**: Fetches users and their associated systems via JumpCloud API (v1 and v2), uses API key authentication, and manages user/member lifecycle based on JumpCloud user state.
Sources: [apps/api/src/integration-platform/controllers/sync.controller.ts:25-885](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/sync.controller.ts#L25-L885)
### 7.3 Setting Employee Sync Provider
Organizations can configure which external system acts as their primary source for employee synchronization.
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :------------------------------------------------ |
| `GET` | `/integrations/sync/employee-sync-provider` | Get the current employee sync provider for an organization. |
| `POST` | `/integrations/sync/employee-sync-provider` | Set the employee sync provider for an organization. |
Sources: [apps/api/src/integration-platform/controllers/sync.controller.ts:888-963](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/sync.controller.ts#L888-L963)
## 8. Webhook Handling
The `WebhookController` is responsible for receiving and processing incoming webhooks from integrated services. It includes mechanisms for signature verification to ensure the authenticity and integrity of webhook payloads.
### 8.1 Webhook Processing Flow
When a webhook is received, the system first identifies the provider, verifies the connection, and then, if configured, validates the webhook's signature using a shared secret. If the signature is valid, the webhook payload is passed to the integration's custom handler for processing.
Sources: [apps/api/src/integration-platform/controllers/webhook.controller.ts:33-100](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/webhook.controller.ts#L33-L100)
### 8.2 Signature Verification
The `WebhookController` implements a robust signature verification process using HMAC. This protects against tampering and ensures that webhooks originate from trusted sources.
#### Key Functions
* `extractSignature(headers, headerName)`: Extracts the signature string from request headers.
* `parseSignatureValue(signature)`: Parses signature values that might include prefixes (e.g., `sha256=...`).
* `verifyHmac(rawBody, secret, algorithm, providedSignature)`: Performs a timing-safe HMAC verification against the raw request body and a stored secret.
Using `timingSafeEqual` for comparing HMAC signatures is crucial to prevent timing attacks, where an attacker could deduce parts of the secret by measuring the time it takes for the comparison function to return.
Sources: [apps/api/src/integration-platform/controllers/webhook.controller.ts:18-30](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/webhook.controller.ts#L18-L30), [apps/api/src/integration-platform/controllers/webhook.controller.ts:102-132](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/webhook.controller.ts#L102-L132)
#### API Endpoints
| Method | Path | Description |
| :----- | :------------------------------------- | :------------------------------------------------ |
| `POST` | `/integrations/webhooks/:providerSlug/:connectionId` | Receives and processes incoming webhooks for a specific connection. |
Sources: [apps/api/src/integration-platform/controllers/webhook.controller.ts:33-100](https://github.com/blade47/comp/blob/main/apps/api/src/integration-platform/controllers/webhook.controller.ts#L33-L100)
---
## Technical docs: GET Get comments for an entity
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/comments/commentscontroller-getcomments
## Parameters
## Responses
## Try It
---
## Technical docs: Cloud Security
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-5/cloud-security
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/cloud-security/cloud-security.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts)
- [apps/api/src/cloud-security/providers/azure-security.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/azure-security.service.ts)
- [apps/api/src/cloud-security/providers/aws-security.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/aws-security.service.ts)
- [apps/api/src/cloud-security/cloud-security.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.controller.ts)
- [apps/api/src/cloud-security/providers/gcp-security.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/gcp-security.service.ts)
- [apps/api/src/cloud-security/cloud-security.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.module.ts)
The Cloud Security module provides functionality to scan various cloud providers (AWS, Azure, GCP) for security findings. It integrates with the system's credential management to securely access cloud resources, orchestrates scan tasks, and stores the collected security findings in the database. This module is crucial for maintaining a continuous security posture across integrated cloud environments.
At a high level, the module exposes API endpoints to trigger and monitor security scans. The core logic handles authentication, delegates to provider-specific services for actual scanning, and then processes and persists the results.
## Architecture Overview
The Cloud Security module is structured around a central `CloudSecurityService` which orchestrates the scanning process, and provider-specific services (`AWSSecurityService`, `AzureSecurityService`, `GCPSecurityService`) that handle the unique API interactions for each cloud platform. The `CloudSecurityController` exposes the external API for initiating and monitoring scans.
Sources: [apps/api/src/cloud-security/cloud-security.module.ts:1-17](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.module.ts#L1-L17), [apps/api/src/cloud-security/cloud-security.service.ts:20-30](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L20-L30), [apps/api/src/cloud-security/cloud-security.controller.ts:13-16](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.controller.ts#L13-L16)
## Data Structures
The module defines key interfaces for representing security findings and scan results.
### SecurityFinding
The `SecurityFinding` interface represents a single security issue identified during a scan.
| Field | Type | Description |
| :------------- | :----------------------------------- | :------------------------------------------------------------------------ |
| `id` | `string` | Unique identifier for the finding. |
| `title` | `string` | A brief title for the finding. |
| `description` | `string` | Detailed description of the finding. |
| `severity` | `'info' \| 'low' \| 'medium' \| 'high' \| 'critical'` | The severity level of the finding. |
| `resourceType` | `string` | The type of cloud resource affected (e.g., `EC2 Instance`, `Storage Account`). |
| `resourceId` | `string` | The identifier of the affected resource. |
| `remediation` | `string?` | Optional steps to remediate the finding. |
| `evidence` | `Record?` | Optional additional context or raw data related to the finding. |
| `createdAt` | `string` | Timestamp when the finding was created or observed. |
| `passed` | `boolean?` | Indicates if this is a passing check (default: `false`). |
Sources: [apps/api/src/cloud-security/cloud-security.service.ts:10-20](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L10-L20)
### ScanResult
The `ScanResult` interface encapsulates the outcome of a security scan for a given connection.
| Field | Type | Description |
| :---------- | :------------------- | :--------------------------------------------- |
| `success` | `boolean` | Indicates if the scan completed successfully. |
| `provider` | `string` | The cloud provider that was scanned. |
| `findings` | `SecurityFinding[]` | An array of security findings discovered. |
| `scannedAt` | `string` | Timestamp when the scan was performed. |
| `error` | `string?` | Optional error message if the scan failed. |
Sources: [apps/api/src/cloud-security/cloud-security.service.ts:22-29](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L22-L29)
## Cloud Security Service (`CloudSecurityService`)
The `CloudSecurityService` is the core service responsible for orchestrating cloud security scans. It handles connection validation, credential retrieval (including OAuth token refresh), delegation to provider-specific scanning logic, and persistence of scan results.
### Scan Process (`scan` method)
The `scan` method initiates a security scan for a given `connectionId` and `organizationId`.
Sources: [apps/api/src/cloud-security/cloud-security.service.ts:32-167](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L32-L167)
#### Credential Management
The service dynamically handles credential retrieval based on the integration provider's authentication type.
```mermaid
sequenceDiagram
participant CloudSecurityService
participant CredentialVaultService
participant OAuthCredentialsService
participant IntegrationPlatform
CloudSecurityService->>IntegrationPlatform: getManifest(providerSlug)
alt If OAuth2
CloudSecurityService->>OAuthCredentialsService: getCredentials(providerSlug, orgId)
OAuthCredentialsService-->>CloudSecurityService: oauthCreds
CloudSecurityService->>CredentialVaultService: getValidAccessToken(connectionId, oauthConfig)
CredentialVaultService-->>CloudSecurityService: accessToken
CloudSecurityService->>CredentialVaultService: getDecryptedCredentials(connectionId)
CredentialVaultService-->>CloudSecurityService: decryptedCreds
CloudSecurityService->>CloudSecurityService: Combine decryptedCreds + accessToken
else If Non-OAuth
CloudSecurityService->>CredentialVaultService: getDecryptedCredentials(connectionId)
CredentialVaultService-->>CloudSecurityService: decryptedCreds
CloudSecurityService->>CloudSecurityService: Use decryptedCreds directly
end
CloudSecurityService-->>CloudSecurityService: Credentials ready for provider scan
```
Sources: [apps/api/src/cloud-security/cloud-security.service.ts:60-99](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L60-L99)
### Triggering and Monitoring Scans
The `CloudSecurityService` also provides methods to trigger scans asynchronously and monitor their status using `@trigger.dev/sdk`.
- **`triggerScan(connectionId: string, organizationId: string)`**: Initiates an asynchronous scan by triggering a `run-cloud-security-scan` task. This returns a `runId` to track the task's progress.
- **`getRunStatus(runId: string, connectionId: string, organizationId: string)`**: Retrieves the status of a previously triggered scan run. It verifies the connection's ownership and fetches the run status from the `@trigger.dev/sdk`.
The `triggerScan` method leverages an external task orchestration system (`@trigger.dev/sdk`) to execute the scan asynchronously. This prevents long-running HTTP requests and allows for more robust background processing.
Sources: [apps/api/src/cloud-security/cloud-security.service.ts:170-218](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L170-L218)
### Storing Findings (`storeFindings` method)
After a scan, the `storeFindings` private method persists the `SecurityFinding` objects into the database. It creates an `IntegrationCheckRun` record and then `IntegrationCheckResult` records for each finding within a database transaction to ensure data consistency.
```typescript
// Example of storing findings
await db.$transaction(async (tx) => {
const scanRun = await tx.integrationCheckRun.create({
data: {
connectionId,
checkId: `${provider}-security-scan`,
// ... other run details
},
});
if (findings.length > 0) {
await tx.integrationCheckResult.createMany({
data: findings.map((finding) => ({
checkRunId: scanRun.id,
passed: finding.passed ?? false,
// ... other finding details
})),
});
}
});
```
Sources: [apps/api/src/cloud-security/cloud-security.service.ts:220-256](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.service.ts#L220-L256)
## Cloud Security Controller (`CloudSecurityController`)
The `CloudSecurityController` exposes the REST API endpoints for interacting with the Cloud Security module.
| Method | Path | Guard | Description ---
### Cloud Security Module
The `CloudSecurityModule` is responsible for setting up the Cloud Security feature. It imports necessary modules, registers controllers, and provides the services required for cloud security scanning.
```typescript
@Module({
imports: [IntegrationPlatformModule, AuthModule],
controllers: [CloudSecurityController],
providers: [
CloudSecurityService,
GCPSecurityService,
AWSSecurityService,
AzureSecurityService,
],
exports: [CloudSecurityService],
})
export class CloudSecurityModule {}
```
Sources: [apps/api/src/cloud-security/cloud-security.module.ts:6-17](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/cloud-security.module.ts#L6-L17)
## Provider-Specific Security Services
Each cloud provider has a dedicated service to handle its unique API interactions for security scanning.
### AWS Security Service (`AWSSecurityService`)
The `AWSSecurityService` is responsible for scanning AWS environments for security findings, primarily leveraging AWS Security Hub.
#### Key Features:
- **Credential Handling**: Supports both IAM Role assumption (two-hop process) and direct Access Key authentication.
- **Region Awareness**: Scans multiple AWS regions, determined from connection credentials or variables, defaulting to `us-east-1`.
- **Security Hub Integration**: Fetches findings from AWS Security Hub.
- **Error Tolerance**: Handles per-region failures gracefully (e.g., Security Hub not enabled in a region) by logging warnings and continuing with other regions.
#### IAM Role Assumption Flow
When using IAM Role authentication, the service performs a two-hop role assumption:
1. Assumes an internal `SECURITY_HUB_ROLE_ASSUMER_ARN` role.
2. Using the credentials from the first hop, assumes the customer's specified `customerRoleArn` with an `externalId`.
```mermaid
sequenceDiagram
participant AppService as CloudSecurityService
participant STSClientBase as AWS STS (Base)
participant STSClientAssumer as AWS STS (Assumer)
participant CustomerAWS as Customer AWS Account
AppService->>STSClientBase: AssumeRoleCommand (RoleAssumer ARN)
STSClientBase-->>AppService: Temp Creds (for RoleAssumer)
AppService->>STSClientAssumer: AssumeRoleCommand (Customer Role ARN, External ID)
STSClientAssumer-->>AppService: Temp Creds (for Customer Role)
AppService->>CustomerAWS: Perform Security Hub Scan (using Customer Role Temp Creds)
CustomerAWS-->>AppService: Security Findings
```
Sources: [apps/api/src/cloud-security/providers/aws-security.service.ts:133-184](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/aws-security.service.ts#L133-L184)
#### Finding Mapping
AWS Security Hub findings are mapped to the generic `SecurityFinding` interface, including severity mapping and region appending to the title for clarity.
Sources: [apps/api/src/cloud-security/providers/aws-security.service.ts:230-267](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/aws-security.service.ts#L230-L267)
### Azure Security Service (`AzureSecurityService`)
The `AzureSecurityService` focuses on collecting security alerts and assessments from Azure subscriptions.
#### Key Features:
- **OAuth2 Authentication**: Obtains an access token using client credentials flow (`tenantId`, `clientId`, `clientSecret`).
- **Microsoft Defender for Cloud Integration**: Fetches security alerts and assessments.
- **Pagination**: Handles paginated API responses to retrieve all available findings.
- **Permission Handling**: Provides specific remediation advice if 403 (AuthorizationFailed) errors are encountered, suggesting the "Security Reader" role.
Sources: [apps/api/src/cloud-security/providers/azure-security.service.ts:32-192](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/azure-security.service.ts#L32-L192)
### GCP Security Service (`GCPSecurityService`)
The `GCPSecurityService` integrates with Google Cloud Security Command Center to retrieve security findings.
#### Key Features:
- **OAuth2 Authentication**: Requires an access token (obtained via the `CloudSecurityService`'s credential flow).
- **Organization-Level Scan**: Scans findings at the GCP organization level.
- **Security Command Center Integration**: Queries the Security Command Center API for active findings.
- **Pagination**: Iterates through paginated results to collect all findings.
- **Permission Handling**: Provides specific remediation advice for `PERMISSION_DENIED` errors, suggesting the "Security Center Findings Viewer" role.
Sources: [apps/api/src/cloud-security/providers/gcp-security.service.ts:20-109](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/gcp-security.service.ts#L20-L109)
When integrating with cloud providers, correct permissions are critical. The services are designed to provide specific feedback for common permission-related issues:
- **AWS**: If Security Hub is not enabled in a region or `AccessDenied` errors occur, the service logs a warning and skips that region. For role assumption, ensure the `SECURITY_HUB_ROLE_ASSUMER_ARN` is configured and the customer's IAM role trust policy allows assumption by the `roleAssumer`.
Sources: [apps/api/src/cloud-security/providers/aws-security.service.ts:109-112](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/aws-security.service.ts#L109-L112), [apps/api/src/cloud-security/providers/aws-security.service.ts:140-143](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/aws-security.service.ts#L140-L143)
- **Azure**: For 403 or `AuthorizationFailed` errors when fetching alerts or assessments, the service adds a `SecurityFinding` indicating "Unable to access Security Alerts/Assessments" with a remediation step: "Assign the 'Security Reader' role to your App Registration on the subscription."
Sources: [apps/api/src/cloud-security/providers/azure-security.service.ts:80-92](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/azure-security.service.ts#L80-L92), [apps/api/src/cloud-security/providers/azure-security.service.ts:133-145](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/azure-security.service.ts#L133-L145)
- **GCP**: For `ACCESS_TOKEN_SCOPE_INSUFFICIENT` or `PERMISSION_DENIED` (403) errors, the service throws errors with specific messages: "OAuth scopes insufficient. Please disconnect and reconnect the GCP integration." or "Permission denied. Grant the 'Security Center Findings Viewer' role to your Google account at the organization level."
Sources: [apps/api/src/cloud-security/providers/gcp-security.service.ts:80-93](https://github.com/blade47/comp/blob/main/apps/api/src/cloud-security/providers/gcp-security.service.ts#L80-L93)
---
## Technical docs: Notifications
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-5/notifications
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/comments/comment-mention-notifier.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comment-mention-notifier.service.ts)
- [apps/api/src/notifications/novu.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/notifications/novu.service.ts)
- [apps/api/src/email/templates/finding-notification.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/finding-notification.tsx)
- [apps/api/src/email/templates/comment-mentioned.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/comment-mentioned.tsx)
- [apps/api/src/email/resend.ts](https://github.com/blade47/comp/blob/main/apps/api/src/email/resend.ts)
- [apps/api/src/email/components/footer.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/footer.tsx)
- [apps/api/src/email/components/logo.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/logo.tsx)
- [apps/api/src/email/templates/task-status-changed.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-status-changed.tsx)
The notification system provides mechanisms for alerting users about significant events within the application, such as mentions in comments, changes in task status, or new findings. It leverages both email and in-app notifications to ensure timely communication. The system is designed to be flexible, allowing for different notification types and user-specific preferences, including unsubscribe options.
At its core, the notification architecture integrates with external services like Novu for in-app notifications and Resend for email delivery, while utilizing React-based templates for consistent and branded email content.
## Notification Architecture Overview
The notification system is composed of several key services and components that work together to deliver alerts to users. This includes a dedicated service for handling in-app notifications via Novu, a utility for sending emails via Resend, and various React components for rendering email templates. A primary example of its usage is the `CommentMentionNotifierService`, which orchestrates the process of notifying users when they are mentioned in a comment.
Sources: [apps/api/src/comments/comment-mention-notifier.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comment-mention-notifier.service.ts#L1-L269), [apps/api/src/notifications/novu.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/notifications/novu.service.ts#L1-L57), [apps/api/src/email/resend.ts](https://github.com/blade47/comp/blob/main/apps/api/src/email/resend.ts#L1-L89)
## In-App Notifications with Novu
The `NovuService` is responsible for sending in-app notifications by interacting with the Novu API. It provides a `trigger` method that allows other services to initiate notification workflows.
### NovuService Structure
The `NovuService` is an injectable NestJS service that encapsulates the logic for calling the Novu API.
```typescript
@Injectable()
export class NovuService {
private readonly logger = new Logger(NovuService.name);
async trigger(params: {
workflowId: string;
subscriberId: string;
email: string;
payload: Record;
}): Promise {
// ... implementation ...
}
}
```
Sources: [apps/api/src/notifications/novu.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/notifications/novu.service.ts#L3-L12)
### Triggering Novu Workflows
The `trigger` method sends a POST request to the Novu API endpoint `/v1/events/trigger`. It requires a `workflowId`, `subscriberId`, `email`, and a `payload` containing dynamic data for the notification. The `NOVU_API_KEY` environment variable is essential for authentication. If it's not configured, a warning is logged, and the notification is skipped.
The `NOVU_API_KEY` environment variable must be configured for Novu in-app notifications to function. Without it, the `NovuService` will log a warning and skip triggering any workflows.
```mermaid
sequenceDiagram
participant Caller as Calling Service
participant NovuS as NovuService
participant NovuAPI as Novu API
Caller->>NovuS: trigger({workflowId, subscriberId, email, payload})
alt NOVU_API_KEY is missing
NovuS-->>Caller: Logs warning, returns
else NOVU_API_KEY is present
NovuS->>NovuAPI: POST /v1/events/trigger Headers: Authorization, Content-Type Body: {name: workflowId, to: {subscriberId, email}, payload}
alt API call fails
NovuAPI--xNovuS: HTTP Error Response (e.g., 4xx, 5xx)
NovuS-->>Caller: Logs error, returns
else API call succeeds
NovuAPI-->>NovuS: HTTP 200 OK
NovuS-->>Caller: Logs success, returns
end
end
```
Sources: [apps/api/src/notifications/novu.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/notifications/novu.service.ts#L14-L57)
## Email Notifications with Resend
Email notifications are handled through the `sendEmail` utility, which integrates with the Resend email service. This utility allows for sending rich HTML emails using React components as templates.
### Resend Configuration
The `resend` client is initialized using the `RESEND_API_KEY` environment variable. If this key is not present, the `resend` object will be `null`, and any attempt to send an email will result in an error.
```typescript
export const resend = process.env.RESEND_API_KEY
? new Resend(process.env.RESEND_API_KEY)
: null;
```
Sources: [apps/api/src/email/resend.ts](https://github.com/blade47/comp/blob/main/apps/api/src/email/resend.ts#L4-L6)
### The `sendEmail` Function
The `sendEmail` function is a central utility for dispatching emails. It takes various parameters, including the recipient, subject, and a React component for the email body. It also supports different `from` addresses based on whether the email is `marketing` or `system` related.
| Parameter | Type | Description
Sources: [apps/api/src/notifications/novu.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/notifications/novu.service.ts#L3-L57), [apps/api/src/email/resend.ts](https://github.com/blade47/comp/blob/main/apps/api/src/email/resend.ts#L1-L89), [apps/api/src/comments/comment-mention-notifier.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comment-mention-notifier.service.ts#L1-L269), [apps/api/src/email/templates/comment-mentioned.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/comment-mentioned.tsx#L1-L105), [apps/api/src/email/templates/finding-notification.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/finding-notification.tsx#L1-L127), [apps/api/src/email/templates/task-status-changed.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-status-changed.tsx#L1-L103), [apps/api/src/email/components/footer.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/footer.tsx#L1-L12), [apps/api/src/email/components/logo.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/logo.tsx#L1-L10)
## Email Templates
The system uses React components to define email templates, ensuring a consistent look and feel across different notification types. These templates leverage `@react-email/components` for structuring the email content and Tailwind CSS for styling.
### Common Email Components
All email templates utilize shared components for branding and consistency:
* **`Logo`**: Displays the Comp AI logo at the top of the email.
* **`Footer`**: Contains standard footer information, including a link to Comp AI and unsubscribe options.
Sources: [apps/api/src/email/components/logo.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/logo.tsx#L1-L10), [apps/api/src/email/components/footer.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/footer.tsx#L1-L12)
### Specific Email Templates
#### `CommentMentionedEmail`
This template is used when a user is mentioned in a comment. It displays who mentioned the user, the entity where the mention occurred, and a snippet of the comment content. It also provides a direct link to view the comment.
```typescript
interface Props {
toName: string;
toEmail: string;
commentContent: string;
mentionedByName: string;
entityName: string;
entityRoutePath: string;
entityId: string;
organizationId: string;
commentUrl: string;
}
```
The `CommentMentionedEmail` component includes a `getPlainText` utility to extract readable text from TipTap JSON content, which is then used for the email preview and display.
Sources: [apps/api/src/email/templates/comment-mentioned.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/comment-mentioned.tsx#L14-L44)
#### `FindingNotificationEmail`
This template is used for notifications related to findings, such as new findings or status updates. It includes details like the organization, task title, finding type, content, and a link to view the finding.
```typescript
interface Props {
toName: string;
toEmail: string;
heading: string;
message: string;
taskTitle: string;
organizationName: string;
findingType: string;
findingContent: string;
newStatus?: string;
findingUrl: string;
}
```
Sources: [apps/api/src/email/templates/finding-notification.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/finding-notification.tsx#L14-L24)
#### `TaskStatusChangedEmail`
This template informs users when the status of a task has changed. It specifies the old and new statuses, who made the change, and provides a link to the task.
```typescript
interface Props {
toName: string;
toEmail: string;
taskTitle: string;
oldStatus: string;
newStatus: string;
changedByName: string;
organizationName: string;
taskUrl: string;
}
```
Sources: [apps/api/src/email/templates/task-status-changed.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-status-changed.tsx#L14-L23)
## Comment Mention Notification Flow
The `CommentMentionNotifierService` is a concrete implementation of the notification system, specifically designed to handle mentions within comments. It ensures that mentioned users receive both email and in-app notifications, provided they have not unsubscribed.
### `notifyMentionedUsers` Method
This method orchestrates the entire process of notifying users mentioned in a comment.
### Extract Mentioned User IDs
The `extractMentionedUserIds` function parses the comment content (expected to be in a JSON format, potentially from a rich text editor like TipTap) to identify all unique user IDs that have been mentioned.
### Retrieve User Information
It fetches details for the user who made the mention (`mentionedByUserId`) and all the mentioned users (`mentionedUserIds`) from the database.
### Normalize Context URL and Build Fallback
The system attempts to normalize the provided `contextUrl` to ensure it's a valid and safe URL within the application's allowed origins and organization. If no valid `contextUrl` is provided or it's invalid, `buildFallbackCommentContext` generates a default URL based on the comment's `entityType` and `entityId` (e.g., task, vendor, risk, policy). This ensures notifications always link to a relevant page.
### Check Unsubscribe Status
Before sending any notification, the system checks if the recipient user has unsubscribed from "task mentions" (currently used as the preference for comment mentions). If unsubscribed, the notification is skipped for that user.
### Send Email Notification
For each eligible mentioned user, an email is sent using the `sendEmail` utility with the `CommentMentionedEmail` React component. The email includes details about the mention, the entity, and a direct link to the comment.
### Send In-App Notification
Concurrently, an in-app notification is triggered via the `NovuService` for each eligible mentioned user. This notification uses the `comment-mentioned` workflow and includes relevant payload data.
Sources: [apps/api/src/comments/comment-mention-notifier.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comment-mention-notifier.service.ts#L1-L269)
### URL Context Resolution
The notification system includes robust logic to construct valid and secure URLs for linking to specific entities within the application.
* **`getAppBaseUrl()`**: Retrieves the base URL for the application, prioritizing `NEXT_PUBLIC_APP_URL`, then `BETTER_AUTH_URL`, and falling back to `https://app.trycomp.ai`.
* **`getAllowedOrigins()`**: Gathers a list of allowed origins from environment variables to validate incoming `contextUrl`s.
* **`tryNormalizeContextUrl()`**: Validates a provided `contextUrl` against allowed origins and ensures it belongs to the correct organization to prevent malicious deep-linking.
* **`buildFallbackCommentContext()`**: If a `contextUrl` is not provided or invalid, this function constructs a URL based on the `entityType` (e.g., `task`, `vendor`, `risk`, `policy`) and `entityId`, querying the database to retrieve relevant entity names and routes.
The `tryNormalizeContextUrl` and `buildFallbackCommentContext` functions are critical for security, ensuring that notification links point to valid, internal application pages and prevent potential path traversal or phishing attempts by validating against allowed origins and organization IDs.
Sources: [apps/api/src/comments/comment-mention-notifier.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comment-mention-notifier.service.ts#L41-L150)
## Unsubscribe Mechanism
The system integrates with an unsubscribe mechanism (`isUserUnsubscribed` from `@trycompai/email`) to respect user preferences. When sending notifications, it checks if a user has opted out of specific notification types. For comment mentions, the `taskMentions` preference is currently used.
Sources: [apps/api/src/comments/comment-mention-notifier.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comment-mention-notifier.service.ts#L187-L191)
---
## Technical docs: POST Create a new comment
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/comments/commentscontroller-createcomment
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: PUT Update a comment
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/comments/commentscontroller-updatecomment
## Parameters
## Request Body
## Responses
## Try It
---
## Technical docs: Comments System
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-5/comments-system
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts)
- [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts)
- [apps/api/src/comments/dto/comment-responses.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/comment-responses.dto.ts)
- [apps/api/src/comments/comments.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.module.ts)
- [apps/api/src/comments/dto/update-comment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/update-comment.dto.ts)
- [apps/api/src/comments/dto/create-comment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/create-comment.dto.ts)
- [apps/api/src/comments/dto/delete-comment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/delete-comment.dto.ts)
The Comments System provides a robust API for managing comments associated with various entities within the application, such as tasks, policies, vendors, and risks. It supports creating, retrieving, updating, and deleting comments, including handling file attachments and user mentions. The system is designed to ensure data consistency through transactions and integrates with an attachment service for file management and a notification service for user mentions.
## Architecture Overview
The Comments System is implemented as a NestJS module, following a standard Controller-Service pattern.
* **`CommentsController`**: Handles incoming HTTP requests, validates input, and delegates business logic to the `CommentsService`. It defines the API endpoints for comment operations.
* **`CommentsService`**: Contains the core business logic for managing comments, interacting with the database, `AttachmentsService`, and `CommentMentionNotifierService`. It also includes access validation for entities.
* **DTOs (Data Transfer Objects)**: Define the structure for request payloads and API responses, ensuring clear data contracts.
* **`CommentsModule`**: Orchestrates the components, declares providers and controllers, and manages dependencies.
The system leverages `HybridAuthGuard` for flexible authentication (JWT or API Key) and uses `AuthContext` to determine the authenticated user and organization.
Sources: [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts#L22-L30), [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L21-L24), [apps/api/src/comments/comments.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.module.ts#L6-L12)
## Data Transfer Objects (DTOs)
The Comments System uses several DTOs to structure data for requests and responses.
### Request DTOs
* **`CreateCommentDto`**: Used when creating a new comment.
* `content`: The text content of the comment (max 2000 characters).
* `entityId`: The ID of the entity the comment belongs to.
* `entityType`: The type of entity (`task`, `policy`, `vendor`, `risk`).
* `contextUrl?`: Optional URL for deep-linking in notifications.
* `attachments?`: An array of `UploadAttachmentDto` for files to be attached.
* `userId?`: Required for API key authentication to specify the author.
* **`UpdateCommentDto`**: Used when updating an existing comment.
* `content`: The new text content of the comment (max 2000 characters).
* `contextUrl?`: Optional updated URL for deep-linking.
* `userId?`: Required for API key authentication to specify the author.
* **`DeleteCommentDto`**: Used when deleting a comment.
* `userId?`: Required for API key authentication to specify the user performing the deletion.
Sources: [apps/api/src/comments/dto/create-comment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/create-comment.dto.ts#L9-L55), [apps/api/src/comments/dto/update-comment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/update-comment.dto.ts#L6-L34), [apps/api/src/comments/dto/delete-comment.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/delete-comment.dto.ts#L5-L15)
### Response DTOs
The primary response DTO is `CommentResponseDto`, which includes nested DTOs for author information and attachment metadata.
* **`CommentResponseDto`**: Represents a single comment returned by the API.
* `id`: Unique identifier for the comment.
* `content`: The comment's text content.
* `author`: An `AuthorResponseDto` object containing details about the comment's author.
* `attachments`: An array of `AttachmentMetadataDto` objects, providing metadata about attached files (without download URLs, which are generated on-demand).
* `createdAt`: Timestamp of when the comment was created.
* **`AuthorResponseDto`**: Details about the user who authored the comment.
* `id`, `name`, `email`, `image`, `deactivated`.
* **`AttachmentMetadataDto`**: Basic metadata for an attachment associated with a comment.
* `id`, `name`, `type`, `createdAt`.
* **`AttachmentResponseDto`**: Similar to `AttachmentMetadataDto` but includes a `downloadUrl` and `size`. This is used internally by the service when handling attachments, but `AttachmentMetadataDto` is returned in `CommentResponseDto`.
Sources: [apps/api/src/comments/dto/comment-responses.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/dto/comment-responses.dto.ts#L4-L95)
## API Endpoints
The `CommentsController` exposes the following REST API endpoints:
| Method | Path | Description | Request Body | Response Type | Authentication |
| :----- | :-------------------- | :------------------------------------------------------------------------ | :-------------------- | :------------------- | :------------- |
| `GET` | `/comments` | Retrieve all comments for a specific entity. | Query: `entityId`, `entityType` | `CommentResponseDto[]` | HybridAuthGuard |
| `POST` | `/comments` | Create a new comment on an entity with optional attachments. | `CreateCommentDto` | `CommentResponseDto` | HybridAuthGuard |
| `PUT` | `/comments/:commentId` | Update the content of an existing comment (author only). | `UpdateCommentDto` | `CommentResponseDto` | HybridAuthGuard |
| `DELETE` | `/comments/:commentId` | Delete a comment and all its attachments (author only). | `DeleteCommentDto` | `{ success: boolean, deletedCommentId: string, message: string }` | HybridAuthGuard |
All endpoints require an `X-Organization-Id` header for session authentication, which is optional for API key authentication.
Sources: [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts#L22-L218)
## Core Logic: `CommentsService`
The `CommentsService` encapsulates the business logic for comment management.
### Entity Access Validation
Before performing any comment operation, the service validates that the target entity exists within the specified organization and that the user has access. This is handled by the `validateEntityAccess` method.
The supported `CommentEntityType` values are:
* `task`: Checks for `TaskItem` first, then `Task`.
* `policy`: Checks for `Policy`.
* `vendor`: Checks for `Vendor`. Includes logging if a vendor exists in a different organization.
* `risk`: Checks for `Risk`.
Sources: [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L40-L124)
### Creating a Comment
The `createComment` method handles the creation of new comments, including optional attachments and user mention notifications.
### Validate Entity Access
The system first calls `validateEntityAccess` to ensure the `entityId` and `entityType` are valid and accessible within the `organizationId`.
### Verify Member
It retrieves the `Member` record for the `userId` within the `organizationId` to confirm the user is an active member.
### Create Comment and Attachments in Transaction
A database transaction is used to ensure atomicity.
1. The comment record is created in the database.
2. If `createCommentDto.attachments` are provided, each attachment is uploaded via `AttachmentsService.uploadAttachment` and associated with the new comment.
### Notify Mentioned Users
After the comment is successfully created, the `extractMentionedUserIds` utility parses the comment content for user mentions. If any users are mentioned, `CommentMentionNotifierService.notifyMentionedUsers` is called asynchronously to send notifications.
Sources: [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L162-L253)
```mermaid
sequenceDiagram
participant Client
participant CommentsController
participant CommentsService
participant AttachmentsService
participant CommentMentionNotifierService
participant Database
Client->>CommentsController: POST /comments (CreateCommentDto)
CommentsController->>CommentsService: createComment(orgId, userId, dto)
CommentsService->>CommentsService: validateEntityAccess(orgId, dto.entityId, dto.entityType)
CommentsService->>Database: Find Member (userId, orgId)
alt Member not found
CommentsService-->>CommentsController: BadRequestException
CommentsController-->>Client: 400 Bad Request
else Member found
CommentsService->>Database: Begin Transaction
CommentsService->>Database: Create Comment
opt Attachments present
loop For each attachment
CommentsService->>AttachmentsService: uploadAttachment(orgId, commentId, type, attachmentDto, userId)
AttachmentsService-->>CommentsService: AttachmentResponseDto
end
end
CommentsService->>Database: Commit Transaction
CommentsService->>CommentsService: extractMentionedUserIds(content)
opt Users mentioned
CommentsService->>CommentMentionNotifierService: notifyMentionedUsers(notificationData)
end
CommentsService-->>CommentsController: CommentResponseDto
CommentsController-->>Client: 201 Created (CommentResponseDto)
end
```
### Retrieving Comments
The `getComments` method fetches all comments for a given entity.
1. It first validates entity access using `validateEntityAccess`.
2. It queries the database for comments matching the `organizationId`, `entityId`, and `entityType`, ordering them by creation date (descending).
3. For each comment, it retrieves attachment metadata using `AttachmentsService.getAttachmentMetadata`.
4. The results are mapped to `CommentResponseDto` objects, including author details and attachment metadata.
Sources: [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L127-L160)
### Updating a Comment
The `updateComment` method allows modifying the content of an existing comment.
1. It retrieves the existing comment and verifies that the `userId` is the author of the comment.
2. The comment's `content` is updated in the database.
3. Existing attachments are retrieved using `AttachmentsService.getAttachments`.
4. It compares the newly mentioned users with previously mentioned users in the comment content. Only newly mentioned users are notified via `CommentMentionNotifierService`.
Sources: [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L256-L338)
### Deleting a Comment
The `deleteComment` method removes a comment and its associated attachments.
1. It retrieves the existing comment and verifies that the `userId` is the author of the comment.
2. A database transaction is initiated.
3. All attachments linked to the comment are retrieved.
4. Each attachment is deleted from storage via `AttachmentsService.deleteAttachment`.
5. Finally, the comment record is deleted from the database.
Sources: [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L341-L397)
## Mention Notification
The system includes functionality to notify users who are mentioned in a comment.
The `extractMentionedUserIds` utility function parses the comment content (expected to be JSON) to find user IDs marked as 'mention' nodes.
When a comment is created or updated, if new mentions are detected, the `CommentMentionNotifierService` is used to send notifications to the mentioned users. This process is fire-and-forget to avoid blocking comment operations.
Sources: [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L10-L33), [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L225-L242), [apps/api/src/comments/comments.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.service.ts#L310-L327)
## Authentication and Authorization
The `CommentsController` uses `HybridAuthGuard` to support both JWT (session-based) and API key authentication.
The `@AuthContext()` decorator provides an `AuthContextType` object, which indicates whether the request originated from an API key (`isApiKey`) and provides the `userId`.
When using API key authentication, the `userId` must be explicitly provided in the request body (`CreateCommentDto`, `UpdateCommentDto`, `DeleteCommentDto`). For JWT authentication, the `userId` is extracted directly from the authenticated session. This ensures that the correct user context is always available for authorization checks (e.g., verifying comment authorship).
Sources: [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts#L26-L30), [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts#L86-L101), [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts#L130-L145), [apps/api/src/comments/comments.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.controller.ts#L191-L206)
## Module Structure
The `CommentsModule` integrates all necessary components for the Comments System.
```typescript
@Module({
imports: [AuthModule, AttachmentsModule],
controllers: [CommentsController],
providers: [CommentsService, CommentMentionNotifierService, NovuService],
exports: [CommentsService],
})
export class CommentsModule {}
```
* **`imports`**:
* `AuthModule`: Provides authentication-related services and guards, including `HybridAuthGuard`.
* `AttachmentsModule`: Provides `AttachmentsService` for handling file uploads and management.
* **`controllers`**: Registers `CommentsController` to handle API routes.
* **`providers`**: Registers `CommentsService`, `CommentMentionNotifierService`, and `NovuService` as injectable services.
* **`exports`**: Makes `CommentsService` available for injection into other modules if needed.
Sources: [apps/api/src/comments/comments.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/comments/comments.module.ts#L6-L12)
---
## Technical docs: Operations Endpoints
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-5/operations-endpoints
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/browserbase/browserbase.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/browserbase.service.ts)
- [apps/api/src/browserbase/browserbase.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/browserbase.controller.ts)
- [apps/api/src/device-agent/schemas/device-agent-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/device-agent/schemas/device-agent-operations.ts)
- [apps/api/src/browserbase/dto/browserbase.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/dto/browserbase.dto.ts)
- [apps/api/src/device-agent/device-agent.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/device-agent/device-agent.controller.ts)
- [apps/api/src/device-agent/device-agent.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/device-agent/device-agent.service.ts)
- [apps/api/src/health/health.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/health/health.controller.ts)
- [apps/api/src/health/health.module.ts](https://github.com/blade4s/comp/blob/main/apps/api/src/health/health.module.ts)
- [apps/api/src/device-agent/scripts/common.ts](https://github.com/blade47/comp/blob/main/apps/api/src/device-agent/scripts/common.ts)
- [apps/api/src/devices/devices.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/devices/devices.controller.ts)
- [apps/api/src/devices/devices.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/devices/devices.service.ts)
- [apps/api/src/lib/fleet.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/lib/fleet.service.ts)
This document outlines the various operational endpoints exposed by the API, covering browser automation, device agent management, device listing, and health checks. These endpoints facilitate interaction with external services like Browserbase HQ and internal device management systems, providing functionalities ranging from automated browser tasks to device monitoring and agent distribution.
The API is structured using NestJS controllers and services, with authentication handled by `HybridAuthGuard` and organization context managed via `X-Organization-Id` headers or API keys.
## Browser Automation Endpoints
The Browser Automation endpoints provide a comprehensive interface for managing and executing automated browser tasks using Browserbase HQ. This includes managing organization-specific browser contexts, controlling browser sessions, performing navigation, and handling the lifecycle of browser automations and their execution runs.
### Architecture
The `BrowserbaseController` exposes the API endpoints, which delegate business logic to the `BrowserbaseService`. The service interacts with the `@browserbasehq/sdk` and `@browserbasehq/stagehand` libraries, as well as an S3 client for screenshot storage and a Prisma client (`@trycompai/db`) for persistence of automation configurations and run history.
A Browserbase context represents a persistent browser environment for an organization. It allows sessions to share cookies, local storage, and other browser data, enabling consistent authentication and state across multiple automation runs.
### API Endpoints
The `BrowserbaseController` (`/browserbase`) provides the following endpoints:
| Method | Path | Summary | Description | DTOs (Request/Response) |
|---|----------------------------------------|--------------------------------------------------|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| `/browserbase/org-context` | `POST` | Get or create organization browser context | Gets the existing browser context for the org or creates a new one. | Request: `{}` (Implicitly uses `X-Organization-Id` header) Response: `ContextResponseDto`
---
## Technical docs: DELETE Delete a comment
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/comments/commentscontroller-deletecomment
## Parameters
## Request Body
Delete comment request body
## Responses
## Try It
---
## Technical docs: GET Get all context entries
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/context/getallcontext
## Parameters
## Responses
## Try It
---
## Technical docs: UI Surfaces
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-6/ui-surfaces
Relevant source files
The following files were used as context for generating this wiki page:
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [apps/api/src/email/components/footer.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/footer.tsx)
- [apps/api/src/email/components/logo.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/logo.tsx)
- [apps/api/src/email/templates/access-granted.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/access-granted.tsx)
- [apps/api/src/email/templates/task-item-assigned.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-item-assigned.tsx)
The "UI Surfaces" within the Comp AI project primarily refer to the email templates and reusable components designed for transactional communications. These surfaces are built using React for Email (`@react-email/components`) and styled with Tailwind CSS, enabling a consistent and branded user experience across various automated email notifications. They are located within the `apps/api/src/email` directory, indicating their role in the API application's communication layer.
These UI surfaces serve to inform users about critical events, such as granting access to compliance documentation or assigning tasks. By leveraging a component-based approach, the system ensures maintainability, reusability, and a standardized look and feel for all outgoing emails.
## Email Components
The email UI surfaces utilize a set of foundational React components to construct the email layouts. These components encapsulate common visual elements and styling, promoting consistency across different email templates.
### Logo Component
The `Logo` component is a simple, reusable element responsible for displaying the Comp AI brand logo at the top of email communications. It uses an `Img` tag from `@react-email/components` to embed the logo image.
```tsx
import { Img, Section } from '@react-email/components';
export function Logo() {
return (
);
}
```
Sources: [apps/api/src/email/components/logo.tsx:1-14](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/logo.tsx#L1-L14)
### Footer Component
The `Footer` component provides standard legal and branding information at the bottom of email templates. It includes a link to the Comp AI website and the company's physical address.
```tsx
import { Hr, Link, Section, Text } from '@react-email/components';
export function Footer() {
return (
AI that handles compliance for you -{' '}
Comp AI.
Comp AI | 2261 Market Street, San Francisco, CA 94114
);
}
```
Sources: [apps/api/src/email/components/footer.tsx:1-18](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/footer.tsx#L1-L18)
## Email Templates
The project includes specific email templates for different transactional purposes, each designed to convey particular information to the recipient. These templates integrate the reusable `Logo` and `Footer` components.
### Access Granted Email
The `AccessGrantedEmail` template is used to notify a user that they have been granted access to an organization's policy documentation. It provides details about the access, including the organization name, expiration date, and a direct link to view the documents.
#### Props
The `AccessGrantedEmail` component expects the following properties:
| Prop Name | Type | Description |
| :--------------- | :------- | :---------------------------------------------- |
| `toName` | `string` | The name of the recipient. |
| `organizationName` | `string` | The name of the organization. |
| `expiresAt` | `Date` | The date when the access will expire. |
| `portalUrl` | `string` | The URL to the portal where documents can be viewed. |
Sources: [apps/api/src/email/templates/access-granted.tsx:13-18](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/access-granted.tsx#L13-L18)
### Task Item Assigned Email
The `TaskItemAssignedEmail` template is sent to a user when they are assigned a new task within an organization. It includes information about the task, who assigned it, and a direct link to the task. It also provides an option for the user to manage their email preferences.
#### Props
The `TaskItemAssignedEmail` component expects the following properties:
| Prop Name | Type | Description |
| :--------------- | :------- | :---------------------------------------------- |
| `toName` | `string` | The name of the recipient. |
| `toEmail` | `string` | The email address of the recipient, used for unsubscribe links. |
| `taskTitle` | `string` | The title of the assigned task. |
| `assignedByName` | `string` | The name of the user who assigned the task. |
| `organizationName` | `string` | The name of the organization where the task was assigned. |
| `taskUrl` | `string` | The URL to view the assigned task. |
The template also utilizes the `getUnsubscribeUrl` function from the `@trycompai/email` package to generate a dynamic unsubscribe link.
Sources: [apps/api/src/email/templates/task-item-assigned.tsx:17-24](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-item-assigned.tsx#L17-L24), [apps/api/src/email/templates/task-item-assigned.tsx:26](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-item-assigned.tsx#L26)
## Architecture and Design
The email UI surfaces are built upon a modern web development stack tailored for email rendering:
* **@react-email/components**: This library allows developers to write email templates using React, providing a familiar component-based approach and abstracting away the complexities of cross-client email compatibility.
* **Tailwind CSS**: Integrated via the `Tailwind` component from `@react-email/components`, it enables utility-first styling directly within the React components, ensuring responsive and consistent designs.
* **Custom Fonts**: The `Geist` font is specified for use in the emails, with `Helvetica` as a fallback, to maintain brand aesthetics.
* **Monorepo Structure**: The email components and templates are part of the `apps/api` application, suggesting they are rendered server-side as part of API-triggered workflows.
### Email Template Composition
The following diagram illustrates how the email templates are composed from shared components.
Sources: [apps/api/src/email/templates/access-granted.tsx:1-18](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/access-granted.tsx#L1-L18), [apps/api/src/email/templates/task-item-assigned.tsx:1-24](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-item-assigned.tsx#L1-L24), [apps/api/src/email/components/logo.tsx:1-14](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/logo.tsx#L1-L14), [apps/api/src/email/components/footer.tsx:1-18](https://github.com/blade47/comp/blob/main/apps/api/src/email/components/footer.tsx#L1-L18)
### Email Rendering Flow
The general flow for rendering and preparing an email for dispatch involves several steps, from data provision to final HTML generation.
Sources: [apps/api/src/email/templates/access-granted.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/access-granted.tsx), [apps/api/src/email/templates/task-item-assigned.tsx](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-item-assigned.tsx)
The `TaskItemAssignedEmail` template imports `getUnsubscribeUrl` from `@trycompai/email`. This indicates the presence of a dedicated package for email-related utilities, which likely handles common email functionalities beyond just rendering, such as generating unsubscribe links.
Sources: [apps/api/src/email/templates/task-item-assigned.tsx:10](https://github.com/blade47/comp/blob/main/apps/api/src/email/templates/task-item-assigned.tsx#L10)
---
## Technical docs: GET Get context entry by ID
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/context/getcontextbyid
## Parameters
## Responses
## Try It
---
## Technical docs: Deployment Guide
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-7/deployment-guide
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/buildspec.yml](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml)
- [apps/api/docker-compose.yml](https://github.com/blade47/comp/blob/main/apps/api/docker-compose.yml)
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [apps/api/src/config/load-env.ts](https://github.com/blade47/comp/blob/main/apps/api/src/config/load-env.ts)
This guide provides comprehensive instructions for deploying and running the Comp AI platform, covering both cloud-based CI/CD processes for the API and local development setups. It details the steps for building, packaging, and deploying the API service using AWS CodeBuild, as well as configuring a local development environment with Docker Compose, database setup, and environment variable management.
The document consolidates information from various configuration and setup files to offer a unified view of the deployment landscape for the Comp AI project.
## Cloud Deployment: AWS CodeBuild for API
The `apps/api` service utilizes an AWS CodeBuild pipeline for its continuous integration and deployment process. This pipeline automates the building of the application, creation of Docker images, and deployment to Amazon Elastic Container Registry (ECR) and Amazon Elastic Container Service (ECS).
### CodeBuild Pipeline Overview
The CodeBuild process is divided into three main phases: `pre_build`, `build`, and `post_build`, each executing a series of commands to prepare, build, and deploy the application.
Sources: [apps/api/buildspec.yml:1-85](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L1-L85)
### Pre-Build Phase
This phase focuses on initial setup and authentication required before the main build process begins.
- **ECR Login**: Authenticates with Amazon ECR using AWS credentials to allow pushing and pulling Docker images.
- **Variable Setup**: Defines `REPOSITORY_URI`, `COMMIT_HASH`, and `IMAGE_TAG` based on ECR repository URI and the Git commit hash. The `IMAGE_TAG` defaults to `latest` if no commit hash is available.
- **Dependency Manager Installation**: Installs `bun`, the JavaScript runtime and package manager used across the project.
Sources: [apps/api/buildspec.yml:4-12](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L4-L12)
### Build Phase
The core of the pipeline, where the application is built, packaged, and a Docker image is created.
#### Environment Setup and Validation
Critical environment variables are set and validated to ensure the build environment is correctly configured. The build will fail if any of these required variables are not present.
The following environment variables are critical for the API build and deployment. Their absence will cause the CodeBuild process to fail.
| Environment Variable | Description |
| :---------------------------- | :----------------------------------------------------------------------- |
| `DATABASE_URL` | Connection string for the PostgreSQL database. |
| `BASE_URL` | Base URL for the application. |
| `BETTER_AUTH_URL` | URL for the authentication service. |
| `TRUST_APP_URL` | URL for the trust application. |
| `APP_AWS_BUCKET_NAME` | AWS S3 bucket name for application assets. |
| `APP_AWS_ACCESS_KEY_ID` | AWS access key ID for S3 access. |
| `APP_AWS_SECRET_ACCESS_KEY` | AWS secret access key for S3 access. |
Other environment variables set during this phase include:
- `PATH`: Updated to include Bun's binary directory.
- `PGSSLMODE`: Set to `require` for PostgreSQL SSL.
- `NODE_ENV`: Set to `production`.
- `NEXT_TELEMETRY_DISABLED`: Set to `1` to disable Next.js telemetry.
- `UV_THREADPOOL_SIZE`: Set to `36`.
- `NODE_OPTIONS`: Set to `--max-old-space-size=65536` to increase Node.js memory limit.
Sources: [apps/api/buildspec.yml:15-30](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L15-L30)
#### Application Build Process
1. **Dependency Installation**: Installs only the API workspace dependencies using `bun install --filter=@comp/api`.
2. **Workspace Package Build**: Builds shared packages (`packages/db`, `packages/integration-platform`) that the API depends on.
3. **NestJS Application Build**: Navigates to `apps/api` and executes `bun run build` to compile the NestJS application.
4. **Build Output Handling**: Copies the compiled API application files (`dist/`) into a temporary `../docker-build` directory, handling potential variations in the output structure (`dist/apps/api/src` or `dist/src`).
5. **Prisma Schema Copy**: Copies the `prisma` directory to the Docker build context.
6. **Node Modules & Workspace Symlink Resolution**:
* Copies the root `node_modules` directory.
* Removes workspace symlinks for `@trycompai/utils`, `@trycompai/db`, and `@comp/integration-platform`.
* Replaces these symlinks with the actual built output and `package.json` files from their respective `packages/` directories.
7. **Dockerfile Preparation**: Copies the `Dockerfile` to the Docker build context and modifies `package.json` to remove the workspace dependency for `@comp/integration-platform` (as it's handled manually).
8. **Docker Image Build**: Constructs the Docker image using the prepared `../docker-build` context and tags it with the ECR repository URI and the generated `IMAGE_TAG`. It also tags the image with `latest`.
Sources: [apps/api/buildspec.yml:33-77](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L33-L77)
### Post-Build Phase
This phase handles the final deployment steps after the Docker image is successfully built.
- **ECR Push**: Pushes the newly built Docker image (both tagged and `latest`) to Amazon ECR.
- **ECS Service Update**: Triggers a forced new deployment for the specified ECS service, ensuring the ECS cluster pulls the latest image.
- **Image Definitions**: Generates an `imagedefinitions.json` file, which is used by AWS CodePipeline or other services to specify the image URI for the deployed container.
Sources: [apps/api/buildspec.yml:80-85](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L80-L85)
### Caching and Artifacts
The CodeBuild configuration includes caching paths for `node_modules` and Bun's install cache to speed up subsequent builds. The `imagedefinitions.json` file is specified as a build artifact.
Sources: [apps/api/buildspec.yml:87-95](https://github.com/blade47/comp/blob/main/apps/api/buildspec.yml#L87-L95)
## Local Development & Testing
Setting up the Comp AI project for local development involves several steps, including installing prerequisites, configuring environment variables, and initializing the database.
### Prerequisites
To run Comp AI locally, ensure you have the following installed:
- **Node.js**: Version `>=20.x`
- **Bun**: Version `>=1.1.36`
- **Postgres**: Version `>=15.x`
Sources: [README.md:94-98](https://github.com/blade47/comp/blob/main/README.md#L94-L98)
### Initial Setup
### Clone the Repository
Clone the Comp AI repository from GitHub.
```sh
git clone https://github.com/trycompai/comp.git
cd comp
```
### Install Dependencies
Install all project dependencies using Bun.
```sh
bun install
```
### Prepare Environment Files
Copy the example environment files for the `app`, `portal`, and `db` workspaces.
```sh
cp apps/app/.env.example apps/app/.env
cp apps/portal/.env.example apps/portal/.env
cp packages/db/.env.example packages/db/.env
```
```cmd
copy apps\app\.env.example apps\app\.env
copy apps\portal\.env.example apps\portal\.env
copy packages\db\.env.example packages\db\.env
```
```powershell
Copy-Item apps\app\.env.example -Destination apps\app\.env
Copy-Item apps\portal\.env.example -Destination apps\portal\.env
Copy-Item packages\db\.env.example -Destination packages\db\.env
```
### Generate Prisma Types
Navigate to each application directory and generate the Prisma client.
```sh
cd apps/app
bun run db:generate
cd ../portal
bun run db:generate
cd ../api
bun run db:generate
```
Sources: [README.md:104-142](https://github.com/blade47/comp/blob/main/README.md#L104-L142)
### Environment Variables
After copying the `.env.example` files, you must fill them with your credentials. Additionally, ensure the following variables are present in `comp/apps/app/.env`:
| Variable | Description | Example Value |
| :------------------------ | :------------------------------------------------------------------------------------------------------ | :------------------------------------------- |
| `AUTH_SECRET` | Secret key for authentication. Generate using `openssl rand -base64 32`. | `your_auth_secret` |
| `DATABASE_URL` | PostgreSQL connection string. | `postgresql://user:password@host:port/database` |
| `RESEND_API_KEY` | API key for Resend email service. | `re_your_resend_api_key` |
| `NEXT_PUBLIC_PORTAL_URL` | Public URL for the portal application. | `http://localhost:3002` |
| `REVALIDATION_SECRET` | Secret key for revalidation. Generate using `openssl rand -base64 32`. | `your_revalidation_secret` |
Some environment variables might not load correctly from `.env` files. In such cases, you may need to hard-code their values directly into the relevant source files as indicated in the "Cloud & Auth Configuration" section.
Sources: [README.md:144-166](https://github.com/blade47/comp/blob/main/README.md#L144-L166)
### Cloud & Authentication Configuration
Several external services require specific configuration for local development.
#### Trigger.dev
1. Create an account on [https://cloud.trigger.dev](https://cloud.trigger.dev).
2. Create a project and copy its Project ID.
3. Update `comp/apps/app/trigger.config.ts` with your Project ID:
```typescript
project: 'proj_****az***ywb**ob*';
```
Sources: [README.md:170-176](https://github.com/blade47/comp/blob/main/README.md#L170-L176)
#### Google OAuth
1. Go to [Google Cloud OAuth Console](https://console.cloud.google.com/auth/clients).
2. Create an OAuth client of type "Web Application".
3. Add the following Authorized Redirect URIs:
- `http://localhost`
- `http://localhost:3000`
- `http://localhost:3002`
- `http://localhost:3000/api/auth/callback/google`
- `http://localhost:3002/api/auth/callback/google`
- `http://localhost:3000/auth`
- `http://localhost:3002/auth`
4. Copy the `GOOGLE_ID` and `GOOGLE_SECRET` and add them to your `.env` files.
5. If environment variables are not recognized, hard-code them in `comp/apps/portal/src/app/lib/auth.ts`.
Sources: [README.md:178-197](https://github.com/blade47/comp/blob/main/README.md#L178-L197)
#### Redis (Upstash)
1. Go to [https://console.upstash.com](https://console.upstash.com).
2. Create a Redis database.
3. Copy the Redis URL and TOKEN.
4. Add them to your `.env` file.
5. If environment variables are not recognized, hard-code them in `comp/packages/kv/src/index.ts`.
Sources: [README.md:199-207](https://github.com/blade47/comp/blob/main/README.md#L199-L207)
### Database Setup
The project uses PostgreSQL, managed with Prisma. Docker is used to run the database locally.
### Start Database Container
Navigate to the `packages/db` directory and start the PostgreSQL container.
```sh
cd packages/db
bun docker:up
```
Default credentials:
- Database name: `comp`
- Username: `postgres`
- Password: `postgres`
### Change Default Password (Optional)
To change the default `postgres` user password:
```sql
ALTER USER postgres WITH PASSWORD 'new_password';
```
### Fix Function Definition Error (If encountered)
If you see a "No function matches the given name and argument types..." error, run the following fix:
```sh
psql "postgresql://postgres:@localhost:5432/comp" -f ./packages/db/prisma/functionDefinition.sql
```
### Apply Schema and Seed Data
Generate the Prisma client, push the schema to the database, and optionally seed with initial data.
```sh
bun db:generate # Generate Prisma client
bun db:push # Push the schema to the database
bun db:seed # Optional: Seed the database with initial data
```
- `bun db:studio`: Open Prisma Studio to view/edit data.
- `bun db:migrate`: Run database migrations.
- `bun docker:down`: Stop the database container.
- `bun docker:clean`: Remove the database container and volume.
Sources: [README.md:211-256](https://github.com/blade47/comp/blob/main/README.md#L211-L256)
### Starting Development Servers
Once all configurations are complete, you can start the development servers.
```sh
bun run dev
```
Alternatively, if you have Turbo installed globally, you can use the Turbo repo script:
```sh
turbo dev
```
Sources: [README.md:258-267](https://github.com/blade47/comp/blob/main/README.md#L258-L267)
### Docker Compose for Local API Testing
The `apps/api/docker-compose.yml` file is provided **for local testing only**. It defines a service to build and run the API in a Docker container.
The `api` service is configured as follows:
| Configuration Item | Value / Description
## About
### AI that handles compliance for you in hours.
Comp AI is the fastest way to get compliant with frameworks like SOC 2, ISO 27001, HIPAA and GDPR. Comp AI automates evidence collection, policy management, and control implementation while keeping you in control of your data and infrastructure.
## Recognition
#### [ProductHunt](https://www.producthunt.com/posts/comp-ai)
#### [Vercel](https://vercel.com/)
### Built With
- [Next.js](https://nextjs.org/?ref=trycomp.ai)
- [Trigger.dev](https://trigger.dev/?ref=trycomp.ai)
- [Prisma](https://prisma.io/?ref=trycomp.ai)
- [Tailwind CSS](https://tailwindcss.com/?ref=trycomp.ai)
- [Upstash](https://upstash.com/?ref=trycomp.ai)
- [Vercel](https://vercel.com/?ref=trycomp.ai)
## Contact us
Contact our founders at hello@trycomp.ai to learn more about how we can help you achieve compliance.
## Stay Up-to-Date
Get access to the cloud hosted version of [Comp AI](https://trycomp.ai).
## Getting Started
To get a local copy up and running, please follow these simple steps.
### Prerequisites
Here is what you need to be able to run Comp AI.
- Node.js (Version: >=20.x)
- Bun (Version: >=1.1.36)
- Postgres (Version: >=15.x)
## Development
To get the project working locally with all integrations, follow these extended development steps
### Setup
## Add environment variables and fill them out with your credentials
```sh
cp apps/app/.env.example apps/app/.env
cp apps/portal/.env.example apps/portal/.env
cp packages/db/.env.example packages/db/.env
```
## Get code running locally
1. Clone the repo
```sh
git clone https://github.com/trycompai/comp.git
```
2. Navigate to the project directory
```sh
cd comp
```
3. Install dependencies using Bun
```sh
bun install
```
4. Get Database Running
```sh
cd packages/db
bun run docker:up # Spin up docker container
bun run db:migrate # Run migrations
```
5. Generate Prisma Types for each app
```sh
cd apps/app
bun run db:generate
cd ../
```
---
## Technical docs: POST Create a new context entry
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/context/createcontext
## Parameters
## Request Body
Data for creating a new context entry.
## Responses
## Try It
---
## Technical docs: Third-party Services
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-7/third-party-services
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/browserbase/browserbase.service.ts](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/browserbase.service.ts)
- [apps/api/src/assistant-chat/upstash-redis.client.ts](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/upstash-redis.client.ts)
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [apps/api/src/email/resend.ts](https://github.com/blade47/comp/blob/main/apps/api/src/email/resend.ts)
- [apps/api/src/config/aws.config.ts](https://github.com/blade47/comp/blob/main/apps/api/src/config/aws.config.ts)
The project leverages several third-party services to provide core functionalities such as browser automation, email communication, caching, and cloud storage. These integrations enhance the application's capabilities, allowing for complex operations like automated web interactions and efficient data management.
This document outlines the key third-party services integrated into the application, detailing their purpose, configuration, and how they are utilized within the codebase.
## Browser Automation with Browserbase
The `BrowserbaseService` (`apps/api/src/browserbase/browserbase.service.ts`) is responsible for orchestrating browser automation tasks using the Browserbase SDK and Stagehand. This service enables functionalities like creating and managing browser sessions, navigating to URLs, checking login status, and executing complex automation instructions. It also integrates with AWS S3 for storing screenshots generated during automation runs.
### Key Components and Functionality
* **Browserbase SDK**: Used for creating and managing browser contexts and sessions.
* **Stagehand**: An AI agent framework that interacts with the browser to execute instructions and extract data.
* **Organization Context Management**: The service manages `BrowserbaseContext` records in the database, ensuring each organization has a persistent browser context for automations. The `getOrCreateOrgContext` method handles the creation and retrieval of these contexts, including a retry mechanism for concurrent creation attempts.
* **Session Management**: `createSessionWithContext` establishes a new browser session linked to an organization's context, providing a `liveViewUrl` for real-time monitoring. `closeSession` is used to terminate sessions.
* **Browser Actions**:
* `navigateToUrl`: Directs a browser session to a specified URL.
* `checkLoginStatus`: Verifies if a user is logged into a website within a session by extracting page elements.
* `executeAutomation`: The core method that uses Stagehand to perform a sequence of actions based on given instructions and a target URL. It includes logic for checking authentication status and capturing screenshots.
* **Automation CRUD**: Provides methods for creating, retrieving, updating, and deleting browser automation definitions stored in the database (`db.browserAutomation`).
* **Automation Execution**:
* `startAutomationWithLiveView`: Initiates an automation run, creates a live session, and returns a URL for live monitoring.
* `executeAutomationOnSession`: Executes a predefined automation within an existing live session.
* `runBrowserAutomation`: Executes an automation in a non-live session, handling session creation and closure internally.
* **Screenshot Management**: Integrates with AWS S3 to upload and retrieve presigned URLs for screenshots captured during automation runs.
### Browserbase Context Creation Flow
The `getOrCreateOrgContext` method ensures that a unique Browserbase context exists for each organization, handling potential race conditions during creation.
Sources: [apps/api/src/browserbase/browserbase.service.ts:40-104](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/browserbase.service.ts#L40-L104)
### Browser Automation Execution Flow
The `runBrowserAutomation` method orchestrates the full lifecycle of a browser automation, from session creation to execution and cleanup.
```mermaid
sequenceDiagram
participant API as API Service
participant DB as Database
participant BB as Browserbase SDK
participant Stagehand as Stagehand Agent
participant S3 as AWS S3
API->>DB: Get Browser Automation details
API->>DB: Get Organization Context
alt No Org Context
API-->>API: Return needsReauth error
end
API->>DB: Create BrowserAutomationRun record (status: running)
API->>BB: Create Session with Context (createSessionWithContext)
BB-->>API: sessionId, liveViewUrl
API->>Stagehand: Create Stagehand instance (createStagehand)
Stagehand->>Stagehand: Initialize
API->>Stagehand: Ensure Active Page (ensureActivePage)
API->>Stagehand: Navigate to targetUrl (page.goto)
API->>Stagehand: Check for login status (stagehand.extract)
alt Not Logged In
API-->>API: Return needsReauth error
end
API->>Stagehand: Execute automation instruction (stagehand.agent().execute)
Stagehand-->>API: Automation result
API->>Stagehand: Capture screenshot (page.screenshot)
API->>S3: Upload screenshot (uploadScreenshot)
S3-->>API: Screenshot key
API->>DB: Update BrowserAutomationRun (status: completed, screenshotUrl, evaluation)
API->>BB: Close Session (closeSession)
API->>Stagehand: Safe Close Stagehand (safeCloseStagehand)
API-->>API: Return success result
alt Error during execution
API->>DB: Update BrowserAutomationRun (status: failed, error)
API-->>API: Return error result
end
```
Sources: [apps/api/src/browserbase/browserbase.service.ts:167-393](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/browserbase.service.ts#L167-L393)
## Email Service with Resend
The application uses Resend for sending emails. The `resend.ts` file (`apps/api/src/email/resend.ts`) provides a utility function `sendEmail` to abstract the email sending process.
### Key Components and Functionality
* **Resend Client**: Initialized with `RESEND_API_KEY` from environment variables. If the API key is missing, the client is `null`, and `sendEmail` will throw an error.
* **`sendEmail` Function**:
* Takes parameters like `to`, `subject`, `react` (for React-based email templates), `marketing`, `system`, `test`, `cc`, `scheduledAt`, and `attachments`.
* Dynamically determines the `from` and `to` addresses based on `marketing`, `system`, or `test` flags and corresponding environment variables (`RESEND_FROM_MARKETING`, `RESEND_FROM_SYSTEM`, `RESEND_FROM_DEFAULT`, `RESEND_TO_TEST`).
* Supports attachments by converting `EmailAttachment` objects into the format expected by the Resend SDK.
* Includes error handling for API calls and missing environment variables.
### Email Sending Logic
Sources: [apps/api/src/email/resend.ts:1-74](https://github.com/blade47/comp/blob/main/apps/api/src/email/resend.ts#L1-L74)
## Caching and Key-Value Store with Upstash Redis
The `upstash-redis.client.ts` file (`apps/api/src/assistant-chat/upstash-redis.client.ts`) provides a client for a key-value store, primarily used for caching and managing state, particularly for assistant chat history.
### Key Components and Functionality
* **Upstash Redis**: A serverless Redis solution used for persistent key-value storage.
* **`InMemoryRedis`**: A local, in-memory implementation of a Redis-like client. This is used as a fallback when Upstash Redis configuration is not provided, making the application runnable without external Redis. It supports `get`, `set`, and `del` operations with optional expiration.
* **`assistantChatRedisClient`**: This is the exported client instance. It is conditionally initialized:
* If `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` environment variables are present, it uses the `@upstash/redis` SDK.
* Otherwise, it defaults to the `InMemoryRedis` instance.
* **Purpose**: Used by services like `AssistantChatService` to save and clear chat history.
### Redis Client Initialization Flow
Sources: [apps/api/src/assistant-chat/upstash-redis.client.ts:1-35](https://github.com/blade47/comp/blob/main/apps/api/src/assistant-chat/upstash-redis.client.ts#L1-L35)
## Cloud Storage with AWS S3
AWS S3 is utilized for storing large binary objects, specifically screenshots generated during browser automation runs. The `BrowserbaseService` interacts with S3 for this purpose, and the `aws.config.ts` file (`apps/api/src/config/aws.config.ts`) defines the configuration for AWS services.
### Key Components and Functionality
* **`S3Client`**: An instance of the AWS SDK S3 client, configured with region and credentials.
* **`bucketName`**: Configured via `APP_AWS_BUCKET_NAME` environment variable, defaulting to `comp-attachments`.
* **`uploadScreenshot`**: Uploads a base64 encoded screenshot to a specified S3 key.
* **`getPresignedUrl`**: Generates a temporary, time-limited URL for accessing private S3 objects, ensuring secure access to screenshots.
* **`awsConfig`**: A configuration object (`apps/api/src/config/aws.config.ts`) that registers AWS settings, including `region`, `accessKeyId`, `secretAccessKey`, and `bucketName`. It uses Zod for schema validation of these environment variables at application startup.
### AWS Configuration
The `awsConfig` is registered using `@nestjs/config`'s `registerAs` function, ensuring that AWS settings are validated and available throughout the application.
```typescript
// apps/api/src/config/aws.config.ts
const awsConfigSchema = z.object({
region: z.string().default('us-east-1'),
accessKeyId: z.string().min(1, 'AWS_ACCESS_KEY_ID is required'),
secretAccessKey: z.string().min(1, 'AWS_SECRET_ACCESS_KEY is required'),
bucketName: z.string().min(1, 'AWS_BUCKET_NAME is required'),
endpoint: z.string().optional(),
});
export const awsConfig = registerAs('aws', (): AwsConfig => {
const config = {
region: process.env.APP_AWS_REGION || 'us-east-1',
accessKeyId: process.env.APP_AWS_ACCESS_KEY_ID || '',
secretAccessKey: process.env.APP_AWS_SECRET_ACCESS_KEY || '',
bucketName: process.env.APP_AWS_BUCKET_NAME || '',
endpoint: process.env.APP_AWS_ENDPOINT || '',
};
const result = awsConfigSchema.safeParse(config);
if (!result.success) {
throw new Error(
`AWS configuration validation failed: ${result.error.issues
.map((e) => `${e.path.join('.')}: ${e.message}`)
.join(', ')}`,
);
}
return result.data;
});
```
Sources: [apps/api/src/browserbase/browserbase.service.ts:16-37](https://github.com/blade47/comp/blob/main/apps/api/src/browserbase/browserbase.service.ts#L16-L37), [apps/api/src/config/aws.config.ts:1-29](https://github.com/blade47/comp/blob/main/apps/api/src/config/aws.config.ts#L1-L29)
## Other Third-Party Integrations
The project's `README.md` (`README.md`) also highlights several other key technologies and platforms that contribute to its overall architecture and functionality.
* **Next.js**: A React framework for building web applications.
* **Trigger.dev**: Likely used for background jobs, workflows, or event-driven tasks. The `README.md` mentions setting up a Trigger.dev project ID.
* **Prisma**: An ORM (Object-Relational Mapper) used for database access and management.
* **Tailwind CSS**: A utility-first CSS framework for styling user interfaces.
* **Vercel**: A platform for frontend developers, used for deployment and hosting.
These integrations form the foundation of the application, enabling a robust and scalable solution.
Sources: [README.md:60-65](https://github.com/blade47/comp/blob/main/README.md#L60-L65), [README.md:162-165](https://github.com/blade47/comp/blob/main/README.md#L162-L165)
---
## Technical docs: Release Process
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-7/release-process
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/package.json](https://github.com/blade47/comp/blob/main/apps/api/package.json)
- [README.md](https://github.com/blade47/comp/blob/main/README.md)
- [.syncpackrc.json](https://github.com/blade47/comp/blob/main/.syncpackrc.json)
The release process for the `comp` project encompasses automated package publishing, rigorous dependency management, and defined build and deployment strategies. This ensures consistency across internal packages, simplifies the integration of external dependencies, and provides a structured approach to delivering the application and its components.
This document outlines the mechanisms in place for managing releases, from internal package versioning to deployment considerations.
## Automated Package Publishing
The project utilizes `semantic-release` to automate the publishing of internal packages to npm. This process is triggered upon merging pull requests into the `release` branch, ensuring that new versions are released consistently and follow semantic versioning principles based on conventional commits.
### Published Packages
The following internal packages are automatically published:
| Package Name | Description |
| :----------- | :---------- |
| `@comp/db` | Database utilities with Prisma client |
| `@comp/email` | Email templates and components |
| `@comp/kv` | Key-value store utilities using Upstash Redis |
| `@comp/ui` | UI component library with Tailwind CSS |
Sources: [README.md:166-169](https://github.com/blade47/comp/blob/main/README.md#L166-L169)
### Publishing Workflow
The publishing workflow is designed to be fully automated once changes are merged into the `release` branch.
### Setup NPM Token
To enable `semantic-release` to publish packages, an npm token must be configured as a GitHub repository secret named `NPM_TOKEN`.
### Trigger Release
Create and merge pull requests into the `release` branch. `semantic-release` will automatically detect conventional commits and bump versions accordingly.
Sources: [README.md:173-174](https://github.com/blade47/comp/blob/main/README.md#L173-L174)
### Local Development and Testing
Developers can build and test packages locally before they are published.
```bash
# Build all packages in the monorepo
bun run build
# Build a specific package, e.g., @comp/ui
bun run -F @comp/ui build
# Test the release process without actually publishing
bun run release:packages --dry-run
```
Sources: [README.md:180-185](https://github.com/blade47/comp/blob/main/README.md#L180-L185)
## Dependency Management and Consistency
The project uses `syncpack` to enforce consistent dependency versions across all packages within the monorepo. This tool is configured via `.syncpackrc.json` to manage internal package references, ensure uniform versions for critical external dependencies, and prevent the inclusion of forbidden modules.
### Configuration Overview
Sources: [.syncpackrc.json](https://github.com/blade47/comp/blob/main/.syncpackrc.json)
### Key Consistency Rules
All internal `@comp` packages are configured to use exact `workspace:*` versions. This ensures that all parts of the monorepo reference the latest local version of internal dependencies.
**`semverGroups`**:
This group specifically targets internal packages, ensuring they use `workspace:*` for their version ranges.
| Label | Packages | Dependencies | Range |
| :---- | :------- | :----------- | :---- |
| Use exact versions for internal packages | `@comp/**` | `@comp/**` | `workspace:*` |
**`versionGroups`**:
These groups ensure that critical external dependencies maintain consistent versions across all packages in the monorepo.
| Label | Dependencies |
| :---- | :----------- |
| Ensure React is consistent | `react`, `react-dom`, `@types/react`, `@types/react-dom`, `react-is` |
| Ensure Next.js is consistent | `next` |
| Ensure TypeScript is consistent | `typescript` |
| Ensure common build tools are consistent | `postcss`, `tailwindcss`, `@tailwindcss/**`, `autoprefixer` |
| Ensure testing tools are consistent | `@types/node`, `prettier`, `turbo` |
| Ensure ESLint is consistent | `eslint`, `eslint-config-next` |
**`lintRules`**:
A set of rules to forbid certain dependencies, typically Node.js built-in modules or mistakenly added packages, to maintain a clean and predictable dependency graph.
| Rule | Dependencies | Message |
| :--- | :----------- | :------ |
| `forbiddenDependencies` | `crypto`, `buffer`, `fs`, `path`, `os`, `install`, `npm` | This is a Node.js built-in module or a mistakenly added dependency |
Sources: [.syncpackrc.json](https://github.com/blade47/comp/blob/main/.syncpackrc.json)
## Deployment Considerations
While detailed deployment steps for Docker and Vercel are "coming soon", the project's `package.json` files provide insights into the planned deployment strategies and build processes.
### Build and Deployment Scripts
The `apps/api/package.json` defines several scripts relevant to building and deploying the API service:
| Script Name | Command | Description |
| :---------- | :------ | :---------- |
| `prebuild` | `bun run db:generate` | Generates the Prisma client before the main build process. |
| `build` | `nest build` | Compiles the NestJS application. |
| `build:docker` | `bunx prisma generate && nest build` | Prepares the application for Docker by generating Prisma client and then building the NestJS app. |
| `deploy:trigger-prod` | `npx trigger.dev@4.0.6 deploy` | Deploys Trigger.dev jobs to the production environment. |
| `start:prod` | `node dist/main` | Starts the compiled NestJS application in production mode. |
Sources: [apps/api/package.json:96-100](https://github.com/blade47/comp/blob/main/apps/api/package.json#L96-L100), [apps/api/package.json:104](https://github.com/blade47/comp/blob/main/apps/api/package.json#L104)
The `README.md` indicates that detailed steps for deploying Comp AI on Docker and Vercel are currently under development and will be provided soon.
Sources: [README.md:154-157](https://github.com/blade47/comp/blob/main/README.md#L154-L157)
---
## Technical docs: PATCH Update a context entry
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/context/updatecontext
## Parameters
## Request Body
Data for updating the context entry.
## Responses
## Try It
---
## Technical docs: Framework Editor
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-8/framework-editor
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/src/framework-editor/task-template/dto/create-task-template.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/dto/create-task-template.dto.ts)
- [apps/api/src/framework-editor/task-template/schemas/task-template-operations.ts](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/schemas/task-template-operations.ts)
- [apps/api/src/framework-editor/task-template/dto/update-task-template.dto.ts](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/dto/update-task-template.dto.ts)
- [apps/api/src/framework-editor/task-template/task-template.controller.ts](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts)
- [apps/api/src/framework-editor/task-template/task-template.service.ts](https://github.com/blade47/task-template/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts)
- [apps/api/src/framework-editor/task-template/task-template.module.ts](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.module.ts)
The Framework Editor module provides an API for managing "Task Templates". These templates define recurring tasks with properties such as name, description, frequency, and responsible department. The module offers standard CRUD (Create, Read, Update, Delete) operations for these task templates, exposed via a RESTful API.
This module is built using the NestJS framework, adhering to its architectural patterns of controllers, services, and DTOs (Data Transfer Objects) to ensure a clear separation of concerns and maintainability. It integrates with an authentication system and uses database interactions for persistence.
## Architecture Overview
The Framework Editor's Task Template component follows a typical NestJS modular architecture, consisting of a module, controller, and service. This structure facilitates organized code and clear responsibilities for handling API requests, business logic, and data persistence.
Sources: [apps/api/src/framework-editor/task-template/task-template.module.ts:1-7](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.module.ts#L1-L7), [apps/api/src/framework-editor/task-template/task-template.controller.ts:20-22](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L20-L22), [apps/api/src/framework-editor/task-template/task-template.service.ts:1-4](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts#L1-L4)
### TaskTemplateModule
The `TaskTemplateModule` is the entry point for this part of the Framework Editor. It imports the `AuthModule` for authentication capabilities and registers `TaskTemplateController` and `TaskTemplateService`. The `TaskTemplateService` is also exported, making it available for use by other modules if needed.
```typescript
@Module({
imports: [AuthModule],
controllers: [TaskTemplateController],
providers: [TaskTemplateService],
exports: [TaskTemplateService],
})
export class TaskTemplateModule {}
```
Sources: [apps/api/src/framework-editor/task-template/task-template.module.ts:1-7](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.module.ts#L1-L7)
### TaskTemplateController
The `TaskTemplateController` handles incoming HTTP requests related to task templates. It defines the API endpoints, applies authentication guards (`HybridAuthGuard`), and uses DTOs for request body validation. It delegates business logic to the `TaskTemplateService`. All endpoints are secured and require an `X-Organization-Id` header.
```mermaid
sequenceDiagram
participant Client
participant Controller as TaskTemplateController
participant Service as TaskTemplateService
participant DB as Database
Client->>Controller: PATCH /framework-editor/task-template/:id (UpdateTaskTemplateDto)
activate Controller
Controller->>Controller: Validate ID (ValidateIdPipe)
Controller->>Controller: Validate Body (ValidationPipe)
Controller->>Service: updateById(id, updateDto)
activate Service
Service->>Service: findById(id)
activate Service
Service->>DB: Query existing template
deactivate Service
alt Template not found
Service-->>Controller: NotFoundException
Controller-->>Client: 404 Not Found
else Template found
Service->>DB: Update template data
DB-->>Service: Updated template
Service-->>Controller: Updated template
end
deactivate Service
Controller-->>Client: 200 OK (Updated template + AuthContext)
deactivate Controller
```
Sources: [apps/api/src/framework-editor/task-template/task-template.controller.ts:1-87](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L1-L87)
### TaskTemplateService
The `TaskTemplateService` encapsulates the business logic for managing task templates. It interacts directly with the database via `db.frameworkEditorTaskTemplate` (presumably a Prisma client or similar ORM) to perform CRUD operations. It includes error handling and logging for database interactions and throws `NotFoundException` for non-existent resources.
Sources: [apps/api/src/framework-editor/task-template/task-template.service.ts:6-96](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts#L6-L96)
## Data Structures
The module defines specific Data Transfer Objects (DTOs) for creating and updating task templates, ensuring data integrity and clear API contracts.
### CreateTaskTemplateDto
This DTO defines the required fields for creating a new task template. All fields are mandatory and include validation rules.
```typescript
export class CreateTaskTemplateDto {
name: string; // Task template name
description: string; // Detailed description
frequency: Frequency; // Frequency of the task (enum)
department: Departments; // Department responsible (enum)
}
```
The `Frequency` and `Departments` enums are imported from `@trycompai/db`, indicating predefined categories for these fields.
| Field | Type | Description | Validation | Example |
| :----------- | :---------- | :---------------------------------------------- | :---------------------------------------- | :------------------------ |
| `name` | `string` | Task template name | `@IsString()`, `@IsNotEmpty()` | "Monthly Security Review" |
| `description`| `string` | Detailed description of the task template | `@IsString()`, `@IsNotEmpty()` | "Review and update..." |
| `frequency` | `Frequency` | Frequency of the task (e.g., monthly, weekly) | `@IsEnum(Frequency)` | `Frequency.monthly` |
| `department` | `Departments` | Department responsible for the task (e.g., IT) | `@IsEnum(Departments)` | `Departments.it` |
Sources: [apps/api/src/framework-editor/task-template/dto/create-task-template.dto.ts:1-30](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/dto/create-task-template.dto.ts#L1-L30)
### UpdateTaskTemplateDto
The `UpdateTaskTemplateDto` is based on `CreateTaskTemplateDto` but uses `PartialType` from `@nestjs/swagger`. This makes all fields optional, allowing for partial updates of a task template.
```typescript
import { PartialType } from '@nestjs/swagger';
import { CreateTaskTemplateDto } from './create-task-template.dto';
export class UpdateTaskTemplateDto extends PartialType(CreateTaskTemplateDto) {}
```
Sources: [apps/api/src/framework-editor/task-template/dto/update-task-template.dto.ts:1-4](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/dto/update-task-template.dto.ts#L1-L4)
## API Endpoints
The `TaskTemplateController` exposes a set of RESTful API endpoints for managing task templates. All endpoints are versioned under `/v1/framework-editor/task-template` and require authentication.
| Method | Path | Description | Service Method Called | Request Body |
| :----- | :-------------------- | :----------------------------------------- | :-------------------- | :------------------- |
| `GET` | `/` | Retrieve all task templates | `findAll()` | N/A |
| `GET` | `/:id` | Retrieve a specific task template by ID | `findById(id)` | N/A |
| `PATCH`| `/:id` | Update an existing task template by ID | `updateById(id, dto)` | `UpdateTaskTemplateDto` |
| `DELETE`| `/:id` | Delete a task template by ID | `deleteById(id)` | N/A |
Sources: [apps/api/src/framework-editor/task-template/task-template.controller.ts:31-87](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L31-L87), [apps/api/src/framework-editor/task-template/schemas/task-template-operations.ts:1-16](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/schemas/task-template-operations.ts#L1-L16)
### Authentication and Authorization
All endpoints in the `TaskTemplateController` are protected by `HybridAuthGuard`. This guard enforces authentication, which can be either session-based or API key-based. The `X-Organization-Id` header is required for session authentication and optional for API key authentication. The `AuthContext` decorator is used to inject authentication details (e.g., `userId`, `userEmail`, `authType`) into controller methods.
Sources: [apps/api/src/framework-editor/task-template/task-template.controller.ts:14-19](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L14-L19), [apps/api/src/framework-editor/task-template/task-template.controller.ts:50-51](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L50-L51)
### Request Validation
The module employs several validation mechanisms:
* **`ValidateIdPipe`**: Ensures that the `id` parameter in the URL is valid before it reaches the service layer.
* **`ValidationPipe`**: Applied to the `updateTaskTemplate` endpoint, it validates the `UpdateTaskTemplateDto` request body against the rules defined in the DTO (e.g., `@IsString`, `@IsEnum`). It is configured to `whitelist` and `forbidNonWhitelisted` properties, ensuring only expected data is processed.
Sources: [apps/api/src/framework-editor/task-template/task-template.controller.ts:12](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L12), [apps/api/src/framework-editor/task-template/task-template.controller.ts:63-68](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.controller.ts#L63-L68)
## Service Logic Details
The `TaskTemplateService` implements the core logic for interacting with the database. It uses a `Logger` for operational insights and robust error handling.
### `findAll()`
Retrieves all task templates from the database, ordered alphabetically by `name`. Logs the number of retrieved templates.
Sources: [apps/api/src/framework-editor/task-template/task-template.service.ts:9-23](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts#L9-L23)
### `findById(id: string)`
Fetches a single task template by its unique `id`. If no template is found, it throws a `NotFoundException`.
Sources: [apps/api/src/framework-editor/task-template/task-template.service.ts:25-47](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts#L25-L47)
### `updateById(id: string, updateDto: UpdateTaskTemplateDto)`
Updates an existing task template. It first calls `findById` to ensure the template exists. If found, it proceeds with the update operation using the provided `updateDto`.
Sources: [apps/api/src/framework-editor/task-template/task-template.service.ts:49-69](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts#L49-L69)
### `deleteById(id: string)`
Deletes a task template by its `id`. Similar to `updateById`, it first verifies the template's existence using `findById` before performing the deletion. It returns a confirmation message and details of the deleted template.
Sources: [apps/api/src/framework-editor/task-template/task-template.service.ts:71-96](https://github.com/blade47/comp/blob/main/apps/api/src/framework-editor/task-template/task-template.service.ts#L71-L96)
---
## Technical docs: DELETE Delete a context entry
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/api/context/deletecontext
## Parameters
## Responses
## Try It
---
## Technical docs: Developer Tooling
URL: https://www.doc0.dev/docs/49c89830-117b-4def-8edc-b5bcc50766e0/technical/section-8/developer-tooling
Relevant source files
The following files were used as context for generating this wiki page:
- [apps/api/scripts/encode-badge-icons.ts](https://github.com/blade47/comp/blob/main/apps/api/scripts/encode-badge-icons.ts)
- [apps/api/nest-cli.json](https://github.com/blade47/comp/blob/main/apps/api/nest-cli.json)
- [apps/api/package.json](https://github.com/blade47/comp/blob/main/apps/api/package.json)
Developer tooling for the `api` application encompasses a suite of scripts, configurations, and dependencies designed to streamline development, build processes, and maintain code quality. This includes automated asset processing, project-specific CLI configurations, and a comprehensive set of development and production dependencies alongside various utility scripts for tasks like database management, testing, and deployment.
This page details the core components of the `api` application's developer tooling, providing insights into its structure and functionality.
## Badge Icon Encoding Script
The `encode-badge-icons.ts` script is a crucial utility for pre-processing static SVG assets. Its primary purpose is to extract SVG definitions of badge icons from a React component file, encode them as base64 data URLs, and then embed these data URLs into a TypeScript service file. This process optimizes the delivery of small icons by inlining them directly into the application's code, reducing HTTP requests.
### Architecture and Data Flow
The script operates by reading a source file containing SVG components, parsing their content, performing base64 encoding, and finally updating a target service file with the generated data.
This script is designed to be run manually or as part of a build process, indicated by its usage instruction: `bun run apps/api/scripts/encode-badge-icons.ts`.
Sources: [apps/api/scripts/encode-badge-icons.ts:1-66](https://github.com/blade47/comp/blob/main/apps/api/scripts/encode-badge-icons.ts#L1-L66)
### Key Components
* **`logos.tsx`**: The source file (located at `../../app/src/app/(app)/[orgId]/trust/portal-settings/components/logos.tsx`) from which SVG definitions are extracted.
* **`extractSvg(componentName: string)`**: A function that uses regular expressions to find and extract the SVG content corresponding to a given React component name within `logos.tsx`. It also cleans up `props` spread attributes from the SVG.
```typescript
function extractSvg(componentName: string): string | null {
const regex = new RegExp(`export const ${componentName} = \\(props.*?\\) => \\(\\s*(