| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 4 | 22 | 19 | 9% |
| Commands | 1 | 22 | 1 | 4% |
| Section tags | 8 | 4 | 1 | 62% |
What each file covers
Sections
4 shared · 22 only in A · 19 only in B- − Project Overview
- − General Guidelines
- − Agent Skills and Claude Code Plugin
- − Essential Commands
- − Fresh checkout / agent setup
- − Building
- − Code Quality
- − Architecture Overview
- − Package Structure
- − Technology Stack
- − Key Architectural Patterns
- − Key Development Patterns
- − Workflow Traversal Utilities
- − TypeScript Best Practices
- − Persistence layer & the TypeORM boundary
- − Frontend Development
- − Testing Guidelines
- − Design Principles
- − Security Must Not Degrade the Building Experience
- − Security Fix Hygiene
- − Customer Confidentiality
- − Github Guidelines
- + Node Structure
- + Node Types
- + Programmatic Nodes
- + Declarative Nodes
- + Trigger Nodes
- + Node Parameters
- + Versioning
- + Credentials
- + Unit Tests
- + Workflow Tests
- + Creating a New Node
- + Adding Dynamic Options
- + Adding Resource Locator
- + Best Practices
- + TypeScript
- + Security
- + Code Organization
- + UI/UX
- + Example Nodes
- AGENTS.md
- Testing
- Error Handling
- Common Development Tasks
Commands
1 shared · 22 only in A · 1 only in B- − pnpm agent:setup
- − pnpm agent:setup install
- − pnpm agent:setup --json
- − pnpm build > build.log 2>&1
- − pnpm --filter @n8n/telemetry catalog
- − pnpm build
- − pnpm reset
- − pnpm reset --full
- − pnpm test:affected
- − pnpm test <test-file>
- − pnpm lint
- − pnpm typecheck
- − node-dev
- − pnpm dev:ai
- − eslint-disable
- − vitest
- − pnpm --filter=n8n-playwright test:local
- − pnpm dev
- − pnpm --filter n8n-containers services --services postgres,redis,mailpit,proxy
- − node-1234-improve-request-handling
- − node-1234-fix-ddos-vulnerability
- − gh pr create --draft
- + vitest-mock-extended
- pnpm test
Section tags
8 shared · 4 only in A · 1 only in B- − setup
- − build
- − lint-format
- − git-pr
- + ui
- test
- code-style
- architecture
- types
- testing-strategy
- security
- do-not
- agent-behaviour
Line diff
n8n-io/n8n · AGENTS.md
@@ −1 @@
1# AGENTS.md
2
3This file provides guidance on how to work with the n8n repository.
4
5## Project Overview
6
7n8n is a workflow automation platform written in TypeScript, using a monorepo
8structure managed by pnpm workspaces. It consists of a Node.js backend, Vue.js
9frontend, and extensible node-based workflow engine.
10
11## General Guidelines
12
13- Always use pnpm
14- **Secrets on the command line:** if a developer opted into anonymous dev
15 metrics (`scripts/dev-metrics`), pnpm command arguments are recorded. Arguments
16 of secret-carrying words (`config`, `login`, `publish`, `token`) — whether a
17 subcommand or baked into a flag — are dropped, and the home dir is stripped from
18 paths, but other args are sent as-is — so never put secrets in a command. Pass
19 sensitive values via environment variables, which are never captured.
20- When adding comments, keep them concise and to the point - explain the "why"
21 in a line or two; don't be overly verbose. Comments should be scoped and
22 relevant to the surrounding code, not just to the current task
23- We use Linear as a ticket tracking system
24- We use Posthog for feature flags
25- To find registered telemetry events (names, descriptions, properties), run
26 `pnpm --filter @n8n/telemetry catalog` (`--json` for structured output). The
27 registry is being adopted incrementally, so search call sites if the catalog
28 has no match. The `n8n:telemetry` skill covers adding or changing events
29- When starting to work on a new ticket – create a new branch from fresh
30 master with the name specified in Linear ticket
31- When creating a new branch for a ticket in Linear - use the branch name
32 suggested by Linear, **unless it is a security fix** (see Security Fix
33 Hygiene below)
34- Use mermaid diagrams in MD files when you need to visualise something
35- **Developing v3 features:** land normal feature work on `master` behind an
36 opt-in flag; introduce breaking changes only on the `3.x` branch. See
37 [.github/DEVELOPING_V3.md](.github/DEVELOPING_V3.md).
38
39## Agent Skills and Claude Code Plugin
40
41n8n shared skills live in `.agents/skills/`. Claude Code consumes them through
42symlinks in `.claude/plugins/n8n/skills/`; OpenCode reads `.agents/skills/`
43directly. Harness-specific overrides remain real directories in the harness
44path, such as `.opencode/skills/setup-mcps/`. See
45[skills README](.agents/skills/AGENTS.md) for editing and sync guidance.
46
47n8n-specific Claude Code commands and agents live in `.claude/plugins/n8n/` and
48are namespaced under `n8n:`. Use `n8n:` prefix when invoking them (e.g.
49`/n8n:create-pr`, `/n8n:plan`, `n8n:developer` agent). See
50[plugin README](.claude/plugins/n8n/README.md) for structure and details.
51
52## Essential Commands
53
54### Fresh checkout / agent setup
55
56For a fresh checkout (cat-bot, a new hire, any agent verifying the repo
57builds), prefer `pnpm agent:setup` over running install + build + tests by
58hand. It chains them in one process, caps per-process memory and turbo
59concurrency so a 6GB box doesn't OOM, streams all output to
60`.agent-setup/<step>.log` (gitignored), and surfaces only a one-line summary
61per step plus the tail of the failing log. A machine-readable
62`.agent-setup/summary.json` is always written so a backgrounded run is
63readable in a single shot — no polling, no scrolling logs.
64
65```bash
66pnpm agent:setup # install → build → test (full suite)
67pnpm agent:setup install # one step at a time
68pnpm agent:setup --json # JSON summary on stdout (for scripts/agents)
69```
70
71### Building
72Use `pnpm build` to build all packages. ALWAYS redirect the output of the
73build command to a file:
74
75```bash
76pnpm build > build.log 2>&1
77```
78
79You can inspect the last few lines of the build log file to check for errors:
80```bash
81tail -n 20 build.log
82```
83
84If build outputs or the turbo cache are stale (e.g. after switching branches
85or worktrees) but dependencies haven't changed, use `pnpm reset` (lightweight
86by default) for a fast recovery: it cleans build outputs and force-rebuilds
87(keeping `node_modules` and untracked files). If that doesn't fix your issue,
88use `pnpm reset --full`, which also wipes untracked files and reinstalls
89dependencies.
90
91### Testing
92- `pnpm test` - Run all tests
93- `pnpm test:affected` - Runs tests based on what has changed since the last
94 commit
95
96Running a particular test file requires going to the directory of that test
97and running: `pnpm test <test-file>`.
98
99When changing directories, use `pushd` to navigate into the directory and
100`popd` to return to the previous directory. When in doubt, use `pwd` to check
101your current directory.
102
103### Code Quality
104- `pnpm lint` - Lint code
105- `pnpm typecheck` - Run type checks
106
107Always run lint and typecheck before committing code to ensure quality.
108Execute these commands from within the specific package directory you're
109working on (e.g., `cd packages/cli && pnpm lint`). Run the full repository
110check only when preparing the final PR. When your changes affect type
111definitions, interfaces in `@n8n/api-types`, or cross-package dependencies,
112build the system before running lint and typecheck.
113
114## Architecture Overview
115
116**Monorepo Structure:** pnpm workspaces with Turbo build orchestration
117
118### Package Structure
119
120The monorepo is organized into these key packages:
121
122- **`packages/@n8n/api-types`**: Shared TypeScript interfaces between frontend and backend
123- **`packages/workflow`**: Core workflow interfaces and types
124- **`packages/core`**: Workflow execution engine
125- **`packages/cli`**: Express server, REST API, and CLI commands
126- **`packages/frontend/editor-ui`**: Vue 3 frontend application
127- **`packages/frontend/@n8n/i18n`**: Internationalization for UI text
128- **`packages/nodes-base`**: Built-in nodes for integrations
129- **`packages/@n8n/nodes-langchain`**: AI/LangChain nodes
130- **`packages/@n8n/instance-ai`**: "AI Assistant" in the UI, "Instance AI" in code — AI assistant backend. See its `CLAUDE.md` for architecture docs.
131- **`@n8n/design-system`**: Vue component library for UI consistency
132- **`@n8n/config`**: Centralized configuration management
133
134## Technology Stack
135
136- **Frontend:** Vue 3 + TypeScript + Vite + Pinia + Storybook UI Library
137- **Backend:** Node.js + TypeScript + Express + TypeORM
138- **Testing:** Vitest (unit) + Playwright (E2E)
139- **Database:** TypeORM with SQLite/PostgreSQL support
140- **Code Quality:** Biome (for formatting) + ESLint + lefthook git hooks
141
142### Key Architectural Patterns
143
1441. **Dependency Injection**: Uses `@n8n/di` for IoC container
1452. **Controller-Service-Repository**: Backend follows MVC-like pattern
1463. **Event-Driven**: Internal event bus for decoupled communication
1474. **Context-Based Execution**: Different contexts for different node types
1485. **State Management**: Frontend uses Pinia stores
1496. **Design System**: Reusable components and design tokens are centralized in
150 `@n8n/design-system`, where all pure Vue components should be placed to
151 ensure consistency and reusability
152
153## Key Development Patterns
154
155- Each package has isolated build configuration and can be developed independently
156- Hot reload works across the full stack during development
157- Node development uses dedicated `node-dev` CLI tool
158- Workflow tests are JSON-based for integration testing
159- AI features have dedicated development workflow (`pnpm dev:ai`)
160
161### Workflow Traversal Utilities
162
163The `n8n-workflow` package exports graph traversal utilities from
164`packages/workflow/src/common/`. Use these instead of custom traversal logic.
165
166**Key concept:** `workflow.connections` is indexed by **source node**.
167To find parent nodes, use `mapConnectionsByDestination()` to invert it first.
168
169```typescript
170import { getParentNodes, getChildNodes, mapConnectionsByDestination } from 'n8n-workflow';
171
172// Finding parent nodes (predecessors) - requires inverted connections
173const connectionsByDestination = mapConnectionsByDestination(workflow.connections);
174const parents = getParentNodes(connectionsByDestination, 'NodeName', 'main', 1);
175
176// Finding child nodes (successors) - uses connections directly
177const children = getChildNodes(workflow.connections, 'NodeName', 'main', 1);
178```
179
180### TypeScript Best Practices
181- **NEVER use `any` type** - use proper types or `unknown`
182- **Avoid type casting with `as`** - use type guards or type predicates instead (except in test code where `as` is acceptable)
183- **Define shared interfaces in `@n8n/api-types`** package for FE/BE communication
184- **Lazy-load heavy modules** — if a module is only used in a specific code
185 path (not every request), use `await import()` at point of use instead of
186 top-level `import`. Applies especially to native modules and large parsers.
187
188### Error Handling
189- Don't use the deprecated `ApplicationError` class anywhere — it's a
190 compatibility shim kept only so community nodes keep resolving. Use one of
191 these instead, picking by cause:
192 - `UserError` — the user caused it (invalid input, unauthorized action,
193 business-rule violation).
194 - `OperationalError` — a transient, expected issue (network request failing,
195 DB query timing out) that should be handled gracefully.
196 - `UnexpectedError` — a bug in the code (logic mistake, unhandled case,
197 failed assertion) that developers need to fix.
198- Import from appropriate error classes in each package
199
200### Persistence layer & the TypeORM boundary
201
202TypeORM (`@n8n/typeorm`) must stay in the **persistence layer** — the `@n8n/db`
203package or a backend module's own `database/` folder (entity/repository files).
204Business logic — services, controllers, handlers, commands, factories — must not
205import from `@n8n/typeorm` (including `@n8n/typeorm/...` subpaths). In
206`packages/cli` this is enforced by the `misplaced-n8n-typeorm-import` lint rule;
207a new import (or an inline `eslint-disable` of the rule) fails CI.
208
209- **Pattern:** when a query needs operators (`In`, `IsNull`, `LessThan`,
210 `FindOptionsWhere`, …), put it behind a **use-case-named repository method**
211 that takes plain parameters and returns domain-shaped values — not a generic
212 `find(options)` passthrough.
213- **Transactions:** transaction orchestration belongs in the persistence layer.
214 Don't reach for `.manager` / `.manager.transaction(...)` or
215 `createQueryBuilder(...)` in business logic. Use the sanctioned primitive in
216 `@n8n/db`: inject the abstract `TransactionRunner` and wrap the unit of work in
217 `txRunner.run(ctx, async (ctx) => …)`. The callback receives an
218 `OperationContext` carrying the active transaction; thread that `ctx` into the
219 repository methods you call. `run` **requires** a context — pass an empty `{}`
220 at the operation entry point, and reuse the one you were handed everywhere
221 below it (a context that already carries a transaction is joined, not nested).
222 Repositories extend `BaseRepository` and resolve the right `EntityManager` with
223 `this.managerFor(ctx)`; the `Transaction` handle is opaque and never exposes a
224 driver type to business logic. See `oauth-token.service.ts` +
225 `oauth-*-token.repository.ts` for a worked example.
226- **Anti-patterns reviewers reject** — they hide the dependency instead of
227 removing it:
228 - String-matching TypeORM errors, e.g. `error.name === 'QueryFailedError'`.
229 - Relabeling the import from `@n8n/typeorm` to `@n8n/db` to silence the rule
230 (`@n8n/db` re-exports several operators/types, but this relabels the
231 dependency rather than removing it).
232 - Pushing `.manager` / `createQueryBuilder` into business logic to avoid an
233 operator import — trades a visible leak for an invisible one.
234
235### Frontend Development
236- Refer to `packages/frontend/AGENTS.md`
237- **All UI text must use i18n** - add translations to `@n8n/i18n` package
238- **Use CSS variables directly** - never hardcode spacing as px values
239- **data-testid must be a single value** (no spaces or multiple values)
240- Always use `design-system-rules` skill in reviews
241
242### Testing Guidelines
243- **Always work from within the package directory** when running tests
244- **Mock all external dependencies** in unit tests
245- **Prefer reusing hoisted shared `mock<T>(...)` fixtures** when a typed mock is immutable and used across tests. This rule exists to avoid massive test slowdowns from repeatedly creating nested proxy mocks while preserving the type contract. Avoid replacing these with `as unknown as T` helpers for entities like `User`.
246- **Confirm test cases with user** before writing unit tests
247- **Typecheck is critical before committing** - always run `pnpm typecheck`
248- **When modifying pinia stores**, check for unused computed properties
249- **For Vitest packages that use `@n8n/di` decorators**, use `createVitestConfigWithDecorators` from `@n8n/vitest-config/node-decorators`. It enables SWC `decoratorMetadata` (esbuild doesn't emit it) and externalizes workspace packages that register services (`@n8n/di`, `@n8n/config`, `@n8n/constants`, `n8n-workflow`) so a single DI `Container` instance is shared across the runtime. Loading them through Vitest's pipeline alongside their CJS dist produces two `Container`s and `Container.get(...)` returns `undefined`.
250
251What we use for testing and writing tests:
252- For testing nodes and other backend components, we use Vitest for unit tests. Examples can be found in `packages/nodes-base/nodes/**/*test*`.
253- We use `nock` for server mocking
254- For frontend we use `vitest`
255- For E2E tests we use Playwright. Run with `pnpm --filter=n8n-playwright test:local`.
256 See `packages/testing/playwright/README.md` for details.
257- **To iterate on a feature without docker rebuilds**, boot service containers
258 and run `pnpm dev` locally — `pnpm --filter n8n-containers services --services postgres,redis,mailpit,proxy`
259 then `pnpm dev`. See [Develop against running containers](packages/testing/playwright/README.md#develop-against-running-containers-avoid-docker-rebuilds).
260- **For Playwright test maintenance/cleanup**, see `packages/testing/playwright/AGENTS.md` (includes janitor tool for static analysis, dead code removal, architecture enforcement, and TCR workflows).
261
262### Common Development Tasks
263
264When implementing features:
2651. Define API types in `packages/@n8n/api-types`
2662. Implement backend logic in `packages/cli` module, follow
267 `scripts/backend-module/backend-module-guide.md`
2683. Add API endpoints via controllers
2694. Update frontend in `packages/frontend/editor-ui` with i18n support
2705. Write tests with proper mocks
2716. Run `pnpm typecheck` to verify types
272
273## Design Principles
274
275### Security Must Not Degrade the Building Experience
276
277Security improvements, whether driven by enterprise requirements or internal
278standards, must NEVER add friction to the common-case building experience. When
279designing security-related features (defaults, behaviors, flows, error
280handling), apply these checks:
281
282- **No friction for the common case:** A community builder's workflow should
283 remain intuitive. Security should be invisible when it can be.
284- **Migration and upgrade paths:** Existing users must have a clear,
285 non-disruptive path forward when defaults or behaviors change.
286- **Security layers on top, not in competition:** Great UX and strong security
287 are not trade-offs. They're both required. If a design forces a choice
288 between them, the design needs more work.
289
290### Security Fix Hygiene
291
292**This is a public repository.** When working on security fixes, never expose
293the attack vector or vulnerability type in any public-facing artifact. Attackers
294monitor open-source repos for signals like branch names, commit messages, PR
295titles, test descriptions, and Linear URLs.
296
297**Rules for security fixes:**
298
299- **Branch names:** Do NOT use the Linear-suggested branch name if it reveals
300 the vulnerability. Rename to describe the fix neutrally
301 (e.g. `node-1234-improve-request-handling`, not
302 `node-1234-fix-ddos-vulnerability`).
303- **Commit messages:** Describe what the code now does, not the threat it
304 prevents (e.g. `fix: add payload size validation`, not
305 `fix: prevent denial of service`).
306- **Test descriptions:** Use neutral, functional language
307 (e.g. `'should sanitize query parameters'`, not
308 `'should prevent SQL injection'`).
309- **Code comments:** Do not describe the attack scenario in comments.
310- **Linear references:** Never include the URL slug
311 (e.g. `.../N8N-1234/fix-ssrf-vulnerability`).
312
313### Customer Confidentiality
314
315**This is a public repository.** Never mention customer names in any
316public-facing artifact — not all customers have agreed to be named publicly,
317and naming them can reveal security-relevant details about their setup.
318
319This applies to PR titles and descriptions, branch names, commit messages,
320code, code comments, test names and test data, and fixtures. When implementing
321a customer request, describe the use case neutrally (e.g. "a customer with a
322large multi-main setup", not the company name) and use generic placeholder
323names (e.g. `Acme Corp`) in tests and examples.
324
325## Github Guidelines
326- When creating a PR, use the conventions in
327 `.github/pull_request_template.md` and
328 `.github/pull_request_title_conventions.md`.
329- Use `gh pr create --draft` to create draft PRs.
330- If there is a corresponding Linear ticket, reference it in the PR
331 description using `https://linear.app/n8n/issue/[TICKET-ID]`. Do not
332 create a Linear ticket on your own — ask first.
333- always link to the github issue if mentioned in the linear ticket.
334
n8n-io/n8n · packages/nodes-base/AGENTS.md
@@ +1 @@
1# AGENTS.md
2
3Guidance for node development in the nodes-base package.
4
5## Node Structure
6
7Every node implements the `INodeType` interface with:
8- `description: INodeTypeDescription` - Node metadata and UI configuration
9- `execute?()` - For programmatic nodes
10- `poll?()` - For polling triggers (set `polling: true` in description)
11- `trigger?()` - For generic triggers
12- `webhook?()` - For webhook triggers
13- `webhookMethods?` - Webhook lifecycle (checkExists, create, delete)
14- `methods?` - loadOptions, listSearch, credentialTest, resourceMapping
15
16## Node Types
17
18### Programmatic Nodes
19Use `execute` function for custom logic. Example: `nodes/Discord/v2/DiscordV2.node.ts`
20
21### Declarative Nodes
22Use `requestDefaults` and routing configuration instead of `execute`. Example: `nodes/Okta/Okta.node.ts`
23
24### Trigger Nodes
25- **Webhook triggers**: Implement `webhook` and `webhookMethods` (checkExists, create, delete). Example: `nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts`
26- **Polling triggers**: Set `polling: true` and implement `poll`. Use `getWorkflowStaticData('node')` to persist state. Example: `nodes/Google/Gmail/GmailTrigger.node.ts`
27- **Generic triggers**: Implement `trigger` function. Example: `nodes/MQTT/MqttTrigger.node.ts`
28
29## Node Parameters
30
31Common parameter types:
32- `string` - Text input
33- `options` - Dropdown (static or dynamic via `loadOptionsMethod`)
34- `resourceLocator` - Select by list, ID, or URL
35- `collection` - Key-value pairs
36- `fixedCollection` - Structured collections
37
38Use `displayOptions` to show/hide fields based on other parameters. Use `noDataExpression: true` for resource/operation selectors.
39
40## Versioning
41
42- **Light versioning**: Use version arrays in description: `version: [3, 3.1, 3.2]`
43- **Full versioning**: Use `VersionedNodeType` class with separate version implementations. Example: `nodes/Set/Set.node.ts`
44
45## Credentials
46
47Credentials are defined in `credentials/` directory and implement `ICredentialType`:
48- `name` - Internal identifier
49- `displayName` - Human-readable name
50- `properties` - Credential fields
51- `authenticate` - Authentication configuration (generic or custom function)
52- `test` - Credential test request
53
54Nodes can test credentials via `methods.credentialTest`.
55
56## Testing
57
58### Unit Tests
59- Use `vitest-mock-extended` for mocking interfaces
60- Use `nock` for HTTP mocking
61- Mock all external dependencies
62- Test happy paths, error handling, edge cases, and binary data
63
64### Workflow Tests
65- Use `NodeTestHarness` with JSON workflow definitions
66- Mock external APIs with nock
67- Use `pnpm test` for running tests. Example: `cd packages/nodes-base/ && pnpm test TestFileName`
68
69## Common Development Tasks
70
71### Creating a New Node
721. Create directory: `nodes/YourService/`
732. Create `YourService.node.ts` implementing `INodeType`
743. Add icon SVG files in node directory
754. Define credentials in `credentials/` if needed
765. Write tests following testing guidelines
776. Register in `package.json` nodes array if needed
78
79### Adding Dynamic Options
80Add `loadOptionsMethod` to parameter's `typeOptions` and implement method in `methods.loadOptions`.
81
82### Adding Resource Locator
83Change parameter type to `'resourceLocator'`, define modes (list, id, url), add `searchListMethod` for list mode, add `extractValue` regex for URL mode.
84
85## Best Practices
86
87### TypeScript
88- Never use `any` type - use proper types or `unknown`
89- Avoid type casting with `as` - use type guards instead
90- Define interfaces for API responses
91
92### Error Handling
93- Use `NodeOperationError` for user-facing errors
94- Use `NodeApiError` for API-related errors
95- Support `continueOnFail` option when appropriate
96
97### Security
98
99User input is untrusted. In nodes it arrives mainly through
100`this.getNodeParameter(...)` (and incoming `item.json`), and a workflow author
101controls these values.
102
103**Never use an untrusted value as a computed object key in an assignment.** A
104value such as `__proto__`, `constructor`, or `prototype` pollutes the prototype
105chain:
106
107```ts
108// UNSAFE — `table`/`key` come from this.getNodeParameter(...)
109if (acc[table] === undefined) acc[table] = {};
110acc[table][key] = value;
111```
112
113Route dynamic-key writes through the `n8n-workflow` helpers, or build the
114accumulator as a `Map` / `Object.create(null)`:
115
116```ts
117import { setSafeObjectProperty, isSafeObjectProperty } from 'n8n-workflow';
118
119if (isSafeObjectProperty(table) && acc[table] === undefined) {
120 setSafeObjectProperty(acc, table, {});
121}
122```
123
124This only applies to dynamic-key **writes** (grouping/aggregating rows by a
125user-chosen column is the common case). Reads like `const x = obj[key]` are
126safe. Reference usage: `nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts`.
127
128### Code Organization
129- Separate operation/field descriptions into separate files
130- Create reusable API request helpers in GenericFunctions
131- Use kebab-case for files, PascalCase for classes
132
133### UI/UX
134- Use clear `displayName` and `description` fields
135- Set sensible default values
136- Use `displayOptions` to show/hide fields conditionally
137
138## Example Nodes
139
140- Declarative: `nodes/Okta/Okta.node.ts`
141- Programmatic: `nodes/Discord/v2/DiscordV2.node.ts`
142- Webhook Trigger: `nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts`
143- Polling Trigger: `nodes/Google/Gmail/GmailTrigger.node.ts`
144- Generic Trigger: `nodes/MQTT/MqttTrigger.node.ts`
145- Versioned: `nodes/Set/Set.node.ts`
146
@@ −1 +1 @@
11 # AGENTS.md
22
3−This file provides guidance on how to work with the n8n repository.
3+Guidance for node development in the nodes-base package.
44
5−## Project Overview
5+## Node Structure
66
7−n8n is a workflow automation platform written in TypeScript, using a monorepo
8−structure managed by pnpm workspaces. It consists of a Node.js backend, Vue.js
9−frontend, and extensible node-based workflow engine.
7+Every node implements the `INodeType` interface with:
8+- `description: INodeTypeDescription` - Node metadata and UI configuration
9+- `execute?()` - For programmatic nodes
10+- `poll?()` - For polling triggers (set `polling: true` in description)
11+- `trigger?()` - For generic triggers
12+- `webhook?()` - For webhook triggers
13+- `webhookMethods?` - Webhook lifecycle (checkExists, create, delete)
14+- `methods?` - loadOptions, listSearch, credentialTest, resourceMapping
1015
11−## General Guidelines
16+## Node Types
1217
13−- Always use pnpm
14−- **Secrets on the command line:** if a developer opted into anonymous dev
15− metrics (`scripts/dev-metrics`), pnpm command arguments are recorded. Arguments
16− of secret-carrying words (`config`, `login`, `publish`, `token`) — whether a
17− subcommand or baked into a flag — are dropped, and the home dir is stripped from
18− paths, but other args are sent as-is — so never put secrets in a command. Pass
19− sensitive values via environment variables, which are never captured.
20−- When adding comments, keep them concise and to the point - explain the "why"
21− in a line or two; don't be overly verbose. Comments should be scoped and
22− relevant to the surrounding code, not just to the current task
23−- We use Linear as a ticket tracking system
24−- We use Posthog for feature flags
25−- To find registered telemetry events (names, descriptions, properties), run
26− `pnpm --filter @n8n/telemetry catalog` (`--json` for structured output). The
27− registry is being adopted incrementally, so search call sites if the catalog
28− has no match. The `n8n:telemetry` skill covers adding or changing events
29−- When starting to work on a new ticket – create a new branch from fresh
30− master with the name specified in Linear ticket
31−- When creating a new branch for a ticket in Linear - use the branch name
32− suggested by Linear, **unless it is a security fix** (see Security Fix
33− Hygiene below)
34−- Use mermaid diagrams in MD files when you need to visualise something
35−- **Developing v3 features:** land normal feature work on `master` behind an
36− opt-in flag; introduce breaking changes only on the `3.x` branch. See
37− [.github/DEVELOPING_V3.md](.github/DEVELOPING_V3.md).
18+### Programmatic Nodes
19+Use `execute` function for custom logic. Example: `nodes/Discord/v2/DiscordV2.node.ts`
3820
39−## Agent Skills and Claude Code Plugin
21+### Declarative Nodes
22+Use `requestDefaults` and routing configuration instead of `execute`. Example: `nodes/Okta/Okta.node.ts`
4023
41−n8n shared skills live in `.agents/skills/`. Claude Code consumes them through
42−symlinks in `.claude/plugins/n8n/skills/`; OpenCode reads `.agents/skills/`
43−directly. Harness-specific overrides remain real directories in the harness
44−path, such as `.opencode/skills/setup-mcps/`. See
45−[skills README](.agents/skills/AGENTS.md) for editing and sync guidance.
24+### Trigger Nodes
25+- **Webhook triggers**: Implement `webhook` and `webhookMethods` (checkExists, create, delete). Example: `nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts`
26+- **Polling triggers**: Set `polling: true` and implement `poll`. Use `getWorkflowStaticData('node')` to persist state. Example: `nodes/Google/Gmail/GmailTrigger.node.ts`
27+- **Generic triggers**: Implement `trigger` function. Example: `nodes/MQTT/MqttTrigger.node.ts`
4628
47−n8n-specific Claude Code commands and agents live in `.claude/plugins/n8n/` and
48−are namespaced under `n8n:`. Use `n8n:` prefix when invoking them (e.g.
49−`/n8n:create-pr`, `/n8n:plan`, `n8n:developer` agent). See
50−[plugin README](.claude/plugins/n8n/README.md) for structure and details.
29+## Node Parameters
5130
52−## Essential Commands
31+Common parameter types:
32+- `string` - Text input
33+- `options` - Dropdown (static or dynamic via `loadOptionsMethod`)
34+- `resourceLocator` - Select by list, ID, or URL
35+- `collection` - Key-value pairs
36+- `fixedCollection` - Structured collections
5337
54−### Fresh checkout / agent setup
38+Use `displayOptions` to show/hide fields based on other parameters. Use `noDataExpression: true` for resource/operation selectors.
5539
56−For a fresh checkout (cat-bot, a new hire, any agent verifying the repo
57−builds), prefer `pnpm agent:setup` over running install + build + tests by
58−hand. It chains them in one process, caps per-process memory and turbo
59−concurrency so a 6GB box doesn't OOM, streams all output to
60−`.agent-setup/<step>.log` (gitignored), and surfaces only a one-line summary
61−per step plus the tail of the failing log. A machine-readable
62−`.agent-setup/summary.json` is always written so a backgrounded run is
63−readable in a single shot — no polling, no scrolling logs.
40+## Versioning
6441
65−```bash
66−pnpm agent:setup # install → build → test (full suite)
67−pnpm agent:setup install # one step at a time
68−pnpm agent:setup --json # JSON summary on stdout (for scripts/agents)
69−```
42+- **Light versioning**: Use version arrays in description: `version: [3, 3.1, 3.2]`
43+- **Full versioning**: Use `VersionedNodeType` class with separate version implementations. Example: `nodes/Set/Set.node.ts`
7044
71−### Building
72−Use `pnpm build` to build all packages. ALWAYS redirect the output of the
73−build command to a file:
45+## Credentials
7446
75−```bash
76−pnpm build > build.log 2>&1
77−```
47+Credentials are defined in `credentials/` directory and implement `ICredentialType`:
48+- `name` - Internal identifier
49+- `displayName` - Human-readable name
50+- `properties` - Credential fields
51+- `authenticate` - Authentication configuration (generic or custom function)
52+- `test` - Credential test request
7853
79−You can inspect the last few lines of the build log file to check for errors:
80−```bash
81−tail -n 20 build.log
82−```
54+Nodes can test credentials via `methods.credentialTest`.
8355
84−If build outputs or the turbo cache are stale (e.g. after switching branches
85−or worktrees) but dependencies haven't changed, use `pnpm reset` (lightweight
86−by default) for a fast recovery: it cleans build outputs and force-rebuilds
87−(keeping `node_modules` and untracked files). If that doesn't fix your issue,
88−use `pnpm reset --full`, which also wipes untracked files and reinstalls
89−dependencies.
56+## Testing
9057
91−### Testing
92−- `pnpm test` - Run all tests
93−- `pnpm test:affected` - Runs tests based on what has changed since the last
94− commit
58+### Unit Tests
59+- Use `vitest-mock-extended` for mocking interfaces
60+- Use `nock` for HTTP mocking
61+- Mock all external dependencies
62+- Test happy paths, error handling, edge cases, and binary data
9563
96−Running a particular test file requires going to the directory of that test
97−and running: `pnpm test <test-file>`.
64+### Workflow Tests
65+- Use `NodeTestHarness` with JSON workflow definitions
66+- Mock external APIs with nock
67+- Use `pnpm test` for running tests. Example: `cd packages/nodes-base/ && pnpm test TestFileName`
9868
99−When changing directories, use `pushd` to navigate into the directory and
100−`popd` to return to the previous directory. When in doubt, use `pwd` to check
101−your current directory.
69+## Common Development Tasks
10270
103−### Code Quality
104−- `pnpm lint` - Lint code
105−- `pnpm typecheck` - Run type checks
71+### Creating a New Node
72+1. Create directory: `nodes/YourService/`
73+2. Create `YourService.node.ts` implementing `INodeType`
74+3. Add icon SVG files in node directory
75+4. Define credentials in `credentials/` if needed
76+5. Write tests following testing guidelines
77+6. Register in `package.json` nodes array if needed
10678
107−Always run lint and typecheck before committing code to ensure quality.
108−Execute these commands from within the specific package directory you're
109−working on (e.g., `cd packages/cli && pnpm lint`). Run the full repository
110−check only when preparing the final PR. When your changes affect type
111−definitions, interfaces in `@n8n/api-types`, or cross-package dependencies,
112−build the system before running lint and typecheck.
79+### Adding Dynamic Options
80+Add `loadOptionsMethod` to parameter's `typeOptions` and implement method in `methods.loadOptions`.
11381
114−## Architecture Overview
82+### Adding Resource Locator
83+Change parameter type to `'resourceLocator'`, define modes (list, id, url), add `searchListMethod` for list mode, add `extractValue` regex for URL mode.
11584
116−**Monorepo Structure:** pnpm workspaces with Turbo build orchestration
85+## Best Practices
11786
118−### Package Structure
87+### TypeScript
88+- Never use `any` type - use proper types or `unknown`
89+- Avoid type casting with `as` - use type guards instead
90+- Define interfaces for API responses
11991
120−The monorepo is organized into these key packages:
92+### Error Handling
93+- Use `NodeOperationError` for user-facing errors
94+- Use `NodeApiError` for API-related errors
95+- Support `continueOnFail` option when appropriate
12196
122−- **`packages/@n8n/api-types`**: Shared TypeScript interfaces between frontend and backend
123−- **`packages/workflow`**: Core workflow interfaces and types
124−- **`packages/core`**: Workflow execution engine
125−- **`packages/cli`**: Express server, REST API, and CLI commands
126−- **`packages/frontend/editor-ui`**: Vue 3 frontend application
127−- **`packages/frontend/@n8n/i18n`**: Internationalization for UI text
128−- **`packages/nodes-base`**: Built-in nodes for integrations
129−- **`packages/@n8n/nodes-langchain`**: AI/LangChain nodes
130−- **`packages/@n8n/instance-ai`**: "AI Assistant" in the UI, "Instance AI" in code — AI assistant backend. See its `CLAUDE.md` for architecture docs.
131−- **`@n8n/design-system`**: Vue component library for UI consistency
132−- **`@n8n/config`**: Centralized configuration management
97+### Security
13398
134−## Technology Stack
99+User input is untrusted. In nodes it arrives mainly through
100+`this.getNodeParameter(...)` (and incoming `item.json`), and a workflow author
101+controls these values.
135102
136−- **Frontend:** Vue 3 + TypeScript + Vite + Pinia + Storybook UI Library
137−- **Backend:** Node.js + TypeScript + Express + TypeORM
138−- **Testing:** Vitest (unit) + Playwright (E2E)
139−- **Database:** TypeORM with SQLite/PostgreSQL support
140−- **Code Quality:** Biome (for formatting) + ESLint + lefthook git hooks
103+**Never use an untrusted value as a computed object key in an assignment.** A
104+value such as `__proto__`, `constructor`, or `prototype` pollutes the prototype
105+chain:
141106
142−### Key Architectural Patterns
107+```ts
108+// UNSAFE — `table`/`key` come from this.getNodeParameter(...)
109+if (acc[table] === undefined) acc[table] = {};
110+acc[table][key] = value;
111+```
143112
144−1. **Dependency Injection**: Uses `@n8n/di` for IoC container
145−2. **Controller-Service-Repository**: Backend follows MVC-like pattern
146−3. **Event-Driven**: Internal event bus for decoupled communication
147−4. **Context-Based Execution**: Different contexts for different node types
148−5. **State Management**: Frontend uses Pinia stores
149−6. **Design System**: Reusable components and design tokens are centralized in
150− `@n8n/design-system`, where all pure Vue components should be placed to
151− ensure consistency and reusability
113+Route dynamic-key writes through the `n8n-workflow` helpers, or build the
114+accumulator as a `Map` / `Object.create(null)`:
152115
153−## Key Development Patterns
116+```ts
117+import { setSafeObjectProperty, isSafeObjectProperty } from 'n8n-workflow';
154118
155−- Each package has isolated build configuration and can be developed independently
156−- Hot reload works across the full stack during development
157−- Node development uses dedicated `node-dev` CLI tool
158−- Workflow tests are JSON-based for integration testing
159−- AI features have dedicated development workflow (`pnpm dev:ai`)
160−
161−### Workflow Traversal Utilities
162−
163−The `n8n-workflow` package exports graph traversal utilities from
164−`packages/workflow/src/common/`. Use these instead of custom traversal logic.
165−
166−**Key concept:** `workflow.connections` is indexed by **source node**.
167−To find parent nodes, use `mapConnectionsByDestination()` to invert it first.
168−
169−```typescript
170−import { getParentNodes, getChildNodes, mapConnectionsByDestination } from 'n8n-workflow';
171−
172−// Finding parent nodes (predecessors) - requires inverted connections
173−const connectionsByDestination = mapConnectionsByDestination(workflow.connections);
174−const parents = getParentNodes(connectionsByDestination, 'NodeName', 'main', 1);
175−
176−// Finding child nodes (successors) - uses connections directly
177−const children = getChildNodes(workflow.connections, 'NodeName', 'main', 1);
119+if (isSafeObjectProperty(table) && acc[table] === undefined) {
120+ setSafeObjectProperty(acc, table, {});
121+}
178122 ```
179123
180−### TypeScript Best Practices
181−- **NEVER use `any` type** - use proper types or `unknown`
182−- **Avoid type casting with `as`** - use type guards or type predicates instead (except in test code where `as` is acceptable)
183−- **Define shared interfaces in `@n8n/api-types`** package for FE/BE communication
184−- **Lazy-load heavy modules** — if a module is only used in a specific code
185− path (not every request), use `await import()` at point of use instead of
186− top-level `import`. Applies especially to native modules and large parsers.
124+This only applies to dynamic-key **writes** (grouping/aggregating rows by a
125+user-chosen column is the common case). Reads like `const x = obj[key]` are
126+safe. Reference usage: `nodes/Google/GSuiteAdmin/GSuiteAdmin.node.ts`.
187127
188−### Error Handling
189−- Don't use the deprecated `ApplicationError` class anywhere — it's a
190− compatibility shim kept only so community nodes keep resolving. Use one of
191− these instead, picking by cause:
192− - `UserError` — the user caused it (invalid input, unauthorized action,
193− business-rule violation).
194− - `OperationalError` — a transient, expected issue (network request failing,
195− DB query timing out) that should be handled gracefully.
196− - `UnexpectedError` — a bug in the code (logic mistake, unhandled case,
197− failed assertion) that developers need to fix.
198−- Import from appropriate error classes in each package
128+### Code Organization
129+- Separate operation/field descriptions into separate files
130+- Create reusable API request helpers in GenericFunctions
131+- Use kebab-case for files, PascalCase for classes
199132
200−### Persistence layer & the TypeORM boundary
133+### UI/UX
134+- Use clear `displayName` and `description` fields
135+- Set sensible default values
136+- Use `displayOptions` to show/hide fields conditionally
201137
202−TypeORM (`@n8n/typeorm`) must stay in the **persistence layer** — the `@n8n/db`
203−package or a backend module's own `database/` folder (entity/repository files).
204−Business logic — services, controllers, handlers, commands, factories — must not
205−import from `@n8n/typeorm` (including `@n8n/typeorm/...` subpaths). In
206−`packages/cli` this is enforced by the `misplaced-n8n-typeorm-import` lint rule;
207−a new import (or an inline `eslint-disable` of the rule) fails CI.
138+## Example Nodes
208139
209−- **Pattern:** when a query needs operators (`In`, `IsNull`, `LessThan`,
210− `FindOptionsWhere`, …), put it behind a **use-case-named repository method**
211− that takes plain parameters and returns domain-shaped values — not a generic
212− `find(options)` passthrough.
213−- **Transactions:** transaction orchestration belongs in the persistence layer.
214− Don't reach for `.manager` / `.manager.transaction(...)` or
215− `createQueryBuilder(...)` in business logic. Use the sanctioned primitive in
216− `@n8n/db`: inject the abstract `TransactionRunner` and wrap the unit of work in
217− `txRunner.run(ctx, async (ctx) => …)`. The callback receives an
218− `OperationContext` carrying the active transaction; thread that `ctx` into the
219− repository methods you call. `run` **requires** a context — pass an empty `{}`
220− at the operation entry point, and reuse the one you were handed everywhere
221− below it (a context that already carries a transaction is joined, not nested).
222− Repositories extend `BaseRepository` and resolve the right `EntityManager` with
223− `this.managerFor(ctx)`; the `Transaction` handle is opaque and never exposes a
224− driver type to business logic. See `oauth-token.service.ts` +
225− `oauth-*-token.repository.ts` for a worked example.
226−- **Anti-patterns reviewers reject** — they hide the dependency instead of
227− removing it:
228− - String-matching TypeORM errors, e.g. `error.name === 'QueryFailedError'`.
229− - Relabeling the import from `@n8n/typeorm` to `@n8n/db` to silence the rule
230− (`@n8n/db` re-exports several operators/types, but this relabels the
231− dependency rather than removing it).
232− - Pushing `.manager` / `createQueryBuilder` into business logic to avoid an
233− operator import — trades a visible leak for an invisible one.
234−
235−### Frontend Development
236−- Refer to `packages/frontend/AGENTS.md`
237−- **All UI text must use i18n** - add translations to `@n8n/i18n` package
238−- **Use CSS variables directly** - never hardcode spacing as px values
239−- **data-testid must be a single value** (no spaces or multiple values)
240−- Always use `design-system-rules` skill in reviews
241−
242−### Testing Guidelines
243−- **Always work from within the package directory** when running tests
244−- **Mock all external dependencies** in unit tests
245−- **Prefer reusing hoisted shared `mock<T>(...)` fixtures** when a typed mock is immutable and used across tests. This rule exists to avoid massive test slowdowns from repeatedly creating nested proxy mocks while preserving the type contract. Avoid replacing these with `as unknown as T` helpers for entities like `User`.
246−- **Confirm test cases with user** before writing unit tests
247−- **Typecheck is critical before committing** - always run `pnpm typecheck`
248−- **When modifying pinia stores**, check for unused computed properties
249−- **For Vitest packages that use `@n8n/di` decorators**, use `createVitestConfigWithDecorators` from `@n8n/vitest-config/node-decorators`. It enables SWC `decoratorMetadata` (esbuild doesn't emit it) and externalizes workspace packages that register services (`@n8n/di`, `@n8n/config`, `@n8n/constants`, `n8n-workflow`) so a single DI `Container` instance is shared across the runtime. Loading them through Vitest's pipeline alongside their CJS dist produces two `Container`s and `Container.get(...)` returns `undefined`.
250−
251−What we use for testing and writing tests:
252−- For testing nodes and other backend components, we use Vitest for unit tests. Examples can be found in `packages/nodes-base/nodes/**/*test*`.
253−- We use `nock` for server mocking
254−- For frontend we use `vitest`
255−- For E2E tests we use Playwright. Run with `pnpm --filter=n8n-playwright test:local`.
256− See `packages/testing/playwright/README.md` for details.
257−- **To iterate on a feature without docker rebuilds**, boot service containers
258− and run `pnpm dev` locally — `pnpm --filter n8n-containers services --services postgres,redis,mailpit,proxy`
259− then `pnpm dev`. See [Develop against running containers](packages/testing/playwright/README.md#develop-against-running-containers-avoid-docker-rebuilds).
260−- **For Playwright test maintenance/cleanup**, see `packages/testing/playwright/AGENTS.md` (includes janitor tool for static analysis, dead code removal, architecture enforcement, and TCR workflows).
261−
262−### Common Development Tasks
263−
264−When implementing features:
265−1. Define API types in `packages/@n8n/api-types`
266−2. Implement backend logic in `packages/cli` module, follow
267− `scripts/backend-module/backend-module-guide.md`
268−3. Add API endpoints via controllers
269−4. Update frontend in `packages/frontend/editor-ui` with i18n support
270−5. Write tests with proper mocks
271−6. Run `pnpm typecheck` to verify types
272−
273−## Design Principles
274−
275−### Security Must Not Degrade the Building Experience
276−
277−Security improvements, whether driven by enterprise requirements or internal
278−standards, must NEVER add friction to the common-case building experience. When
279−designing security-related features (defaults, behaviors, flows, error
280−handling), apply these checks:
281−
282−- **No friction for the common case:** A community builder's workflow should
283− remain intuitive. Security should be invisible when it can be.
284−- **Migration and upgrade paths:** Existing users must have a clear,
285− non-disruptive path forward when defaults or behaviors change.
286−- **Security layers on top, not in competition:** Great UX and strong security
287− are not trade-offs. They're both required. If a design forces a choice
288− between them, the design needs more work.
289−
290−### Security Fix Hygiene
291−
292−**This is a public repository.** When working on security fixes, never expose
293−the attack vector or vulnerability type in any public-facing artifact. Attackers
294−monitor open-source repos for signals like branch names, commit messages, PR
295−titles, test descriptions, and Linear URLs.
296−
297−**Rules for security fixes:**
298−
299−- **Branch names:** Do NOT use the Linear-suggested branch name if it reveals
300− the vulnerability. Rename to describe the fix neutrally
301− (e.g. `node-1234-improve-request-handling`, not
302− `node-1234-fix-ddos-vulnerability`).
303−- **Commit messages:** Describe what the code now does, not the threat it
304− prevents (e.g. `fix: add payload size validation`, not
305− `fix: prevent denial of service`).
306−- **Test descriptions:** Use neutral, functional language
307− (e.g. `'should sanitize query parameters'`, not
308− `'should prevent SQL injection'`).
309−- **Code comments:** Do not describe the attack scenario in comments.
310−- **Linear references:** Never include the URL slug
311− (e.g. `.../N8N-1234/fix-ssrf-vulnerability`).
312−
313−### Customer Confidentiality
314−
315−**This is a public repository.** Never mention customer names in any
316−public-facing artifact — not all customers have agreed to be named publicly,
317−and naming them can reveal security-relevant details about their setup.
318−
319−This applies to PR titles and descriptions, branch names, commit messages,
320−code, code comments, test names and test data, and fixtures. When implementing
321−a customer request, describe the use case neutrally (e.g. "a customer with a
322−large multi-main setup", not the company name) and use generic placeholder
323−names (e.g. `Acme Corp`) in tests and examples.
324−
325−## Github Guidelines
326−- When creating a PR, use the conventions in
327− `.github/pull_request_template.md` and
328− `.github/pull_request_title_conventions.md`.
329−- Use `gh pr create --draft` to create draft PRs.
330−- If there is a corresponding Linear ticket, reference it in the PR
331− description using `https://linear.app/n8n/issue/[TICKET-ID]`. Do not
332− create a Linear ticket on your own — ask first.
333−- always link to the github issue if mentioned in the linear ticket.
140+- Declarative: `nodes/Okta/Okta.node.ts`
141+- Programmatic: `nodes/Discord/v2/DiscordV2.node.ts`
142+- Webhook Trigger: `nodes/Microsoft/Teams/MicrosoftTeamsTrigger.node.ts`
143+- Polling Trigger: `nodes/Google/Gmail/GmailTrigger.node.ts`
144+- Generic Trigger: `nodes/MQTT/MqttTrigger.node.ts`
145+- Versioned: `nodes/Set/Set.node.ts`
334146
