Architecture Note: All services consolidated to single Docker host bennybeen (10.0.0.250). External IP 76.150.65.61 is DYNAMIC - see DNS-UPDATE-PROCESS.md for update procedures. Use deploy.sh for automated deployment.

Tasks & Todos - Mad Monkey Project

Project: "Where Has Benny Been?" - Implementation Checklist

📋 Task Progress Management
Your progress is automatically saved in your browser
Jump to Phase: Phase 1: Infrastructure Phase 2: Workflows Phase 3: QR Production Phase 4: Testing Phase 5: Pilot Phase 6: Full Deploy

Overall Progress

Phase Status Progress Blockers
Phase 1: Infrastructure Setup IN PROGRESS
35%
-
Phase 2: n8n Workflows PENDING
0%
-
Phase 3: QR Production PENDING
0%
-
Phase 4: E2E Testing PENDING
0%
-
Phase 5: Pilot Launch PENDING
0%
-
Phase 6: Full Deployment PENDING
0%
-

Phase 1: Infrastructure Setup

DNS Configuration

A record: @ → 76.150.65.61
A record: www → 76.150.65.61
A record: admin → 76.150.65.61
Verify DNS propagation with dig/nslookup
A record: @ → 10.0.0.60
A record: www → 10.0.0.60
A record: hooks → 76.150.65.61
A record: list → 76.150.65.61
A record: mail → 76.150.65.61
MX record: @ → mail.mad-monkey-creations.com (priority 10)
TXT record: SPF configuration
TXT record: DKIM key (from ForwardMail.net)
TXT record: DMARC policy
Verify with mxtoolbox.com
A record: n8n → 76.150.65.61
A record: traefik → 76.150.65.61
Verify DNS propagation with dig/nslookup
Review DNS-UPDATE-PROCESS.md for update procedures
Set up automated IP change detection
Configure notifications for IP changes
Test DNS update workflow

PostgreSQL Database

All containers run on single Docker host: bennybeen (10.0.0.250)
Create docker-compose.yml with postgres:16 image
Set POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB env vars
Mount persistent volume for data at /home/bennybeen/mad-monkey/
Expose port 5432 on 10.0.0.250
Test connection: psql -h 10.0.0.250 -U mm_user -d madmonkey
Connection string: postgresql://mm_user:PASSWORD@10.0.0.250:5432/madmonkey
Option: Use deploy.sh script for automated deployment
Create 'cards' table (qr_id, type, status, current_slug, move_code, etc.)
Create 'scans' table (qr_id, ts, ip, ua, geo_city, geo_region, etc.)
Create 'entries' table (qr_id, email, name, user_city, verified, month_bucket)
Create 'moves' table (traveler_id, rev, mover_email, pickup_city, drop_city)
Create 'nests' table (venue_id, name, city, contact_email, health_score)
Create 'users' table (email PK, name, city, tags[], referral_code)
Create 'referrals' table (inviter_email, invitee_email, status)
Create 'prizes' table (month, winner_email, fulfillment_status)
Add indexes on foreign keys and frequently queried fields
Use docker exec commands with container names on bennybeen
Verify table creation and relationships

Shlink URL Shortener

Pull shlinkio/shlink:stable image
Configure docker-compose with PostgreSQL connection to 10.0.0.250:5432
Set DEFAULT_DOMAIN=mmlnk.us
Expose port 8081 on 10.0.0.250
Generate API key via Shlink CLI
Test API: curl -H "X-Api-Key: xxx" http://10.0.0.250:8081/rest/v3/short-urls
Option: Use deploy.sh script for automated deployment
Deploy Traefik container on bennybeen (10.0.0.250:8080)
Add Traefik labels to Shlink container
Configure public routing: mmlnk.us → Shlink
Configure admin routing: admin.mmlnk.us/rest → Shlink API (IP-restricted)
Enable Let's Encrypt SSL for mmlnk.us
Test https://mmlnk.us (should return 404 - no short URL)
Test https://admin.mmlnk.us/rest/v3/short-urls
Access Traefik dashboard at traefik.janness.com

Listmonk Email Platform

Pull listmonk/listmonk:latest image
Create config.toml with PostgreSQL connection to 10.0.0.250:5432
Run initial setup: ./listmonk --install
Expose port 9000 on 10.0.0.250
Access web UI: http://10.0.0.250:9000
API endpoint: http://10.0.0.250:9000/api/
Create admin account
Option: Use deploy.sh script for automated deployment
Get SMTP credentials from ForwardMail.net
Configure SMTP settings in Listmonk UI
SMTP server: smtp.forwardemail.net:587
Set sender: hello@mad-monkey-creations.com
Test email delivery to personal email
Verify SPF/DKIM/DMARC pass with mail-tester.com
Create list: "Mad Monkey - Benny Campaign"
Enable double opt-in for list
Create template: Welcome email
Create template: Story email (Day 1)
Create template: Reward email (Day 3 - BANANAS10 code)
Create template: Monthly winner announcement
Configure webhook endpoint for confirmation events

