@cotal-ai/cli 0.17.0 → 0.18.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/cotal-skills/skills/team-topology/SKILL.md +1 -1
- package/dist/commands/agents.d.ts +10 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +57 -8
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/meshes-add.d.ts +17 -0
- package/dist/commands/meshes-add.d.ts.map +1 -1
- package/dist/commands/meshes-add.js +49 -4
- package/dist/commands/meshes-add.js.map +1 -1
- package/dist/commands/meshes-wizard.d.ts +3 -0
- package/dist/commands/meshes-wizard.d.ts.map +1 -1
- package/dist/commands/meshes-wizard.js +78 -6
- package/dist/commands/meshes-wizard.js.map +1 -1
- package/dist/commands/meshes.d.ts +4 -0
- package/dist/commands/meshes.d.ts.map +1 -1
- package/dist/commands/meshes.js +14 -4
- package/dist/commands/meshes.js.map +1 -1
- package/dist/commands/spawn.d.ts.map +1 -1
- package/dist/commands/spawn.js +1 -1
- package/dist/commands/spawn.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +46 -2
- package/dist/commands/up.js.map +1 -1
- package/dist/ext-loader.d.ts.map +1 -1
- package/dist/ext-loader.js +1 -0
- package/dist/ext-loader.js.map +1 -1
- package/dist/lib/control.d.ts +6 -1
- package/dist/lib/control.d.ts.map +1 -1
- package/dist/lib/control.js +7 -2
- package/dist/lib/control.js.map +1 -1
- package/dist/lib/join-target.d.ts +112 -0
- package/dist/lib/join-target.d.ts.map +1 -0
- package/dist/lib/join-target.js +130 -0
- package/dist/lib/join-target.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which broker addresses `cotal meshes add` may register.
|
|
3
|
+
*
|
|
4
|
+
* Registering a mesh is how a machine begins sending its agent credentials to a broker it does
|
|
5
|
+
* not run. NATS sends the initial INFO in plaintext and unauthenticated, so an on-path attacker
|
|
6
|
+
* can forge one that does not set `tls_required`, and a client that was never told to demand
|
|
7
|
+
* encryption puts its credentials in the CONNECT line for the attacker to read. The fence has to
|
|
8
|
+
* be client-side.
|
|
9
|
+
*
|
|
10
|
+
* The URL scheme is NOT that fence. Measured against `@nats-io/transport-node` 3.4.0 (the client
|
|
11
|
+
* this repo uses) pointed at a broker with no TLS configured at all:
|
|
12
|
+
*
|
|
13
|
+
* nats://host:port {} -> CONNECTED
|
|
14
|
+
* tls://host:port {} -> CONNECTED <- the scheme is cosmetic
|
|
15
|
+
* nats://host:port {tls:{}} -> REFUSED: server does not support 'tls'
|
|
16
|
+
*
|
|
17
|
+
* Only the explicit `tls` connect option makes the client demand TLS, and that option is the
|
|
18
|
+
* broker-TLS work's surface, not this one's. So this gates on the ADDRESS instead, in two steps.
|
|
19
|
+
* Step one, here: refuse public addresses, ordinary private ranges and every hostname outright,
|
|
20
|
+
* and refuse an overlay literal TOO unless the operator accepted it explicitly
|
|
21
|
+
* ({@link DialPolicy.allowUnencryptedOverlay}); accepted, it returns a {@link JoinTarget.residual}
|
|
22
|
+
* the caller prints and records as consent. Step two, once a record can carry TLS intent: the
|
|
23
|
+
* acceptance stops being needed because the transport is proven instead of promised.
|
|
24
|
+
*
|
|
25
|
+
* ONE deliberate limitation, stated rather than papered over: this classifies an ADDRESS, which
|
|
26
|
+
* is a guard against the obvious mistake (dialing a LAN or public address in the clear), not
|
|
27
|
+
* proof that the bytes are encrypted. An overlay address only rides an encrypted tunnel while the
|
|
28
|
+
* overlay is actually up on this machine; the same literal with the overlay down is just a CGNAT
|
|
29
|
+
* address.
|
|
30
|
+
*
|
|
31
|
+
* WHERE THIS GOES NEXT, so the next editor does not relax it for the wrong reason. Measured on
|
|
32
|
+
* this stack: the `tls` connect option verifies the certificate CHAIN AND THE HOSTNAME (Node
|
|
33
|
+
* defaults, `rejectUnauthorized: true`, servername from the URL), so a redirected name cannot
|
|
34
|
+
* complete the handshake and hostnames become safe — but only once something REQUIRES TLS. The
|
|
35
|
+
* relaxation is therefore a function of recorded strictness, not of time passing, and the agreed
|
|
36
|
+
* eventual shape is `classifyJoinTarget(url, { tlsRequired })` rather than a third verdict: one
|
|
37
|
+
* place keeps answering "may this machine send credentials to that address", with strictness as
|
|
38
|
+
* an input it cannot otherwise see. Two counterintuitive consequences worth keeping in view:
|
|
39
|
+
* the overlay ranges are the AWKWARD case under TLS (CGNAT space, no public CA will issue for it,
|
|
40
|
+
* verifying a literal needs an IP SAN, so it implies a private CA on every joiner), while the
|
|
41
|
+
* `<host>.ts.net` names refused below are the EASY case (publicly resolvable, publicly-trusted
|
|
42
|
+
* certs). Do not pre-relax: with nothing requiring TLS today, literals-only is the correct rule.
|
|
43
|
+
*/
|
|
44
|
+
/** The address class a permitted target belongs to. This is a REACHABILITY allowlist: it says the
|
|
45
|
+
* address is one we are willing to consider, never that the connection is protected. What
|
|
46
|
+
* protects it is {@link DialPolicy.tlsRequired}, except on loopback where nothing leaves the
|
|
47
|
+
* machine to protect. */
|
|
48
|
+
export type JoinReach =
|
|
49
|
+
/** A loopback literal: the bytes never leave the machine. */
|
|
50
|
+
"loopback"
|
|
51
|
+
/** A private-overlay literal. Permitted TODAY without required TLS, carrying a
|
|
52
|
+
* {@link JoinTarget.residual} that says so; it becomes conditional on TLS in step two. The
|
|
53
|
+
* address alone is not a guarantee - see {@link DialPolicy.tlsRequired}. */
|
|
54
|
+
| "overlay";
|
|
55
|
+
export interface JoinTarget {
|
|
56
|
+
/** The normalized dial URL, port defaulted. */
|
|
57
|
+
server: string;
|
|
58
|
+
reach: JoinReach;
|
|
59
|
+
/**
|
|
60
|
+
* Set when the target is permitted but its protection is NOT guaranteed, carrying the sentence
|
|
61
|
+
* the operator needs to read. Exactly one case produces it today: an overlay literal dialed
|
|
62
|
+
* without required TLS, where the address class is right but the tunnel being up is an
|
|
63
|
+
* assumption rather than a fact.
|
|
64
|
+
*
|
|
65
|
+
* It is a returned value rather than a throw because by the time it is produced the operator has
|
|
66
|
+
* ALREADY accepted the risk, so the remaining job is to carry the sentence: the caller prints it
|
|
67
|
+
* as a second notice and records it on the entry as evidence of consent. The refusal for someone
|
|
68
|
+
* who has NOT accepted happens earlier, and is a throw.
|
|
69
|
+
*/
|
|
70
|
+
residual?: string;
|
|
71
|
+
}
|
|
72
|
+
/** What the eventual connection will insist on, which the address alone cannot tell us. */
|
|
73
|
+
export interface DialPolicy {
|
|
74
|
+
/**
|
|
75
|
+
* Will the dial to this target REQUIRE TLS?
|
|
76
|
+
*
|
|
77
|
+
* This is the difference between an address and a guarantee, and getting it wrong was the
|
|
78
|
+
* defect this parameter exists to close. An overlay address is not proof the overlay transport
|
|
79
|
+
* is up: with the tunnel daemon stopped, `100.64.0.0/10` is ordinary CGNAT space, and hostile
|
|
80
|
+
* DHCP or routing can answer a dial to it. So the address class establishes only that we are
|
|
81
|
+
* willing to consider the target; requiring TLS is what makes it safe.
|
|
82
|
+
*
|
|
83
|
+
* There is no field on the mesh record to source this from yet — it arrives with the work that
|
|
84
|
+
* teaches the broker to serve TLS — so callers pass `false` today, and with it false an overlay
|
|
85
|
+
* literal is refused unless {@link DialPolicy.allowUnencryptedOverlay} says the operator
|
|
86
|
+
* accepted the tunnel dependency. When this can be `true` the acceptance stops being needed:
|
|
87
|
+
* the transport is then proven rather than promised, and the flag can go.
|
|
88
|
+
*/
|
|
89
|
+
tlsRequired: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Did the operator EXPLICITLY accept the overlay's tunnel dependency?
|
|
92
|
+
*
|
|
93
|
+
* The default is `false` and the default refuses, because the previous shape — permit and print
|
|
94
|
+
* a warning — is not a fence. A warning is written to stderr, which a non-interactive caller
|
|
95
|
+
* need not read, and it was not persisted, so nothing repeated it at the dials that followed. A
|
|
96
|
+
* scripted registration therefore got the risk with none of the notice.
|
|
97
|
+
*
|
|
98
|
+
* A flag is the one form of notice a script cannot miss: without it the command fails, and the
|
|
99
|
+
* failure is in the exit code rather than in prose nobody reads. The acceptance is then recorded
|
|
100
|
+
* on the mesh entry, so a later dial can tell that a human agreed to this rather than inferring
|
|
101
|
+
* consent from an address.
|
|
102
|
+
*/
|
|
103
|
+
allowUnencryptedOverlay: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Classify a registration target, or throw the reason it is refused.
|
|
107
|
+
*
|
|
108
|
+
* Refusal is the point: an unclassifiable target is one whose credentials would cross a network
|
|
109
|
+
* this build cannot encrypt, so it fails loud here rather than dialing and hoping.
|
|
110
|
+
*/
|
|
111
|
+
export declare function classifyJoinTarget(raw: string, policy: DialPolicy): JoinTarget;
|
|
112
|
+
//# sourceMappingURL=join-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"join-target.d.ts","sourceRoot":"","sources":["../../src/lib/join-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH;;;0BAG0B;AAC1B,MAAM,MAAM,SAAS;AACnB,6DAA6D;AAC3D,UAAU;AACZ;;6EAE6E;GAC3E,SAAS,CAAC;AAEd,MAAM,WAAW,UAAU;IACzB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,2FAA2F;AAC3F,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;;;;OAcG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAuCD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CA8C9E"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which broker addresses `cotal meshes add` may register.
|
|
3
|
+
*
|
|
4
|
+
* Registering a mesh is how a machine begins sending its agent credentials to a broker it does
|
|
5
|
+
* not run. NATS sends the initial INFO in plaintext and unauthenticated, so an on-path attacker
|
|
6
|
+
* can forge one that does not set `tls_required`, and a client that was never told to demand
|
|
7
|
+
* encryption puts its credentials in the CONNECT line for the attacker to read. The fence has to
|
|
8
|
+
* be client-side.
|
|
9
|
+
*
|
|
10
|
+
* The URL scheme is NOT that fence. Measured against `@nats-io/transport-node` 3.4.0 (the client
|
|
11
|
+
* this repo uses) pointed at a broker with no TLS configured at all:
|
|
12
|
+
*
|
|
13
|
+
* nats://host:port {} -> CONNECTED
|
|
14
|
+
* tls://host:port {} -> CONNECTED <- the scheme is cosmetic
|
|
15
|
+
* nats://host:port {tls:{}} -> REFUSED: server does not support 'tls'
|
|
16
|
+
*
|
|
17
|
+
* Only the explicit `tls` connect option makes the client demand TLS, and that option is the
|
|
18
|
+
* broker-TLS work's surface, not this one's. So this gates on the ADDRESS instead, in two steps.
|
|
19
|
+
* Step one, here: refuse public addresses, ordinary private ranges and every hostname outright,
|
|
20
|
+
* and refuse an overlay literal TOO unless the operator accepted it explicitly
|
|
21
|
+
* ({@link DialPolicy.allowUnencryptedOverlay}); accepted, it returns a {@link JoinTarget.residual}
|
|
22
|
+
* the caller prints and records as consent. Step two, once a record can carry TLS intent: the
|
|
23
|
+
* acceptance stops being needed because the transport is proven instead of promised.
|
|
24
|
+
*
|
|
25
|
+
* ONE deliberate limitation, stated rather than papered over: this classifies an ADDRESS, which
|
|
26
|
+
* is a guard against the obvious mistake (dialing a LAN or public address in the clear), not
|
|
27
|
+
* proof that the bytes are encrypted. An overlay address only rides an encrypted tunnel while the
|
|
28
|
+
* overlay is actually up on this machine; the same literal with the overlay down is just a CGNAT
|
|
29
|
+
* address.
|
|
30
|
+
*
|
|
31
|
+
* WHERE THIS GOES NEXT, so the next editor does not relax it for the wrong reason. Measured on
|
|
32
|
+
* this stack: the `tls` connect option verifies the certificate CHAIN AND THE HOSTNAME (Node
|
|
33
|
+
* defaults, `rejectUnauthorized: true`, servername from the URL), so a redirected name cannot
|
|
34
|
+
* complete the handshake and hostnames become safe — but only once something REQUIRES TLS. The
|
|
35
|
+
* relaxation is therefore a function of recorded strictness, not of time passing, and the agreed
|
|
36
|
+
* eventual shape is `classifyJoinTarget(url, { tlsRequired })` rather than a third verdict: one
|
|
37
|
+
* place keeps answering "may this machine send credentials to that address", with strictness as
|
|
38
|
+
* an input it cannot otherwise see. Two counterintuitive consequences worth keeping in view:
|
|
39
|
+
* the overlay ranges are the AWKWARD case under TLS (CGNAT space, no public CA will issue for it,
|
|
40
|
+
* verifying a literal needs an IP SAN, so it implies a private CA on every joiner), while the
|
|
41
|
+
* `<host>.ts.net` names refused below are the EASY case (publicly resolvable, publicly-trusted
|
|
42
|
+
* certs). Do not pre-relax: with nothing requiring TLS today, literals-only is the correct rule.
|
|
43
|
+
*/
|
|
44
|
+
/** The NATS default client port, used when the join URL omits one. */
|
|
45
|
+
const DEFAULT_PORT = 4222;
|
|
46
|
+
/** Loopback: `127.0.0.0/8` and `::1`. LITERALS ONLY, never `localhost`, because the whole point is
|
|
47
|
+
* a verdict that does not depend on a resolver an attacker could influence (a hosts-file entry or
|
|
48
|
+
* a poisoned lookup would otherwise turn "loopback" into any address at all). */
|
|
49
|
+
function isLoopbackLiteral(host) {
|
|
50
|
+
if (host === "::1" || host === "0:0:0:0:0:0:0:1")
|
|
51
|
+
return true;
|
|
52
|
+
const v4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
|
|
53
|
+
if (!v4)
|
|
54
|
+
return false;
|
|
55
|
+
const parts = v4.slice(1).map(Number);
|
|
56
|
+
if (parts.some((n) => n > 255))
|
|
57
|
+
return false;
|
|
58
|
+
return parts[0] === 127;
|
|
59
|
+
}
|
|
60
|
+
/** The private overlay's address space: `100.64.0.0/10` (the CGNAT range Tailscale assigns) and
|
|
61
|
+
* `fd7a:115c:a1e0::/48` (its ULA prefix). LITERALS ONLY, for the same reason as loopback: a
|
|
62
|
+
* MagicDNS name like `<host>.ts.net` is a resolver answer, and accepting it would let whoever
|
|
63
|
+
* answers that lookup redirect a plaintext credential-bearing dial anywhere they like. */
|
|
64
|
+
function isOverlayLiteral(host) {
|
|
65
|
+
const v4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
|
|
66
|
+
if (v4) {
|
|
67
|
+
const parts = v4.slice(1).map(Number);
|
|
68
|
+
if (parts.some((n) => n > 255))
|
|
69
|
+
return false;
|
|
70
|
+
// 100.64.0.0/10 == 100.64.0.0 through 100.127.255.255.
|
|
71
|
+
return parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127;
|
|
72
|
+
}
|
|
73
|
+
return /^fd7a:115c:a1e0:/i.test(host);
|
|
74
|
+
}
|
|
75
|
+
/** A URL's hostname with an IPv6 literal's brackets removed (`[::1]` -> `::1`). */
|
|
76
|
+
function bareHost(url) {
|
|
77
|
+
return url.hostname.startsWith("[") && url.hostname.endsWith("]")
|
|
78
|
+
? url.hostname.slice(1, -1)
|
|
79
|
+
: url.hostname;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Classify a registration target, or throw the reason it is refused.
|
|
83
|
+
*
|
|
84
|
+
* Refusal is the point: an unclassifiable target is one whose credentials would cross a network
|
|
85
|
+
* this build cannot encrypt, so it fails loud here rather than dialing and hoping.
|
|
86
|
+
*/
|
|
87
|
+
export function classifyJoinTarget(raw, policy) {
|
|
88
|
+
let url;
|
|
89
|
+
try {
|
|
90
|
+
url = new URL(raw);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
throw new Error(`${JSON.stringify(raw)} is not a URL - pass a broker address like nats://100.64.0.1:4222`);
|
|
94
|
+
}
|
|
95
|
+
if (url.protocol !== "nats:" && url.protocol !== "tls:")
|
|
96
|
+
throw new Error(`${JSON.stringify(raw)} must be a nats:// or tls:// URL, not ${url.protocol}//`);
|
|
97
|
+
const host = bareHost(url);
|
|
98
|
+
if (!host)
|
|
99
|
+
throw new Error(`${JSON.stringify(raw)} has no host`);
|
|
100
|
+
const port = url.port || String(DEFAULT_PORT);
|
|
101
|
+
const server = `${url.protocol}//${url.hostname}:${port}`;
|
|
102
|
+
// Loopback is the one class that needs no transport guarantee: nothing leaves the machine, so
|
|
103
|
+
// there is nothing on a wire for anyone to sit on.
|
|
104
|
+
if (isLoopbackLiteral(host))
|
|
105
|
+
return { server, reach: "loopback" };
|
|
106
|
+
if (isOverlayLiteral(host)) {
|
|
107
|
+
if (policy.tlsRequired)
|
|
108
|
+
return { server, reach: "overlay" };
|
|
109
|
+
if (!policy.allowUnencryptedOverlay)
|
|
110
|
+
throw new Error(`${JSON.stringify(raw)} refused: ${host} is a private-overlay address, and this build cannot require TLS on the connection.\n` +
|
|
111
|
+
` That is safe while the overlay tunnel is up, and NOT safe if it is down: the range is then ordinary carrier-grade\n` +
|
|
112
|
+
` NAT, and whoever answers the dial receives the credentials this machine sends.\n` +
|
|
113
|
+
` Only you can know whether the tunnel is up, so accept it explicitly with --allow-unencrypted-overlay, which records\n` +
|
|
114
|
+
` your acceptance on the mesh entry. The flag goes away once the broker can be served over TLS.`);
|
|
115
|
+
return {
|
|
116
|
+
server,
|
|
117
|
+
reach: "overlay",
|
|
118
|
+
residual: `${host} is a private-overlay address, and this build cannot yet require TLS on the connection.\n` +
|
|
119
|
+
` That is safe while the overlay tunnel is actually up: it authenticates both machines and encrypts the link.\n` +
|
|
120
|
+
` It is NOT safe if the tunnel is down. That range is then ordinary carrier-grade NAT space, and whoever\n` +
|
|
121
|
+
` answers the dial receives the credentials this machine will send. Keep the tunnel up, and expect this to\n` +
|
|
122
|
+
` become a refusal once the broker can be served over TLS and the record can say TLS is required.`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`${JSON.stringify(raw)} refused: this build cannot protect a connection to ${host}, and a machine that registers a mesh sends its agent credentials to that broker.\n` +
|
|
126
|
+
` Only a loopback literal (127.0.0.0/8, ::1) or a private-overlay literal (100.64.0.0/10, fd7a:115c:a1e0::/48) may be registered. An overlay literal additionally needs --allow-unencrypted-overlay, which records that you accepted its tunnel dependency.\n` +
|
|
127
|
+
` Ordinary private ranges are refused too: a cafe network is private, and private is not the same as yours.\n` +
|
|
128
|
+
` A hostname is refused even when it resolves somewhere permitted - otherwise whoever answers the lookup chooses which machine receives the credentials. Pass the address itself.`);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=join-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"join-target.js","sourceRoot":"","sources":["../../src/lib/join-target.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/cli",
|
|
3
3
|
"description": "Cotal mesh CLI: up, join, watch, console, spawn, mint, channels, history, ext.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"yaml": "^2.9.0",
|
|
29
29
|
"zod": "^4.4.3",
|
|
30
30
|
"ws": "^8.21.0",
|
|
31
|
-
"@cotal-ai/core": "0.
|
|
32
|
-
"@cotal-ai/workspace": "0.
|
|
31
|
+
"@cotal-ai/core": "0.18.0",
|
|
32
|
+
"@cotal-ai/workspace": "0.18.0"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
35
|
"@eplightning/nats-server-darwin-arm64": "^2.14.0",
|