name: workflow-json-architect description: n8n workflow JSON structure management for Vigil Guard v2.0.0 (24-node 3-branch parallel pipeline). Use for node manipulation, connections, Code node JavaScript, 3-Branch Executor logic, Arbiter v2 integration, and critical reminder to import workflow to n8n GUI. version: 2.0.0 allowed-tools: [Read, Write, Edit, Bash, Grep, Glob]
n8n Workflow JSON Architecture Management (v2.0.0)
Overview
Expert guidance for managing n8n workflow JSON files (24 nodes, 3-branch parallel detection) in Vigil Guard v2.0.0. This skill focuses exclusively on the JSON structure, node manipulation, connections, and workflow lifecycle - NOT on detection patterns or business logic (see n8n-vigil-workflow for patterns).
When to Use This Skill
- Modifying workflow JSON structure (
Vigil Guard v2.0.0.json) - Adding/removing/reordering nodes
- Editing Code node embedded JavaScript
- Managing node connections and data flow
- Working with 3-Branch Executor node
- Configuring Arbiter v2 weighted fusion
- Migrating workflows between versions
- Debugging workflow execution errors
- Understanding n8n JSON schema
- Explaining to user: "Import workflow to n8n GUI NOW!"
v2.0.0 Architecture Change
From 40-node Sequential → 24-node 3-Branch Parallel
OLD (v1.x): 40+ nodes, sequential processing, rules.config.json
NEW (v2.0.0): 24 nodes, 3-branch parallel detection
- 3-Branch Executor (parallel HTTP calls)
- Arbiter v2 (weighted fusion)
- No rules.config.json (patterns in heuristics-service)
CRITICAL: Workflow Lifecycle Understanding
The #1 Most Important Concept
n8n workflows are stored in SQLite database, NOT in JSON files!
TRUTH:
- Workflow execution reads from: SQLite database (/home/node/.n8n/database.sqlite)
- JSON file in repo is: Backup/version control ONLY
- Changes to JSON have NO EFFECT until user imports to n8n GUI
WORKFLOW:
1. Edit JSON file in repo
2. IMMEDIATELY tell user: " IMPORT THIS FILE TO n8n GUI NOW!"
3. User imports via: n8n GUI → Import from File
4. n8n copies JSON → SQLite database
5. Workflow executes from SQLite (NOT from file)
CROWN RULE: WORKFLOW IMPORT STATUS - OPERATOR TRUST AXIOM
ABSOLUTELY FORBIDDEN DEBUGGING HYPOTHESES:
NEVER CHECK:
- "Was the workflow imported?"
- "Is the workflow in n8n SQL database?"
- "Is the workflow active?"
- "Did you activate the workflow after import?"
INDISPUTABLE FACT:
- If operator confirmed import: workflow IS imported
- Workflow IS in SQLite database
- Workflow IS active
- All Code nodes have LATEST JavaScript from JSON file
- This is 100% TRUTH without verification
CORRECT DEBUGGING APPROACH:
- Operator confirms import → END OF STORY
- Problem is NOT in import
- Problem IS in workflow logic
- Debug: analyze node code, tests, configuration
- DO NOT waste tokens verifying import
User Communication Template
Workflow JSON updated successfully!
**CRITICAL NEXT STEP - DO THIS NOW:**
1. Open n8n GUI: http://localhost:5678
2. Click menu (≡) → **Import from File**
3. Select: `services/workflow/workflows/Vigil Guard v2.0.0.json`
4. Confirm import
5. Activate workflow (toggle switch)
**Your changes will NOT work until you complete this import!**
After importing, test with: n8n GUI → Test workflow → Chat tab
Workflow JSON Structure (v2.0.0)
Top-Level Schema
{
"name": "Vigil Guard v2.0.0",
"nodes": [
{
"parameters": {},
"id": "unique-uuid",
"name": "Node_Name",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [x, y]
}
],
"connections": {
"Source_Node": {
"main": [
[
{
"node": "Target_Node",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"updatedAt": "2025-01-30T10:00:00.000Z",
"versionId": "uuid"
}
24-Node Architecture (v2.0.0)
1. When chat message received # Chat trigger
2. Webhook v2 # HTTP webhook trigger
3. Extract Input # Normalize input format
4. Load allowlist.schema.json # Load allowlist config
5. Load pii.conf # Load PII patterns (361 lines)
6. Load unified_config.json # Load main config (303 lines, v5.0.0)
7. Extract allowlist # Parse allowlist
8. Extract pii.conf # Parse PII config
9. Extract unified_config # Parse main config
10. Merge Config # Combine all configs
11. Config Loader v2 # Final config object
12. Input Validator v2 # Validate input (length, format)
13. Validation Check # Route based on validation
14. 3-Branch Executor # PARALLEL branch execution (CRITICAL)
15. Arbiter v2 # Weighted score fusion (CRITICAL)
16. Arbiter Decision # Route based on score
17. PII_Redactor_v2 # Dual-language PII detection
18. Block Response v2 # Generate block response
19. Merge Final # Merge all paths
20. Build NDJSON v2 # Format for logging
21. Log to ClickHouse v2 # Send to analytics DB
22. Clean Output v2 # User-facing result
23. Early Block v2 # Fast-path for validation failures
24. output to plugin # Browser extension response
Node Types
Core Processing Nodes:
- Code - Custom JavaScript logic (most common)
- HTTP Request - API calls (3-branch services, Presidio, ClickHouse)
- Merge - Combine data streams (config merge, path merge)
- Switch - Conditional routing (thresholds)
- Set - Variable assignment
- Webhook - Trigger endpoint
Data Flow (3-Branch Parallel)
Webhook Trigger
→ Extract Input
→ Config Loader v2 (merge configs)
→ Input Validator v2
→ 3-Branch Executor (PARALLEL):
├─ HTTP: heuristics-service:5005 (Branch A)
├─ HTTP: semantic-service:5006 (Branch B)
└─ HTTP: prompt-guard-api:8000 (Branch C)
→ Arbiter v2 (weighted fusion: A*0.30 + B*0.35 + C*0.35)
→ Arbiter Decision (Switch)
├─ ALLOW → Clean Output
├─ SANITIZE → PII_Redactor_v2 → Clean Output
└─ BLOCK → Block Response
→ Build NDJSON v2
→ Log to ClickHouse v2
→ Clean Output v2
Common Tasks
Task 1: Modify 3-Branch Executor
Example: Add timeout to branch calls
// Find 3-Branch Executor node
const branchNode = workflow.nodes.find(n => n.name === "3-Branch Executor");
// Modify HTTP parameters for each branch
branchNode.parameters = {
jsCode: `
const items = $input.all();
const text = items[0].json.sanitized_input || items[0].json.chatInput;
const requestId = items[0].json.request_id || 'default';
// Branch timeouts (v2.0.0)
const TIMEOUTS = {
A: 1000, // Heuristics: 1 second
B: 2000, // Semantic: 2 seconds
C: 3000 // LLM Guard: 3 seconds
};
// Parallel branch execution
const [branchA, branchB, branchC] = await Promise.allSettled([
// Branch A: Heuristics Service
fetch('http://heuristics-service:5005/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, request_id: requestId }),
signal: AbortSignal.timeout(TIMEOUTS.A)
}).then(r => r.json()),
// Branch B: Semantic Service
fetch('http://semantic-service:5006/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, request_id: requestId }),
signal: AbortSignal.timeout(TIMEOUTS.B)
}).then(r => r.json()),
// Branch C: LLM Guard
fetch('http://prompt-guard-api:8000/detect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
signal: AbortSignal.timeout(TIMEOUTS.C)
}).then(r => r.json())
]);
return [{
json: {
...items[0].json,
branch_results: {
A: branchA.status === 'fulfilled' ? branchA.value : { score: 0, degraded: true },
B: branchB.status === 'fulfilled' ? branchB.value : { score: 0, degraded: true },
C: branchC.status === 'fulfilled' ? branchC.value : { score: 0, degraded: true }
}
}
}];
`
};
Task 2: Modify Arbiter v2 Weights
Example: Change branch weights
// Find Arbiter v2 node
const arbiterNode = workflow.nodes.find(n => n.name === "Arbiter v2");
// Modify weighted fusion logic
arbiterNode.parameters.jsCode = `
const items = $input.all();
const branches = items[0].json.branch_results;
// v2.0.0 Weighted fusion
const WEIGHTS = {
A: 0.30, // Heuristics
B: 0.35, // Semantic
C: 0.35 // LLM Guard
};
// Calculate weighted score
const weightedScore =
(branches.A.score || 0) * WEIGHTS.A +
(branches.B.score || 0) * WEIGHTS.B +
(branches.C.score || 0) * WEIGHTS.C;
// Critical signal override
const criticalOverride =
branches.A.critical_signals?.obfuscation_heavy ||
branches.C.critical_signals?.llm_attack;
// Decision matrix
let decision;
if (criticalOverride || weightedScore >= 85) {
decision = 'BLOCK';
} else if (weightedScore >= 30) {
decision = 'SANITIZE';
} else {
decision = 'ALLOW';
}
return [{
json: {
...items[0].json,
threat_score: weightedScore,
arbiter_decision: decision,
critical_override: criticalOverride
}
}];
`;
Task 3: Add New Node
Example: Add post-processing step
// 1. Create new node
const newNode = {
"parameters": {
"jsCode": `
const items = $input.all();
// Your processing logic
return items.map((item, index) => ({
json: {
...item.json,
processed: true
},
pairedItem: { item: index }
}));
`
},
"id": crypto.randomUUID(),
"name": "Post_Processing",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1500, 300]
};
// 2. Insert into nodes array
workflow.nodes.push(newNode);
// 3. Update connections
// Find where to insert (after Arbiter Decision, before Clean Output)
workflow.connections["Arbiter Decision"].main[0][0].node = "Post_Processing";
workflow.connections["Post_Processing"] = {
"main": [[{ "node": "Clean Output v2", "type": "main", "index": 0 }]]
};
// 4. Save and tell user to import
Task 4: Migrate from v1.x to v2.0.0
Migration Checklist:
// 1. Update workflow name
workflow.name = "Vigil Guard v2.0.0";
// 2. Replace 40-node sequential with 24-node parallel
// - Remove all sequential pattern matching nodes
// - Add 3-Branch Executor
// - Add Arbiter v2
// 3. Remove rules.config.json references
// - Pattern matching now in heuristics-service
// - Categories in unified_config.json
// 4. Update ClickHouse logging
const loggingNode = workflow.nodes.find(n => n.name.includes("NDJSON"));
loggingNode.parameters.jsCode = loggingNode.parameters.jsCode
.replace(/"pipeline_version": "1\.\d+\.\d+"/, '"pipeline_version": "2.0.0"')
+ `
// v2.0.0: Add branch columns
result.branch_a_score = items[0].json.branch_results?.A?.score || 0;
result.branch_b_score = items[0].json.branch_results?.B?.score || 0;
result.branch_c_score = items[0].json.branch_results?.C?.score || 0;
result.arbiter_decision = items[0].json.arbiter_decision;
result.branch_a_timing_ms = items[0].json.timing?.branch_a_ms || 0;
result.branch_b_timing_ms = items[0].json.timing?.branch_b_ms || 0;
result.branch_c_timing_ms = items[0].json.timing?.branch_c_ms || 0;
`;
// 5. Update workflow metadata
workflow.updatedAt = new Date().toISOString();
workflow.versionId = crypto.randomUUID();
Code Node Patterns (v2.0.0)
Pattern 1: Data Access
// Get all input items
const items = $input.all();
// Access first item's JSON
const data = items[0].json;
// Access branch results (v2.0.0)
const branchA = data.branch_results?.A;
const branchB = data.branch_results?.B;
const branchC = data.branch_results?.C;
Pattern 2: Returning Modified Data
// Return modified items (preserves pairedItem for tracing)
return items.map((item, index) => ({
json: {
...item.json,
new_field: "value"
},
pairedItem: {
item: index
}
}));
Pattern 3: Accessing Config Data
// Config loaded via Merge node, available in all downstream Code nodes
const items = $input.all();
const contextItem = items[0];
// Access config sections (v2.0.0: unified_config.json)
const unified = contextItem.json.config; // unified_config.json (303 lines)
const piiConfig = contextItem.json.pii; // pii.conf (361 lines)
// Use config values
const allowMax = unified.thresholds?.allow_max || 29;
const sanitizeLight = unified.thresholds?.sanitize_light || 64;
Pattern 4: Branch Result Handling
// Handle degraded branches (timeout/error)
const branches = items[0].json.branch_results;
for (const [id, result] of Object.entries(branches)) {
if (result.degraded) {
console.log(`Branch ${id} degraded, using score 0`);
result.score = 0;
}
}
// Calculate weighted score
const score =
branches.A.score * 0.30 +
branches.B.score * 0.35 +
branches.C.score * 0.35;
Pattern 5: HTTP Request Error Handling
try {
const response = await fetch('http://heuristics-service:5005/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, request_id: requestId }),
signal: AbortSignal.timeout(1000) // 1 second timeout
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Branch A error:', error.message);
return { score: 0, degraded: true, error: error.message };
}
Node Positioning (Visual Layout)
Grid System
- X-axis: 0 (left) → 2000+ (right)
- Y-axis: 0 (top) → 1200+ (bottom)
- Spacing: 220 pixels between nodes horizontally
- Vertical offset: 100-150 pixels for parallel branches
Example Layout (v2.0.0)
// Sequential flow (left to right)
nodes[0].position = [240, 300]; // Webhook Trigger
nodes[1].position = [460, 300]; // Extract Input
nodes[2].position = [680, 300]; // Config Loader
nodes[3].position = [900, 300]; // Input Validator
// 3-Branch Executor (single node, handles parallel internally)
nodes[4].position = [1120, 300]; // 3-Branch Executor
// Post-branch processing
nodes[5].position = [1340, 300]; // Arbiter v2
nodes[6].position = [1560, 300]; // Arbiter Decision
Connection Syntax
Simple Connection (A → B)
{
"connections": {
"Node_A": {
"main": [
[{ "node": "Node_B", "type": "main", "index": 0 }]
]
}
}
}
Switch Node (Conditional - Arbiter Decision)
{
"connections": {
"Arbiter Decision": {
"main": [
[{ "node": "Clean Output v2", "type": "main", "index": 0 }],
[{ "node": "PII_Redactor_v2", "type": "main", "index": 0 }],
[{ "node": "Block Response v2", "type": "main", "index": 0 }]
]
}
}
}
Debugging Workflow Execution
Common Error Patterns
Error: Branch timeout
// Problem: Branch service takes too long
// Solution: Increase timeout or check service health
// Check service health
curl http://localhost:5005/health // Heuristics
curl http://localhost:5006/health // Semantic
curl http://localhost:8000/health // LLM Guard
Error: "Cannot read property 'branch_results' of undefined"
// Problem: 3-Branch Executor didn't return results
// Solution: Check branch node output and add fallback
const branches = items[0].json.branch_results || {
A: { score: 0, degraded: true },
B: { score: 0, degraded: true },
C: { score: 0, degraded: true }
};
Enable Debug Logging
// Add to Code nodes for debugging
console.log('=== Node Name Debug ===');
console.log('Input:', JSON.stringify($input.all(), null, 2));
console.log('Branch Results:', JSON.stringify(items[0].json.branch_results, null, 2));
console.log('Arbiter Decision:', items[0].json.arbiter_decision);
View Logs:
docker logs vigil-n8n | grep "Debug"
Performance Optimization
Parallel Branch Execution
// 3-Branch Executor uses Promise.allSettled for parallel execution
// Each branch runs independently, no blocking
const [branchA, branchB, branchC] = await Promise.allSettled([
fetchBranchA(text),
fetchBranchB(text),
fetchBranchC(text)
]);
// All branches complete in max(timeoutA, timeoutB, timeoutC)
// Not sequential: timeoutA + timeoutB + timeoutC
Cache Config Data
// Config loaded once at start, reused in all nodes
const config = $('Config_Loader_v2').all()[0].json.config;
// Don't reload in every node
Integration with Other Skills
When to Use Other Skills
n8n-vigil-workflow:
- Understanding 3-branch detection logic
- Modifying decision thresholds
- PII detection configuration
- Business logic changes
pattern-library-manager:
- Adding detection patterns (heuristics-service)
- ReDoS validation
vigil-testing-e2e:
- Writing tests for modified workflow
- Validating arbiter decisions
- Testing branch behavior
clickhouse-grafana-monitoring:
- Verifying workflow logs branch columns correctly
- Analyzing branch metrics
Reference Files
Workflow
- Current:
services/workflow/workflows/Vigil Guard v2.0.0.json - Backups:
services/workflow/workflows/backups/
Configuration
services/workflow/config/unified_config.json(303 lines, v5.0.0)services/workflow/config/pii.conf(361 lines)
Documentation
- Architecture:
docs/ARCHITECTURE.md - n8n docs: https://docs.n8n.io/
Quick Reference
Essential Commands
# View workflow structure
cat services/workflow/workflows/Vigil*.json | jq .
# Count nodes (expected: 24)
jq '.nodes | length' services/workflow/workflows/Vigil*.json
# List node names
jq -r '.nodes[].name' services/workflow/workflows/Vigil*.json
# Find 3-Branch Executor
jq '.nodes[] | select(.name | contains("Branch"))' services/workflow/workflows/Vigil*.json
# Extract Arbiter v2 code
jq -r '.nodes[] | select(.name == "Arbiter v2") | .parameters.jsCode' services/workflow/workflows/Vigil*.json
Node Checklist (Before Commit)
- [ ] Node has unique
id(UUID) - [ ] Node has valid
type(n8n-nodes-base.*) - [ ] Node has
position[x, y] - [ ] Node has incoming connection (or is trigger)
- [ ] Node has outgoing connection (or is terminal)
- [ ] Code node JavaScript is syntactically valid
- [ ] pairedItem preserved in return statements
- [ ] Error handling implemented (try/catch)
- [ ] Branch degradation handled (score=0 on timeout)
- [ ] Workflow
updatedAttimestamp updated - [ ] User instructed: "Import to n8n NOW!"
Last Updated: 2025-12-09 Workflow Version: v2.0.0 (24 nodes, 3-branch parallel) Maintained By: Vigil Guard Development Team
Version History
- v2.0.0 (Current): 24 nodes, 3-branch parallel, Arbiter v2
- v1.7.0: 41 nodes, sequential pipeline
- v1.6.11: 40 nodes, rules.config.json (DEPRECATED)
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.