n8n Workflow Platform

Pull n8nio/n8n:latest image
Configure docker-compose with PostgreSQL connection to 10.0.0.250:5432
Expose port 5678 on 10.0.0.250
Access web UI: http://10.0.0.250:5678
Configure external webhook URL: hooks.mad-monkey-creations.com
Set up Traefik routing for n8n.janness.com
Option: Use deploy.sh script for automated deployment

Landing Page

Design form layout (Name, Email, "Where did you find Benny?")
Add Benny branding and imagery
Implement form validation (client-side)
Add consent checkbox with privacy policy link
Configure form POST to n8n webhook endpoint (hooks.mad-monkey-creations.com)
Add CAPTCHA (Cloudflare Turnstile or hCaptcha)
Test responsive design on mobile devices
Deploy to /benny path on main website (10.0.0.60)
Alternative: Serve from bennybeen via nginx container

Deployment Automation

Review deploy.sh script in /home/bennybeen/mad-monkey/
Execute deploy.sh to deploy all containers
Verify all containers are running on 10.0.0.250
Check container health status

Phase 2: n8n Workflow Creation

Workflow 1: Scan Intake

Create webhook trigger: /scan?qr_id=X on n8n (10.0.0.250:5678)
Extract qr_id, IP, User-Agent from request
Call IP geolocation API (ipapi.co or ipinfo)
Anonymize IP address (remove last octet)
Insert scan record to PostgreSQL 'scans' table (10.0.0.250:5432)
Update 'cards.last_scan_ts' for qr_id
Return 302 redirect to mad-monkey-creations.com/benny?qr_id=X
Add error handling for invalid qr_id
Test with curl: curl -L https://hooks.mad-monkey-creations.com/scan?qr_id=T42

Workflow 2: Email Capture

