docs: complete v1.1 research (4 researchers + synthesis)
Files: - STACK.md: Socket proxy, n8n API, Telegram keyboards - FEATURES.md: Table stakes, differentiators, MVP scope - ARCHITECTURE.md: Integration points, data flow changes - PITFALLS.md: Top 5 risks with prevention strategies - SUMMARY.md: Executive summary, build order, confidence Key findings: - Stack: LinuxServer socket-proxy, HTTP Request nodes for keyboards - Architecture: TCP curl migration (~15 nodes), new callback routes - Critical pitfall: Socket proxy breaks existing curl commands Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
# Stack Research: v1.1 Features
|
||||
|
||||
**Project:** Unraid Docker Manager
|
||||
**Researched:** 2026-02-02
|
||||
**Focus:** Stack additions for n8n API, Docker socket security, Telegram keyboards, Unraid integration
|
||||
|
||||
## n8n API Access
|
||||
|
||||
### Overview
|
||||
|
||||
n8n provides a public REST API for programmatic workflow management. This enables Claude Code to read, update, and test workflows without manual UI interaction.
|
||||
|
||||
### Authentication
|
||||
|
||||
**Method:** API Key via HTTP header
|
||||
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| Header name | `X-N8N-API-KEY` |
|
||||
| Key location | n8n UI: Settings > n8n API > Create an API key |
|
||||
| Base path | `/api/v1` |
|
||||
|
||||
**Environment Variables:**
|
||||
- `N8N_PUBLIC_API_DISABLED=false` (default) - API enabled
|
||||
- `N8N_PUBLIC_API_SWAGGERUI_DISABLED=false` (default) - Swagger UI enabled
|
||||
|
||||
The API is **enabled by default** on self-hosted n8n. No additional configuration needed unless it was explicitly disabled.
|
||||
|
||||
### Key Endpoints
|
||||
|
||||
| Endpoint | Method | Purpose |
|
||||
|----------|--------|---------|
|
||||
| `/api/v1/workflows` | GET | List all workflows |
|
||||
| `/api/v1/workflows/{id}` | GET | Get workflow JSON |
|
||||
| `/api/v1/workflows/{id}` | PUT | Update workflow |
|
||||
| `/api/v1/workflows/{id}/activate` | POST | Activate workflow |
|
||||
| `/api/v1/workflows/{id}/deactivate` | POST | Deactivate workflow |
|
||||
| `/api/v1/executions` | GET | List executions (with logs) |
|
||||
| `/api/v1/executions/{id}` | GET | Get execution details |
|
||||
|
||||
### API Playground
|
||||
|
||||
Self-hosted n8n includes a built-in Swagger UI at `/api/v1/docs` (or similar path based on configuration). This provides interactive documentation for testing API calls.
|
||||
|
||||
### Integration with Claude Code
|
||||
|
||||
To enable Claude Code workflow management:
|
||||
|
||||
1. Create API key in n8n Settings > n8n API
|
||||
2. Store key securely (not in repository)
|
||||
3. Use HTTP requests to n8n API endpoints
|
||||
4. Example: `curl -H "X-N8N-API-KEY: <key>" http://localhost:5678/api/v1/workflows`
|
||||
|
||||
**Confidence:** MEDIUM - Official docs confirm API exists and authentication method. Specific endpoint paths verified through multiple sources but not directly tested.
|
||||
|
||||
**Sources:**
|
||||
- [n8n API Authentication](https://docs.n8n.io/api/authentication/)
|
||||
- [n8n API Reference](https://docs.n8n.io/api/api-reference/)
|
||||
- [Disable Public API](https://docs.n8n.io/hosting/securing/disable-public-api/)
|
||||
|
||||
---
|
||||
|
||||
## Docker Socket Security
|
||||
|
||||
### The Problem
|
||||
|
||||
Current setup mounts Docker socket directly into internet-exposed n8n container:
|
||||
```
|
||||
-v /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
This is a security risk: if n8n is compromised, attacker has full Docker (root) access to the host.
|
||||
|
||||
### Solution: Docker Socket Proxy
|
||||
|
||||
A socket proxy sits between containers and the Docker socket, filtering API requests to only allow specific operations.
|
||||
|
||||
### Option Comparison
|
||||
|
||||
| Feature | Tecnativa | LinuxServer | Wollomatic |
|
||||
|---------|-----------|-------------|------------|
|
||||
| Base | HAProxy (Alpine) | HAProxy (Alpine) | Go (scratch) |
|
||||
| Image size | ~10MB | ~10MB | ~3MB |
|
||||
| Config method | Environment variables | Environment variables | Regex allowlists |
|
||||
| Granularity | Per-API section | Per-API section | Per-endpoint regex |
|
||||
| Active maintenance | Yes | Yes | Yes |
|
||||
| Unraid compatibility | Yes | Yes | Yes |
|
||||
|
||||
### Recommendation: LinuxServer/socket-proxy
|
||||
|
||||
**Why:** Drop-in replacement for Tecnativa with better documentation and active LinuxServer community support. Familiar to Unraid users.
|
||||
|
||||
### Configuration for This Project
|
||||
|
||||
The bot needs these Docker API operations:
|
||||
- List containers (`/containers/json`)
|
||||
- Inspect container (`/containers/{id}/json`)
|
||||
- Start container (`/containers/{id}/start`)
|
||||
- Stop container (`/containers/{id}/stop`)
|
||||
- Restart container (`/containers/{id}/restart`)
|
||||
- Pull image (`/images/create`)
|
||||
- Create container (`/containers/create`)
|
||||
- Remove container (`/containers/{id}`)
|
||||
- Container logs (`/containers/{id}/logs`)
|
||||
|
||||
**Required Environment Variables:**
|
||||
|
||||
```bash
|
||||
# LinuxServer socket-proxy configuration
|
||||
CONTAINERS=1 # Container list/inspect
|
||||
IMAGES=1 # Image pull
|
||||
POST=1 # Enable POST requests (needed for start/stop/restart/create)
|
||||
ALLOW_START=1 # /containers/{id}/start
|
||||
ALLOW_STOP=1 # /containers/{id}/stop
|
||||
ALLOW_RESTARTS=1 # /containers/{id}/restart (also enables /kill)
|
||||
|
||||
# Keep defaults (already enabled)
|
||||
EVENTS=1 # Default
|
||||
PING=1 # Default
|
||||
VERSION=1 # Default
|
||||
```
|
||||
|
||||
### Deployment Architecture
|
||||
|
||||
```
|
||||
[Internet]
|
||||
|
|
||||
[n8n container]
|
||||
|
|
||||
[socket-proxy] <-- internal network only
|
||||
|
|
||||
[Docker socket]
|
||||
```
|
||||
|
||||
**n8n container changes:**
|
||||
- Remove: `-v /var/run/docker.sock:/var/run/docker.sock`
|
||||
- Add: `DOCKER_HOST=tcp://socket-proxy:2375`
|
||||
- Add: Connect to same Docker network as socket-proxy
|
||||
|
||||
**Socket-proxy container:**
|
||||
```bash
|
||||
docker run -d \
|
||||
--name socket-proxy \
|
||||
--restart=unless-stopped \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
--read-only \
|
||||
--tmpfs /run \
|
||||
-e CONTAINERS=1 \
|
||||
-e IMAGES=1 \
|
||||
-e POST=1 \
|
||||
-e ALLOW_START=1 \
|
||||
-e ALLOW_STOP=1 \
|
||||
-e ALLOW_RESTARTS=1 \
|
||||
--network internal \
|
||||
lscr.io/linuxserver/socket-proxy:latest
|
||||
```
|
||||
|
||||
**CRITICAL:** Never expose socket-proxy port (2375) to external networks. Use internal Docker network only.
|
||||
|
||||
**Confidence:** HIGH - Official documentation from both Tecnativa and LinuxServer confirms configuration options and security model.
|
||||
|
||||
**Sources:**
|
||||
- [Tecnativa docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
- [LinuxServer socket-proxy](https://docs.linuxserver.io/images/docker-socket-proxy/)
|
||||
- [Wollomatic socket-proxy](https://github.com/wollomatic/socket-proxy)
|
||||
|
||||
---
|
||||
|
||||
## Telegram Inline Keyboards
|
||||
|
||||
### The Problem
|
||||
|
||||
n8n's native Telegram node has limitations with inline keyboards:
|
||||
1. Cannot pass dynamic JSON for `reply_markup`
|
||||
2. Expressions in keyboard fields cause "The value is not supported!" errors
|
||||
3. PR #17258 adding JSON keyboard support has been pending since July 2025
|
||||
|
||||
### Solution Options
|
||||
|
||||
| Option | Approach | Pros | Cons |
|
||||
|--------|----------|------|------|
|
||||
| HTTP Request node | Direct Telegram API calls | Full control, no dependencies | Token in URL, more setup |
|
||||
| Custom community node | @topvisor/n8n-nodes-telegram-send-message-custom | Easy JSON support | External dependency |
|
||||
| Wait for PR #17258 | Native n8n support | No workarounds needed | Indefinite timeline |
|
||||
|
||||
### Recommendation: HTTP Request Node
|
||||
|
||||
**Why:** No external dependencies, full Telegram API access, already proven pattern in similar projects.
|
||||
|
||||
### Implementation
|
||||
|
||||
**Send Message with Inline Keyboard:**
|
||||
|
||||
```json
|
||||
// HTTP Request node
|
||||
// URL: https://api.telegram.org/bot{{ $credentials.telegramApi.token }}/sendMessage
|
||||
// Method: POST
|
||||
// Body Type: JSON
|
||||
{
|
||||
"chat_id": "={{ $json.message.chat.id }}",
|
||||
"text": "Select a container:",
|
||||
"parse_mode": "HTML",
|
||||
"reply_markup": {
|
||||
"inline_keyboard": [
|
||||
[
|
||||
{"text": "plex", "callback_data": "start:plex"},
|
||||
{"text": "sonarr", "callback_data": "start:sonarr"}
|
||||
],
|
||||
[
|
||||
{"text": "radarr", "callback_data": "start:radarr"},
|
||||
{"text": "Cancel", "callback_data": "cancel"}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Handle Callback Query:**
|
||||
|
||||
The workflow already handles `callback_query` via the Telegram Trigger node (confirmed in current workflow). The callback_data format `action:container` allows parsing:
|
||||
|
||||
```javascript
|
||||
// In Code node
|
||||
const callbackData = $json.callback_query.data;
|
||||
const [action, container] = callbackData.split(':');
|
||||
return { action, container };
|
||||
```
|
||||
|
||||
**Answer Callback Query (remove loading spinner):**
|
||||
|
||||
```json
|
||||
// HTTP Request node
|
||||
// URL: https://api.telegram.org/bot{{ $credentials.telegramApi.token }}/answerCallbackQuery
|
||||
// Method: POST
|
||||
{
|
||||
"callback_query_id": "={{ $json.callback_query.id }}",
|
||||
"text": "Processing..."
|
||||
}
|
||||
```
|
||||
|
||||
### Callback Data Limits
|
||||
|
||||
Telegram limits `callback_data` to 64 bytes. Use short encodings:
|
||||
- `s:plex` instead of `start:plex`
|
||||
- Single char actions: s=start, t=stop, r=restart, u=update, l=logs
|
||||
|
||||
### Alternative: Custom Community Node
|
||||
|
||||
If HTTP Request becomes unwieldy, install:
|
||||
```
|
||||
Settings > Community Nodes > Install
|
||||
Package: @topvisor/n8n-nodes-telegram-send-message-custom
|
||||
```
|
||||
|
||||
This allows passing any Telegram API parameters as JSON, including `reply_markup`.
|
||||
|
||||
**Confidence:** MEDIUM - HTTP Request approach confirmed working by multiple community members. Native node limitations confirmed by open issues and PR #17258.
|
||||
|
||||
**Sources:**
|
||||
- [n8n Telegram Callback Operations](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.telegram/callback-operations/)
|
||||
- [PR #17258: JSON Keyboard Support](https://github.com/n8n-io/n8n/pull/17258)
|
||||
- [Custom Telegram Node](https://github.com/topvisor/n8n-nodes-telegram-send-message-custom)
|
||||
- [Telegram Bot API - Inline Keyboards](https://core.telegram.org/bots/api#inlinekeyboardmarkup)
|
||||
|
||||
---
|
||||
|
||||
## Unraid Integration
|
||||
|
||||
### How Unraid Detects Updates
|
||||
|
||||
Unraid checks for Docker image updates by comparing local image digests against remote registry digests:
|
||||
|
||||
1. Local image has digest (e.g., `35049b54ac64`)
|
||||
2. Unraid queries registry for `latest` tag digest (e.g., `96c1da19c304`)
|
||||
3. If different, shows "Update Available" badge
|
||||
|
||||
Update status is stored in: `/var/lib/docker/unraid-update-status.json`
|
||||
|
||||
### The Problem
|
||||
|
||||
When containers are updated externally (Watchtower, Portainer, or our bot), Unraid doesn't detect the change:
|
||||
- Container updates successfully
|
||||
- Unraid still shows "Update Available" badge
|
||||
- Manual "Check for Updates" doesn't fix it
|
||||
- Only deleting `unraid-update-status.json` and re-checking clears it
|
||||
|
||||
### Root Cause
|
||||
|
||||
Unraid only checks for **newly available** updates, not for containers that are **no longer** out of date. This is a known regression/limitation in Unraid's Docker management.
|
||||
|
||||
### Solution Options
|
||||
|
||||
| Option | Approach | Reliability |
|
||||
|--------|----------|-------------|
|
||||
| Delete status file | `rm /var/lib/docker/unraid-update-status.json` after update | HIGH - forces full recheck |
|
||||
| Trigger recheck | Unraid WebUI "Check for Updates" after file delete | MEDIUM - requires UI or API |
|
||||
| Accept mismatch | Document that badge may be stale | LOW - poor UX |
|
||||
|
||||
### Recommendation: Delete Status File + Document
|
||||
|
||||
**Approach:**
|
||||
1. After bot successfully updates a container, delete the status file
|
||||
2. Document that users should click "Check for Updates" in Unraid UI to refresh badges
|
||||
3. Future enhancement: investigate if Unraid has an API to trigger update check
|
||||
|
||||
**Implementation:**
|
||||
|
||||
Add to update workflow after successful container recreation:
|
||||
|
||||
```bash
|
||||
# In Execute Command node (runs on Unraid host)
|
||||
rm -f /var/lib/docker/unraid-update-status.json
|
||||
```
|
||||
|
||||
**Caveat:** This requires the n8n container to have access to `/var/lib/docker/` on the host, which is a security consideration. Alternative: document the manual step.
|
||||
|
||||
### Integration Points
|
||||
|
||||
| File/API | Purpose | Access Method |
|
||||
|----------|---------|---------------|
|
||||
| `/var/lib/docker/unraid-update-status.json` | Update badge status | Host filesystem |
|
||||
| Unraid WebUI | Trigger update check | Manual (no API found) |
|
||||
|
||||
**Confidence:** MEDIUM - File location and behavior confirmed by multiple Unraid forum threads. No official Unraid API documentation found for programmatic update checks.
|
||||
|
||||
**Sources:**
|
||||
- [Unraid Forum: Docker shows "update ready" after updating](https://forums.unraid.net/topic/157820-docker-shows-update-ready-after-updating/)
|
||||
- [Watchtower + Unraid Discussion](https://github.com/containrrr/watchtower/discussions/1389)
|
||||
- [Unraid Forum: Incorrect docker update notification](https://forums.unraid.net/bug-reports/stable-releases/regression-incorrect-docker-update-notification-r2807/)
|
||||
|
||||
---
|
||||
|
||||
## Recommendations Summary
|
||||
|
||||
### Stack Additions
|
||||
|
||||
| Component | Recommendation | Confidence |
|
||||
|-----------|----------------|------------|
|
||||
| n8n API | Use existing public API with API key auth | MEDIUM |
|
||||
| Docker security | LinuxServer socket-proxy | HIGH |
|
||||
| Telegram keyboards | HTTP Request node to Telegram API | MEDIUM |
|
||||
| Unraid sync | Delete status file after update | MEDIUM |
|
||||
|
||||
### Implementation Order
|
||||
|
||||
1. **Docker socket proxy** - Security improvement, low risk, well-documented
|
||||
2. **Telegram inline keyboards** - UX improvement, proven pattern
|
||||
3. **n8n API access** - Developer tooling, not user-facing
|
||||
4. **Unraid update sync** - Nice-to-have, requires additional host access
|
||||
|
||||
### No New Dependencies Required
|
||||
|
||||
All solutions use existing n8n capabilities:
|
||||
- HTTP Request node (Telegram API, Docker via proxy)
|
||||
- Execute Command node (Unraid file deletion)
|
||||
- n8n public API (existing feature)
|
||||
|
||||
The only new container is `socket-proxy`, which is infrastructure, not application code.
|
||||
|
||||
### Key Risks
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Socket proxy misconfiguration | Test each Docker operation after setup |
|
||||
| Telegram API token exposure | Use n8n credentials, never log full URLs |
|
||||
| Unraid status file access | May need additional volume mount or manual step |
|
||||
| n8n API key security | Store outside repository, rotate periodically |
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
**Research date:** 2026-02-02
|
||||
**Valid until:** 2026-03-02 (30 days)
|
||||
|
||||
**Confidence breakdown:**
|
||||
- n8n API: MEDIUM - Docs confirm existence, specific endpoints need validation
|
||||
- Docker socket proxy: HIGH - Official docs, multiple implementations
|
||||
- Telegram keyboards: MEDIUM - Community-confirmed workarounds
|
||||
- Unraid integration: MEDIUM - Forum-confirmed behavior, no official API
|
||||
|
||||
**Gaps:**
|
||||
- n8n API specific endpoint paths should be validated via Swagger UI
|
||||
- Unraid may have undocumented API for update checks (not found)
|
||||
- PR #17258 merge timeline unknown
|
||||
Reference in New Issue
Block a user