ftown 0.19.52 → 0.19.53
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/claude-mod/hooks/register.js +6 -5
- package/dist/bridge-auth.d.ts +1 -0
- package/dist/bridge-auth.js +2 -2
- package/dist/bridge-auth.js.map +1 -1
- package/dist/centrifugo-client.d.ts +11 -1
- package/dist/centrifugo-client.js +71 -69
- package/dist/centrifugo-client.js.map +1 -1
- package/dist/cli-client.d.ts +6 -0
- package/dist/cli-client.js +43 -18
- package/dist/cli-client.js.map +1 -1
- package/dist/command-rpc.d.ts +5 -2
- package/dist/command-rpc.js +41 -4
- package/dist/command-rpc.js.map +1 -1
- package/dist/direct-transport/loopback-server.d.ts +5 -1
- package/dist/direct-transport/loopback-server.js +19 -6
- package/dist/direct-transport/loopback-server.js.map +1 -1
- package/dist/direct-transport/peer-manager.d.ts +4 -0
- package/dist/direct-transport/peer-manager.js +16 -2
- package/dist/direct-transport/peer-manager.js.map +1 -1
- package/dist/ftown-cli.js +4 -1
- package/dist/ftown-cli.js.map +1 -1
- package/dist/index.js +54 -37
- package/dist/index.js.map +1 -1
- package/dist/local-api-server.d.ts +12 -0
- package/dist/local-api-server.js +182 -41
- package/dist/local-api-server.js.map +1 -1
- package/dist/local-pair-cli.d.ts +2 -3
- package/dist/local-pair-cli.js +12 -53
- package/dist/local-pair-cli.js.map +1 -1
- package/dist/mcp/fleet.d.ts +11 -9
- package/dist/mcp/fleet.js +61 -44
- package/dist/mcp/fleet.js.map +1 -1
- package/dist/mcp/http.d.ts +10 -9
- package/dist/mcp/http.js +133 -8
- package/dist/mcp/http.js.map +1 -1
- package/dist/mcp/legacy-relay.d.ts +2 -2
- package/dist/mcp/legacy-relay.js +4 -43
- package/dist/mcp/legacy-relay.js.map +1 -1
- package/dist/mcp/mcp-key-store.d.ts +52 -0
- package/dist/mcp/mcp-key-store.js +206 -0
- package/dist/mcp/mcp-key-store.js.map +1 -0
- package/dist/mcp/oauth.d.ts +5 -1
- package/dist/mcp/oauth.js +113 -39
- package/dist/mcp/oauth.js.map +1 -1
- package/dist/mcp/relay-handler.d.ts +2 -1
- package/dist/mcp/relay-handler.js +5 -40
- package/dist/mcp/relay-handler.js.map +1 -1
- package/dist/mcp/relay.d.ts +4 -4
- package/dist/mcp/relay.js +35 -85
- package/dist/mcp/relay.js.map +1 -1
- package/dist/mcp/secure-client.d.ts +23 -0
- package/dist/mcp/secure-client.js +124 -0
- package/dist/mcp/secure-client.js.map +1 -0
- package/dist/mcp/security-events.d.ts +65 -0
- package/dist/mcp/security-events.js +210 -0
- package/dist/mcp/security-events.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/test-support.d.ts +32 -0
- package/dist/mcp/test-support.js +120 -0
- package/dist/mcp/test-support.js.map +1 -0
- package/dist/mcp-cli.js +56 -36
- package/dist/mcp-cli.js.map +1 -1
- package/dist/pairing-client.d.ts +1 -0
- package/dist/pairing-client.js +2 -2
- package/dist/pairing-client.js.map +1 -1
- package/dist/push-notifier.js +3 -1
- package/dist/push-notifier.js.map +1 -1
- package/dist/security/bridge/audit.d.ts +37 -0
- package/dist/security/bridge/audit.js +185 -0
- package/dist/security/bridge/audit.js.map +1 -0
- package/dist/security/bridge/direct.d.ts +22 -0
- package/dist/security/bridge/direct.js +72 -0
- package/dist/security/bridge/direct.js.map +1 -0
- package/dist/security/bridge/errors.d.ts +7 -0
- package/dist/security/bridge/errors.js +12 -0
- package/dist/security/bridge/errors.js.map +1 -0
- package/dist/security/bridge/execute.d.ts +3 -0
- package/dist/security/bridge/execute.js +37 -0
- package/dist/security/bridge/execute.js.map +1 -0
- package/dist/security/bridge/identity.d.ts +5 -0
- package/dist/security/bridge/identity.js +42 -0
- package/dist/security/bridge/identity.js.map +1 -0
- package/dist/security/bridge/local-client.d.ts +4 -0
- package/dist/security/bridge/local-client.js +43 -0
- package/dist/security/bridge/local-client.js.map +1 -0
- package/dist/security/bridge/metadata.d.ts +99 -0
- package/dist/security/bridge/metadata.js +23 -0
- package/dist/security/bridge/metadata.js.map +1 -0
- package/dist/security/bridge/native-wake.d.ts +16 -0
- package/dist/security/bridge/native-wake.js +15 -0
- package/dist/security/bridge/native-wake.js.map +1 -0
- package/dist/security/bridge/permissions.d.ts +11 -0
- package/dist/security/bridge/permissions.js +97 -0
- package/dist/security/bridge/permissions.js.map +1 -0
- package/dist/security/bridge/policy.d.ts +35 -0
- package/dist/security/bridge/policy.js +263 -0
- package/dist/security/bridge/policy.js.map +1 -0
- package/dist/security/bridge/security.d.ts +50 -0
- package/dist/security/bridge/security.js +298 -0
- package/dist/security/bridge/security.js.map +1 -0
- package/dist/security/bridge/snapshot.d.ts +14 -0
- package/dist/security/bridge/snapshot.js +77 -0
- package/dist/security/bridge/snapshot.js.map +1 -0
- package/dist/security/crypto.d.ts +17 -0
- package/dist/security/crypto.js +280 -0
- package/dist/security/crypto.js.map +1 -0
- package/dist/security/protocol.d.ts +389 -0
- package/dist/security/protocol.js +9 -0
- package/dist/security/protocol.js.map +1 -0
- package/dist/security-cli.d.ts +3 -0
- package/dist/security-cli.js +62 -0
- package/dist/security-cli.js.map +1 -0
- package/dist/solo/solo-server.js +24 -2
- package/dist/solo/solo-server.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/wire-types.d.ts +3 -0
- package/dist/wire-types.js.map +1 -1
- package/hooks/notify.sh +1 -1
- package/muse-plugin/lib/ftown-hook.sh +6 -22
- package/opencode-plugin/ftown.js +33 -5
- package/package.json +4 -2
- package/pi-extension/ftown.js +33 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-handler.js","sourceRoot":"","sources":["../../src/mcp/relay-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"relay-handler.js","sourceRoot":"","sources":["../../src/mcp/relay-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,0GAA0G;AAC1G,MAAM,UAAU,kBAAkB,CAAC,KAAa,EAAE,MAAc;IAC9D,OAAO,KAAK,EAAE,GAAW,EAAE,QAAiC,EAAkB,EAAE;QAC9E,MAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,yCAAyC,CAAC,CAAC;IACtF,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/mcp/relay.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { type BridgeRequest } from "./fleet.js";
|
|
2
|
-
|
|
2
|
+
import { type McpRequestContext } from "./secure-client.js";
|
|
3
|
+
/** Renewable routing connections carry ciphertext only; identities stay request-bound. */
|
|
3
4
|
export declare class FleetRelay {
|
|
4
5
|
private url;
|
|
5
6
|
private tokenSecret;
|
|
6
7
|
private clients;
|
|
7
8
|
constructor(url: string, tokenSecret: string);
|
|
8
9
|
private connection;
|
|
9
|
-
request(userId: string, machineId: string, request: BridgeRequest, signal?: AbortSignal): Promise<
|
|
10
|
-
private
|
|
11
|
-
private rpc;
|
|
10
|
+
request(userId: string, machineId: string, request: BridgeRequest, signal?: AbortSignal, context?: McpRequestContext): Promise<unknown>;
|
|
11
|
+
private exchange;
|
|
12
12
|
close(): void;
|
|
13
13
|
}
|
package/dist/mcp/relay.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { Centrifuge } from "centrifuge";
|
|
3
2
|
import WebSocket from "ws";
|
|
4
3
|
import jwt from "jsonwebtoken";
|
|
5
4
|
import { FleetError, requestSchema } from "./fleet.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import { parseSecureEnvelope } from "../security/crypto.js";
|
|
6
|
+
import { SecureMcpClient } from "./secure-client.js";
|
|
7
|
+
/** Renewable routing connections carry ciphertext only; identities stay request-bound. */
|
|
8
8
|
export class FleetRelay {
|
|
9
9
|
url;
|
|
10
10
|
tokenSecret;
|
|
@@ -17,25 +17,18 @@ export class FleetRelay {
|
|
|
17
17
|
const cached = this.clients.get(userId);
|
|
18
18
|
if (cached)
|
|
19
19
|
return cached;
|
|
20
|
-
const mint = () => jwt.sign({}, this.tokenSecret, {
|
|
21
|
-
|
|
22
|
-
audience: "ftown:centrifugo",
|
|
23
|
-
expiresIn: "10m",
|
|
24
|
-
});
|
|
25
|
-
const client = new Centrifuge(this.url, {
|
|
26
|
-
websocket: WebSocket,
|
|
27
|
-
token: mint(),
|
|
28
|
-
getToken: async () => mint(),
|
|
29
|
-
timeout: 10000,
|
|
30
|
-
});
|
|
20
|
+
const mint = () => jwt.sign({}, this.tokenSecret, { subject: userId, audience: "ftown:centrifugo", expiresIn: "10m" });
|
|
21
|
+
const client = new Centrifuge(this.url, { websocket: WebSocket, token: mint(), getToken: async () => mint(), timeout: 10000 });
|
|
31
22
|
const sub = client.newSubscription(`commands:rpc#${userId}`);
|
|
32
23
|
const pending = new Map();
|
|
33
|
-
sub.on("publication",
|
|
34
|
-
|
|
35
|
-
if (ctx.info?.user !== userId || ctx.data?.type !== "command_response")
|
|
24
|
+
sub.on("publication", ctx => {
|
|
25
|
+
if (ctx.info?.user !== userId)
|
|
36
26
|
return;
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
try {
|
|
28
|
+
const packet = parseSecureEnvelope(ctx.data);
|
|
29
|
+
pending.get(packet.messageId)?.accept(packet);
|
|
30
|
+
}
|
|
31
|
+
catch { /* Plaintext/unknown/oversized traffic never enters the application. */ }
|
|
39
32
|
});
|
|
40
33
|
sub.subscribe();
|
|
41
34
|
client.connect();
|
|
@@ -43,60 +36,21 @@ export class FleetRelay {
|
|
|
43
36
|
this.clients.set(userId, entry);
|
|
44
37
|
return entry;
|
|
45
38
|
}
|
|
46
|
-
async request(userId, machineId, request, signal) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
request, expiresAt: deadline,
|
|
52
|
-
}, deadline, mutation, signal);
|
|
53
|
-
if (response.success)
|
|
54
|
-
return this.unwrap(response.data);
|
|
55
|
-
// Only this rejection proves the operation was not executed. Never fall
|
|
56
|
-
// back after a timeout, connection failure or ambiguous execution error.
|
|
57
|
-
if (response.error !== "Unknown command type: mcp_request")
|
|
58
|
-
throw new FleetError("bridge_error", "Bridge could not execute the relay request", mutation);
|
|
59
|
-
const lifecycle = request.method === "POST"
|
|
60
|
-
? /^\/api\/sessions\/([a-zA-Z0-9_-]+)\/(stop|retry)$/.exec(request.path) : null;
|
|
61
|
-
const type = lifecycle ? `${lifecycle[2]}_session` : "bridge_exec";
|
|
62
|
-
const payload = lifecycle ? { sessionId: lifecycle[1] } : {
|
|
63
|
-
command: legacyRelayCommand(machineId, request, deadline),
|
|
64
|
-
timeout: Math.max(1, deadline - Date.now()),
|
|
65
|
-
};
|
|
66
|
-
const legacy = await this.rpc(userId, machineId, type, payload, deadline, mutation, signal);
|
|
67
|
-
if (!legacy.success) {
|
|
68
|
-
if (legacy.error === "bridge_exec disabled (FTOWN_DISABLE_BRIDGE_EXEC=1)")
|
|
69
|
-
throw new FleetError("bridge_upgrade_required", "Upgrade this bridge to enable native MCP support; legacy execution is disabled");
|
|
70
|
-
throw new FleetError("bridge_error", "Bridge rejected the compatibility request", mutation);
|
|
71
|
-
}
|
|
72
|
-
if (lifecycle)
|
|
73
|
-
return legacy.data;
|
|
74
|
-
if (legacy.data?.exitCode !== 0)
|
|
75
|
-
throw new FleetError("bridge_error", "Legacy bridge helper failed; check Node availability or upgrade this bridge", mutation);
|
|
76
|
-
let result;
|
|
77
|
-
try {
|
|
78
|
-
result = JSON.parse(legacy.data.stdout);
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
throw new FleetError("bridge_error", "Legacy bridge returned an invalid result", mutation);
|
|
82
|
-
}
|
|
83
|
-
return this.unwrap(result);
|
|
84
|
-
}
|
|
85
|
-
unwrap(result) {
|
|
86
|
-
if (result?.error)
|
|
87
|
-
throw new FleetError(result.error.code, result.error.message, result.error.outcomeUnknown, result.error.status);
|
|
88
|
-
return result?.data;
|
|
39
|
+
async request(userId, machineId, request, signal, context) {
|
|
40
|
+
if (!context)
|
|
41
|
+
throw new FleetError("approval_required", "An encrypted MCP grant is required");
|
|
42
|
+
const client = new SecureMcpClient(context, machineId, `commands:rpc#${userId}`, (packet, deadline, mutating, abort) => this.exchange(userId, packet, deadline, mutating, abort));
|
|
43
|
+
return client.request(requestSchema.parse(request), signal);
|
|
89
44
|
}
|
|
90
|
-
async
|
|
45
|
+
async exchange(userId, packet, deadline, mutating, signal) {
|
|
91
46
|
if (Date.now() >= deadline)
|
|
92
|
-
throw new FleetError("timeout", "
|
|
47
|
+
throw new FleetError("timeout", "Encrypted request expired before publication");
|
|
93
48
|
if (signal?.aborted)
|
|
94
|
-
throw new FleetError("cancelled", "
|
|
49
|
+
throw new FleetError("cancelled", "Encrypted request cancelled");
|
|
95
50
|
const { sub, pending } = this.connection(userId);
|
|
96
|
-
const id =
|
|
51
|
+
const id = packet.messageId;
|
|
97
52
|
return new Promise((resolve, reject) => {
|
|
98
|
-
let sent = false;
|
|
99
|
-
let settled = false;
|
|
53
|
+
let sent = false, settled = false;
|
|
100
54
|
const finish = (error, value) => {
|
|
101
55
|
if (settled)
|
|
102
56
|
return;
|
|
@@ -106,37 +60,33 @@ export class FleetRelay {
|
|
|
106
60
|
signal?.removeEventListener("abort", cancel);
|
|
107
61
|
if (error)
|
|
108
62
|
reject(error);
|
|
109
|
-
else
|
|
63
|
+
else if (value)
|
|
110
64
|
resolve(value);
|
|
111
65
|
};
|
|
112
|
-
const cancel = () => finish(new FleetError("cancelled", "
|
|
113
|
-
const timer = setTimeout(() => finish(new FleetError("timeout", "
|
|
66
|
+
const cancel = () => finish(new FleetError("cancelled", "Encrypted relay request cancelled", sent && mutating));
|
|
67
|
+
const timer = setTimeout(() => finish(new FleetError("timeout", "Encrypted bridge did not respond before the deadline", sent && mutating)), Math.max(1, deadline - Date.now()));
|
|
114
68
|
signal?.addEventListener("abort", cancel, { once: true });
|
|
115
69
|
if (signal?.aborted) {
|
|
116
70
|
cancel();
|
|
117
71
|
return;
|
|
118
72
|
}
|
|
119
|
-
pending.set(id,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
73
|
+
pending.set(id, { accept: value => {
|
|
74
|
+
// Other concurrent gateways/endpoints can publish the same visible ID; only accept the exact route here.
|
|
75
|
+
if (value.senderId === packet.recipientId && value.recipientId === packet.senderId && value.ownerId === packet.ownerId && value.bridgeId === packet.bridgeId && value.channel === packet.channel)
|
|
76
|
+
finish(undefined, value);
|
|
77
|
+
}, reject: () => finish(new FleetError("connection_error", "Encrypted relay is closing", sent && mutating)) });
|
|
78
|
+
void sub.ready(Math.max(1, deadline - Date.now())).then(async () => {
|
|
123
79
|
if (settled)
|
|
124
80
|
return;
|
|
125
|
-
// No retries: publication failure may still mean the request was delivered.
|
|
126
81
|
sent = true;
|
|
127
|
-
await sub.publish(
|
|
128
|
-
|
|
129
|
-
requestId: id,
|
|
130
|
-
payload: { ...payload, bridgeId: machineId },
|
|
131
|
-
});
|
|
132
|
-
})
|
|
133
|
-
.catch(() => finish(new FleetError("connection_error", "Relay connection unavailable", sent && mutation)));
|
|
82
|
+
await sub.publish(packet);
|
|
83
|
+
}).catch(() => finish(new FleetError("connection_error", "Encrypted relay connection unavailable", sent && mutating)));
|
|
134
84
|
});
|
|
135
85
|
}
|
|
136
86
|
close() {
|
|
137
87
|
for (const entry of this.clients.values()) {
|
|
138
|
-
for (const
|
|
139
|
-
|
|
88
|
+
for (const pending of [...entry.pending.values()])
|
|
89
|
+
pending.reject();
|
|
140
90
|
entry.client.disconnect();
|
|
141
91
|
}
|
|
142
92
|
this.clients.clear();
|
package/dist/mcp/relay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay.js","sourceRoot":"","sources":["../../src/mcp/relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"relay.js","sourceRoot":"","sources":["../../src/mcp/relay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { McpActionContext, SecureEnvelope } from '../security/protocol.js';
|
|
2
|
+
import type { McpGrantIdentity } from './mcp-key-store.js';
|
|
3
|
+
import { type BridgeRequest } from './fleet.js';
|
|
4
|
+
export interface McpRequestContext {
|
|
5
|
+
grant: McpGrantIdentity;
|
|
6
|
+
action: McpActionContext;
|
|
7
|
+
revalidate?: () => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export type SecureExchange = (envelope: SecureEnvelope, deadline: number, mutating: boolean, signal?: AbortSignal) => Promise<SecureEnvelope>;
|
|
10
|
+
/** Both endpoint adapters use the same semantic allowlist. */
|
|
11
|
+
export declare function isMcpMutation(request: Pick<BridgeRequest, 'method' | 'path'>): boolean;
|
|
12
|
+
/** An immutable grant-bound endpoint; no state is shared across callers or grants. */
|
|
13
|
+
export declare class SecureMcpClient {
|
|
14
|
+
private context;
|
|
15
|
+
private bridgeId;
|
|
16
|
+
private channel;
|
|
17
|
+
private exchange;
|
|
18
|
+
constructor(context: McpRequestContext, bridgeId: string, channel: string, exchange: SecureExchange);
|
|
19
|
+
private pin;
|
|
20
|
+
private header;
|
|
21
|
+
private send;
|
|
22
|
+
request(request: BridgeRequest, signal?: AbortSignal): Promise<unknown>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { seal, open, SecurityError } from '../security/crypto.js';
|
|
4
|
+
import { FleetError } from './fleet.js';
|
|
5
|
+
import { classifyHttp } from '../security/bridge/permissions.js';
|
|
6
|
+
/** Both endpoint adapters use the same semantic allowlist. */
|
|
7
|
+
export function isMcpMutation(request) {
|
|
8
|
+
try {
|
|
9
|
+
return classifyHttp(request.method, request.path).mutating;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new FleetError('permission_denied', 'MCP request is outside the bridge semantic allowlist');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const responseSchema = z.object({ type: z.literal('application'), route: z.object({ kind: z.literal('response'), requestId: z.string() }).strict(), data: z.unknown(), mcp: z.unknown().nullable() }).strict();
|
|
16
|
+
const welcomeSchema = z.object({ type: z.literal('welcome'), challenge: z.string(), receiverEpoch: z.string().regex(/^[A-Za-z0-9_-]{1,128}$/) }).strict();
|
|
17
|
+
function json(bytes) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new SecurityError('invalid_envelope', 'Bridge returned an invalid encrypted response');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function applicationJson(value, depth = 0) {
|
|
26
|
+
if (depth > 32)
|
|
27
|
+
throw new FleetError('invalid_request', 'MCP request nesting exceeds the encrypted transport limit');
|
|
28
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'string')
|
|
29
|
+
return value;
|
|
30
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
31
|
+
return value;
|
|
32
|
+
if (Array.isArray(value))
|
|
33
|
+
return value.map(item => applicationJson(item, depth + 1));
|
|
34
|
+
if (typeof value === 'object' && value !== null)
|
|
35
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined).map(([key, item]) => [key, applicationJson(item, depth + 1)]));
|
|
36
|
+
throw new FleetError('invalid_request', 'MCP request must contain JSON values');
|
|
37
|
+
}
|
|
38
|
+
/** An immutable grant-bound endpoint; no state is shared across callers or grants. */
|
|
39
|
+
export class SecureMcpClient {
|
|
40
|
+
context;
|
|
41
|
+
bridgeId;
|
|
42
|
+
channel;
|
|
43
|
+
exchange;
|
|
44
|
+
constructor(context, bridgeId, channel, exchange) {
|
|
45
|
+
this.context = context;
|
|
46
|
+
this.bridgeId = bridgeId;
|
|
47
|
+
this.channel = channel;
|
|
48
|
+
this.exchange = exchange;
|
|
49
|
+
}
|
|
50
|
+
pin() {
|
|
51
|
+
const pin = this.context.grant.bridges.find(candidate => candidate.bridgeId === this.bridgeId);
|
|
52
|
+
if (!pin || pin.endpoint.ownerId !== this.context.grant.ownerId || !pin.permissions.includes('read'))
|
|
53
|
+
throw new FleetError('approval_required', 'Bridge encryption approval is required; configure machine.id as the actual bridgeId and use label for aliases');
|
|
54
|
+
if (Date.parse(this.context.grant.activeExpiresAt) <= Date.now())
|
|
55
|
+
throw new FleetError('expired', 'Bridge encryption approval has expired');
|
|
56
|
+
return pin;
|
|
57
|
+
}
|
|
58
|
+
header(id, purpose, epoch, deadline) {
|
|
59
|
+
return {
|
|
60
|
+
type: 'ftown_encrypted', version: 1, senderId: this.context.grant.identity.endpoint.id,
|
|
61
|
+
recipientId: this.pin().endpoint.id, ownerId: this.context.grant.ownerId, bridgeId: this.bridgeId,
|
|
62
|
+
channel: this.channel, purpose, messageId: id, createdAt: Date.now(), expiresAt: Math.min(deadline, Date.now() + 120_000), receiverEpoch: epoch,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async send(header, value, mutating, deadline, signal) {
|
|
66
|
+
if (signal?.aborted || Date.now() >= deadline)
|
|
67
|
+
throw new FleetError('cancelled', 'Encrypted request cancelled before dispatch');
|
|
68
|
+
const packet = await seal(this.context.grant.identity, this.pin().endpoint, header, new TextEncoder().encode(JSON.stringify(value)));
|
|
69
|
+
const reply = await this.exchange(packet, deadline, mutating, signal);
|
|
70
|
+
if (reply.messageId !== header.messageId)
|
|
71
|
+
throw new FleetError('bridge_error', 'Bridge response correlation failed', mutating);
|
|
72
|
+
const expected = { ownerId: this.context.grant.ownerId, bridgeId: this.bridgeId, channel: this.channel, recipientId: this.context.grant.identity.endpoint.id, purpose: header.purpose === 'hello' ? 'welcome' : 'application' };
|
|
73
|
+
try {
|
|
74
|
+
return json(await open(this.context.grant.identity, this.pin().endpoint, reply, expected));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
throw new FleetError('authentication_failed', 'Bridge encrypted response authentication failed', mutating);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async request(request, signal) {
|
|
81
|
+
await this.context.revalidate?.();
|
|
82
|
+
const deadline = Date.now() + request.timeoutMs;
|
|
83
|
+
const pin = this.pin();
|
|
84
|
+
const mutating = isMcpMutation(request);
|
|
85
|
+
if (mutating && (!pin.permissions.includes('control') || !this.context.grant.permissions.includes('control')))
|
|
86
|
+
throw new FleetError('permission_denied', 'This MCP grant cannot control the bridge');
|
|
87
|
+
const challenge = randomBytes(32).toString('base64url');
|
|
88
|
+
const hello = await this.send(this.header(randomUUID(), 'hello', null, deadline), { type: 'hello', challenge }, false, deadline, signal);
|
|
89
|
+
const welcome = welcomeSchema.safeParse(hello);
|
|
90
|
+
if (!welcome.success || welcome.data.challenge !== challenge)
|
|
91
|
+
throw new FleetError('authentication_failed', 'Bridge encryption handshake failed');
|
|
92
|
+
const activationId = randomUUID();
|
|
93
|
+
const activation = {
|
|
94
|
+
type: 'application', route: { kind: 'command', command: 'security_activate_mcp', requestId: activationId, sessionId: null },
|
|
95
|
+
data: { type: 'security_activate_mcp', requestId: activationId, payload: { bridgeId: this.bridgeId, grantId: this.context.grant.grantId } }, mcp: this.context.action,
|
|
96
|
+
};
|
|
97
|
+
const activated = responseSchema.safeParse(await this.send(this.header(activationId, 'application', welcome.data.receiverEpoch, deadline), activation, false, deadline, signal));
|
|
98
|
+
if (!activated.success || activated.data.route.requestId !== activationId)
|
|
99
|
+
throw new FleetError('bridge_error', 'Bridge activation response is invalid');
|
|
100
|
+
const activationResponse = z.object({ type: z.literal('command_response'), response: z.object({ requestId: z.literal(activationId), success: z.boolean(), error: z.string().optional(), data: z.unknown().optional() }) }).safeParse(activated.data.data);
|
|
101
|
+
if (!activationResponse.success || !activationResponse.data.response.success)
|
|
102
|
+
throw new FleetError('approval_required', 'Bridge MCP approval could not be activated');
|
|
103
|
+
await this.context.revalidate?.();
|
|
104
|
+
const requestId = randomUUID();
|
|
105
|
+
const application = {
|
|
106
|
+
type: 'application', route: { kind: 'http', requestId, method: request.method, path: request.path },
|
|
107
|
+
data: applicationJson({ timeoutMs: Math.max(1000, deadline - Date.now()), ...(request.body === undefined ? {} : { body: request.body }) }), mcp: this.context.action,
|
|
108
|
+
};
|
|
109
|
+
const result = responseSchema.safeParse(await this.send(this.header(requestId, 'application', welcome.data.receiverEpoch, deadline), application, mutating, deadline, signal));
|
|
110
|
+
if (!result.success || result.data.route.requestId !== requestId)
|
|
111
|
+
throw new FleetError('bridge_error', 'Bridge response is invalid', mutating);
|
|
112
|
+
const http = z.object({ status: z.number().int().min(100).max(599), body: z.unknown() }).strict().safeParse(result.data.data);
|
|
113
|
+
if (!http.success)
|
|
114
|
+
throw new FleetError('bridge_error', 'Bridge HTTP response is invalid', mutating);
|
|
115
|
+
if (http.data.status < 200 || http.data.status >= 300) {
|
|
116
|
+
const error = z.object({ code: z.string().optional(), outcomeUnknown: z.boolean().optional() }).passthrough().safeParse(http.data.body);
|
|
117
|
+
const allowed = ['unknown_session', 'invalid_cursor', 'unknown_attention', 'permission_denied', 'endpoint_revoked', 'upgrade_required', 'audit_unavailable', 'outcome_unknown'];
|
|
118
|
+
const code = error.success && error.data.code && allowed.includes(error.data.code) ? error.data.code : 'bridge_error';
|
|
119
|
+
throw new FleetError(code, 'Bridge rejected the encrypted request', mutating && (http.data.status >= 500 || (error.success && error.data.outcomeUnknown === true)), http.data.status);
|
|
120
|
+
}
|
|
121
|
+
return http.data.body;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=secure-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-client.js","sourceRoot":"","sources":["../../src/mcp/secure-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Pool } from 'pg';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { SecurityEvent, SecurityEventStatus, SecurityPage, SecurityPageQuery } from '../security/protocol.js';
|
|
4
|
+
export declare const KNOWN_MCP_TOOLS: Set<string>;
|
|
5
|
+
export declare const READ_MCP_TOOLS: Set<string>;
|
|
6
|
+
declare const cursorSchema: z.ZodObject<{
|
|
7
|
+
version: z.ZodLiteral<1>;
|
|
8
|
+
ownerId: z.ZodString;
|
|
9
|
+
bridgeId: z.ZodNullable<z.ZodString>;
|
|
10
|
+
afterAt: z.ZodString;
|
|
11
|
+
afterId: z.ZodString;
|
|
12
|
+
upperAt: z.ZodString;
|
|
13
|
+
upperId: z.ZodString;
|
|
14
|
+
}, "strict", z.ZodTypeAny, {
|
|
15
|
+
version: 1;
|
|
16
|
+
bridgeId: string | null;
|
|
17
|
+
ownerId: string;
|
|
18
|
+
afterAt: string;
|
|
19
|
+
afterId: string;
|
|
20
|
+
upperAt: string;
|
|
21
|
+
upperId: string;
|
|
22
|
+
}, {
|
|
23
|
+
version: 1;
|
|
24
|
+
bridgeId: string | null;
|
|
25
|
+
ownerId: string;
|
|
26
|
+
afterAt: string;
|
|
27
|
+
afterId: string;
|
|
28
|
+
upperAt: string;
|
|
29
|
+
upperId: string;
|
|
30
|
+
}>;
|
|
31
|
+
export type SecurityQuery = (text: string, values: unknown[]) => Promise<unknown[]>;
|
|
32
|
+
export interface SecurityOutcome {
|
|
33
|
+
status: Exclude<SecurityEventStatus, 'started'>;
|
|
34
|
+
errorCode: string | null;
|
|
35
|
+
}
|
|
36
|
+
export interface SecurityFinalizationState {
|
|
37
|
+
event: SecurityEvent;
|
|
38
|
+
finished: boolean;
|
|
39
|
+
finishing?: Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/** Concurrent close/send paths share one write; a failed write remains retryable. */
|
|
42
|
+
export declare function finishSecurityEvent(audit: {
|
|
43
|
+
finish(event: SecurityEvent, outcome: SecurityOutcome): Promise<void>;
|
|
44
|
+
} | undefined, state: SecurityFinalizationState, outcome: SecurityOutcome): Promise<void>;
|
|
45
|
+
export declare function safeTool(value: unknown): string;
|
|
46
|
+
export declare function safeFailureCode(value: unknown): string;
|
|
47
|
+
export declare function securityOutcome(value: unknown): SecurityOutcome;
|
|
48
|
+
export declare function validateSecurityEvent(value: unknown): SecurityEvent;
|
|
49
|
+
export declare function encodeSecurityCursor(ownerId: string, bridgeId: string | undefined, last: SecurityEvent, first: SecurityEvent): string;
|
|
50
|
+
export declare function decodeSecurityCursor(cursor: string, ownerId: string, bridgeId?: string): z.infer<typeof cursorSchema>;
|
|
51
|
+
export declare function listSecurityPage(query: SecurityQuery, ownerId: string, page?: SecurityPageQuery): Promise<SecurityPage>;
|
|
52
|
+
export declare class SecurityEventStore {
|
|
53
|
+
readonly pool: Pool;
|
|
54
|
+
private readonly runId;
|
|
55
|
+
private heartbeat?;
|
|
56
|
+
constructor(pool: Pool);
|
|
57
|
+
start(): Promise<void>;
|
|
58
|
+
private renew;
|
|
59
|
+
reconcile(): Promise<void>;
|
|
60
|
+
admit(input: SecurityEvent): Promise<void>;
|
|
61
|
+
finish(event: SecurityEvent, outcome: SecurityOutcome): Promise<void>;
|
|
62
|
+
list(ownerId: string, page?: SecurityPageQuery): Promise<SecurityPage>;
|
|
63
|
+
close(): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export {};
|