# Gmail Push Notifications — Deployment Patterns ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Google Cloud Platform │ │ │ │ ┌─────────────┐ ┌──────────────────────────────────────┐ │ │ │ Gmail API │────────▶│ Pub/Sub Topic: gmail-push │ │ │ └─────────────┘ └──────────────────────────────────────┘ │ │ │ New email │ │ │ │ triggers ├──── Push Subscription │ │ │ watch() │ (pushEndpoint) │ │ │ │ │ │ │ └──── Pull Subscription │ │ │ (no endpoint) │ │ │ │ └───────┼─────────────────────────────────────────────────────────────┘ │ │ │ │ │ History API │ │ (fallback poll) │ │ │ ▼ ▼ ┌────────────────────────────────────────────────────────────────────┐ │ Flynn │ │ │ │ ┌──────────────┐ ┌────────────────┐ ┌────────────────────┐ │ │ │ Push Handler │◀───│ Gateway Server │ │ Pull Subscriber │ │ │ │ │ │ │ │ │ │ │ │ POST /gmail/ │ │ Port 18800 │ │ Pull every 60s │ │ │ │ push │ └────────────────┘ └────────────────────┘ │ │ └──────────────┘ │ │ │ │ │ │ │ └──────────────┬───────────────────────────┘ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ GmailWatcher │ │ │ │ (ChannelAdapter)│ │ │ └──────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ AgentOrchestrator│ │ │ └──────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ Output Channel │ │ │ │ (Telegram/etc) │ │ │ └──────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## Pattern 1: Push Only (Public Endpoint) **Config**: ```yaml automation: gmail: pubsub_topic: projects/my-project/topics/gmail-push ``` **Flow**: 1. Gmail API sends notification to Pub/Sub topic 2. Push subscription forwards to `POST https://flynn.example.com/gmail/push` 3. Flynn processes immediately (~real-time) 4. History API polls every 5min (fallback) **Requirements**: - Public IP or domain - DNS A/AAAA record - SSL/TLS certificate - Firewall allows inbound HTTP/HTTPS **Network**: ``` Internet ──▶ Public IP ──▶ Flynn Gateway ──▶ GmailWatcher (Port 443) (Port 18800) ``` --- ## Pattern 2: Push Only (Tailscale Funnel) **Config**: ```yaml server: tailscale: serve: true funnel: true # ← Required for Google to reach endpoint automation: gmail: pubsub_topic: gmail-push ``` **Flow**: 1. Gmail API sends notification to Pub/Sub topic 2. Push subscription forwards to `POST https://flynn.tailnet-name.ts.net/gmail/push` 3. Tailscale funnel proxies to local Flynn 4. Flynn processes immediately (~real-time) **Requirements**: - Tailscale installed and authenticated - Funnel enabled (exposes service to public internet) **Network**: ``` Internet ──▶ Tailscale Funnel ──▶ Flynn Gateway ──▶ GmailWatcher (*.ts.net) (Port 18800) ``` --- ## Pattern 3: Pull Only (Private Deployment) **Config**: ```yaml automation: gmail: pubsub_subscription_id: projects/my-project/subscriptions/gmail-pull pubsub_pull_interval: "60s" ``` **Flow**: 1. Gmail API sends notification to Pub/Sub topic 2. Message sits in pull subscription queue 3. Flynn polls subscription every 60s 4. Flynn pulls and processes messages 5. History API polls every 5min (fallback) **Requirements**: - None (no inbound connections) - Works behind NAT, firewall, VPN **Network**: ``` Flynn ──(poll)──▶ GCP Pub/Sub API ──▶ Pull Subscription (HTTPS outbound) ``` --- ## Pattern 4: Hybrid (Recommended) **Config**: ```yaml automation: gmail: pubsub_topic: gmail-push pubsub_subscription_id: gmail-pull pubsub_pull_interval: "60s" poll_interval: "300s" ``` **Flow**: 1. Gmail API sends notification to Pub/Sub topic 2. **Push subscription** forwards to Flynn gateway (if reachable) - Processes immediately (~real-time) 3. **Pull subscription** also receives message (60s latency) - Deduplicates via historyId comparison 4. **History API** polls every 5min (tertiary fallback) **Requirements**: - Push subscription: Public endpoint OR Tailscale funnel - Pull subscription: Always works (no inbound) **Network**: ``` ┌──────────────────────────────────────────────┐ │ Primary: Internet ──▶ Flynn (push) │ ~Real-time ├──────────────────────────────────────────────┤ │ Fallback: Flynn ──(poll)──▶ GCP (pull) │ ~60s latency ├──────────────────────────────────────────────┤ │ Tertiary: Flynn ──(poll)──▶ Gmail History │ ~300s latency └──────────────────────────────────────────────┘ ``` **Benefits**: - ✅ **Best latency** when push is reachable - ✅ **Always reliable** (pull fallback) - ✅ **Network-agnostic** (works behind NAT/firewall) - ✅ **Self-healing** (network changes don't break it) --- ## Pattern 5: Polling Only (Development) **Config**: ```yaml automation: gmail: poll_interval: "60s" # No pubsub_topic or pubsub_subscription_id ``` **Flow**: 1. Flynn polls Gmail History API every 60s 2. Fetches new messages since last historyId 3. No GCP Pub/Sub setup required **Requirements**: - None (just OAuth2 credentials) **Network**: ``` Flynn ──(poll)──▶ Gmail History API (HTTPS outbound) ``` **Use Case**: - Development/testing - Quick setup without GCP project - Low-volume inboxes (polling is fine) --- ## Comparison Table | Pattern | Latency | Reliability | Network Req | GCP Setup | Recommended For | |---------|---------|-------------|-------------|-----------|-----------------| | **Push only (public)** | ~1s | High* | Public IP | Topic + push sub | Production with ingress | | **Push only (funnel)** | ~1s | High* | Tailscale funnel | Topic + push sub | Private with funnel | | **Pull only** | ~60s | High | Any | Topic + pull sub | Private behind NAT | | **Hybrid** ⭐ | ~1s† | Highest | Any | Topic + both subs | All production | | **Polling only** | ~300s | Medium | Any | None | Development only | \* Single point of failure (push endpoint unreachable = delayed notification) † Falls back to pull (~60s) if push fails --- ## GCP Setup Commands ### 1. Create Topic ```bash gcloud pubsub topics create gmail-push --project=my-project ``` ### 2. Grant Gmail API Permission ```bash gcloud pubsub topics add-iam-policy-binding projects/my-project/topics/gmail-push \ --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \ --role=roles/pubsub.publisher ``` ### 3. Create Push Subscription (for push mode) ```bash gcloud pubsub subscriptions create gmail-push-sub \ --topic=gmail-push \ --push-endpoint=https://flynn.example.com/gmail/push \ --ack-deadline=60 ``` ### 4. Create Pull Subscription (for pull mode) ```bash gcloud pubsub subscriptions create gmail-pull-sub \ --topic=gmail-push \ --ack-deadline=60 ``` ### 5. Verify Setup ```bash # List subscriptions gcloud pubsub subscriptions list --filter="topic:gmail-push" # Test pull subscription gcloud pubsub subscriptions pull gmail-pull-sub --limit=5 ``` --- ## Troubleshooting Decision Tree ``` Start here: flynn doctor │ ├─▶ ✓ Gmail configured (push + pull + poll-fallback) │ └─▶ ✅ Hybrid mode active (best config) │ ├─▶ ✓ Gmail configured (push + poll-fallback) │ └─▶ ⚠️ Single point of failure (add pull subscription) │ ├─▶ ⚠️ Gmail configured (push ⚠️ push requires public endpoint) │ └─▶ Enable Tailscale funnel OR add pull subscription │ ├─▶ ✓ Gmail configured (pull + poll-fallback) │ └─▶ ✅ Reliable but slower (60s latency) │ └─▶ ✓ Gmail configured (poll) └─▶ ⚠️ Slow (300s latency) — add push or pull ``` --- ## Migration Guide ### From: Polling Only ```yaml # Before automation: gmail: poll_interval: "60s" ``` ### To: Hybrid (Recommended) ```yaml # After automation: gmail: pubsub_topic: gmail-push # Add push pubsub_subscription_id: gmail-pull-sub # Add pull pubsub_pull_interval: "60s" poll_interval: "300s" # Keep as fallback ``` **Steps**: 1. Create GCP topic and subscriptions (see above) 2. Update config with new fields 3. Restart Flynn: `systemctl restart flynn` 4. Verify: `flynn doctor` shows "push + pull + poll-fallback"