Files
unraid-docker-manager/.planning/phases/01-foundation/01-RESEARCH.md
T
Lucas Berger 2cb6f3a689 docs(01): research phase domain
Phase 1: Foundation
- Standard stack identified (n8n Telegram nodes, Bot API)
- Architecture patterns documented (auth, echo, formatting)
- Pitfalls catalogued (webhook SSL, merge deadlock, rate limits)
- Code examples from official sources

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 18:28:17 -05:00

21 KiB
Raw Blame History

Phase 1: Foundation - Research

Researched: 2026-01-28 Domain: Telegram Bot + n8n Workflow Automation Confidence: HIGH

Summary

This phase establishes basic Telegram ↔ n8n communication for a Docker management bot. The research confirms that n8n provides robust, production-ready Telegram integration through dedicated nodes (Telegram Trigger for receiving messages, Telegram node for sending). The standard approach uses Telegram's webhook architecture, which n8n manages automatically, requiring only BotFather token setup and proper environment configuration.

The research revealed critical security patterns for user authentication (verifying from.id in message objects), webhook security (secret tokens), and n8n environment variable management. Common pitfalls center around webhook URL configuration, SSL/TLS requirements, and merge node blocking issues.

Primary recommendation: Use n8n's Telegram Trigger node (automatic webhook management) + Telegram node (send operations) with environment variable-based user ID authentication. This approach is well-documented, actively maintained, and handles all webhook complexity automatically.

Standard Stack

The established libraries/tools for Telegram bot development with n8n:

Core

Library Version Purpose Why Standard
n8n Latest (2026) Workflow automation platform Official Telegram node support, active development, webhook abstraction
Telegram Bot API Current Message sending/receiving Official Telegram API, comprehensive documentation
n8n Telegram Trigger Built-in Receive messages via webhook Automatic webhook registration, event filtering
n8n Telegram Node Built-in Send messages and operations Full Bot API coverage, supports all message types

Supporting

Library Version Purpose When to Use
n8n IF Node Built-in Conditional branching User ID verification, message routing
n8n Code Node Built-in Custom JavaScript/Python Complex message parsing, timestamp formatting
n8n Stop And Error Built-in Error workflow triggering Handle unauthorized access, API failures

Alternatives Considered

Instead of Could Use Tradeoff
Telegram Trigger Generic Webhook Manual webhook management, no automatic registration
n8n python-telegram-bot More control but requires coding, hosting, maintenance
Environment Variables Hardcoded Values Less flexible, requires workflow edits to change config

Installation: n8n already running on Unraid. No additional packages required for Telegram integration.

Architecture Patterns

Telegram Bot Workflow
├── Telegram Trigger          # Entry point - receives all messages
├── IF (User ID Check)        # Authentication gate
│   ├── TRUE branch
│   │   └── Code (Echo)       # Echo message with timestamp
│   │   └── Telegram (Send)   # Send response back
│   └── FALSE branch
│       └── (no nodes)        # Silent ignore - workflow ends

Pattern 1: User Authentication via Message Object

What: Verify sender identity using Telegram's from.id field When to use: Every workflow requiring access control Example:

// In IF node expression
// Source: https://core.telegram.org/bots/api#message
{{ $json.message.from.id }} === {{ $env.TELEGRAM_USER_ID }}

Key fields from Message object:

  • message.from.id - Unique user identifier (up to 52-bit integer)
  • message.from.username - User's @username (optional, can change)
  • message.chat.id - Chat identifier (needed for sending responses)
  • message.text - Message content

Pattern 2: Echo with Metadata

What: Confirm message processing with timestamp proof When to use: Testing webhook round-trip, debugging message flow Example:

// In Code node
// Source: n8n best practices
const message = $input.item.json.message.text;
const timestamp = new Date().toISOString();
const userId = $input.item.json.message.from.id;

return {
  chatId: $input.item.json.message.chat.id,
  text: `Got: ${message}\n\nProcessed: ${timestamp}\nUser ID: ${userId}`
};

Pattern 3: Environment Variable Configuration

What: Store sensitive config (user ID, tokens) in environment variables When to use: All credentials and user-specific configuration Example:

# In n8n Docker environment
# Source: https://docs.n8n.io/hosting/configuration/environment-variables/
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
TELEGRAM_USER_ID=987654321

Access in workflows:

{{ $env.TELEGRAM_USER_ID }}
{{ $env.TELEGRAM_BOT_TOKEN }}

Pattern 4: Telegram Message Formatting

What: Use HTML or Markdown for rich text formatting When to use: Error messages, status updates, formatted responses Example:

