☁️ Cloudflare Paid Plan Optimization

2nth Platform
Code & Infrastructure Review

Comprehensive review of 2nth-skills and 2nth-site β€” including a full skills.2nth.ai redesign spec with shared auth, membership gating, and activity tracking β€” plus prioritized Cloudflare optimizations for your paid account.

83
Architecture Score
3
Security Issues
11
CF Products to Unlock
8
Quick Wins Available
2
P1 Fixes Needed
πŸ—οΈ

Architecture Overview

How the two repos compose into the 2nth.ai platform
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Cloudflare Edge β”‚ β”‚ β”‚ β”‚ skills.2nth.ai 2nth.ai / app.2nth.ai β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ 2nth-skills β”‚ β”‚ 2nth-site β”‚ β”‚ β”‚ β”‚ Pages (Static)β”‚ β”‚ Pages + Functions (Full-stack)β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ /public/*.htmlβ”‚ β”‚ /public/ (Vanilla HTML/CSS/JS)β”‚ β”‚ β”‚ β”‚ SKILL.md filesβ”‚ ←───│ /functions/api/ (TypeScript) β”‚ β”‚ β”‚ β”‚ (AI context) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Bindings: β”‚ β”‚ β”‚ β”‚ β”œβ”€ D1 (SQLite DB) β”‚ β”‚ β”‚ npx skills add β”‚ β”œβ”€ KV (Sessions + Cache) β”‚ β”‚ β”‚ imbilawork/2nth-skills β”‚ β”œβ”€ Workers AI (Llama 3.3 70B) β”‚ β”‚ β”‚ ↓ β”‚ └─ (Missing: Queues, R2, DO) β”‚ β”‚ β”‚ Injected into AI agents β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ working on client builds β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ External services (should move to CF where possible): β”œβ”€ Resend (email) β”œβ”€ Paystack (payments β€” SA-specific, keep) └─ GitHub Actions (CI/CD β†’ migrate to CF Pages CI)
Strong

Clean Edge-Native Architecture

Both repos are fully Cloudflare-native from day one β€” D1, KV, Workers AI, Pages Functions. No Node.js server to manage.

Strong

Logical Separation

Skills catalog (public, installable AI context) cleanly separated from the client platform. Skills inform the agents that build for clients.

Gap

Missing Async Layer

AI inference runs synchronously in HTTP request handlers. Long-running multi-agent analyses will hit the 30s CPU limit and leave clients hanging.

Gap

No Real-time Connections

Agent conversations are request/response. No WebSockets, no streaming. Users submit a brief and wait β€” no live progress feedback.

πŸ“¦

2nth-skills

github.com/imbilawork/2nth-skills β€” AI agent skills catalog
Overall: Well-structured, production-ready skills format The SKILL.md format with YAML frontmatter is clean and correctly targets the Claude Code skills CLI. Good use of allowed-tools directives to constrain agent capabilities per-skill.

What's working well

SKILL.md format is correct and complete
YAML frontmatter (name, description, license, metadata, allowed-tools) aligns with the skills CLI spec. The allowed-tools pattern (e.g. Bash(curl:*) Bash(npx:*)) properly constrains tool permissions per-skill β€” a good security practice.
Auto-deploy pipeline is minimal and correct
GitHub Actions β†’ Wrangler Pages deploy on push to main. Zero build step, deploying pre-built public/. The simplest possible CI/CD β€” appropriate for a static skills catalog.
Skills cover the right domain coverage
Sage X3, ERPNext, Shopify β€” these are exactly the systems a client-facing AI build platform would need. The Cloudflare skills page is a smart addition showing platform dogfooding.

Issues & Improvements

Catalog HTML files are extremely large (44–50KB each)
Each skill explainer page appears to be a single-file bundle with embedded CSS and JS. Consider splitting shared CSS into a styles.css and using Cloudflare's edge cache + compression. With a paid plan, you get Brotli compression and can set custom cache rules to cache these aggressively (1 year TTL + cache-busting on deploy).
Performance CF: Cache Rules
CLOUDFLARE_ACCOUNT_ID hardcoded in GitHub Actions workflow
The account ID is in plaintext in .github/workflows/deploy.yml. While Cloudflare account IDs are technically semi-public, best practice is to move it to a GitHub repo secret alongside the API token to prevent leakage and enable multi-account management.
Security Low Risk
No versioning strategy for skill updates
Skills are installed via npx skills add imbilawork/2nth-skills@<skill-name>. As skills evolve, users will get the latest version implicitly. Consider adding semantic version tags to GitHub releases so users can pin to a version (@sagex3-ai@1.2.0) β€” especially important as clients rely on these for production builds.
Enhancement
Missing: 2nth-site skill for agents building on the platform itself
There's no 2nth-platform skill that would let AI agents understand the platform's own API, D1 schema, token model, and agent patterns. Adding this would enable agents to help clients build platform integrations and would be a strong dogfooding signal.
Enhancement
Opportunity: Vectorize for semantic skill search
As the skills library grows, a semantic search layer using Cloudflare Vectorize would let agents find relevant skills by description rather than exact name. Embed SKILL.md content at deploy time using Workers AI embeddings (@cf/baai/bge-base-en-v1.5) and query Vectorize from the catalog site.
CF: Vectorize CF: Workers AI
🌐

2nth-site

github.com/imbilawork/2nth-site β€” Full-stack client platform
Overall: Strong foundation, edge-specific gaps to address The architecture choices are excellent for Cloudflare Pages Functions. The gaps are around reliability patterns (no async queue, no retry logic), AI output parsing robustness, and several optimization opportunities unlocked by the paid plan.

Architecture Strengths

Excellent

Typed D1 Query Layer

All D1 queries live in src/db.ts with TypeScript wrappers. No raw SQL scattered in route handlers. db.batch() used for atomic token deduction β€” correct approach.

Excellent

Auth Middleware Pattern

Auth context injected once in _middleware.ts global, available via context.data everywhere. No repetitive session checks in route handlers.

Good

Web Crypto JWT (No Library)

Custom HMAC-SHA256 JWT using Web Crypto API β€” correct for the edge environment where Node crypto is not available. Avoids a bundled dependency.

Good

Built-in CRM Layer

CRM stages, lead scoring, activity logs, and nurture queues built into D1 β€” avoids an external CRM dependency and keeps client data on Cloudflare infrastructure.


Issues Requiring Attention

Synchronous AI inference will hit CPU limits under load
The POST /api/projects/:id/analyse endpoint runs 5 AI agent calls synchronously within a single HTTP request. Cloudflare Pages Functions have a 30-second CPU time limit. Multi-agent analysis chains will timeout on complex briefs. Fix: move AI inference to Cloudflare Queues β†’ Workers.
Critical CF: Queues CF: Workers
Fragile JSON parsing of AI output
In analyse.ts, AI response is parsed with /\{[\s\S]*\}/ regex then JSON.parse. If Llama 3.3 adds preamble text, wraps in markdown code fences, or returns partial output, this silently fails with a 500 error. Use structured outputs (Workers AI supports JSON schema enforcement) or a robust extraction function.
Reliability CF: Workers AI
Token estimation is too rough for non-ASCII content
text.length / 4 is a character-count heuristic that works for ASCII English but will undercount significantly for code-heavy briefs (symbols, brackets), non-Latin scripts, or structured data. Consider using a tiktoken-compatible WASM module or accepting a 1.5x safety margin by using text.length / 3 as an alternative.
Accuracy
Agent system prompts managed in SQL migrations
The 5 AI agent system prompts are seeded via INSERT OR IGNORE in 0001_initial.sql. Updating a system prompt requires a new migration. Consider storing system prompts in KV (versioned by key) or a prompt_versions table with an active flag β€” enabling A/B testing and rollbacks without schema changes.
Maintainability CF: KV
No retry logic on external API calls
Paystack payment verification (verify.ts) and Resend email sends have no retry logic. If Paystack returns a 500 on verification, the webhook will succeed but the token credit will fail silently. Add exponential backoff retry with Queues β€” queue the credit operation, retry on failure.
Reliability CF: Queues
Email templates inconsistent (dark vs light theme)
Partner application confirmation email uses a dark HTML template while all other transactional emails use a light theme. Standardize on one template system β€” the CRM nurture queue will compound this inconsistency as more emails are added.
Polish
Vanilla JS frontend will become a bottleneck
The frontend is pure HTML + vanilla JS files (auth-bar.js, app-switcher.js, browse-gate.js, tier-gate.js). This works now but becomes painful as the platform grows. Consider migrating to a lightweight framework (Solid.js, Preact) compiled to Pages β€” still fully static, but with component reuse.
Future
πŸ”—

Integration Quality

How the two repos work together as a product ecosystem
Integration Point Current State Quality Recommendation
Skills β†’ Platform Agents Implicit β€” agents in 2nth-site would use skills CLI to load context when building Informal Formalize: agents in 2nth-site should auto-suggest relevant SKILL.md installs based on project brief content
Deployment Consistency Both use Wrangler + Cloudflare Pages, same compatibility date format Good Consolidate to a monorepo or at minimum share a wrangler.toml conventions document
Domain routing skills.2nth.ai and 2nth.ai as separate Pages projects Good Use Cloudflare DNS + Pages custom domains β€” already the right pattern
Shared auth No cross-domain SSO between skills catalog and platform N/A If skills catalog gets authenticated features, use Cloudflare Access with the same JWT secret to share sessions
CI/CD pipeline GitHub Actions in 2nth-skills; npm scripts in 2nth-site Inconsistent Migrate both to Cloudflare Pages CI (built-in, no separate GitHub Actions token management)
CORS allowed origins skills.2nth.ai listed in 2nth-site's CORS config Good Already planned for β€” future skills catalog API calls to platform will work
🎨

skills.2nth.ai β€” Redesign Brief

Transform the static catalog into a membership-aware sub-platform of 2nth.ai
Design intent: skills.2nth.ai should feel like a seamless extension of 2nth.ai β€” same session, same user identity, same membership context. A user logged into 2nth.ai is automatically recognized on skills.2nth.ai. A visitor to skills.2nth.ai can see the catalog but must join/log in to install skills or access premium content.

Current vs Target State

Dimension Current State Target State
Auth None β€” fully public static site Reads 2nth_session cookie from 2nth.ai via cross-domain API call. Shows personalized header if authenticated.
Install button Public β€” anyone can see the install command Gated by plan tier. Explorer: all free skills. Starter+: premium skills. Copy command visible only to logged-in users.
Skill catalog Static HTML, no categorization Filterable by domain, tier, compatibility. Personalized: highlight skills relevant to user's active projects.
Tracking None Every skill view, install click, and search tracked to the user's activity_log in 2nth-site D1. Feeds CRM lead scoring.
Membership context None Shows current plan badge, token balance, and an upgrade CTA if the user is on Explorer browsing a Starter-tier skill.
Navigation Isolated from 2nth.ai Shared nav component with links back to Dashboard, Projects, Account. Sub-nav specific to Skills.

Proposed Page Structure

skills.2nth.ai/ β”œβ”€β”€ index Browse all skills (public catalog, auth-aware header) β”‚ β”œβ”€β”€ Filter bar: Domain (ERP / Ecommerce / CRM / Cloudflare / Platform) β”‚ β”œβ”€β”€ Filter: Tier (Free / Starter / Designer / Builder) β”‚ └── Search box β†’ Vectorize semantic search (future) β”‚ β”œβ”€β”€ /[skill-slug] Skill detail page β”‚ β”œβ”€β”€ Overview, references, changelog β”‚ β”œβ”€β”€ Install panel (gated by membership tier) β”‚ β”‚ β”œβ”€β”€ [Logged out]: "Log in to 2nth.ai to install" β”‚ β”‚ β”œβ”€β”€ [Explorer]: Shows free skills. Premium shows upgrade CTA. β”‚ β”‚ └── [Starter+]: Full install command + one-click copy β”‚ └── Related skills (same domain) β”‚ └── /my-skills Authenticated: installed skills history + recommendations β”œβ”€β”€ Skills installed via CLI (tracked via activity_log) └── Recommended based on active project briefs
πŸ”‘

Shared Auth Architecture

How skills.2nth.ai reads the 2nth.ai session without a separate login
The challenge: The 2nth_session cookie is set with SameSite=Lax; Domain=2nth.ai. Subdomains like skills.2nth.ai can receive this cookie on navigation but not via cross-origin fetch() calls from JS. The solution is a lightweight session check API on the parent domain that skills.2nth.ai calls client-side.

Option A β€” Cross-origin Session API (Recommended)

The simplest approach. skills.2nth.ai JS calls https://2nth.ai/api/auth/session with credentials: 'include'. The browser sends the cookie automatically. The endpoint already exists.

// In skills.2nth.ai β€” client-side auth check (runs on every page load)
async function getSession() {
  try {
    const res = await fetch('https://2nth.ai/api/auth/session', {
      credentials: 'include'  // sends 2nth_session cookie cross-origin
    });
    if (!res.ok) return null;
    return await res.json(); // { user: { id, email, plan, token_balance } }
  } catch { return null; }
}

// Then show/hide install buttons based on session.user.plan
const session = await getSession();
renderSkillPage(skill, session?.user ?? null);
Required CORS change in 2nth-site: Currently skills.2nth.ai is in the CORS allowedOrigins list β€” but verify that GET /api/auth/session specifically returns Access-Control-Allow-Credentials: true and that the origin is in the allowed list. Without credentials: true in the CORS response, the cookie won't be sent cross-origin.
// src/middleware/cors.ts β€” ensure credentials are allowed for skills subdomain
const ALLOWED = ['https://2nth.ai', 'https://skills.2nth.ai', 'https://2nth-site.pages.dev'];

export function cors(req: Request, res: Response): Response {
  const origin = req.headers.get('Origin') ?? '';
  if (ALLOWED.includes(origin)) {
    res.headers.set('Access-Control-Allow-Origin', origin); // not '*' β€” must be specific
    res.headers.set('Access-Control-Allow-Credentials', 'true'); // ← critical
    res.headers.set('Vary', 'Origin');
  }
  return res;
}

Option B β€” Cloudflare Access JWT sharing (No code change)

With Cloudflare Access, configure both 2nth.ai and skills.2nth.ai under the same Access application with a wildcard policy (*.2nth.ai). Access injects a Cf-Access-Jwt-Assertion header to all requests β€” your Pages Function can read it without needing the session cookie at all. This is the most elegant approach for a multi-subdomain platform.

// functions/_middleware.ts (skills site) β€” read CF Access JWT
const cfJwt = req.headers.get('Cf-Access-Jwt-Assertion');
if (cfJwt) {
  // Verify with the CF Access JWKS (no secret sharing required)
  const user = await verifyCfAccessToken(cfJwt, TEAM_DOMAIN);
  // Fetch full user profile from 2nth.ai API using the verified email
  const profile = await fetch(`https://2nth.ai/api/auth/profile?email=${user.email}`, {
    headers: { 'Authorization': `Bearer ${SERVICE_TOKEN}` }
  });
}

Shared Login Flow (User Journey)

[skills.2nth.ai β€” not logged in] ↓ User clicks "Install Skill" ↓ skills.2nth.ai detects no session β†’ shows modal: "Log in or join 2nth.ai to install skills" [Log in] β†’ redirect to https://2nth.ai/join.html?return=skills.2nth.ai/[slug] ↓ User completes magic link auth on 2nth.ai 2nth_session cookie set for domain=.2nth.ai (note leading dot for subdomain sharing) ↓ Redirect back to skills.2nth.ai/[slug]?install=true ↓ skills.2nth.ai reads session β†’ plan check β†’ shows install command
Cookie domain change required: Currently the Set-Cookie in auth/verify.ts sets Domain=2nth.ai. Change this to Domain=.2nth.ai (leading dot) so the cookie is valid on all subdomains including skills.2nth.ai, app.2nth.ai, demo.2nth.ai.

This is a one-line change in functions/api/auth/verify.ts: Domain=.2nth.ai
🎫

Membership Gating

Which skills are accessible at which plan tier β€” and how to enforce it

Proposed Tier Structure for Skills

Plan Skills Access Install Limit Features
Explorer (Free) Free-tier skills only
Shopify basics, ERPNext intro, Cloudflare intro
3 skills max active Browse catalog, view skill docs, limited installs
Starter All free + Starter skills
Full Shopify, ERPNext, Sage X3 basics
10 skills max active + Skill history, usage analytics, email notifications on updates
Designer All skills + Designer exclusives
Advanced Sage X3, full Cloudflare, 2nth-platform
Unlimited + Custom skill requests, early access, semantic search
Builder All skills + private client skills
Custom-built skills for Builder clients
Unlimited + private repo + Private skills, custom SKILL.md authoring, SLA

SKILL.md Metadata β€” Add Tier Field

Add a tier field to the YAML frontmatter so the catalog can enforce gating server-side:

---
name: sagex3-ai
description: Sage X3 ERP GraphQL integration patterns for AI coding agents
license: MIT
tier: starter          # free | starter | designer | builder
metadata:
  domain: erp
  systems: [sage-x3]
  category: enterprise-erp
allowed-tools: Bash(curl:*) Bash(npx:*)
---

Gating Logic β€” Client-Side + Edge Enforcement

// skills.2nth.ai β€” tier check before showing install command
const TIER_ORDER = ['explorer', 'starter', 'designer', 'builder'];

function canInstall(userPlan: string, skillTier: string): boolean {
  return TIER_ORDER.indexOf(userPlan) >= TIER_ORDER.indexOf(skillTier);
}

function renderInstallPanel(skill, user) {
  if (!user) {
    return `<div class="install-gate">
      <a href="https://2nth.ai/join.html?return=${location.href}">Log in to install</a>
    </div>`;
  }
  if (!canInstall(user.plan, skill.tier)) {
    return `<div class="install-gate upgrade">
      <p>This skill requires <strong>${skill.tier}</strong> plan</p>
      <a href="https://2nth.ai/bill.html">Upgrade β€” from R1,850/mo</a>
    </div>`;
  }
  return `<div class="install-command">
    <code>npx skills add imbilawork/2nth-skills@${skill.name}</code>
    <button onclick="copyInstall('${skill.name}')">Copy</button>
  </div>`;
}
Edge enforcement via Cloudflare Pages Functions: The client-side gating above is for UX. For API endpoints that serve skill content (if you add a skills API), enforce tier checks in the Pages Function using the same session validation as 2nth-site. The SKILL.md files themselves should not be gated (they're in a public GitHub repo) β€” the gate is on the install UX and premium documentation content.
πŸ“‘

Activity Tracking

Track skill interactions back to the 2nth-site activity_log and CRM lead scoring

Events to Track

Event Trigger CRM Impact Data to Log
skill_viewed User opens a skill detail page +1 lead score if not yet a client skill_name, referrer, plan
skill_install_copied User clicks "Copy" on install command +5 lead score, move to demo_user stage skill_name, plan, timestamp
skill_upgrade_prompted User hits a gated skill above their tier Flag for nurture email skill_name, user_plan, required_tier
skill_search User searches the catalog Enrich lead_interests query, results_count, clicked
skill_catalog_visited First visit to skills.2nth.ai If no account: create anon_identity referrer, utm_source, utm_campaign

Tracking Implementation β€” POST to 2nth.ai API

Add a lightweight tracking endpoint to 2nth-site, then fire it from skills.2nth.ai with fetch(..., {credentials: 'include', keepalive: true}):

// 2nth-site: functions/api/track/skill.ts (new endpoint)
export async function onRequestPost({ request, env, data }) {
  const { event, skill_name, metadata } = await request.json();
  const user = data.user; // injected by global _middleware.ts

  if (user) {
    // Log to activity_log table (already exists in D1)
    await env.DB.prepare(
      `INSERT INTO activity_log (user_id, action, metadata, created_at)
       VALUES (?, ?, ?, ?)`
    ).bind(user.id, event, JSON.stringify({ skill_name, ...metadata }), Date.now()).run();

    // Update lead score for high-intent events
    if (event === 'skill_install_copied') {
      await env.DB.prepare(
        `UPDATE users SET lead_score = lead_score + 5 WHERE id = ?`
      ).bind(user.id).run();
    }
  } else {
    // Anonymous: log to anon_identities fingerprint
    const fp = getFingerprint(request);
    await env.DB.prepare(
      `INSERT OR IGNORE INTO anon_identities (fingerprint, first_seen, metadata)
       VALUES (?, ?, ?)`
    ).bind(fp, Date.now(), JSON.stringify({ referrer: request.headers.get('Referer') })).run();
  }

  return new Response(null, { status: 204 });
}

// skills.2nth.ai β€” fire tracking events
function track(event, data = {}) {
  fetch('https://2nth.ai/api/track/skill', {
    method: 'POST',
    credentials: 'include',
    keepalive: true,      // survives page unload
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ event, ...data })
  }).catch(() => {}); // fire-and-forget, never block UI
}

Cloudflare Web Analytics β€” Custom Events

Layer Cloudflare Web Analytics custom events on top of the API tracking for funnel visibility:

// Fire both: API tracking (to D1) + CF Analytics (to CF dashboard)
function track(event, data = {}) {
  // 1. Server-side: logs to D1 activity_log + CRM
  fetch('https://2nth.ai/api/track/skill', { method: 'POST', credentials: 'include',
    keepalive: true, body: JSON.stringify({ event, ...data }) }).catch(() => {});

  // 2. Client-side: CF Web Analytics custom event (appears in CF dashboard)
  if (typeof window.cfAnalytics !== 'undefined') {
    window.cfAnalytics.pushEvent({ type: event, ...data });
  }
}

Personalization Based on Tracking

Personalization

Relevant Skills by Project

When a user has active projects in D1, the skills catalog highlights skills matching their project domains. "You're building an ERPNext integration β€” here are relevant skills."

Nurture

Upgrade Prompt Emails

When skill_upgrade_prompted is logged, add user to nurture_queue with context. Resend email: "You browsed [skill] β€” it's included in Starter at R1,850/mo."

CRM

Skill Interest in Lead Profile

Store the domains of viewed/installed skills in lead_interests column. Sales context: "This lead has installed Shopify and ERPNext skills β€” likely a commerce + ERP integration project."

πŸ”’

Security Findings

Issues ranked by severity β€” address P1s before any paid feature work
P1
Webhook endpoint lacks signature verification
The Paystack webhook at POST /api/billing/webhook should verify the x-paystack-signature HMAC header before processing. Without this, any actor can POST fake payment events and fraudulently credit tokens. Paystack signs all webhooks with your secret key β€” validate it with crypto.subtle.verify().
Critical Revenue Impact
Low Effort
P1
Admin endpoints rely solely on a flag in the user table
The /api/admin/* routes check user.is_admin from D1. If there's an SQL injection or a privilege escalation bug anywhere in the auth layer, attackers gain full admin access. Back this up with a secondary check β€” a fixed admin email allowlist checked server-side, or Cloudflare Access protecting /api/admin/* with a service token.
High CF: Access
Low Effort
P2
PAYSTACK_PUBLIC_KEY committed to wrangler.toml in the repo
The live pk_live_ Paystack publishable key is committed in wrangler.toml. While publishable keys are designed to be client-facing and cannot authorize charges, exposing the live key links your payment processor identity to the repo. Move it to a wrangler var (non-secret env var set via dashboard) or accept it as a build-time env var in CI.
Medium
Low Effort
Security positives worth noting: Magic link tokens use KV TTL (15 min), rate limiting prevents abuse (1/min per email), cookies are HttpOnly; Secure; SameSite=Lax, JWT uses HMAC-SHA256 with Web Crypto (no library vulnerabilities), anonymous user fingerprinting is IP+UA (no persistent tracking).
⚑

Quick Wins β€” Days

High-impact improvements that take less than a day to implement with your paid plan
1
Enable Cloudflare Cache Rules for skills.2nth.ai static assets
The 44–50KB skill catalog HTML pages are served fresh on every request. Add a Cache Rule: hostname eq "skills.2nth.ai" β†’ Edge TTL: 1 year, Browser TTL: 1 day. Bust the cache on each Pages deploy via a deploy hook. With paid plan, you get Cache Rules instead of Page Rules (more powerful, per-path control).
Cache Rules Performance
// wrangler.toml β€” Cache Rules are set in CF Dashboard > Caching > Cache Rules
// Or via Terraform / CF API for the paid plan
2 hours
2
Fix Paystack webhook signature verification
Add HMAC-SHA512 signature verification to functions/api/billing/webhook.ts before any processing logic.
Security
// functions/api/billing/webhook.ts
const hash = await crypto.subtle.importKey(
  'raw',
  new TextEncoder().encode(env.PAYSTACK_SECRET_KEY),
  { name: 'HMAC', hash: 'SHA-512' },
  false, ['sign']
);
const sig = await crypto.subtle.sign('HMAC', hash, new TextEncoder().encode(body));
const expected = Array.from(new Uint8Array(sig))
  .map(b => b.toString(16).padStart(2, '0')).join('');
if (expected !== req.headers.get('x-paystack-signature')) {
  return new Response('Unauthorized', { status: 401 });
}
1 hour
3
Protect /api/admin/* with Cloudflare Access Service Token
Add a Cloudflare Access policy to the path /api/admin/* on 2nth.ai requiring a service token. Admin API calls from your dashboard/tools include the CF-Access-Client-Id header. External callers are rejected at the edge before hitting your Functions β€” zero code change needed.
Security CF: Access
30 min
4
Enable Cloudflare Web Analytics on both domains
Add the one-line Cloudflare Web Analytics beacon to all HTML pages. With a paid plan, you get unlimited data retention, custom events, and privacy-friendly analytics without cookies. Add data-cf-beacon='{token: "..."}' to each page β€” or inject via Cloudflare's automatic injection in Pages settings.
Web Analytics Visibility
<script defer src='https://static.cloudflareinsights.com/beacon.min.js'
  data-cf-beacon='{"token": "YOUR_TOKEN"}'></script>
30 min
5
Migrate GitHub Actions CI/CD to Cloudflare Pages CI
2nth-skills uses GitHub Actions with a Cloudflare API token. With paid Pages, connect the repo directly in the Cloudflare Dashboard (Settings β†’ Git integration). This removes the GitHub Actions token management, provides preview deployments per PR, and gives you a CF-native deployment history.
Pages CI DevEx
1 hour
6
Enable Cloudflare WAF Managed Rules
The paid plan includes the Cloudflare Managed Ruleset (OWASP Core, Cloudflare Managed). Enable it for 2nth.ai with a default action of Block. Your API routes will get automatic protection against SQL injection, XSS, and common attack patterns β€” zero code change, edge-level protection.
WAF Security
1 hour
7
Add D1 Time-Travel read replicas for read-heavy routes
Routes like GET /api/agents, GET /api/billing/plans, and GET /api/tokens/balance are read-heavy. D1's time-travel allows reads from the closest replica globally. Set experimental_d1_localMode: true in wrangler for local dev, and ensure read queries use db.prepare().all() (already done) β€” Cloudflare handles replica routing automatically on paid D1.
D1 Performance
Config only
8
Replace KV-based rate limiting with Cloudflare Rate Limiting rules
Your current rate limiting (src/middleware/rate-limit.ts) uses KV reads/writes on every request. The paid plan's Rate Limiting rules run at the edge before your Worker executes β€” zero KV cost, sub-millisecond evaluation. Set up a rule: path: /api/auth/send-magic-link, limit: 1 per 60s per IP.
Rate Limiting Cost
1 hour
πŸ“ˆ

Medium Term β€” Weeks

Architectural improvements unlocked by the paid Cloudflare stack
A
Cloudflare Queues β€” Async AI inference pipeline
Move the multi-agent analysis out of the HTTP request/response cycle. The POST /api/projects/:id/analyse handler enqueues a job; a separate Consumer Worker runs the 5-agent chain, updates D1, and sends the email. Clients get an immediate 202 response and poll for status (or use a webhook/email notification, which already exists).
CF: Queues Reliability
// wrangler.toml additions
[[queues.producers]]
queue = "brief-analysis"
binding = "ANALYSIS_QUEUE"

[[queues.consumers]]
queue = "brief-analysis"
max_batch_size = 1
max_retries = 3
dead_letter_queue = "brief-analysis-dlq"

// functions/api/projects/[id]/analyse.ts
await env.ANALYSIS_QUEUE.send({ projectId: id, userId: user.id });
return new Response(JSON.stringify({ status: 'queued' }), { status: 202 });
1–2 days
B
Cloudflare Durable Objects β€” Real-time agent streaming
Replace the request/response agent chat pattern with WebSocket connections managed by Durable Objects. Each project gets a DO instance that maintains conversation state and streams AI tokens to the client in real time. Workers AI supports streaming (stream: true) β€” pipe the ReadableStream through the DO to the client's WebSocket.
CF: Durable Objects UX
// Agent DO: maintains WebSocket connection, streams Workers AI output
export class AgentSession {
  async fetch(req: Request) {
    const { 0: client, 1: server } = new WebSocketPair();
    this.ctx.acceptWebSocket(server);
    const stream = await this.env.AI.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
      messages, stream: true
    });
    // Pipe stream chunks to WebSocket
    return new Response(null, { status: 101, webSocket: client });
  }
}
3–5 days
C
Workers AI β€” Use structured outputs to fix JSON parsing
Workers AI supports a response_format parameter with JSON schema enforcement. Replace the regex extraction in analyse.ts with a typed schema that guarantees structured output β€” no regex, no 500 errors on malformed LLM output.
CF: Workers AI Reliability
const result = await env.AI.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
  messages,
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'brief_analysis',
      schema: {
        type: 'object',
        properties: {
          design: { type: 'object' },
          software: { type: 'object' },
          hardware: { type: 'object' },
          robotics: { type: 'object' },
          total_tokens: { type: 'number' }
        },
        required: ['design', 'software', 'hardware', 'robotics', 'total_tokens']
      }
    }
  }
});
Half a day
D
R2 β€” Store project briefs and AI-generated concepts
Currently, project brief content is stored in D1 (SQLite text columns). As clients submit rich briefs with attachments (images, PDFs, specs), D1 is the wrong store. Add Cloudflare R2 for binary assets: store brief attachments and generated concept documents as R2 objects, store only the R2 key in D1.
CF: R2 Scalability
// wrangler.toml
[[r2_buckets]]
binding = "BRIEFS"
bucket_name = "2nth-briefs"

// Store brief attachment
const key = `briefs/${projectId}/${filename}`;
await env.BRIEFS.put(key, file.stream(), {
  httpMetadata: { contentType: file.type }
});
await db.updateProjectBriefAttachment(projectId, key);
1–2 days
E
KV β€” Version-controlled agent system prompts
Move agent system prompts from D1 seed migrations to KV with version keys. Enables prompt A/B testing, instant rollback, and per-environment prompt management without DB migrations.
CF: KV Maintainability
// Store: PROMPTS:brief-analyst:v3 = "You are..."
// Active version pointer: PROMPTS:brief-analyst:current = "v3"
const version = await env.PROMPTS.get(`PROMPTS:${agentSlug}:current`);
const prompt = await env.PROMPTS.get(`PROMPTS:${agentSlug}:${version}`);
Half a day
πŸš€

Strategic β€” Months

Platform-level capabilities that unlock new product features
Strategic

Vectorize β€” Semantic Skills Search

As 2nth-skills grows to 20+ skills, embed SKILL.md content at deploy time using @cf/baai/bge-base-en-v1.5 (free via Workers AI). Clients can search "I need ERP integration for furniture manufacturing" and get the right skill β€” not just by name.

Strategic

Cloudflare Images β€” Client Brief Assets

When clients attach brand guidelines, design assets, or reference images to briefs, Cloudflare Images provides transformation, optimization, and delivery. Use the Images API for responsive variants β€” /cdn-cgi/image/width=800,format=webp/r2/key.

Strategic

Workers AI Fine-Tuning (LoRA)

As 2nth accumulates successful project scopes and client briefs, fine-tune the brief-analyst agent using LoRA adapters on Workers AI. Your platform's successful project outcomes become training data for a domain-specific model β€” a compounding competitive advantage.

Strategic

Hyperdrive β€” External Database Connections

When clients have their own Postgres databases (e.g., ERPNext self-hosted), Cloudflare Hyperdrive allows Workers to connect to external Postgres with connection pooling at the edge. Relevant for the ERPNext and Sage X3 skills β€” agents can query client DBs directly during build.

Strategic

Cloudflare for Teams (Zero Trust)

As 2nth grows a team, Cloudflare Zero Trust (Access + Gateway) replaces VPN for internal tool access. The admin panel at /admin.html becomes an Access-protected app requiring team SSO β€” not just an is_admin flag in D1.

Strategic

Cloudflare Browser Rendering

For generating PDF concept documents from the AI's structured output, Browser Rendering (a paid feature) allows Workers to render HTML to PDF at the edge β€” no external service like Puppeteer Cloud. Generate client-ready concept PDFs and store in R2.

🟠

Product Recommendations

Full map of Cloudflare products relevant to the 2nth platform on a paid plan
πŸ“¬
Cloudflare Queues
Paid

Async message queue for Workers. Solves the synchronous AI inference timeout problem.

  • Decouple brief analysis from HTTP request lifecycle
  • Retry failed AI calls with exponential backoff (up to 3 retries)
  • Dead-letter queue for failed analysis jobs
  • Queue Paystack credit operations for reliability
  • $0.40 per million messages β€” near-free at current scale
πŸ“¦
Cloudflare R2
Paid

S3-compatible object storage with zero egress fees. Natural home for project assets.

  • Store client brief attachments (images, PDFs, specs)
  • Store AI-generated concept documents and outputs
  • Store compiled Tailwind CSS (cache-bust on each deploy)
  • $0.015/GB/month storage, $0 egress β€” S3 egress costs $0.09/GB
  • Public buckets via r2.dev subdomain or custom domain
πŸ”΄
Durable Objects
Paid

Stateful, globally unique Worker instances. Enable real-time agent interactions.

  • WebSocket connections for streaming AI output to clients
  • Maintain conversation history in-memory during a session
  • Per-project state (one DO per project = isolated conversation)
  • Coordinate multi-agent pipelines without polling
  • $0.15 per million requests, $12.50 per million GB-seconds
πŸ”
Cloudflare Access
Zero Trust

Identity-aware proxy. Protect admin routes and internal tools without code changes.

  • Protect /api/admin/* with a service token policy
  • Protect /admin.html with team SSO (Google/GitHub)
  • Free for up to 50 users on Zero Trust free tier
  • Audit logs for all admin access β€” compliance-ready
  • Replace the KV-based rate limiter with Access policies
🧠
Workers AI
Included

Already in use β€” but unlocking additional capabilities with paid plan.

  • Structured outputs (JSON schema) β€” replace regex parsing
  • Streaming responses β€” pipe to Durable Object WebSockets
  • Embedding models for Vectorize indexing (bge-base-en-v1.5)
  • LoRA fine-tuning for custom brief-analyst specialization
  • Consider @cf/google/gemma-7b-it for lighter tasks (faster)
πŸ—„οΈ
D1 (Paid Tier)
Paid

Already in use β€” paid tier removes row limits and adds global read replicas.

  • 10B row reads/month (vs 5M free tier)
  • 100M row writes/month (vs 100K free tier)
  • Time Travel: point-in-time recovery up to 30 days
  • Global read replicas β€” queries routed to closest replica
  • Add index on users.email, projects.user_id, token_transactions.user_id
πŸ”
Vectorize
Paid

Vector database for semantic search. Pair with Workers AI embeddings.

  • Index SKILL.md content for semantic skill discovery
  • Enable "find a skill for my use case" in the catalog
  • Index project briefs for similarity matching (suggest past wins)
  • $0.04 per million vectors queried β€” minimal cost
  • Deploy embed pipeline as a Queue consumer on skill deploys
πŸ›‘οΈ
WAF & Rate Limiting
Paid

Edge-level security rules that run before your Workers. Zero latency impact.

  • Cloudflare Managed Ruleset (OWASP + CF rules) β€” enable immediately
  • Rate limit: 1 magic link per IP per 60s (replaces KV middleware)
  • Rate limit: 10 agent messages per user per minute
  • Bot Fight Mode β€” prevent credential stuffing on auth routes
  • Custom rules: block non-SA IPs from payment routes (reduce fraud)
πŸ“Š
Web Analytics
Included

Privacy-first analytics. No cookies, GDPR-safe, real-time data.

  • Add beacon to all pages (automatic via Pages settings)
  • Track funnel: visit β†’ magic link β†’ verify β†’ first brief β†’ checkout
  • Custom events: track skill installs and agent interactions
  • Web Vitals (LCP, CLS, FID) for both sites
  • Zero cost, no sampling on paid plan
πŸ“§
Email Routing
Free

Route inbound email to Workers or external addresses. Available on paid plan.

  • Route partners@2nth.ai to a Worker that auto-creates a D1 record
  • Route support@2nth.ai to your team inbox
  • Reduce reliance on Resend for all outbound β€” use Email Routing for inbound
  • Workers Email Sending (beta) β€” send transactional email from Workers
πŸ–ΌοΈ
Cloudflare Images
Paid

Transform, optimize, and serve images at the edge. Pairs with R2.

  • Serve client brand assets with automatic WebP/AVIF conversion
  • Resize brief attachments to appropriate dimensions on-the-fly
  • Polish: auto-optimize images on both sites (2nth.ai + skills.2nth.ai)
  • $5/month for first 100K images, $1 per 1K thereafter
🌐
Hyperdrive
Paid

Connect Workers to external Postgres databases with connection pooling.

  • ERPNext and Sage X3 skills could query client external DBs directly
  • Relevant when building integrations that need live ERP data access
  • Workers establish fast, pooled connections to Postgres from the edge
  • Reduces need for client-managed API middleware
πŸ—ΊοΈ

Implementation Roadmap

Prioritized execution order for the paid plan migration
Week Task Effort Impact Cloudflare Product
Week 1 Fix Paystack webhook HMAC verification 1 hr Revenue Security β€”
Enable WAF Managed Rules on 2nth.ai 1 hr Security WAF
Add Cloudflare Access policy on /api/admin/* 30 min Security Access
Enable Web Analytics on both domains 30 min Visibility Web Analytics
Week 2 Migrate GitHub Actions CI to Cloudflare Pages CI 1 hr DevEx Pages CI
Replace KV rate limiter with CF Rate Limiting rules 1 hr Cost + Perf Rate Limiting
Add Cache Rules for skills.2nth.ai 2 hrs Performance Cache Rules
Fix Workers AI structured output (replace regex parsing) half day Reliability Workers AI
Week 3 Implement Cloudflare Queues for async AI analysis 1–2 days Reliability Queues
Move agent prompts from D1 seeds to KV with versioning half day Maintainability KV
Week 4 Provision R2 bucket for project assets 1–2 days Scalability R2
Add D1 indexes on hot query paths 2 hrs Performance D1
Month 2 Durable Objects for agent WebSocket streaming 3–5 days Product UX Durable Objects
Vectorize + Workers AI embeddings for skill search 2 days Product Vectorize
Month 3+ Workers AI LoRA fine-tuning on accumulated brief data 1 week Competitive Workers AI (LoRA)
Browser Rendering for PDF concept generation 2 days Product Browser Rendering
Cost estimate for Cloudflare paid features (at current 2nth scale): Workers: included in paid plan Β· D1: $5/month Β· KV: included Β· Queues: ~$0.40/million messages Β· R2: $0.015/GB/month Β· Durable Objects: ~$15/month Β· Vectorize: ~$0.04/million Β· WAF: included Β· Total additional: est. $25–50/month for full stack.

Critical path summary

Do this first: Paystack webhook HMAC verification
This is a live revenue vulnerability. A malicious actor can POST fake payment confirmations to /api/billing/webhook and receive token credits without paying. Fix before any other work.
Do this second: Cloudflare Queues for analysis pipeline
This is the most impactful architectural change β€” it unblocks the entire async AI inference problem, improves reliability, and enables future multi-step agent pipelines. Everything else builds on top of this.
Then: Durable Objects for real-time streaming
Once async inference is working via Queues, layer on WebSocket streaming via Durable Objects. This transforms the UX from "submit and wait for email" to "watch agents think in real time" β€” a core product differentiator for a premium AI build platform.