Create webhook trigger: POST /entry on n8n (10.0.0.250:5678)
Extract form data: qr_id, email, name, user_city
Validate email format and required fields
Check rate limits (monthly entry limit per email)
Insert to PostgreSQL 'entries' table (verified=false) at 10.0.0.250:5432
Call Listmonk API (http://10.0.0.250:9000/api/) to create/update subscriber
Add tags: qr_campaign, benny, entry_month_YYYY-MM
Trigger double opt-in email via Listmonk
Return success response to landing page
Add error handling for duplicate entries

Workflow 3: Confirmation Handler

Create webhook trigger: POST /listmonk/confirm on n8n (10.0.0.250:5678)
Validate webhook signature (if supported by Listmonk)
Extract subscriber email from webhook payload
Update PostgreSQL 'entries.verified=true' for email at 10.0.0.250:5432
Update 'users' table with confirmed status
Trigger Welcome email workflow via Listmonk API (10.0.0.250:9000)
Schedule Story email (Day 1 delay)
Schedule Reward email (Day 3 delay)
Test with manual webhook POST

Workflow 4: Email Drip Campaign

Create scheduled trigger (daily cron) in n8n (10.0.0.250:5678)
Query users eligible for Day 1 story email from PostgreSQL (10.0.0.250:5432)
Fetch card scan history and build story content
Send story email via Listmonk API (http://10.0.0.250:9000/api/)
Query users eligible for Day 3 reward email
Send reward email with BANANAS10 code
Mark drip status in database to avoid duplicates
Test with test subscriber

Workflow 5: Monthly Winner Selection

Create cron trigger (1st of each month at 9 AM) in n8n (10.0.0.250:5678)
Query all verified entries from previous month from PostgreSQL (10.0.0.250:5432)
Select random entry (ORDER BY RANDOM() LIMIT 1)
Insert winner to 'prizes' table
Send winner notification email via Listmonk (10.0.0.250:9000)
Send announcement to full list
Log winner selection event
Test with mock data

Workflow 6: Card Health Monitoring

Create cron trigger (daily at 8 AM) in n8n (10.0.0.250:5678)
Query cards with no scans in 10+ days from PostgreSQL (10.0.0.250:5432)
Query cards with no scans in 45+ days (stagnant threshold)
Send alert email with card locations via Listmonk (10.0.0.250:9000)
Trigger "rescue mission" re-engagement campaign
Update 'nests.health_score' based on card activity
Test with aged test data

Workflow Testing

Export each workflow as JSON from n8n (10.0.0.250:5678)
Store in version control repository at /home/bennybeen/mad-monkey/
Document environment variables required (DB: 10.0.0.250:5432, Listmonk: 10.0.0.250:9000)
Test import on fresh n8n instance

Phase 3: QR Card Production

Test Batch

Create 5 test Nest cards (N01-N05)
Create 5 test Traveler cards (T01-T05)
Register cards in PostgreSQL 'cards' table (10.0.0.250:5432)
Generate unique move codes for travelers
Create short URLs in Shlink via API (http://10.0.0.250:8081/rest/v3/...)
Generate QR codes using mini-qr or qrencode
Verify all QR codes scan correctly
Create front design with Big Benny graphic
Add tagline: "Track My Monkey"
Create back design with QR code
Add short URL below QR code
Add instructions: "Scan. Tell us where. Confirm email. Monthly winner!"
Add move code for traveler cards
Export as print-ready PDF (300 DPI, CMYK)
Get quotes from PVC card printers
Order 10 business card size (3.5" x 2") PVC cards
Verify print quality and QR code scannability
Test durability (water resistance, scratch resistance)

Full Production

Create 200-300 Nest card IDs (N001-N300)
Create 50-100 Traveler card IDs (T001-T100)
Bulk insert to PostgreSQL 'cards' table (10.0.0.250:5432)
Bulk create short URLs via Shlink API (http://10.0.0.250:8081/rest/v3/short-urls)
Generate all QR codes
Create print-ready PDF sheets (multiple cards per page)
Finalize card quantities based on pilot results
Get bulk pricing quotes
Submit print order
Quality check received cards
Verify random sample of QR codes scan correctly

Phase 4: End-to-End Testing

Integration Testing

Scan test QR code on mobile device
Verify redirect to landing page
Submit form with test data
Receive double opt-in email
Click confirmation link
Verify entry marked as verified in database
Receive welcome email
Receive story email (Day 1)
Receive reward email with discount code (Day 3)
Verify all database records created correctly
Scan traveler card in City A
Submit entry form with move code
Move card to City B (simulate with VPN)
Scan again in City B
Verify 'moves' table records distance and cities
Verify story email includes movement narrative

Load and Performance Testing

Attempt rapid-fire scans from same IP
Verify rate limiting triggers
Attempt multiple entries with same email
Verify monthly entry limit enforcement
Test CAPTCHA with bot tools
Verify VPN detection and validation
Send test emails to Gmail
Send test emails to Outlook
Send test emails to Yahoo
Verify SPF/DKIM/DMARC pass on all providers
Check spam score with mail-tester.com (aim for 10/10)
Verify unsubscribe links work correctly

Error Handling

Test invalid QR code ID
Test malformed email addresses
Test database connection failure
Test Listmonk API unavailability
Test geolocation API failure
Verify graceful error messages shown to users
Verify errors logged for debugging

Phase 5: Pilot Launch (20-50 cards)

Pilot Deployment

Print 20-50 pilot cards (mix of Nest and Traveler)
Identify 10-15 friendly local venues
Place Nest cards at venues
Distribute Traveler cards to trusted testers
Record venue information in 'nests' table
Brief venue owners on the campaign

Monitoring and Analytics

Create SQL queries for key metrics
Track: Total scans, form submissions, verified entries
Track: Scan-to-submission rate, submission-to-verified rate
Track: Email deliverability (bounces, opens, clicks)
Track: Stuck cards, stagnant cards
Track: Geographic distribution of scans
Create Grafana or Metabase dashboard (optional)
Daily check of scan activity for first week
Review email delivery logs
Check for errors or workflow failures
Gather feedback from venue owners
Gather feedback from participants
Identify underperforming venues or cards

Optimization

Adjust email copy based on open/click rates
Refine landing page based on conversion rates
Fine-tune drip campaign timing
Optimize CAPTCHA settings to reduce friction
Identify and fix any UX pain points
Document lessons learned

Phase 6: Full Deployment (200-300 cards)

Scaling

Review database performance on bennybeen (10.0.0.250:5432) and add indexes if needed
Increase PostgreSQL container resources if necessary
Configure database backups (daily automated) on bennybeen
Set up monitoring alerts (email/SMS for failures)
Document disaster recovery procedures for single-server architecture
Verify all containers running on 10.0.0.250
Place 200-300 Nest cards at venues across region
Distribute 50-100 Traveler cards
Create venue partnership agreements
Provide venues with marketing materials
Track all venue locations in 'nests' table

Ongoing Operations

Set up weekly reporting on key metrics
Create playbook for stuck card retrieval
Create playbook for monthly winner fulfillment
Document venue onboarding process
Set up support email for participant questions
Create FAQ page on website
Execute monthly winner workflow
Contact winner and collect shipping details
Order tee-shirt via Printify API
Track fulfillment in 'prizes' table
Send announcement to email list
Post winner announcement on social media

Growth and Iteration

Implement referral system (share Benny, earn extra entries)
Create leaderboard for most active cards
Add seasonal or themed campaigns
Integrate with Etsy shop for product recommendations
Create "Benny Ambassador" program for top participants
Expand to new geographic regions