// HTML formatting (recommended - more robust)
// Source: https://core.telegram.org/bots/api#html-style
const formatted = `<b>Container Status</b>
<code>Plex:</code> Running
<code>Sonarr:</code> Stopped

<i>Last updated: ${timestamp}</i>`;

// In Telegram node, set Parse Mode to "HTML"

Available formatting:

  • HTML: <b>bold</b>, <i>italic</i>, <code>code</code>, <pre>block</pre>
  • MarkdownV2: *bold*, _italic_, `code`, ```block```
  • Default: HTML (more forgiving of syntax errors)

Pattern 5: Silent Ignore for Unauthorized Users

What: End workflow without response for unauthorized messages When to use: Security pattern - bot appears offline to strangers Example:

Telegram Trigger → IF (auth check)
                    ├── TRUE → (process message)
                    └── FALSE → (no nodes, workflow ends)

Why this works:

  • No error thrown
  • No response sent
  • No indication bot exists
  • Logs optional (can disable execution saving for production)

Anti-Patterns to Avoid

  • Hardcoded credentials: Store tokens in credentials manager or env vars, never in workflow JSON
  • Waiting on both merge branches: IF splits create conditional paths - don't merge both branches if one may not execute
  • Saving execution progress in production: Causes excessive database writes (3000/day for 30-node × 100 executions)
  • Using username for auth: Usernames can change; use immutable from.id instead
  • Forgetting HTTPS requirement: Telegram webhooks require TLS 1.2+ on ports 443, 80, 88, or 8443

Don't Hand-Roll

Problems that look simple but have existing solutions:

Problem Don't Build Use Instead Why
Webhook management Custom webhook server n8n Telegram Trigger Automatic registration, handles test/prod URLs, manages SSL
Message parsing Custom JSON extraction n8n's $json syntax Built-in access to all message fields, expression editor
User auth validation Custom middleware IF node with env variable Simple, visual, auditable in workflow
Telegram formatting String concatenation Parse Mode (HTML/Markdown) Handles escaping, provides rich formatting, less error-prone
Retry logic Custom loops n8n error workflow + retries Centralized error handling, exponential backoff, monitoring

Key insight: Telegram's Bot API and n8n's integration layer handle webhook security, message routing, and connection management. Building custom solutions means reimplementing TLS verification, update deduplication, and rate limiting—all of which are already solved.

Common Pitfalls

Pitfall 1: Webhook URL Misconfiguration

What goes wrong: Telegram Trigger stays in "Test URL" mode, production webhook never registers Why it happens: Missing or incorrect WEBHOOK_URL environment variable in n8n How to avoid:

  • Set WEBHOOK_URL=https://your-n8n-domain.com/ (must be HTTPS)
  • Set N8N_PROXY_HOPS=1 if behind reverse proxy
  • Verify URL is publicly accessible on ports 443, 80, 88, or 8443 Warning signs:
  • Workflow only works when manually executed
  • Telegram shows "Connection not secure" or no webhook registered
  • Messages not triggering workflow automatically

Source: https://docs.n8n.io/hosting/configuration/environment-variables/

Pitfall 2: Certificate/SSL Issues

What goes wrong: Webhook registration fails silently or with "certificate verification failed" Why it happens:

  • Self-signed certificates not uploaded to Telegram
  • Missing intermediate certificates in chain
  • Using TLS version < 1.2
  • Wrong domain in certificate CN How to avoid:
  • Use Let's Encrypt or valid CA certificate
  • If self-signed, upload cert when calling setWebhook
  • Verify cert chain includes all intermediates
  • Test with: curl --tlsv1.2 -v -k https://your-domain:443/ Warning signs:
  • setWebhook returns error about SSL
  • Webhook works locally but not in production
  • Certificate warnings in browser

Source: https://core.telegram.org/bots/webhooks

Pitfall 3: Secret Token Not Verified

What goes wrong: Webhook accepts spoofed requests from attackers Why it happens: Not checking X-Telegram-Bot-Api-Secret-Token header How to avoid:

  • Telegram Trigger handles this automatically
  • If using custom webhook: verify header matches your secret
  • Use long random string for secret token Warning signs:
  • Receiving messages you didn't send
  • Unexpected workflow executions
  • Security audit flags missing token verification

Note: n8n Telegram Trigger validates this automatically—only relevant if building custom webhook.

Source: https://core.telegram.org/bots/api

Pitfall 4: Merge Node Deadlock

