From 103620e10a9e926fc202b49a458b49c48914be8c Mon Sep 17 00:00:00 2001 From: root Date: Tue, 11 Aug 2026 08:48:43 -0400 Subject: [PATCH] HotNow Savannah v2: city-pivot proposal + full technical docs (Batch 001 research, Aug 11 2026) --- projects/hotnow-savannah-technical.md | 2400 +++++++++++++++++++++++++ projects/hotnow-savannah-v2.md | 981 ++++++++++ 2 files changed, 3381 insertions(+) create mode 100644 projects/hotnow-savannah-technical.md create mode 100644 projects/hotnow-savannah-v2.md diff --git a/projects/hotnow-savannah-technical.md b/projects/hotnow-savannah-technical.md new file mode 100644 index 0000000..3674b34 --- /dev/null +++ b/projects/hotnow-savannah-technical.md @@ -0,0 +1,2400 @@ +# HotNow Savannah -- Technical Features, Enhancements & Implementation Guide + +**Date:** August 11, 2026 +**Product:** HotNow (hotnow.io) -- Savannah Metro Launch +**Classification:** IT Pro Partner -- Internal Technical Documentation +**Status:** Draft for Germaine Review + +--- + +## Table of Contents + +1. [Architecture Overview](#1-architecture-overview) +2. [New Technical Features](#2-new-technical-features) +3. [Data Model](#3-data-model) +4. [API Surface](#4-api-surface) +5. [Ranking Algorithm](#5-ranking-algorithm) +6. [Implementation Roadmap](#6-implementation-roadmap) +7. [Infrastructure & Deployment](#7-infrastructure--deployment) +8. [Integration Points](#8-integration-points) +9. [Testing & QA Strategy](#9-testing--qa-strategy) +10. [Operational Runbook](#10-operational-runbook) + +--- + +## 1. Architecture Overview + +### 1.1 System Diagram (Savannah-Focused) + +``` ++-------------------------------------------------------------------+ +| USERS (PWA + Web) | +| app.hotnow.io/savannah -- Map View -- Discovery Feed -- | +| Neighborhood Browser -- Ambassador Portal -- Auth | ++-------------------------------------------------------------------+ + | + v ++-------------------------------------------------------------------+ +| CLOUDFLARE CDN / DNS | +| Static assets (JS/CSS/icons) | DDoS protection | DNS routing | +| hotnow.io A -> 152.53.192.33 | app/api/admin subdomains | ++-------------------------------------------------------------------+ + | + v ++-------------------------------------------------------------------+ +| CADDY REVERSE PROXY (Core: 152.53.192.33) | +| SSL termination (LetsEncrypt) | Rate limiting | Route by | +| subdomain | hotnow.io -> /var/www/hotnow/ | api -> :8001 | ++-------------------------------------------------------------------+ + | + +-------------------+-------------------+ + v v v ++------------------+ +------------------+ +------------------+ +| hotnow.io | | api.hotnow.io | | admin.hotnow.io | +| (Marketing Pg) | | (FastAPI :8001) | | (Static Admin) | +| /var/www/hotnow | | /root/hotnow-api| | /var/www/hotnow- | +| | | | | admin/ | ++------------------+ +------------------+ +------------------+ + | + +-------------------+-------------------+-------------------+ + v v v v ++------------------+ +------------------+ +------------------+ +------------------+ +| PostgreSQL 16 | | Redis 7 | | Super Search v2 | | HotNow MCP | +| + PostGIS 3 | | Cache + Pub/Sub | | (FastMCP :8899) | | Server | +| hotnow DB | | trending:* keys | | 7 providers | | (FastMCP :8901) | +| Core :5432 | | Core :6379 | | /root/docker/ | | /opt/hotnow-mcp/ | +| | | | | super-search/ | | | ++------------------+ +------------------+ +------------------+ +------------------+ + | | | | + v v v v ++-------------------------------------------------------------------+ +| EXTERNAL SERVICES | +| Stripe | Mapbox GL | Eventbrite API | Ticketmaster API | +| Meetup | PRAW (Reddit) | admin-ai (LiteLLM / deepseek-v4-pro) | +| Yelp Fusion | Google Places | Instagram Graph API | ++-------------------------------------------------------------------+ +``` + +### 1.2 Component Breakdown + +| Component | Location | Technology | Status | +|-----------|----------|------------|--------| +| **PWA Frontend** | `/var/www/hotnow-app/` | SvelteKit (static export) + Leaflet.js | Placeholder exists | +| **Marketing Landing** | `/var/www/hotnow/` | Static HTML/CSS | Has index.html (1342 lines) | +| **REST API** | `/root/hotnow-api/` | FastAPI 0.115+ (Python 3.13) | Service running (hotnow-api.service), partial impl | +| **PostgreSQL + PostGIS** | Core:5432 | PostgreSQL 16 + PostGIS 3 | Running; hotnow DB needs schema | +| **Redis** | Core:6379 | Redis 7 (allkeys-lru) | Running; key namespace needed | +| **Super Search v2 MCP** | `/root/docker/super-search/` | FastMCP 4.x, Streamable HTTP | Running (:8899), 17 tools, 7 providers | +| **HotNow MCP Server** | `/opt/hotnow-mcp/` (to create) | FastMCP 4.x | Not yet built | +| **Caddy Reverse Proxy** | `/etc/caddy/Caddyfile` | Caddy 2.x | hotnow routes already configured | +| **Systemd Services** | `/etc/systemd/system/` | systemd | hotnow-api.service, super-search.service | +| **ARQ Task Queue** | Core (to deploy) | ARQ + Redis | Not yet deployed | +| **Reddit Automation** | Core (cron) | PRAW + cron | Not yet built | +| **AI Model** | admin-ai (LiteLLM) | deepseek-v4-pro | Operational | +| **Stripe** | API integration | Subscriptions + Webhooks | Auth key reserved, not wired | +| **Mapbox** | PWA (Leaflet.js) | OpenStreetMap tiles (free tier) | Mapbox optional for satellite/3D | + +### 1.3 Savannah Metro Scope + +Savannah metro area boundaries for initial launch: + +| Zone | Neighborhoods | Est. Venues | +|------|--------------|-------------| +| **Historic District** | Downtown, River Street, City Market, Broughton St | 80-120 | +| **Starland / Thomas Square** | Starland District, Bull Street, Forsyth Park area | 40-60 | +| **Midtown / Southside** | Abercorn corridor, Oglethorpe Mall area | 30-50 | +| **Tybee Island** | Beachfront bars, pier, restaurants | 20-30 | +| **Pooler** | Airport area, outlet malls, chain restaurants | 25-40 | +| **SCAD Campus Area** | Buildings, galleries, student venues | 15-25 | +| **Total Seed Target** | | **200-300 venues** | + +GeoJSON boundaries stored in `neighborhoods` table (PostGIS polygons). Corridor browsing via spatial queries against these boundaries plus linear buffers along named streets. + +--- + +## 2. New Technical Features + +### 2.1 Real-Time Ranking Algorithm + +**Problem:** Existing platforms rank by accumulated reviews (Yelp), date (Eventbrite), or manual curation (Thrillist). None answer "what's hot right now." + +**Solution:** Multi-signal scoring engine that recalculates every 5 minutes via ARQ cron job. + +#### Signal Categories + +| Signal | Weight | Source | Decay Model | Refresh | Implementation | +|--------|--------|--------|-------------|---------|---------------| +| **Freshness** | 30% | How recently posted/updated/checked-into | Exponential half-life: 6h | Every 5 min | PostgreSQL `updated_at` + `created_at` columns | +| **Velocity** | 25% | Social mention acceleration, check-in velocity, view/clicks/min | Exponential half-life: 2h | Every 5 min | Redis pulse counters + rolling window | +| **Social Proof** | 20% | Instagram/TikTok mention counts, Reddit mentions | Exponential half-life: 4h | Every 15 min | Super Search v2 social crawling | +| **Contextual** | 15% | Weather, time of day, day of week, proximity | Contextual (no decay) | Every 30 min | OpenWeatherMap API + PostgreSQL time functions | +| **Manual Boost** | 10% | Featured Placement ($97/mo), Event Boost ($47) | Fixed duration | On purchase | Business feature table | + +#### Core Formula + +``` +HOT_SCORE = ( + FRESHNESS(x) * W_f + + VELOCITY(x) * W_v + + SOCIAL_PROOF(x) * W_s + + CONTEXTUAL(x) * W_c + + MANUAL_BOOST(x) * W_m +) * CITY_NORMALIZATION * TIME_BOOST +``` + +Where: + +- `W_f=0.30, W_v=0.25, W_s=0.20, W_c=0.15, W_m=0.10` +- `CITY_NORMALIZATION = log10(active_users + 1) / log10(total_events + 1)` -- prevents large cities from dominating; Savannah benefits from small denominator +- `TIME_BOOST = ramp_up(t)`: linearly increases from 24h before event start, peaks at 2h before, decays after event starts + +#### Velocity Calculation + +```python +def velocity_score(event_id: str) -> float: + """Rolling 2-hour window of engagement metrics.""" + now = datetime.utcnow() + window_start = now - timedelta(hours=2) + + # Redis sorted set: pulse:{event_id} -> [{timestamp, metric_type}] + views = redis.zcount(f"pulse:{event_id}:views", window_start.timestamp(), now.timestamp()) + clicks = redis.zcount(f"pulse:{event_id}:clicks", window_start.timestamp(), now.timestamp()) + saves = redis.zcount(f"pulse:{event_id}:saves", window_start.timestamp(), now.timestamp()) + + # Weighted and normalized + raw_velocity = (views * 0.3 + clicks * 0.4 + saves * 0.5) + return math.log1p(raw_velocity) / 10.0 # Cap at ~1.0 +``` + +#### Anti-Gaming Measures + +- Velocity cap: engagement increase >300% in 15 min -> throttled to previous 15-min rate +- Bot detection: same IP rapid-fire views -> excluded from pulse counters +- User-submitted events: require admin approval before entering trending +- Manual boost transparency: labeled "Promoted" in UI, tracked separately in analytics +- Minimum signal threshold: events with <5 total signals get `trending_score = 0.0` + +#### Fallback Strategy + +When social signals are sparse (common in Savannah at launch): +1. **Freshness dominates** -- recently added/updated content naturally ranks +2. **Manual curation boost** -- admin-curated events get weight multiplier (1.5x) +3. **Randomized tiebreaks** -- prevents stale top-of-list from accumulating +4. **Proximity boost** -- events closest to user's current location get +0.05 score + +#### Implementation Location + +``` +/root/hotnow-api/ranking.py # Score calculator +/root/hotnow-api/trend_worker.py # ARQ cron job (every 5 min) +/root/hotnow-api/signals/ # Per-signal modules + freshness.py # Time-decay calculations + velocity.py # Redis pulse aggregation + social.py # Social mention counting + contextual.py # Weather/time/proximity + boost.py # Manual boost management +``` + +#### Redis Key Schema for Ranking + +``` +trending:savannah:all Sorted Set {event_id: score} +trending:savannah:music Sorted Set {event_id: score} +trending:savannah:food_drink Sorted Set {event_id: score} +trending:savannah:arts_culture Sorted Set {event_id: score} +trending:savannah:nightlife Sorted Set {event_id: score} +trending:savannah:pop_up Sorted Set {event_id: score} +pulse:{event_id}:views Sorted Set {timestamp: count} +pulse:{event_id}:clicks Sorted Set {timestamp: count} +pulse:{event_id}:saves Sorted Set {timestamp: count} +``` + +--- + +### 2.2 Structured AI-Readable Data Layer (PeerPush Pattern) + +**Problem:** AI assistants (ChatGPT, Claude, Perplexity) cannot query HotNow data. Users asking "what's hot in Savannah tonight?" to AI get no answer. This is the next SEO frontier. + +**Solution:** Every listing and page emits structured data in standardized formats consumable by AI crawlers and LLMs. + +#### Implementation Layers + +**Layer 1: Schema.org Markup (HTML pages)** + +Every venue and event detail page includes JSON-LD: + +```html + + + + + +``` + +**Layer 2: Open Graph and Twitter Cards** + +```html + + + + + + + +``` + +**Layer 3: Sitemap with Structured Data** + +`https://hotnow.io/sitemap-savannah.xml`: + +```xml + + + + https://hotnow.io/savannah/event/550e8400-e29b + 2026-08-11 + hourly + 0.9 + + + +``` + +**Layer 4: AI-Readable API Endpoints** + +``` +GET /api/v1/ai/events?city=savannah&date=today + -> Returns JSON optimized for LLM context windows + -> Includes venue info, prices, times, descriptions + -> Structured for function-calling consumption + +GET /api/v1/ai/trending?city=savannah + -> Compact JSON with rank, name, category, score + -> Designed for <500 token response +``` + +#### Implementation Plan + +| Step | File | Effort | +|------|------|--------| +| SvelteKit JSON-LD component | `src/lib/components/StructuredData.svelte` | 3h | +| OG + Twitter meta tags | `src/routes/event/[id]/+page.svelte` | 2h | +| Sitemap generator (cron) | `sitemap_generator.py` + `crontab` | 4h | +| AI-readable API endpoints | `/root/hotnow-api/routers/ai_readable.py` | 5h | +| **Total** | | **14 hours** | + +--- + +### 2.3 MCP Server Integration + +**Problem:** Developers and AI agents need programmatic access to HotNow data in the same way they query Super Search v2. The MCP protocol is becoming the standard for AI-tool integration. + +**Solution:** New FastMCP 4.x server exposing HotNow as MCP tools -- same pattern as Super Search v2 (already proven on Core). + +#### Architecture + +``` ++-------------------+ +--------------------+ +------------------+ +| AI Client / Agent | <---> | HotNow MCP Server | <---> | PostgreSQL | +| (e.g., Hermes) | MCP | (FastMCP :8901) | SQL | + Redis | ++-------------------+ +--------------------+ +------------------+ + | + v + +------------------+ + | Super Search v2 | + | (MCP :8899) | + +------------------+ +``` + +#### MCP Tools + +| Tool | Description | Parameters | +|------|-------------|------------| +| `hotnow_search_events` | Search events by query, category, date range, location | `query` (str), `category` (optional), `city` (str), `date_from` (optional), `date_to` (optional), `limit` (int=10) | +| `hotnow_get_venue` | Get full venue details with upcoming events | `venue_id` (UUID) or `venue_name` (str) | +| `hotnow_trending` | Get trending events in a city/neighborhood | `city` (str), `neighborhood` (optional), `category` (optional), `limit` (int=10) | +| `hotnow_neighborhood` | List neighborhoods for a city with venue counts | `city` (str) | +| `hotnow_recommendations` | AI-powered personalized recommendations | `city` (str), `preferences` (list[str]), `lat` (float), `lng` (float) | +| `hotnow_venue_search` | Search venues by name, type, or neighborhood | `query` (str), `city` (str), `venue_type` (optional), `limit` (int=10) | +| `hotnow_health` | Health check -- returns status of DB, Redis, Super Search | (none) | + +#### Implementation Pattern (matching Super Search v2) + +```python +#!/usr/bin/env python3 +"""HotNow MCP Server v1.0.0 -- FastMCP 4.x, Streamable HTTP.""" +from __future__ import annotations +from fastmcp import FastMCP +import asyncpg + +mcp = FastMCP("HotNow Savannah MCP") + +@mcp.tool(description="Search events in Savannah metro area. Returns events matching query with venue info, times, and trending scores.") +async def hotnow_search_events( + query: str, + category: str | None = None, + city: str = "Savannah", + date_from: str | None = None, + date_to: str | None = None, + limit: int = 10, +) -> str: + """Search events with full-text search + category + date filters.""" + conn = await _get_db() + # ... SQL query with tsvector, PostGIS proximity ... + return json.dumps(results, indent=2, ensure_ascii=False) + +@mcp.tool(description="Get trending events in Savannah. Returns real-time ranked events with scores, venue info, and pulse data.") +async def hotnow_trending( + city: str = "Savannah", + neighborhood: str | None = None, + category: str | None = None, + limit: int = 10, +) -> str: + """Read from Redis sorted sets for sub-millisecond response.""" + # ... Redis ZREVRANGE trending:{city}:{category} ... + +@mcp.tool(description="Get neighborhood definitions and venue counts for Savannah metro area.") +async def hotnow_neighborhood(city: str = "Savannah") -> str: + """Return GeoJSON boundaries + venue counts per neighborhood.""" + # ... PostGIS ST_Within queries ... + +# ... other tools ... + +if __name__ == "__main__": + from fastmcp.server.http import create_streamable_http_app + import uvicorn + app = create_streamable_http_app(server=mcp, streamable_http_path="/mcp") + uvicorn.run(app, host="127.0.0.1", port=8901) +``` + +#### Systemd Service + +File: `/etc/systemd/system/hotnow-mcp.service` + +```ini +[Unit] +Description=HotNow Savannah MCP Server +After=postgresql.service redis-server.service network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/hotnow-mcp +Environment=PATH=/opt/hotnow-mcp/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +Environment=PYTHONUNBUFFERED=1 +EnvironmentFile=/root/hotnow-api/.env +ExecStart=/opt/hotnow-mcp/venv/bin/python3 /opt/hotnow-mcp/server.py +Restart=always +RestartSec=3 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +``` + +#### Caddy Route Addition + +Add to `/etc/caddy/Caddyfile`: + +``` +mcp.hotnow.io { + reverse_proxy 127.0.0.1:8901 + header Access-Control-Allow-Origin * + header Access-Control-Allow-Methods "GET, POST, OPTIONS" +} +``` + +#### Hermes Agent Integration + +In `~/.hermes/hermes.yaml`: + +```yaml +tools: + mcp_servers: + - name: hotnow-savannah + url: https://mcp.hotnow.io/mcp + transport: streamable-http +``` + +--- + +### 2.4 Reddit Flywheel Automation + +**Problem:** Paid user acquisition is expensive ($5-15/install). Savannah has active subreddits with 15K+ SCAD students plus locals. Free community-driven growth is proven by Locale-NYC's success. + +**Solution:** Automated, scheduled content posting to Savannah subreddits with AI-generated summaries, event CTA links, and UTM tracking. + +#### Target Subreddits + +| Subreddit | Subscribers | Content Type | Posting Frequency | +|-----------|-------------|-------------|-------------------| +| r/savannah | ~45K | General local events | Daily | +| r/savannahga | ~8K | Savannah metro | 3x/week | +| r/scad | ~12K | Student events + nightlife | 3x/week (during semester) | + +#### Post Templates + +Daily "What's Hot Tonight in Savannah" post: + +```markdown +**What's Hot Tonight in Savannah -- {{ date }}** + +Here's what's happening according to real-time signals: + +🔥 **TRENDING** +- {{ top_3_events_by_trending_score }} + +🎵 **Live Music Tonight** +- {{ live_music_events }} + +🍽️ **Food & Drink** +- {{ food_events_with_specials }} + +🎨 **Arts & Culture** +- {{ arts_events }} + +[View all events on HotNow](https://hotnow.io/savannah?utm_source=reddit&utm_medium=social&utm_campaign=savannah_daily&utm_content={{ date_iso }}) + +*HotNow tracks what's actually happening right now -- not what Yelp reviewed 3 years ago.* +``` + +#### Automation Architecture + +``` ++------------------+ Every 6 hours +------------------+ +| PostgreSQL | -----------------> | Reddit Worker | +| (events table) | | (PRAW + cron) | ++------------------+ +------------------+ + | + v + +------------------+ + | AI Summarizer | + | (deepseek-v4-pro)| + +------------------+ + | + v + +------------------+ + | Reddit API | + | (PRAW library) | + | -> r/savannah | + | -> r/scad | + +------------------+ +``` + +#### Implementation + +```python +#!/usr/bin/env python3 +"""Reddit Flywheel -- automated content posting for HotNow Savannah.""" + +import os +import json +import praw +import asyncpg +from datetime import datetime, timedelta +from openai import OpenAI + +# AI client via admin-ai (LiteLLM) +client = OpenAI( + base_url="https://admin-ai.itpropartner.com/v1", + api_key=os.environ["LITELLM_API_KEY"], +) + +REDDIT_SUBREDDITS = { + "savannah": {"freq": "daily", "type": "daily_whats_hot"}, + "scad": {"freq": "3x_week", "type": "student_events"}, + "savannahga": {"freq": "3x_week", "type": "daily_whats_hot"}, +} + +async def generate_daily_post(subreddit: str) -> str: + """Fetch top events from DB, generate AI summary, format post.""" + conn = await asyncpg.connect(os.environ["DATABASE_URL"]) + + # Fetch trending events + events = await conn.fetch(""" + SELECT e.title, e.description, e.start_time, v.name as venue_name, + e.trending_score, e.category + FROM events e + JOIN venues v ON e.venue_id = v.id + WHERE v.city = 'Savannah' + AND e.start_time >= NOW() + AND e.start_time < NOW() + INTERVAL '24 hours' + AND e.is_active = true + ORDER BY e.trending_score DESC + LIMIT 20 + """) + await conn.close() + + # Generate AI summary + event_summaries = format_events_for_ai(events) + ai_prompt = f"""Write a friendly, Gen-Z-coded Reddit post about tonight's events +in Savannah. Use these events: +{event_summaries} + +Format: markdown. Include emojis naturally. Keep it helpful, not salesy. +Add a link to hotnow.io/savannah with UTM params at the bottom. +Max 800 characters.""" + + response = client.chat.completions.create( + model="deepseek-v4-pro", + messages=[{"role": "user", "content": ai_prompt}], + max_tokens=600, + ) + return response.choices[0].message.content + +def post_to_reddit(subreddit: str, content: str) -> str: + """Post content to specified subreddit using PRAW.""" + reddit = praw.Reddit( + client_id=os.environ["REDDIT_CLIENT_ID"], + client_secret=os.environ["REDDIT_CLIENT_SECRET"], + user_agent="HotNow/1.0 (by /u/hotnow_app)", + username=os.environ.get("REDDIT_USERNAME", "hotnow_app"), + password=os.environ.get("REDDIT_PASSWORD"), + ) + sub = reddit.subreddit(subreddit) + today = datetime.now().strftime("%A, %B %d") + title = f"What's Hot Tonight in Savannah -- {today}" + post = sub.submit(title=title, selftext=content, flair_id=None) + return post.id +``` + +#### Cron Schedule + +``` +# /etc/cron.d/hotnow-reddit +0 9,14,18 * * * root /root/hotnow-api/venv/bin/python3 /root/hotnow-api/reddit_worker.py >> /var/log/hotnow-reddit.log 2>&1 +``` + +| Time | Action | Target | +|------|--------|--------| +| 09:00 ET | Daily "What's Hot Today" | r/savannah | +| 14:00 ET | Afternoon update | r/scad, r/savannahga | +| 18:00 ET | Evening "Tonight" post | r/savannah | + +#### UTM Tracking Convention + +``` +https://hotnow.io/savannah?utm_source=reddit&utm_medium=social&utm_campaign=savannah_daily&utm_content=2026-08-11_r_savannah +``` + +Tracked in `discover_events` table via `source_context = 'reddit_flywheel'`. + +#### Content Calendar (First 2 Weeks) + +| Day | r/savannah Post | r/scad Post | +|-----|----------------|-------------| +| Mon | "This Week in Savannah" -- weekly preview | "SCAD Week Ahead" -- student events | +| Tue | "Taco Tuesday + Trivia Night Roundup" | Skip (low engagement) | +| Wed | "Midweek Music -- Live Shows Tonight" | "Hump Day Happenings -- student nightlife" | +| Thu | "Weekend Preview -- What's Already Buzzing" | "Thirsty Thursday -- drink specials near campus" | +| Fri | "What's Hot Tonight in Savannah" (weekend edition) | "Weekend Kickoff -- parties, shows, events" | +| Sat | "Saturday Night Live -- music, clubs, nightlife" | Skip (students out) | +| Sun | "Sunday Funday -- brunch, markets, chill events" | "Sunday Scaries -- chill study break spots" | + +--- + +### 2.5 SCAD Ambassador Program System + +**Problem:** Savannah has 15,000+ SCAD students who are the ideal early-adopter demographic -- Gen Z, discovery-driven, social, always looking for what's happening. Beli proved campus ambassador programs work (30M reviews, 80% users under 35). + +**Solution:** SCAD ambassador portal with student verification, referral tracking, incentives, and content contribution system. + +#### Ambassador Portal Features + +| Feature | Description | Tech | +|---------|-------------|------| +| **SCAD Email Verification** | `*@scad.edu` domain validation + magic link | FastAPI auth + Resend | +| **Referral Dashboard** | Track referrals, earned credits, leaderboard | React PWA component | +| **Content Submission** | Submit venues, events, photos, reviews | API + admin review queue | +| **Social Share Tracking** | Track shares to Instagram/TikTok with attribution | UTM + Redis counters | +| **Incentive Credits** | Pro credits for contributions (see table below) | DB transaction + Stripe coupon | + +#### Incentive System + +| Action | Credits Earned | Cap (per week) | +|--------|---------------|----------------| +| Verify SCAD email | 1 month Pro free | One-time | +| Submit new venue (approved) | 1 month Pro free | 5 | +| Submit event (approved) | 2 weeks Pro free | 10 | +| Share to Instagram (with #hotnowsavannah) | 1 week Pro free | 3 | +| Refer a friend (friend signs up) | 1 month Pro free each | 10 | +| Report outdated event | 1 week Pro free | 5 | +| Top 10 weekly leaderboard | 1 month Concierge | 1 winner | + +Credits implemented as Stripe coupons applied to subscription: `ambassador_credits_{user_id}` with amount equal to monthly Pro cost multiplied by credits earned. + +#### Verification Flow + +``` +1. User clicks "SCAD Student? Get Pro Free" on PWA +2. Enters @scad.edu email in verification form +3. System sends magic link to that email via Resend +4. User clicks link -> verifies ownership +5. System checks: email domain == "scad.edu" && not previously verified +6. If pass: create ambassador profile, grant 1-month Pro credit +7. Redirect to ambassador dashboard +``` + +#### Database Tables + +```sql +CREATE TABLE ambassadors ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, + scad_email VARCHAR(255) NOT NULL, + verification_status VARCHAR(20) DEFAULT 'pending', -- pending, verified, revoked + verified_at TIMESTAMPTZ, + total_referrals INTEGER DEFAULT 0, + total_submissions INTEGER DEFAULT 0, + total_credits_earned INTEGER DEFAULT 0, + current_streak_weeks INTEGER DEFAULT 0, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE TABLE ambassador_actions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + ambassador_id UUID REFERENCES ambassadors(id) ON DELETE CASCADE, + action_type VARCHAR(50) NOT NULL, -- referral, submission, share, report + credits_earned INTEGER DEFAULT 0, + reference_id UUID, -- event_id, venue_id, or referred user_id + metadata JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +#### Ambassador Dashboard Component + +Route: `app.hotnow.io/ambassador` (auth-gated) + +SvelteKit page with: +- Stats cards: credits balance, referrals, submissions, rank +- Quick actions: submit venue, submit event, share to social +- Leaderboard: top 10 ambassadors this week +- Activity feed: recent submissions and their approval status +- Pro status: days remaining, credits history + +--- + +### 2.6 Neighborhood/Corridor Browsing (Locale-NYC Pattern) + +**Problem:** Users don't always want "what's near me" -- they want "what's happening on River Street" or "what's good in Starland." Locale-NYC proves corridor/neighborhood browsing drives engagement. + +**Solution:** PostGIS-powered neighborhood browsing with GeoJSON boundaries and corridor "strip" queries. + +#### Savannah Neighborhood GeoJSON Definitions + +Stored in `neighborhoods` table with `boundary` column as PostGIS `GEOGRAPHY(POLYGON, 4326)`. + +| Neighborhood | Key Streets | Approx Center | +|-------------|-------------|---------------| +| Historic District | Broughton St, River St, Bay St, Congress St | 32.0809, -81.0912 | +| Starland District | Bull St, 41st St, Whitaker St | 32.0517, -81.0988 | +| Midtown | Abercorn St, DeRenne Ave | 32.0180, -81.1112 | +| Tybee Island | Butler Ave, Strand Ave | 32.0002, -80.8456 | +| Pooler | Pooler Pkwy, US-80 | 32.1155, -81.2472 | +| SCAD Campus Area | Montgomery St, W Boundary St | 32.0735, -81.0980 | +| Victorian District | Bull St, Park Ave | 32.0640, -81.0950 | + +#### Spatial Queries + +**Neighborhood query: "What's happening in Starland tonight?"** + +```sql +SELECT e.*, v.name as venue_name, + ST_Distance(v.location, n.boundary::geometry) as distance_to_boundary +FROM events e +JOIN venues v ON e.venue_id = v.id +JOIN neighborhoods n ON ST_Within(v.location::geometry, n.boundary::geometry) +WHERE n.slug = 'starland' + AND e.start_time >= NOW() + AND e.start_time < NOW() + INTERVAL '24 hours' + AND e.is_active = true +ORDER BY e.trending_score DESC; +``` + +**Corridor query: "What's on River Street?"** + +```sql +-- Create a 250m buffer around River Street linestring +WITH corridor AS ( + SELECT ST_Buffer( + ST_GeomFromText('LINESTRING(-81.0940 32.0810, -81.0870 32.0800)', 4326)::geography, + 250 -- 250m buffer + )::geometry AS geom +) +SELECT e.*, v.name as venue_name +FROM events e +JOIN venues v ON e.venue_id = v.id +JOIN corridor c ON ST_Within(v.location::geometry, c.geom) +ORDER BY e.trending_score DESC; +``` + +#### API Endpoint Additions + +``` +GET /api/v1/savannah/neighborhoods + -> List neighborhoods with venue counts and boundaries + +GET /api/v1/savannah/neighborhood/:slug + -> Events in neighborhood, paginated, with trending scores + +GET /api/v1/savannah/corridor?street=broughton&city=savannah + -> Events along a street corridor (250m buffer) +``` + +#### Map UI: Clustering by Neighborhood + +Leaflet.js implementation: + +```javascript +// Cluster markers by neighborhood polygon containment +const neighborhoodLayers = {}; +neighborhoods.forEach(n => { + neighborhoodLayers[n.slug] = L.layerGroup(); + neighborhoodLayers[n.slug].addTo(map); +}); + +// Assign each venue marker to its neighborhood layer +venues.forEach(v => { + const marker = createVenueMarker(v); + const hood = findContainingNeighborhood(v.lat, v.lng); + if (hood) { + marker.addTo(neighborhoodLayers[hood.slug]); + } +}); + +// Neighborhood toggle controls +L.control.layers(null, neighborhoodLayers, {collapsed: false}).addTo(map); +``` + +--- + +### 2.7 City-Launch Playbook Automation + +**Problem:** Seeding a new city with 200-300 venues manually takes 1-2 weeks of dedicated work. This must be repeatable for each new city launch (Charleston, Atlanta, etc.). + +**Solution:** Automated venue ingestion pipeline with AI-assisted curation. + +#### Ingestion Pipeline + +``` ++------------------+ +------------------+ +------------------+ +| Yelp Fusion API | | Google Places API | | Eventbrite API | ++--------+---------+ +--------+---------+ +--------+---------+ + | | | + v v v ++------------------------------------------------------------------+ +| Venue Ingestion Worker | +| - Query "bars restaurants Savannah GA" from each source | +| - Dedup by name + address fuzzy match | +| - Geocode addresses via Nominatim (free, no API key) | +| - Assign neighborhood via PostGIS ST_Within | +| - Enrich with category, hours, social links | ++------------------------------------------------------------------+ + | + v ++------------------------------------------------------------------+ +| AI Curation Worker | +| - For each ingested venue: generate tags, description, vibe | +| - Rate quality (1-5) based on completeness + relevance | +| - Flag high-quality venues for admin review | +| - Suggest category and venue_type | ++------------------------------------------------------------------+ + | + v ++------------------------------------------------------------------+ +| Admin Review Queue | +| POST /admin/review/:id -> approve / edit / reject | +| Dashboard URL: admin.hotnow.io/review | ++------------------------------------------------------------------+ + | + v ++------------------------------------------------------------------+ +| PostgreSQL (venues table) | ++------------------------------------------------------------------+ +``` + +#### Seed Scraper Script + +```python +#!/usr/bin/env python3 +"""city_seed.py -- Ingest seed venues for a new HotNow city.""" + +import asyncio +import json +import os +import asyncpg +import httpx +from geopy.geocoders import Nominatim +from shapely.geometry import Point +import geoalchemy2 + +YELP_API_KEY = os.environ["YELP_FUSION_API_KEY"] +GOOGLE_PLACES_KEY = os.environ["GOOGLE_PLACES_API_KEY"] +EVENTBRITE_TOKEN = os.environ["EVENTBRITE_API_TOKEN"] + +DEFAULT_CATEGORIES = ["bars", "restaurants", "musicvenues", "nightlife", "arts"] + +async def fetch_yelp_venues(city: str, state: str, category: str) -> list[dict]: + """Fetch venues from Yelp Fusion API.""" + url = "https://api.yelp.com/v3/businesses/search" + headers = {"Authorization": f"Bearer {YELP_API_KEY}"} + venues = [] + for offset in range(0, 1000, 50): # Yelp caps at 1000 results + params = { + "location": f"{city}, {state}", + "categories": category, + "limit": 50, + "offset": offset, + } + async with httpx.AsyncClient() as client: + resp = await client.get(url, headers=headers, params=params) + if resp.status_code != 200: + break + data = resp.json() + venues.extend(data.get("businesses", [])) + if len(data.get("businesses", [])) < 50: + break + return venues + +async def dedup_and_insert(venues: list[dict], city: str) -> int: + """Dedup by name + address fuzzy match, insert into venues table.""" + conn = await asyncpg.connect(os.environ["DATABASE_URL"]) + inserted = 0 + for v in venues: + name = v.get("name", "") + address = " ".join(v.get("location", {}).get("display_address", [])) + lat = v.get("coordinates", {}).get("latitude") + lng = v.get("coordinates", {}).get("longitude") + + # Check for duplicates + existing = await conn.fetchrow(""" + SELECT id FROM venues + WHERE name ILIKE $1 + AND address ILIKE $2 + AND city = $3 + LIMIT 1 + """, name, f"%{address}%", city) + if existing: + continue + + await conn.execute(""" + INSERT INTO venues ( + name, description, venue_type, address, city, state, + postal_code, location, phone, website, source, source_venue_id + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, + ST_SetSRID(ST_MakePoint($8, $9), 4326)::geography, + $10, $11, 'yelp_fusion', $12 + ) + """, + name, + v.get("categories", [{}])[0].get("title", ""), + map_yelp_to_venue_type(v.get("categories", [])), + address, + city, + v.get("location", {}).get("state", "GA"), + v.get("location", {}).get("zip_code", ""), + lng, lat, + v.get("display_phone", ""), + v.get("url", ""), + v.get("id", ""), + ) + inserted += 1 + await conn.close() + return inserted + +async def ai_curate_venues(city: str) -> None: + """Run AI curation on un-reviewed ingested venues.""" + conn = await asyncpg.connect(os.environ["DATABASE_URL"]) + venues = await conn.fetch(""" + SELECT id, name, description, venue_type, address + FROM venues + WHERE city = $1 AND source != 'manual' + AND reviewed_by_admin = false + LIMIT 50 + """, city) + + for v in venues: + # Call deepseek-v4-pro via admin-ai to generate: + # - polished description (1-2 sentences) + # - suggested tags (array) + # - vibe rating (1-5) + # - suggested category corrections + ai_result = await generate_venue_metadata(v["name"], v["description"]) + await conn.execute(""" + UPDATE venues + SET description = $1, + tags = $2, + metadata = jsonb_set(COALESCE(metadata, '{}'), '{ai_curated}', 'true'), + reviewed_by_admin = ($3 >= 4) -- auto-approve high-quality + WHERE id = $4 + """, ai_result["description"], ai_result["tags"], + ai_result["quality_score"], v["id"]) + + await conn.close() +``` + +#### Launch Checklist Automation + +| Check | Script/Query | Threshold | +|-------|-------------|-----------| +| Total venues | `SELECT COUNT(*) FROM venues WHERE city='Savannah'` | >=200 | +| Venues with descriptions | `SELECT COUNT(*) FROM venues WHERE city='Savannah' AND description IS NOT NULL` | >=80% | +| Venues with geolocation | `SELECT COUNT(*) FROM venues WHERE city='Savannah' AND location IS NOT NULL` | >=95% | +| Venues with hours | `SELECT COUNT(*) FROM venues WHERE city='Savannah' AND hours IS NOT NULL` | >=60% | +| Active events next 7 days | `SELECT COUNT(*) FROM events WHERE venue_id IN (...) AND start_time > NOW() AND start_time < NOW() + INTERVAL '7 days'` | >=50 | +| Neighborhood coverage | `SELECT n.slug, COUNT(v.id) FROM neighborhoods n LEFT JOIN venues v ON ST_Within(...) GROUP BY n.slug` | All hoods have >=5 venues | +| Trending scores populated | Redis `ZCARD trending:savannah:all` | >=50 | + +--- + +## 3. Data Model + +### 3.1 Full PostgreSQL Schema + +#### Enums + +```sql +CREATE TYPE event_category AS ENUM ( + 'music', 'food_drink', 'arts_culture', 'nightlife', + 'sports', 'family', 'pop_up', 'other' +); + +CREATE TYPE venue_type AS ENUM ( + 'bar', 'restaurant', 'club', 'theater', 'park', + 'gallery', 'pop_up', 'cafe', 'hotel', 'other' +); + +CREATE TYPE busy_level AS ENUM ('quiet', 'moderate', 'busy', 'packed', 'unknown'); +CREATE TYPE subscription_tier AS ENUM ('explorer', 'pro', 'concierge'); +CREATE TYPE subscription_status AS ENUM ('active', 'past_due', 'canceled', 'inactive'); +CREATE TYPE auth_provider AS ENUM ('email', 'google', 'apple'); +CREATE TYPE discover_action AS ENUM ('view', 'click', 'save', 'share', 'check_in'); +CREATE TYPE feature_type AS ENUM ('featured_placement', 'event_boost'); +CREATE TYPE feature_status AS ENUM ('active', 'expired', 'canceled'); +``` + +#### Tables + +**neighborhoods** (new -- Savannah metro boundaries) + +```sql +CREATE TABLE neighborhoods ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(100) NOT NULL, + slug VARCHAR(100) UNIQUE NOT NULL, + city VARCHAR(100) NOT NULL DEFAULT 'Savannah', + state VARCHAR(2) NOT NULL DEFAULT 'GA', + boundary GEOGRAPHY(POLYGON, 4326) NOT NULL, + center GEOGRAPHY(POINT, 4326), + description TEXT, + cover_image_url VARCHAR(500), + sort_order INTEGER DEFAULT 0, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_neighborhoods_boundary ON neighborhoods USING GIST (boundary); +CREATE INDEX idx_neighborhoods_city ON neighborhoods (city, state); +``` + +**venues** (extending existing schema with neighborhood FK) + +```sql +ALTER TABLE venues ADD COLUMN neighborhood_id UUID REFERENCES neighborhoods(id); +ALTER TABLE venues ADD COLUMN metadata JSONB DEFAULT '{}'; +CREATE INDEX idx_venues_neighborhood ON venues (neighborhood_id); + +-- Existing columns from hotnow-phase1.md schema: +-- id, name, description, venue_type, address, city, state, postal_code, +-- location (GEOGRAPHY), geo_json, phone, website, social_links, hours, +-- cover_image_url, media_urls, trending_score, current_busy_level, +-- owner_user_id, source, source_venue_id, created_at, updated_at +``` + +**events** (as defined in hotnow-phase1.md, no changes) + +```sql +-- id, title, description, category, start_time, end_time, timezone, +-- venue_id (FK), cover_image_url, media_urls, price_info, ticket_url, +-- source, source_event_id, source_url, trending_score, popularity_pulse, +-- social_mention_count, search_volume_24h, check_in_count, +-- ai_sentiment_score, tags, is_active, reviewed_by_admin, created_at, updated_at +``` + +**users** (as defined in hotnow-phase1.md) + +```sql +-- id, email, display_name, avatar_url, password_hash, auth_provider, +-- auth_provider_id, email_verified, tier (subscription_tier), +-- stripe_customer_id, subscription_status, subscription_expires_at, +-- home_city, home_location (GEOGRAPHY), preferred_categories, +-- preferred_radius_km, notification_prefs, push_subscription, +-- is_business, is_admin, last_active_at, created_at +``` + +**discover_events** (as defined in hotnow-phase1.md) + +```sql +-- id, event_id (FK), user_id, action (discover_action), +-- source_context, created_at +``` + +**business_features** (as defined in hotnow-phase1.md) + +```sql +-- id, venue_id (FK), feature_type, status, starts_at, ends_at, +-- stripe_payment_id, amount_paid_cents, metadata, created_at +``` + +**ambassadors** (new -- SCAD program) + +```sql +CREATE TABLE ambassadors ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, + email VARCHAR(255) NOT NULL, + school VARCHAR(100) DEFAULT 'SCAD', + verification_status VARCHAR(20) DEFAULT 'pending', + verified_at TIMESTAMPTZ, + total_referrals INTEGER DEFAULT 0, + total_submissions INTEGER DEFAULT 0, + total_credits_earned INTEGER DEFAULT 0, + current_streak_weeks INTEGER DEFAULT 0, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_ambassadors_user ON ambassadors (user_id); +CREATE INDEX idx_ambassadors_status ON ambassadors (verification_status); +``` + +**ambassador_actions** (new) + +```sql +CREATE TABLE ambassador_actions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + ambassador_id UUID REFERENCES ambassadors(id) ON DELETE CASCADE, + action_type VARCHAR(50) NOT NULL, + credits_earned INTEGER DEFAULT 0, + reference_id UUID, + metadata JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_ambassador_actions_amb ON ambassador_actions (ambassador_id, created_at DESC); +``` + +**reddit_posts** (new -- track flywheel posts) + +```sql +CREATE TABLE reddit_posts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + subreddit VARCHAR(100) NOT NULL, + post_type VARCHAR(50) NOT NULL, -- daily_whats_hot, student_events, weekend_preview + reddit_post_id VARCHAR(50), + title TEXT NOT NULL, + content TEXT, + url VARCHAR(500), + utm_params JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_reddit_posts_date ON reddit_posts (subreddit, created_at DESC); +``` + +**seed_ingestion_log** (new -- track city seed pipeline) + +```sql +CREATE TABLE seed_ingestion_log ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + city VARCHAR(100) NOT NULL, + source VARCHAR(50) NOT NULL, -- yelp_fusion, google_places, eventbrite, manual + venues_found INTEGER DEFAULT 0, + venues_inserted INTEGER DEFAULT 0, + venues_skipped INTEGER DEFAULT 0, + errors TEXT[], + completed_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +### 3.2 PostGIS Spatial Schema + +```sql +-- Enable extensions +CREATE EXTENSION IF NOT EXISTS postgis; +CREATE EXTENSION IF NOT EXISTS pg_trgm; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- Spatial indexes +CREATE INDEX idx_venues_location_gist ON venues USING GIST (location); +CREATE INDEX idx_neighborhoods_boundary_gist ON neighborhoods USING GIST (boundary); +CREATE INDEX idx_users_home_location_gist ON users USING GIST (home_location); + +-- Spatial helper function: find neighborhood for a point +CREATE OR REPLACE FUNCTION find_neighborhood(lat DOUBLE PRECISION, lng DOUBLE PRECISION, city_name VARCHAR DEFAULT 'Savannah') +RETURNS UUID AS $$ +DECLARE + hood_id UUID; +BEGIN + SELECT n.id INTO hood_id + FROM neighborhoods n + WHERE n.city = city_name + AND ST_Within( + ST_SetSRID(ST_MakePoint(lng, lat), 4326)::geography, + n.boundary + ) + LIMIT 1; + RETURN hood_id; +END; +$$ LANGUAGE plpgsql; + +-- Spatial helper: venues within radius +CREATE OR REPLACE FUNCTION venues_within_radius( + lat DOUBLE PRECISION, + lng DOUBLE PRECISION, + radius_km DOUBLE PRECISION DEFAULT 10.0 +) RETURNS TABLE ( + id UUID, name VARCHAR, distance_meters DOUBLE PRECISION +) AS $$ +BEGIN + RETURN QUERY + SELECT v.id, v.name, + ST_Distance(v.location, ST_SetSRID(ST_MakePoint(lng, lat), 4326)::geography) as distance_meters + FROM venues v + WHERE ST_DWithin(v.location, ST_SetSRID(ST_MakePoint(lng, lat), 4326)::geography, radius_km * 1000) + ORDER BY distance_meters; +END; +$$ LANGUAGE plpgsql; + +-- Full-text search on events + venues +ALTER TABLE events ADD COLUMN search_vector tsvector + GENERATED ALWAYS AS ( + setweight(to_tsvector('english', COALESCE(title, '')), 'A') || + setweight(to_tsvector('english', COALESCE(description, '')), 'B') || + setweight(to_tsvector('english', COALESCE(array_to_string(tags, ' '), '')), 'C') + ) STORED; + +CREATE INDEX idx_events_search ON events USING GIN (search_vector); + +ALTER TABLE venues ADD COLUMN search_vector tsvector + GENERATED ALWAYS AS ( + setweight(to_tsvector('english', COALESCE(name, '')), 'A') || + setweight(to_tsvector('english', COALESCE(description, '')), 'B') + ) STORED; + +CREATE INDEX idx_venues_search ON venues USING GIN (search_vector); +``` + +### 3.3 Redis Key Schema (Extended) + +``` +# Trending (from Phase 1 doc, extended with Savannah neighborhoods) +trending:savannah:all Sorted Set {event_id: hot_score} +trending:savannah:music Sorted Set {event_id: hot_score} +trending:savannah:food_drink Sorted Set {event_id: hot_score} +trending:savannah:arts_culture Sorted Set {event_id: hot_score} +trending:savannah:nightlife Sorted Set {event_id: hot_score} +trending:savannah:pop_up Sorted Set {event_id: hot_score} +trending:savannah:starland Sorted Set {event_id: hot_score} +trending:savannah:historic_district Sorted Set {event_id: hot_score} +# ... one per neighborhood + +# Pulse (per-event real-time activity, 15-min TTL) +pulse:{event_id}:views Sorted Set {timestamp_ms: count} +pulse:{event_id}:clicks Sorted Set {timestamp_ms: count} +pulse:{event_id}:saves Sorted Set {timestamp_ms: count} +pulse:{event_id}:shares Sorted Set {timestamp_ms: count} + +# Sessions +session:{user_id}:{device_id} Hash {refresh_token, expires_at, device_info} + +# Rate limiting +ratelimit:{ip}:{endpoint} String {counter} TTL: 1h + +# Geo-index cache (5-min TTL) +geo:{lat}:{lng}:{radius_km} Set {event_ids} + +# Ambassador +ambassador:leaderboard:{week} Sorted Set {user_id: points} +ambassador:{user_id}:credits String {total_credits} + +# Reddit flywheel +reddit:last_post:{subreddit} String {timestamp} TTL: 24h + +# City launch status +city_launch:{city}:checklist Hash {check_name: value} +``` + +--- + +## 4. API Surface + +### 4.1 REST API Endpoints (api.hotnow.io/v1) + +#### Events + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `GET` | `/events/trending` | Optional | Trending events by city/neighborhood/category | +| `GET` | `/events/nearby` | Optional | Events within radius of lat/lng | +| `GET` | `/events/:id` | Optional | Full event detail with venue + pulse data | +| `GET` | `/events/search` | Optional | Full-text search (tsvector) across events | +| `POST` | `/events/submit` | User | Submit event to admin review queue | +| `POST` | `/events/:id/save` | User | Bookmark event | +| `POST` | `/events/:id/check-in` | User | Check in (feeds trending pulse) | +| `GET` | `/events/:id/pulse` | Optional | Real-time activity data for event | + +#### Venues + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `GET` | `/venues/nearby` | Optional | Venues within radius | +| `GET` | `/venues/:id` | Optional | Venue detail + upcoming events | +| `GET` | `/venues/search` | Optional | Full-text search across venues | +| `GET` | `/venues/:id/busy` | Optional | Current busy level estimate | + +#### Savannah-Specific + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `GET` | `/savannah/neighborhoods` | None | List neighborhoods with counts | +| `GET` | `/savannah/neighborhood/:slug` | None | Events in neighborhood | +| `GET` | `/savannah/corridor` | None | Events along named street (query: `?street=broughton`) | +| `GET` | `/savannah/stats` | Admin | City-level stats (venues, events, users, MAU) | + +#### Discovery & AI + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `GET` | `/discover/feed` | Optional | Personalized feed (AI if Pro, trending if free) | +| `GET` | `/discover/recommendations` | Pro | AI-powered "Best Right Now" picks | +| `POST` | `/discover/action` | Optional | Log view/click/save for trending signals | +| `GET` | `/ai/events` | None | AI-optimized compact event list (<500 tokens) | +| `GET` | `/ai/trending` | None | AI-optimized trending list | + +#### Auth + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `POST` | `/auth/register` | None | Create account (email + password) | +| `POST` | `/auth/login` | None | Login -> JWT access + refresh token | +| `POST` | `/auth/refresh` | Refresh | Get new access token | +| `POST` | `/auth/logout` | Access | Revoke refresh token | +| `POST` | `/auth/oauth/google` | None | Google OAuth login | +| `POST` | `/auth/oauth/apple` | None | Apple Sign In | +| `GET` | `/auth/me` | Access | Current user profile | + +#### SCAD Ambassador + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `POST` | `/ambassador/verify` | User | Submit SCAD email for verification | +| `GET` | `/ambassador/status` | User | Ambassador status, credits, stats | +| `GET` | `/ambassador/leaderboard` | None | Top 10 ambassadors this week | +| `POST` | `/ambassador/submit-venue` | Ambassador | Submit new venue | +| `POST` | `/ambassador/submit-event` | Ambassador | Submit new event | +| `GET` | `/ambassador/activity` | Ambassador | Recent actions + approval status | + +#### Billing + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `GET` | `/billing/plans` | None | Available subscription tiers | +| `POST` | `/billing/subscribe` | User | Create Stripe checkout session | +| `GET` | `/billing/portal` | User | Redirect to Stripe Customer Portal | +| `POST` | `/billing/webhook` | Stripe | Stripe webhook receiver (no auth, verified by signature) | + +#### Admin (Internal) + +| Method | Endpoint | Auth | Description | +|--------|----------|------|-------------| +| `GET` | `/admin/review-queue` | Admin | Events/venues pending review | +| `POST` | `/admin/review/:id` | Admin | Approve/edit/reject submission | +| `POST` | `/admin/recalculate-trending` | Admin | Force trend recalculation | +| `GET` | `/admin/stats` | Admin | Platform metrics dashboard | +| `GET` | `/admin/city-launch-checklist/:city` | Admin | Seed completeness check | +| `POST` | `/admin/seed-city` | Admin | Trigger seed ingestion pipeline | + +### 4.2 MCP Tools (mcp.hotnow.io) + +| Tool | Parameters | Returns | +|------|-----------|---------| +| `hotnow_search_events` | `query`, `category?`, `city`, `date_from?`, `date_to?`, `limit` | JSON array of events with venue info | +| `hotnow_get_venue` | `venue_id` or `venue_name` | Full venue detail + upcoming events | +| `hotnow_trending` | `city`, `neighborhood?`, `category?`, `limit` | JSON array ranked by trending_score | +| `hotnow_neighborhood` | `city` | GeoJSON boundaries + venue counts per neighborhood | +| `hotnow_recommendations` | `city`, `preferences`, `lat`, `lng` | AI-ranked personalized recommendations | +| `hotnow_venue_search` | `query`, `city`, `venue_type?`, `limit` | JSON array of matching venues | +| `hotnow_health` | (none) | Service health status + DB/Redis connectivity | + +### 4.3 Auth Flow + +``` +1. User registers/logs in -> receives: + - access_token (JWT, HS256, 15-min expiry, contains: sub, tier, is_admin) + - refresh_token (opaque, 30-day expiry, stored in Redis: session:{user_id}:{device_id}) + +2. Every API call: Authorization: Bearer + +3. Access token expires -> POST /auth/refresh { "refresh_token": "..." } + -> new access_token + rotated refresh_token + +4. Logout -> delete Redis session key, revoke refresh token + +5. Anonymous users: session_id (UUID v4, stored in localStorage) + - Limited rate: 100 requests/hour + - Cannot save/bookmark (no persistence without account) +``` + +### 4.4 Rate Limits + +| Tier | Requests/Hour | AI Recs | Save Limit | Search Limit | +|------|--------------|---------|------------|-------------| +| Anonymous | 100 | N/A | 0 | 10/hr | +| Explorer (Free) | 300 | 10/month | 50 | 50/hr | +| Pro ($4.99/mo) | 1000 | Unlimited | Unlimited | 200/hr | +| Concierge ($19.99/mo) | 5000 | Unlimited + Concierge Chat | Unlimited | 500/hr | +| Ambassador | 500 | 50/month (or per credits) | 50 | 100/hr | +| Business | 500 | N/A | N/A | N/A | +| Admin | Unlimited | N/A | N/A | N/A | + +--- + +## 5. Ranking Algorithm + +### 5.1 Complete Formula + +``` +HOT_SCORE(event_id, user_context) = ( + FRESHNESS(event_id) * W_f (0.30) + + VELOCITY(event_id) * W_v (0.25) + + SOCIAL_PROOF(event_id) * W_s (0.20) + + CONTEXTUAL(event_id, user_context) * W_c (0.15) + + MANUAL_BOOST(event_id) * W_m (0.10) +) * CITY_NORMALIZATION(city) * TIME_RAMP(event_id) +``` + +### 5.2 Signal Detail + +#### FRESHNESS (30%) +``` +f_created = max(0, 1.0 - hours_since_created / 168) # Linear decay over 7 days +f_updated = max(0, 1.0 - hours_since_updated / 48) # Linear decay over 2 days +f_checkin = min(1.0, check_ins_last_24h / 20.0) # Cap at 20 check-ins + +FRESHNESS = 0.4 * f_created + 0.4 * f_updated + 0.2 * f_checkin +``` + +#### VELOCITY (25%) +```python +# Pulse metrics from Redis (2-hour rolling window) +views_per_min = redis.zcount(f"pulse:{eid}:views", now-7200, now) / 120 +clicks_per_min = redis.zcount(f"pulse:{eid}:clicks", now-7200, now) / 120 +saves_per_min = redis.zcount(f"pulse:{eid}:saves", now-7200, now) / 120 + +# Acceleration: compare current velocity to previous 2-hour window +accel = (views_per_min + clicks_per_min * 1.5 + saves_per_min * 2.0) / prev_velocity + +VELOCITY = clamp(normalize(accel), 0.0, 1.0) +``` + +#### SOCIAL_PROOF (20%) +```python +# Super Search v2 crawl results (cache 15 min) +ig_mentions = social_cache.get(f"ig:{venue_name}", 0) +tt_mentions = social_cache.get(f"tt:{venue_name}", 0) +reddit_mentions = social_cache.get(f"reddit:{venue_name}", 0) + +SOCIAL_PROOF = ( + 0.40 * min(1.0, log1p(ig_mentions) / 5.0) + + 0.35 * min(1.0, log1p(tt_mentions) / 4.0) + + 0.25 * min(1.0, log1p(reddit_mentions) / 3.0) +) +``` + +#### CONTEXTUAL (15%) +```python +# Weather (OpenWeatherMap API, cache 30 min) +weather_score = { + "clear": 1.0, "clouds": 0.8, "rain": 0.5, "thunderstorm": 0.2 +}.get(current_weather, 0.5) + +# Time of day match +time_scores = { + (6, 11): "food_drink", # Breakfast/brunch + (11, 14): "food_drink", # Lunch + (14, 17): "arts_culture", # Afternoon activities + (17, 21): "all", # Evening - everything + (21, 2): "nightlife", # Late night + (2, 6): "nightlife", # Very late / after-hours +} + +# Day of week boost +weekend_boost = 1.2 if current_day in [5, 6] else 1.0 # Friday/Saturday + +CONTEXTUAL = ( + 0.40 * weather_score + + 0.30 * category_time_match + + 0.15 * weekend_boost + + 0.15 * proximity_boost # Distance decay: 1.0 at 0km, 0.1 at 50km +) +``` + +#### MANUAL_BOOST (10%) +```python +# From business_features table +feature = get_active_feature(venue_id) +if feature.type == "featured_placement": + MANUAL_BOOST = 0.85 # Strong boost +elif feature.type == "event_boost": + MANUAL_BOOST = 0.50 # Event-specific boost +else: + MANUAL_BOOST = 0.0 +``` + +### 5.3 City Normalization (Savannah-Specific) + +``` +CITY_NORMALIZATION = log10(active_users_in_city + 1) / log10(total_events_in_city + 20) +``` + +For Savannah at launch: +- `active_users` = 50 (early adopters) +- `total_events` = 150 (seeded) +- `CITY_NORMALIZATION` = log10(51) / log10(170) = 1.71 / 2.23 = **0.77** + +This means Savannah events get a 0.77x multiplier initially -- lower than a mature city, but workable. As user count grows, normalization approaches 1.0. The "+20" floor in the denominator prevents division-by-zero at launch. + +### 5.4 Time Ramp + +``` +TIME_RAMP(event) = + 0.5 if hours_until_start > 24 + 0.5 + 0.5 * (24 - hours_until_start) / 24 if 0 <= hours_until_start <= 24 + 1.0 - 0.3 * hours_since_start / 4 if hours_since_start <= 4 + 0.3 if hours_since_start > 4 +``` + +Peaks at event start time, decays after 4 hours post-start. + +### 5.5 Fallback for Sparse Data (Savannah Launch Mode) + +When social signals are low (common for new city launches): + +```python +def hot_score_with_fallback(event_id, user_context): + social = SOCIAL_PROOF(event_id) + velocity = VELOCITY(event_id) + + # If social + velocity signals are both weak, freshness dominates + if social < 0.15 and velocity < 0.15: + # Rebalance weights: freshness at 50%, manual at 20% + return ( + 0.50 * FRESHNESS(event_id) + + 0.15 * velocity + + 0.10 * social + + 0.15 * CONTEXTUAL(event_id, user_context) + + 0.10 * MANUAL_BOOST(event_id) + ) * CITY_NORMALIZATION * TIME_RAMP(event_id) + + return full_formula(event_id, user_context) +``` + +### 5.6 Ranking Worker Implementation + +File: `/root/hotnow-api/trend_worker.py` + +```python +#!/usr/bin/env python3 +"""ARQ worker: recalculates trending scores every 5 minutes.""" + +import asyncio +import math +import asyncpg +import redis.asyncio as redis +from datetime import datetime, timedelta +from arq import cron +from arq.connections import RedisSettings + +async def recalculate_trending(ctx): + """Recalculate HOT_SCORE for all active events.""" + conn = await asyncpg.connect(ctx["DATABASE_URL"]) + r = await redis.from_url(ctx["REDIS_URL"]) + + events = await conn.fetch(""" + SELECT e.id, e.created_at, e.updated_at, e.start_time, + e.category, v.id as venue_id, v.city + FROM events e + JOIN venues v ON e.venue_id = v.id + WHERE e.is_active = true + AND e.start_time >= NOW() - INTERVAL '24 hours' + """) + + for e in events: + score = calculate_score(e, conn, r) + await conn.execute( + "UPDATE events SET trending_score = $1, updated_at = NOW() WHERE id = $2", + score, e["id"] + ) + # Update Redis sorted sets + city = e["city"].lower() + category = e["category"] + await r.zadd(f"trending:{city}:all", {str(e["id"]): score}) + await r.zadd(f"trending:{city}:{category}", {str(e["id"]): score}) + + await conn.close() + await r.close() + +async def cleanup_old_pulses(ctx): + """Remove pulse data older than 6 hours.""" + r = await redis.from_url(ctx["REDIS_URL"]) + cutoff = (datetime.utcnow() - timedelta(hours=6)).timestamp() + keys = await r.keys("pulse:*") + for key in keys: + await r.zremrangebyscore(key, 0, cutoff) + await r.close() + +class WorkerSettings: + redis_settings = RedisSettings.from_dsn(os.environ["REDIS_URL"]) + cron_jobs = [ + cron(recalculate_trending, minute={0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55}), + cron(cleanup_old_pulses, hour=range(0, 24, 6)), # Every 6 hours + ] +``` + +--- + +## 6. Implementation Roadmap + +### 6.1 4-Week Sprint Plan (Savannah Launch) + +#### Phase 0: Foundation -- Week 1 (40 hours) + +| Day | Task | Hours | Files/Outputs | Dependencies | +|-----|------|-------|---------------|-------------| +| Mon | DNS + Caddy: Configure hotnow.io subdomains at Cloudflare | 2 | Cloudflare DNS panel, Caddyfile | Domain owned | +| Mon | PostgreSQL: Create hotnow DB, run full schema migration | 4 | Migration files in `/root/hotnow-api/migrations/` | PostgreSQL running | +| Mon | PostGIS: Install extension, create spatial indexes, neighborhoods table | 3 | Spatial schema SQL | PostgreSQL | +| Tue | Redis: Configure key namespace, set up trending key pattern | 2 | Redis config | Redis running | +| Tue | PWA shell: Initialize SvelteKit project with Savannah viewport | 6 | `/var/www/hotnow-app/` scaffold | Node.js | +| Wed | PWA: Leaflet.js map implementation with OSM tiles | 6 | Map component, marker clustering | SvelteKit | +| Wed | PWA: Bottom nav, filter chips, category bar | 4 | UI components | Map done | +| Thu | API: Core FastAPI routes (events, venues, trending) | 6 | `/root/hotnow-api/routers/` | Postgres schema | +| Thu | API: Auth flow (JWT, refresh, register/login) | 4 | `/root/hotnow-api/auth.py` | Users table | +| Fri | Seed data: Build seed scraper for Savannah (Yelp + Google Places) | 8 | `/root/hotnow-api/city_seed.py` | API keys | +| Sat | Seed data: Run ingestion, verify 200+ venues in DB | 3 | Verified in psql | Scraper done | +| **Subtotal** | | **48h** | | | + +#### Phase 1: Core Engine -- Week 2 (45 hours) + +| Day | Task | Hours | Files/Outputs | Dependencies | +|-----|------|-------|---------------|-------------| +| Mon | Ranking engine: Implement core HOT_SCORE calculator | 6 | `/root/hotnow-api/ranking.py` | Redis + Postgres | +| Mon | Ranking: Signal modules (freshness, velocity, social, contextual) | 4 | `/root/hotnow-api/signals/*.py` | Ranking core | +| Tue | Ranking: ARQ worker with 5-min cron, Redis population | 4 | `/root/hotnow-api/trend_worker.py` | Ranking done | +| Tue | Ranking: Systemd service for ARQ worker | 1 | `/etc/systemd/system/hotnow-worker.service` | ARQ | +| Tue | Event aggregator: Eventbrite API connector | 4 | `/root/hotnow-api/aggregators/eventbrite.py` | API key | +| Wed | Event aggregator: Ticketmaster API connector | 4 | `/root/hotnow-api/aggregators/ticketmaster.py` | API key | +| Wed | Event aggregator: Meetup API connector | 3 | `/root/hotnow-api/aggregators/meetup.py` | API key | +| Thu | AI curation: Venue metadata enrichment via deepseek-v4-pro | 5 | `/root/hotnow-api/ai_curator.py` | Venues seeded | +| Thu | AI curation: Event description generation pipeline | 3 | Extension of ai_curator.py | Events | +| Fri | Testing: End-to-end test of ranking pipeline with seed data | 6 | Test output, score verification | All above | +| Fri | PWA: Wire map view to API endpoints (real data) | 5 | Frontend API integration | API running | +| **Subtotal** | | **45h** | | | + +#### Phase 2: User-Facing -- Week 3 (50 hours) + +| Day | Task | Hours | Files/Outputs | Dependencies | +|-----|------|-------|---------------|-------------| +| Mon | PWA: Event detail page with cover image, venue, actions | 6 | `src/routes/event/[id]/+page.svelte` | API | +| Mon | PWA: Discovery feed (vertical scroll EventCards) | 4 | `src/routes/discover/+page.svelte` | API | +| Tue | PWA: "Best Right Now" AI recommendation UI | 5 | AI rec component + API integration | AI curation | +| Tue | PWA: User auth (login, register, profile) | 5 | Auth pages + auth store | Auth API | +| Wed | PWA: Neighborhood browsing (map clustering + list view) | 6 | Neighborhood components + API | PostGIS | +| Wed | PWA: Search with filters (category, date, neighborhood) | 4 | Search page + API | Events | +| Thu | Stripe: Subscription checkout flow + webhooks | 5 | Stripe integration in API + PWA | Stripe keys | +| Thu | Stripe: Business featured placement + event boost purchase | 3 | Business feature purchase flow | Stripe | +| Fri | Structured data: JSON-LD, OG tags, Twitter cards | 4 | SvelteKit components | PWA pages | +| Fri | Sitemap generator for search engines | 2 | `/root/hotnow-api/sitemap_generator.py` + cron | Data | +| Fri | PWA: Offline support (service worker, IndexedDB cache) | 4 | Service worker, offline helpers | PWA shell | +| Sat | Testing: Cross-browser mobile testing (iOS Safari, Chrome Android) | 3 | Test report | PWA done | +| **Subtotal** | | **51h** | | | + +#### Phase 3: Growth & Launch -- Week 4 (40 hours) + +| Day | Task | Hours | Files/Outputs | Dependencies | +|-----|------|-------|---------------|-------------| +| Mon | Reddit flywheel: PRAW setup, AI post generator, templates | 5 | `/root/hotnow-api/reddit_worker.py` | Events data | +| Mon | Reddit: Cron schedule + content calendar + UTM tracking | 2 | `/etc/cron.d/hotnow-reddit` | Reddit API | +| Tue | SCAD ambassador: Verification flow (magic link + domain check) | 5 | Auth + ambassador endpoints | Users table | +| Tue | SCAD ambassador: Dashboard (credits, referrals, submissions) | 4 | `src/routes/ambassador/+page.svelte` | Auth | +| Wed | SCAD ambassador: Incentive system (Stripe coupon generation) | 3 | Stripe coupon logic | Stripe | +| Wed | MCP server: Implement all 7 tools with FastMCP | 6 | `/opt/hotnow-mcp/server.py` | API + Postgres | +| Thu | MCP server: Systemd service, Caddy route, testing | 2 | systemd + Caddy config | MCP server | +| Thu | City-launch playbook: Automate checklist, seed pipeline script | 4 | Refine city_seed.py, checklist endpoint | Seed data | +| Fri | Testing: Full integration test -- map to API to DB to Redis | 4 | Test suite | All above | +| Fri | Bug fixes + polish from testing | 4 | Various | Testing | +| Sat | Documentation: Deployment runbook, API docs, admin guide | 3 | This document + FastAPI /docs | All above | +| Sat | Beta launch: Release to 20-50 SCAD student early users | 2 | Live deployment | All above | +| **Subtotal** | | **44h** | | | + +#### Grand Total: ~188 hours (4 weeks) + +### 6.2 Effort Breakdown by Component + +| Component | Hours | % of Total | +|-----------|-------|-----------| +| Database + PostGIS | 10 | 5% | +| PWA Frontend | 42 | 22% | +| REST API Backend | 20 | 11% | +| Ranking Engine | 15 | 8% | +| Event Aggregators | 11 | 6% | +| AI Curation Pipeline | 8 | 4% | +| Auth + User System | 12 | 6% | +| Stripe Billing | 8 | 4% | +| Structured Data Layer | 6 | 3% | +| MCP Server | 8 | 4% | +| Reddit Flywheel | 7 | 4% | +| SCAD Ambassador | 12 | 6% | +| City Launch Playbook | 4 | 2% | +| Seed Data Pipeline | 11 | 6% | +| Testing + QA | 10 | 5% | +| Documentation | 4 | 2% | +| **Total** | **188** | **100%** | + +### 6.3 Critical Path + +``` +PostgreSQL schema -> Seed data pipeline -> API endpoints -> Ranking engine -> +PWA shell -> Map view -> Discovery feed -> Event detail -> Stripe -> +Neighborhood browsing -> Reddit flywheel -> SCAD ambassador -> MCP server -> +Testing -> Launch +``` + +Bottleneck risk: Seed data pipeline (depends on Yelp/Google API keys being provisioned). Mitigation: start API key provisioning on Day 0. + +--- + +## 7. Infrastructure & Deployment + +### 7.1 DNS Configuration (Cloudflare) + +``` +A hotnow.io -> 152.53.192.33 (TTL: Auto) +A app.hotnow.io -> 152.53.192.33 +A api.hotnow.io -> 152.53.192.33 +A admin.hotnow.io -> 152.53.192.33 +A mcp.hotnow.io -> 152.53.192.33 +CNAME www.hotnow.io -> hotnow.io +``` + +### 7.2 Caddy Configuration + +Existing routes in `/etc/caddy/Caddyfile` (lines 359-383) already cover: +- `hotnow.io, www.hotnow.io` -> `/var/www/hotnow` +- `app.hotnow.io` -> `/var/www/hotnow-app` +- `api.hotnow.io` -> reverse_proxy `127.0.0.1:8001` +- `admin.hotnow.io` -> `/var/www/hotnow-admin` + +Add after line 383: + +``` +# HotNow MCP Server +mcp.hotnow.io { + reverse_proxy 127.0.0.1:8901 + header Access-Control-Allow-Origin * + header Access-Control-Allow-Methods "GET, POST, OPTIONS" +} +``` + +### 7.3 Systemd Services + +| Service | File | Status | +|---------|------|--------| +| **hotnow-api** | `/etc/systemd/system/hotnow-api.service` | Running (partial impl) | +| **hotnow-worker** | `/etc/systemd/system/hotnow-worker.service` | To create | +| **hotnow-mcp** | `/etc/systemd/system/hotnow-mcp.service` | To create | +| **super-search** | `/etc/systemd/system/super-search.service` | Running (8899) | +| **postgresql** | System package | Running (5432) | +| **redis-server** | System package | Running (6379) | + +#### hotnow-worker.service (ARQ background tasks) + +```ini +[Unit] +Description=HotNow ARQ Worker (Trending, Ingestion, Notifications) +After=redis-server.service network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/root/hotnow-api +EnvironmentFile=/root/hotnow-api/.env +ExecStart=/root/hotnow-api/venv/bin/arq trend_worker.WorkerSettings +Restart=always +RestartSec=3 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +``` + +#### Service Startup Order + +```bash +systemctl enable hotnow-api hotnow-worker hotnow-mcp +systemctl start hotnow-api +systemctl start hotnow-worker +systemctl start hotnow-mcp +``` + +### 7.4 Server Environment + +File: `/root/hotnow-api/.env` + +```bash +# Database +DATABASE_URL=postgresql://hotnow_app:@localhost:5432/hotnow +REDIS_URL=redis://localhost:6379/0 + +# Auth +JWT_SECRET= +JWT_REFRESH_SECRET= + +# Stripe +STRIPE_SECRET_KEY=sk_live_... +STRIPE_WEBHOOK_SECRET=whsec_... + +# Super Search v2 +SUPER_SEARCH_ENDPOINT=http://127.0.0.1:8899/mcp + +# AI (admin-ai LiteLLM) +LITELLM_API_KEY= +LITELLM_BASE_URL=https://admin-ai.itpropartner.com/v1 + +# External APIs +YELP_FUSION_API_KEY= +GOOGLE_PLACES_API_KEY= +EVENTBRITE_API_TOKEN= +TICKETMASTER_API_KEY= +MEETUP_API_KEY= + +# Reddit (PRAW) +REDDIT_CLIENT_ID= +REDDIT_CLIENT_SECRET= +REDDIT_USERNAME=hotnow_app +REDDIT_PASSWORD= + +# Weather (OpenWeatherMap) +OPENWEATHER_API_KEY= + +# Email +RESEND_API_KEY= + +# CORS +CORS_ORIGINS=https://app.hotnow.io,https://admin.hotnow.io + +# Environment +ENVIRONMENT=production +CITY=Savannah +DEBUG=false +``` + +### 7.5 Database Backup Strategy + +| Frequency | Method | Location | Retention | +|-----------|--------|----------|-----------| +| Hourly | WAL archiving | `/var/lib/postgresql/wal_archive/` | 24h | +| Daily | `pg_dump hotnow` | `rclone -> Wasabi S3 (hotnow-backups bucket)` | 30 days | +| Weekly | Full dump + schema | `rclone -> Wasabi S3` | 90 days | +| Monthly | Full + config files | `rclone -> Wasabi S3` | 12 months | + +#### Backup Script + +```bash +#!/bin/bash +# /root/hotnow-api/scripts/backup.sh +DATE=$(date +%Y-%m-%d_%H%M) +BACKUP_DIR=/var/backups/hotnow +mkdir -p $BACKUP_DIR + +# Database dump +pg_dump -U hotnow_app -d hotnow -Fc > $BACKUP_DIR/hotnow_$DATE.dump + +# Redis snapshot +redis-cli SAVE +cp /var/lib/redis/dump.rdb $BACKUP_DIR/redis_$DATE.rdb + +# Config backup +tar czf $BACKUP_DIR/config_$DATE.tar.gz \ + /root/hotnow-api/.env \ + /etc/caddy/Caddyfile \ + /etc/systemd/system/hotnow-*.service + +# Upload to Wasabi S3 +rclone copy $BACKUP_DIR wasabi:hotnow-backups/daily/$DATE/ + +# Cleanup old local backups (>3 days) +find $BACKUP_DIR -type f -mtime +3 -delete +``` + +Cron: `0 3 * * * root /root/hotnow-api/scripts/backup.sh` + +### 7.6 Restore Procedure + +```bash +# 1. Stop services +systemctl stop hotnow-api hotnow-worker hotnow-mcp + +# 2. Restore PostgreSQL +dropdb -U hotnow_app hotnow +createdb -U hotnow_app hotnow +pg_restore -U hotnow_app -d hotnow /path/to/hotnow_YYYY-MM-DD_HHMM.dump + +# 3. Restore Redis +systemctl stop redis-server +cp /path/to/redis_YYYY-MM-DD_HHMM.rdb /var/lib/redis/dump.rdb +systemctl start redis-server + +# 4. Start services +systemctl start hotnow-api hotnow-worker hotnow-mcp + +# 5. Verify +curl https://api.hotnow.io/health +curl https://mcp.hotnow.io/health +``` + +--- + +## 8. Integration Points + +### 8.1 Super Search v2 Integration + +| Integration | Endpoint/Pattern | Purpose | +|-------------|-----------------|---------| +| Social mention crawling | `web_search("venue name Savannah GA")` with time_range filter | Count Instagram/TikTok/Reddit mentions for social proof signal | +| Event discovery | `web_search_news("Savannah GA events tonight")` | Discover events not on Eventbrite/Ticketmaster | +| Venue enrichment | `web_search("venue name")` + `web_extract(venue_url)` | Enrich venue descriptions, hours, social links | +| API health dependency | `health_check()` call before HotNow health endpoint | Propagate Super Search status in HotNow health | +| Circuit breaker awareness | `circuit_status()` before bulk operations | Avoid hammering degraded providers during ingest | + +Super Search endpoint: `http://127.0.0.1:8899/mcp` (Streamable HTTP, FastMCP 4.x) + +### 8.2 admin-ai (LiteLLM) Integration + +| Integration | Model | Purpose | +|-------------|-------|---------| +| AI curation pipeline | deepseek-v4-pro | Generate venue descriptions, tags, quality ratings | +| "Best Right Now" recommendations | deepseek-v4-pro | Personalized event recommendations based on user preferences, weather, time, location | +| Concierge chat | deepseek-v4-pro | AI-powered concierge for itinerary building | +| Reddit post generation | deepseek-v4-pro | Generate daily "What's Hot Tonight" post content | +| Event description polish | deepseek-v4-pro | Clean up and enhance user-submitted event descriptions | + +Base URL: `https://admin-ai.itpropartner.com/v1` +API Key: `LITELLM_API_KEY` in env + +### 8.3 Stripe Integration + +| Integration | Endpoint | Purpose | +|-------------|----------|---------| +| Consumer subscriptions | Stripe Checkout + Customer Portal | Pro ($4.99/mo) and Concierge ($19.99/mo) | +| Business featured placement | Stripe Checkout one-time + recurring | $97/mo featured venue placement | +| Event boost | Stripe Checkout one-time | $47/event one-time boost | +| Ambassador credits | Stripe Coupons API | Generate discount coupons for ambassador credits | +| Webhooks | `POST /billing/webhook` | Handle subscription lifecycle (created, updated, canceled, payment_failed) | + +### 8.4 Mapbox / Leaflet.js + +| Integration | Purpose | +|-------------|---------| +| Leaflet.js + OpenStreetMap | Primary map tiles (free, no API key, offline-cacheable) | +| Mapbox GL JS (optional future) | Satellite imagery, 3D buildings, dark theme tiles | +| Leaflet.markercluster | Cluster venue markers by proximity | +| Leaflet GeoJSON | Render neighborhood boundaries | + +Decision: Start with Leaflet.js + OSM tiles (free). Swap to Mapbox when revenue supports it (>$500 MRR). Mapbox free tier: 50K monthly loads. + +### 8.5 External API Dependencies + +| API | Free Tier | Rate Limit | HotNow Usage | +|-----|----------|------------|-------------| +| Yelp Fusion | 5K calls/day | 500/hr | Seed scraper (one-time per city) | +| Google Places | $200/mo credit | Varies | Seed scraper, venue enrichment | +| Eventbrite | Free | 1K/hr | Ongoing event ingestion | +| Ticketmaster | Free (Discovery API) | 5K/day | Ongoing event ingestion | +| Meetup | Free (GraphQL) | Limited | Ongoing event ingestion | +| OpenWeatherMap | 1K calls/day free | 60/min | Contextual weather signal | +| Reddit (PRAW) | Free | 60 posts/min (OAuth) | Daily flywheel posts | +| Resend (email) | 3K emails/mo free | N/A | Auth magic links, notifications | + +--- + +## 9. Testing & QA Strategy + +### 9.1 Test Categories + +| Category | Approach | Coverage Target | Tools | +|----------|----------|----------------|-------| +| **Unit Tests** | pytest for ranking engine, signal calcs, API models | 80% | pytest, pytest-cov | +| **Integration Tests** | httpx async test client against live API | Core flows (auth, trending, search, submit) | pytest-asyncio, httpx | +| **Database Tests** | Test against test DB with seed data fixtures | All PostGIS spatial queries, trending calc | pytest-postgresql | +| **Redis Tests** | Test against test Redis with mock pulse data | Trending sorted sets, rate limiting, sessions | fakeredis or test instance | +| **PWA E2E Tests** | Manual mobile testing | Critical paths (browse -> view event -> save) | iOS Safari, Chrome Android | +| **Performance Tests** | Apache Bench / wrk against trending endpoint | <200ms p95 for Redis-backed trending | wrk, ab | +| **Load Tests** | Simulate 100 concurrent users browsing map | API handles 100 req/s without degradation | locust | +| **Accessibility** | Lighthouse audit | PWA score >90 | Lighthouse CLI | +| **Security** | OWASP ZAP basic scan + manual JWT/CORS review | No critical/high findings | ZAP, manual | + +### 9.2 Test Data + +Seed fixture file: `/root/hotnow-api/tests/fixtures/savannah_seed.sql` + +Contains: +- 50 venues across all Savannah neighborhoods +- 30 events (various categories, some starting in next 24h) +- 5 users (1 admin, 2 pro, 1 explorer, 1 ambassador) +- 2 neighborhoods (Historic District, Starland) with GeoJSON boundaries +- Redis fixtures: pulse data for 10 events, trending sorted sets + +### 9.3 Critical Test Cases + +| # | Test | Expected | Priority | +|---|------|----------|----------| +| 1 | Anonymous user sees trending events near Savannah | Events returned with scores, paginated | P0 | +| 2 | Pro user gets AI "Best Right Now" picks | Personalized JSON with 5 picks | P0 | +| 3 | Ranking engine recalculates scores every 5 min | trending_score column updates, Redis sorted set updates | P0 | +| 4 | SCAD email verification works | Magic link sent, account marked verified, 1-month credit issued | P0 | +| 5 | Reddit flywheel generates daily post | Markdown post with top events + UTM link output to log | P1 | +| 6 | Stripe subscription flow end-to-end | Checkout -> webhook -> tier upgrade -> UI reflects Pro | P1 | +| 7 | Neighborhood browsing returns correct events | Starland query returns only Starland venues | P1 | +| 8 | Corridor browsing (Broughton St) | Only venues within 250m of Broughton St linestring | P1 | +| 9 | SEO: sitemap-savannah.xml accessible | Valid XML with event URLs, lastmod dates | P1 | +| 10 | MCP tools return correct JSON | hotnow_trending returns valid ranked event list | P1 | +| 11 | Offline PWA: cached events visible without network | Previously loaded events show in discovery feed | P2 | +| 12 | Anti-gaming: velocity cap prevents score manipulation | Burst engagement throttled to previous rate | P2 | + +### 9.4 Pre-Launch Checklist + +| Check | Method | Pass Criteria | +|-------|--------|---------------| +| All systemd services running | `systemctl status hotnow-api hotnow-worker hotnow-mcp` | All "active (running)" | +| Health endpoints responding | `curl https://api.hotnow.io/health` | HTTP 200, all services healthy | +| SSL valid on all subdomains | `curl -I https://app.hotnow.io` | No cert errors, HSTS header present | +| 200+ Savannah venues in DB | `SELECT COUNT(*) FROM venues WHERE city='Savannah'` | >=200 | +| 50+ upcoming events | `SELECT COUNT(*) FROM events WHERE start_time > NOW() AND start_time < NOW() + INTERVAL '7 days'` | >=50 | +| Trending scores populated | `redis-cli ZCARD trending:savannah:all` | >=30 | +| Stripe webhook receiving | Check Stripe dashboard -> Webhooks -> Recent deliveries | All 200 OK | +| PWA Lighthouse score | `lighthouse https://app.hotnow.io --preset=perf` | Performance >80, PWA >90 | +| Reddit cron configured | `cat /etc/cron.d/hotnow-reddit` | Correct schedule, valid script path | +| Backup cron configured | `cat /etc/cron.d/hotnow-backup` | Daily at 3am | +| Sitemap accessible | `curl https://hotnow.io/sitemap-savannah.xml` | HTTP 200, valid XML | + +--- + +## 10. Operational Runbook + +### 10.1 Monitoring + +#### Health Checks + +| Endpoint | Check | Alert Threshold | +|----------|-------|----------------| +| `https://api.hotnow.io/health` | API responds 200, DB + Redis connected | 3 consecutive failures | +| `https://mcp.hotnow.io/health` | MCP responds 200 | 3 consecutive failures | +| `https://hotnow.io` | Landing page loads | 2 consecutive failures | +| `https://app.hotnow.io` | PWA loads (200) | 2 consecutive failures | +| Super Search v2 :8899 | health_check() tool returns all healthy | Any provider degraded >5 min | +| PostgreSQL :5432 | `pg_isready -U hotnow_app` | Connection refused >1 min | +| Redis :6379 | `redis-cli PING` | PONG not returned >1 min | + +Uptime Kuma: Add monitors at `uptimekuma.itpropartner.com` for all endpoints above. + +#### Key Metrics to Monitor + +| Metric | Source | Warning | Critical | +|--------|--------|---------|----------| +| API response time (p95) | Caddy access log / app metrics | >500ms | >2s | +| Trending score recalc cycle time | ARQ worker log | >3 min | >5 min (missed cycle) | +| Redis memory usage | `redis-cli INFO memory` | >200MB | >230MB (approaching 256MB limit) | +| PostgreSQL connection count | `pg_stat_activity` | >80 | >100 (default max) | +| Stripe webhook failure rate | Stripe dashboard | >5% | >10% | +| Event ingestion failures | Seed ingestion log | >20% of fetches fail | >50% | +| Reddit post success rate | Reddit worker log | Any post fails | 3 consecutive failures | +| Yelp/Google API quota usage | API dashboards | >70% daily quota | >90% daily quota | +| Disk usage | `df -h /` | >70% | >85% | + +### 10.2 Logging + +All services log to journald. Key log files: + +```bash +# API logs +journalctl -u hotnow-api -f + +# Worker logs (trending, ingestion, reddit) +journalctl -u hotnow-worker -f + +# MCP server logs +journalctl -u hotnow-mcp -f + +# Caddy access logs +journalctl -u caddy -f + +# Reddit flywheel log (cron output) +tail -f /var/log/hotnow-reddit.log + +# Seed ingestion log (cron output) +tail -f /var/log/hotnow-seed.log + +# PostgreSQL logs +journalctl -u postgresql -f +``` + +### 10.3 Common Operations + +#### Force Trending Recalculation + +```bash +curl -X POST https://api.hotnow.io/admin/recalculate-trending \ + -H "Authorization: Bearer " +``` + +#### Manually Trigger Reddit Post + +```bash +/root/hotnow-api/venv/bin/python3 /root/hotnow-api/reddit_worker.py --force +``` + +#### Seed a New City + +```bash +/root/hotnow-api/venv/bin/python3 /root/hotnow-api/city_seed.py --city="Charleston" --state="SC" +``` + +#### Verify Ranking Scores + +```bash +# Check Redis sorted set +redis-cli ZREVRANGE trending:savannah:all 0 9 WITHSCORES + +# Check DB +psql -U hotnow_app -d hotnow -c \ + "SELECT title, trending_score FROM events WHERE is_active=true ORDER BY trending_score DESC LIMIT 10;" +``` + +#### Check Ambassador Stats + +```sql +SELECT u.display_name, a.total_referrals, a.total_submissions, a.total_credits_earned +FROM ambassadors a +JOIN users u ON a.user_id = u.id +ORDER BY a.total_credits_earned DESC +LIMIT 10; +``` + +### 10.4 Incident Response + +#### Scenario 1: API is down / unresponsive + +```bash +# 1. Check service status +systemctl status hotnow-api +journalctl -u hotnow-api --since "5 min ago" + +# 2. Common causes: +# - PostgreSQL unreachable -> check pg_isready +# - Port conflict -> check `ss -tlnp | grep 8001` +# - Python dependency broke -> check .env file, reinstall venv + +# 3. Restart if needed +systemctl restart hotnow-api +``` + +#### Scenario 2: Trending scores not updating + +```bash +# 1. Check worker status +systemctl status hotnow-worker +journalctl -u hotnow-worker --since "30 min ago" + +# 2. Manual trigger +curl -X POST https://api.hotnow.io/admin/recalculate-trending \ + -H "Authorization: Bearer " + +# 3. If Redis is full (allkeys-lru evicting trending keys): +redis-cli INFO memory +redis-cli CONFIG SET maxmemory 512mb # Double if needed +``` + +#### Scenario 3: Seed scraper failing + +```bash +# Check API key validity +curl -H "Authorization: Bearer $YELP_FUSION_API_KEY" \ + "https://api.yelp.com/v3/businesses/search?location=Savannah,GA&limit=1" + +# Check quota usage on Yelp/Google developer dashboards +# If rate-limited: wait until reset, then retry with lower batch size +``` + +#### Scenario 4: Reddit posts not appearing + +```bash +# Check if shadowbanned by posting manually via Reddit web UI +# Check PRAW credentials +/root/hotnow-api/venv/bin/python3 -c " +import praw +r = praw.Reddit(client_id='...', client_secret='...', user_agent='HotNow/1.0') +print(r.user.me()) +" +``` + +### 10.5 Backup / Restore (Detailed) + +#### Automated Backups + +File: `/etc/cron.d/hotnow-backup` + +``` +# Daily database backup at 3am ET +0 3 * * * root /root/hotnow-api/scripts/backup.sh >> /var/log/hotnow-backup.log 2>&1 + +# Weekly Redis RDB backup (Sunday 4am) +0 4 * * 0 root cp /var/lib/redis/dump.rdb /var/backups/hotnow/redis_weekly_$(date +\%Y-\%m-\%d).rdb +``` + +#### Restore from Backup + +```bash +#!/bin/bash +# /root/hotnow-api/scripts/restore.sh +# Example: restore.sh 2026-08-11_0300 + +BACKUP_DATE=$1 +BACKUP_DIR=/var/backups/hotnow + +# Stop all HotNow services +systemctl stop hotnow-api hotnow-worker hotnow-mcp + +# Restore PostgreSQL +dropdb -U hotnow_app hotnow --if-exists +createdb -U hotnow_app hotnow +pg_restore -U hotnow_app -d hotnow -j 4 $BACKUP_DIR/hotnow_$BACKUP_DATE.dump + +# Restore Redis +systemctl stop redis-server +cp $BACKUP_DIR/redis_$BACKUP_DATE.rdb /var/lib/redis/dump.rdb +chown redis:redis /var/lib/redis/dump.rdb +systemctl start redis-server + +# Verify +pg_isready -U hotnow_app +redis-cli PING + +# Start services +systemctl start hotnow-api hotnow-worker hotnow-mcp + +# Health check +sleep 5 +curl -f https://api.hotnow.io/health || echo "WARNING: Health check failed" +``` + +### 10.6 Scaling Considerations + +| Threshold | Action | +|-----------|--------| +| 1,000 MAU | Monitor Redis memory (currently 256MB). Trending sorted set for 500+ events with scores: ~2-5MB | +| 5,000 MAU | Increase Redis maxmemory to 512MB. Add Mapbox for better map tiles | +| 10,000 MAU | Consider read replica for PostgreSQL (app3 has MySQL -- would need PostgreSQL on app3). Add CDN caching for static PWA assets | +| 50,000 MAU | Dedicated VPS for PostgreSQL. HotNow MCP server on separate port. Redis cluster for trending data | +| 100,000+ MAU | Multi-region deployment. Consider managed PostgreSQL hosting. Separate ingestion pipeline from serving API | + +### 10.7 Security Checklist + +| Check | Status | Notes | +|-------|--------|-------| +| JWT tokens signed with HS256, 15-min expiry | To implement | | +| Refresh token rotation on use | To implement | | +| CORS restricted to hotnow.io subdomains only | Configured in Caddyfile | | +| Stripe webhook signature verification | To implement | | +| SQL injection prevention via parameterized queries (asyncpg) | To implement | | +| Rate limiting per tier (Redis token bucket) | To implement | | +| User input sanitization (event submissions, reviews) | To implement | | +| HTTPS only (Caddy auto-LetsEncrypt) | Configured | | +| Environment secrets not in code (`.env` file, 600 perms) | To enforce | | +| Admin endpoints Tailscale-only or strong auth | To configure | | + +--- + +## Appendix A: File Structure Reference + +``` +/root/hotnow-api/ # FastAPI backend + main.py # FastAPI app entrypoint + config.py # Settings from .env + models.py # Pydantic models (exists, 125 lines) + auth.py # JWT auth, login, register + database.py # asyncpg connection pool + ranking.py # HOT_SCORE calculator + trend_worker.py # ARQ worker with cron + ai_curator.py # LLM-powered venue/event enrichment + reddit_worker.py # PRAW Reddit flywheel + city_seed.py # Yelp/Google/Eventbrite ingestion + sitemap_generator.py # Sitemap XML generator + routers/ + __init__.py + events.py # Events endpoints + venues.py # Venues endpoints + discover.py # Discovery/feed endpoints + auth.py # Auth endpoints + billing.py # Stripe billing endpoints + ambassador.py # SCAD ambassador endpoints + admin.py # Admin endpoints + savannah.py # Savannah-specific (neighborhoods, corridors) + ai_readable.py # AI-optimized endpoints + health.py # Health check endpoint + signals/ + __init__.py + freshness.py # Freshness signal calculation + velocity.py # Velocity signal from Redis pulses + social.py # Social proof from Super Search + contextual.py # Weather/time/proximity signals + boost.py # Manual boost management + aggregators/ + __init__.py + eventbrite.py # Eventbrite API connector + ticketmaster.py # Ticketmaster API connector + meetup.py # Meetup API connector + migrations/ # SQL migration files + 001_initial_schema.sql + 002_neighborhoods.sql + 003_ambassadors.sql + tests/ + conftest.py + fixtures/ + savannah_seed.sql + test_ranking.py + test_api_events.py + test_api_auth.py + test_signals.py + test_mcp.py + scripts/ + backup.sh + restore.sh + .env # Environment variables (chmod 600) + requirements.txt + pyproject.toml + +/opt/hotnow-mcp/ # MCP server + server.py # FastMCP 4.x server + venv/ # Virtual environment + requirements.txt + +/var/www/hotnow/ # Marketing landing page + index.html # Exists (1342 lines) + savannah/ + index.html # Savannah-specific landing page + +/var/www/hotnow-app/ # PWA (SvelteKit static export) + index.html + _app/ # Bundled JS/CSS + manifest.json + sw.js # Service worker + icons/ + +/var/www/hotnow-admin/ # Admin dashboard + index.html + _app/ + +/etc/caddy/Caddyfile # Reverse proxy config (hotnow routes exist) +/etc/systemd/system/ + hotnow-api.service # FastAPI service (exists) + hotnow-worker.service # ARQ worker (to create) + hotnow-mcp.service # MCP server (to create) +/etc/cron.d/ + hotnow-reddit # Reddit flywheel cron (to create) + hotnow-backup # Daily backup cron (to create) +``` + +## Appendix B: Key Decisions Log + +| Decision | Rationale | Date | +|----------|-----------|------| +| Launch Savannah first (not Austin) | Smaller market = faster network effects; SCAD = 15K Gen Z users; Germaine's home turf; Locale-NYC city-first strategy validation | Aug 10, 2026 | +| Leaflet.js + OSM (not Mapbox) for maps | Free, no API key dependency, works offline. Mapbox becomes viable at >$500 MRR | Phase 1 doc | +| FastMCP 4.x pattern (matching Super Search v2) | Proven pattern, same infrastructure, same deployment model, shared Python 3.13 venv | Phase 1 doc | +| SvelteKit over React | Faster, smaller bundles, better PWA support, matches Phase 1 architecture decision | Phase 1 doc | +| ARQ over Celery for task queue | Lighter weight, native Redis support, same Python process model | Phase 1 doc | +| JWT (stateless) over server-side sessions | PWA-friendly, no cookie dependency, works across subdomains | Phase 1 doc | +| Stripe Customer Portal for billing self-serve | Zero custom billing UI build; Stripe handles upgrades, downgrades, invoices | Phase 1 doc | +| One city at a time (depth-first) | Build strong network effects in Savannah before expanding; prevents thin data problem | Phase 1 doc | +| Reddit flywheel over paid ads | Locale-NYC pattern validated; zero ad spend for first city; community-driven growth | Aug 10, 2026 | + +## Appendix C: Glossary + +| Term | Definition | +|------|-----------| +| **PWA** | Progressive Web App -- installable from browser, works offline, no app store | +| **MCP** | Model Context Protocol -- AI agent tool integration standard | +| **FastMCP** | Python MCP server framework used by Super Search v2 and HotNow MCP | +| **ARQ** | Async Python task queue with Redis backend | +| **PostGIS** | PostgreSQL geospatial extension for proximity/containment queries | +| **SCAD** | Savannah College of Art and Design -- 15K+ students in Savannah | +| **PRAW** | Python Reddit API Wrapper -- library for automated Reddit posting | +| **HOT_SCORE** | HotNow's real-time ranking metric (0.0-1.0 scale) | +| **PeerPush Pattern** | Structured AI-readable data layer for AI crawler/LLM consumption | +| **Locale-NYC Pattern** | City-branded discovery with Reddit flywheel community growth | +| **UTM** | Urchin Tracking Module -- URL parameters for campaign attribution | +| **SOM** | Serviceable Obtainable Market -- addressable within 3-year window | + +--- + +Document prepared by: Hermes Agent (subagent) for Germaine Brown +Date: August 11, 2026 +Repository: ITPP Infrastructure (https://git.itpropartner.com/ippadmin/itpp-infrastructure) +Classification: Internal Technical Documentation diff --git a/projects/hotnow-savannah-v2.md b/projects/hotnow-savannah-v2.md new file mode 100644 index 0000000..185c0ff --- /dev/null +++ b/projects/hotnow-savannah-v2.md @@ -0,0 +1,981 @@ +# HotNow Savannah Business Proposal (v2) + +**Prepared for:** Germaine Brown & Advisory Team +**Date:** August 11, 2026 +**Company:** IT Pro Partner - Product Division +**Product:** HotNow (hotnow.io) - Savannah, GA Launch +**Classification:** Confidential - Advisory Review +**Version:** 2.0 - City Pivot (Austin -> Savannah) + +--- + +## Table of Contents + +1. [Executive Summary](#1-executive-summary) +2. [Elevator Pitch](#2-elevator-pitch) +3. [Problem Statement](#3-problem-statement) +4. [Market Analysis](#4-market-analysis) +5. [Product Overview](#5-product-overview) +6. [Revenue Model](#6-revenue-model) +7. [Competitive Advantages](#7-competitive-advantages) +8. [Go-to-Market Strategy](#8-go-to-market-strategy) +9. [Risk Analysis](#9-risk-analysis) +10. [Financial Projections](#10-financial-projections) +11. [The Ask](#11-the-ask) + +--- + +## 1. Executive Summary + +HotNow is a real-time local discovery engine that surfaces hidden gems, trending spots, and live events near you - in real time. Unlike Yelp (review-focused, static), Eventbrite (event ticketing, not discovery), or editorial curation platforms (slow, limited cities), HotNow aggregates and ranks everything happening around you right now based on social signals, check-in data, freshness, and AI-powered curation. + +**The strategic pivot:** HotNow will launch first in Savannah, Georgia, not Austin, Texas. This decision is based on competitive intelligence gathered in August 2026 (Batch 001 research analyzing 20 competitor sites) and validated by the Locale-NYC pattern - a pre-revenue NYC-only local discovery platform that achieved organic growth entirely through Reddit community engagement. Savannah offers decisive advantages: a manageable metro population (~400K) for rapid network effects, the Savannah College of Art and Design (SCAD) with 15,000 Gen Z students as a built-in early adopter base, Germaine's home-turf venue relationships, and a competitive vacuum with zero serious local discovery apps. + +The global local discovery and events market is massive. The online event ticketing market alone was valued at **$28.4 billion in 2024** and is projected to reach **$43.6 billion by 2032** (Allied Market Research, 2025). The broader "things to do" local discovery segment - encompassing restaurants, nightlife, pop-ups, street festivals, live music, and art shows - represents a **$100B+ annual consumer spend category** in the US alone (US Bureau of Labor Statistics, Consumer Expenditure Survey, 2024). Yet no single platform answers the question "what's good right now near me?" in real time. + +HotNow fills this gap. Built on IT Pro Partner's existing Super Search v2 infrastructure - a battle-tested multi-provider search engine with 7 providers, circuit breakers, and caching already running on netcup VPS - HotNow adds a map-first mobile PWA, real-time ranking algorithm, user accounts, and location-based discovery. Approximately **70% of the core search and aggregation engine already exists and is production-hardened**. + +With a freemium model anchored by Pro ($4.99/mo) and Concierge ($19.99/mo) tiers, plus business featured placements ($97/mo) and event promotion boosts ($47/event), HotNow monetizes both consumer willingness to pay for curation and business willingness to pay for visibility. At **200-500 Pro subscribers and 20-50 featured businesses in Savannah**, HotNow projects **~$3,500-$8,500 MRR** (~$42K-$102K ARR) with approximately **90%+ gross margins** - a capital-efficient consumer platform with near-zero marginal delivery cost. The single-city model proves the concept at low risk; expansion to additional cities follows after validated product-market fit. + +**Key changes from v1 (August 1, 2026):** + +| Dimension | v1 (Austin) | v2 (Savannah) | +|-----------|-------------|---------------| +| Launch city | Austin, TX (~2.2M metro) | Savannah, GA (~400K metro) | +| Year 1 Pro subscribers | 1,000 | 200-500 | +| Year 1 businesses | 100 | 20-50 | +| Year 1 ARR | ~$166K | ~$42K-$102K | +| MVP timeline | 2-3 months | 3-4 weeks | +| Seed content per city | 500+ venues/events | 200-300 Savannah venues/events | +| GTM engine | Multi-city influencer + campus | Reddit flywheel + SCAD ambassadors | +| Paid acquisition | Months 10-12 | None (months 1-6) | +| New technical layer | - | AI-readable structured data + MCP server | + +**TL;DR:** HotNow launches in Savannah to prove the model fast, cheap, and on home turf. A 15,000-student art school provides the early adopter base. Reddit and TikTok content drive zero-cost growth. The Super Search v2 engine is already 70% built. 3-4 weeks to MVP, ~$42K-$102K Year 1 ARR target, 90%+ margins. + +--- + +## 2. Elevator Pitch + +HotNow tells you what's good, right now, near you - starting in Savannah, Georgia. Open the app, see a live map of trending spots, pop-ups, live music, secret menus, and street festivals happening around you, ranked in real time by social buzz, freshness, and AI curation. Free to browse. $4.99/month unlocks "Best Right Now" - AI picks tailored to your tastes, the weather, the time of day, and real-time crowd signals. For Savannah's 15,000 SCAD students, 15 million annual tourists, and everyone who's ever asked "what should we do tonight?", HotNow is the answer. Launch in Savannah first, prove the model, then expand. + +--- + +## 3. Problem Statement + +### 3.1 The Discovery Gap + +Every day, millions of people ask some version of the same question: "what's good around here?" or "what should we do tonight?" The answers are scattered across a fragmented landscape of platforms, none of which solve the problem end-to-end in real time. + +| Platform | What It Does | What It Misses | +|----------|-------------|----------------| +| Yelp / Google Maps | Restaurant reviews + ratings | Not real-time; does not surface pop-ups, events, live music, or trending spots | +| Eventbrite / Ticketmaster | Event ticketing | Only ticketed events; misses free pop-ups, street festivals, hidden gems | +| TikTok / Instagram | Social discovery | Unstructured, algorithmic feed; not map-based; no real-time ranking | +| Thrillist / Infatuation | Editorial curation | Slow, static, limited to major cities; misses neighborhood-level gems | +| Locale-NYC | Curated NYC event discovery | NYC-only; no real-time signals; pre-revenue; no AI personalization | +| Google "Events near me" | Event listings | Generic, incomplete, no social signals, no curation | + +The result: **people miss the best stuff happening around them**. The pop-up ramen shop that is only open tonight. The street festival three blocks away that did not show up on Eventbrite. The bar with a secret live jazz set. By the time editorial coverage or Yelp reviews catch up, the moment is gone. + +In Savannah specifically, this gap is acute. The city hosts over 15 million visitors annually and maintains a dense year-round events calendar (Savannah Music Festival, Film Festival, St. Patrick's Day - 2nd largest in the US, Food and Wine Festival, Tour of Homes, First Friday Art March). Yet there is zero local discovery platform dedicated to Savannah. Visitors search Google and get TripAdvisor's top 10. Locals rely on word of mouth and scattered Instagram accounts. SCAD students - 15,000 art and design students with voracious appetite for gallery openings, live shows, and pop-ups - have no centralized "what's happening tonight" source. + +### 3.2 Who Feels This Pain + +- **SCAD students (15,000+ in Savannah):** Gen Z art and design students. Spontaneous, social, discovery-driven. They decide at 7pm what to do at 8pm. Deeply embedded in local culture but fragmented across Instagram, group chats, and flyers. +- **Savannah residents (18-40):** Urban professionals, service industry workers, artists, and young families in the Historic District, Starland, Midtown, and surrounding areas. They know the city has hidden gems but discovery requires following the right 50 Instagram accounts. +- **Tourists and visitors (15M+/year):** Savannah's tourism economy is massive. Visitors want what locals actually do - not the TripAdvisor top 10. They arrive for weddings, conferences, SCAD parents' weekends, and historic tours, then ask "what else?" +- **Event-goers and nightlife enthusiasts:** Tired of missing pop-ups, secret shows, gallery openings, and limited-run experiences because they did not follow the right social account. +- **Venue and business owners:** River Street bars, Starland galleries, Broughton Street restaurants, and Tybee Island spots that want to be discovered by the right people at the right time. + +### 3.3 The Pain Points HotNow Solves + +| Pain Point | HotNow Solution | +|------------|----------------| +| "I do not know what is happening around me right now" | Real-time map of trending spots, events, and pop-ups in Savannah | +| Yelp only shows established places, not what is hot tonight | Social signal + freshness ranking surfaces the new and trending | +| Events scattered across 5+ platforms | Single aggregated feed of everything: food, music, art, nightlife | +| Editorial coverage is slow and does not cover Savannah at all | AI-powered, automated, neighborhood-level precision for Savannah | +| No personalization without hours of research | Pro tier: AI picks tailored to your tastes, weather, and time | +| "My friends and I cannot decide" | Concierge tier: group coordination, itinerary builder | + +--- + +## 4. Market Analysis + +### 4.1 Why Savannah? + +Competitive intelligence from Batch 001 (August 10, 2026) - analyzing 20 competitor sites across local discovery, AI tools, and growth platforms - confirmed that Locale-NYC's city-first approach is the right model for HotNow. Locale-NYC grew its NYC-only platform entirely through Reddit community cross-posting, with zero paid acquisition. The lesson: a smaller, denser city builds network effects faster than a large, spread-out metro. + +Savannah was selected over Austin for five decisive reasons: + +| Factor | Savannah Advantage | +|--------|-------------------| +| **Manageable size (400K metro)** | Faster network effects. Fewer venues to seed. Higher user density per square mile. | +| **SCAD (15,000 Gen Z students)** | Built-in early adopter base. Art/design students = natural discovery app users. Campus ambassador program is obvious. | +| **15M+ annual tourists** | Doubles the addressable market. Tourists are the highest-intent local discovery users. | +| **Germaine's home turf** | Existing venue relationships, local knowledge, personal network. Unfair advantage that Austin does not provide. | +| **Competitive vacuum** | Zero serious local discovery apps focused on Savannah. Locale-NYC is not coming here. Yelp/Google Maps are the only options, and they are weak on events. | + +Additional advantages: + +- **Year-round events calendar:** Savannah Music Festival, SCAD Savannah Film Festival, St. Patrick's Day (2nd largest in US), Food and Wine Festival, Tour of Homes, First Friday Art March, weekly farmers markets, and a dense gallery-hop scene. +- **Compact geography:** The walkable Historic District concentrates venues and users. Starland District, Midtown, Tybee Island, and Pooler are natural neighborhood/corridor browsing categories. +- **Proving-ground logic:** If HotNow works in Savannah (smaller, seasonal, tourism-dependent), it will work in larger markets with better unit economics. Savannah validates the model at low cost and low risk. + +### 4.2 Total Addressable Market (TAM) + +The local discovery and events market spans several overlapping segments: + +| Segment | Market Size | Source / Methodology | +|---------|------------|---------------------| +| Online event ticketing (global) | $28.4B (2024) to $43.6B (2032) | Allied Market Research, 2025; CAGR 5.5% | +| US restaurant + food service spend | $1.1T annually | National Restaurant Association, 2025 | +| US live music + entertainment | $35B annually | IBISWorld, 2024 | +| US nightlife + bars | $28B annually | IBISWorld, 2024 | +| US "things to do" / experiences consumer spend | ~$150B annually | BLS Consumer Expenditure Survey, 2024; aggregate of food away from home, entertainment, recreation | +| Global local search advertising | $14.8B (2024) to $25.3B (2030) | Grand View Research, 2025; CAGR 9.4% | + +**TAM (Consumer Discovery Apps + Local Event Aggregation):** Conservative estimate of **$5B-$10B** in addressable consumer and business revenue globally, growing as mobile-first discovery replaces traditional search and editorial curation. + +### 4.3 Serviceable Addressable Market (SAM) - Savannah Focus + +HotNow's initial SAM is **Savannah metro area residents and visitors** who use smartphones for local discovery. + +| Parameter | Value | Source / Methodology | +|-----------|-------|---------------------| +| Savannah metro population | ~400,000 | US Census Bureau, 2024 | +| SCAD students in Savannah | ~15,000 | SCAD enrollment data | +| Annual Savannah visitors | 15,000,000+ | Visit Savannah / Savannah Area Chamber of Commerce | +| Addressable local population (18-40, smartphone users) | ~120,000 | ~30% of metro population in target age bracket | +| Annual visitors who search "things to do in Savannah" | ~3,000,000+ | ~20% of visitors; Google Trends data for destination search behavior | +| Addressable local users willing to pay $5/mo for discovery app | ~6,000-12,000 | 5-10% of addressable locals (consumer subscription benchmarks) | +| Addressable visitor Pro conversions (per year) | ~15,000-45,000 | 0.5%-1.5% of visitors (low-friction impulse purchase during trip) | +| **SAM (consumer subscriptions, Savannah)** | **~$105K-$285K annual** | (6K-12K locals + 15K-45K visitors) x $4.99/mo x avg 1-2 months retention for visitors | +| **SAM (business featured placements, Savannah)** | **~$230K-$580K annually** | ~2,000-5,000 food/entertainment/hospitality businesses in Savannah metro x $97/mo x 5-10% adoption | + +### 4.4 Serviceable Obtainable Market (SOM) + +HotNow's SOM focuses on capturing Savannah first, then expanding to additional cities after proving the model. + +| Year | SOM Estimate | Methodology | +|------|-------------|-------------| +| Year 1 | 200-500 Pro subscribers + 20-50 featured businesses | Savannah single-city launch. Organic + Reddit + SCAD ambassador growth. Zero paid acquisition months 1-6. | +| Year 2 | 1,000-3,000 Pro subscribers + 50-150 businesses | Expand to 2-3 additional cities (Charleston, SC; Asheville, NC; or Austin, TX). Referral flywheel + proven playbook. | +| Year 3 | 5,000-15,000 Pro subscribers + 200-500 businesses | 5-8 cities. Brand establishment in Southeast corridor. Network effects from city density. | + +Compared to v1 (Austin-first), the Year 1 SOM is scaled down proportionally to Savannah's market size but the underlying model is validated at lower risk and lower capital requirement. + +### 4.5 Competitive Landscape + +| Competitor | Price | Primary Strength | Primary Weakness | HotNow Advantage | +|------------|-------|-----------------|-----------------|-----------------| +| **Yelp** | Free (ads) | Massive review database, SEO dominance | Static, review-focused; no real-time ranking; no pop-up/event discovery | Real-time social signals + AI curation; surfaces what is hot NOW | +| **Google Maps "Explore"** | Free | Universal adoption, location data | Generic, no curation; misses pop-ups and trending spots | AI-powered personalization; real-time ranking; dedicated to discovery | +| **Eventbrite** | Free (ticketing fees) | Event creation + ticketing infrastructure | Only ticketed events; misses free pop-ups, street festivals, nightlife | Aggregates everything: ticketed + free + pop-ups + trending spots | +| **TikTok "near me"** | Free | Massive engagement, trend-spotting | Unstructured; no map; no systematic ranking; algorithm-dependent | Map-first structured discovery; real-time ranking; save and plan | +| **Locale-NYC** | Free | Reddit community flywheel, NYC curation | NYC-only; pre-revenue; no AI personalization; no real-time signals; no business monetization | Multi-city architecture; AI curation; consumer + business revenue model; real-time ranking algorithm | +| **IQHub / TownIQ** | Unknown | Dual B2B/B2C architecture | Unclear value prop; likely under-resourced; no consumer traction | Clear consumer proposition; proven Super Search infrastructure; 2,400+ waitlist | +| **PeerPush** | $39-$229/mo | Structured AI-readable data, MCP server | Business-facing (not consumer); developer tool, not discovery app | Consumer-first with structured data layer planned; same MCP server approach for developer ecosystem | +| **Thrillist / Infatuation** | Free (ads) | Editorial quality, brand trust | Slow publication cycle; limited city coverage; no Savannah presence | Automated, real-time, scalable to any city; Savannah from day one | +| **Dice / Bandsintown** | Free (ticketing fees) | Live music focus, artist following | Music-only; misses food, art, pop-ups, nightlife | Everything combined: food + music + art + nightlife + events | + +**Key insight from Batch 001:** Three new competitors have emerged since the original proposal. Locale-NYC validates the city-first + Reddit flywheel model but is NYC-only and pre-revenue. IQHub/TownIQ has a dual B2B/B2C architecture worth studying for future business analytics features. PeerPush proves that structured AI-readable data is becoming a standalone competitive moat - HotNow should adopt this pattern early. None of these competitors are focused on Savannah or the Southeast corridor. + +HotNow does not need to replace Yelp or Google Maps. It needs to answer the specific, high-intent question those platforms handle poorly: "what is good right now near me?" This is a new category - real-time local discovery - that combines elements of social media (freshness), maps (location), and AI curation (personalization) into a single experience. + +--- + +## 5. Product Overview + +### 5.1 Architecture + +HotNow is built on a layered architecture that maximizes reuse of existing IT Pro Partner infrastructure: + +``` ++-----------------------------------------------------------+ +| HotNow PWA (React + Mapbox) | +| Map-first mobile experience, user accounts, tiers | ++-----------------------------------------------------------+ +| API Layer (FastAPI + Auth) | +| REST endpoints, geolocation, user profiles, billing | ++-----------------------------------------------------------+ +| HotNow Engine (Python) | +| Real-time ranking algorithm, AI curation, social signals | ++---------------------------+-------------------------------+ +| Super Search v2 | Event Aggregators | +| (7 providers, caching, | (Eventbrite, Ticketmaster, | +| circuit breakers) | Meetup, Facebook Events, | +| | scraping layer) | ++---------------------------+-------------------------------+ +| Structured Data Layer | MCP Server (Developer API) | +| (AI-readable event/venue | (Model Context Protocol | +| schemas, JSON-LD, | endpoint for AI assistants | +| schema.org markup) | to query local discovery) | ++---------------------------+-------------------------------+ +| PostgreSQL - Places, users, events, reviews | ++-----------------------------------------------------------+ +| Stripe - Billing, subscriptions, payouts | ++-----------------------------------------------------------+ +| Mapbox - Maps, geocoding, location services | ++-----------------------------------------------------------+ +``` + +**New additions (v2):** +- **Structured AI-readable data layer:** Pattern borrowed from PeerPush. All venue and event data is published with JSON-LD / schema.org markup, making HotNow the canonical AI-queryable source for Savannah local discovery. This future-proofs the platform for AI assistant integration (ChatGPT, Claude, Perplexity) and creates a data moat. +- **MCP Server endpoint:** A Model Context Protocol server that allows AI assistants and developer tools to query HotNow's structured event and venue data directly. This turns HotNow into infrastructure, not just a consumer app. + +**Infrastructure:** +- **Hosting:** netcup VPS (IT Pro Partner existing infra) - one production server + staging +- **Super Search v2 MCP Server:** Already running on app1 - provides search across 7 providers with circuit breakers, caching, and health monitoring +- **API Layer (to build):** FastAPI with JWT auth, geolocation queries, user profiles +- **PWA (to build):** React SPA with Mapbox GL JS, offline support, push notifications +- **Database (to add):** PostgreSQL with PostGIS for geospatial queries on places, events, users +- **Billing (to add):** Stripe for consumer subscriptions + business featured placement billing +- **Ranking Engine (to build):** Real-time scoring algorithm combining social signals, freshness, check-in velocity, and review sentiment +- **LLM:** deepseek-v4-pro for AI curation, personalized recommendations, itinerary building + +**Existing vs. to-build breakdown:** + +| Component | Status | Effort Estimate | +|-----------|--------|----------------| +| Super Search v2 engine | **Existing** | 0 hours | +| Search provider orchestration | **Existing** | 0 hours | +| Circuit breakers, caching, health checks | **Existing** | 0 hours | +| Event aggregator connectors (Eventbrite, Ticketmaster, Meetup) | **To build** | ~30 hours | +| Social signal ingestion (check-in data, review velocity) | **To build** | ~25 hours | +| Real-time ranking algorithm | **To build** | ~40 hours | +| AI curation / recommendation engine | **~40% existing** | ~35 hours | +| Structured data layer (JSON-LD, schema.org, MCP server) | **To build** | ~25 hours | +| FastAPI multi-tenant API layer | **To build** | ~30 hours | +| React PWA (Mapbox, offline, push) | **To build** | ~100 hours | +| PostgreSQL + PostGIS schema | **To build** | ~20 hours | +| Stripe billing integration | **To build** | ~20 hours | +| Auth (JWT + social login: Google, Apple) | **To build** | ~15 hours | +| Business portal (claim listing, analytics, featured placement) | **To build** | ~40 hours | +| Push notification engine | **To build** | ~15 hours | +| Testing, DevOps, CI/CD, PWA compliance | **To build** | ~35 hours | +| Documentation, moderation tools | **To build** | ~20 hours | +| **Total remaining build** | | **~450 hours** | + +### 5.2 Tier Structure + +#### Explorer - Free + +**Target:** Everyone. The top of the funnel. + +**Features:** +- Real-time map of trending spots and events near you in Savannah +- Browse by category: food, music, nightlife, art, festivals, pop-ups +- Neighborhood/corridor browsing: Historic District, Starland, Midtown, Tybee Island, Pooler +- Event and place detail pages with photos, descriptions, social links +- Basic search and filtering +- "Trending Now" feed for your current location +- Limited to 10 "Best Right Now" AI picks per month +- Ad-supported + +#### Pro - $4.99/month (annual: $49.99/yr, save 17%) + +**Target:** Power users who want curated, personalized discovery. + +**Features (everything in Explorer, plus):** +- Unlimited "Best Right Now" AI picks - personalized to your tastes, weather, time of day, and real-time crowd data +- Taste profile: teach HotNow what you like (cuisines, music genres, vibe preferences) +- Saved places and collections ("Date Night Spots," "Best Rooftops in Savannah") +- Custom alerts: get notified when your favorite type of event pops up nearby +- Ad-free experience +- "Friends Are Going" social signals (opt-in) +- Early access to limited-capacity events + +#### Concierge - $19.99/month (annual: $199.99/yr, save 17%) + +**Target:** Power planners, group organizers, frequent entertainers. + +**Features (everything in Pro, plus):** +- Trip planner: build multi-stop itineraries with time/location optimization +- Group coordination: share plans, vote on options, see where friends want to go +- "Perfect Night Out" AI: give it a vibe ("romantic," "wild," "low-key") and it builds the night +- Priority support (chat, 2-hour response) +- Concierge badge (verified power user status) +- Early access to new features +- Export itineraries to calendar + +### 5.3 Business Revenue Products + +| Product | Price | What It Is | +|---------|-------|------------| +| **Featured Placement** | $97/mo per location | Priority placement in "Trending Now" feed + map highlight + "Featured" badge. Analytics dashboard showing impressions, clicks, direction requests. | +| **Event Boost** | $47/event | One-time boost for a specific event (pop-up, special menu, guest DJ, etc.). Surfaces the event to users in a 5-mile radius for 48 hours. | +| **Business Profile** | Free (claimed) | Claim and manage your listing with photos, hours, menus, event posts. Free for all businesses. | + +### 5.4 Savannah-Specific Features + +| Feature | Description | Priority | +|---------|-------------|----------| +| **Neighborhood/corridor browsing** | Historic District, Starland District, Midtown, Tybee Island, Pooler - browse by neighborhood, not just category | P1 | +| **SCAD event integration** | Scrape and ingest SCAD events calendar (art shows, performances, lectures, gallery openings) | P0 | +| **Tourist mode toggle** | "I am visiting" vs "I live here" - different default views for tourists vs locals | P1 | +| **Visit Savannah calendar ingestion** | Aggregate from visit-savannah.com and Savannah Master Calendar | P0 | +| **Reddit content pipeline** | Automated cross-posting engine for r/savannah and r/scad | P0 | + +### 5.5 Deployment Status Grid + +| Site | Status | Detail | +|------|--------|--------| +| hotnow.io | 🟢 Live | Domain registered at Cloudflare ($33/yr). Landing page live at /var/www/hotnow. | +| hotnow.io/savannah | 🔴 Not created | Savannah-specific landing page needed (city-branded, local content) | +| app.hotnow.io | 🔴 Not created | PWA dashboard | +| api.hotnow.io | 🔴 Not created | Backend API (Super Search extended) | +| MCP endpoint | 🔴 Not created | Structured data + MCP server for developer/assistant access | + +--- + +## 6. Revenue Model + +### 6.1 Pricing Rationale + +HotNow's consumer pricing is anchored to impulse-buy psychology - less than a single cocktail or a month of Netflix: + +- **Explorer (Free):** "Try it. You will wonder how you lived without it." +- **Pro ($4.99/mo):** "Less than a latte. Unlimited AI-curated picks for the price of a single drink." +- **Concierge ($19.99/mo):** "The cost of one mediocre appetizer. Your personal nightlife planner." + +Business pricing is aggressive compared to Yelp Ads ($150-$500+/mo for basic placement) and Eventbrite promotion ($0.79-$2.50/ticket in fees): + +- **Featured Placement ($97/mo):** "Less than Yelp Ads. More targeted. Real-time visibility when people are deciding where to go." +- **Event Boost ($47/event):** "One-time. No revenue share. 48 hours of priority visibility to everyone nearby." + +### 6.2 Cost Structure (Monthly Operating) + +| Expense | Monthly Cost | Annual Cost | Notes | +|---------|-------------|-------------|-------| +| Super Search infrastructure | $0 | $0 | Already running on ITPP infra | +| Netcup VPS (production + staging) | $50 | $600 | Incremental to existing; largely absorbed | +| Mapbox (maps + geocoding) | $50-$100 | $600-$1,200 | Free tier: 50K monthly loads. Savannah-scale MAU stays under threshold. | +| Eventbrite / Ticketmaster API | $0-$50 | $0-$600 | Many have free tiers for discovery apps | +| deepseek-v4-pro API calls | $50-$150 | $600-$1,800 | Variable; scales with Pro/Concierge user count. Lower at Savannah scale. | +| Stripe fees | ~2.9% + $0.30/transaction | Variable | ~3% of revenue | +| Domain + SSL | $3 | $36 | hotnow.io at Cloudflare ($33/yr) | +| Email delivery (Resend/SendGrid) | $20 | $240 | Transactional + notification delivery | +| Push notifications (OneSignal/FCM) | $0 | $0 | Free tier: 10K subscribers | +| Monitoring + logging | $30 | $360 | Basic observability | +| **Total baseline operating cost** | **~$203-$403/mo** | **~$2,436-$4,836/yr** | | + +At Savannah scale (500-2,000 MAU), these costs remain near the floor of every pricing tier. Mapbox and LLM costs scale with usage but stay well within free/low tier limits at single-city volumes. + +### 6.3 Revenue Projections - Savannah Scale + +All consumer figures assume a mix of monthly and annual pricing. Month-to-month Pro at $4.99/mo; annual at $4.17/mo equivalent. + +#### Scenario: Consumer Subscriptions Only (Savannah Year 1) + +| Pro Subscribers | Concierge (10% of Pro) | Monthly Consumer Revenue | Annual Revenue | +|-----------------|----------------------|-------------------------|----------------| +| 100 | 10 | $617 | $7,404 | +| 200 | 20 | $1,233 | $14,800 | +| 300 | 30 | $1,850 | $22,200 | +| 400 | 40 | $2,467 | $29,600 | +| 500 | 50 | $3,083 | $37,000 | + +#### Scenario: Consumer + Business Revenue (Savannah Year 1) + +| Revenue Source | Volume/Month | Monthly Revenue | Annual Revenue | +|---------------|-------------|----------------|---------------| +| Pro Subscribers ($4.17/mo avg) | 300 | $1,251 | $15,012 | +| Concierge ($16.67/mo avg) | 30 | $500 | $6,000 | +| Featured Placements ($97/mo) | 30 | $2,910 | $34,920 | +| Event Boosts ($47/event) | 15 | $705 | $8,460 | +| **Total** | | **$5,366** | **$64,392** | + +#### Gross Margin Analysis (Savannah Scale) + +At 300 Pro subscribers + 30 featured businesses (~$5.4K MRR), monthly costs of ~$300 vs. revenue of ~$5,400 yields a **gross margin of ~94%**. The underlying unit economics are equally strong at single-city scale. + +### 6.4 12-Month Revenue Ramp (Savannah Realistic Case) + +| Month | Pro Users | Businesses | MRR | Cumulative Revenue | Notes | +|-------|-----------|-----------|-----|-------------------|-------| +| 1 | 0 | 0 | $0 | $0 | Pre-launch: build completion, seed content | +| 2 | 0 | 0 | $0 | $0 | Beta testing, SCAD ambassador recruitment | +| 3 | 10 | 2 | $236 | $236 | Soft launch in Savannah | +| 4 | 25 | 5 | $589 | $825 | First Reddit/social traction | +| 5 | 50 | 8 | $984 | $1,809 | SCAD ambassador program active | +| 6 | 80 | 12 | $1,498 | $3,307 | Word-of-mouth begins | +| 7 | 120 | 18 | $2,246 | $5,553 | "What's Hot in Savannah Tonight" content series | +| 8 | 160 | 22 | $2,801 | $8,354 | First tourist season boost | +| 9 | 200 | 28 | $3,550 | $11,904 | Business flywheel: placements attract users | +| 10 | 250 | 32 | $4,146 | $16,050 | Referral program launched | +| 11 | 300 | 36 | $4,742 | $20,792 | Network effects visible in Savannah | +| 12 | 350 | 40 | $5,338 | $26,130 | **Year 1 exit ARR: ~$64K** | + +**Key assumptions:** +- Single-city focus: Savannah only for Year 1 +- Zero paid acquisition in months 1-6 (organic, Reddit, SCAD ambassadors only) +- Monthly consumer churn: 4-6% (typical for consumer subscription apps) +- Monthly business churn: 3-5% (lower; business subscriptions are stickier) +- Average revenue per Pro user: $4.17/mo (mix of monthly and annual pricing) +- Savannah seeded with 200-300 manually curated venues and events before user launch +- Tourist season (March-October) provides natural MAU boost + +### 6.5 3-Year Savannah-First Projection + +| | Year 1 | Year 2 | Year 3 | +|---|--------|--------|--------| +| **Pro Subscribers (end of year)** | 350 | 2,000 | 5,000 | +| **Concierge Subscribers** | 35 | 200 | 500 | +| **Featured Businesses** | 40 | 150 | 300 | +| **Event Boosts (per month)** | 15 | 60 | 120 | +| **Cities Live** | 1 (Savannah) | 3-5 | 8-10 | +| **ARR (end of year)** | $64,000 | $370,000 | $920,000 | +| **Total Revenue** | $26,130 | $275,000 | $720,000 | +| **Gross Margin** | 90%+ (from Month 4) | 92% | 93% | +| **OpEx** | $30,000-$40,000 | $150,000 | $300,000 | +| **Net Income** | -$5,000 to -$10,000 | $80,000-$120,000 | $350,000-$420,000 | + +Year 2 expands to Charleston, SC and Asheville, NC - similar-size markets in the Southeast corridor with tourism economies. Year 3 adds 5-7 more cities as the playbook is proven. + +--- + +## 7. Competitive Advantages + +### 7.1 Why HotNow Wins + +#### 1. Real-Time, Not Static + +Every major competitor is static or slow. Yelp shows you the top-rated restaurants from the last 5 years. Thrillist publishes a "Best New Restaurants" list twice a year. Eventbrite lists events, but does not rank or recommend them. HotNow is the only platform that answers "what is good RIGHT NOW" - not "what was good last month" or "what is generally good in this city." + +This is a fundamental architectural advantage. HotNow's ranking engine combines: +- **Freshness signals:** how recently was this posted/updated/checked-into +- **Velocity signals:** how fast are social mentions, check-ins, and reviews accelerating +- **Social proof:** real-time Instagram/TikTok mentions, not just accumulated Yelp stars +- **Contextual signals:** weather, time of day, day of week, proximity + +No competitor combines all four in real time. + +#### 2. Everything in One Place + +Users currently need 5+ apps to cover what HotNow does in one: +- Yelp for restaurants +- Eventbrite for events +- Instagram/TikTok for pop-ups and trending spots +- Google Maps for navigation +- Bandsintown/Dice for live music + +HotNow unifies these into a single map-first experience. The aggregation is the product. + +#### 3. ~70% Already Built on Super Search v2 + +Super Search v2 - the multi-provider search engine with 7 providers, circuit breakers, intelligent caching, and health monitoring - is already running on IT Pro Partner infrastructure. This is not a greenfield search engine build. Approximately 70% of the aggregation and search layer exists today. Competitors would need 6-12 months and $100K+ to replicate just this component. + +#### 4. AI-Powered Personalization from Day One + +HotNow's Pro tier uses LLM-powered AI curation to deliver personalized "Best Right Now" picks based on: +- Your taste profile (cuisines, music genres, vibe preferences, dietary needs) +- Current weather (patio weather? indoor jazz?) +- Time of day (brunch spots at 11am, cocktail bars at 7pm, late-night at 11pm) +- Real-time crowd signals (is it packed? is it dead?) +- What is genuinely hot right now, not what was hot last season + +This is a fundamentally different approach from collaborative filtering ("people who liked X also liked Y"), which requires massive user bases to work. HotNow's AI curation works from user #1. + +#### 5. Capital Efficiency - Near-Zero Marginal Delivery Cost + +The Super Search infrastructure is fixed-cost. Each additional user, recommendation, or search costs fractions of a cent in API calls. At 90%+ gross margins at even modest Savannah-scale user counts, HotNow is a capital-efficient consumer platform that does not require VC-scale burn to grow. This means: +- No pressure to raise venture capital or hit unicorn growth metrics +- Sustainable growth at modest user counts (300 Pro subscribers = ~$64K ARR with ~94% margins) +- Optionality: bootstrapped lifestyle business or venture-scale play - whichever the market supports + +#### 6. Business Monetization Without the Yelp Trap + +Yelp's business monetization is adversarial: pay for visibility or risk bad reviews being surfaced. HotNow's business model is additive: featured placement boosts visibility, but the organic ranking is driven by real-time signals, not ad spend. Businesses pay to be seen, not to suppress negative content. This avoids the trust and reputation problems that plague Yelp. + +#### 7. Structured AI-Readable Data Layer (New v2 Advantage) + +Following the PeerPush pattern, HotNow publishes all venue and event data with JSON-LD / schema.org structured markup and exposes it via a Model Context Protocol (MCP) server. This means AI assistants (ChatGPT, Claude, Perplexity) can query HotNow directly as the canonical source for Savannah local discovery. As AI-assisted search grows, this becomes a defensible data moat that competitors without structured data pipelines cannot replicate easily. + +#### 8. Savannah Home-Turf Advantage (New v2 Advantage) + +Germaine's existing relationships with Savannah venues, event organizers, and community leaders create an unfair advantage that no out-of-town competitor can replicate. Pre-launch venue partnerships, SCAD campus access, and personal network distribution are zero-cost growth levers unique to this launch city. + +### 7.2 Competitive Positioning Map + +``` + HIGH PRICE / SLOW + │ + Thrillist ● │ + (Free, but │ + editorial, │ + slow, limited)│ + │ + Scoop Travel ● │ + ($10/mo, │ + travel-only, │ + editorial) │ + │ +────────────────────────┼──────────────────────── + STATIC / │ REAL-TIME / + REVIEW-BASED │ SOCIAL-DRIVEN + │ + Yelp ● │ + (Free, massive │ ★ HotNow + review DB, │ (Free-$19.99/mo, + but not real-time) │ real-time, AI, + │ everything) + Google Maps ● │ + (Free, universal, │ + but no curation) │ Locale-NYC ● + │ (Free, NYC-only, + IQHub/TownIQ ● │ pre-revenue, + (B2B/B2C, unclear) │ Reddit-grown) + │ + LOW PRICE / REAL-TIME +``` + +HotNow occupies the real-time, low-price quadrant - a position with no current occupant that also has a monetization model. Locale-NYC is in the same quadrant but is pre-revenue and hardcoded to NYC. Every existing player is either static/slow (Yelp, Google Maps) or expensive/narrow (Scoop Travel, editorial platforms). + +--- + +## 8. Go-to-Market Strategy + +### 8.1 Philosophy: Community-First, Zero Paid Acquisition + +HotNow Savannah's GTM strategy is modeled on Locale-NYC's proven Reddit community flywheel, adapted to Savannah's unique assets (SCAD students, tourism economy, compact geography). The core principle: **zero paid acquisition for months 1-6.** Growth comes from community engagement, content, and word-of-mouth. + +This approach is validated by Batch 001 research: Locale-NYC grew its entire NYC user base organically through systematic Reddit cross-posting. PeerPush grew via gamified community engagement. MicroLaunch grew via roast/boost feedback loops. None of the most successful local discovery or community platforms in the competitive analysis relied on paid ads. + +### 8.2 Phase 1: Foundation (Weeks 1-4) + +**Objective:** Complete minimum build, seed Savannah content, recruit SCAD ambassadors. + +**Activities:** +- Complete MVP build: PWA, ranking algorithm, API, billing (~450 hours, prioritized for Savannah launch) +- Seed 200-300 Savannah venues and events manually (restaurants, bars, galleries, music venues, event calendars) +- Build hotnow.io/savannah city-branded landing page +- Configure Super Search v2 Savannah filter and test event ingestion from 7 providers +- Set up Reddit content pipeline: r/savannah and r/scad cross-posting schedule +- Recruit 5-10 SCAD campus ambassadors (free Pro accounts + swag + commission on referrals) +- Outreach to 20-30 key Savannah venues for pre-launch partnerships +- Build social media presence: TikTok, Instagram, X accounts with Savannah-specific handles +- Produce launch content: "HotNow is coming to Savannah" teasers +- Set up Stripe, email, push notification infrastructure + +**KPIs:** +- Seed listings: 200-300+ +- SCAD ambassadors recruited: 5-10 +- Venue partnerships: 20-30 +- Social followers (combined): 500+ +- Reddit posts live: 5-10 across r/savannah and r/scad + +### 8.3 Phase 2: Soft Launch (Month 2-3) + +**Objective:** Invite-only beta, gather feedback, build initial user density. + +**Activities:** +- Launch invite-only beta for 100 Savannah users (SCAD students, venue partners, Germaine's network) +- Reddit flywheel activation: weekly "What's Happening in Savannah This Weekend" curated posts on r/savannah and r/scad, each ending with "Discover more on HotNow - hotnow.io/savannah" +- SCAD ambassador program goes live: ambassadors host "HotNow discovery nights" on campus +- TikTok/Reels content series: "What's Hot in Savannah Tonight" - 3-5 videos per week +- Collect beta feedback, iterate on ranking quality, fix bugs +- First 10-20 featured businesses onboarded from venue partner pipeline + +**Reddit Flywheel - Content Strategy:** + +| Day | Subreddit | Post Type | Example | +|-----|-----------|-----------|---------| +| Monday | r/savannah | "This Week in Savannah" | Curated list of 5-8 events for the week ahead | +| Wednesday | r/scad | "SCAD + Savannah This Weekend" | Gallery openings, student shows, nightlife picks | +| Friday | r/savannah | "Savannah Weekend Picks" | The weekend's best food, music, art, and pop-ups | +| Saturday | r/savannah | "What's Good Tonight" | Real-time Saturday night recommendations | +| Sunday | r/scad | "Next Week Preview" | Upcoming SCAD events + downtown happenings | + +Every post ends with a soft CTA: "Find more Savannah events at hotnow.io/savannah." The tone is community-member, not advertiser - following the Locale-NYC model. + +**SCAD Ambassador Program:** + +| Element | Detail | +|---------|--------| +| Target recruitment | 5-10 SCAD students (art, design, film, performing arts majors) | +| Compensation | Free HotNow Pro account + HotNow swag (stickers, tote bags) + $5 commission per Pro signup referral | +| Responsibilities | 1 social media post/week tagging HotNow, 1 campus event mention/week, distribute promo codes to friends | +| Onboarding | 30-minute Zoom orientation + shared content calendar | +| Duration | Semester-long commitment (renewable) | + +**KPIs:** +- Beta users: 100+ +- Pro subscribers: 10-25 +- Featured businesses: 2-5 +- MAU: 500-1,000 +- Reddit post engagement: 10+ upvotes average, 2-5 click-throughs per post +- TikTok/Reels views: 500-2,000 per video + +### 8.4 Phase 3: Public Launch (Month 3-4) + +**Objective:** Open access, activate word-of-mouth, first press coverage. + +**Activities:** +- Public launch: remove invite wall, open to all Savannah users +- Launch event: "HotNow Savannah Launch Party" at a partner venue (River Street rooftop or Starland gallery) +- Press outreach: Savannah Morning News, Connect Savannah, WSAV, SCAD District, Savannah Magazine +- Ramp Reddit posting to 3-5x/week across both subreddits +- "What's Hot in Savannah Tonight" TikTok series increases to daily posts +- Cross-promotion with partner venues: QR codes at bars/restaurants, "Find us on HotNow" signage +- Begin business outreach for $97/mo Featured Placement (target: 10-15 by end of phase) + +**KPIs:** +- Pro subscribers: 50-80 +- Featured businesses: 8-12 +- MAU: 2,000-5,000 +- App store rating: 4.5+ stars +- Press mentions: 1-3 + +### 8.5 Phase 4: Growth (Months 5-12) + +**Objective:** Activate referral flywheel, ride tourist season wave, prove model. + +**Activities:** +- Launch referral program: "Give a month free, get a month free" +- Tourist season strategy (March-October): "Visiting Savannah?" landing page variant, hotel/rental partnerships, concierge outreach +- Expand business sales: direct outreach to River Street, Broughton Street, Starland District, and Tybee Island venues +- User-generated content campaigns: "Tag #HotNowSavannah for a chance to be featured" +- Weekly "What's Hot in Savannah" email newsletter +- Begin scouting expansion city (Charleston, SC) - seed content, research subreddits + +**KPIs:** +- Pro subscribers: 200-350 +- Featured businesses: 30-40 +- MAU: 10,000-25,000 +- Cities live: 1 (Savannah), expansion city in preparation + +### 8.6 Customer Acquisition Channels + +| Channel | CAC Estimate | Time to Mature | Scalability | Priority | +|---------|-------------|----------------|-------------|----------| +| Reddit flywheel (r/savannah + r/scad) | $0 | Immediate | Medium per city | ★★★★★ | +| SCAD campus ambassadors | $0-$50 (swag + commission) | 1-2 months | Medium | ★★★★★ | +| Venue/bar/restaurant cross-promotion | $0 | Immediate | High per city | ★★★★★ | +| Organic TikTok/Reels ("What's Hot in Savannah Tonight") | $0 | 2-4 weeks | Very High | ★★★★ | +| Referral program | $0 | 3+ months | Very High | ★★★★ | +| Local press (Savannah Morning News, Connect Savannah) | $0 | 1-2 months | Low (one-time) | ★★★ | +| Launch event (partner venue) | $200-$500 | One-time | Low | ★★★ | +| Hotel/concierge partnerships (tourist channel) | $0-$100 | 2-3 months | Medium | ★★★ | +| Product Hunt | $0 | One-time | Low (one-time) | ★★ | +| Paid social (Instagram/TikTok ads) | $5-$15/install | 1-2 weeks | Very High | ★★ (Year 2) | + +### 8.7 Savannah Data Pipeline + +| Source | Method | Priority | +|--------|--------|----------| +| Super Search v2 (7 providers) | Filter to Savannah metro area | P0 | +| VisitSavannah.com | Scrape official tourism events calendar | P0 | +| Savannah Master Calendar | Scrape community events | P0 | +| SCAD events page | Scrape + ingest university events (art shows, performances, lectures) | P0 | +| Facebook Events API | Public Savannah-area events | P1 | +| Venue partnerships | 20-30 key venues provide direct feeds via form/email | P0 | +| Manual curation | 200-300 seed venues/events pre-launch | P0 | +| User submissions | Moderated "Add Event" form in-app | P1 | + +--- + +## 9. Risk Analysis + +### 9.1 Market Risks + +| Risk | Probability | Impact | Mitigation | +|------|-----------|--------|------------| +| **Savannah market too small for sustainable consumer subscription business** | Medium | High | Year 1 ARR target is modest ($42K-$102K) and achievable at 200-500 Pro subscribers. If Savannah revenue plateaus, expand to Charleston/Asheville (Year 2) - the playbook is designed to be portable. Savannah proves product-market fit; scaling is about adding cities, not growing Savannah indefinitely. | +| **SCAD dependency - user base concentrated in one institution** | Medium-High | Medium | SCAD is the early adopter wedge, not the entire market. Savannah has 400K metro residents, 15M+ annual tourists, and a robust local service/hospitality workforce. Diversify user acquisition to locals and tourists by Month 4. If SCAD engagement wanes during summer/holiday breaks, tourist traffic partially offsets. | +| **Tourism seasonality creates revenue lumpiness** | Medium | Medium | Savannah's peak tourism is March-October, with dips in winter. Buffer with annual Pro subscriptions (smooths revenue) and business featured placements (less seasonal - venues operate year-round). Tourist-mode feature capitalizes on peak season; local-user base sustains off-season. | +| **Consumer discovery apps have high churn / low willingness to pay** | Medium-High | High | Validate with beta before full investment. Free tier must be genuinely useful to drive habit formation. Freemium conversion rate in consumer apps averages 2-5% - HotNow targets 3%. If consumer subscriptions underperform, shift to business-first monetization (featured placements as primary revenue). | + +### 9.2 Product Risks + +| Risk | Probability | Impact | Mitigation | +|------|-----------|--------|------------| +| **"Cold start" problem: no content in Savannah at launch** | High | High | Manual seeding of 200-300 places/events before any user sees the app. Event aggregator connectors (Eventbrite, Ticketmaster, Meetup, Facebook Events) provide automated baseline content. Venue partnerships supply direct feeds. No launch until 200+ listings are live. | +| **Real-time ranking algorithm quality falls short** | Medium | High | Start simple: freshness + social velocity as primary signals. Layer on AI curation complexity incrementally. Beta test ranking quality with real Savannah users. Allow user feedback ("not relevant" / "great pick") to train ranking. | +| **Mapbox costs scale unexpectedly** | Low | Low | At Savannah-scale MAU (5K-25K), usage stays well within Mapbox free tier (50K monthly loads). Cost risk only emerges at multi-city scale in Year 2+. | +| **PWA adoption friction (no native app store presence)** | Medium | Medium | PWA wrapper for App Store / Google Play submission gives native app store listing. Users can install directly from browser. Promote PWA install aggressively in onboarding. At Savannah scale, word-of-mouth + QR codes at venues are the primary install channel. | +| **Structured data / MCP server adoption by AI assistants is slow** | Medium | Low | This is a forward-looking moat, not a launch dependency. Build the infrastructure now so it is in place when AI assistant queries for local discovery become common. Even without AI assistant adoption, structured data improves SEO and Google rich results. | + +### 9.3 Competitive Risks + +| Risk | Probability | Impact | Mitigation | +|------|-----------|--------|------------| +| **Locale-NYC expands to other cities (including Savannah)** | Low | Medium | Locale-NYC is pre-revenue, NYC-only, and appears to have a hardcoded architecture. Expanding requires a rebuild. Even if they expand, HotNow has real-time ranking, AI personalization, and consumer + business monetization that Locale-NYC lacks. Speed is the counter: establish Savannah before anyone else arrives. | +| **Yelp launches real-time "trending" feature** | Medium | Medium-High | Yelp's DNA is review-driven, not real-time. Adding trending requires a fundamentally different data pipeline. Even if launched, Yelp's business model (ads for established businesses) conflicts with surfacing new/pop-up spots. HotNow has 12-18 month head start. | +| **Google builds better "Explore" with real-time signals** | Medium | High | Google has the data (Maps, search, location history) but historically underinvests in local discovery UX. Google's incentives favor search ads, not discovery feeds. If Google enters, HotNow competes on curation quality, community, and focus on a specific city where Google is generic. | +| **VC-funded competitor targets Savannah** | Low-Medium | Medium | Savannah is a sub-500K metro - too small for VC-backed plays. VC-funded local discovery startups target top-10 metros. HotNow's capital efficiency at this scale means no one can outspend us on Savannah user acquisition because no one will try. | + +### 9.4 Operational Risks + +| Risk | Probability | Impact | Mitigation | +|------|-----------|--------|------------| +| **Germaine bandwidth - only person who can build/support** | High | High | Single biggest risk. Mitigation: (1) aggressive documentation from day one, (2) SCAD ambassadors reduce community management burden, (3) self-serve business portal minimizes support, (4) single-city focus means lower operational overhead than multi-city launch, (5) consider part-time developer or community manager if revenue exceeds $3K MRR. | +| **Content moderation at scale (spam, fake events, inappropriate content)** | Low-Medium | Medium | At Savannah scale (200-300 venues), manual review is feasible. User reporting for free tier. Automated spam detection for event submissions. Moderation cost stays near zero until multi-city expansion. | +| **deepseek-v4-pro API changes or price increases** | Low-Medium | Medium | LLM abstraction layer allows provider switching. OpenAI, Claude, and open-source models are fallbacks. AI curation quality is model-dependent but architecture is model-agnostic. At Savannah scale, LLM costs are under $150/mo - any provider switch has minimal financial impact. | + +### 9.5 Savannah-Specific Risks + +| Risk | Probability | Impact | Mitigation | +|------|-----------|--------|------------| +| **SCAD restricts or opposes commercial student ambassador program** | Low-Medium | Medium | Ambassadors are individual students, not officially affiliated with SCAD. Program is opt-in, compensated in product value (free Pro + swag) rather than significant cash. Avoid any branding that implies SCAD endorsement. If SCAD objects, pivot to general Savannah student ambassadors (Georgia Southern Armstrong campus, Savannah State). | +| **Hurricane season disrupts events/tourism (June-November)** | Low-Medium | Low-Medium | Savannah's event calendar has built-in seasonality. A major hurricane disruption would be industry-wide, not HotNow-specific. Revenue impact is temporary. Business continuity: infrastructure is cloud-hosted (netcup is in Germany, unaffected by Atlantic hurricanes). | +| **Savannah's small market limits press/TechCrunch interest** | Medium | Low | HotNow does not need TechCrunch. Local press (Savannah Morning News, Connect Savannah, WSAV) is more valuable for user acquisition than national tech press. The story is "Savannah startup builds app for Savannah" - hyperlocal narrative works for hyperlocal product. National press can wait for multi-city expansion. | + +### 9.6 90-Day Launch KPIs (Months 3-5) + +| KPI | Target | Red Flag Threshold | +|-----|--------|-------------------| +| MAU (Savannah, Month 3) | 1,000+ | <300 | +| Pro conversion rate | 3%+ | <1% | +| App store rating | 4.5+ | <4.0 | +| Weekly active user retention (Day 7) | 40%+ | <20% | +| Places/events listed in Savannah | 300+ | <150 | +| User-reported accuracy ("Great pick" rate) | 70%+ | <50% | +| Business outreach response rate | 25%+ | <10% | +| Reddit post engagement (avg upvotes) | 10+ | <3 | +| SCAD ambassador signups | 5+ | <2 | + +**If red flags trigger on 3+ KPIs:** Pivot to business-first monetization. De-prioritize consumer subscriptions and focus on building the supply side (businesses, events, venues) as a data/API play sold to platforms that need real-time local data (delivery apps, travel platforms, mapping services). + +### 9.7 Pre-Mortem: What Kills HotNow Savannah Within 6 Months? + +1. **Cold start death spiral:** Users open the app, see nothing in Savannah, never come back. **Prevention:** no launch without 200+ manually seeded listings. Reddit content establishes awareness before users arrive. + +2. **SCAD ambassador program fails to activate:** No students sign up, or they sign up but do not post. **Prevention:** recruit via personal SCAD connections. Offer real value (free Pro, event access). Start with 3 committed students, not 10 lukewarm ones. + +3. **Pro conversion rate below 1%:** Free tier is good enough that nobody upgrades. **Prevention:** free tier must be genuinely useful BUT with clear upgrade triggers (limited AI picks, ads, no saved places). The "Best Right Now" feature must feel like magic. + +4. **Germaine burns out:** The build (~450 hours) plus community management is a solo effort. **Prevention:** SCAD ambassadors carry community load. Venue partners self-manage listings. Do not try to do everything. Single-city scope is the burnout safeguard. + +5. **Savannah is genuinely too small:** Revenue plateaus at $2K-$3K MRR and cannot justify the time investment. **Prevention:** if 200 Pro subscribers is the ceiling after 6 months, the model is not working. Expand to Charleston or pivot to business-first monetization before declaring failure. The exit cost is low (~$5K-$10K cash outlay, mostly Germaine's time). + +--- + +## 10. Financial Projections + +### 10.1 12-Month P&L Projection (Savannah Realistic Case) + +| Line Item | Month 1-3 | Month 4-6 | Month 7-9 | Month 10-12 | Year 1 Total | +|-----------|-----------|-----------|-----------|-------------|-------------| +| **Revenue** | | | | | | +| MRR (end of period) | $236 | $1,498 | $3,550 | $5,338 | - | +| Cumulative Revenue | $236 | $3,307 | $11,904 | $26,130 | **$26,130** | +| **Cost of Revenue** | | | | | | +| Infrastructure + Mapbox + APIs | $150 | $300 | $500 | $800 | $1,750 | +| LLM API costs | $50 | $150 | $300 | $500 | $1,000 | +| Stripe fees (~3%) | $7 | $99 | $357 | $784 | $1,247 | +| **Total COGS** | **$207** | **$549** | **$1,157** | **$2,084** | **$3,997** | +| **Gross Profit** | **$29** | **$2,758** | **$10,747** | **$24,046** | **$22,133** | +| *Gross Margin* | *12%* | *83%* | *90%* | *92%* | *85%* | +| **Operating Expenses** | | | | | | +| Development (remaining build) | $10,000 | $5,000 | $2,500 | $1,000 | $18,500 | +| Seed content curation | $1,000 | $500 | $0 | $0 | $1,500 | +| SCAD ambassador program (swag/commissions) | $200 | $400 | $500 | $600 | $1,700 | +| Content + social media | $500 | $1,000 | $1,000 | $1,500 | $4,000 | +| Launch event | $500 | $0 | $0 | $0 | $500 | +| Influencer / community | $0 | $300 | $500 | $800 | $1,600 | +| Paid acquisition | $0 | $0 | $0 | $0 | $0 | +| Tools + software | $200 | $300 | $400 | $500 | $1,400 | +| Legal + compliance | $2,000 | $0 | $0 | $500 | $2,500 | +| Miscellaneous | $200 | $300 | $400 | $500 | $1,400 | +| **Total OpEx** | **$14,600** | **$7,800** | **$5,300** | **$5,400** | **$33,100** | +| **Net Income** | **-$14,571** | **-$5,042** | **$5,447** | **$18,646** | **-$10,967** | +| *Net Margin* | *Negative* | *Negative* | *15%* | *35%* | *Negative* | + +**Key observations:** +- Year 1 total investment: ~$11K net loss (vs ~$48K net loss in original v1 multi-city plan) +- Business becomes cash-flow positive by Month 7 (vs Month 10-11 in v1) +- Gross margins exceed 80% by Month 4 - the underlying unit economics are strong immediately +- Exit run-rate in Month 12: ~$64K ARR with 92% gross margins +- Total Year 1 cash outlay: approximately **$11K** (primarily development time + legal) +- The single-city model radically reduces financial risk while proving the concept + +### 10.2 Unit Economics (Savannah Steady State) + +| Metric | Value | Industry Benchmark | Assessment | +|--------|-------|-------------------|------------| +| Average Pro subscriber LTV (annual) | ~$50 | $20-$100 (consumer subscription apps) | Strong | +| Average Concierge subscriber LTV (annual) | ~$200 | $100-$300 (premium consumer) | Strong | +| Average Featured Business LTV (annual) | ~$1,164 | $500-$2,000 (local SMB SaaS) | Healthy | +| Consumer CAC (blended) | $0-$3 | $5-$20 (consumer apps) | Exceptional | +| Business CAC | $0-$50 | $100-$500 (local SMB sales) | Exceptional | +| LTV:CAC ratio (consumer) | 16:1+ | >3:1 (good) | Exceptional | +| LTV:CAC ratio (business) | 23:1+ | >3:1 (good) | Exceptional | +| Gross margin | 90-94% | 70-80% (good SaaS) | Excellent | +| Monthly consumer churn | 4-5% | 3-8% (consumer apps) | Target zone | +| Monthly business churn | 3-4% | 3-7% (SMB SaaS) | Good | + +The unit economics are favorable because: +1. **Near-zero marginal delivery cost** - Super Search and LLM API calls cost fractions of a cent per user +2. **Zero-CAC organic acquisition** - Reddit, SCAD ambassadors, and venue cross-promotion dominate early growth +3. **Dual revenue streams** - consumer subscriptions + business placements diversify and compound +4. **Network effects at city density** - each new user in Savannah increases value for other users (more check-ins, more social signals, better ranking) + +### 10.3 Capital Requirements + +HotNow Savannah is designed to be bootstrapped: + +| Item | Cost | Notes | +|------|------|-------| +| Remaining development (~450 hours) | $0 | Built by Germaine / internal team | +| Initial infrastructure setup | $500 | Domain ($33/yr), SSL, minor VPS adjustments | +| Legal (terms, privacy policy, TOS) | $2,000-$3,000 | One-time | +| Brand identity + design | $1,000-$2,000 | Logo, color system, PWA design | +| Seed content curation | $500-$1,000 | Manual venue/event data entry | +| SCAD ambassador program (Year 1) | $1,000-$2,000 | Swag, commissions, stipends | +| Content + social media | $2,000-$4,000 | First 6 months | +| Launch event | $500-$1,000 | Partner venue, basic production | +| **Total initial outlay** | **$7,500-$13,500** | | + +This is approximately half the capital requirement of the original v1 multi-city plan ($15K-$30.5K) and achieves product-market fit validation at lower risk. + +### 10.4 Break-Even Analysis + +| Scenario | Break-Even Point | Timeline (from launch) | +|----------|-----------------|----------------------| +| Consumer-only (Pro + Concierge) | ~150 subscribers | Month 5-6 | +| Consumer + Business | ~80 Pro + 10 businesses | Month 4-5 | +| Including development cost recovery | ~250 Pro + 25 businesses | Month 10-12 | + +Cumulative break-even (recovering full ~$11K Year 1 investment) occurs in Month 3-4 of Year 2, assuming continued growth trajectory within Savannah or expansion to the first additional city. + +--- + +## 11. The Ask + +### 11.1 What We Need to Launch + +| Resource | Details | Timeline | Cost | +|----------|---------|----------|------| +| **Development capacity** | ~450 hours to build PWA, ranking algorithm, API, billing, structured data layer, business portal | Weeks 1-4 | $0 (Germaine's time) | +| **Savannah seed content** | 200-300 manually curated venues and events. SCAD events calendar, Visit Savannah, Savannah Master Calendar ingestion. | Week 1-2 | $500-$1,000 (contractor time if needed) | +| **SCAD ambassador recruitment** | Identify and onboard 5-10 student ambassadors. Swag production (stickers, tote bags). Commission tracking. | Week 2-3 | $200-$500 setup + $50-$100/mo ongoing | +| **Venue partnerships** | Outreach to 20-30 key Savannah venues (River Street bars, Starland galleries, Broughton Street restaurants). QR code signage. | Week 3-4 | $0 (mutual benefit) | +| **Legal review** | Terms of service, privacy policy, data aggregation compliance review. LLC/entity structure. | Week 1-2 | $2,000-$3,000 | +| **Brand identity** | Logo, color system, PWA design, app store assets. Savannah-specific landing page design. | Week 1-2 | $1,000-$2,000 | +| **Domain + DNS setup** | hotnow.io/savannah landing page. DNS + Caddy config for app.hotnow.io, api.hotnow.io. | Week 1 | $0 (already registered) | +| **Beta testers** | 50-100 Savannah users (SCAD students, venue partners, Germaine's network) | Week 3-4 | $0 | +| **Launch event** | "HotNow Savannah Launch Party" at partner venue (River Street or Starland). | Week 4-5 | $500-$1,000 | +| **Go-to-market execution** | Germaine's time for Reddit content, SCAD ambassador management, venue outreach, social media | Ongoing (8-12 hrs/week) | $0 (Germaine's time) | +| **Initial operating capital** | One-time setup + first 3 months of contractor/ambassador costs | Month 1 | $3,000-$5,000 | + +### 11.2 Immediate Decisions Required + +1. **Savannah-first confirmation** - formal approval to pivot from Austin to Savannah as the launch city. This is the single most important decision. Austin remains a future expansion city (Year 2) but Savannah is the launch. + +2. **Timeline commitment** - 3-4 week sprint to MVP, then launch. Can Germaine dedicate focused development time in the next 30 days? The window is now: semester starts late August, SCAD students are arriving, fall tourism season begins September. + +3. **Pricing model confirmation** - are $4.99/$19.99 the right consumer anchor points? Should annual discount be 17% (1 month free) or deeper for the Savannah launch to drive early adoption? + +4. **Business pricing validation** - is $97/mo for featured placement and $47/event for boosts the right level for Savannah's market? Savannah business costs are lower than major metros - should we test $67/mo and $37/event initially? + +5. **Brand identity** - does HotNow operate as "HotNow Savannah" (city-branded, Locale-NYC pattern) or "HotNow" (city-agnostic, with Savannah landing page)? Recommendation: hotnow.io with hotnow.io/savannah landing page. City-agnostic brand preserves expansion optionality. + +6. **Legal entity structure** - does HotNow operate as a division of IT Pro Partner, or as a separate LLC with ITPP as parent? Recommendation: separate LLC (liability isolation for consumer-facing product) with ITPP as managing member. + +7. **Domain strategy** - hotnow.io is already purchased. Should we also acquire hotnowsavannah.com ($12/yr, 301 redirect to hotnow.io/savannah)? Recommendation: yes. Low cost, prevents competitor squatting. + +8. **SCAD ambassador compensation** - free Pro accounts + swag + $5 commission per referral signup. Is this structure right? Should we add a monthly stipend for top ambassadors? + +9. **Expansion trigger** - at what MRR or MAU threshold do we greenlight the second city (Charleston, SC)? Recommendation: 300 Pro subscribers or $5K MRR, whichever comes first. + +### 11.3 What Success Looks Like (Month 12) + +- **350 Pro subscribers** in Savannah, paying $4.99/mo (or $49.99/yr) +- **40 featured businesses** generating $97/mo each in placement revenue +- **~$64,000 ARR** with 90%+ gross margins +- **10,000-25,000 MAU** with 40%+ weekly active retention +- **Savannah fully seeded** with 500+ listings (300 manual + organic growth) +- **SCAD ambassador program** with 8-12 active student ambassadors +- **Reddit flywheel** generating 15-25% of new user acquisition +- **4.5+ star app store rating** with 50+ reviews +- **TikTok/Instagram presence** with 5K-10K combined followers (Savannah-focused content) +- **"What's Hot in Savannah Tonight"** recognized as the go-to local discovery source +- **Team:** Germaine + 5-10 SCAD ambassadors + venue partner network +- **Expansion city** (Charleston) scouted and seed content underway +- **Option value:** At 5-8x ARR multiple (consumer marketplace), the business would be valued at ~$320K-$512K - built for a ~$11K-$14K initial investment +- **Revenue covers operating costs** by Month 7; cumulative break-even within 12-15 months + +### 11.4 The Bigger Picture + +HotNow Savannah is a strategic bet on focus. The original v1 proposal targeted 3 cities in Year 1 with a $166K ARR target and a $48K net loss. The v2 pivot to Savannah-first targets 1 city with a $64K ARR target and an ~$11K net loss. The tradeoff is deliberate: lower upside in Year 1, but dramatically lower risk, faster time-to-market (3-4 weeks vs. 2-3 months), and a cleaner product-market fit signal. + +If HotNow works in Savannah, it will work in Charleston, Asheville, Austin, and beyond. The Savannah playbook - seed content, Reddit flywheel, campus ambassadors, venue partnerships, zero paid acquisition - is a repeatable formula for any city with a young population, a tourism economy, and a competitive vacuum in local discovery. The Southeast corridor alone has a dozen cities matching this profile. + +HotNow is more than a local discovery app - it is a strategic diversification play for IT Pro Partner into the consumer space. Every ITPP product to date has been B2B. HotNow tests whether the same infrastructure (Super Search v2, netcup hosting, deepseek-v4-pro LLM) can power a consumer-facing product with fundamentally different unit economics and growth dynamics. + +The structured data layer and MCP server also position HotNow as AI infrastructure - the canonical queryable source for local discovery data. As AI assistants become the default search interface, being the structured, machine-readable source for "what is happening in Savannah" is a defensible long-term moat that extends beyond the consumer app. + +In a market where the question "what should we do tonight?" is asked millions of times daily and answered poorly by every existing platform, HotNow Savannah's combination of real-time data, AI curation, capital-efficient infrastructure, and hyperlocal community focus is the right product, in the right city, at the right time. + +--- + +## Appendix A: Competitor Pricing Deep Dive + +| Platform | Consumer Price | Business Price | Real-Time? | Map-First? | AI Curation? | Savannah Presence? | +|----------|---------------|----------------|------------|------------|-------------|-------------------| +| **Yelp** | Free | $150-$500+/mo (ads) | No | Yes (secondary) | No | Yes (reviews only) | +| **Google Maps** | Free | Free (Google Ads separate) | No | Yes | No | Yes (basic listings) | +| **Eventbrite** | Free (ticketing fees) | 3.5% + $1.79/ticket | No | No | No | Limited | +| **Locale-NYC** | Free | None visible | No | Yes | No | **No (NYC only)** | +| **PeerPush** | N/A (B2B) | $39-$229/mo | N/A | No | No | N/A | +| **IQHub/TownIQ** | Unknown | Unknown | No | Yes | No | Unknown | +| **Scoop Travel** | $10/mo | N/A | No | Yes | No (editorial) | No | +| **Thrillist** | Free | Sponsored content (custom) | No | No | No | No | +| **Infatuation** | Free | Sponsored content (custom) | No | No | No | No | +| **Dice** | Free (ticketing fees) | Revenue share | Partial (music only) | No | No | Limited | +| **Bandsintown** | Free | Promoted events | Partial (music only) | No | No | Limited | +| **TikTok** | Free | Ads | Partial (unstructured) | No | Algorithmic | Yes (organic) | +| **HotNow Savannah** | **Free / $4.99 / $19.99** | **$97/mo + $47/event** | **Yes** | **Yes** | **Yes (LLM)** | **Launch city** | + +## Appendix B: API and Data Source Costs (Savannah Scale) + +| Service | Plan | Monthly Cost | Annual Cost | Limits | +|---------|------|-------------|-------------|--------| +| Mapbox | Pay-as-you-go | $50-$100 (est.) | $600-$1,200 | 50K free loads; Savannah MAU stays under threshold | +| Eventbrite API | Free tier | $0 | $0 | Rate-limited; sufficient for Savannah aggregation | +| Ticketmaster API | Free tier | $0 | $0 | Rate-limited; sufficient | +| Meetup API | Free tier | $0 | $0 | Rate-limited | +| Facebook Events API | Free tier | $0 | $0 | Limited availability | +| deepseek-v4-pro | Pay-per-token | $50-$150 (est.) | $600-$1,800 | Variable; scales with user count | +| Super Search v2 | Internal | $0 | $0 | Already running on ITPP infrastructure | +| Netcup VPS | Existing infra | $0 (absorbed) | $0 | Existing ITPP servers | +| OneSignal (push) | Free tier | $0 | $0 | 10K free subscribers | +| Resend (email) | Free tier | $20 | $240 | 3K emails/mo free; scales | + +## Appendix C: Savannah Competitive Intelligence Summary (Batch 001) + +| Finding | Source | Impact on HotNow Savannah | +|---------|--------|--------------------------| +| Reddit community flywheel is the #1 growth tactic for local discovery | Locale-NYC analysis, Batch 001 Master Synthesis | Adopted as primary GTM channel. Zero-cost, hyper-targeted, proven. | +| Structured AI-readable data + MCP server is an emerging competitive moat | PeerPush analysis, Batch 001 Master Synthesis | Added to v2 product architecture. Future-proofs for AI assistant search. | +| Neighborhood/corridor browsing is a P1 feature for user engagement | Locale-NYC deep dive, Team Charlie report | Added to Savannah product roadmap. Historic District, Starland, Midtown, Tybee Island, Pooler. | +| City-branded landing pages convert better than generic | Locale-NYC analysis | hotnow.io/savannah landing page replaces generic landing page for Savannah visitors. | +| Event-first marketing language outperforms venue-first | Locale-NYC deep dive | "What's Hot in Savannah Tonight" content series leads with events. | +| Dual B2B/B2C architecture is worth studying for business analytics features | IQHub/TownIQ analysis | Deferred to Year 2. Business analytics portal for Featured Placement customers. | +| Zero paid acquisition is viable for community-driven growth | Locale-NYC, MicroLaunch, PeerPush analyses | Confirmed. Months 1-6: zero paid acquisition budget. | + +## Appendix D: Glossary + +| Term | Definition | +|------|-----------| +| **ARR** | Annual Recurring Revenue - the annualized value of subscription contracts | +| **MRR** | Monthly Recurring Revenue | +| **MAU** | Monthly Active Users | +| **CAC** | Customer Acquisition Cost - total sales and marketing spend / new customers acquired | +| **LTV** | Lifetime Value - average revenue per customer over their lifetime | +| **PWA** | Progressive Web App - a web application that behaves like a native mobile app | +| **MCP** | Model Context Protocol - protocol for AI assistant integration with external data sources | +| **SCAD** | Savannah College of Art and Design - 15,000+ students in Savannah, primary early adopter target | +| **COGS** | Cost of Goods Sold - direct costs attributable to delivering the service | +| **JSON-LD** | JavaScript Object Notation for Linked Data - structured data format for AI/SEO readability | +| **Reddit Flywheel** | Systematic community cross-posting strategy that drives organic user acquisition | + +--- + +**Document prepared by:** HotNow Product Division, IT Pro Partner +**Contact:** Germaine Brown +**Classification:** Confidential - For Advisory Team Review Only +**Version:** 2.0 - August 11, 2026 (City Pivot: Austin -> Savannah, GA) +**Based on:** Competitive Landscape Research Batch 001 (August 10, 2026), Locale-NYC Deep Dive (Team Charlie), HotNow v1 Proposal (August 1, 2026)