Skip to main content
Cookie preferences

We use analytics cookies to understand usage and improve CleanTextLab. You can accept or decline Privacy policy. Manage preferences.

Back to Docs
System Documentation

Architecture

CleanTextLab Architecture Documentation

Version: 1.7.0 Last Updated: January 5, 2026 Maintainer: Tyson K.


Table of Contents

  1. System Overview
  2. Architecture Principles
  3. Folder Structure
  4. Data Flow
  5. Key Components
  6. State Management
  7. Authentication & Authorization
  8. API Architecture
  9. Tool Architecture Pattern
  10. Feature Flags & Entitlements
  11. Performance & Optimization
  12. Security Model
  13. Deployment
  14. Decision Log

System Overview

CleanTextLab is a Next.js 15 application providing 29 text processing tools (including a workflow builder), API access, and Pro features. The application follows a client-first architecture where all text processing happens in the browser, with optional cloud sync for Pro users.

Core Value Proposition

  • Privacy-First: All processing happens client-side (browser)
  • Developer-Friendly: REST API for automation (/api/v1/run)
  • Pro Features: Cloud history sync, batch processing, API keys
  • Workflow Automation: Chain multiple tools together
  • Multi-Language: 9 locales (en, es, fr, pt, de, hi, ar, sw, zu)

Technology Stack

┌─────────────────────────────────────────────────┐
│                  Frontend                        │
├─────────────────────────────────────────────────┤
│ Next.js 15.5.9 (App Router)                     │
│ React 19 + TypeScript 5.x                       │
│ Tailwind CSS v4 (CSS Variables Theme System)    │
│ Lucide Icons                                     │
│ React Markdown (Docs rendering)                 │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│                  Backend                         │
├─────────────────────────────────────────────────┤
│ Next.js API Routes                              │
│ Clerk (Authentication)                          │
│ Upstash Redis (Cloud Storage)                   │
│ Stripe (Payments)                               │
│ Google Analytics 4                              │
│ Microsoft Clarity (optional)                    │
│ Resend (transactional email)                    │
│ AI SDK (Google/OpenAI) for smart routing        │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│                Infrastructure                    │
├─────────────────────────────────────────────────┤
│ Vercel (Hosting + Edge Functions)               │
│ GitHub (Version Control)                        │
│ Playwright (E2E Testing)                        │
│ MCP CLI (bin/mcp.js)                            │
└─────────────────────────────────────────────────┘

Architecture Principles

1. Client-First Processing

All text transformations happen in the browser using Web APIs and custom JavaScript logic. Nothing is sent to the server unless explicitly requested (sharing, cloud sync).

Rationale: Privacy, speed, and cost efficiency.

2. Progressive Enhancement

Free users get full tool functionality. Pro users get enhancements:

  • Cloud history sync (encrypted)
  • API access (5,000 req/day)
  • Batch processing (5,000 lines vs 100 free)
  • Priority support

3. Composable Tools

Each tool is a self-contained React component with:

  • Input state management
  • Processing logic
  • Output rendering
  • History tracking
  • Keyboard shortcuts

Tools can be chained together via the Workflow Builder.

4. API-First for Automation

The /api/v1/run endpoint allows chaining multiple tools in a single API call:

POST /api/v1/run
{
  "input": "messy text",
  "steps": ["trim-lines", "upper-case", "dedupe"]
}

5. Themeable by Design

11 themes (Dark, Light, Ocean, Neon, etc.) using CSS variables:

  • --bg, --surface, --text-primary, --accent
  • All components use these variables
  • Theme state persists in localStorage

Folder Structure

