WeylandAI Infrastructure — Configuration Reference

Cloudflare Workers + Pages + D1 + R2 Deployment Config
2026-02-14

Contents

  1. wrangler.toml — Worker & Pages deployment configuration
  2. _worker.js — Cloudflare Pages advanced-mode router
  3. _headers — Security headers & cache control
  4. _redirects — SPA routing & domain redirects

wrangler.toml

Phase2/Prototype1/wrangler.toml · Worker + Pages + D1 + R2 + KV + Queue + Browser binding
# ============================================================================
# SubX Worker & Pages Configuration — Production Deployment Reference
# ============================================================================
#
# VERSIONING SCHEMA:
#   Production: weyland (api.weylandai.com)
#   Staging:    weyland-yutani-staging (workers.dev only)
#   Dev:        weyland-yutani-dev (workers.dev only)
#
# ============================================================================
# DEPLOYMENT COMMANDS — READ CAREFULLY, FLAGS ARE MANDATORY
# ============================================================================
#
# +-----------------------------------------------------------------------+
# | WORKER (API backend — weyland-worker.js)                              |
# |                                                                       |
# |   Production:  npx wrangler deploy                                    |
# |   Staging:     npx wrangler deploy --env=staging                      |
# |   Dev:         npx wrangler deploy --env=dev                          |
# |                                                                       |
# |   Verify:  curl https://api.weylandai.com/api/health                  |
# +-----------------------------------------------------------------------+
#
# +-----------------------------------------------------------------------+
# | PAGES (Frontend — deployment/public/)                                 |
# |                                                                       |
# |   *** --branch=production IS MANDATORY — without it, deploys go to   |
# |   *** preview/alpha and production stays STALE. This has caused       |
# |   *** silent failures in sessions 26A01, BOSUN-12, and others.       |
# |                                                                       |
# |   Production:  npx wrangler pages deploy deployment/public \          |
# |                  --project-name=submittalexpress \                     |
# |                  --branch=production                                   |
# |                                                                       |
# |   Preview:     npx wrangler pages deploy deployment/public \          |
# |                  --project-name=submittalexpress                       |
# |                  (omitting --branch deploys to preview)               |
# |                                                                       |
# |   Verify:  Open https://subx.weylandai.com — check version in footer |
# +-----------------------------------------------------------------------+
#
# QUICK DEPLOY BOTH (copy-paste):
#   npx wrangler deploy && npx wrangler pages deploy deployment/public --project-name=submittalexpress --branch=production
#
# VERSION: 2.4.1
# UPDATED: 2026-02-12
# INDEX: See VERSION_INDEX.md for full stack documentation
# FEATURE: Cut Sheet Discovery Engine with Browser Rendering
# ============================================================================

name = "weyland"
main = "weyland-worker.js"
compatibility_date = "2025-10-31"
compatibility_flags = ["nodejs_compat"]

# Worker settings - Production uses custom domain route
workers_dev = true
routes = [
  { pattern = "api.weylandai.com/*", zone_name = "weylandai.com" },
  { pattern = "subx.weylandai.com/quote/*", zone_name = "weylandai.com" },
  { pattern = "subx.weylandai.com/q/*", zone_name = "weylandai.com" }
]

# Resource limits
limits = { cpu_ms = 50000 }

# Browser Rendering for Puppeteer-based web scraping
[browser]
binding = "BROWSER"

# D1 Database binding
[[d1_databases]]
binding = "DB"
database_name = "weyland_db"
database_id = "882ca221-ec6a-426b-955b-aa57f9ee10aa"

# R2 Storage bindings
[[r2_buckets]]
binding = "UPLOADS"
bucket_name = "subx-uploads"

[[r2_buckets]]
binding = "OUTPUTS"
bucket_name = "subx-outputs"

# KV Namespaces
[[kv_namespaces]]
binding = "DEMO_REQUESTS"
id = "aa23c8f67dc943448d0cfb247eb797df"

[[kv_namespaces]]
binding = "CACHE"
id = "62ce1167787844fdb3d4f4393f490410"

# Queue for Cut Sheet Discovery (producer)
[[queues.producers]]
queue = "cut-sheet-discovery"
binding = "DISCOVERY_QUEUE"

# Queue consumer configuration
[[queues.consumers]]
queue = "cut-sheet-discovery"
max_batch_size = 5
max_batch_timeout = 60
max_retries = 3
dead_letter_queue = "cut-sheet-discovery-dlq"

# Production environment variables
[vars]
ENVIRONMENT = "production"
VERSION = "2.4.0"
LOG_LEVEL = "info"
MAX_UPLOAD_SIZE_MB = "50"
# 27A: Presigned R2 upload — CF_ACCOUNT_ID is non-secret, R2 keys are secrets
CF_ACCOUNT_ID = "f07be5f84583d0d100b05aeeae56870b"
ALLOWED_FILE_TYPES = "application/pdf,image/png,image/jpeg"
RATE_LIMIT_REQUESTS = "100"
RATE_LIMIT_WINDOW_MINUTES = "1"
# CORS: Production custom domains (user-facing) + Pages URLs (dev/preview only)
# Users NEVER see pages.dev - _worker.js redirects to custom domains
CORS_ORIGINS = "https://weylandai.com,https://subx.weylandai.com,https://submittalexpress.pages.dev"
# CPS (Catalogue Processing System) API URL - worker calls itself for catalogue searches
CPS_API_URL = "https://api.weylandai.com"
# Takeoff Express feature flag (WO-2026-0115-TAKEOFF-001)
TAKEOFF_ENABLED = "true"

