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

Dependencies

Service Dependencies - CleanTextLab

Last Updated: January 5, 2026 Purpose: Document all external dependencies and internal service relationships Audience: Developers, DevOps, and AI assistants


Table of Contents

  1. External Services
  2. NPM Package Dependencies
  3. Internal Service Graph
  4. API Dependency Chain
  5. Critical Path Analysis
  6. Fallback Strategies

External Services

1. Vercel (Hosting & Edge Functions)

Purpose: Application hosting, serverless functions, CDN Critical: ✅ Yes - App won't run without it Failover: None (Vercel or nothing)

Dependencies:

  • Production deployment: cleantextlab.com
  • Preview deployments: Auto-generated URLs
  • Edge Functions: /api/* routes
  • Analytics: Vercel Analytics (optional)

Configuration:

  • vercel.json - Build settings
  • Environment variables in Vercel dashboard

Cost: Free tier → Pro ($20/mo for team)


2. Clerk (Authentication)

Purpose: User authentication, OAuth, session management Critical: ⚠️ Partial - Tools work without auth, but Pro features require it

Features Used:

  • OAuth providers (Google, GitHub)
  • Session management
  • User metadata storage (Stripe plan)
  • Public metadata for entitlements

API Endpoints:

  • /api/auth/* - Clerk webhook handlers
  • Middleware: middleware.ts for protected routes

Configuration:

// src/middleware.ts
export default clerkMiddleware((auth, req) => {
  const isPublicRoute = publicRoutes.includes(req.nextUrl.pathname);
  if (!isPublicRoute) {
    auth().protect();
  }
});

Environment Variables:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

Cost: Free (10,000 MAU) → Pro ($25/mo)

Fallback: App continues to work for anonymous users, Pro features disabled


3. Upstash Redis (Cloud Storage)

Purpose: Cloud history sync, API key storage, rate limiting Critical: ⚠️ Partial - Only for Pro users with cloud sync

Features Used:

  • History sync (E2E encrypted)
  • API key + entitlement storage
  • Rate limit counters
  • Share links

Implementation (REST KV):

// src/lib/server/storage.ts
const endpoint = `${KV_REST_API_URL}/${cmd}/${encodedArgs}`;
await fetch(endpoint, { method: "POST", headers: { Authorization: `Bearer ${token}` } });

Key Prefixes (examples):

apikey:{key}           → ApiKeyRecord
ent:email:{email}      → EntitlementRecord
ent:key:{key}          → EntitlementRecord
hist:user:{userId}     → HistoryItem[]
share:{id}             → ShareRecord

Environment Variables:

UPSTASH_REDIS_REST_URL=https://...
UPSTASH_REDIS_REST_TOKEN=...
KV_REST_API_URL=https://... (alias)
KV_REST_API_TOKEN=... (alias)

Cost: Free (10k commands/day) → Pro ($10/mo)

Fallback: localStorage only (no cloud sync)


4. Stripe (Payments)

Purpose: Subscription billing, payment processing Critical: ⚠️ Partial - Only for Pro plan upgrades

Features Used:

  • Subscription management
  • Webhook handling
  • Customer portal
  • Usage-based billing (API calls)

Webhooks:

// src/app/api/stripe/webhook/route.ts
export async function POST(req: Request) {
  const event = await stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  );

  switch (event.type) {
    case "customer.subscription.created":
      // Update Clerk metadata
      await clerkClient.users.updateUserMetadata(userId, {
        publicMetadata: { stripePlan: "pro" }
      });
      break;
    // ... other events
  }
}

Environment Variables:

STRIPE_SECRET_KEY=sk_live_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...

Cost: 2.9% + $0.30 per transaction

Fallback: Users can't upgrade, existing Pro users unaffected


5. Google Analytics 4

Purpose: Usage analytics, conversion tracking Critical: ❌ No - Optional analytics

Implementation:

// src/components/AnalyticsScripts.tsx
// Injected only after explicit consent.
<AnalyticsScripts gaId={process.env.NEXT_PUBLIC_GA_ID} />

Events Tracked:

  • Tool usage
  • Keyboard shortcuts
  • API calls
  • Conversion events (sign-up, upgrade)

Environment Variables:

NEXT_PUBLIC_GA_ID=G-...

Cost: Free

Fallback: No analytics (graceful degradation, opt-in default)


6. Microsoft Clarity

Purpose: Session analytics and conversion funnel tagging Critical: ❌ No - Optional analytics

Configuration:

NEXT_PUBLIC_CLARITY_ID=...

Cost: Free

Fallback: No session analytics (consent-gated)


7. Resend (Email Delivery)

Purpose: Transactional emails for contact, bug reports, and email capture Critical: ❌ No - App logs to console without Resend configured

Environment Variables:

RESEND_API_KEY=...
FROM_EMAIL=...

Cost: Free tier + usage

Fallback: Email delivery skipped, request still succeeds


8. Sentry (Optional)

Purpose: Error monitoring in production Critical: ❌ No - Optional

Configuration: sentry.client.config.ts, sentry.server.config.ts

Fallback: Errors are only logged to console


9. Google AI / OpenAI (AI SDK)

Purpose: Smart input detection and AI-assisted flows Critical: ❌ No - System falls back to regex-based detection

Endpoints:

  • generativelanguage.googleapis.com (Google AI)

Fallback: Local heuristics and deterministic routing


NPM Package Dependencies

Production Dependencies

Core Framework

{
  "next": "^15.5.9",
  "react": "^19.0.0",
  "react-dom": "^19.0.0",
  "typescript": "^5"
}

Critical: ✅ Yes - App won't compile without these


UI & Styling

{
  "tailwindcss": "^4",
  "lucide-react": "^0.559.0",
  "cmdk": "^1.1.1"
}

Critical: ✅ Yes - UI won't render correctly

Icons Used:

  • Undo2, Redo2 (undo/redo)
  • Copy, Check (copy states)
  • Download, Share2, History (actions)
  • Settings, Moon, Sun (preferences)
  • Zap, Code, FileText, Lock (feature icons)

Authentication

{
  "@clerk/nextjs": "^6.36.2"
}

Critical: ⚠️ Partial - Required for Pro features


Storage & KV (REST)

{
  "none": "Uses Upstash REST API via fetch"
}

Critical: ⚠️ Partial - Required for cloud sync + API key storage


Payments

{
  "stripe": "^16.0.0"
}

Critical: ⚠️ Partial - Required for subscription management


Text & Data Processing

{
  "sql-formatter": "^15.6.11",
  "cronstrue": "^3.9.0",
  "cron-parser": "^5.4.0",
  "diff": "^8.0.2",
  "fast-xml-parser": "^5.3.3",
  "ipaddr.js": "^2.3.0",
  "ip-address": "^10.1.0",
  "papaparse": "^5.5.3",
  "xlsx": "^0.18.5",
  "uuid": "^13.0.0",
  "reading-time": "^1.5.0",
  "pako": "^2.1.0"
}

Critical: ⚠️ Per-tool - Each tool relies on specific library

Fallback: If library fails, show error message for that specific tool


Markdown & Docs

{
  "react-markdown": "^10.1.0",
  "remark-gfm": "^4.0.1",
  "rehype-highlight": "^7.0.2"
}

AI & MCP

{
  "ai": "^5.0.113",
  "@ai-sdk/google": "^2.0.46",
  "@ai-sdk/openai": "^2.0.86",
  "@ai-sdk/react": "^2.0.115",
  "@modelcontextprotocol/sdk": "^1.25.1",
  "js-tiktoken": "^1.0.21"
}

Observability

{
  "@sentry/nextjs": "^10.29.0"
}

Development Dependencies

{
  "@playwright/test": "^1.57.0",
  "playwright-chromium": "^1.57.0",
  "eslint": "^9",
  "@types/node": "^20.19.26",
  "@types/react": "^19",
  "@types/react-dom": "^19",
  "tsx": "^4.21.0"
}

Critical: ❌ No - Only for development


Internal Service Graph

┌─────────────────────────────────────────────────────────────────┐
│                        USER REQUEST                              │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             ▼
                ┌────────────────────────┐
                │  Next.js Middleware    │
                │  (Auth Check)          │
                └────────┬───────────────┘
                         │
            ┌────────────┴───────────┐
            │                        │
            ▼                        ▼
   ┌────────────────┐      ┌────────────────┐
   │  Public Routes │      │ Protected Routes│
   │  (Tools, Docs) │      │ (/dashboard)    │
   └────────┬───────┘      └────────┬────────┘
            │                       │
            │                       ▼
            │              ┌─────────────────┐
            │              │  Clerk Auth     │
            │              │  (Session)      │
            │              └────────┬────────┘
            │                       │
            │                       ▼
            │              ┌─────────────────┐
            │              │  Entitlements   │
            │              │  (isPro check)  │
            │              └────────┬────────┘
            │                       │
            └───────────┬───────────┘
                        │
                        ▼
            ┌───────────────────────┐
            │   Tool Component      │
            │   (Client-side)       │
            └───────┬───────────────┘
                    │
        ┌───────────┼───────────┐
        │           │           │
        ▼           ▼           ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│useUndo   │ │useHistory│ │useShare  │
│Redo      │ │          │ │          │
└────┬─────┘ └────┬─────┘ └────┬─────┘
     │            │            │
     │            ▼            │
     │   ┌────────────────┐   │
     │   │  localStorage  │   │
     │   │  (Free users)  │   │
     │   └────────────────┘   │
     │            │            │
     │            ▼            │
     │   ┌────────────────┐   │
     │   │  Upstash Redis │   │
     │   │  (Pro users)   │   │
     │   └────────────────┘   │
     │                        │
     └────────────┬───────────┘
                  │
                  ▼
         ┌────────────────┐
         │  GA4 Analytics │
         │  (Optional)    │
         └────────────────┘

API Dependency Chain

Public API (/api/v1/run)

User Request
    │
    ▼
API Route Handler
    │
    ├─→ Rate Limit Check (Upstash Redis)
    │   └─→ FAIL: Return 429 Too Many Requests
    │
    ├─→ API Key Check (if x-api-key header present)
    │   └─→ Upstash Redis lookup
    │       └─→ Invalid: Return 401 Unauthorized
    │
    ├─→ Input Validation
    │   └─→ Invalid: Return 400 Bad Request
    │
    ├─→ Workflow Engine
    │   │
    │   ├─→ Load default executors (in-memory map)
    │   │
    │   ├─→ Execute steps sequentially
    │   │   └─→ Each step processes output from previous step
    │   │
    │   └─→ Error Handling
    │       └─→ Return 500 on server error
    │
    └─→ Return JSON Response

Protected API (/api/user/history)

User Request
    │
    ▼
API Route Handler
    │
    ├─→ Clerk Auth Check
    │   └─→ No session: Return 401 Unauthorized
    │
    ├─→ Entitlements Check
    │   └─→ Not Pro: Return 403 Forbidden
    │
    ├─→ Upstash Redis Lookup
    │   └─→ GET user:{userId}:history:{toolSlug}
    │
    └─→ Return Encrypted History

Critical Path Analysis

Path 1: Anonymous User Using Tool (Client-Side Only)

User → Tool Component → Processing Logic → localStorage → Display Result

Dependencies:

  • None (fully client-side)

Single Point of Failure:

  • None (works offline)

Recovery Time:

  • Instant (no external services)

Path 2: Pro User with Cloud Sync

User → Clerk Auth → Tool Component → Processing → Upstash Redis → Display Result

Dependencies:

  1. Clerk (authentication)
  2. Upstash Redis (cloud storage)

Single Points of Failure:

  • Clerk outage → User can't authenticate → Fallback to localStorage
  • Upstash outage → Cloud sync fails → Fallback to localStorage

Recovery Time:

  • Graceful degradation: Immediate (local-only mode)
  • Full recovery: When service restores

Path 3: API Request (External Integration)

API Client → Vercel Edge → Rate Limit (Redis) → API Key Check (Redis) → Workflow Engine → Return JSON

Dependencies:

  1. Vercel (hosting)
  2. Upstash Redis (rate limiting + API keys)
  3. Tool processor libraries

Single Points of Failure:

  • Vercel outage → API completely down → No fallback
  • Upstash outage → Rate limiting disabled (security risk) → Emergency rate limit via edge config
  • Library failure → Specific tool fails → Return error for that step only

Recovery Time:

  • Vercel: 99.99% uptime SLA (< 1 min recovery)
  • Upstash: 99.9% uptime SLA (< 5 min recovery)
  • Library: Immediate (error returned)

Fallback Strategies

1. Authentication Failure (Clerk Down)

Impact: Pro users can't sign in

Fallback:

// Graceful degradation in middleware.ts
export default clerkMiddleware((auth, req) => {
  try {
    const isPublicRoute = publicRoutes.includes(req.nextUrl.pathname);
    if (!isPublicRoute) {
      auth().protect();
    }
  } catch (error) {
    // Clerk error - allow access to public routes only
    console.error("[Auth] Clerk unavailable:", error);

    if (!publicRoutes.includes(req.nextUrl.pathname)) {
      return NextResponse.redirect(new URL("/", req.url));
    }
  }
});

User Experience:

  • Can't access /dashboard or Pro features
  • All tools still work client-side
  • No data loss (localStorage continues working)

2. Cloud Storage Failure (Upstash Down)

Impact: Cloud sync and API key lookups fail

Fallback:

// src/lib/upstash.ts
export async function saveToCloud(userId: string, data: any) {
  try {
    await redis.set(`user:${userId}:history`, data);
  } catch (error) {
    console.error("[Upstash] Cloud save failed:", error);

    // Fallback to localStorage
    localStorage.setItem(`clt-cloud-queue-${userId}`, JSON.stringify(data));

    // Retry later
    setTimeout(() => retryCloudSave(userId), 60000); // Retry in 1 min
  }
}

User Experience:

  • History saves to localStorage only
  • API requests without API key still work (public rate limit)
  • Background retry when service restores

3. Payment Processing Failure (Stripe Down)

Impact: Users can't upgrade to Pro

Fallback:

// src/app/api/stripe/checkout/route.ts
export async function POST(req: Request) {
  try {
    const session = await stripe.checkout.sessions.create({...});
    return NextResponse.json({ url: session.url });
  } catch (error) {
    console.error("[Stripe] Checkout failed:", error);

    // Log to monitoring
    await logError("stripe-checkout-failed", error);

    // Show user-friendly error
    return NextResponse.json(
      { error: "Payment system temporarily unavailable. Please try again in a few minutes." },
      { status: 503 }
    );
  }
}

User Experience:

  • Upgrade button shows "Temporarily unavailable"
  • Existing Pro users unaffected
  • Manual upgrade option via email support

4. Library Failure (e.g., libphonenumber-js crash)

Impact: Specific tool (Phone Number Formatter) fails

Fallback:

// src/components/tools/PhoneNumberFormatterTool.tsx
const output = useMemo(() => {
  try {
    const parsed = parsePhoneNumber(input);
    return parsed.format("E.164");
  } catch (error) {
    console.error("[Phone Formatter] Library error:", error);

    // Basic fallback: regex-based formatting
    return input.replace(/[^\d+]/g, "");
  }
}, [input]);

User Experience:

  • Tool shows degraded results (basic formatting instead of full library)
  • Error message: "Advanced formatting unavailable. Using basic mode."
  • Other tools unaffected

Dependency Update Strategy

Critical Updates (Security Patches)

  1. Monitor GitHub Dependabot alerts
  2. Update immediately: npm update [package]
  3. Run tests: npm test && npm run build
  4. Deploy to preview: git push origin preview
  5. Test in preview environment
  6. Merge to main if passing

Non-Critical Updates

  1. Weekly check: npm outdated
  2. Batch updates: npm update --save
  3. Review changelog for breaking changes
  4. Update code if needed
  5. Full test suite: npm run test:e2e
  6. Deploy

Major Version Updates

  1. Read migration guide
  2. Create feature branch: git checkout -b upgrade/[package]-v[X]
  3. Update package: npm install [package]@latest
  4. Fix breaking changes
  5. Test thoroughly
  6. Code review
  7. Merge to main

Service Health Monitoring

External Services

ServiceStatus PageAlert Method
Vercelstatus.vercel.comEmail + Slack
Clerkstatus.clerk.comEmail
Upstashstatus.upstash.comEmail
Stripestatus.stripe.comEmail

Internal Monitoring

// src/app/api/health/route.ts
export async function GET() {
  const health = {
    status: "ok",
    timestamp: Date.now(),
    services: {
      clerk: await checkClerk(),
      upstash: await checkUpstash(),
      stripe: await checkStripe(),
    }
  };

  const allHealthy = Object.values(health.services).every(s => s.status === "ok");

  return NextResponse.json(
    health,
    { status: allHealthy ? 200 : 503 }
  );
}

async function checkUpstash() {
  try {
    await redis.ping();
    return { status: "ok" };
  } catch (error) {
    return { status: "error", message: error.message };
  }
}

Cost Breakdown

ServiceFree TierCurrent UsageProjected @ 10K UsersProjected @ 100K Users
Vercel100GB bandwidth~5GB/mo~50GB/mo ($20/mo)~500GB/mo ($100/mo)
Clerk10K MAU~100 MAU~10K MAU (free)~100K MAU ($250/mo)
Upstash10K commands/day~500/day~5K/day (free)~50K/day ($50/mo)
Stripe2.9% + $0.30$0~$100 MRR ($3/mo fees)~$1K MRR ($30/mo fees)
GA4UnlimitedFreeFreeFree

Total Monthly Cost:

  • Current: $0
  • @ 10K users: ~$23/mo
  • @ 100K users: ~$430/mo

Document Owner: Tyson K. Last Updated: January 5, 2026 Next Review: March 1, 2026

Dependencies | CleanTextLab Docs