Cloudflare Workers Deployment
Complete guide for deploying Loyalteez Cloudflare Workers to production with mainnet configurations.
Overview
Loyalteez infrastructure runs on 4 Cloudflare Workers that handle API requests, wallet creation, reward processing, and gasless transactions. This guide covers deployment, configuration, and troubleshooting.
Core Workers:
| Worker | Domain | Purpose |
|---|---|---|
| Event Handler | api.loyalteez.app | Manual events, Stripe minting, SDK serving |
| Pregeneration | register.loyalteez.app | Email/OAuth wallet creation via Privy |
| Reward Processor | Scheduled (Cron) | Background reward distribution |
| Gas Relayer | relayer.loyalteez.app | Gasless transaction execution |
Social Engagement Workers:
| Worker | Domain | Purpose |
|---|---|---|
| Twitter Loyalty Router | x-{brand}.loyalteez.app | Twitter/X engagement rewards (multi-tenant) |
| Farcaster Single-Brand | farcaster-bot.loyalteez.app | Loyalteez's own Farcaster tracking |
| Farcaster Multi-Tenant | fc.loyalteez.app | Shared Farcaster tracking for all brands |
Prerequisites
# Install Wrangler CLI
npm install -g wrangler
# Authenticate with Cloudflare
wrangler login
# Verify authentication
wrangler whoami
Mainnet Configuration
All workers are configured for Soneium Mainnet (Chain ID: 1868).
Network Settings
CHAIN_ID=1868
NETWORK_NAME=soneium
RPC_URL=https://rpc.soneium.org
BLOCK_EXPLORER=https://soneium.blockscout.com/
Contract Addresses (Mainnet - Deployed 2024)
# LTZ Token (EIP-2612 Permit enabled)
LOYALTEEZ_ADDRESS=0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9
# Perk NFT (v6 - no salt requirement)
PERK_NFT_ADDRESS=0x6ae30d6Dcf3e75456B6582b057f1Bf98A90F2CA0
# Points Sale (V3 with Permit)
POINTS_SALE_ADDRESS=0x5269B83F6A4E31bEdFDf5329DC052FBb661e3c72
# USDC (Mainnet)
USDC_ADDRESS=0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369
Worker 1: Event Handler
Handles main API endpoints and SDK serving.
Routes
api.loyalteez.app/*reward.loyalteez.app/*
Endpoints
# Health Check
GET https://api.loyalteez.app/health
# Manual Event Submission
POST https://api.loyalteez.app/loyalteez-api/manual-event
# Stripe Minting
POST https://api.loyalteez.app/loyalteez-api/stripe-mint
# SDK Serving
GET https://api.loyalteez.app/sdk.js
Configuration (wrangler.event-handler.toml)
name = "loyalteez-event-handler"
main = "src/event-handler/index.js"
compatibility_date = "2024-10-01"
compatibility_flags = ["nodejs_compat"]
[vars]
ENVIRONMENT = "production"
CHAIN_ID = "1868"
NETWORK_NAME = "soneium"
RPC_URL = "https://rpc.soneium.org"
BLOCK_EXPLORER = "https://soneium.blockscout.com/"
LOYALTEEZ_ADDRESS = "0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9"
PERK_NFT_ADDRESS = "0x6ae30d6Dcf3e75456B6582b057f1Bf98A90F2CA0"
POINTS_SALE_ADDRESS = "0x5269B83F6A4E31bEdFDf5329DC052FBb661e3c72"
PRIVY_APP_ID = "your_privy_app_id"
SUPABASE_URL = "https://your-project.supabase.co"
[[routes]]
pattern = "api.loyalteez.app/*"
zone_name = "loyalteez.app"
[[routes]]
pattern = "reward.loyalteez.app/*"
zone_name = "loyalteez.app"
Secrets (Encrypted)
# Set via wrangler secret put
wrangler secret put PRIVY_SECRET --config wrangler.event-handler.toml
wrangler secret put SUPABASE_SECRET_KEY --config wrangler.event-handler.toml
wrangler secret put WALLET_PRIVATE_KEY --config wrangler.event-handler.toml
wrangler secret put STRIPE_SECRET_KEY --config wrangler.event-handler.toml
wrangler secret put RESEND_API_KEY --config wrangler.event-handler.toml
Deploy
cd cloudflare-automation
npx wrangler deploy --config wrangler.event-handler.toml
Health Check Fix (Jan 2025)
Issue: Worker only responded to /loyalteez-api/health, not root /health.
Solution: Add root-level health check before API routing:
// src/event-handler/index.js
if (path === '/health' && request.method === 'GET') {
return successResponse({
status: 'healthy',
service: 'event-handler',
timestamp: new Date().toISOString(),
hostname: url.hostname,
routes: ['api.loyalteez.app/*', 'reward.loyalteez.app/*']
}, request);
}
Worker 2: Pregeneration
Creates Privy wallets for email addresses and OAuth users.
Route
register.loyalteez.app/*
Endpoints
# Health Check
GET https://register.loyalteez.app/health
# Wallet Pregeneration
POST https://register.loyalteez.app/loyalteez-api/pregenerate-user
Configuration (wrangler.pregeneration.toml)
name = "loyalteez-pregeneration"
main = "src/pregeneration/index.js"
compatibility_date = "2024-10-01"
compatibility_flags = ["nodejs_compat"]
[vars]
ENVIRONMENT = "production"
PRIVY_APP_ID = "your_privy_app_id"
SUPABASE_PUBLISH_KEY = "sb_publishable_..."
[[routes]]
pattern = "register.loyalteez.app/*"
zone_name = "loyalteez.app"
Secrets
wrangler secret put PRIVY_APP_SECRET --config wrangler.pregeneration.toml
wrangler secret put SUPABASE_URL --config wrangler.pregeneration.toml
wrangler secret put SUPABASE_SECRET_KEY --config wrangler.pregeneration.toml
Deploy
npx wrangler deploy --config wrangler.pregeneration.toml
Worker 3: Reward Processor
Background worker that processes pending rewards every minute.
Type
Scheduled Worker (Cron: */1 * * * *)
Endpoints
# Health Check (via workers.dev URL)
GET https://loyalteez-reward-processor.taylor-cox1199.workers.dev/health
# Manual Processing
POST https://loyalteez-reward-processor.taylor-cox1199.workers.dev/process-pending
# Queue Status
GET https://loyalteez-reward-processor.taylor-cox1199.workers.dev/queue
Configuration (wrangler.reward-processor.toml)
name = "loyalteez-reward-processor"
main = "src/reward-processor/index.js"
compatibility_date = "2023-11-01"
[vars]
ENVIRONMENT = "production"
CHAIN_ID = "1868"
RPC_URL = "https://rpc.soneium.org"
LOYALTEEZ_ADDRESS = "0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9"
PRIVY_APP_ID = "your_privy_app_id"
# Scheduled trigger - every 1 minute
[triggers]
crons = ["*/1 * * * *"]
# Note: No custom route - handled by Event Handler at reward.loyalteez.app
Secrets
wrangler secret put PRIVY_SECRET --config wrangler.reward-processor.toml
wrangler secret put SUPABASE_SECRET_KEY --config wrangler.reward-processor.toml
wrangler secret put WALLET_PRIVATE_KEY --config wrangler.reward-processor.toml
wrangler secret put RESEND_API_KEY --config wrangler.reward-processor.toml
Deploy
npx wrangler deploy --config wrangler.reward-processor.toml
Worker 4: Gas Relayer
Executes gasless transactions on behalf of users.
Route
relayer.loyalteez.app/*
Endpoints
# Health Check
GET https://relayer.loyalteez.app/health
# Relay Transaction (requires Privy auth)
POST https://relayer.loyalteez.app/relay
Configuration (wrangler-gas-relayer.toml)
name = "loyalteez-gas-relayer"
main = "src/gas-relayer/worker.js"
compatibility_date = "2024-10-01"
[vars]
VITE_RPC_URL = "https://rpc.soneium.org"
VITE_PRIVY_APP_ID = "your_privy_app_id"
DEV_WALLET_ADDRESS = "0x92627010DF82F6aA16eeCb45C4bD2207140C52A7"
VITE_LOYALTEEZ_ADDRESS = "0x5242b6DB88A72752ac5a54cFe6A7DB8244d743c9"
VITE_PERK_NFT_ADDRESS = "0x6ae30d6Dcf3e75456B6582b057f1Bf98A90F2CA0"
VITE_POINTS_SALE_ADDRESS = "0x5269B83F6A4E31bEdFDf5329DC052FBb661e3c72"
[[kv_namespaces]]
binding = "RELAYER_KV"
id = "your_kv_namespace_id"
[env.production]
name = "loyalteez-gas-relayer"
routes = [
{ pattern = "relayer.loyalteez.app/*", zone_name = "loyalteez.app" }
]
Secrets
wrangler secret put DEV_WALLET_PRIVATE_KEY --config wrangler-gas-relayer.toml
wrangler secret put PRIVY_SECRET --config wrangler-gas-relayer.toml
wrangler secret put VITE_SUPABASE_SECRET_KEY --config wrangler-gas-relayer.toml
wrangler secret put VITE_SUPABASE_PUBLISH_KEY --config wrangler-gas-relayer.toml
Deploy
npx wrangler deploy --config wrangler-gas-relayer.toml --env production
Automated Deployment
Use the provided deployment script to deploy all workers at once.
deploy-workers.sh
#!/bin/bash
# Deploy all workers to production
echo "🚀 Deploying all Cloudflare Workers..."
# 1. Event Handler
echo "📦 [1/4] Deploying Event Handler..."
npx wrangler deploy --config wrangler.event-handler.toml
# 2. Pregeneration
echo "📦 [2/4] Deploying Pregeneration..."
npx wrangler deploy --config wrangler.pregeneration.toml
# 3. Reward Processor
echo "📦 [3/4] Deploying Reward Processor..."
npx wrangler deploy --config wrangler.reward-processor.toml
# 4. Gas Relayer
echo "📦 [4/4] Deploying Gas Relayer..."
npx wrangler deploy --config wrangler-gas-relayer.toml --env production
echo "✅ All workers deployed successfully!"
Usage
cd cloudflare-automation
chmod +x deploy-workers.sh
./deploy-workers.sh
Health Check Verification
After deployment, verify all workers are healthy:
# Event Handler
curl https://api.loyalteez.app/health
# Expected: {"status":"healthy","service":"event-handler",...}
# Pregeneration
curl https://register.loyalteez.app/health
# Expected: {"status":"healthy","service":"oauth-pregeneration",...}
# Gas Relayer
curl https://relayer.loyalteez.app/health
# Expected: {"status":"healthy","service":"gas-relayer",...}
# Reward Processor (workers.dev URL)
curl https://loyalteez-reward-processor.taylor-cox1199.workers.dev/health
# Expected: {"status":"healthy","worker":"reward-processor",...}
Monitoring & Logs
Tail Logs in Real-Time
# Event Handler
wrangler tail loyalteez-event-handler
# Pregeneration
wrangler tail loyalteez-pregeneration
# Reward Processor
wrangler tail loyalteez-reward-processor
# Gas Relayer
wrangler tail loyalteez-gas-relayer
View Deployment History
# List deployments
wrangler deployments list --name loyalteez-event-handler
# View specific deployment
wrangler deployments view <deployment-id>
Check Worker Status
# Worker details
wrangler status --name loyalteez-event-handler
# KV namespace list
wrangler kv:namespace list
# KV namespace keys
wrangler kv:key list --namespace-id <namespace-id>
Troubleshooting
404 on Health Endpoints
Symptoms:
curl https://api.loyalteez.app/healthreturns 404- Worker shows as deployed in dashboard
Causes:
- Worker only has
/loyalteez-api/health, not root/health - DNS propagation delay (5-30 minutes)
- Route not configured
Solutions:
- Add root-level health check (Fixed in Jan 2025):
// Before API routing
if (path === '/health' && request.method === 'GET') {
return successResponse({
status: 'healthy',
service: 'event-handler',
timestamp: new Date().toISOString()
}, request);
}
- Wait for DNS propagation:
# Check DNS
nslookup api.loyalteez.app
# Test via workers.dev URL while waiting
curl https://loyalteez-event-handler.taylor-cox1199.workers.dev/health
- Verify routes in dashboard:
- Cloudflare Dashboard → Workers & Pages → loyalteez-event-handler
- Check "Domains & Routes" tab
Route Conflicts
Error: "A route with the same pattern already exists"
Solution: Remove duplicate route from wrangler.toml:
# BAD: Both workers define reward.loyalteez.app/*
# In event-handler:
[[routes]]
pattern = "reward.loyalteez.app/*"
# In reward-processor:
[[routes]]
pattern = "reward.loyalteez.app/*" # ❌ CONFLICT!
# GOOD: Only Event Handler serves reward.loyalteez.app
# reward-processor has no custom route (runs on schedule)
Secrets Not Working
Symptoms:
- Worker deploys but fails at runtime
- Authentication errors in logs
Solution:
# List existing secrets
wrangler secret list --config wrangler.event-handler.toml
# Set missing secrets
wrangler secret put PRIVY_SECRET --config wrangler.event-handler.toml
# Redeploy after setting secrets
npx wrangler deploy --config wrangler.event-handler.toml
Cron Not Firing
Symptoms:
- Reward Processor not running
- No scheduled executions in logs
Solution:
- Verify cron trigger in wrangler.toml:
[triggers]
crons = ["*/1 * * * *"] # Every minute
- Check scheduled logs:
wrangler tail loyalteez-reward-processor --format pretty
- Manually trigger:
curl -X POST https://loyalteez-reward-processor.taylor-cox1199.workers.dev/process-pending
Security Best Practices
1. Never Commit Secrets
# ✅ Good - stored encrypted
wrangler secret put PRIVY_SECRET
# ❌ Bad - visible in code
[vars]
PRIVY_SECRET = "actual_secret_value"
2. Use Environment-Specific Configs
# Production
[env.production]
name = "loyalteez-gas-relayer"
routes = [
{ pattern = "relayer.loyalteez.app/*", zone_name = "loyalteez.app" }
]
# Development
[env.development]
name = "loyalteez-gas-relayer-dev"
# No custom routes - use workers.dev URL
3. Restrict CORS Origins
// Development
const corsHeaders = {
'Access-Control-Allow-Origin': '*'
};
// Production
const corsHeaders = {
'Access-Control-Allow-Origin': 'https://partners.loyalteez.app'
};
4. Rate Limiting
Use KV namespaces for rate limiting:
const rateLimit = async (userAddress, limit = 35) => {
const key = `rate_limit_${userAddress}_${Math.floor(Date.now() / 3600000)}`;
const count = await env.RELAYER_KV.get(key) || 0;
if (count >= limit) {
throw new Error('Rate limit exceeded');
}
await env.RELAYER_KV.put(key, count + 1, { expirationTtl: 3600 });
};
Performance Optimization
1. Use KV for Caching
// Cache DNS lookups
const cachedDNS = await env.DNS_CACHE.get(`dns_${domain}`);
if (cachedDNS) {
return JSON.parse(cachedDNS);
}
const dnsRecord = await fetchDNSTxtRecord(domain);
await env.DNS_CACHE.put(`dns_${domain}`, JSON.stringify(dnsRecord), {
expirationTtl: 3600 // 1 hour
});
2. Batch Database Queries
// BAD: N+1 queries
for (const event of events) {
await database.getEvent(event.id);
}
// GOOD: Single batch query
const eventIds = events.map(e => e.id);
const eventData = await database.batchGetEvents(eventIds);
3. Use Durable Objects Sparingly
Durable Objects are powerful but expensive. Use KV for:
- Simple key-value storage
- Rate limiting
- Caching
Use Durable Objects for:
- Real-time collaboration
- Strong consistency requirements
- Coordination between workers
Rollback Strategy
Rollback to Previous Version
# List deployments
wrangler deployments list --name loyalteez-event-handler
# Rollback to specific version
wrangler rollback --name loyalteez-event-handler --version <version-id>
# Verify rollback
curl https://api.loyalteez.app/health
Gradual Rollout
# Deploy to 10% of traffic first
[env.canary]
name = "loyalteez-event-handler-canary"
routes = [
{ pattern = "api.loyalteez.app/*", zone_name = "loyalteez.app", percentage = 10 }
]
Social Engagement Workers
In addition to the core infrastructure, Loyalteez provides social engagement workers that track brand mentions and reward users automatically.
Twitter Loyalty Bot
Polls Twitter API for brand mentions and rewards users.
| Property | Value |
|---|---|
| Domain | x-{brand}.loyalteez.app |
| API | Twitter API v2 |
| Events | tweet_mention, tweet_reply, tweet_like, tweet_retweet |
| Documentation | Twitter Integration |
Quick Deploy:
cd x-loyalty-bot
npx wrangler kv:namespace create TWITTER_BOT_KV
npx wrangler secret put TWITTER_BEARER_TOKEN
npx wrangler secret put SUPABASE_PUBLISH_KEY
npx wrangler deploy
Farcaster Loyalty Bot
Polls Neynar API for Farcaster engagements and rewards users.
| Property | Value |
|---|---|
| Single-Brand Domain | farcaster-bot.loyalteez.app |
| Multi-Tenant Domain | fc.loyalteez.app |
| API | Neynar API |
| Events | farcaster_mention, farcaster_reply, farcaster_like, farcaster_recast, farcaster_follow |
| Documentation | Farcaster Integration |
Quick Deploy (Single-Brand):
cd farcaster-loyalty-bot
npx wrangler kv namespace create FARCASTER_BOT_KV
npx wrangler secret put NEYNAR_API_KEY
npx wrangler secret put SUPABASE_PUBLISH_KEY
npx wrangler deploy
Quick Deploy (Multi-Tenant Router):
cd farcaster-loyalty-bot
npx wrangler kv namespace create FARCASTER_ROUTER_KV
npx wrangler secret put NEYNAR_API_KEY -c wrangler.router.toml
npx wrangler deploy -c wrangler.router.toml
Common Configuration
Both social bots follow the same patterns:
# wrangler.toml template for social bots
name = "{platform}-loyalty-bot"
main = "src/index.js"
compatibility_date = "2024-11-23"
compatibility_flags = ["nodejs_compat"]
[[kv_namespaces]]
binding = "{PLATFORM}_BOT_KV"
id = "your_kv_id"
# Service binding for direct worker communication
[[services]]
binding = "EVENT_HANDLER"
service = "loyalteez-event-handler"
# Poll every 15 minutes
[triggers]
crons = ["*/15 * * * *"]
[vars]
BRAND_ID = "0xYourBrandWallet"
LOYALTEEZ_API_URL = "https://api.loyalteez.app"
User Identification Pattern
Both bots use synthetic emails for deterministic wallet creation:
// Twitter
const email = `twitter_${userId}@loyalteez.app`;
// Farcaster
const email = `farcaster_${fid}@loyalteez.app`;
Related Documentation
- Environment Variables - Configuration reference
- API Reference - API endpoints
- Testing Guide - Test your workers
- Authentication - Privy integration
- Twitter Integration - Twitter Loyalty Bot setup
- Farcaster Integration - Farcaster Loyalty Bot setup
Changelog
January 2025
- ✅ Added root-level
/healthendpoint to Event Handler - ✅ Verified mainnet configurations (Chain ID: 1868)
- ✅ Fixed route conflicts (reward.loyalteez.app only on Event Handler)
- ✅ Updated contract addresses for 2024 mainnet deployment
- ✅ Created automated deployment script
- ✅ Documented health check verification
December 2024
- Initial Cloudflare Workers documentation
Need Help? Join our Discord or email [email protected]