cleantextlab/
├── src/
│   ├── app/                    # Next.js App Router
│   │   ├── api/               # Backend API routes
│   │   │   ├── v1/           # Public API (requires API key)
│   │   │   │   ├── run/      # Workflow runner endpoint
│   │   │   │   ├── sanitize/ # URL sanitizer endpoint
│   │   │   │   └── sort-dedupe/ # Sort & dedupe endpoint
│   │   │   ├── stripe/       # Payment webhooks
│   │   │   ├── entitlement/  # Pro plan checks
│   │   │   ├── history/      # Cloud history sync
│   │   │   ├── share/        # Share link generation
│   │   │   └── settings/     # User preferences sync
│   │   ├── tools/            # Tool pages (29 tools)
│   │   │   ├── json-formatter/
│   │   │   ├── sort-remove-duplicates/
│   │   │   ├── workflows/   # Workflow builder
│   │   │   └── [tool-name]/
│   │   ├── docs/            # Documentation pages
│   │   │   ├── api/         # Public API docs
│   │   │   └── cloud-history-sync/
│   │   ├── ar/              # Arabic localization
│   │   ├── de/              # German localization
│   │   ├── es/              # Spanish localization
│   │   ├── fr/              # French localization
│   │   ├── hi/              # Hindi localization
│   │   ├── pt/              # Portuguese localization
│   │   ├── sw/              # Swahili localization
│   │   ├── zu/              # Zulu localization
│   │   └── (pages)/         # Marketing/support pages
│   │
│   ├── components/
│   │   ├── tools/           # 29 tool implementations
│   │   │   ├── JsonFormatterTool.tsx
│   │   │   ├── SortRemoveDuplicatesTool.tsx
│   │   │   └── [ToolName]Tool.tsx
│   │   ├── GlobalNav.tsx     # Top navigation
│   │   ├── CommandMenu.tsx   # Cmd+K search
│   │   ├── MagicInput.tsx    # Smart paste detection
│   │   ├── ShareActions.tsx  # Share/export dropdown
│   │   ├── ToolHistoryDropdown.tsx
│   │   ├── PresetManager.tsx # Workflow presets
│   │   └── (shared components)/
│   │
│   ├── hooks/              # Custom React hooks
│   │   ├── useUndoRedo.ts     # History management (50-item stack)
│   │   ├── useToolHistory.ts   # Tool usage tracking
│   │   ├── useShareableTool.ts # Share link generation
│   │   ├── useEntitlement.ts   # Pro plan checks
│   │   ├── useMagicPayload.ts  # Smart paste integration
│   │   ├── useKeyboardShortcuts.ts
│   │   ├── useSettingsSync.ts  # Cloud settings sync
│   │   └── useDebounce.ts
│   │
│   ├── lib/                # Utilities & business logic
│   │   ├── agents/
│   │   │   └── contentDetector.ts  # Smart paste detection
│   │   ├── server/
│   │   │   ├── storage.ts          # Upstash Redis client
│   │   │   └── apiKeyRotation.ts
│   │   ├── analytics.ts            # GA4 tracking
│   │   ├── encryption.ts           # AES-256-GCM for cloud sync
│   │   ├── workflowEngine.ts       # Tool chaining logic
│   │   ├── textTransforms.ts       # Core text processing
│   │   ├── toolMetadata.ts         # Tool metadata (SEO + docs)
│   │   ├── toolsRegistry.ts        # Tool catalog for UI
│   │   ├── pdfExport.ts           # Client-side PDF generation
│   │   ├── csvExport.ts           # CSV generation
│   │   └── userPreferences.ts     # Theme/settings management
│   │
│   └── workers/            # Web Workers (future)
│       └── textProcessor.worker.ts
│
├── e2e/                    # Playwright E2E tests
│   ├── api-smoke.spec.ts
│   ├── comprehensive-tools.spec.ts
│   ├── magic-input.spec.ts
│   └── (50+ test files)
│
├── public/                 # Static assets
│   ├── images/
│   └── robots.txt
│
└── docs/                   # Documentation (you are here)
    ├── ARCHITECTURE.md     ← This file
    ├── TOOL_REGISTRY.md
    ├── PATTERNS.md
    ├── API_REFERENCE.md
    └── CHANGELOG.md

Key Directories Explained

src/app/ - Next.js App Router file-based routing src/components/tools/ - Each tool is a self-contained component src/hooks/ - Reusable React hooks (business logic extraction) src/lib/ - Pure functions, utilities, and server-side code e2e/ - End-to-end tests for critical user flows


Data Flow

Client-Side Processing Flow

┌─────────────┐
│   User      │
│  Input      │ (types text)
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────────┐
│  Tool Component                     │
│  - Input state (useUndoRedo)        │
│  - Processing logic (pure function) │
│  - Output rendering                 │
└──────┬──────────────────────────────┘
       │
       ├───► [Tool History] → localStorage
       │
       ├───► [Analytics] → GA4
       │
       └───► [Share/Export]
              ├─► Cloud Share → Upstash Redis
              ├─► PDF Export → Client-side PDF.js
              └─► CSV Export → Blob download

Cloud Sync Flow (Pro Users)

┌─────────────┐
│   User      │
│  (Pro Plan) │
└──────┬──────┘
       │
       ▼
