docs: complete v1.3 project research (STACK, FEATURES, ARCHITECTURE, PITFALLS, SUMMARY)

This commit is contained in:
Lucas Berger
2026-02-08 19:52:57 -05:00
parent c071b890ef
commit 07cde0490a
5 changed files with 1554 additions and 1288 deletions
+314 -304
View File
@@ -1,385 +1,395 @@
# Stack Research: v1.1 Features
# Technology Stack — Unraid Update Status Sync
**Project:** Unraid Docker Manager
**Researched:** 2026-02-02
**Focus:** Stack additions for n8n API, Docker socket security, Telegram keyboards, Unraid integration
**Milestone:** Update Status Sync (v1.3)
**Researched:** 2026-02-08
## n8n API Access
## Executive Summary
### Overview
**Recommendation:** Use Unraid's native GraphQL API with `updateContainer` mutation to sync status after bot-initiated container updates.
n8n provides a public REST API for programmatic workflow management. This enables Claude Code to read, update, and test workflows without manual UI interaction.
This approach leverages Unraid's official API (available since 7.2, via Connect plugin for <7.2) to call the same update mechanism the WebGUI uses. The GraphQL `updateContainer` mutation triggers Dynamix Docker Manager's update workflow, which automatically handles image digest comparison and updates `/var/lib/docker/unraid-update-status.json`.
### 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/)
**Confidence:** HIGH — Based on official Unraid API documentation, source code analysis, and GraphQL schema.
---
## Docker Socket Security
## Recommended Stack Additions
### The Problem
### 1. Unraid GraphQL API Client
Current setup mounts Docker socket directly into internet-exposed n8n container:
```
-v /var/run/docker.sock:/var/run/docker.sock
```
| Component | Version | Purpose | Why |
|-----------|---------|---------|-----|
| HTTP Request (n8n built-in) | n8n 1.x | GraphQL API calls | Already available, no new dependencies |
| Unraid API | 7.2+ or Connect plugin | Container update status sync | Official Unraid API, supported mechanism |
This is a security risk: if n8n is compromised, attacker has full Docker (root) access to the host.
**Installation:** None required in n8n container. Requires Unraid API key creation on host.
### Solution: Docker Socket Proxy
**Network Access:**
- From n8n container → Unraid host: `http://host.docker.internal/graphql` (requires `--add-host=host.docker.internal:host-gateway`)
- Or use Unraid server IP: `http://192.168.x.x/graphql` or `http://tower.local/graphql`
A socket proxy sits between containers and the Docker socket, filtering API requests to only allow specific operations.
### 2. Authentication
### 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:**
| Component | Purpose | Storage | Why |
|-----------|---------|---------|-----|
| Unraid API Key | GraphQL authentication | `.env.unraid-api` (gitignored) | Same pattern as n8n API credentials |
**Permissions Required:**
```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
unraid-api apikey --create \
--name "Docker Manager Bot" \
--permissions "DOCKER:UPDATE_ANY" \
--description "Telegram bot container updates" \
--json
```
### 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)
Minimum permission: `DOCKER:UPDATE_ANY` (allows calling `updateContainer` mutation)
---
## Telegram Inline Keyboards
## Integration Architecture
### The Problem
### How Unraid Tracks Update Status
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
**Three-layer system:**
### Solution Options
1. **Image Digest Comparison** — Unraid's `DockerManifestService` compares local image digest with registry manifest
2. **Update Status File**`/var/lib/docker/unraid-update-status.json` stores per-container status (`true` = up-to-date, `false` = update available, `undef` = unknown)
3. **Template Metadata** — XML templates in `/boot/config/plugins/dockerMan/templates-user/` store container configuration and version info
| 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 |
**Critical Finding:** Unraid does NOT auto-detect external updates (e.g., via Watchtower or direct Docker API). The `updateContainer` mutation must be called to sync status.
### Recommendation: HTTP Request Node
**Source:** [limetech/dynamix DockerClient.php](https://github.com/limetech/dynamix/blob/master/plugins/dynamix.docker.manager/include/DockerClient.php) — `syncVersions()` function updates status file after successful update.
**Why:** No external dependencies, full Telegram API access, already proven pattern in similar projects.
### Update Sync Workflow
### Implementation
```
Bot updates container (current approach)
↓ docker pull + recreate via Docker API
↓ Container running new image
↓ Unraid still shows "update available" ← PROBLEM
Call Unraid GraphQL API (new addition)
↓ mutation { docker { updateContainer(id: "...") { ... } } }
↓ Unraid's DockerService → Dynamix scripts
↓ syncVersions() writes to unraid-update-status.json
↓ Unraid UI shows "up-to-date" ← SOLVED
```
**Send Message with Inline Keyboard:**
**Idempotency:** Safe to call `updateContainer` even if already updated — Unraid checks current vs. remote digest and no-ops if already synced.
```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"}
]
]
---
## GraphQL API Details
### Endpoint
```
http://<unraid-host>/graphql
```
**Authentication:** Header `x-api-key: <your-api-key>`
### Key Mutations
```graphql
mutation UpdateSingleContainer {
docker {
updateContainer(id: "docker:HmiXBlJefBRPMS0m4iNYc") {
id
name
state
}
}
}
mutation UpdateMultipleContainers {
docker {
updateContainers(ids: ["docker:abc123", "docker:def456"]) {
id
name
state
}
}
}
```
**Handle Callback Query:**
**Container ID Format:** `docker:<container_name>` or retrieve via query first.
The workflow already handles `callback_query` via the Telegram Trigger node (confirmed in current workflow). The callback_data format `action:container` allows parsing:
**Source:** [Unraid API generated-schema.graphql](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql)
```javascript
// In Code node
const callbackData = $json.callback_query.data;
const [action, container] = callbackData.split(':');
return { action, container };
### n8n Integration Pattern
**Node:** HTTP Request (built-in)
**Configuration:**
- Method: `POST`
- URL: `http://host.docker.internal/graphql` (or Unraid IP)
- Authentication: Header Auth
- Name: `x-api-key`
- Value: `{{$env.UNRAID_API_KEY}}` (from `.env.unraid-api`)
- Body (JSON):
```json
{
"query": "mutation { docker { updateContainer(id: \"docker:{{$json.containerName}}\") { id name state } } }"
}
```
**Data Flow:**
```
Telegram callback (container updated)
→ Extract container name
→ HTTP Request to Unraid GraphQL
→ Parse response
→ Telegram confirmation message
```
**Answer Callback Query (remove loading spinner):**
---
## Alternatives Considered
| Approach | Pros | Cons | Verdict |
|----------|------|------|---------|
| **GraphQL API `updateContainer`** | Official API, handles all sync logic, idempotent, supported | Requires API key setup | **RECOMMENDED** |
| **Direct file write to `unraid-update-status.json`** | Simple, no auth needed | Brittle (file format undocumented), doesn't trigger UI refresh, race conditions with Unraid's update checker | **DO NOT USE** |
| **Shell exec via Docker socket** | Could call Dynamix scripts directly | Extremely fragile, requires PHP runtime, breaks on Unraid updates | **DO NOT USE** |
| **Watchtower pattern (do nothing)** | No code changes | Leaves stale "update available" badges, false-positive notifications | **Current problem** |
| **GraphQL `updateAllContainers`** | Syncs everything | Overkill (re-checks all containers), slower | **Use for batch sync only** |
---
## File System Access (NOT Recommended)
**Investigated but rejected:**
### `/var/lib/docker/unraid-update-status.json`
**Structure (observed):**
```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..."
"container1": true,
"container2": false,
"container3": "undef"
}
```
### Callback Data Limits
**Why not write directly:**
1. **Format undocumented** — File structure found via forum posts, not official docs
2. **No UI refresh trigger** — Writing file doesn't notify WebGUI to refresh
3. **Race conditions** — Unraid's update checker runs periodically, may overwrite changes
4. **Brittle** — File location/format could change without notice
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
**Confidence:** MEDIUM (structure inferred from forum posts, confirmed in source code)
### Alternative: Custom Community Node
**Source:** [Unraid Forums — Docker Update Status](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/page/9/)
If HTTP Request becomes unwieldy, install:
```
Settings > Community Nodes > Install
Package: @topvisor/n8n-nodes-telegram-send-message-custom
```
### `/boot/config/plugins/dockerMan/templates-user/`
This allows passing any Telegram API parameters as JSON, including `reply_markup`.
**Contains:** XML templates with container configuration, including `<Date>` and `<DateInstalled>` fields (internal, auto-managed by Unraid).
**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)
**Why not modify:** Templates are for configuration, not runtime status. Unraid ignores template dates for update checking (uses image digests instead).
---
## Unraid Integration
## Network Configuration
### How Unraid Detects Updates
### Docker Network Modes in Unraid
Unraid checks for Docker image updates by comparing local image digests against remote registry digests:
| Mode | Access Unraid Host | Use Case |
|------|-------------------|----------|
| **Bridge** | Via `172.17.0.1` (gateway) or `host.docker.internal` | Default, port mapping |
| **Host** | Via `localhost` | Direct host network access |
| **Custom:br0** | Via Unraid IP (macvlan, separate network) | Own IP on LAN |
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
**n8n container likely uses:** Bridge mode (most common for Unraid containers)
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:
### Accessing Unraid API from n8n Container
**Option 1: host.docker.internal (recommended)**
```bash
# In Execute Command node (runs on Unraid host)
rm -f /var/lib/docker/unraid-update-status.json
# Add to n8n container config (via Unraid Docker template):
--add-host=host.docker.internal:host-gateway
# Then in n8n workflow:
URL: http://host.docker.internal/graphql
```
**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.
**Option 2: Unraid hostname/IP**
```
URL: http://tower.local/graphql
# or
URL: http://192.168.1.100/graphql # Replace with actual Unraid IP
```
### Integration Points
**Verification:**
```bash
# From inside n8n container:
docker exec -it n8n curl -I http://host.docker.internal/graphql
# Should return HTTP 400 (GraphQL requires POST) or 200
```
| 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/)
**Source:** [Docker host.docker.internal guide](https://eastondev.com/blog/en/posts/dev/20251217-docker-host-access/)
---
## Recommendations Summary
## Security Considerations
### Stack Additions
### API Key Management
| 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 |
**Storage:**
- Add to `.env.unraid-api` (already gitignored)
- Load in n8n workflow via environment variables
### Implementation Order
**Permissions:**
- Use **least privilege**: `DOCKER:UPDATE_ANY` only
- Avoid `--roles ADMIN` (grants full access)
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
**Rotation:**
- API keys don't expire, but should be rotated periodically
- Delete via CLI: `unraid-api apikey --delete <key-id>`
### No New Dependencies Required
**Source:** [Unraid API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/)
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)
### Network Exposure
The only new container is `socket-proxy`, which is infrastructure, not application code.
**Unraid API runs on:**
- HTTP (port 80) or HTTPS (port 443) on Unraid host
- Same interface as WebGUI
### Key Risks
**Risk:** n8n container can access full GraphQL API (not just Docker mutations)
| 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 |
**Mitigation:** Use scoped API key (limits permissions to Docker operations only)
---
## Metadata
## Implementation Checklist
**Research date:** 2026-02-02
**Valid until:** 2026-03-02 (30 days)
- [ ] Create Unraid API key with `DOCKER:UPDATE_ANY` permission
- [ ] Add API key to `.env.unraid-api` file
- [ ] Verify n8n container can reach `http://host.docker.internal/graphql` (add `--add-host` if needed)
- [ ] Create HTTP Request credential in n8n (Header Auth with `x-api-key`)
- [ ] Add GraphQL mutation call to Update sub-workflow after successful Docker API update
- [ ] Test: Update container via bot → verify Unraid UI shows "up-to-date"
- [ ] Handle errors: GraphQL response parsing, network failures, auth errors
**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
## What NOT to Add
**Do NOT add:**
- ❌ File system watchers for `/var/lib/docker/unraid-update-status.json`
- ❌ Custom PHP scripts to call Dynamix functions
- ❌ Cron jobs to periodically sync status
- ❌ Docker socket mounting to execute Unraid shell commands
- ❌ Watchtower or similar auto-update tools (conflicts with bot's update control)
**Why:** These approaches are brittle, unsupported, or conflict with the bot's explicit update model.
---
## Version Compatibility
| Unraid Version | API Availability | Notes |
|----------------|-----------------|-------|
| 7.2+ | Built-in | Native GraphQL API |
| 6.9 - 7.1 | Via Connect plugin | Install from Community Applications |
| <6.9 | Not available | Upgrade Unraid required |
**Check version:**
```bash
# SSH to Unraid:
cat /etc/unraid-version
```
**Install Connect plugin (pre-7.2):**
1. Apps → Search "Unraid Connect"
2. Install plugin
3. Settings → Management Access → API → Enable
**Source:** [Unraid API Documentation](https://docs.unraid.net/API/)
---
## Open Questions & Validation Needed
**Low-priority research flags:**
1. **Container ID resolution** — Does GraphQL require `docker:<name>` prefix, or just `<name>`?
- **Resolution:** Query containers first to get exact ID format
- **Impact:** Low (easily testable during implementation)
2. **Rate limiting** — Does Unraid API have rate limits for mutations?
- **Confidence:** LOW (not documented)
- **Impact:** Low (bot updates are infrequent, <10/min even in batch mode)
3. **GraphQL subscription for status changes** — Could bot subscribe to Docker events?
- **Finding:** Schema includes subscriptions, but not needed for status sync
- **Verdict:** Defer to future enhancement (real-time status monitoring)
---
## Sources & Confidence Assessment
| Finding | Confidence | Primary Source |
|---------|-----------|----------------|
| GraphQL API exists with updateContainer mutation | HIGH | [Unraid API Schema](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql) |
| updateContainer triggers Dynamix sync workflow | HIGH | [DeepWiki — Docker Integration](https://deepwiki.com/unraid/api/2.4-docker-integration) |
| syncVersions() updates status JSON file | HIGH | [limetech/dynamix source](https://github.com/limetech/dynamix/blob/master/plugins/dynamix.docker.manager/include/DockerClient.php) |
| API key requires DOCKER:UPDATE_ANY permission | MEDIUM | [API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/) (exact permission not explicitly stated) |
| Container ID format is "docker:<name>" | MEDIUM | [Unraid API examples](https://github.com/domalab/unraid-api-client/blob/main/UNRAIDAPI.md) (needs verification) |
| host.docker.internal works in Unraid Docker | MEDIUM | [Forum discussions](https://forums.unraid.net/topic/95050-how-to-use-the-hosts-ip-address-inside-a-docker-container/) (standard Docker feature, but Unraid-specific confirmation limited) |
| /var/lib/docker/unraid-update-status.json structure | MEDIUM | [Forum posts](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/page/9/) (observed, not officially documented) |
| No rate limiting on GraphQL API | LOW | Assumption (not documented either way) |
**Overall Confidence: HIGH** — Core approach (GraphQL API) is well-documented and official. Implementation details need testing but low-risk.
---
## Next Steps for Roadmap
**Phase structure recommendations:**
1. **Phase 1: API Setup & Testing**
- Create API key
- Test GraphQL query/mutation from command line
- Verify network access from n8n container
- **Why first:** Validates approach before n8n integration
2. **Phase 2: n8n Integration**
- Add HTTP Request node to Update sub-workflow
- GraphQL mutation call after Docker update
- Error handling for API failures
- **Why second:** Builds on validated API access
3. **Phase 3: Batch Sync Support**
- Optional: Call `updateContainers` (plural) for batch updates
- Handle partial failures (some containers sync, others fail)
- **Why third:** Enhancement after core functionality works
**Research flags:**
- Phase 1: Likely needs deeper research (network access testing, container ID format verification)
- Phase 2: Standard patterns, unlikely to need additional research
- Phase 3: Defer until Phase 2 validates approach
---
## Additional Resources
**Official Documentation:**
- [Unraid API Overview](https://docs.unraid.net/API/)
- [Using the Unraid API](https://docs.unraid.net/API/how-to-use-the-api/)
- [GraphQL Schema (raw)](https://raw.githubusercontent.com/unraid/api/main/api/generated-schema.graphql)
- [API Key Management](https://docs.unraid.net/API/programmatic-api-key-management/)
**Community Resources:**
- [Unraid MCP Server](https://github.com/jmagar/unraid-mcp) — Reference implementation of GraphQL client
- [Home Assistant Unraid Integration](https://github.com/domalab/unraid-api-client) — Another GraphQL client example
**Source Code:**
- [limetech/dynamix](https://github.com/limetech/dynamix) — Unraid's Docker Manager plugin
- [unraid/api](https://github.com/unraid/api) — Official API monorepo
**Forum Discussions:**
- [Watchtower not syncing status](https://github.com/containrrr/watchtower/discussions/1389)
- [Docker update status not reflected](https://forums.unraid.net/topic/149953-docker-update-via-watchtower-status-not-reflected-in-unraid/)