@ironfang/n8n-nodes-ironfang 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Use Ironfang Render, Finance, Audit and Rig, and Ironfang's public developer
6
6
  tools, in n8n. The Ironfang action node has **162 operations**, grouped by
7
7
  product. Ironfang Trigger receives signed Audit, Finance and Render events.
8
8
 
9
- **Release: 0.4.1.** This patch makes the package installable on n8n releases
9
+ **Release: 0.4.3.** Credits, cache status and webhook signatures are read from the `X-Ironfang-*` and `Ironfang-*` headers; update before the API stops sending the launch-era ones. 0.4.2 moved the API to `api.ironfang.com` (credentials saved with the old `api.ironfang.uk` address keep working unchanged), and 0.4.1 made the package installable on n8n releases
10
10
  before 2.33 that use PostgreSQL; see [Installation](#installation-and-credentials).
11
11
  It includes 0.4.0, in which the products are named Ironfang Render, Finance and Audit
12
12
  (formerly Renderwolf, Financewolf and Auditwolf), and the node follows: requests
@@ -27,7 +27,7 @@ node's newest version as an integer and these packages' newest node version was
27
27
  1.1 or 1.2. Install 0.4.1 or later, or upgrade n8n. n8n Cloud installation requires n8n's manual package verification;
28
28
  npm publishing and automated checks do not establish that approval.
29
29
 
30
- Create an `if_live_` platform API key at [portal.ironfang.uk](https://portal.ironfang.uk)
30
+ Create an `if_live_` platform API key at [portal.ironfang.com](https://portal.ironfang.com)
31
31
  with the scopes your workflow needs, then select an Ironfang API credential.
32
32
  Existing `rw_live_` Render keys and both origin-only and `/renderwolf` base
33
33
  URLs remain supported. Other product bases and custom gateway prefixes work too.
@@ -54,10 +54,10 @@ The [operation reference](docs/operations.md) lists each action's endpoint,
54
54
  permission, query fields and request example. Complex API bodies use a JSON
55
55
  editor so nested fields remain available. Replace the example values before
56
56
  running a write operation. Request definitions are available in the product
57
- specifications: [Render](https://api.ironfang.uk/render/openapi.yaml),
58
- [Finance](https://api.ironfang.uk/finance/openapi.yaml),
59
- [Audit](https://api.ironfang.uk/audit/openapi.yaml),
60
- [Rig](https://api.ironfang.uk/rig/openapi.yaml).
57
+ specifications: [Render](https://api.ironfang.com/render/openapi.yaml),
58
+ [Finance](https://api.ironfang.com/finance/openapi.yaml),
59
+ [Audit](https://api.ironfang.com/audit/openapi.yaml),
60
+ [Rig](https://api.ironfang.com/rig/openapi.yaml).
61
61
 
62
62
  Screenshots support PNG/JPEG/WebP and phone/tablet device presets. Advanced
63
63
  Render Options accepts custom `css`, a page `script`, ordered `actions` (click,
@@ -164,8 +164,9 @@ The trigger verifies HMAC-SHA256 over the timestamp and exact body bytes using
164
164
  constant-time comparison, accepts timestamps within five minutes, and rejects
165
165
  invalid signatures before starting a workflow. Render and Finance secrets are
166
166
  decoded from hex; Audit secrets are used as returned. Deliveries are signed in
167
- the `Renderwolf-`, `Financewolf-` and `Auditwolf-` headers the products launched
168
- with; the trigger reads the ones for the credential's product. The signed event ID, rather
167
+ the `Ironfang-Signature` and `Ironfang-Timestamp` headers; the trigger still
168
+ reads the `Renderwolf-`, `Financewolf-` or `Auditwolf-` headers the products
169
+ launched with when those are all a delivery carries. The signed event ID, rather
169
170
  than the unsigned header ID, is used for duplicate detection.
170
171
 
171
172
  Duplicate state covers seven days/up to 10,000 IDs in n8n workflow static data.
@@ -5,7 +5,7 @@ class IronfangApi {
5
5
  name = 'ironfangApi';
6
6
  icon = { light: 'file:ironfang.svg', dark: 'file:ironfang.dark.svg' };
7
7
  displayName = 'Ironfang API';
8
- documentationUrl = 'https://ironfang.uk/render/docs';
8
+ documentationUrl = 'https://ironfang.com/render/docs';
9
9
  properties = [
10
10
  {
11
11
  displayName: 'API Key',
@@ -20,7 +20,7 @@ class IronfangApi {
20
20
  displayName: 'Base URL',
21
21
  name: 'baseUrl',
22
22
  type: 'string',
23
- default: 'https://api.ironfang.uk/render',
23
+ default: 'https://api.ironfang.com/render',
24
24
  description: 'API origin or product base URL. Origin-only and /renderwolf values saved by earlier versions remain valid; the node selects the requested product path.',
25
25
  },
26
26
  {
@@ -5,7 +5,7 @@ class IronfangS3 {
5
5
  name = 'ironfangS3';
6
6
  displayName = 'Ironfang S3 Destination';
7
7
  icon = { light: 'file:ironfang.svg', dark: 'file:ironfang.dark.svg' };
8
- documentationUrl = 'https://ironfang.uk/renderwolf/docs';
8
+ documentationUrl = 'https://ironfang.com/renderwolf/docs';
9
9
  properties = [
10
10
  { displayName: 'Access Key', name: 'accessKey', type: 'string', default: '', required: true },
11
11
  { displayName: 'Secret Key', name: 'secretKey', type: 'string', typeOptions: { password: true }, default: '', required: true },
@@ -5,7 +5,7 @@ class IronfangWebhookApi {
5
5
  name = 'ironfangWebhookApi';
6
6
  displayName = 'Ironfang Webhook API';
7
7
  icon = { light: 'file:ironfang.svg', dark: 'file:ironfang.dark.svg' };
8
- documentationUrl = 'https://ironfang.uk/audit/docs';
8
+ documentationUrl = 'https://ironfang.com/audit/docs';
9
9
  properties = [
10
10
  { displayName: 'Product', name: 'product', type: 'options', default: 'auditwolf', options: [{ name: 'Audit', value: 'auditwolf' }, { name: 'Finance', value: 'financewolf' }, { name: 'Render', value: 'renderwolf' }] },
11
11
  { displayName: 'Signing Secret', name: 'signingSecret', type: 'string', typeOptions: { password: true }, default: '', required: true, description: 'The one-time secret returned when creating the webhook endpoint or destination. This is not an API key.' },
@@ -2,7 +2,7 @@ import type { IExecuteFunctions, INodeExecutionData, INodeTypeDescription } from
2
2
  import { credentialTest } from '../nodes/Ironfang/credentialTest';
3
3
  /**
4
4
  * Ironfang node. One node per vendor: pick a product with Resource, then an
5
- * operation within it. Ironfang Render (https://ironfang.uk/render/docs) was the
5
+ * operation within it. Ironfang Render (https://ironfang.com/render/docs) was the
6
6
  * first product, and its saved resource value keeps the name it launched under; later ones are added as further resources rather than as
7
7
  * separate nodes.
8
8
  *
@@ -6,7 +6,7 @@ const transport_1 = require("../nodes/Ironfang/transport");
6
6
  const credentialTest_1 = require("../nodes/Ironfang/credentialTest");
7
7
  /**
8
8
  * Ironfang node. One node per vendor: pick a product with Resource, then an
9
- * operation within it. Ironfang Render (https://ironfang.uk/render/docs) was the
9
+ * operation within it. Ironfang Render (https://ironfang.com/render/docs) was the
10
10
  * first product, and its saved resource value keeps the name it launched under; later ones are added as further resources rather than as
11
11
  * separate nodes.
12
12
  *
@@ -43,7 +43,7 @@ async function runOperation(context, item, op) {
43
43
  const publicMode = op.product === 'tools' || (op.product === 'financewolf' && parameter(context, 'authentication', item, 'apiKey') === 'public');
44
44
  if (publicMode && !op.public)
45
45
  throw new Error('This operation requires an API key; public mode cannot access saved results');
46
- const base = publicMode ? parameter(context, 'publicBaseUrl', item, 'https://api.ironfang.uk') : (await context.getCredentials('ironfangApi')).baseUrl;
46
+ const base = publicMode ? parameter(context, 'publicBaseUrl', item, 'https://api.ironfang.com') : (await context.getCredentials('ironfangApi')).baseUrl;
47
47
  const path = op.path.replace(/\{(\w+)\}/g, (_, name) => (0, transport_1.identifier)(context, parameter(context, name, item), item));
48
48
  const url = (0, transport_1.productBase)(base, op.product) + path;
49
49
  const qs = { ...parameter(context, 'query', item, {}) };
@@ -20,7 +20,7 @@ function queryField(field) {
20
20
  }
21
21
  exports.productParameters = [
22
22
  { displayName: 'Authentication', name: 'authentication', type: 'options', noDataExpression: true, default: 'apiKey', displayOptions: { show: { resource: ['financewolf'] } }, options: [{ name: 'API Key', value: 'apiKey' }, { name: 'Public (No Saved Results)', value: 'public' }], description: 'Public validation, generation, ruleset reads and report verification only. An invalid key never falls back to public access.' },
23
- { displayName: 'Public API Base URL', name: 'publicBaseUrl', type: 'string', default: 'https://api.ironfang.uk', displayOptions: { show: { resource: ['tools', 'financewolf'] } }, description: 'Used by public requests only; authenticated requests use the credential base URL' },
23
+ { displayName: 'Public API Base URL', name: 'publicBaseUrl', type: 'string', default: 'https://api.ironfang.com', displayOptions: { show: { resource: ['tools', 'financewolf'] } }, description: 'Used by public requests only; authenticated requests use the credential base URL' },
24
24
  { displayName: 'Public tools share per-address limits. Screenshot is limited to 20/hour/address and a shared daily ceiling. Render Screenshot is the account-based automation service.', name: 'toolsNotice', type: 'notice', default: '', displayOptions: { show: { resource: ['tools'] } } },
25
25
  ...catalog_1.operations.flatMap((op) => {
26
26
  const fields = [{ displayName: `Permission: ${op.scope}. ${op.description}.`, name: 'operationNotice', type: 'notice', default: '', displayOptions: show(op) }];
@@ -13,10 +13,16 @@ exports.productBase = productBase;
13
13
  exports.renderwolfBase = renderwolfBase;
14
14
  const n8n_workflow_1 = require("n8n-workflow");
15
15
  function apiBase(value) {
16
- const url = new URL(String(value || 'https://api.ironfang.uk/render'));
16
+ const url = new URL(String(value || 'https://api.ironfang.com/render'));
17
17
  if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
18
18
  throw new Error('Base URL must be an HTTP(S) URL without credentials, a query or fragment');
19
19
  }
20
+ // The API moved from ironfang.uk to ironfang.com on 28 Sep 2026. The old
21
+ // names redirect, but a redirect to another domain drops the API key, so
22
+ // credentials saved with an ironfang.uk address are sent to .com directly.
23
+ if (url.hostname === 'ironfang.uk' || url.hostname.endsWith('.ironfang.uk')) {
24
+ url.hostname = url.hostname.slice(0, -'uk'.length) + 'com';
25
+ }
20
26
  return url.toString().replace(/\/+$/, '');
21
27
  }
22
28
  function identifier(context, value, itemIndex) {
@@ -30,9 +36,9 @@ function responseMetadata(headers, statusCode) {
30
36
  const result = { statusCode };
31
37
  if (headers['x-ironfang-request-id'])
32
38
  result.requestId = headers['x-ironfang-request-id'];
33
- if (headers['x-renderwolf-cache'])
34
- result.cacheStatus = headers['x-renderwolf-cache'];
35
- const credits = headers['x-renderwolf-credits'];
39
+ if (headers['x-ironfang-cache'])
40
+ result.cacheStatus = headers['x-ironfang-cache'];
41
+ const credits = headers['x-ironfang-credits'];
36
42
  if (credits !== undefined && credits !== '' && Number.isFinite(Number(credits)))
37
43
  result.creditsCharged = Number(credits);
38
44
  return result;
@@ -4,8 +4,10 @@ exports.signingKey = signingKey;
4
4
  exports.verifyEvent = verifyEvent;
5
5
  exports.webhookCredentialTest = webhookCredentialTest;
6
6
  const crypto_1 = require("crypto");
7
- // Signature headers keep the launch names (Renderwolf-Signature and so on), which
8
- // are also the saved product values. Render and Finance issue hex secrets.
7
+ // Every product signs with Ironfang-Signature and Ironfang-Timestamp. The saved
8
+ // product values keep the launch names (renderwolf and so on), and deliveries
9
+ // from before the Ironfang-* headers carried only <Launch name>-Signature, which
10
+ // is still read when the new header is absent. Render and Finance issue hex secrets.
9
11
  function signingKey(product, secret) {
10
12
  if (['renderwolf', 'financewolf'].includes(product) && /^[a-f0-9]{64}$/i.test(secret))
11
13
  return Buffer.from(secret, 'hex');
@@ -16,8 +18,8 @@ function signingKey(product, secret) {
16
18
  function verifyEvent(raw, headers, product, secret, now) {
17
19
  if (!raw.length || raw.length > 65_536)
18
20
  throw new Error('Webhook payload size is invalid');
19
- const timestamp = headers[`${product}-timestamp`];
20
- const signature = headers[`${product}-signature`];
21
+ const timestamp = headers['ironfang-timestamp'] ?? headers[`${product}-timestamp`];
22
+ const signature = headers['ironfang-signature'] ?? headers[`${product}-signature`];
21
23
  if (typeof timestamp !== 'string' || !/^\d{10,11}$/.test(timestamp) || Math.abs(now / 1000 - Number(timestamp)) > 300)
22
24
  throw new Error('Webhook timestamp is invalid or expired');
23
25
  if (typeof signature !== 'string' || !/^v1=[a-f0-9]{64}$/.test(signature))