┌─────────────────────────────────┐
│  Tool Usage                     │
│  - Input/Output generated       │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│  useToolHistory Hook            │
│  - Saves to localStorage        │
│  - Debounced sync to cloud      │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│  /api/history (POST)            │
│  - Encrypts data (AES-256-GCM)  │
│  - Stores in Upstash Redis      │
│  - TTL: 90 days                 │
└─────────────────────────────────┘

API Request Flow

┌─────────────┐
│  External   │
│  Client     │ (curl, n8n, Zapier)
└──────┬──────┘
       │ POST /api/v1/run
       │ x-api-key: abc123
       ▼
┌─────────────────────────────────┐
│  Middleware                     │
│  - Rate limiting (10 req/min)   │
│  - API key validation           │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│  /api/v1/run Handler            │
│  - Parse steps array            │
│  - Execute workflow             │
│  - Return result                │
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│  workflowEngine.ts              │
│  - Chain tool executors         │
│  - Run transforms sequentially  │
└─────────────────────────────────┘

Key Components

1. Tool Component Pattern

Every tool follows this structure:

// Example: JsonFormatterTool.tsx
export function JsonFormatterTool() {
  // 1. State management with undo/redo
  const { state: input, setState: setInput, undo, redo, canUndo, canRedo }
    = useUndoRedo("");

  // 2. Tool-specific processing logic
  const output = useMemo(() => processInput(input), [input]);

  // 3. History tracking
  const { history, clearHistory } = useToolHistory(TOOL_SLUG, () => ({
    input,
    output,
    metadata: { /* tool-specific data */ }
  }));

  // 4. Share functionality
  const { handleShare, shareStatus } = useShareableTool({
    toolSlug: TOOL_SLUG,
    sharePayload: output || input
  });

  // 5. Keyboard shortcuts
  useKeyboardShortcuts([
    { key: "Enter", ctrlOrCmd: true, action: handleProcess },
    { key: "z", ctrlOrCmd: true, action: undo }
  ]);

  // 6. Render: Input → Actions → Output
  return (
    <div>
      <textarea value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={handleProcess}>Process</button>
      <textarea value={output} readOnly />
    </div>
  );
}

2. MagicInput Component

Smart paste detection that suggests tools based on content:

// lib/agents/contentDetector.ts
export function detectContent(text: string): {
  type: string;
  confidence: number;
  suggestedTool: string;
}

// Detects:
// - JSON → json-formatter
// - URLs → sanitize-url
// - Phone numbers → phone-number-formatter
// - Base64 → base64-encode-decode
// - Cron expressions → cron-generator
// - SQL → sql-formatter

3. Workflow Builder

Allows chaining multiple tools:

// User creates workflow:
[
  { tool: "trim-lines", config: {} },
  { tool: "dedupe", config: { ignoreCase: true } },
  { tool: "sort", config: { direction: "asc" } }
]

// Executed via workflowEngine.ts
const result = await executeWorkflow(input, steps);

4. Share System

Two modes:

  1. URL Encoding (small payloads): ?shared=base64EncodedData
  2. Cloud Storage (large payloads): /share/abc123 → Redis
// useShareableTool.ts
if (payload.length < 1000) {
  // Use URL param
  return `${baseUrl}?shared=${btoa(payload)}`;
} else {
  // Store in Redis
  const id = generateId();
  await redis.set(`share:${id}`, payload, { ex: 2592000 }); // 30 days
  return `${baseUrl}/share/${id}`;
}

State Management

Local State (React)

  • Component state: useState for ephemeral UI state
  • Form state: useUndoRedo for input fields (50-item history)
  • Derived state: useMemo for computed outputs

Persistent State (localStorage)

{
  "user_preferences": {
    "theme": "dark",
    "compactMode": false,
    "historyLimit": 50
  },
  "tool_history_json-formatter": [...], // Last 50 uses
  "recent_tools": ["json-formatter", "url-sanitizer"],
  "workflow_presets": [...]
}

Cloud State (Upstash Redis) - Pro Only

// Key pattern: userId:tool:timestamp
"user_abc123:json-formatter:1702500000": {
  input: "...",
  output: "...",
  metadata: {},
  encrypted: true // AES-256-GCM
}

Global State (React Context)

  • Clerk Auth Context: User session, Pro plan status
  • Theme Context: Current theme, theme switcher

Authentication & Authorization

Authentication Provider: Clerk

// Middleware checks authentication on protected routes
export default clerkMiddleware((auth, req) => {
  const { userId, sessionClaims } = auth();

  // Public routes: /tools/*, /
  // Protected routes: /settings, /api/keys, /history

  if (isProtectedRoute(req.nextUrl.pathname) && !userId) {
    return redirectToSignIn({ returnBackUrl: req.url });
  }
});