What goes wrong: Workflow hangs indefinitely waiting for data that never arrives Why it happens: IF node creates conditional branch, but Merge waits for both TRUE and FALSE paths How to avoid:

  • Don't merge after conditional splits unless both paths always execute
  • Use Switch node instead of IF for multiple outcomes that need merging
  • Design workflows to avoid needing to merge conditional branches Warning signs:
  • Workflow shows "Waiting for input" forever
  • Only one branch of IF executes but merge expects two
  • Manual execution works, but automated runs hang

Source: https://medium.com/@juanm.acebal/7-common-n8n-workflow-mistakes-that-can-break-your-automations-9638903fb076

Pitfall 5: Rate Limit Exceeded

What goes wrong: Telegram returns 429 errors, messages fail to send Why it happens: Sending >30 messages per second to same chat How to avoid:

  • Add delay between messages if sending multiple
  • Use batch processing with sleep intervals
  • For high volume, implement queue-based sending Warning signs:
  • sendMessage returns HTTP 429
  • Messages delivered inconsistently
  • Some messages disappear

Source: https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.telegram/message-operations.md

Pitfall 6: Using getUpdates with Active Webhook

What goes wrong: Bot stops receiving webhook updates Why it happens: Telegram's API prevents both polling (getUpdates) and webhooks simultaneously How to avoid:

  • Choose one method: webhook (for production) or polling (for local dev)
  • Delete webhook before using getUpdates: call deleteWebhook
  • n8n Telegram Trigger uses webhooks—don't mix with polling libraries Warning signs:
  • Webhook suddenly stops working after testing with getUpdates
  • Messages not delivered despite active workflow
  • Telegram API shows "conflict" errors

Source: https://core.telegram.org/bots/webhooks

Code Examples

Verified patterns from official sources:

Creating Telegram Credential in n8n

# 1. Get token from BotFather
# Open Telegram, search @BotFather
# Send: /newbot
# Follow prompts for name and username (must end with 'bot')
# Copy the token: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz

# 2. Add to n8n
# UI: Credentials → + → Telegram API
# Paste token in "Access Token" field
# Save

# Source: https://docs.n8n.io/integrations/builtin/credentials/telegram/

Telegram Trigger Configuration

// Telegram Trigger Node Settings
{
  "credential": "Telegram API",
  "updates": "message",  // Trigger on new messages
  "additionalFields": {
    "download": false  // Don't auto-download files/photos
  }
}

// Output structure ($json):
{
  "update_id": 123456789,
  "message": {
    "message_id": 1,
    "from": {
      "id": 987654321,
      "is_bot": false,
      "first_name": "John",
      "username": "johndoe"
    },
    "chat": {
      "id": 987654321,
      "first_name": "John",
      "username": "johndoe",
      "type": "private"
    },
    "date": 1640000000,
    "text": "Hello bot!"
  }
}

// Source: https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/

User ID Authentication Check

// IF Node Expression
// Compare incoming user ID with authorized user
{{ $json.message.from.id }} === {{ $('Telegram Trigger').item.json.message.from.id }}

// Or using environment variable (recommended)
{{ $json.message.from.id.toString() }} === {{ $env.TELEGRAM_USER_ID }}

// Note: Ensure TELEGRAM_USER_ID is set in n8n environment:
// Docker: environment variable in docker-compose.yml
// Docker run: -e TELEGRAM_USER_ID=987654321

// Source: https://core.telegram.org/bots/api#message

Echo Message with Timestamp

// Code Node (JavaScript)
// Generate echo response with metadata

const message = $input.item.json.message;
const timestamp = new Date().toISOString();

return {
  chatId: message.chat.id,
  text: `Got: ${message.text}\n\nProcessed at: ${timestamp}\nFrom user: ${message.from.id}`
};

// Source: n8n Code node documentation

Send Formatted Response

// Telegram Node Configuration
{
  "resource": "message",
  "operation": "sendMessage",
  "chatId": "={{ $json.chatId }}",
  "text": "={{ $json.text }}",
  "additionalFields": {
    "parse_mode": "HTML",
    "disable_notification": false,
    "append_attribution": false  // Remove "sent with n8n" footer
  }
}

// HTML formatting example:
const text = `<b>Echo Test</b>
<code>Message:</code> ${message.text}
<code>Time:</code> ${timestamp}

<i>User ID verified: ${userId}</i>`;

// Available HTML tags:
// <b>bold</b>, <i>italic</i>, <code>inline code</code>
// <pre>code block</pre>, <a href="url">link</a>

// Source: https://core.telegram.org/bots/api#html-style

Environment Variable Setup

# docker-compose.yml for n8n
version: '3'
services:
  n8n:
    image: n8nio/n8n:latest
    environment:
      - WEBHOOK_URL=https://n8n.example.com/
      - N8N_PROXY_HOPS=1
      - TELEGRAM_USER_ID=987654321  # Your Telegram user ID
      - N8N_ENCRYPTION_KEY=your_encryption_key
    ports:
      - "5678:5678"
    volumes:
      - ~/.n8n:/home/node/.n8n

