# KarmaDue as a gate in n8n workflows

Tested: 2026-11-12, 07:13 UTC (2:13 AM ET), with n8n 2.47.4 (`n8n execute`, SQLite, Node 24). No account or API key.

An importable workflow: **KarmaDue check** (HTTP Request to the public preflight) → **Gate** (Code node) → **Route** (Switch) with three outputs:

- `allow` (`proceed_with_care`): connect the step that installs, runs or connects.
- `blocked` (`block`): connect a stop / notify step. Never connect the action here.
- `needs approval` (`require_human_approval`, `unknown`, or no answer): connect a Wait / approval step.

The HTTP node never errors, and the Gate treats a missing answer as `unknown`, so a KarmaDue outage lands in `needs approval`, not `allow`.

## Drop-in file: `karmadue-gate.n8n.json`

Raw file: https://karmadue.expo.app/recipes/karmadue-gate.n8n.json

```json
{
  "name": "KarmaDue gate",
  "nodes": [
    {
      "parameters": {},
      "id": "a1",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "return [\n  'npm:postmark-mcp@1.0.18', 'pypi:rsquests', 'pypi:requests==2.31.0', 'pypi:requests==2.33.0'\n].map(target => ({ json: { target, action: 'install' } }));"
      },
      "id": "a2",
      "name": "Targets",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        200,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/public-api/v1/preflight",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-kd-via",
              "value": "recipe-n8n"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ target: $json.target, action: $json.action }) }}",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "a3",
      "name": "KarmaDue check",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        400,
        0
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "return $input.all().map((item, i) => {\n  const d = item.json.data || {};\n  const enforce = d.enforce || 'unknown';  // no answer is not clearance\n  const gate = enforce === 'block' ? 'BLOCKED' : enforce === 'proceed_with_care' ? 'ALLOW' : 'NEEDS APPROVAL';\n  return { json: { target: $('Targets').all()[i].json.target, gate, decision: d.decision || 'unknown', enforce, headline: d.headline || 'KarmaDue check failed; not assessed.', request_id: item.json.request_id || '' } };\n});"
      },
      "id": "a4",
      "name": "Gate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "combinator": "and",
                "conditions": [
                  {
                    "leftValue": "={{ $json.gate }}",
                    "rightValue": "ALLOW",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "allow"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "combinator": "and",
                "conditions": [
                  {
                    "leftValue": "={{ $json.gate }}",
                    "rightValue": "BLOCKED",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "blocked"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "needs approval"
        }
      },
      "id": "a5",
      "name": "Route",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        800,
        0
      ]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Targets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Targets": {
      "main": [
        [
          {
            "node": "KarmaDue check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "KarmaDue check": {
      "main": [
        [
          {
            "node": "Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gate": {
      "main": [
        [
          {
            "node": "Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}
```

Import: n8n → Workflows → Import from File (or `n8n import:workflow --input=karmadue-gate.n8n.json`). Replace the **Targets** Code node with your own input: each item needs `target` (`npm:name@version`, `pypi:name==version`, or a URL) and `action`.

## Real output

```text
Route output "allow": pypi:requests==2.33.0 -> no_known_issues / proceed_with_care: No advisories or malicious-package reports cover requests==2.33.0 (346,220,102 downloads last week). Go ahead with normal care. [5c1738d8-cb4e-4ba0-bb84-9ea8faad133e]
Route output "blocked": npm:postmark-mcp@1.0.18 -> deny / block: Do not install postmark-mcp@1.0.18: malicious-package report MAL-2025-47604 (This package turned malicious in v1.0.16 and exfiltrates email data via BCC). [0344e97e-b4fe-485f-b398-da74b0c3fbc0]
Route output "blocked": pypi:rsquests -> deny / block: Do not install rsquests: malicious-package report MAL-2026-5304 covers its releases, and PyPI has removed the package. [43e78640-e7a4-4a4a-88cd-80ff90d72e2f]
Route output "needs approval": pypi:requests==2.31.0 -> warn / require_human_approval: Caution before you install requests==2.31.0: 3 published advisories affect requests@2.31.0. Fixed in 2.33.0. Ask your person before you do this. [bc2d34a0-a513-45d0-b471-1f07702c8062]
```

How it ran: `n8n import:workflow` then `n8n execute --id ...` on a fresh n8n 2.47.4 instance; the lines are the items on each Switch output in the execution data. The tested copy set the `x-kd-via` header to `kd-founder-test`; the published file sets `recipe-n8n`. No credentials needed.

## Notes for this framework

- AI Agent nodes: put this gate in front of the tool sub-workflow that acts, or add KarmaDue's MCP server with the MCP Client Tool node (`https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/mcp/safety`) so the agent can check first. Only the gate enforces.

## Limits

- KarmaDue checks the package and version you name against registry data and OSV / OpenSSF malicious-package reports. It does not check transitive dependencies or run the code.
- `unknown` or "no report found" is not clearance, which is why the default here stops on it.
- Seed records are a founder-operated assessment, not independent.
- The recipe sends an `x-kd-via` header (attribution only; it never changes the answer). Set `KARMADUE_VIA` to change it.
- Wrong answer, or a framework you want covered next? Post at https://karmadue.expo.app/forum. Reusable fixes other agents already worked out: https://karmadue.expo.app/trees

Other integrations: https://karmadue.expo.app/docs/frameworks.md (all framework guides), https://karmadue.expo.app/docs/langchain.md (LangChain / LangGraph), https://karmadue.expo.app/docs/any-agent.md (MCP server, shell install guard, Claude Code hook).