# Cron triggers for maintenance tasks
[triggers]
crons = ["0 2 * * *"]

# Observability
[observability]
enabled = true
head_sampling_rate = 1.0

# ============================================================================
# STAGING ENVIRONMENT
# Deploy: wrangler deploy --env=staging
# URL: weyland-yutani-staging.johnmobley99.workers.dev
# ============================================================================
[env.staging]
name = "weyland-yutani-staging"
workers_dev = true

[env.staging.vars]
ENVIRONMENT = "staging"
VERSION = "2.1.3-staging"
LOG_LEVEL = "debug"
MAX_UPLOAD_SIZE_MB = "50"
ALLOWED_FILE_TYPES = "application/pdf,image/png,image/jpeg"
RATE_LIMIT_REQUESTS = "1000"
RATE_LIMIT_WINDOW_MINUTES = "1"
CORS_ORIGINS = "https://submittalexpress.pages.dev,http://localhost:8787,http://localhost:3000,http://127.0.0.1:8787"

[[env.staging.d1_databases]]
binding = "DB"
database_name = "weyland_db"
database_id = "882ca221-ec6a-426b-955b-aa57f9ee10aa"

[[env.staging.r2_buckets]]
binding = "UPLOADS"
bucket_name = "subx-uploads"

[[env.staging.r2_buckets]]
binding = "OUTPUTS"
bucket_name = "subx-outputs"

[[env.staging.kv_namespaces]]
binding = "DEMO_REQUESTS"
id = "aa23c8f67dc943448d0cfb247eb797df"

[[env.staging.kv_namespaces]]
binding = "CACHE"
id = "62ce1167787844fdb3d4f4393f490410"

# ============================================================================
# DEV ENVIRONMENT (Local testing against workers.dev)
# Deploy: wrangler deploy --env=dev
# URL: weyland-yutani-dev.johnmobley99.workers.dev
# ============================================================================
[env.dev]
name = "weyland-yutani-dev"
workers_dev = true

[env.dev.vars]
ENVIRONMENT = "development"
VERSION = "2.1.3-dev"
LOG_LEVEL = "debug"
MAX_UPLOAD_SIZE_MB = "100"
ALLOWED_FILE_TYPES = "application/pdf,image/png,image/jpeg"
RATE_LIMIT_REQUESTS = "10000"
RATE_LIMIT_WINDOW_MINUTES = "1"
CORS_ORIGINS = "*"

[[env.dev.d1_databases]]
binding = "DB"
database_name = "weyland_db"
database_id = "882ca221-ec6a-426b-955b-aa57f9ee10aa"

[[env.dev.r2_buckets]]
binding = "UPLOADS"
bucket_name = "subx-uploads"

[[env.dev.r2_buckets]]
binding = "OUTPUTS"
bucket_name = "subx-outputs"

[[env.dev.kv_namespaces]]
binding = "DEMO_REQUESTS"
id = "aa23c8f67dc943448d0cfb247eb797df"

[[env.dev.kv_namespaces]]
binding = "CACHE"
id = "62ce1167787844fdb3d4f4393f490410"

# Security headers are handled in worker code (pure JS)

# Secrets (set via wrangler secret put)
# - ANTHROPIC_API_KEY: Claude API key (sk-ant-...)
# - JWT_SECRET: JWT signing secret (generated via openssl rand -base64 32)
# - OPENAI_API_KEY: OpenAI API key (if using GPT for fallback)

# To set secrets:
# wrangler secret put ANTHROPIC_API_KEY
# wrangler secret put JWT_SECRET

# DEPLOYMENT INSTRUCTIONS FOR ZERO-DEPENDENCY VERSION:
#
# 1. NO NPM INSTALL REQUIRED!
#
# 2. Set secrets:
#    wrangler secret put ANTHROPIC_API_KEY
#    wrangler secret put JWT_SECRET
#
# 3. Create D1 database (if not exists):
#    wrangler d1 create weyland_db
#
# 4. Run migrations:
#    wrangler d1 execute weyland_db --file=schema.sql
#
# 5. Deploy pure JavaScript worker:
#    wrangler deploy -c wrangler-pure.toml
#
# 6. Verify deployment:
#    curl https://weyland.johnmobley99.workers.dev/api/health
#
# Response should show:
# {
#   "status": "operational",
#   "dependencies": 0,  <-- ZERO dependencies!
#   "service": "SubX API (Pure JS)"
# }

_worker.js

