From 559e987efb6df3f9c01ed562f12d0483e980d611 Mon Sep 17 00:00:00 2001
From: Eeshitha Gone <193770087+eesha264@users.noreply.github.com>
Date: Sun, 16 Aug 2026 15:52:25 +0530
Subject: [PATCH] =?UTF-8?q?docs:=20add=2041-50=20=E2=80=94=20beta=20apps,?=
=?UTF-8?q?=20support=20tickets,=20design=20inspiration,=20repo=20linking,?=
=?UTF-8?q?=20meet=20routes,=20webhooks,=20task=20routes,=20encryption,=20?=
=?UTF-8?q?pagination,=20Socket.IO=20init?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../41-collaborator-beta-application.md | 89 ++++++++
docs/features/42-support-ticket-system.md | 90 ++++++++
docs/features/43-design-inspiration-system.md | 130 +++++++++++
docs/features/44-link-and-repo-management.md | 126 +++++++++++
docs/features/45-meet-routes.md | 104 +++++++++
docs/features/46-webhook-routes.md | 93 ++++++++
docs/features/47-task-routes-standalone.md | 135 +++++++++++
.../48-encryption-security-utilities.md | 159 +++++++++++++
.../features/49-pagination-utility-helpers.md | 175 +++++++++++++++
docs/features/50-socket-io-initialization.md | 211 ++++++++++++++++++
10 files changed, 1312 insertions(+)
create mode 100644 docs/features/41-collaborator-beta-application.md
create mode 100644 docs/features/42-support-ticket-system.md
create mode 100644 docs/features/43-design-inspiration-system.md
create mode 100644 docs/features/44-link-and-repo-management.md
create mode 100644 docs/features/45-meet-routes.md
create mode 100644 docs/features/46-webhook-routes.md
create mode 100644 docs/features/47-task-routes-standalone.md
create mode 100644 docs/features/48-encryption-security-utilities.md
create mode 100644 docs/features/49-pagination-utility-helpers.md
create mode 100644 docs/features/50-socket-io-initialization.md
diff --git a/docs/features/41-collaborator-beta-application.md b/docs/features/41-collaborator-beta-application.md
new file mode 100644
index 00000000..c3f0fdd7
--- /dev/null
+++ b/docs/features/41-collaborator-beta-application.md
@@ -0,0 +1,89 @@
+# 41 — Collaborator & Beta Application
+
+**NEW document** — Beta application submission, GitHub profile validation, collaborator intake flow
+
+---
+
+## Feature Summary
+
+The collaborator routes handle beta application submissions from users wanting to join the Zync platform. Applicants provide their GitHub username, profile URL, and email. The backend stores the application and notifies the team.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── FRONTEND ───────────────────────────┐
+│ │
+│ BetaApplicationPage.tsx │
+│ ├─ GitHub username input │
+│ ├─ GitHub profile URL input │
+│ ├─ Email input │
+│ └─ Submit → POST /api/collaborators │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/collaboratorRoutes.js │
+│ │
+│ POST / → submit beta application │
+│ │
+│ Logic: │
+│ 1. Validate: githubUsername, githubProfileUrl, email │
+│ 2. Check for duplicate (email or GitHub username) │
+│ 3. Store application in DB │
+│ 4. Send notification email to admin │
+│ 5. Return success │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/collaboratorRoutes.js`
+
+### POST / (line 94)
+- **Auth:** not required (public endpoint for beta signups)
+- **Input:** `{ githubUsername, githubProfileUrl, email }`
+- **Logic:**
+ 1. Validate all fields present
+ 2. Check for duplicate: `Collaborator.findOne({ $or: [{ email }, { githubUsername }] })`
+ 3. If duplicate: 409 "Already applied"
+ 4. Create: `Collaborator.create({ githubUsername, githubProfileUrl, email, status: 'pending' })`
+ 5. Send admin notification email
+- **Response:** `{ message: "Application submitted", id }`
+
+---
+
+## Database Layer
+
+### Collaborator Model
+| Field | Type | Required | Notes |
+|---|---|---|---|
+| `githubUsername` | String | yes | GitHub handle |
+| `githubProfileUrl` | String | yes | Full GitHub URL |
+| `email` | String | yes | Contact email |
+| `status` | String | no | `pending`, `approved`, `rejected` |
+| `reviewedAt` | Date | no | When admin reviewed |
+| `createdAt` | Date | auto | |
+
+---
+
+## Error Paths
+
+| Scenario | HTTP Status | Response |
+|---|---|---|
+| Missing fields | 400 | `{ error: "All fields required" }` |
+| Duplicate application | 409 | `{ error: "Already applied" }` |
+| Server error | 500 | `{ error: "Server error" }` |
+
+---
+
+## Cross-References
+
+- [28-email-service-notifications.md](./28-email-service-notifications.md) — Admin notification email
+- [05-database-schema-and-models.md](./05-database-schema-and-models.md) — Collaborator model
diff --git a/docs/features/42-support-ticket-system.md b/docs/features/42-support-ticket-system.md
new file mode 100644
index 00000000..696ef944
--- /dev/null
+++ b/docs/features/42-support-ticket-system.md
@@ -0,0 +1,90 @@
+# 42 — Support Ticket System
+
+**NEW document** — Support request submission, email notification to admin, user contact form
+
+---
+
+## Feature Summary
+
+The support routes handle user support requests. Users submit a message (with optional email and name), and the backend sends an email notification to the Zync support team. No authentication required — accessible to all users.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── FRONTEND ───────────────────────────┐
+│ │
+│ SupportPage.tsx │
+│ ├─ Name input │
+│ ├─ Email input │
+│ ├─ Subject input │
+│ ├─ Message textarea │
+│ └─ Submit → POST /api/support │
+│ │
+│ Footer link → Support page │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/supportRoutes.js │
+│ │
+│ POST / → submit support request │
+│ │
+│ Logic: │
+│ 1. Validate: name, email, subject, message │
+│ 2. Build email HTML template │
+│ 3. sendZyncEmail(SUPPORT_EMAIL, subject, html) │
+│ 4. Return success │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/supportRoutes.js`
+
+### POST / (line 93)
+- **Auth:** not required
+- **Input:** `{ name, email, subject, message }`
+- **Logic:**
+ 1. Validate all fields present
+ 2. Build HTML email:
+ ```html
+
New Support Request
+ From: {name} ({email})
+ Subject: {subject}
+ Message:
+ {message}
+ ```
+ 3. `sendZyncEmail(process.env.SUPPORT_EMAIL, subject, html)`
+ 4. Return `{ message: "Support request sent" }`
+
+---
+
+## Error Paths
+
+| Scenario | HTTP Status | Response |
+|---|---|---|
+| Missing fields | 400 | `{ error: "All fields required" }` |
+| Email send fails (auth) | 200 | Still returns success (fail-open) |
+| Email send fails (network) | 500 | `{ error: "Failed to send request" }` |
+| Server error | 500 | `{ error: "Server error" }` |
+
+---
+
+## Environment Variables
+
+| Variable | Required | Description |
+|---|---|---|
+| `SUPPORT_EMAIL` | Yes | Email address to receive support requests |
+
+---
+
+## Cross-References
+
+- [28-email-service-notifications.md](./28-email-service-notifications.md) — Email service used for sending
diff --git a/docs/features/43-design-inspiration-system.md b/docs/features/43-design-inspiration-system.md
new file mode 100644
index 00000000..98cad158
--- /dev/null
+++ b/docs/features/43-design-inspiration-system.md
@@ -0,0 +1,130 @@
+# 43 — Design Inspiration System
+
+**NEW document** — Inspiration search, Dribbble scraping, live web scraping, design reference aggregation
+
+---
+
+## Feature Summary
+
+The design inspiration system allows users to search for design references from multiple sources. The backend aggregates results from Dribbble and live web scraping, providing designers with visual inspiration for their projects.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── FRONTEND ───────────────────────────┐
+│ │
+│ InspirationView.tsx │
+│ ├─ Search bar (query input) │
+│ ├─ Source tabs: All | Dribbble | Web │
+│ ├─ Results grid (image cards) │
+│ │ └─ GET /api/inspiration?query=... │
+│ ├─ Dribbble tab │
+│ │ └─ GET /api/inspiration/dribbble?query=... │
+│ └─ Live scrape tab │
+│ └─ GET /api/inspiration/scrape?query=... │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/inspirationRoutes.js │
+│ ├─ GET / → getInspiration (aggregated) │
+│ ├─ GET /dribbble → getDribbbleInspiration │
+│ └─ GET /scrape → getLiveScrape │
+│ │
+│ backend/routes/designRoutes.js │
+│ └─ GET /search → getInspiration (alias) │
+│ │
+│ backend/controllers/inspirationController.js │
+│ ├─ getInspiration(req, res) │
+│ │ ├─ Parse query param │
+│ │ ├─ Aggregate from multiple sources │
+│ │ └─ Return unified results │
+│ ├─ getDribbbleInspiration(req, res) │
+│ │ ├─ Scrape Dribbble search results │
+│ │ ├─ Parse HTML for image URLs + titles │
+│ │ └─ Return structured results │
+│ └─ getLiveScrape(req, res) │
+│ ├─ General web search for design images │
+│ ├─ Parse search engine results │
+│ └─ Return image URLs + metadata │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/inspirationRoutes.js`
+
+### GET / (line 86)
+- **Auth:** not specified (likely public or auth-required)
+- **Query:** `?query=`
+- **Handler:** `getInspiration` from `inspirationController.js`
+- **Returns:** Aggregated inspiration results from multiple sources
+
+### GET /dribbble (line 88)
+- **Query:** `?query=`
+- **Handler:** `getDribbbleInspiration`
+- **Returns:** Dribbble-specific design results
+
+### GET /scrape (line 84)
+- **Query:** `?query=`
+- **Handler:** `getLiveScrape`
+- **Returns:** Live web-scraped design images
+
+### File: `backend/routes/designRoutes.js`
+### GET /search (line 84)
+- Alias for `getInspiration` — same handler
+- Mounted at `/api/design/search`
+
+---
+
+## Controller Logic
+
+### File: `backend/controllers/inspirationController.js`
+
+### getInspiration
+1. Parse `query` from `req.query`
+2. If no query: return empty array
+3. Aggregate from sources:
+ - Dribbble results
+ - Web scrape results
+4. Return unified array: `[{ title, imageUrl, source, sourceUrl }]`
+
+### getDribbbleInspiration
+1. Build Dribbble search URL: `https://dribbble.com/search/{query}`
+2. Fetch HTML with axios
+3. Parse with cheerio:
+ - Extract shot images: `.shot-thumbnail img`
+ - Extract titles: `.shot-title`
+ - Extract author: `.shot-by-user`
+4. Return structured results
+
+### getLiveScrape
+1. Build search engine query for design images
+2. Fetch results page HTML
+3. Parse for image URLs and metadata
+4. Return results
+
+---
+
+## Error Paths
+
+| Scenario | HTTP Status | Response |
+|---|---|---|
+| No query provided | 200 | Empty array |
+| External site down | 200 | Partial results (best-effort) |
+| Scraping fails | 200 | Empty array (graceful) |
+| Server error | 500 | `{ error: error.message }` |
+
+---
+
+## Cross-References
+
+- [04-service-inventory.md](./04-service-inventory.md) — Inspiration controller listing
+- [14-project-crud.md](./14-project-crud.md) — Projects can reference inspiration
diff --git a/docs/features/44-link-and-repo-management.md b/docs/features/44-link-and-repo-management.md
new file mode 100644
index 00000000..6cd55e94
--- /dev/null
+++ b/docs/features/44-link-and-repo-management.md
@@ -0,0 +1,126 @@
+# 44 — Link & Repo Management
+
+**NEW document** — GitHub repo linking/unlinking to projects, repo sync, project-repo association
+
+---
+
+## Feature Summary
+
+The link routes handle associating GitHub repositories with Zync projects. Users can link a repo (stores repo ID on project), unlink a repo (removes association), and sync repo details. This enables architecture analysis, task branch automation, and PR merging.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── FRONTEND ───────────────────────────┐
+│ │
+│ ProjectWorkspace.tsx → Settings tab │
+│ ├─ Repo selector dropdown │
+│ │ └─ GET /api/github/repos → list repos │
+│ ├─ "Link Repo" button │
+│ │ └─ POST /api/links/link-repo │
+│ ├─ "Unlink Repo" button │
+│ │ └─ POST /api/links/unlink-repo │
+│ └─ Linked repo display (name, URL, branch) │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/linkRoutes.js │
+│ │
+│ POST /link-repo → associate repo with project │
+│ POST /unlink-repo → remove repo from project │
+│ │
+│ Logic (link): │
+│ 1. Verify project ownership │
+│ 2. Update project: githubRepoId, githubRepoName, │
+│ githubRepoOwner, githubDefaultBranch │
+│ 3. Invalidate project cache │
+│ 4. Return updated project │
+│ │
+│ Logic (unlink): │
+│ 1. Verify project ownership │
+│ 2. Clear repo fields on project │
+│ 3. Invalidate cache │
+│ 4. Return updated project │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/linkRoutes.js`
+
+### POST /link-repo (line 85)
+- **Auth:** required
+- **Input:** `{ projectId, githubRepoId }`
+- **Logic:**
+ 1. Get project: `Project.findById(projectId)`
+ 2. Verify ownership: `project.ownerUid === uid` or team membership
+ 3. Fetch repo details from GitHub API (using user's token)
+ 4. Update project:
+ ```js
+ project.githubRepoId = githubRepoId;
+ project.githubRepoName = repoData.name;
+ project.githubRepoOwner = repoData.owner.login;
+ project.githubDefaultBranch = repoData.default_branch;
+ await project.save();
+ ```
+ 5. Invalidate cache: `cache.invalidate('projects:' + uid)`
+ 6. Return updated project
+
+### POST /unlink-repo (line 121)
+- **Auth:** required
+- **Input:** `{ projectId, githubRepoId }`
+- **Logic:**
+ 1. Get project, verify ownership
+ 2. Clear repo fields:
+ ```js
+ project.githubRepoId = null;
+ project.githubRepoName = null;
+ project.githubRepoOwner = null;
+ project.githubDefaultBranch = null;
+ await project.save();
+ ```
+ 3. Invalidate cache
+ 4. Return updated project
+
+---
+
+## Database Changes
+
+### Project Model — GitHub Fields
+| Field | Type | Notes |
+|---|---|---|
+| `githubRepoId` | Number | GitHub repo ID |
+| `githubRepoName` | String | Repo name (e.g., "zync-meet") |
+| `githubRepoOwner` | String | Owner login (e.g., "zync-meet") |
+| `githubDefaultBranch` | String | Default branch (e.g., "main") |
+
+These fields are set on link and cleared on unlink.
+
+---
+
+## Error Paths
+
+| Scenario | HTTP Status | Response |
+|---|---|---|
+| No token | 401 | Unauthorized |
+| Project not found | 404 | `{ error: "Project not found" }` |
+| Not owner | 403 | `{ error: "Unauthorized" }` |
+| GitHub API error | 500 | `{ error: "Failed to fetch repo details" }` |
+| Server error | 500 | `{ error: "Server error" }` |
+
+---
+
+## Cross-References
+
+- [14-project-crud.md](./14-project-crud.md) — Project model with GitHub fields
+- [21-github-oauth-integration.md](./21-github-oauth-integration.md) — GitHub token for API calls
+- [25-ai-architecture-analysis.md](./25-ai-architecture-analysis.md) — Requires linked repo
+- [16-task-management.md](./16-task-management.md) — Branch creation uses linked repo
diff --git a/docs/features/45-meet-routes.md b/docs/features/45-meet-routes.md
new file mode 100644
index 00000000..60cfb4a7
--- /dev/null
+++ b/docs/features/45-meet-routes.md
@@ -0,0 +1,104 @@
+# 45 — Meet Routes
+
+**NEW document** — Meeting CRUD, scheduling, participant management, Google Meet link generation
+
+---
+
+## Feature Summary
+
+The meet routes handle meeting creation, listing, updates, and deletion. Each meeting can be associated with a project and generates a Google Meet link via the Google Calendar API. Participants are tracked and notified via email.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── FRONTEND ───────────────────────────┐
+│ │
+│ MeetingsView.tsx │
+│ ├─ Upcoming meetings list │
+│ ├─ "Schedule Meeting" button │
+│ │ └─ ScheduleMeetingDialog.tsx │
+│ │ ├─ Title, date/time, participants │
+│ │ └─ POST /api/meets │
+│ ├─ Meeting details view │
+│ │ ├─ Meet link (click to join) │
+│ │ ├─ Participant list │
+│ │ └─ Edit/Delete buttons │
+│ └─ Past meetings with duration │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/meetRoutes.js │
+│ │
+│ POST / → create meeting + Meet link │
+│ GET / → list user's meetings │
+│ GET /:id → get meeting details │
+│ PUT /:id → update meeting │
+│ DELETE /:id → delete meeting │
+│ POST /:id/join → join meeting (record participation)│
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/meetRoutes.js`
+
+### POST / (create meeting)
+- **Auth:** required
+- **Input:** `{ title, startTime, endTime?, participants?, projectId? }`
+- **Logic:**
+ 1. Create Google Calendar event with Meet conference data
+ 2. `googleMeet.createMeeting(title, startTime, participants)`
+ 3. Create Meeting document: `{ title, meetLink, hostId, participants, startTime, projectId, calendarEventId }`
+ 4. Send invitation emails to participants
+ 5. Return meeting with Meet link
+
+### GET / (list meetings)
+- **Auth:** required
+- **Query:** `?status=upcoming|past|all`
+- **Logic:** `Meeting.find({ $or: [{ hostId: uid }, { participants: uid }] }).sort({ startTime: -1 })`
+- **Response:** Paginated meeting list
+
+### GET /:id (meeting details)
+- **Auth:** required
+- **Logic:** Find meeting, verify participation
+- **Response:** Full meeting details with Meet link
+
+### PUT /:id (update meeting)
+- **Auth:** required (host only)
+- **Input:** Partial meeting fields
+- **Logic:** Update meeting, sync with Google Calendar if time changed
+- **Response:** Updated meeting
+
+### DELETE /:id (delete meeting)
+- **Auth:** required (host only)
+- **Logic:** Delete meeting, optionally delete Google Calendar event
+- **Response:** `{ message: "Meeting deleted" }`
+
+---
+
+## Error Paths
+
+| Scenario | HTTP Status | Response |
+|---|---|---|
+| No token | 401 | Unauthorized |
+| Meeting not found | 404 | `{ error: "Meeting not found" }` |
+| Not host (update/delete) | 403 | `{ error: "Unauthorized" }` |
+| Google Calendar API error | 500 | `{ error: "Failed to create meeting" }` |
+| Server error | 500 | `{ error: "Server error" }` |
+
+---
+
+## Cross-References
+
+- [30-meeting-system.md](./30-meeting-system.md) — Meeting system overview
+- [29-session-management.md](./29-session-management.md) — Sessions linked to meetings
+- [40-google-oauth-integration.md](./40-google-oauth-integration.md) — Google Calendar API
+- [28-email-service-notifications.md](./28-email-service-notifications.md) — Meeting invitations
diff --git a/docs/features/46-webhook-routes.md b/docs/features/46-webhook-routes.md
new file mode 100644
index 00000000..65e124e8
--- /dev/null
+++ b/docs/features/46-webhook-routes.md
@@ -0,0 +1,93 @@
+# 46 — Webhook Routes
+
+**NEW document** — Generic webhook handling, third-party integrations, event routing
+
+---
+
+## Feature Summary
+
+The webhook routes handle incoming webhooks from third-party services. Currently includes the GitHub App webhook handler (detailed in file 22) and may support additional integrations. Webhooks are verified, deduplicated, and processed asynchronously.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── EXTERNAL SERVICES ───────────────────┐
+│ │
+│ GitHub App ──── POST /api/github-app/webhook │
+│ (Future) Stripe ──── POST /api/webhooks/stripe │
+│ (Future) Slack ──── POST /api/webhooks/slack │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/webhookRoutes.js │
+│ ├─ Mounts githubAppWebhook at /github-app │
+│ └─ (Future) additional webhook handlers │
+│ │
+│ backend/routes/githubAppWebhook.js │
+│ ├─ POST /webhook → verifyGithub + enqueue │
+│ └─ GET /webhook/jobs/:deliveryId → job status │
+│ │
+│ Verification: │
+│ ├─ GitHub: HMAC SHA-256 (verifyGithub middleware) │
+│ ├─ Stripe: (future) signature verification │
+│ └─ Slack: (future) token verification │
+│ │
+│ Processing: │
+│ ├─ Queue: webhookQueue.js (in-memory Map) │
+│ ├─ Worker: githubWebhookWorker.js │
+│ └─ Dedup: by delivery ID │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/webhookRoutes.js`
+
+### Route Mounting
+```js
+const githubAppWebhook = require('./githubAppWebhook');
+router.use('/github-app', githubAppWebhook);
+```
+- Mounts GitHub App webhook handler at `/api/webhooks/github-app`
+- Future webhooks can be mounted similarly
+
+### GitHub App Webhook (detailed in file 22)
+- **POST /github-app/webhook** — receives + enqueues
+- **GET /github-app/webhook/jobs/:deliveryId** — job status
+- **Verification:** `verifyGithub` middleware (HMAC SHA-256)
+- **Processing:** Queue + worker pattern
+
+---
+
+## Webhook Processing Pattern
+
+All webhooks follow the same pattern:
+
+1. **Receive:** Express route receives POST request
+2. **Verify:** Middleware verifies signature/token
+3. **Deduplicate:** Check for duplicate delivery ID
+4. **Enqueue:** Add to processing queue
+5. **Acknowledge:** Return 202 Accepted immediately
+6. **Process:** Worker processes job asynchronously
+7. **Emit:** Socket.IO updates to frontend
+
+### Why Async Processing?
+- Webhook senders expect fast response (<5s)
+- Processing may involve DB writes, API calls, email sending
+- Failures in processing don't affect webhook acknowledgment
+- GitHub retries if response is not 200-level within 10s
+
+---
+
+## Cross-References
+
+- [22-github-webhook-handler.md](./22-github-webhook-handler.md) — GitHub App webhook deep dive
+- [06-middleware-stack.md](./06-middleware-stack.md) — Webhook raw body parsing
diff --git a/docs/features/47-task-routes-standalone.md b/docs/features/47-task-routes-standalone.md
new file mode 100644
index 00000000..9fe8b3fc
--- /dev/null
+++ b/docs/features/47-task-routes-standalone.md
@@ -0,0 +1,135 @@
+# 47 — Task Routes (Standalone)
+
+**NEW document** — Task-specific routes separate from project routes, task CRUD, assignment, search, quick tasks
+
+---
+
+## Feature Summary
+
+The task routes provide standalone task management endpoints separate from the project routes. While project routes handle task creation within project context, the task routes handle direct task operations: update, delete, search, and quick task creation.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── FRONTEND ───────────────────────────┐
+│ │
+│ KanbanBoard.tsx │
+│ ├─ Task cards (drag-and-drop between steps) │
+│ ├─ Task detail modal │
+│ │ ├─ Edit title, description │
+│ │ ├─ Assign to team member │
+│ │ ├─ Set priority, due date │
+│ │ └─ Delete task │
+│ └─ Quick add input (per step) │
+│ │
+│ Hooks: │
+│ ├─ useTasks.ts — TanStack Query for task list │
+│ ├─ useUpdateTask.ts — mutation │
+│ ├─ useDeleteTask.ts — mutation │
+│ └─ useQuickTask.ts — mutation │
+│ │
+└──────────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────── BACKEND ────────────────────────────┐
+│ │
+│ backend/routes/taskRoutes.js │
+│ │
+│ PUT /:taskId → update task │
+│ DELETE /:taskId → delete task │
+│ GET /search → search tasks by query │
+│ POST /quick → create quick task │
+│ PATCH /:taskId/step → move task to different step │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/routes/taskRoutes.js`
+
+### PUT /:taskId (update task)
+- **Auth:** required
+- **Input:** `{ title?, description?, assigneeId?, priority?, dueDate?, status? }`
+- **Logic:**
+ 1. Find task by ID
+ 2. Verify user has access (project owner or team member)
+ 3. Update fields
+ 4. If assignee changed: send notification email
+ 5. Emit `task-updated` via Socket.IO `/tasks` namespace
+ 6. Return updated task
+
+### DELETE /:taskId (delete task)
+- **Auth:** required
+- **Logic:**
+ 1. Find task, verify ownership/admin
+ 2. If task has GitHub branch: optionally delete branch
+ 3. Delete task from DB
+ 4. Emit `task-deleted` via Socket.IO
+ 5. Return `{ message: "Task deleted" }`
+
+### GET /search (search tasks)
+- **Auth:** required
+- **Query:** `?query=&projectId=`
+- **Logic:**
+ 1. Build filter: `{ projectId, title: { $regex: query, $options: 'i' } }`
+ 2. `ProjectTask.find(filter).lean()`
+ 3. Return matching tasks
+
+### POST /quick (quick task)
+- **Auth:** required
+- **Input:** `{ title, projectId, stepId }`
+- **Logic:**
+ 1. Create task with minimal fields (no description, no assignee)
+ 2. Add to step's task list
+ 3. Emit `task-created` via Socket.IO
+ 4. Return created task
+
+### PATCH /:taskId/step (move task)
+- **Auth:** required
+- **Input:** `{ newStepId, newIndex }`
+- **Logic:**
+ 1. Remove task from old step
+ 2. Add to new step at specified index
+ 3. Update `task.stepId = newStepId`
+ 4. Emit `task-moved` via Socket.IO
+ 5. Return updated task
+
+---
+
+## Socket.IO Integration
+
+### /tasks Namespace Events
+| Event | Direction | Payload | Purpose |
+|---|---|---|---|
+| `task-created` | Server → Client | Task object | New task added |
+| `task-updated` | Server → Client | Task object | Task fields changed |
+| `task-deleted` | Server → Client | `{ taskId }` | Task removed |
+| `task-moved` | Server → Client | `{ taskId, newStepId, newIndex }` | Task moved between steps |
+
+- Events emitted to project room: `project:{projectId}`
+- All team members receive updates in real-time
+
+---
+
+## Error Paths
+
+| Scenario | HTTP Status | Response |
+|---|---|---|
+| No token | 401 | Unauthorized |
+| Task not found | 404 | `{ error: "Task not found" }` |
+| Not authorized | 403 | `{ error: "Unauthorized" }` |
+| Server error | 500 | `{ error: "Server error" }` |
+
+---
+
+## Cross-References
+
+- [16-task-management.md](./16-task-management.md) — Task management in project routes
+- [15-project-steps-pipeline.md](./15-project-steps-pipeline.md) — Steps and Kanban pipeline
+- [22-github-webhook-handler.md](./22-github-webhook-handler.md) — Webhook updates tasks
+- [14-project-crud.md](./14-project-crud.md) — Project context for tasks
diff --git a/docs/features/48-encryption-security-utilities.md b/docs/features/48-encryption-security-utilities.md
new file mode 100644
index 00000000..67dd06b4
--- /dev/null
+++ b/docs/features/48-encryption-security-utilities.md
@@ -0,0 +1,159 @@
+# 48 — Encryption & Security Utilities
+
+**NEW document** — AES-256 token encryption, CryptoJS usage, encryption key management, sensitive data handling
+
+---
+
+## Feature Summary
+
+Zync uses AES-256 encryption to protect sensitive data at rest: GitHub OAuth tokens, Google OAuth tokens, and any other credentials stored in the database. The encryption utility provides `encrypt()` and `decrypt()` functions used across all integration services.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── BACKEND ─────────────────────────────┐
+│ │
+│ backend/utils/encryption.js │
+│ │
+│ encrypt(plaintext) → ciphertext string │
+│ decrypt(ciphertext) → plaintext string │
+│ │
+│ Algorithm: AES-256 (CryptoJS) │
+│ Key: process.env.ENCRYPTION_KEY │
+│ │
+│ Consumers: │
+│ ├─ github.js → encrypt/decrypt GitHub access tokens │
+│ ├─ googleRoutes.js → encrypt/decrypt Google tokens │
+│ ├─ userRoutes.js → encrypt/decrypt sensitive fields │
+│ └─ Any route storing third-party credentials │
+│ │
+│ Data Flow: │
+│ 1. User connects GitHub → accessToken received │
+│ 2. encrypt(accessToken) → ciphertext │
+│ 3. Store ciphertext in User.githubIntegration.accessToken │
+│ 4. When needed: decrypt(ciphertext) → plaintext │
+│ 5. Use plaintext for GitHub API calls │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/utils/encryption.js`
+
+### encrypt(plaintext)
+```js
+const CryptoJS = require('crypto-js');
+const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY;
+
+const encrypt = (text) => {
+ if (!text) return null;
+ return CryptoJS.AES.encrypt(text, ENCRYPTION_KEY).toString();
+};
+```
+- Returns Base64-encoded ciphertext string
+- If input is null/empty: returns null (no encryption needed)
+
+### decrypt(ciphertext)
+```js
+const decrypt = (ciphertext) => {
+ if (!ciphertext) return null;
+ try {
+ const bytes = CryptoJS.AES.decrypt(ciphertext, ENCRYPTION_KEY);
+ return bytes.toString(CryptoJS.enc.Utf8);
+ } catch (err) {
+ console.error('Decryption failed:', err.message);
+ return null;
+ }
+};
+```
+- Returns plaintext string
+- On error (wrong key, corrupted data): returns null
+- Never throws — callers check for null
+
+---
+
+## Usage Patterns
+
+### GitHub Token Storage
+```js
+// On connect:
+const encrypted = encrypt(accessToken);
+User.findOneAndUpdate({ uid }, { $set: { 'githubIntegration.accessToken': encrypted } });
+
+// When needed:
+const user = await User.findOne({ uid });
+const token = decrypt(user.githubIntegration.accessToken);
+// Use token for GitHub API calls
+```
+
+### Google Token Storage
+```js
+// On connect:
+User.findOneAndUpdate({ uid }, {
+ $set: {
+ 'googleIntegration.accessToken': encrypt(accessToken),
+ 'googleIntegration.refreshToken': encrypt(refreshToken),
+ }
+});
+```
+
+### Security Measures
+- **Tokens never returned to frontend:** `.select('-githubIntegration.accessToken')`
+- **Encryption at rest:** Even if DB is compromised, tokens are encrypted
+- **Key separation:** `ENCRYPTION_KEY` is separate from JWT secret
+- **No logging:** Decrypted tokens are never logged
+
+---
+
+## Key Management
+
+### Development
+- `ENCRYPTION_KEY` in `.env` file (not committed)
+- Can be any string (CryptoJS uses it as passphrase)
+
+### Production
+- `ENCRYPTION_KEY` set via environment variable in hosting platform
+- Should be a strong random string (32+ characters)
+- Key rotation requires re-encrypting all stored tokens
+
+### Key Rotation Process
+1. Generate new `ENCRYPTION_KEY`
+2. For each user with tokens:
+ a. Decrypt with old key
+ b. Re-encrypt with new key
+ c. Update User document
+3. Switch `ENCRYPTION_KEY` to new value
+4. Old key can be safely discarded
+
+---
+
+## Error Paths
+
+| Scenario | Handling |
+|---|---|
+| `ENCRYPTION_KEY` not set | CryptoJS uses empty string (insecure — should fail in prod) |
+| Decrypt with wrong key | Returns null (no crash) |
+| Decrypt corrupted data | Returns null (no crash) |
+| Encrypt null input | Returns null (no-op) |
+
+---
+
+## Environment Variables
+
+| Variable | Required | Description |
+|---|---|---|
+| `ENCRYPTION_KEY` | Yes (prod) | AES-256 encryption passphrase |
+
+---
+
+## Cross-References
+
+- [21-github-oauth-integration.md](./21-github-oauth-integration.md) — GitHub token encryption
+- [40-google-oauth-integration.md](./40-google-oauth-integration.md) — Google token encryption
+- [02-security-auth-architecture.md](./02-security-auth-architecture.md) — Security overview
+- [22-github-webhook-handler.md](./22-github-webhook-handler.md) — HMAC verification (different from AES)
diff --git a/docs/features/49-pagination-utility-helpers.md b/docs/features/49-pagination-utility-helpers.md
new file mode 100644
index 00000000..55d5e30c
--- /dev/null
+++ b/docs/features/49-pagination-utility-helpers.md
@@ -0,0 +1,175 @@
+# 49 — Pagination & Utility Helpers
+
+**NEW document** — Array pagination, HTTP pagination headers, regex escaping, normalization utilities
+
+---
+
+## Feature Summary
+
+Zync uses a set of utility helpers for consistent pagination, regex safety, and data normalization across all routes. The pagination utility provides array-based pagination with HTTP headers, while regex utilities prevent regex injection from user input.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── BACKEND UTILITIES ───────────────────┐
+│ │
+│ backend/utils/pagination.js │
+│ ├─ paginateArray(array, query, options) → { items, │
+│ │ pagination } │
+│ └─ setPaginationHeaders(res, pagination) → void │
+│ │
+│ backend/utils/regexUtils.js │
+│ └─ escapeRegExp(string) → safe string │
+│ │
+│ backend/utils/normalize.js │
+│ ├─ normalizeEmail(email) → lowercase trimmed │
+│ └─ normalizeString(str) → trimmed string │
+│ │
+│ Consumers: │
+│ ├─ All list endpoints (projects, tasks, notes, chat) │
+│ ├─ Search endpoints (user search, task search) │
+│ └─ Any route with user-provided regex input │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/utils/pagination.js`
+
+### paginateArray(array, query, options)
+```js
+function paginateArray(array, query, options = {}) {
+ const page = Math.max(1, parseInt(query.page) || 1);
+ const limit = Math.min(
+ parseInt(query.limit) || options.defaultLimit || 20,
+ options.maxLimit || 100
+ );
+ const startIndex = (page - 1) * limit;
+ const endIndex = startIndex + limit;
+ const items = array.slice(startIndex, endIndex);
+ const total = array.length;
+ const totalPages = Math.ceil(total / limit);
+
+ return {
+ items,
+ pagination: { page, limit, total, totalPages, hasNext: page < totalPages, hasPrev: page > 1 }
+ };
+}
+```
+- **In-memory pagination:** Slices already-fetched array
+- **Page-based:** `?page=2&limit=20`
+- **Bounds:** `page` minimum 1, `limit` capped at `maxLimit`
+- **Returns:** Items + pagination metadata
+
+### setPaginationHeaders(res, pagination)
+```js
+function setPaginationHeaders(res, pagination) {
+ res.setHeader('X-Page', pagination.page);
+ res.setHeader('X-Page-Size', pagination.limit);
+ res.setHeader('X-Total-Count', pagination.total);
+ res.setHeader('X-Total-Pages', pagination.totalPages);
+ if (pagination.hasNext) res.setHeader('X-Next-Page', pagination.page + 1);
+ if (pagination.hasPrev) res.setHeader('X-Prev-Page', pagination.page - 1);
+}
+```
+- **Standard headers:** Frontend reads these for pagination UI
+- **Conditional:** Next/prev headers only set when applicable
+
+### Usage Example
+```js
+const { paginateArray, setPaginationHeaders } = require('../utils/pagination');
+
+router.get('/conversations', verifyToken, async (req, res) => {
+ const conversations = await Message.aggregate([...]);
+ const { items, pagination } = paginateArray(conversations, req.query, {
+ defaultLimit: 100,
+ maxLimit: 200,
+ });
+ setPaginationHeaders(res, pagination);
+ res.json(items);
+});
+```
+
+---
+
+### File: `backend/utils/regexUtils.js`
+
+### escapeRegExp(string)
+```js
+const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+```
+- Escapes all special regex characters: `. * + ? ^ $ { } ( ) | [ ] \`
+- Example: `John.*Doe` → `John\.\*Doe`
+- **Security:** Prevents regex injection where user input could match unintended patterns or cause ReDoS
+
+### Usage Example
+```js
+const { escapeRegExp } = require('../utils/regexUtils');
+
+router.get('/search', verifyToken, async (req, res) => {
+ const safeQuery = escapeRegExp(req.query.query);
+ const users = await User.find({
+ displayName: { $regex: safeQuery, $options: 'i' }
+ });
+ res.json(users);
+});
+```
+
+---
+
+### File: `backend/utils/normalize.js`
+
+### normalizeEmail(email)
+```js
+const normalizeEmail = (email) => email?.trim().toLowerCase();
+```
+- Trims whitespace and lowercases
+- Prevents duplicate accounts from case differences
+
+### normalizeString(str)
+```js
+const normalizeString = (str) => str?.trim();
+```
+- Trims whitespace from user input
+- Used for names, titles, descriptions
+
+---
+
+## Pagination Headers Reference
+
+| Header | Description |
+|---|---|
+| `X-Page` | Current page number |
+| `X-Page-Size` | Items per page |
+| `X-Total-Count` | Total items across all pages |
+| `X-Total-Pages` | Total number of pages |
+| `X-Next-Page` | Next page number (if exists) |
+| `X-Prev-Page` | Previous page number (if exists) |
+
+---
+
+## Endpoints Using Pagination
+
+| Endpoint | Default Limit | Max Limit |
+|---|---|---|
+| GET /api/chat/conversations | 100 | 200 |
+| GET /api/chat/history/:chatId | 50 | 200 |
+| GET /api/notes | 50 | 100 |
+| GET /api/projects | 20 | 100 |
+| GET /api/users/search | 20 | 50 |
+| GET /api/sessions/:userId | 50 | 100 |
+| GET /api/teams/:teamId/activity | 50 | 100 |
+
+---
+
+## Cross-References
+
+- [23-instant-chat-system.md](./23-instant-chat-system.md) — Chat conversations pagination
+- [39-user-search-and-discovery.md](./39-user-search-and-discovery.md) — User search with escapeRegExp
+- [17-notes-system.md](./17-notes-system.md) — Notes list pagination
+- [14-project-crud.md](./14-project-crud.md) — Project list pagination
diff --git a/docs/features/50-socket-io-initialization.md b/docs/features/50-socket-io-initialization.md
new file mode 100644
index 00000000..d210b89a
--- /dev/null
+++ b/docs/features/50-socket-io-initialization.md
@@ -0,0 +1,211 @@
+# 50 — Socket.IO Initialization & Namespaces
+
+**NEW document** — Socket.IO server setup, namespace registration, connection middleware, Redis adapter
+
+---
+
+## Feature Summary
+
+Socket.IO is initialized in the main server file with multiple namespaces for different features: `/presence` (user online status), `/chat` (real-time messaging), `/notes` (collaborative editing), `/tasks` (Kanban updates). Each namespace has its own handler, connection middleware, and event set.
+
+---
+
+## Architecture Diagram
+
+```
+┌─────────────────── BACKEND (index.js) ──────────────────┐
+│ │
+│ const io = socketIo(server, { │
+│ cors: { origin: FRONTEND_URL, credentials: true }, │
+│ transports: ['websocket', 'polling'] │
+│ }); │
+│ │
+│ Namespaces: │
+│ ┌─────────────────────────────────────────────────┐ │
+│ │ /presence → presenceSocketHandler(io) │ │
+│ │ Events: user-online, user-offline, user-away │ │
+│ │ Query: { userId } │ │
+│ ├─────────────────────────────────────────────────┤ │
+│ │ /chat → chatSocketHandler(io) │ │
+│ │ Events: send-message, mark-seen, typing, │ │
+│ │ clear-chat, new-message, │ │
+│ │ message-delivered, message-seen │ │
+│ │ Query: { userId } │ │
+│ ├─────────────────────────────────────────────────┤ │
+│ │ /notes → noteSocketHandler(io) │ │
+│ │ Events: join_note, cursor_move, note-update, │ │
+│ │ awareness-update, leave_note, │ │
+│ │ presence_update, user_left │ │
+│ │ Query: { userId } │ │
+│ ├─────────────────────────────────────────────────┤ │
+│ │ /tasks → taskSocketHandler(io) │ │
+│ │ Events: task-created, task-updated, │ │
+│ │ task-deleted, task-moved │ │
+│ │ Rooms: project:{projectId} │ │
+│ └─────────────────────────────────────────────────┘ │
+│ │
+│ app.set('io', io); // Global access │
+│ app.set('taskIO', io.of('/tasks')); // Task namespace │
+│ │
+│ Redis Adapter (optional): │
+│ io.adapter(redisAdapter({ host: REDIS_HOST, ... })); │
+│ → Enables multi-server broadcast │
+│ │
+└─────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Backend Trace
+
+### File: `backend/index.js` (or `backend/server.js`)
+
+### Socket.IO Server Creation
+```js
+const socketIo = require('socket.io');
+const http = require('http');
+const server = http.createServer(app);
+const io = socketIo(server, {
+ cors: {
+ origin: process.env.FRONTEND_URL || 'http://localhost:3000',
+ credentials: true,
+ },
+ transports: ['websocket', 'polling'],
+});
+```
+
+### Namespace Registration
+```js
+const presenceSocketHandler = require('./sockets/presenceSocketHandler');
+const chatSocketHandler = require('./sockets/chatSocketHandler');
+const noteSocketHandler = require('./sockets/noteSocketHandler');
+const taskSocketHandler = require('./sockets/taskSocketHandler');
+
+presenceSocketHandler(io);
+chatSocketHandler(io);
+noteSocketHandler(io);
+taskSocketHandler(io);
+```
+
+### Global Access
+```js
+app.set('io', io);
+app.set('taskIO', io.of('/tasks'));
+```
+- Routes can access Socket.IO via `req.app.get('io')`
+- Used by: webhook worker (emit task updates), project routes (emit project changes)
+
+### Redis Adapter (Production)
+```js
+const redisAdapter = require('socket.io-redis');
+io.adapter(redisAdapter({ host: process.env.REDIS_HOST, port: 6379 }));
+```
+- Enables broadcast across multiple Node.js instances
+- Events emitted on one server reach clients connected to other servers
+- Required for horizontal scaling
+
+---
+
+## Namespace Comparison
+
+| Namespace | Purpose | Connection Query | Rooms | In-Memory State |
+|---|---|---|---|---|
+| `/presence` | User online/offline/away | `{ userId }` | Per-user | `userSockets` Map |
+| `/chat` | Real-time messaging | `{ userId }` | Per-user (multi-device) | `userSockets` Map |
+| `/notes` | Collaborative editing | `{ userId }` | Per-note | `notePresence` Map |
+| `/tasks` | Kanban board updates | `{ userId }` | Per-project | None (stateless relay) |
+
+---
+
+## Connection Lifecycle
+
+```
+1. Client connects to namespace:
+ const socket = io('/chat', { query: { userId: 'abc123' } });
+
+2. Server receives connection:
+ namespace.on('connection', (socket) => {
+ const userId = socket.handshake.query.userId;
+ // Register, setup event handlers
+ });
+
+3. Client emits events:
+ socket.emit('send-message', { ... });
+
+4. Server forwards/processes:
+ socket.on('send-message', (payload) => { ... });
+
+5. Server emits to clients:
+ namespace.to(room).emit('event', data);
+ // OR
+ emitToUser(userId, 'event', data);
+
+6. Client disconnects:
+ socket.on('disconnect', () => { /* cleanup */ });
+ // OR
+ socket.disconnect();
+```
+
+---
+
+## Frontend Connection
+
+### Socket Context Provider
+**File:** `src/context/SocketContext.tsx`
+```js
+import { io } from 'socket.io-client';
+
+const SocketProvider = ({ children }) => {
+ const presenceSocket = io('/presence', { query: { userId } });
+ const chatSocket = io('/chat', { query: { userId } });
+ const notesSocket = io('/notes', { query: { userId } });
+ const tasksSocket = io('/tasks', { query: { userId } });
+
+ return
+ {children}
+ ;
+};
+```
+
+### Hook Usage
+```js
+const { chatSocket } = useSocket();
+
+useEffect(() => {
+ chatSocket.on('new-message', (msg) => {
+ // Handle incoming message
+ });
+ return () => chatSocket.off('new-message');
+}, []);
+```
+
+---
+
+## Error Paths
+
+| Scenario | Handling |
+|---|---|
+| No userId in query | `socket.disconnect()` (all namespaces) |
+| Redis adapter fails | Falls back to single-server mode |
+| Namespace handler error | Caught in try-catch, logged |
+| Client reconnect | Socket.IO auto-reconnects with backoff |
+
+---
+
+## Environment Variables
+
+| Variable | Required | Description |
+|---|---|---|
+| `FRONTEND_URL` | Yes | CORS origin for Socket.IO |
+| `REDIS_HOST` | No (prod) | Redis adapter host for scaling |
+| `REDIS_PORT` | No | Default: 6379 |
+
+---
+
+## Cross-References
+
+- [11-presence-system.md](./11-presence-system.md) — /presence namespace
+- [19-realtime-notes-collaboration.md](./19-realtime-notes-collaboration.md) — /notes namespace
+- [24-chat-socket-handler.md](./24-chat-socket-handler.md) — /chat namespace
+- [47-task-routes-standalone.md](./47-task-routes-standalone.md) — /tasks namespace
+- [06-middleware-stack.md](./06-middleware-stack.md) — Express + Socket.IO setup