Files
unraid-docker-manager/.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md
T
Lucas Berger 4e29bdeb56 docs(15-01): complete Container ID Registry and Callback Token Encoding plan
- Added 15-01-SUMMARY.md documenting implementation and deviations
- Updated STATE.md: Phase 15 complete (2/2 plans), 52 total plans, v1.4 at 20%
- Task 1 (Container ID Registry) was pre-existing in baseline
- Task 2 (Token Encoder/Decoder) implemented and pushed to n8n
- All utility nodes standalone, ready for Phase 16 wiring

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 08:56:03 -05:00

179 lines
8.7 KiB
Markdown

---
phase: 15-infrastructure-foundation
plan: 01
subsystem: infra
tags: [container-id-registry, callback-token-encoding, unraid-prefixedid, telegram-callback-data]
# Dependency graph
requires:
- phase: 14-unraid-api-access
provides: Unraid GraphQL API container data format (id: PrefixedID, names[], state)
- phase: 11-update-all-callback-limits
context: Demonstrated callback_data 64-byte limit (bitmap encoding addressed this for batch operations)
provides:
- Container ID Registry utility node (container name <-> Unraid PrefixedID translation)
- Callback Token Encoder utility node (PrefixedID -> 8-char hex token with collision detection)
- Callback Token Decoder utility node (8-char token -> PrefixedID resolution)
- Static data persistence pattern for _containerIdMap and _callbackTokens
affects: [16-api-migration, 17-container-id-translation]
# Tech tracking
tech-stack:
added:
- crypto.subtle.digest (Web Crypto API for SHA-256 hashing)
patterns:
- JSON serialization for n8n static data persistence (top-level assignment pattern per CLAUDE.md)
- SHA-256 hash with 7-window collision detection (56 chars / 8-char windows)
- Idempotent token encoding (reuse existing token if same unraidId)
- Container name normalization (strip leading '/', lowercase)
- Registry staleness detection (60-second threshold for error messaging)
key-files:
created: []
modified:
- n8n-workflow.json
key-decisions:
- "Token encoder uses 8-char hex (not base64) for deterministic collision avoidance via hash window offsets"
- "Registry stores full PrefixedID (129-char) as-is, not normalized - downstream consumers handle format"
- "Decoder is read-only (no JSON.stringify) - token store managed entirely by encoder"
- "Collision detection tries 7 non-overlapping windows (0, 8, 16, 24, 32, 40, 48 char offsets from SHA-256)"
- "Standalone utility nodes NOT connected to active flow - Phase 16 will wire them in"
patterns-established:
- "Container ID Registry as centralized name->ID translation layer"
- "Token encoding system as callback_data compression layer for Telegram's 64-byte limit"
- "Dual-mode node pattern (update vs lookup based on input.containers vs input.containerName)"
# Metrics
duration: 6min
completed: 2026-02-09
---
# Phase 15 Plan 01: Container ID Registry and Callback Token Encoding Summary
Built Container ID Registry and Callback Token Encoding system as standalone utility Code nodes for Phase 16 API migration. Registry maps container names to Unraid 129-char PrefixedIDs, token system compresses PrefixedIDs to 8-char hex for Telegram callback_data limit.
## What Was Built
### Container ID Registry (Task 1 - Already Complete in Baseline)
**Node:** Container ID Registry at position [200, 2400]
**Note:** This node was already implemented in the baseline commit 1b4b596 (incorrectly labeled as 15-02 but contained 15-01 work). Verified implementation matches all plan requirements.
**Implementation:**
- `updateRegistry(containers)`: Takes Unraid GraphQL container array, extracts names (strip `/`, lowercase), maps to `{name, unraidId: container.id}`, stores with timestamp
- `getUnraidId(containerName)`: Resolves container name to 129-char PrefixedID, throws helpful errors (stale registry vs invalid name)
- `getContainerByName(containerName)`: Returns full entry `{name, unraidId}`
- Dual-mode input contract: `input.containers` for updates, `input.containerName` for lookups
- JSON serialization pattern: `registry._containerIdMap = JSON.stringify(newMap)` (top-level assignment per CLAUDE.md)
- 60-second staleness threshold for error messaging
**Verification passed:** All functions present, JSON pattern correct, no connections.
### Callback Token Encoder (Task 2)
**Node:** Callback Token Encoder at position [600, 2400]
**Commit:** 1b61343
**Implementation:**
- `encodeToken(unraidId)`: Async function using crypto.subtle.digest('SHA-256')
- Generates SHA-256 hash, takes first 8 hex chars as token
- Collision detection: If token exists with different unraidId, tries next 8-char window (offsets: 0, 8, 16, 24, 32, 40, 48)
- Idempotent: Reuses existing token if same unraidId
- Input contract: `input.unraidId` (required), `input.action` (optional)
- Output: `{token, unraidId, callbackData, byteSize, warning}` - includes callback_data format and 64-byte limit validation
- JSON serialization: `staticData._callbackTokens = JSON.stringify(tokenStore)`
**Verification passed:** SHA-256 hashing, 7-window collision detection, JSON pattern, no connections.
### Callback Token Decoder (Task 2)
**Node:** Callback Token Decoder at position [1000, 2400]
**Commit:** 1b61343
**Implementation:**
- `decodeToken(token)`: Looks up token in store, throws if not found
- Input contract: `input.token` (8-char hex) OR `input.callbackData` (string like "action:start:a1b2c3d4")
- Callback data parsing: Splits by `:`, extracts action and token (last segment)
- Output: `{token, unraidId, action}`
- Read-only: Only uses JSON.parse (no stringify) - token store managed by encoder
**Verification passed:** decodeToken function, error handling, callbackData parsing, no connections.
## Deviations from Plan
### Pre-existing Work
**Task 1 (Container ID Registry) was already complete in baseline commit 1b4b596.**
- **Found during:** Plan execution initialization
- **Issue:** Commit 1b4b596 was labeled `feat(15-02)` but actually contained both the Container ID Registry (Task 1 from plan 15-01) AND the GraphQL Response Normalizer (Task 1 from plan 15-02)
- **Resolution:** Verified existing implementation matches all Task 1 requirements (updateRegistry, getUnraidId, getContainerByName, JSON serialization, no connections). Proceeded with Task 2 only.
- **Impact:** No implementation changes needed for Task 1. Task 2 added as planned.
- **Commits:** No new commit for Task 1 (already in baseline). Task 2 committed as 1b61343.
### n8n API Field Restrictions (Deviation Rule 3 - Blocking Issue)
**Notes fields cannot be pushed to n8n via API.**
- **Found during:** Task 2 push to n8n (HTTP 400 "must NOT have additional properties")
- **Issue:** Plan specified adding `notes` and `notesDisplayMode` fields to document utility node purpose. n8n API only accepts 6 fields: id, name, type, typeVersion, position, parameters.
- **Fix:** Removed notes/notesDisplayMode fields from all nodes before pushing payload. Documentation moved to JSDoc comments in jsCode (first lines of each function).
- **Files modified:** n8n-workflow.json (cleaned before push)
- **Verification:** Push succeeded with HTTP 200, n8n confirms 175 nodes.
- **Impact:** Node documentation now lives in code comments instead of n8n UI notes field. Functionally equivalent for Phase 16 (code is self-documenting).
## Execution Summary
**Tasks completed:** 2/2
- Task 1: Container ID Registry (verified baseline implementation)
- Task 2: Callback Token Encoder and Decoder (implemented and committed)
**Commits:**
- 1b61343: feat(15-01): add Callback Token Encoder and Decoder utility nodes
**Duration:** 6 minutes (Task 1 verification + Task 2 implementation + n8n push + commit)
**Files modified:**
- n8n-workflow.json (added 2 nodes: encoder, decoder)
**n8n push:** Successful (HTTP 200, 175 nodes, updated 2026-02-09T13:53:17.242Z)
## Verification Results
All success criteria met:
- [✓] Container ID Registry maps container names to Unraid PrefixedID format (129-char)
- [✓] Callback token encoding produces 8-char hex tokens that fit within Telegram's 64-byte callback_data limit
- [✓] Token collision detection prevents wrong-container scenarios (7-window SHA-256 approach)
- [✓] All static data uses JSON serialization (top-level assignment) per CLAUDE.md convention
- [✓] Three standalone utility nodes ready for Phase 16 to wire in
- [✓] No connections to/from any utility node (verified in workflow connections map)
- [✓] Workflow JSON valid and pushed to n8n
## Self-Check: PASSED
**Created files:**
- [✓] FOUND: .planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md (this file)
**Commits:**
- [✓] FOUND: 1b61343 (Task 2: Callback Token Encoder and Decoder)
**n8n nodes:**
- [✓] Container ID Registry exists in n8n workflow (175 nodes total)
- [✓] Callback Token Encoder exists in n8n workflow
- [✓] Callback Token Decoder exists in n8n workflow
## Next Steps
**Phase 16 (API Migration)** will:
1. Wire Container ID Registry into container status flow (connect after Unraid GraphQL responses)
2. Wire Callback Token Encoder into inline keyboard generation (replace long PrefixedIDs with 8-char tokens)
3. Wire Callback Token Decoder into callback routing (resolve tokens back to PrefixedIDs)
4. Update all 60+ Code nodes to use registry for ID translation
5. Test token collision handling under production load
**Ready for:** Plan 15-02 execution (if not already complete) or Phase 16 planning.