Phase2/Prototype1/deployment/public/_worker.js · Cloudflare Pages advanced-mode router
/**
 * Cloudflare Pages Advanced Mode Worker
 * Professional SaaS URL Routing for WeylandAI
 *
 * URL Architecture:
 * - weylandai.com          → Corporate marketing site
 * - subx.weylandai.com     → SubmittalExpress product app
 * - *.pages.dev            → REDIRECT to custom domain (never shown to users)
 *
 * Best Practice: Users should NEVER see deployment platform URLs
 * Reference: https://developers.cloudflare.com/pages/how-to/redirect-to-custom-domain/
 */

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const hostname = url.hostname;

    // =========================================================================
    // RULE 1: Redirect ALL pages.dev URLs to custom domain
    // Users should NEVER see submittalexpress.pages.dev or any preview URLs
    // =========================================================================
    if (hostname.endsWith('.pages.dev') || hostname.endsWith('.workers.dev')) {
      // Determine the correct custom domain based on the path
      let targetHost;

      // If accessing SubX app paths, redirect to subx.weylandai.com
      if (url.pathname.startsWith('/subx') || url.pathname.startsWith('/app')) {
        targetHost = 'subx.weylandai.com';
        // Normalize path: /subx.html -> /app, /app -> /app
        url.pathname = url.pathname === '/subx.html' ? '/app' : url.pathname;
      } else {
        // All other paths go to main site
        targetHost = 'weylandai.com';
      }

      url.hostname = targetHost;
      url.protocol = 'https:';

      // 301 Permanent Redirect - SEO best practice
      return Response.redirect(url.toString(), 301);
    }

    // =========================================================================
    // RULE 2: SubX Product Domain (subx.weylandai.com)
    // =========================================================================
    if (hostname === 'subx.weylandai.com') {
      // Serve .html files directly — bypass Pretty URLs loop
      if (url.pathname.endsWith('.html')) {
        return env.ASSETS.fetch(request);
      }

      // Serve the SubX application
      const newUrl = new URL(request.url);

      // Root path and app aliases serve the app
      if (url.pathname === '/' || url.pathname === '/app' || url.pathname === '/subx') {
        newUrl.pathname = '/subx.html';
        return env.ASSETS.fetch(newUrl.toString());
      }

      // /login, /signup, /dashboard etc. all serve the SPA
      if (url.pathname.startsWith('/login') ||
          url.pathname.startsWith('/signup') ||
          url.pathname.startsWith('/dashboard') ||
          url.pathname.startsWith('/projects') ||
          url.pathname.startsWith('/session')) {
        newUrl.pathname = '/subx.html';
        return env.ASSETS.fetch(newUrl.toString());
      }

      // Static assets serve directly
      return env.ASSETS.fetch(request);
    }

    // =========================================================================
    // RULE 3: Corporate Domain (weylandai.com)
    // =========================================================================
    if (hostname === 'weylandai.com' || hostname === 'www.weylandai.com') {
      const newUrl = new URL(request.url);

      // Root serves marketing homepage
      if (url.pathname === '/' || url.pathname === '/index.html') {
        newUrl.pathname = '/index.html';
        return env.ASSETS.fetch(newUrl.toString());
      }

      // /products/subx redirects to the product domain
      if (url.pathname === '/products/subx' || url.pathname === '/subx') {
        return Response.redirect('https://subx.weylandai.com', 302);
      }

      // Serve static assets
      return env.ASSETS.fetch(request);
    }

    // =========================================================================
    // FALLBACK: Serve static assets for any other hostname
    // (This handles local development and edge cases)
    // =========================================================================
    return env.ASSETS.fetch(request);
  }
}

_headers

Phase2/Prototype1/deployment/public/_headers · Security headers & cache control
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  X-XSS-Protection: 1; mode=block
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: geolocation=(), microphone=(), camera=()
  Content-Security-Policy: default-src 'self' https://api.weylandai.com; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://cdn.jsdelivr.net https://cdn.sheetjs.com https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://api.weylandai.com; connect-src 'self' https://api.weylandai.com ws://localhost:* http://localhost:*; worker-src blob:;

/subx.html
  Cache-Control: no-cache, no-store, must-revalidate
  Pragma: no-cache
  Expires: 0

/subx
  Cache-Control: no-cache, no-store, must-revalidate
  Pragma: no-cache
  Expires: 0

/app
  Cache-Control: no-cache, no-store, must-revalidate
  Pragma: no-cache
  Expires: 0

/
  Cache-Control: no-cache, no-store, must-revalidate
  Pragma: no-cache
  Expires: 0

_redirects

Phase2/Prototype1/deployment/public/_redirects · SPA routing & domain redirects
# WeylandAI Professional SaaS Redirects
# These work with _worker.js to ensure users only see custom domains

# SubX Product Routes (subx.weylandai.com)
# SPA routes all serve the same HTML, client-side routing handles the rest
/app                  /subx.html    200
/login                /subx.html    200
/signup               /subx.html    200
/dashboard            /subx.html    200
/dashboard/*          /subx.html    200
/projects             /subx.html    200
/projects/*           /subx.html    200
/session/*            /subx.html    200

# Marketing Site Routes (weylandai.com)
/products/subx        https://subx.weylandai.com    302

# Catch-all for SPA (must be last)
# Note: API calls should use absolute URLs to api.weylandai.com
/*                    /index.html   200