Authorization: Plan-Based Entitlements

// useEntitlement.ts
const { plan, loading } = useEntitlement();

if (plan === "pro") {
  // Allow API access
  // Allow batch processing (5,000 lines)
  // Enable cloud history sync
} else {
  // Free tier: 100 lines, no API, no cloud sync
}

API Key Management

// /api/entitlement/route.ts - Generates API keys
// /api/entitlement/rotate/route.ts - Rotates API keys
// /api/keys/route.ts - Lists user's API keys (email only)

// Stored in Redis:
"api_key:sk_live_abc123": {
  userId: "user_xyz",
  plan: "pro",
  created: 1702500000,
  rateLimit: 5000 // req/day
}

API Architecture

Public API: /api/v1/*

Authentication: API key via x-api-key header Rate Limiting: Pro only - 5,000/day, enforced per key Response Format: JSON

Endpoints

POST /api/v1/run - Workflow Runner

{
  "input": "text to process",
  "steps": ["trim-lines", "upper-case", "dedupe"]
}
→ { "result": "PROCESSED TEXT" }

MCP Server (Agents):

  • MCP CLI (cleantextlab-mcp) calls /api/v1/run with steps: [toolSlug].
  • API access requires Pro subscription (5,000 calls/day).

POST /api/v1/sanitize - URL Sanitizer

{
  "urls": ["https://example.com?utm_source=..."],
  "removeParams": ["utm_source", "fbclid"]
}
→ { "sanitized": ["https://example.com"] }

POST /api/v1/sort-dedupe - Sort & Deduplicate

{
  "lines": ["apple", "banana", "apple"],
  "sort": true,
  "dedupe": true
}
→ { "result": ["apple", "banana"] }

Internal API: /api/*

POST /api/share - Generate share link GET /api/share/[id] - Retrieve shared content POST /api/history - Sync tool history (Pro) GET /api/history/all - Fetch all history (Pro) POST /api/settings - Sync user preferences (Pro) POST /api/checkout - Stripe checkout session POST /api/stripe/webhook - Stripe events


Tool Architecture Pattern

Tool Component Lifecycle

  1. Mount: Load from localStorage or URL params
  2. User Input: Track changes via useUndoRedo
  3. Processing: Run transformation (client-side)
  4. Output: Display result with copy/share actions
  5. History: Auto-save to localStorage (debounced)
  6. Cloud Sync (Pro): Encrypt and upload to Redis

Standard Hooks Used by Tools

// Undo/Redo with Cmd+Z
const { state, setState, undo, redo, canUndo, canRedo } = useUndoRedo("");

// Tool history tracking
const { history, clearHistory } = useToolHistory(toolSlug, getEntry);

// Share link generation
const { handleShare, shareStatus, lastShareUrl } = useShareableTool({
  toolSlug,
  sharePayload
});

// Keyboard shortcuts
useKeyboardShortcuts([
  { key: "z", ctrlOrCmd: true, action: undo },
  { key: "c", ctrlOrCmd: true, action: copyOutput }
]);

// Pro plan detection
const { plan, isPro } = useEntitlement();

// Smart paste integration
useMagicPayload((payload) => setInput(payload));

Feature Flags & Entitlements

Pro Features Gating

// Check plan status
const { plan, loading } = useEntitlement();

// Conditional rendering
{isPro ? (
  <BatchProcessor limit={5000} />
) : (
  <UpgradePrompt feature="Batch processing" />
)}

// Conditional limits
const MAX_LINES = isPro ? 5000 : 100;

Feature Flags (Future)

Currently hardcoded, but ready for remote config:

// lib/featureFlags.ts (future)
export const FEATURES = {
  CLOUD_SYNC: true,
  WORKFLOW_BUILDER: true,
  API_V2: false, // Not released yet
  AI_SUGGESTIONS: false
};

Performance & Optimization

Client-Side Optimizations

  1. Code Splitting: Each tool lazy-loaded via App Router
  2. Memoization: Heavy computations cached with useMemo
  3. Debouncing: History sync debounced (500ms)
  4. Web Workers (future): Large text processing in worker threads

Server-Side Optimizations

  1. Edge Functions: API routes run on Vercel Edge (low latency)
  2. Redis Caching: Share links cached for 30 days
  3. Rate Limiting: Prevents abuse (10 req/min public API)
  4. CDN: Static assets served via Vercel CDN

Bundle Size

  • Main Bundle: ~102KB (shared chunks)
  • Tool Bundle: ~140KB average per tool
  • Total Initial Load: ~250KB (gzipped)

Security Model

Data Privacy

  1. Client-Side Processing: Text never sent to server (except for sharing)
  2. E2E Encryption: Cloud sync uses AES-256-GCM
  3. No Tracking: Analytics don't capture user text
  4. HTTPS Only: All connections encrypted

API Security

  1. Rate Limiting: 10 req/min per IP, 5,000 req/day per API key
  2. API Key Rotation: Users can rotate keys via /api/entitlement/rotate
  3. Input Validation: All API inputs sanitized
  4. CORS: Restricted to allowed origins

Authentication Security

  1. Clerk Auth: Industry-standard OAuth 2.0
  2. Session Management: Secure HTTP-only cookies
  3. CSRF Protection: Built-in Next.js CSRF tokens

Deployment

Production Environment

Environment Variables

# Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=...
CLERK_SECRET_KEY=...

# Storage
UPSTASH_REDIS_REST_URL=...
UPSTASH_REDIS_REST_TOKEN=...

# Payments
STRIPE_SECRET_KEY=...
STRIPE_WEBHOOK_SECRET=...

# Analytics (consent-gated)
NEXT_PUBLIC_GA_ID=G-...

CI/CD Pipeline

┌─────────────┐
│  Git Push   │
│  to main    │
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│  GitHub Actions     │
│  - Run E2E tests    │ (optional)
│  - Type check       │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│  Vercel Build       │
│  - npm run build    │
│  - Generate static  │
│  - Deploy edge fns  │
└──────┬──────────────┘
       │
       ▼
┌─────────────────────┐
│  Production Live    │
│  cleantextlab.com   │
└─────────────────────┘

Decision Log

Why Next.js App Router?

  • File-based routing: Scales to 30+ tools easily
  • React Server Components: Faster initial loads
  • Edge runtime: Low-latency API routes
  • Built-in optimizations: Image optimization, font optimization

Why Client-Side Processing?

  • Privacy: User data never leaves browser
  • Cost: No server compute costs
  • Speed: Instant processing (no network round-trip)
  • Scalability: Infinite scale (client resources)

Why Upstash Redis?

  • Serverless: Pay-per-request pricing
  • Global: Low latency worldwide
  • Simple: HTTP-based API (no TCP connections)
  • Free Tier: 10,000 commands/day

Why Clerk for Auth?

  • Developer Experience: Easy integration
  • Pre-built UI: Sign-in/sign-up components
  • Session Management: Automatic token refresh
  • Pricing: Free tier includes 5,000 MAU

Why Stripe for Payments?

  • Industry Standard: Trusted payment processor
  • Developer-Friendly: Excellent API and docs
  • Subscription Management: Built-in recurring billing
  • Security: PCI compliant

Why TypeScript?

  • Type Safety: Catch bugs at compile time
  • Autocomplete: Better DX in VS Code
  • Refactoring: Safe large-scale changes
  • Documentation: Types are self-documenting

Maintenance Notes

Adding a New Tool

  1. Create tool component: src/components/tools/NewTool.tsx
  2. Create tool page: src/app/tools/new-tool/page.tsx
  3. Add metadata: src/lib/toolMetadata.ts
  4. Add to workflow engine: src/lib/workflowOperations.ts
  5. Update TOOL_REGISTRY.md
  6. Write E2E test: e2e/new-tool.spec.ts

Updating Architecture

When making architectural changes:

  1. Update this file (ARCHITECTURE.md)
  2. Update CHANGELOG.md with rationale
  3. Run full E2E test suite
  4. Document breaking changes

Code Review Checklist

  • Uses useUndoRedo for input state
  • Follows tool component pattern
  • Includes keyboard shortcuts
  • Tracks analytics events
  • Has E2E test coverage
  • Updates TOOL_REGISTRY.md
  • Client-side processing only

Future Roadmap

Phase 1: Q1 2025

  • Product Hunt launch
  • SEO optimization (sitemap submission)
  • JavaScript/Python SDKs

Phase 2: Q2 2025

  • AI-powered text suggestions
  • More automation platform integrations (n8n, Make, Zapier)
  • VS Code extension

Phase 3: Q3 2025

  • Team collaboration features
  • Custom tool builder
  • Mobile app (React Native)

Document Owner: Tyson K. Last Review: January 5, 2026 Next Review: March 1, 2026 Feedback: Issues at https://github.com/gravitasse/cleantextlab/issues

Architecture | CleanTextLab Docs