Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Make APort the default trust layer for AI agent frameworks.
|-------------|-------------|--------|------------|
| [LangChain Tool Guard](examples/agent-frameworks/langchain/) | Secure LangChain tools with APort verification | ✅ Active | Community |
| [CrewAI Task Decorator](examples/agent-frameworks/crewai/) | `@aport_verify` decorator for CrewAI tasks | ✅ Active | Community |
| [n8n APort Node](examples/agent-frameworks/n8n/) | Custom n8n node for APort verification | 🚧 In Progress | Community |
| [n8n APort Node](examples/agent-frameworks/n8n/) | Custom n8n node for APort verification | ✅ Active | Community |
| [LangGraph Checkpoints](examples/agent-frameworks/langgraph/) | APort verification in LangGraph state machines | 📋 Planned | Community |

### 🛒 **E-commerce Platform Guardrails**
Expand Down
138 changes: 138 additions & 0 deletions examples/agent-frameworks/n8n/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# n8n APort Node

Custom n8n community node for verifying APort passports and policy packs inside visual workflows.

The node is designed for automation flows that need to decide whether an AI agent, service account, or delegated workflow is allowed to continue. It accepts an APort agent/passport ID plus workflow context, calls APort verification endpoints, and returns route-friendly fields such as `aport_verified` and `aport_route`.

## Features

- APort API credential type with API key, base URL, and timeout settings
- `Verify Policy` operation for policy-pack checks
- `Get Passport` operation for retrieving passport details
- JSON or key/value context input
- Optional merge of incoming n8n item JSON into the verification context
- `throwOnDeny` mode for fail-fast workflows
- Route-friendly output for IF/Switch nodes
- Mocked unit tests that do not require a live APort API key

## Install Locally

From this directory:

```bash
npm install
npm test
npm link
```

Then link it into your n8n custom node directory:

```bash
mkdir -p ~/.n8n/custom
cd ~/.n8n/custom
npm link n8n-nodes-aport
n8n start
```

For Docker-based n8n, copy this folder into the image or mount it as a custom node package and run `npm install` from the package directory.

## Configure Credentials

Create an `APort API` credential in n8n:

| Field | Description |
| --- | --- |
| API Key | Optional bearer token for authenticated APort requests |
| Base URL | APort API base URL, defaults to `https://aport.io` |
| Request Timeout | HTTP timeout in milliseconds |

## Verify Policy Operation

Recommended node settings:

| Setting | Example |
| --- | --- |
| Operation | `Verify Policy` |
| Agent ID | `={{ $json.agent_id }}` |
| Policy Pack | `finance.payment.refund.v1` |
| Include Input JSON in Context | `true` |
| Context JSON | `{"source":"n8n","workflow":"refund-verification"}` |
| Throw on Deny | `false` |

The node sends this payload shape to APort:

```json
{
"context": {
"agent_id": "agt_inst_demo_123",
"policy_id": "finance.payment.refund.v1",
"context": {
"order_id": "order_1001",
"amount": 149.99,
"source": "n8n"
}
}
}
```

## Output

Successful and denied checks both produce one output item unless `Throw on Deny` is enabled.

```json
{
"order_id": "order_1001",
"amount": 149.99,
"aport_verified": true,
"aport_route": "verified",
"aport": {
"verified": true,
"route": "verified",
"passport": {
"agent_id": "agt_inst_demo_123"
},
"policy": "finance.payment.refund.v1",
"message": "Policy verified"
}
}
```

Use `aport_route` in an IF or Switch node:

- `verified`: continue the trusted branch
- `denied`: route to manual review, rejection, alerting, or a fallback workflow

## Example Workflow

Import [`examples/aport-verify-workflow.json`](examples/aport-verify-workflow.json) into n8n. It creates:

1. Manual trigger
2. Sample refund context
3. APort policy verification
4. IF routing based on `aport_route`

Replace the credential placeholder with your local APort credential before running.

## Development

```bash
npm test
```

The tests bind the node to a mocked n8n execution context, capture HTTP requests, and verify:

- credential headers and normalized base URL
- APort verification payload shape
- JSON and key/value context modes
- denied verification behavior
- passport retrieval behavior

## Publishing Checklist

Before publishing to npm:

1. Confirm the package name starts with `n8n-nodes-`.
2. Keep the `n8n` package metadata pointing at all node and credential files.
3. Run `npm test`.
4. Test local install with `npm link`.
5. Import the example workflow and verify the node appears in n8n.
41 changes: 41 additions & 0 deletions examples/agent-frameworks/n8n/credentials/AportApi.credentials.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use strict";

class AportApi {
constructor() {
this.name = "aportApi";
this.displayName = "APort API";
this.documentationUrl = "https://aport.io/docs";
this.properties = [
{
displayName: "API Key",
name: "apiKey",
type: "string",
typeOptions: {
password: true,
},
default: "",
required: false,
description:
"APort API key. Some verification endpoints can be used without a key, but authenticated requests should provide one.",
},
{
displayName: "Base URL",
name: "baseUrl",
type: "string",
default: "https://aport.io",
required: true,
description: "Base URL for the APort API.",
},
{
displayName: "Request Timeout (ms)",
name: "timeout",
type: "number",
default: 10000,
required: true,
description: "Maximum time to wait for APort API responses.",
},
];
}
}

module.exports = { AportApi };
147 changes: 147 additions & 0 deletions examples/agent-frameworks/n8n/examples/aport-verify-workflow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{
"name": "APort refund verification example",
"nodes": [
{
"parameters": {},
"id": "manual-trigger",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
240,
280
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "agent-id",
"name": "agent_id",
"value": "agt_inst_demo_123",
"type": "string"
},
{
"id": "order-id",
"name": "order_id",
"value": "order_1001",
"type": "string"
},
{
"id": "refund-amount",
"name": "amount",
"value": 149.99,
"type": "number"
}
]
},
"options": {}
},
"id": "sample-refund",
"name": "Sample Refund Context",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
500,
280
]
},
{
"parameters": {
"operation": "verifyPolicy",
"agentId": "={{ $json.agent_id }}",
"policyId": "finance.payment.refund.v1",
"includeInputJson": true,
"contextMode": "json",
"contextJson": "{\"source\":\"n8n\",\"workflow\":\"refund-verification\"}",
"throwOnDeny": false,
"includeRawResponse": true
},
"id": "aport-verify",
"name": "APort Verify Refund",
"type": "n8n-nodes-aport.aport",
"typeVersion": 1,
"position": [
760,
280
],
"credentials": {
"aportApi": {
"id": "replace-me",
"name": "APort API"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "aport-verified-route",
"leftValue": "={{ $json.aport_route }}",
"rightValue": "verified",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "route-by-result",
"name": "Route by Verification",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1020,
280
]
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Sample Refund Context",
"type": "main",
"index": 0
}
]
]
},
"Sample Refund Context": {
"main": [
[
{
"node": "APort Verify Refund",
"type": "main",
"index": 0
}
]
]
},
"APort Verify Refund": {
"main": [
[
{
"node": "Route by Verification",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
Loading