# Doclea Implementation Plan
> Generated from specialized architect agents | December 2025
> Target: ClickUp task creation and sprint planning
---
## Table of Contents
1. [Project Overview](#1-project-overview)
2. [Architecture Overview](#2-architecture-overview)
3. [Database Design](#3-database-design)
4. [API Specification](#4-api-specification)
5. [Authentication & Security](#5-authentication--security)
6. [AI/LLM Integration](#6-aillm-integration)
7. [Infrastructure & Deployment](#7-infrastructure--deployment)
8. [Testing Strategy](#8-testing-strategy)
9. [Implementation Phases](#9-implementation-phases)
---
## 1. Project Overview
### 1.1 What is Doclea?
Doclea is an AI-powered documentation assistant that helps teams create, manage, and search documentation with intelligent memory features. It combines:
- **MCP Server**: Model Context Protocol integration for AI tool access
- **Vector Search**: Semantic search across all documentation
- **Team Collaboration**: Multi-tenant workspaces with role-based access
- **Memory System**: Persistent AI memory for context continuity
### 1.2 Tech Stack
| Layer | Technology |
|-------|------------|
| Runtime | Bun |
| Framework | Elysia |
| Database | PostgreSQL (Neon) |
| Vector DB | Qdrant Cloud |
| Cache/Queue | Redis (Upstash) |
| Auth | Better Auth |
| ORM | Kysely (queries) + Prisma (schema) |
| Embeddings | Nomic Embed v1.5 |
| Observability | Pino + Sentry + PostHog |
| Deployment | Railway |
---
## 2. Architecture Overview
### 2.1 System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTS │
├─────────────┬─────────────┬─────────────┬─────────────┬─────────┤
│ Web App │ Mobile App │ CLI/MCP │ Webhooks │ API │
└──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┴────┬────┘
│ │ │ │ │
└─────────────┴─────────────┼─────────────┴───────────┘
│
┌──────────────▼──────────────┐
│ API Gateway (Elysia) │
│ • Rate Limiting │
│ • Auth Middleware │
│ • Request Validation │
└──────────────┬──────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
┌──────▼──────┐ ┌─────────▼─────────┐ ┌────────▼────────┐
│ Auth │ │ Core Services │ │ AI Services │
│ Service │ │ │ │ │
│ • Sessions │ │ • Teams │ │ • Embeddings │
│ • OAuth │ │ • Projects │ │ • RAG Pipeline │
│ • API Keys │ │ • Documents │ │ • MCP Tools │
└──────┬──────┘ │ • Usage Tracking │ │ • Memory Search │
│ └─────────┬──────────┘ └────────┬────────┘
│ │ │
└───────────────────────────┼───────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌───────▼───────┐
│ PostgreSQL │ │ Qdrant │ │ Redis │
│ (Neon) │ │ Cloud │ │ (Upstash) │
│ │ │ │ │ │
│ • Users │ │ • Vectors │ │ • Sessions │
│ • Teams │ │ • Metadata │ │ • Rate Limits │
│ • Projects │ │ • Search │ │ • Job Queue │
│ • Billing │ │ │ │ • Cache │
└────────────┘ └─────────────┘ └───────────────┘
```
### 2.2 Directory Structure
```
apps/backend/
├── src/
│ ├── index.ts # App entry point
│ ├── env.ts # Environment validation
│ │
│ ├── auth/ # Authentication module
│ │ ├── index.ts
│ │ ├── auth.service.ts
│ │ └── auth.plugin.ts
│ │
│ ├── common/ # Shared utilities
│ │ ├── observability/ # Logging, metrics, tracing
│ │ ├── plugins/ # Elysia plugins
│ │ ├── services/ # Shared services (Redis, etc)
│ │ └── utils/ # Helpers, validators
│ │
│ ├── database/ # Database layer
│ │ ├── database.service.ts # Kysely instance
│ │ ├── types.ts # Generated by prisma-kysely
│ │ └── enums.ts # Generated enums
│ │
│ ├── modules/ # Feature modules
│ │ ├── teams/
│ │ │ ├── teams.routes.ts
│ │ │ ├── teams.service.ts
│ │ │ ├── teams.types.ts
│ │ │ └── teams.repository.ts
│ │ │
│ │ ├── projects/
│ │ ├── documents/
│ │ ├── memories/
│ │ ├── usage/
│ │ └── billing/
│ │
│ ├── ai/ # AI/LLM services
│ │ ├── embeddings/
│ │ ├── rag/
│ │ ├── mcp/
│ │ └── prompts/
│ │
│ └── __tests__/ # Test files
│ ├── setup.ts
│ ├── unit/
│ ├── integration/
│ └── e2e/
│
├── prisma/
│ ├── schema.prisma
│ └── migrations/
│
└── package.json
```
---
## 3. Database Design
### 3.1 PostgreSQL Schema (Prisma)
#### Core Tables
```prisma
// User & Auth (Better Auth managed)
model User {
id String @id @default(dbgenerated("gen_random_uuid()"))
email String @unique
emailVerified Boolean @default(false) @map("email_verified")
name String?
image String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
sessions Session[]
accounts Account[]
teamMembers TeamMember[]
apiKeys ApiKey[]
@@map("user")
}
model Session {
id String @id
userId String @map("user_id")
expiresAt DateTime @map("expires_at")
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("session")
}
model Account {
id String @id @default(dbgenerated("gen_random_uuid()"))
userId String @map("user_id")
accountId String @map("account_id")
providerId String @map("provider_id")
accessToken String? @map("access_token")
refreshToken String? @map("refresh_token")
accessTokenExpiresAt DateTime? @map("access_token_expires_at")
refreshTokenExpiresAt DateTime? @map("refresh_token_expires_at")
scope String?
idToken String? @map("id_token")
password String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("account")
}
model Verification {
id String @id @default(dbgenerated("gen_random_uuid()"))
identifier String
value String
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("verification")
}
// Teams & RBAC
model Team {
id String @id @default(dbgenerated("gen_random_uuid()"))
name String
slug String @unique
description String?
image String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
members TeamMember[]
projects Project[]
apiKeys ApiKey[]
usageRecords UsageRecord[]
subscription Subscription?
@@map("team")
}
model TeamMember {
id String @id @default(dbgenerated("gen_random_uuid()"))
teamId String @map("team_id")
userId String @map("user_id")
role TeamRole @default(MEMBER)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([teamId, userId])
@@map("team_member")
}
enum TeamRole {
OWNER
ADMIN
MEMBER
VIEWER
}
// Projects & Documents
model Project {
id String @id @default(dbgenerated("gen_random_uuid()"))
teamId String @map("team_id")
name String
slug String
description String?
status ProjectStatus @default(ACTIVE)
settings Json @default("{}")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
documents Document[]
memories Memory[]
@@unique([teamId, slug])
@@map("project")
}
enum ProjectStatus {
ACTIVE
ARCHIVED
DELETED
}
model Document {
id String @id @default(dbgenerated("gen_random_uuid()"))
projectId String @map("project_id")
title String
content String
contentHash String @map("content_hash")
type DocumentType @default(MARKDOWN)
metadata Json @default("{}")
version Int @default(1)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
chunks Chunk[]
@@map("document")
}
enum DocumentType {
MARKDOWN
CODE
CONVERSATION
NOTE
}
model Chunk {
id String @id @default(dbgenerated("gen_random_uuid()"))
documentId String @map("document_id")
content String
startOffset Int @map("start_offset")
endOffset Int @map("end_offset")
tokenCount Int @map("token_count")
qdrantId String @map("qdrant_id")
createdAt DateTime @default(now()) @map("created_at")
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
@@map("chunk")
}
// AI Memory
model Memory {
id String @id @default(dbgenerated("gen_random_uuid()"))
projectId String @map("project_id")
type MemoryType
content String
summary String?
importance Float @default(0.5)
metadata Json @default("{}")
qdrantId String @map("qdrant_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
accessedAt DateTime @default(now()) @map("accessed_at")
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@map("memory")
}
enum MemoryType {
FACT
PREFERENCE
DECISION
CONTEXT
CONVERSATION
}
// API Keys
model ApiKey {
id String @id @default(dbgenerated("gen_random_uuid()"))
teamId String @map("team_id")
userId String @map("user_id")
name String
keyHash String @unique @map("key_hash")
keyPrefix String @map("key_prefix")
scopes String[] @default([])
lastUsedAt DateTime? @map("last_used_at")
expiresAt DateTime? @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
revokedAt DateTime? @map("revoked_at")
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("api_key")
}
// Usage & Billing
model UsageRecord {
id String @id @default(dbgenerated("gen_random_uuid()"))
teamId String @map("team_id")
type UsageType
amount Int
metadata Json @default("{}")
recordedAt DateTime @default(now()) @map("recorded_at")
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@index([teamId, type, recordedAt])
@@map("usage_record")
}
enum UsageType {
API_CALL
EMBEDDING
STORAGE_MB
VECTOR_SEARCH
MCP_TOOL_CALL
}
model Subscription {
id String @id @default(dbgenerated("gen_random_uuid()"))
teamId String @unique @map("team_id")
plan SubscriptionPlan @default(FREE)
status SubscriptionStatus @default(ACTIVE)
currentPeriodStart DateTime @map("current_period_start")
currentPeriodEnd DateTime @map("current_period_end")
stripeCustomerId String? @map("stripe_customer_id")
stripeSubscriptionId String? @map("stripe_subscription_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@map("subscription")
}
enum SubscriptionPlan {
FREE
PRO
TEAM
ENTERPRISE
}
enum SubscriptionStatus {
ACTIVE
PAST_DUE
CANCELED
TRIALING
}
// Audit Log
model AuditLog {
id String @id @default(dbgenerated("gen_random_uuid()"))
teamId String? @map("team_id")
userId String? @map("user_id")
action String
resource String
resourceId String? @map("resource_id")
metadata Json @default("{}")
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
createdAt DateTime @default(now()) @map("created_at")
@@index([teamId, createdAt])
@@index([userId, createdAt])
@@map("audit_log")
}
```
### 3.2 Qdrant Vector Schema
```typescript
// Collection: doclea_vectors
interface VectorPayload {
// Identifiers
id: string; // UUID
type: 'chunk' | 'memory';
// References
teamId: string;
projectId: string;
documentId?: string; // For chunks
memoryId?: string; // For memories
// Content
content: string; // Original text
tokenCount: number;
// Metadata
memoryType?: 'fact' | 'preference' | 'decision' | 'context' | 'conversation';
importance?: number; // 0.0 - 1.0
// Timestamps
createdAt: string; // ISO datetime
accessedAt?: string; // For memories
}
// Qdrant collection config
const collectionConfig = {
name: 'doclea_vectors',
vectors: {
size: 768, // Nomic Embed v1.5 dimensions
distance: 'Cosine'
},
optimizers_config: {
indexing_threshold: 20000
},
hnsw_config: {
m: 16,
ef_construct: 100
}
};
```
### 3.3 Database Tasks
| ID | Task | Priority | Estimate |
|----|------|----------|----------|
| DB-001 | Create full Prisma schema with all models | High | - |
| DB-002 | Run prisma-kysely generation | High | - |
| DB-003 | Create initial migration | High | - |
| DB-004 | Set up Qdrant collection with proper config | High | - |
| DB-005 | Implement database service with connection pooling | High | - |
| DB-006 | Add database health check | Medium | - |
| DB-007 | Create repository layer for each module | Medium | - |
| DB-008 | Add soft delete support where needed | Low | - |
| DB-009 | Set up database indexes for common queries | Medium | - |
| DB-010 | Implement audit log triggers | Low | - |
---
## 4. API Specification
### 4.1 Authentication Endpoints
```
POST /api/auth/sign-up # Email/password registration
POST /api/auth/sign-in # Email/password login
POST /api/auth/sign-out # Logout current session
GET /api/auth/session # Get current session
POST /api/auth/forgot-password # Request password reset
POST /api/auth/reset-password # Reset password with token
POST /api/auth/verify-email # Verify email address
# OAuth
GET /api/auth/oauth/google # Initiate Google OAuth
GET /api/auth/oauth/google/callback
GET /api/auth/oauth/github # Initiate GitHub OAuth
GET /api/auth/oauth/github/callback
GET /api/auth/oauth/apple # Initiate Apple OAuth
GET /api/auth/oauth/apple/callback
```
### 4.2 User Endpoints
```
GET /api/users/me # Get current user profile
PATCH /api/users/me # Update profile
DELETE /api/users/me # Delete account
GET /api/users/me/teams # List user's teams
```
### 4.3 Team Endpoints
```
POST /api/teams # Create team
GET /api/teams # List user's teams
GET /api/teams/:teamId # Get team details
PATCH /api/teams/:teamId # Update team
DELETE /api/teams/:teamId # Delete team
# Members
GET /api/teams/:teamId/members # List team members
POST /api/teams/:teamId/members # Invite member
PATCH /api/teams/:teamId/members/:userId # Update member role
DELETE /api/teams/:teamId/members/:userId # Remove member
# Invitations
POST /api/teams/:teamId/invitations # Create invitation
GET /api/teams/:teamId/invitations # List pending invitations
DELETE /api/teams/:teamId/invitations/:id # Revoke invitation
POST /api/invitations/:token/accept # Accept invitation
```
### 4.4 Project Endpoints
```
POST /api/teams/:teamId/projects # Create project
GET /api/teams/:teamId/projects # List projects
GET /api/projects/:projectId # Get project
PATCH /api/projects/:projectId # Update project
DELETE /api/projects/:projectId # Delete/archive project
```
### 4.5 Document Endpoints
```
POST /api/projects/:projectId/documents # Create document
GET /api/projects/:projectId/documents # List documents
GET /api/documents/:documentId # Get document
PATCH /api/documents/:documentId # Update document
DELETE /api/documents/:documentId # Delete document
# Bulk operations
POST /api/projects/:projectId/documents/bulk # Bulk create/update
```
### 4.6 Memory Endpoints
```
POST /api/projects/:projectId/memories # Create memory
GET /api/projects/:projectId/memories # List memories
GET /api/memories/:memoryId # Get memory
PATCH /api/memories/:memoryId # Update memory
DELETE /api/memories/:memoryId # Delete memory
# Search
POST /api/projects/:projectId/memories/search # Semantic search
```
### 4.7 Search Endpoints
```
POST /api/projects/:projectId/search # Unified search
POST /api/teams/:teamId/search # Cross-project search
# Request body
{
"query": "string",
"types": ["document", "memory"], // optional filter
"limit": 10,
"threshold": 0.7 // similarity threshold
}
```
### 4.8 MCP Tool Endpoints
```
POST /api/mcp/tools/memory-store # Store memory via MCP
POST /api/mcp/tools/memory-search # Search memories via MCP
POST /api/mcp/tools/context-fetch # Fetch relevant context
GET /api/mcp/manifest # MCP server manifest
```
### 4.9 API Key Endpoints
```
POST /api/teams/:teamId/api-keys # Create API key
GET /api/teams/:teamId/api-keys # List API keys
DELETE /api/api-keys/:keyId # Revoke API key
```
### 4.10 Usage & Billing Endpoints
```
GET /api/teams/:teamId/usage # Get usage stats
GET /api/teams/:teamId/usage/export # Export usage data
GET /api/teams/:teamId/subscription # Get subscription
POST /api/teams/:teamId/subscription # Create/update subscription
POST /api/webhooks/stripe # Stripe webhook handler
```
### 4.11 Health Endpoints
```
GET /health # Full health check
GET /ready # Readiness probe
GET /live # Liveness probe
```
### 4.12 API Tasks
| ID | Task | Priority | Estimate |
|----|------|----------|----------|
| API-001 | Implement auth routes with Better Auth | High | - |
| API-002 | Create team CRUD endpoints | High | - |
| API-003 | Create project CRUD endpoints | High | - |
| API-004 | Create document CRUD endpoints | High | - |
| API-005 | Create memory CRUD endpoints | High | - |
| API-006 | Implement search endpoints | High | - |
| API-007 | Implement MCP tool endpoints | High | - |
| API-008 | Create API key management endpoints | Medium | - |
| API-009 | Implement usage tracking endpoints | Medium | - |
| API-010 | Add Stripe webhook handler | Medium | - |
| API-011 | Add OpenAPI/Swagger documentation | Low | - |
| API-012 | Implement rate limiting per endpoint | Medium | - |
| API-013 | Add request validation with Zod | High | - |
| API-014 | Implement pagination helpers | Medium | - |
---
## 5. Authentication & Security
### 5.1 Better Auth Configuration
```typescript
// src/auth/auth.service.ts
import { betterAuth } from 'better-auth';
import { db } from '../database/database.service';
export const auth = betterAuth({
database: {
provider: 'postgresql',
url: env.DATABASE_URL,
},
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
minPasswordLength: 12,
},
socialProviders: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
github: {
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
},
apple: {
clientId: env.APPLE_CLIENT_ID,
clientSecret: env.APPLE_CLIENT_SECRET,
},
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
cookieCache: {
enabled: true,
maxAge: 60 * 5, // 5 minutes
},
},
rateLimit: {
enabled: true,
window: 60,
max: 100,
},
});
```
### 5.2 RBAC (Role-Based Access Control)
```typescript
// Permission definitions
const PERMISSIONS = {
// Team permissions
'team:read': ['owner', 'admin', 'member', 'viewer'],
'team:update': ['owner', 'admin'],
'team:delete': ['owner'],
'team:manage_members': ['owner', 'admin'],
// Project permissions
'project:create': ['owner', 'admin', 'member'],
'project:read': ['owner', 'admin', 'member', 'viewer'],
'project:update': ['owner', 'admin', 'member'],
'project:delete': ['owner', 'admin'],
// Document permissions
'document:create': ['owner', 'admin', 'member'],
'document:read': ['owner', 'admin', 'member', 'viewer'],
'document:update': ['owner', 'admin', 'member'],
'document:delete': ['owner', 'admin', 'member'],
// Memory permissions
'memory:create': ['owner', 'admin', 'member'],
'memory:read': ['owner', 'admin', 'member', 'viewer'],
'memory:update': ['owner', 'admin', 'member'],
'memory:delete': ['owner', 'admin'],
// API key permissions
'apikey:create': ['owner', 'admin'],
'apikey:read': ['owner', 'admin'],
'apikey:delete': ['owner', 'admin'],
// Billing permissions
'billing:read': ['owner', 'admin'],
'billing:manage': ['owner'],
};
```
### 5.3 API Key Authentication
```typescript
// API key format: dk_live_<random32chars>
// Stored as SHA-256 hash in database
interface ApiKeyAuth {
// Header: Authorization: Bearer dk_live_xxxxx
// Or: X-API-Key: dk_live_xxxxx
scopes: string[]; // ['read', 'write', 'admin']
teamId: string;
userId: string;
}
```
### 5.4 Security Headers
```typescript
// Applied by securityHeadersPlugin
const securityHeaders = {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'Content-Security-Policy': "default-src 'self'",
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
};
```
### 5.5 Rate Limiting Strategy
| Endpoint Category | Window | Max Requests | By |
|-------------------|--------|--------------|-----|
| Auth (sign-in/up) | 15 min | 5 | IP |
| Auth (password reset) | 1 hour | 3 | Email |
| API (authenticated) | 1 min | 100 | User |
| API (API key) | 1 min | 1000 | Key |
| Search | 1 min | 30 | User |
| MCP Tools | 1 min | 60 | User |
### 5.6 Security Tasks
| ID | Task | Priority | Estimate |
|----|------|----------|----------|
| SEC-001 | Configure Better Auth with all providers | High | - |
| SEC-002 | Implement RBAC middleware | High | - |
| SEC-003 | Create API key generation/validation | High | - |
| SEC-004 | Add rate limiting with Redis | High | - |
| SEC-005 | Implement security headers plugin | High | - |
| SEC-006 | Set up audit logging | Medium | - |
| SEC-007 | Add input sanitization | High | - |
| SEC-008 | Implement CORS configuration | High | - |
| SEC-009 | Add encryption for sensitive data | Medium | - |
| SEC-010 | Set up CSP headers | Medium | - |
| SEC-011 | Implement session management | High | - |
| SEC-012 | Add brute force protection | Medium | - |
---
## 6. AI/LLM Integration
### 6.1 Embedding Pipeline
```typescript
// src/ai/embeddings/embedding.service.ts
interface EmbeddingService {
// Model: Nomic Embed v1.5 (768 dimensions)
embed(text: string): Promise<number[]>;
embedBatch(texts: string[]): Promise<number[][]>;
// Chunking strategy
chunkDocument(content: string, options: {
maxTokens: number; // 512 default
overlap: number; // 50 tokens
preserveParagraphs: boolean;
}): Chunk[];
}
// Chunking algorithm
// 1. Split by paragraphs
// 2. Merge small paragraphs
// 3. Split large paragraphs by sentences
// 4. Ensure overlap for context continuity
```
### 6.2 RAG (Retrieval-Augmented Generation)
```typescript
// src/ai/rag/rag.service.ts
interface RAGService {
// Search pipeline
search(query: string, options: {
projectId: string;
types: ('document' | 'memory')[];
limit: number;
threshold: number;
rerank: boolean;
}): Promise<SearchResult[]>;
// Build context for LLM
buildContext(results: SearchResult[], maxTokens: number): string;
}
// Search flow:
// 1. Embed query
// 2. Vector search in Qdrant
// 3. Optional: Rerank with cross-encoder
// 4. Return with metadata
```
### 6.3 MCP (Model Context Protocol) Tools
```typescript
// src/ai/mcp/tools.ts
// Tool: memory_store
interface MemoryStoreTool {
name: 'memory_store';
description: 'Store a new memory for the project';
parameters: {
content: string;
type: 'fact' | 'preference' | 'decision' | 'context';
importance?: number;
metadata?: Record<string, unknown>;
};
}
// Tool: memory_search
interface MemorySearchTool {
name: 'memory_search';
description: 'Search memories semantically';
parameters: {
query: string;
types?: MemoryType[];
limit?: number;
};
}
// Tool: context_fetch
interface ContextFetchTool {
name: 'context_fetch';
description: 'Fetch relevant context from documents and memories';
parameters: {
query: string;
maxTokens?: number;
};
}
// Tool: document_search
interface DocumentSearchTool {
name: 'document_search';
description: 'Search documents semantically';
parameters: {
query: string;
limit?: number;
};
}
```
### 6.4 MCP Server Manifest
```json
{
"name": "doclea",
"version": "1.0.0",
"description": "AI documentation assistant with memory",
"tools": [
{
"name": "memory_store",
"description": "Store a memory for persistent context",
"inputSchema": {
"type": "object",
"properties": {
"content": { "type": "string" },
"type": { "type": "string", "enum": ["fact", "preference", "decision", "context"] },
"importance": { "type": "number", "minimum": 0, "maximum": 1 }
},
"required": ["content", "type"]
}
},
{
"name": "memory_search",
"description": "Search stored memories",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"limit": { "type": "integer", "default": 10 }
},
"required": ["query"]
}
},
{
"name": "context_fetch",
"description": "Fetch relevant context for a topic",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"maxTokens": { "type": "integer", "default": 4000 }
},
"required": ["query"]
}
}
]
}
```
### 6.5 Caching Strategy
```typescript
// Redis cache keys
const cacheKeys = {
// Embedding cache (24h TTL)
embedding: (hash: string) => `emb:${hash}`,
// Search results cache (5min TTL)
search: (projectId: string, queryHash: string) => `search:${projectId}:${queryHash}`,
// Memory importance scores (1h TTL)
importance: (memoryId: string) => `imp:${memoryId}`,
};
```
### 6.6 AI Tasks
| ID | Task | Priority | Estimate |
|----|------|----------|----------|
| AI-001 | Implement embedding service with Nomic | High | - |
| AI-002 | Create document chunking algorithm | High | - |
| AI-003 | Implement Qdrant service for vector ops | High | - |
| AI-004 | Build RAG search pipeline | High | - |
| AI-005 | Create MCP tool handlers | High | - |
| AI-006 | Implement MCP manifest endpoint | Medium | - |
| AI-007 | Add embedding caching with Redis | Medium | - |
| AI-008 | Implement batch embedding for bulk ops | Medium | - |
| AI-009 | Add search result reranking | Low | - |
| AI-010 | Create context building logic | High | - |
| AI-011 | Implement memory importance scoring | Medium | - |
| AI-012 | Add prompt templates | Medium | - |
---
## 7. Infrastructure & Deployment
### 7.1 Service Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Railway │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ API Service │ │
│ │ • Elysia application │ │
│ │ • Auto-scaling based on CPU/memory │ │
│ │ • Health checks: /health, /ready, /live │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Neon │ │ Qdrant Cloud │ │ Upstash │
│ PostgreSQL │ │ Vectors │ │ Redis │
│ │ │ │ │ │
│ • Serverless │ │ • Managed │ │ • Serverless │
│ • Auto-scale │ │ • 768 dims │ │ • Global │
│ • Branching │ │ • Cosine sim │ │ • REST API │
└───────────────┘ └───────────────┘ └───────────────┘
```
### 7.2 Environment Configuration
```bash
# Production (.env.production)
NODE_ENV=production
PORT=3000
# Database (Neon)
DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/doclea?sslmode=require
# Redis (Upstash)
REDIS_URL=rediss://default:xxx@xxx.upstash.io:6379
# Auth
AUTH_SECRET=<32+ char secret>
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx
GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx
# CORS
FRONTEND_URL=https://app.doclea.io
MOBILE_URL=doclea://
# Observability
SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
POSTHOG_API_KEY=phc_xxx
LOGTAIL_SOURCE_TOKEN=xxx
# AI
QDRANT_URL=https://xxx.cloud.qdrant.io:6333
QDRANT_API_KEY=xxx
NOMIC_API_KEY=xxx
# Billing
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
```
### 7.3 Railway Configuration
```toml
# railway.toml
[build]
builder = "nixpacks"
[deploy]
startCommand = "bun run start"
healthcheckPath = "/health"
healthcheckTimeout = 30
restartPolicyType = "on_failure"
restartPolicyMaxRetries = 3
[service]
internalPort = 3000
```
### 7.4 CI/CD Pipeline
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun run typecheck
- run: bun run test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: railwayapp/railway-action@v1
with:
token: ${{ secrets.RAILWAY_TOKEN }}
```
### 7.5 Backup Strategy
| Data | Method | Frequency | Retention |
|------|--------|-----------|-----------|
| PostgreSQL | Neon PITR | Continuous | 7 days |
| PostgreSQL | Manual snapshot | Daily | 30 days |
| Qdrant | Snapshot to S3 | Daily | 14 days |
| Redis | Upstash auto-backup | Daily | 7 days |
### 7.6 Infrastructure Tasks
| ID | Task | Priority | Estimate |
|----|------|----------|----------|
| INF-001 | Set up Railway project | High | - |
| INF-002 | Configure Neon database | High | - |
| INF-003 | Set up Qdrant Cloud cluster | High | - |
| INF-004 | Configure Upstash Redis | High | - |
| INF-005 | Set up GitHub Actions CI/CD | High | - |
| INF-006 | Configure environment variables | High | - |
| INF-007 | Set up Sentry project | Medium | - |
| INF-008 | Configure PostHog | Medium | - |
| INF-009 | Set up domain and SSL | Medium | - |
| INF-010 | Configure backup schedules | Medium | - |
| INF-011 | Set up monitoring alerts | Medium | - |
| INF-012 | Create staging environment | Low | - |
---
## 8. Testing Strategy
### 8.1 Testing Stack
- **Test Runner**: Bun test (built-in)
- **Assertions**: Bun expect + custom matchers
- **Mocking**: Bun mock
- **HTTP Testing**: Elysia test utilities
- **Database**: Test containers / in-memory SQLite
### 8.2 Test Structure
```
src/__tests__/
├── setup.ts # Global setup
├── helpers/
│ ├── database.ts # Test DB utilities
│ ├── auth.ts # Auth mocking
│ └── factories.ts # Test data factories
│
├── unit/
│ ├── services/
│ │ ├── auth.test.ts
│ │ ├── teams.test.ts
│ │ └── ...
│ └── utils/
│ ├── validation.test.ts
│ └── ...
│
├── integration/
│ ├── auth.test.ts
│ ├── teams.test.ts
│ ├── projects.test.ts
│ └── ...
│
└── e2e/
├── auth-flow.test.ts
├── team-management.test.ts
└── ...
```
### 8.3 Test Configuration
```typescript
// src/__tests__/setup.ts
import { beforeAll, afterAll, beforeEach } from 'bun:test';
import { setupTestDatabase, teardownTestDatabase, cleanupTestData } from './helpers/database';
beforeAll(async () => {
await setupTestDatabase();
});
afterAll(async () => {
await teardownTestDatabase();
});
beforeEach(async () => {
await cleanupTestData();
});
```
### 8.4 Test Examples
```typescript
// Unit test example
import { describe, it, expect, mock } from 'bun:test';
import { TeamsService } from '@src/modules/teams/teams.service';
describe('TeamsService', () => {
describe('create', () => {
it('should create a team with valid data', async () => {
const service = new TeamsService(mockDb);
const result = await service.create({
name: 'Test Team',
ownerId: 'user-123',
});
expect(result).toMatchObject({
name: 'Test Team',
slug: 'test-team',
});
});
it('should generate unique slug on conflict', async () => {
// ...
});
});
});
// Integration test example
import { describe, it, expect } from 'bun:test';
import { app } from '@src/index';
describe('POST /api/teams', () => {
it('should create team when authenticated', async () => {
const response = await app.handle(
new Request('http://localhost/api/teams', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cookie': `session=${testSession}`,
},
body: JSON.stringify({ name: 'New Team' }),
})
);
expect(response.status).toBe(201);
const body = await response.json();
expect(body.name).toBe('New Team');
});
it('should return 401 when not authenticated', async () => {
const response = await app.handle(
new Request('http://localhost/api/teams', {
method: 'POST',
body: JSON.stringify({ name: 'New Team' }),
})
);
expect(response.status).toBe(401);
});
});
```
### 8.5 Coverage Goals
| Category | Target |
|----------|--------|
| Unit Tests | 80% |
| Integration Tests | 70% |
| E2E Tests | Critical paths |
| Overall | 75% |
### 8.6 Testing Tasks
| ID | Task | Priority | Estimate |
|----|------|----------|----------|
| TEST-001 | Set up test infrastructure | High | - |
| TEST-002 | Create test factories for all models | High | - |
| TEST-003 | Write auth service unit tests | High | - |
| TEST-004 | Write teams module tests | High | - |
| TEST-005 | Write projects module tests | High | - |
| TEST-006 | Write documents module tests | High | - |
| TEST-007 | Write memory/AI service tests | High | - |
| TEST-008 | Create auth integration tests | High | - |
| TEST-009 | Create API integration tests | High | - |
| TEST-010 | Write E2E tests for critical flows | Medium | - |
| TEST-011 | Set up coverage reporting | Medium | - |
| TEST-012 | Add performance benchmarks | Low | - |
---
## 9. Implementation Phases
### Phase 1: Foundation (Sprint 1-2)
**Goal**: Core infrastructure and authentication
| ID | Task | Module | Priority |
|----|------|--------|----------|
| P1-001 | Complete Prisma schema with all models | Database | High |
| P1-002 | Run migrations and generate Kysely types | Database | High |
| P1-003 | Set up Qdrant collection | Database | High |
| P1-004 | Configure Better Auth with OAuth | Auth | High |
| P1-005 | Implement session management | Auth | High |
| P1-006 | Create auth middleware | Auth | High |
| P1-007 | Set up test infrastructure | Testing | High |
| P1-008 | Configure CI/CD pipeline | Infra | High |
| P1-009 | Deploy to Railway (staging) | Infra | High |
**Deliverables**:
- Working authentication (email + OAuth)
- Database with all tables
- CI/CD pipeline
- Staging environment
---
### Phase 2: Core Features (Sprint 3-4)
**Goal**: Teams, projects, and basic CRUD
| ID | Task | Module | Priority |
|----|------|--------|----------|
| P2-001 | Implement teams CRUD | Teams | High |
| P2-002 | Implement RBAC middleware | Auth | High |
| P2-003 | Create team member management | Teams | High |
| P2-004 | Implement projects CRUD | Projects | High |
| P2-005 | Create documents CRUD | Documents | High |
| P2-006 | Implement API key management | Auth | Medium |
| P2-007 | Add audit logging | Security | Medium |
| P2-008 | Write integration tests | Testing | High |
**Deliverables**:
- Full team management
- Project/document CRUD
- API key authentication
- RBAC enforcement
---
### Phase 3: AI Integration (Sprint 5-6)
**Goal**: Embeddings, search, and memory
| ID | Task | Module | Priority |
|----|------|--------|----------|
| P3-001 | Implement embedding service | AI | High |
| P3-002 | Create chunking algorithm | AI | High |
| P3-003 | Build Qdrant service | AI | High |
| P3-004 | Implement memory CRUD | Memory | High |
| P3-005 | Create RAG search pipeline | AI | High |
| P3-006 | Implement semantic search endpoints | API | High |
| P3-007 | Add embedding caching | AI | Medium |
| P3-008 | Write AI service tests | Testing | High |
**Deliverables**:
- Document embedding on create/update
- Semantic search across docs/memories
- Memory management
- Cached embeddings
---
### Phase 4: MCP & Polish (Sprint 7-8)
**Goal**: MCP tools and production readiness
| ID | Task | Module | Priority |
|----|------|--------|----------|
| P4-001 | Implement MCP tool handlers | MCP | High |
| P4-002 | Create MCP manifest endpoint | MCP | High |
| P4-003 | Implement usage tracking | Billing | Medium |
| P4-004 | Add Stripe integration | Billing | Medium |
| P4-005 | Performance optimization | All | Medium |
| P4-006 | Security audit | Security | High |
| P4-007 | Documentation | Docs | Medium |
| P4-008 | Production deployment | Infra | High |
**Deliverables**:
- Working MCP server
- Usage tracking
- Payment integration
- Production deployment
---
## Appendix A: API Response Formats
### Success Response
```json
{
"data": { ... },
"meta": {
"requestId": "req_xxx",
"timestamp": "2025-12-08T12:00:00Z"
}
}
```
### Paginated Response
```json
{
"data": [ ... ],
"meta": {
"total": 100,
"page": 1,
"pageSize": 20,
"totalPages": 5,
"requestId": "req_xxx"
}
}
```
### Error Response
```json
{
"error": "Validation error",
"statusCode": 400,
"method": "POST",
"path": "/api/teams",
"timestamp": "2025-12-08T12:00:00Z",
"requestId": "req_xxx",
"validationErrors": [
{
"field": "name",
"message": "Name is required"
}
]
}
```
---
## Appendix B: Environment Variables Reference
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| NODE_ENV | Yes | development | Environment mode |
| PORT | Yes | 3000 | Server port |
| DATABASE_URL | Yes | - | PostgreSQL connection string |
| REDIS_URL | No | - | Redis connection string |
| AUTH_SECRET | Yes | - | Better Auth secret (32+ chars) |
| GOOGLE_CLIENT_ID | No | - | Google OAuth client ID |
| GOOGLE_CLIENT_SECRET | No | - | Google OAuth client secret |
| GITHUB_CLIENT_ID | No | - | GitHub OAuth client ID |
| GITHUB_CLIENT_SECRET | No | - | GitHub OAuth client secret |
| APPLE_CLIENT_ID | No | - | Apple OAuth client ID |
| APPLE_CLIENT_SECRET | No | - | Apple OAuth client secret |
| FRONTEND_URL | Yes | - | Frontend URL for CORS |
| MOBILE_URL | No | - | Mobile app URL scheme |
| SENTRY_DSN | No | - | Sentry DSN for error tracking |
| POSTHOG_API_KEY | No | - | PostHog API key |
| LOGTAIL_SOURCE_TOKEN | No | - | Logtail token for logging |
| SLACK_WEBHOOK_URL | No | - | Slack webhook for alerts |
| QDRANT_URL | Yes | - | Qdrant Cloud URL |
| QDRANT_API_KEY | Yes | - | Qdrant API key |
| NOMIC_API_KEY | Yes | - | Nomic API key for embeddings |
| STRIPE_SECRET_KEY | No | - | Stripe secret key |
| STRIPE_WEBHOOK_SECRET | No | - | Stripe webhook secret |
| ENCRYPTION_KEY | No | - | Encryption key for sensitive data |
---
## Appendix C: Task Summary by Module
### Database (10 tasks)
- DB-001 to DB-010
### API (14 tasks)
- API-001 to API-014
### Security (12 tasks)
- SEC-001 to SEC-012
### AI/LLM (12 tasks)
- AI-001 to AI-012
### Infrastructure (12 tasks)
- INF-001 to INF-012
### Testing (12 tasks)
- TEST-001 to TEST-012
### Phase Tasks (32 tasks)
- P1-001 to P1-009 (Foundation)
- P2-001 to P2-008 (Core Features)
- P3-001 to P3-008 (AI Integration)
- P4-001 to P4-008 (MCP & Polish)
**Total: ~90 tasks**
---
*This implementation plan was generated by specialized architect agents analyzing the Doclea AI specification. Import into ClickUp to create actionable tasks with proper dependencies and assignments.*