# Get your user ID:
# 1. Message your bot
# 2. Check workflow execution data: message.from.id
# 3. Add to environment variable

# Source: https://docs.n8n.io/hosting/configuration/environment-variables/

Silent Ignore Pattern (No Response)

// Workflow structure for unauthorized users

// IF Node (check user ID)
// TRUE branch:
//   → Code (process message)
//   → Telegram (send response)
// FALSE branch:
//   → (empty - workflow just ends)

// No need for error nodes or explicit "ignore" logic
// Simply don't add nodes to the FALSE branch
// Workflow completes, no response sent, appears offline

// Optional: Disable execution saving for production
// Settings → Workflow → Save Execution Progress: OFF

// Source: n8n workflow best practices

State of the Art

Old Approach Current Approach When Changed Impact
Long polling (getUpdates) Webhooks Always recommended Real-time delivery, sub-second latency vs polling intervals
Hardcoded credentials Environment variables + _FILE suffix 2023+ Docker Secrets/K8s Secrets support, better security
Markdown formatting HTML formatting (default) Ongoing HTML more forgiving of syntax errors, same capabilities
Manual webhook registration n8n Telegram Trigger auto-registration n8n native No manual setWebhook calls, handles test/prod URLs
Username-based auth User ID-based auth Always recommended IDs immutable, usernames can change
Custom error handling per node Centralized error workflow n8n best practice 2025+ Single "Mission Control" for all workflow errors

Deprecated/outdated:

  • Markdown (Legacy): Use MarkdownV2 or HTML instead—legacy version has parsing inconsistencies
  • SSLv2/3, TLS 1.0/1.1: Telegram requires TLS 1.2+ for webhook connections
  • HTTP webhooks: Never supported—HTTPS always required
  • Save Execution Progress in production: Creates excessive DB load; use only for debugging
  • Hardcoded tokens in workflow JSON: Use credentials manager or environment variables

Open Questions

Things that couldn't be fully resolved:

  1. n8n Encryption Key Management

    • What we know: n8n encrypts credentials before database storage using N8N_ENCRYPTION_KEY
    • What's unclear: Best practice for key rotation without breaking existing credentials
    • Recommendation: Set encryption key in initial setup, back up key securely, avoid rotation unless compromised
  2. Webhook IP Whitelisting in Unraid

    • What we know: Telegram sends webhooks from 149.154.160.0/20 and 91.108.4.0/22
    • What's unclear: How to configure IP whitelisting in Unraid firewall/reverse proxy
    • Recommendation: Configure at reverse proxy level (nginx/Caddy), not critical for Phase 1 (HTTPS + secret token sufficient)
  3. Multi-User Support Future-Proofing

    • What we know: Current design uses single TELEGRAM_USER_ID env variable
    • What's unclear: Best pattern for scaling to multiple authorized users later
    • Recommendation: For Phase 1, stick with single user ID; future phases can use array in env var or database lookup
  4. Error Workflow Integration

    • What we know: n8n supports centralized error workflows triggered by failures
    • What's unclear: Whether to implement error workflow in Phase 1 or defer to monitoring phase
    • Recommendation: Defer to later phase—Phase 1 focus is basic communication; add error workflow when building actual Docker commands

Sources

Primary (HIGH confidence)

Secondary (MEDIUM confidence)

Tertiary (LOW confidence)

Metadata

Confidence breakdown:

  • Standard stack: HIGH - Context7 + official docs confirm n8n has native, maintained Telegram support
  • Architecture: HIGH - Patterns drawn from official n8n docs and Telegram API documentation
  • Pitfalls: HIGH - Verified through official docs (webhook SSL, rate limits) and established community patterns (merge deadlock, execution saving)

Research date: 2026-01-28 Valid until: ~60 days (stable APIs, but n8n updates monthly—recheck before major version changes)

Research gaps filled:

  • Telegram webhook security requirements (secret token, TLS 1.2+, ports)
  • n8n Telegram Trigger automatic webhook management
  • User ID authentication pattern using Message.from.id
  • Environment variable configuration for n8n
  • Message formatting options (HTML recommended over Markdown)
  • Common workflow mistakes (merge deadlock, execution saving)
  • BotFather setup flow for token generation

Key decisions supported by research:

  • n8n for orchestration: Native Telegram support confirmed, production-ready
  • User ID auth: from.id is immutable, proper field for authentication
  • Single-user pattern: Simple environment variable approach validated