@taifoon/n8n-nodes-typesafe 0.1.0 → 1.0.0
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 +3 -3
- package/dist/credentials/TypeSafeApi.credentials.d.ts +4 -1
- package/dist/credentials/TypeSafeApi.credentials.js +1 -1
- package/dist/credentials/TypeSafeApi.credentials.js.map +1 -1
- package/dist/credentials/taifoon.dark.svg +1 -0
- package/dist/nodes/TaifoonTypeSafe/TaifoonTypeSafe.node.js +20 -39
- package/dist/nodes/TaifoonTypeSafe/TaifoonTypeSafe.node.js.map +1 -1
- package/dist/nodes/TaifoonTypeSafe/taifoonTypeSafe.dark.svg +1 -0
- package/dist/package.json +4 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/KEY_POLICY.md +15 -52
- package/docs/SECURE_KEYS.md +2 -5
- package/docs/TRANSLATION.md +1 -2
- package/docs/WORKFLOWS.md +1 -4
- package/package.json +4 -4
- package/dist/credentials/TaifoonGatewayApi.credentials.d.ts +0 -13
- package/dist/credentials/TaifoonGatewayApi.credentials.js +0 -31
- package/dist/credentials/TaifoonGatewayApi.credentials.js.map +0 -1
package/docs/KEY_POLICY.md
CHANGED
|
@@ -1,58 +1,21 @@
|
|
|
1
1
|
# Key policy and rotation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This node uses **one secret: your TypeSafe API key.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
| | |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Where it lives | n8n's credential store, encrypted at rest with your instance's `N8N_ENCRYPTION_KEY` |
|
|
8
|
+
| Where it travels | from your n8n to `api.typesafe.ai`, as a bearer token over TLS. Nobody else is in the path |
|
|
9
|
+
| Where it never goes | workflow JSON, expressions, logs, execution records (HTTP errors are reduced to a status and the service's message, with key-shaped strings redacted, before n8n stores them), or to Taifoon |
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
| **Your TypeSafe key** | you | n8n credential store; sent per request over TLS in `x-typesafe-key` | stored, logged or echoed by Taifoon. It exists on the deck for the duration of one call |
|
|
13
|
-
| **The house TypeSafe key** | Taifoon | the deck's 0600 env file on the production host | sent to any browser, node or client. It funds each principal's three trial calls and nothing else |
|
|
11
|
+
**Rotate** it in TypeSafe's console and paste the new value into the credential. Do it on a schedule
|
|
12
|
+
(90 days is a reasonable default) and at once if a teammate leaves, a screenshot or log may have shown
|
|
13
|
+
it, or a credential test ran on a machine you do not control.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**The Free Trial connection uses no secret of yours.** It sends the item and your questions to
|
|
16
|
+
`typesafe.taifoon.dev`, which asks TypeSafe with Taifoon's key: three calls per client, up to 4
|
|
17
|
+
questions and 4,000 characters each. Taifoon logs the outcome, the country and the client type, and a
|
|
18
|
+
salted hash that lets it count clients. It does not log your address, your item or your questions.
|
|
19
|
+
Do not send personal data through the trial; use your own key for real work.
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
cookie valid for 12 hours. n8n caches that session as an expirable token and logs in again when it is
|
|
19
|
-
refused. Every later request carries the cookie and, only for `model: "jev"` after the trial, your
|
|
20
|
-
TypeSafe key in one header. Responses never contain a key; the deck's log lines (call and billing)
|
|
21
|
-
carry latency, tokens, USD and outcome, and never a key or your state.
|
|
22
|
-
|
|
23
|
-
## Rotation
|
|
24
|
-
|
|
25
|
-
**Your TypeSafe key.** Rotate it in TypeSafe's console, paste the new value into the n8n credential,
|
|
26
|
-
save. Nothing on Taifoon's side needs updating, because Taifoon never stored the old one. Rotate on a
|
|
27
|
-
schedule you would use for any vendor key (90 days is a reasonable default) and immediately on any of:
|
|
28
|
-
a teammate leaving, a workflow export shared outside your team, a credential test run on a machine
|
|
29
|
-
you do not control.
|
|
30
|
-
|
|
31
|
-
**Your Taifoon principal key.** Ask the operator to rotate it (`POST /agents/<id>/rotate-key` at the
|
|
32
|
-
identity door). The old key stops working at once; existing 12-hour sessions made with it expire on
|
|
33
|
-
their own and can be cut short by the operator. Update the n8n credential. Your licence, trial count
|
|
34
|
-
and spend ledger are attached to your principal, not to the key string, so they survive rotation.
|
|
35
|
-
|
|
36
|
-
**The house key.** Taifoon's to rotate: replace it in the deck's env file and restart the deck. Trial
|
|
37
|
-
calls pause for the length of the restart. No user action is needed.
|
|
38
|
-
|
|
39
|
-
## If a key leaks
|
|
40
|
-
|
|
41
|
-
| Leaked | Blast radius | Do this |
|
|
42
|
-
|---|---|---|
|
|
43
|
-
| your TypeSafe key | spend on YOUR TypeSafe quota, by anyone, until rotated | rotate in TypeSafe's console now; review their usage page |
|
|
44
|
-
| your Taifoon key | calls billed to your Taifoon key up to its DAILY token ceiling; your three trial calls; read access to the deck as you | ask the operator to rotate; the ceiling bounds the damage per day |
|
|
45
|
-
| the house key | Taifoon's TypeSafe quota | Taifoon rotates; trial calls resume after |
|
|
46
|
-
|
|
47
|
-
A Taifoon key cannot be used to read anyone's TypeSafe key: the deck has none to read.
|
|
48
|
-
|
|
49
|
-
## Policy the deck enforces, so you do not have to trust the node
|
|
50
|
-
|
|
51
|
-
- The first **three** `jev` calls a principal ever makes are funded by Taifoon. The count is per
|
|
52
|
-
principal, persisted, and a call that fails upstream is handed back.
|
|
53
|
-
- After that, a `jev` call needs **both** the `jev` licence on your principal's grant and your own
|
|
54
|
-
TypeSafe key. A missing piece is a `402` that names it. There is no silent fallback to the house key.
|
|
55
|
-
- Every call is metered per key **and** per model, persisted across restarts, against a daily ceiling.
|
|
56
|
-
- Login throttles: ten refused keys in five minutes from one address rests the door for that window.
|
|
57
|
-
- An answer that does not validate is never repaired or defaulted; with **Fail Closed** on (the
|
|
58
|
-
default) the node stops the item instead of passing a null downstream.
|
|
21
|
+
Provisioning a key for a team without anyone seeing it: [Supplying keys securely](SECURE_KEYS.md).
|
package/docs/SECURE_KEYS.md
CHANGED
|
@@ -39,8 +39,6 @@ The `_FILE` suffix works on any n8n setting and exists precisely so secrets stay
|
|
|
39
39
|
variables, which leak into `docker inspect`, process listings, crash dumps and CI logs. On Kubernetes,
|
|
40
40
|
mount a `Secret` at the same paths.
|
|
41
41
|
|
|
42
|
-
For the optional Taifoon gateway the type name is `taifoonGatewayApi`
|
|
43
|
-
(`{"taifoonGatewayApi":{"deckUrl":"https://deck.taifoon.dev","principalKey":"…","typesafeKey":"…"}}`).
|
|
44
42
|
|
|
45
43
|
## 3 · A vault (n8n Enterprise)
|
|
46
44
|
|
|
@@ -58,9 +56,8 @@ and gain nothing.
|
|
|
58
56
|
|
|
59
57
|
## Never send us a key
|
|
60
58
|
|
|
61
|
-
You do not need to give Taifoon a TypeSafe key, ever. On the direct connection we are not in the path
|
|
62
|
-
|
|
63
|
-
that call, and is never stored, logged or echoed. There is deliberately no form, email address or chat
|
|
59
|
+
You do not need to give Taifoon a TypeSafe key, ever. On the direct connection we are not in the path,
|
|
60
|
+
and the free trial uses our key, not yours. There is deliberately no form, email address or chat
|
|
64
61
|
where we accept keys. If anyone asks you for one in our name, it is not us.
|
|
65
62
|
|
|
66
63
|
If two organisations ever must hand a secret to each other, do it with public-key encryption to the
|
package/docs/TRANSLATION.md
CHANGED
|
@@ -49,5 +49,4 @@ threshold on your own labelled items, and leave a question unrouted until you ha
|
|
|
49
49
|
|
|
50
50
|
With `jev`, state is any JSON up to 96,000 characters: `{{ JSON.stringify($json) }}` sends the whole
|
|
51
51
|
incoming item. Compute first, judge second: do arithmetic in a Code node and send the result; ask the
|
|
52
|
-
model for judgment, never for a sum.
|
|
53
|
-
numbers and short symbols, answer noul and choice only, and return an uncalibrated boolean.
|
|
52
|
+
model for judgment, never for a sum.
|
package/docs/WORKFLOWS.md
CHANGED
|
@@ -19,12 +19,9 @@ The importable example is `examples/typed-gate.workflow.json`.
|
|
|
19
19
|
|
|
20
20
|
## Operating notes
|
|
21
21
|
|
|
22
|
-
- **Latency.** Jev answers a 4 to 8 question battery in 0.3 to 0.8 s.
|
|
23
|
-
The edge cuts any request at 100 s.
|
|
22
|
+
- **Latency.** Jev answers a 4 to 8 question battery in 0.3 to 0.8 s.
|
|
24
23
|
- **Cost.** About 450 to 550 input tokens per battery, 0.00002 USD on Jev. Output is free.
|
|
25
24
|
- **Retries.** A `503` means the lane is unavailable and nothing was charged; n8n's *Retry On Fail*
|
|
26
25
|
with backoff is the right response. A `402` is not retryable: it names what is missing.
|
|
27
|
-
- **Observability.** Use **List Lanes** on a schedule to alert on trial exhaustion, spend against the
|
|
28
|
-
daily ceiling, and lane health. The deck logs every call and every charge on its own side.
|
|
29
26
|
- **Batching.** One item is one request. For many items, keep n8n's batching on and the deck's daily
|
|
30
27
|
ceiling in mind; TypeSafe allows 1,200 requests a minute.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taifoon/n8n-nodes-typesafe",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "TypeSafe for n8n: ask Jev (a System One model) yes/no, pick-one and rate-it questions about any workflow item and route on calibrated answers. Works directly with your own TypeSafe key; includes a task-to-questions translator and Pass / Fail / Review outputs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"homepage": "https://github.com/taifoon-io/n8n-nodes-typesafe#readme",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "Taifoon",
|
|
20
|
+
"email": "226868200+yawningmonsoon@users.noreply.github.com",
|
|
20
21
|
"url": "https://github.com/taifoon-io"
|
|
21
22
|
},
|
|
22
23
|
"repository": {
|
|
@@ -45,15 +46,14 @@
|
|
|
45
46
|
"n8nNodesApiVersion": 1,
|
|
46
47
|
"strict": true,
|
|
47
48
|
"credentials": [
|
|
48
|
-
"dist/credentials/TypeSafeApi.credentials.js"
|
|
49
|
-
"dist/credentials/TaifoonGatewayApi.credentials.js"
|
|
49
|
+
"dist/credentials/TypeSafeApi.credentials.js"
|
|
50
50
|
],
|
|
51
51
|
"nodes": [
|
|
52
52
|
"dist/nodes/TaifoonTypeSafe/TaifoonTypeSafe.node.js"
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@n8n/node-cli": "^0.
|
|
56
|
+
"@n8n/node-cli": "^0.48.6",
|
|
57
57
|
"eslint": "^9.39.5",
|
|
58
58
|
"typescript": "5.9.2"
|
|
59
59
|
},
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestHelper, INodeProperties } from 'n8n-workflow';
|
|
2
|
-
export declare class TaifoonGatewayApi implements ICredentialType {
|
|
3
|
-
name: string;
|
|
4
|
-
displayName: string;
|
|
5
|
-
icon: "file:taifoon.svg";
|
|
6
|
-
documentationUrl: string;
|
|
7
|
-
properties: INodeProperties[];
|
|
8
|
-
preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject): Promise<{
|
|
9
|
-
sessionToken: string;
|
|
10
|
-
}>;
|
|
11
|
-
authenticate: IAuthenticateGeneric;
|
|
12
|
-
test: ICredentialTestRequest;
|
|
13
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TaifoonGatewayApi = void 0;
|
|
4
|
-
class TaifoonGatewayApi {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.name = 'taifoonGatewayApi';
|
|
7
|
-
this.displayName = 'Taifoon Gateway API';
|
|
8
|
-
this.icon = 'file:taifoon.svg';
|
|
9
|
-
this.documentationUrl = 'https://deck.taifoon.dev';
|
|
10
|
-
this.properties = [
|
|
11
|
-
{ displayName: 'Session', name: 'sessionToken', type: 'hidden', typeOptions: { expirable: true, password: true }, default: '' },
|
|
12
|
-
{ displayName: 'Deck URL', name: 'deckUrl', type: 'string', default: 'https://deck.taifoon.dev', description: 'The Taifoon deck that fronts the typed-decision lanes' },
|
|
13
|
-
{ displayName: 'Taifoon Principal Key', name: 'principalKey', type: 'string', typeOptions: { password: true }, default: '', required: true, description: 'Your tfn_live_ key. It identifies you, carries your licence and is what usage is billed to.' },
|
|
14
|
-
{ displayName: 'TypeSafe API Key (Optional)', name: 'typesafeKey', type: 'string', typeOptions: { password: true }, default: '', description: 'Your own key from console.typesafe.ai. Leave empty to use the free trial pings. Needs the jev licence on your Taifoon key.' },
|
|
15
|
-
];
|
|
16
|
-
this.authenticate = {
|
|
17
|
-
type: 'generic',
|
|
18
|
-
properties: { headers: { Cookie: '={{$credentials.sessionToken}}', 'x-typesafe-key': '={{$credentials.typesafeKey || undefined}}' } },
|
|
19
|
-
};
|
|
20
|
-
this.test = { request: { baseURL: '={{$credentials.deckUrl}}', url: '/api/typed', method: 'GET' } };
|
|
21
|
-
}
|
|
22
|
-
async preAuthentication(credentials) {
|
|
23
|
-
const base = String(credentials.deckUrl || 'https://deck.taifoon.dev').replace(/\/$/, '');
|
|
24
|
-
const res = (await this.helpers.httpRequest({ method: 'POST', url: `${base}/api/login`, body: { key: credentials.principalKey }, json: true, returnFullResponse: true }));
|
|
25
|
-
const raw = res.headers['set-cookie'];
|
|
26
|
-
const cookie = (Array.isArray(raw) ? raw : [String(raw !== null && raw !== void 0 ? raw : '')]).map((c) => String(c).split(';')[0]).filter(Boolean).join('; ');
|
|
27
|
-
return { sessionToken: cookie };
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
exports.TaifoonGatewayApi = TaifoonGatewayApi;
|
|
31
|
-
//# sourceMappingURL=TaifoonGatewayApi.credentials.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TaifoonGatewayApi.credentials.js","sourceRoot":"","sources":["../../credentials/TaifoonGatewayApi.credentials.ts"],"names":[],"mappings":";;;AAgBA,MAAa,iBAAiB;IAA9B;QACC,SAAI,GAAG,mBAAmB,CAAC;QAE3B,gBAAW,GAAG,qBAAqB,CAAC;QAEpC,SAAI,GAAG,kBAA2B,CAAC;QAEnC,qBAAgB,GAAG,0BAA0B,CAAC;QAE9C,eAAU,GAAsB;YAC/B,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YAC/H,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,0BAA0B,EAAE,WAAW,EAAE,uDAAuD,EAAE;YACvK,EAAE,WAAW,EAAE,uBAAuB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6FAA6F,EAAE;YACxP,EAAE,WAAW,EAAE,6BAA6B,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,4HAA4H,EAAE;SAC5Q,CAAC;QAUF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,4CAA4C,EAAE,EAAE;SACrI,CAAC;QAEF,SAAI,GAA2B,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;IACxH,CAAC;IAdA,KAAK,CAAC,iBAAiB,CAA2B,WAA2C;QAC5F,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1F,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,YAAY,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAA+D,CAAC;QACxO,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,aAAH,GAAG,cAAH,GAAG,GAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/H,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IACjC,CAAC;CAQD;AA9BD,8CA8BC"}
|