SPEC 007 — Documents and Document Generation
| Field | Value |
|---|
| Status | DRAFT |
| Priority | P1 — Core Product |
| Backend | equa-server/modules/data-room/, equa-server/modules/doc-gen/, equa-server/modules/file-storage/ |
| API | equa-server/modules/api/src/endpoints/data-room-endpoints.ts, organization-endpoints.ts, captable-endpoints.ts |
| Frontend | equa-web/src/modules/documents/, equa-web/src/modules/agreements/ |
1. Feature Purpose
Documents and DocGen provides the file management infrastructure for the entire Equa platform. It covers three areas:
- Data Room — General-purpose virtual file storage with folders, uploads, renaming, and deletion
- Governing Documents — Categorized legal document repository (formation, operating, shareholder, board, employment, investor, compliance)
- Document Generation — Automated creation of stock certificates (SVG templates rendered to PDF) and operating agreements (Handlebars DOCX templates)
All files are stored on AWS S3 with content-addressed hashing. The system supports DOCX-to-PDF server-side conversion for inline viewing.
2. Current State (Verified)
2.1 Backend
| Component | Path |
|---|
| Data room persistence (read) | equa-server/modules/persistence/src/data-room/reading.ts |
| Data room persistence (write) | equa-server/modules/persistence/src/data-room/writing.ts |
| File storage (S3) | equa-server/modules/file-storage/src/s3.ts |
| Upload middleware | equa-server/modules/file-storage/src/uploading.ts |
| Doc-gen module | equa-server/modules/doc-gen/ |
| Certificate templates | equa-server/modules/doc-gen/res/templates/certificate-front.svg, certificate-back.svg |
| API doc-gen wrappers | equa-server/modules/api/src/doc-gen/generation.ts |
| Data room endpoints | equa-server/modules/api/src/endpoints/data-room-endpoints.ts |
| Organization endpoints (files) | equa-server/modules/api/src/endpoints/organization-endpoints.ts |
| Cap table endpoints (certs) | equa-server/modules/api/src/endpoints/captable-endpoints.ts |
| Authorization persistence | equa-server/modules/persistence/src/organizations/reading.ts, writing.ts |
2.2 Frontend
| Component | Path |
|---|
| Data room module | equa-web/src/modules/documents/ |
| Agreements module | equa-web/src/modules/agreements/ |
| Web client (upload/download) | equa-web/src/service/services/web-client.ts |
| Shared documents component | equa-web/src/shared/components/documents-and-notes.tsx |
| ESOP plan documents | equa-web/src/modules/esop/pages/all-plan-documents.tsx |
| Cap table doc/notes page | equa-web/src/modules/captable/pages/documents-and-notes-page.tsx |
3. Data Model
3.1 DirectoryItems
Virtual filesystem entries (files and folders) organized by path.
| Column | Type | Constraints | Description |
|---|
| organization | uuid | PK (composite) | Owning organization |
| parentPath | varchar | PK (composite) | Parent folder path (e.g., / or /legal/) |
| name | varchar | PK (composite) | File or folder name |
| file | uuid | nullable, FK → Files | Null for folders, set for files |
| type | smallint | unsigned | 2 = folder, other = file |
| size | number | | File size in bytes (0 for folders) |
Extends: CreatedModifiedDeleted
Source: schema.ts lines 564–582
3.2 Files
Content-addressed file records stored on S3.
| Column | Type | Constraints | Description |
|---|
| id | uuid | PK | File identifier |
| hash | Hash | nullable | Content hash for deduplication |
| filename | varchar | | Original filename |
| url | varchar | | S3 URL or path |
| extension | varchar | | File extension (e.g., pdf, docx) |
| contentType | varchar | | MIME type |
| size | number | | File size in bytes |
| owner | uuid | FK → Users | Uploading user |
Extends: CreatedModified
Source: schema.ts lines 585–609
3.3 Authorizations
Document authorizations linking approval records to organizations.
| Column | Type | Constraints | Description |
|---|
| (hash) | Hash | PK (from HashedTable) | Content-addressed identifier |
| authorizationDate | date | nullable | Date of authorization |
| document | Hash | nullable | Reference to authorized document |
| note | text | nullable | Free-text note |
| organization | uuid | FK → Organizations | Owning organization |
| reason | uuid | nullable | Reason reference |
| target | varchar | | Target entity or path |
| documentTypeName | varchar | nullable | Type label (e.g., “Issuance Agreement”) |
| deleted | boolean | default: false | Soft-delete flag |
Extends: HashedTable
Source: schema.ts lines 1378–1402
3.4 OrganizationTemplates
Stored document templates (certificate legends, operating agreement content).
| Column | Type | Constraints | Description |
|---|
| organization | uuid | PK (composite) | Owning organization |
| type | varchar | PK (composite) | Template type identifier |
| content | Hash | | Content-addressed template body |
Extends: CreatedModifiedDeleted
Source: schema.ts lines 1405–1414
3.5 DataRoomsMembers (Access Control)
This section covers the data model previously planned as Spec 008 (Data Rooms). The entity and persistence functions exist, but no REST API endpoints or frontend UI are implemented for managing data room membership. The legacy Microsoft path restriction (processRestrictedPath in microsoft/src/reading.ts) is deprecated. Modern data room access control needs a separate specification.
Member-level access control for named data rooms.
| Column | Type | Constraints | Description |
|---|
| dataRoomName | varchar | PK (composite) | Named data room (maps to folder path) |
| member | uuid | PK (composite), FK → Members | Authorized member |
| permission | uuid | PK (composite), FK → Permissions | Granted permission |
Source: schema.ts lines 164–174
Persistence functions (exist, not exposed via REST):
getDataRoomsPermissionsForMember(db, organization, member) — Query by org + member (common/reading.ts line 576)
getDataRoomsPermissionsForOrganizationAnUser(db, organization, user) — Query by org + user, joins through Members (common/reading.ts line 580)
insertDataRoomsMembers(db, record) — Insert single record (common/writing.ts line 319)
Legacy path restriction (deprecated):
processRestrictedPath in microsoft/src/reading.ts line 51 restricts folder paths for non-admin users based on their dataRoomName entries. Admins bypass. This was built for Microsoft Graph/SharePoint storage and is not used with the current native file storage.
Gap: No endpoints exist for granting/revoking data room access. No frontend UI for data room member management. A future spec should define modern access control for Google Drive integration or native storage.
3.6 File Storage Backend
Files are stored on AWS S3 (file-storage/src/s3.ts):
- Upload:
uploadS3(config, hash, stream) — stores with hash as S3 object name
- Download:
downloadS3(config, hash) — retrieves by hash
- Configuration:
AwsFileStorageConfig with accessKeyId, secretAccessKey, bucket, cacheBucket, region, tempPath
Relationships
4. API Endpoints
Data Room Endpoints
| Method | Path | Auth Guard | Description |
|---|
| GET | /organization/:organization/document | canViewOrganization | List documents at path (query: ?path=/) |
| GET | /organization/:organization/document/:document/content | canViewDocuments | Download file (attachment disposition) |
| GET | /organization/:organization/document/:document/content/:name | canViewDocuments | View file inline (supports ?format=pdf for DOCX conversion) |
| POST | /organization/:organization/document | canEditDocuments | Upload files (multipart, max 10 MB default) |
| POST | /organization/:organization/folder | canEditDocuments | Create folder |
| POST | /organization/:organization/document/:document/move | canEditDocuments | Rename or move document/folder |
| DELETE | /organization/:organization/document/:document | canEditDocuments | Delete document (query: ?fullPath=...) |
Source: data-room-endpoints.ts lines 40–117
Upload middleware: uploadMiddlewareUsingMemory with maxSizeMegabytes: parseInt(process.env.DATA_ROOM_UPLOAD_SIZE_LIMIT_MB || '10')
Organization File Endpoints
| Method | Path | Auth Guard | Description |
|---|
| POST | /organization/:organization/file | canEditDocuments | Upload organization file (max 21 MB default) |
| GET | /file/:file/content | — | Download file by ID |
| GET | /file/:file/content/:name | — | View file inline by ID |
| GET | /authorization | — | Get authorizations |
| POST | /organization/:organization/authorization | canEditDocuments | Create authorization |
| DELETE | /organization/:organization/authorization/:authorization | canEditDocuments | Delete authorization |
Source: organization-endpoints.ts lines 208–251
Certificate Generation Endpoints
| Method | Path | Auth Guard | Description |
|---|
| GET | /shareholding/:holding/certificate | canViewCapTable | Generate shareholding certificate PDF |
| GET | /legend/:legend/certificate | canViewCapTable | Generate legend certificate PDF |
| GET | /shareholding/:holding/certificate/copy | canViewCapTable | Generate certificate copy PDF |
Source: captable-endpoints.ts lines 207–222
5. Frontend Components
Data Room
| Component | File | Purpose |
|---|
| DataRoomPage | documents/documents.tsx | Main data room with folder navigation, upload dropzone, modals |
| DocumentSection | documents/components/document-section.tsx | File/folder table with actions (view, rename, delete) |
| DocumentButtons | documents/components/document-buttons.tsx | Upload controls (file, folder, link) |
| Modals | documents/components/modals.tsx | Delete confirmation, rename, new folder, add link, introduction |
DataRoomPage behavior:
- Dropzone supports file and folder uploads with progress tracking
- Link creation generates
.url files (Windows Internet Shortcut format)
- Permission-gated: checks
canEditDocument for write operations
- Introduction modal shown on first visit to empty data room
- Supports legacy Microsoft Graph paths via
legacyDataRoom org flag
Governing Documents
| Component | File | Purpose |
|---|
| AgreementsPage | agreements/agreements-page.tsx | Category-filtered document list with upload/view/delete |
| AgreementsTable | agreements/components/agreements-table.tsx | Document table within a category |
Agreement Categories (8 total, from agreements/utils.ts):
| Category | Storage Path | Description |
|---|
| all | governing | Show all governing documents |
| formation | governing/formation | Articles of incorporation, bylaws |
| operating | governing/operating | Operating agreements |
| shareholder | governing/shareholder | Shareholder agreements |
| board | governing/board | Board resolutions, minutes |
| employment | governing/employment | Employment agreements |
| investor | governing/investor | Investor rights, ROFR |
| compliance | governing/compliance | Regulatory filings |
Document Generation
The doc-gen module generates:
-
Stock certificates — Front and back pages rendered from SVG templates (
certificate-front.svg, certificate-back.svg) using pdf-lib for PDF output. Populated with shareholding data (holder name, share count, serial number, legend text, signatures).
-
Legend certificates — Standalone legend document generated from legend content.
-
Operating agreements — DOCX documents generated from Handlebars templates via
modules/api/src/doc-gen/generation.ts. Templates stored in OrganizationTemplates entity.
Shared Components
| Component | File | Purpose |
|---|
| DocumentsAndNotes | shared/components/documents-and-notes.tsx | Reusable form section for document uploads with metadata (type name, approval date, notes). Uses FieldArray for multiple docs. |
Routes
| Route | Component |
|---|
/organization/:organization/dataroom | DataRoomPage |
/organization/:organization/documents/governing | AgreementsPage |
/organization/:organization/documents/governing/:category | AgreementsPage (filtered) |
${captableCertificatePath}/documentsAndNotes | DocumentsAndNotesPage |
${planPath}/documents | AllPlanDocuments |
6. Business Rules and Validation
| Rule | Description | Enforcement |
|---|
| Composite path uniqueness | No two items can have the same (organization, parentPath, name) | Database PK constraint |
| Upload size limits | Data room: 10 MB (configurable via DATA_ROOM_UPLOAD_SIZE_LIMIT_MB); General: 21 MB (via DOCUMENT_UPLOAD_SIZE_LIMIT_MB) | Server-side middleware |
| File type validation | Currently not enforced — validation code exists but is commented out in file-storage/src/validation.ts line 48 | None (gap) |
| Content-addressed deduplication | Files with identical content share the same S3 object (hash-based naming) | S3 upload uses formatS3ObjectName(hash) |
| DOCX-to-PDF conversion | Inline viewing of DOCX files triggers server-side conversion via ?format=pdf query parameter | Backend conversion on request |
| Permission gating | Data room operations require canViewDocuments (read) or canEditDocuments (write) | API endpoint guards |
| Soft delete on authorizations | Authorizations use deleted: boolean flag rather than hard delete | Application-level check |
| Folder type marker | DirectoryItems with type = 2 are folders; others are files | Application convention |
| Legacy data room flag | Organizations with legacyDataRoom = true use Microsoft Graph paths; newer orgs use native S3 storage | Frontend path handling |
7. Acceptance Criteria
8. Risks and Edge Cases
| Risk | Impact | Mitigation |
|---|
| No file type validation | Malicious files could be uploaded | Re-enable validation in file-storage/src/validation.ts |
| Upload size limit mismatch (10 MB vs 21 MB) | User confusion about different limits for data room vs org files | Unify limits or display the applicable limit in the UI |
| S3 credential exposure | File storage compromise | Credentials stored in environment variables, not in code |
| DOCX-to-PDF conversion failure | Unsupported DOCX features may produce corrupt PDF | Fallback to download link when conversion fails |
| Hash collision (content-addressed) | Wrong file served | SHA-256 makes collision negligible; add unique constraint on hash |
| Legacy Microsoft Graph paths | Broken file access for old organizations | Migration path needed; legacyDataRoom flag identifies affected orgs |
| DataRoomsMembers has no API/UI | Cannot manage data room access via the product | Future spec needed for modern access control |
| Concurrent uploads to same path | Naming conflict for duplicate filenames | Server checks for existing names; appends suffix or rejects |
9. Dependencies
| Dependency | Type | Status |
|---|
| 001-Authentication (user sessions) | Required before | DRAFT |
| 002-Organization Management | Required before | DRAFT |
| 012-Team Members and Roles (permissions) | Required before | DRAFT |
| 003-Cap Table (certificate generation) | Integrates with | DRAFT |
| 023-File Storage (S3 infrastructure) | Integrates with | NOT STARTED |
| 016-Google Drive Integration | Integrates with | NOT STARTED |