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:
Lucas Berger
2026-02-02 22:09:06 -05:00
parent ff289677ab
commit 811030cee4
5 changed files with 1614 additions and 0 deletions
+385
View File
@@ -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