borgmcp-server 0.1.1
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/LICENSE +105 -0
- package/NOTICE +8 -0
- package/README.md +119 -0
- package/SECURITY.md +38 -0
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/dist/bootstrap.d.ts +18 -0
- package/dist/bootstrap.js +104 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +96 -0
- package/dist/cli.js.map +1 -0
- package/dist/coordination-api.d.ts +25 -0
- package/dist/coordination-api.js +457 -0
- package/dist/coordination-api.js.map +1 -0
- package/dist/credentials.d.ts +58 -0
- package/dist/credentials.js +244 -0
- package/dist/credentials.js.map +1 -0
- package/dist/enrollment.d.ts +17 -0
- package/dist/enrollment.js +122 -0
- package/dist/enrollment.js.map +1 -0
- package/dist/https-server.d.ts +94 -0
- package/dist/https-server.js +814 -0
- package/dist/https-server.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +67 -0
- package/dist/main.js.map +1 -0
- package/dist/migrations.d.ts +8 -0
- package/dist/migrations.js +366 -0
- package/dist/migrations.js.map +1 -0
- package/dist/network-policy.d.ts +13 -0
- package/dist/network-policy.js +49 -0
- package/dist/network-policy.js.map +1 -0
- package/dist/operator-error.d.ts +3 -0
- package/dist/operator-error.js +63 -0
- package/dist/operator-error.js.map +1 -0
- package/dist/principal.d.ts +32 -0
- package/dist/principal.js +64 -0
- package/dist/principal.js.map +1 -0
- package/dist/protocol-draft.d.ts +2 -0
- package/dist/protocol-draft.js +31 -0
- package/dist/protocol-draft.js.map +1 -0
- package/dist/service.d.ts +61 -0
- package/dist/service.js +455 -0
- package/dist/service.js.map +1 -0
- package/dist/start-options.d.ts +2 -0
- package/dist/start-options.js +46 -0
- package/dist/start-options.js.map +1 -0
- package/dist/store.d.ts +327 -0
- package/dist/store.js +1729 -0
- package/dist/store.js.map +1 -0
- package/npm-shrinkwrap.json +1942 -0
- package/package.json +60 -0
- package/src/bootstrap.ts +127 -0
- package/src/cli.ts +102 -0
- package/src/coordination-api.ts +508 -0
- package/src/credentials.ts +319 -0
- package/src/enrollment.ts +156 -0
- package/src/https-server.ts +962 -0
- package/src/index.ts +3 -0
- package/src/main.ts +73 -0
- package/src/migrations.ts +394 -0
- package/src/network-policy.ts +65 -0
- package/src/operator-error.ts +97 -0
- package/src/principal.ts +106 -0
- package/src/protocol-draft.ts +32 -0
- package/src/service.ts +525 -0
- package/src/start-options.ts +46 -0
- package/src/store.ts +2316 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { createHmac, randomBytes, randomUUID, timingSafeEqual, } from "node:crypto";
|
|
2
|
+
import { clientPrincipal, droneSessionPrincipal, } from "./principal.js";
|
|
3
|
+
import { operatorErrors } from "./operator-error.js";
|
|
4
|
+
const tokenPattern = /^[A-Za-z0-9_-]{43,1024}$/u;
|
|
5
|
+
const dummyVerifier = Buffer.alloc(32);
|
|
6
|
+
export class CredentialDigester {
|
|
7
|
+
#key;
|
|
8
|
+
constructor(key) {
|
|
9
|
+
if (key.length !== 32)
|
|
10
|
+
throw new Error("Credential digest key must contain 32 bytes.");
|
|
11
|
+
this.#key = Buffer.from(key);
|
|
12
|
+
}
|
|
13
|
+
digest(secret, purpose) {
|
|
14
|
+
if (!tokenPattern.test(secret))
|
|
15
|
+
throw new Error("Credential must be unpadded base64url.");
|
|
16
|
+
const lookup = createHmac("sha256", this.#key)
|
|
17
|
+
.update(`lookup:${purpose}:`)
|
|
18
|
+
.update(secret)
|
|
19
|
+
.digest()
|
|
20
|
+
.subarray(0, 16);
|
|
21
|
+
const verifier = createHmac("sha256", this.#key)
|
|
22
|
+
.update(`verify:${purpose}:`)
|
|
23
|
+
.update(secret)
|
|
24
|
+
.digest();
|
|
25
|
+
return { lookup, verifier };
|
|
26
|
+
}
|
|
27
|
+
verify(secret, purpose, stored) {
|
|
28
|
+
let candidate;
|
|
29
|
+
let valid = true;
|
|
30
|
+
try {
|
|
31
|
+
candidate = this.digest(secret, purpose).verifier;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
candidate = dummyVerifier;
|
|
35
|
+
valid = false;
|
|
36
|
+
}
|
|
37
|
+
const expected = stored?.length === 32 ? stored : dummyVerifier;
|
|
38
|
+
return timingSafeEqual(candidate, expected) && stored !== undefined && valid;
|
|
39
|
+
}
|
|
40
|
+
destroy() {
|
|
41
|
+
this.#key.fill(0);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class LiveCredentialRegistry {
|
|
45
|
+
#sessions = new Map();
|
|
46
|
+
#keys = new Map();
|
|
47
|
+
register(identities) {
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const keys = [...new Set(typeof identities === "string" ? [identities] : identities)];
|
|
50
|
+
this.#keys.set(controller, keys);
|
|
51
|
+
for (const key of keys) {
|
|
52
|
+
const sessions = this.#sessions.get(key) ?? new Set();
|
|
53
|
+
sessions.add(controller);
|
|
54
|
+
this.#sessions.set(key, sessions);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
signal: controller.signal,
|
|
58
|
+
release: () => this.#release(controller),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
invalidate(identity) {
|
|
62
|
+
const sessions = this.#sessions.get(identity);
|
|
63
|
+
if (sessions === undefined)
|
|
64
|
+
return;
|
|
65
|
+
for (const controller of [...sessions]) {
|
|
66
|
+
this.#release(controller);
|
|
67
|
+
controller.abort();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
activeSessionCount(clientId) {
|
|
71
|
+
return this.#sessions.get(clientId)?.size ?? 0;
|
|
72
|
+
}
|
|
73
|
+
#release(controller) {
|
|
74
|
+
const keys = this.#keys.get(controller);
|
|
75
|
+
if (keys === undefined)
|
|
76
|
+
return;
|
|
77
|
+
this.#keys.delete(controller);
|
|
78
|
+
for (const key of keys) {
|
|
79
|
+
const sessions = this.#sessions.get(key);
|
|
80
|
+
sessions?.delete(controller);
|
|
81
|
+
if (sessions?.size === 0)
|
|
82
|
+
this.#sessions.delete(key);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export class CredentialAuthority {
|
|
87
|
+
#store;
|
|
88
|
+
#digester;
|
|
89
|
+
#clock;
|
|
90
|
+
#registry;
|
|
91
|
+
constructor(store, digester, clock = () => new Date(), registry = new LiveCredentialRegistry()) {
|
|
92
|
+
this.#store = store;
|
|
93
|
+
this.#digester = digester;
|
|
94
|
+
this.#clock = clock;
|
|
95
|
+
this.#registry = registry;
|
|
96
|
+
}
|
|
97
|
+
createRecoveryCredential() {
|
|
98
|
+
const secret = generateSecret();
|
|
99
|
+
this.#store.createRecoveryCredential(randomUUID(), this.#digester.digest(secret, "recovery"));
|
|
100
|
+
return secret;
|
|
101
|
+
}
|
|
102
|
+
createBootstrapInvitation(ttlMs) {
|
|
103
|
+
return this.#createInvitation("owner", ttlMs);
|
|
104
|
+
}
|
|
105
|
+
createInvitation(recoveryCredential, ttlMs) {
|
|
106
|
+
const digest = safeDigest(this.#digester, recoveryCredential, "recovery");
|
|
107
|
+
const stored = this.#store.findRecoveryCredential(digest.lookup);
|
|
108
|
+
if (!this.#digester.verify(recoveryCredential, "recovery", stored?.verifier))
|
|
109
|
+
return null;
|
|
110
|
+
return this.#createInvitation("client", ttlMs);
|
|
111
|
+
}
|
|
112
|
+
replaceOwnerInvitation(recoveryCredential, ttlMs) {
|
|
113
|
+
const digest = safeDigest(this.#digester, recoveryCredential, "recovery");
|
|
114
|
+
const stored = this.#store.findRecoveryCredential(digest.lookup);
|
|
115
|
+
if (!this.#digester.verify(recoveryCredential, "recovery", stored?.verifier))
|
|
116
|
+
return null;
|
|
117
|
+
return this.#createInvitation("owner", ttlMs);
|
|
118
|
+
}
|
|
119
|
+
#createInvitation(purpose, ttlMs) {
|
|
120
|
+
if (!Number.isSafeInteger(ttlMs) || ttlMs < 1_000 || ttlMs > 86_400_000) {
|
|
121
|
+
throw new Error("Invitation TTL must be an integer from 1000 to 86400000 milliseconds.");
|
|
122
|
+
}
|
|
123
|
+
const secret = generateSecret();
|
|
124
|
+
this.#store.createInvitation({
|
|
125
|
+
id: randomUUID(),
|
|
126
|
+
digest: this.#digester.digest(secret, "invitation"),
|
|
127
|
+
expiresAt: new Date(this.#clock().getTime() + ttlMs).toISOString(),
|
|
128
|
+
purpose,
|
|
129
|
+
});
|
|
130
|
+
return secret;
|
|
131
|
+
}
|
|
132
|
+
exchangeInvitation(request) {
|
|
133
|
+
const invitationDigest = safeDigest(this.#digester, request.invitation, "invitation");
|
|
134
|
+
const stored = this.#store.findInvitation(invitationDigest.lookup);
|
|
135
|
+
const verified = this.#digester.verify(request.invitation, "invitation", stored?.verifier);
|
|
136
|
+
const clientId = randomUUID();
|
|
137
|
+
const claimed = this.#store.claimInvitation({
|
|
138
|
+
invitationId: stored?.id ?? "00000000-0000-4000-8000-000000000000",
|
|
139
|
+
clientId,
|
|
140
|
+
requestedClientName: request.clientName ?? null,
|
|
141
|
+
retryKey: request.retryKey,
|
|
142
|
+
credentialId: randomUUID(),
|
|
143
|
+
credentialDigest: safeDigest(this.#digester, request.clientCredential, "client"),
|
|
144
|
+
});
|
|
145
|
+
return !verified || stored?.expiresAt === undefined || claimed === null ? null : {
|
|
146
|
+
purpose: claimed.purpose,
|
|
147
|
+
clientId: claimed.clientId,
|
|
148
|
+
serverCapabilities: claimed.serverCapabilities,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
authenticate(authorization) {
|
|
152
|
+
const result = this.authenticateStatus(authorization);
|
|
153
|
+
return typeof result === "object" ? result : null;
|
|
154
|
+
}
|
|
155
|
+
authenticateStatus(authorization) {
|
|
156
|
+
if (authorization === undefined)
|
|
157
|
+
return "missing";
|
|
158
|
+
const secret = bearerSecret(authorization);
|
|
159
|
+
const clientDigest = safeDigest(this.#digester, secret, "client");
|
|
160
|
+
const droneDigest = safeDigest(this.#digester, secret, "drone-session");
|
|
161
|
+
const client = this.#store.findClientCredential(clientDigest.lookup);
|
|
162
|
+
const drone = this.#store.findDroneSessionCredential(droneDigest.lookup);
|
|
163
|
+
const clientValid = this.#digester.verify(secret, "client", client?.verifier) &&
|
|
164
|
+
client?.clientId !== undefined;
|
|
165
|
+
const droneValid = this.#digester.verify(secret, "drone-session", drone?.verifier) &&
|
|
166
|
+
drone !== null;
|
|
167
|
+
if (clientValid) {
|
|
168
|
+
if (client.revokedAt != null)
|
|
169
|
+
return "revoked";
|
|
170
|
+
return clientPrincipal(client.clientId);
|
|
171
|
+
}
|
|
172
|
+
if (droneValid) {
|
|
173
|
+
if (drone.revokedAt != null || drone.expiresAt <= this.#clock().toISOString()) {
|
|
174
|
+
return "revoked";
|
|
175
|
+
}
|
|
176
|
+
return droneSessionPrincipal({
|
|
177
|
+
id: drone.sessionId,
|
|
178
|
+
clientId: drone.clientId,
|
|
179
|
+
cubeId: drone.cubeId,
|
|
180
|
+
droneId: drone.droneId,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return "invalid";
|
|
184
|
+
}
|
|
185
|
+
attachSeat(store, request) {
|
|
186
|
+
const credential = generateSecret();
|
|
187
|
+
const record = store.attachSeat({
|
|
188
|
+
...request,
|
|
189
|
+
droneId: randomUUID(),
|
|
190
|
+
sessionId: randomUUID(),
|
|
191
|
+
credentialId: randomUUID(),
|
|
192
|
+
credentialDigest: this.#digester.digest(credential, "drone-session"),
|
|
193
|
+
expiresAt: new Date(this.#clock().getTime() + 86_400_000).toISOString(),
|
|
194
|
+
});
|
|
195
|
+
for (const sessionId of record.revokedSessionIds)
|
|
196
|
+
this.#registry.invalidate(sessionId);
|
|
197
|
+
return { ...record, credential };
|
|
198
|
+
}
|
|
199
|
+
rotateClient(clientId) {
|
|
200
|
+
if (!this.#store.clientIsActive(clientId))
|
|
201
|
+
throw operatorErrors.CLIENT_NOT_FOUND;
|
|
202
|
+
const secret = generateSecret();
|
|
203
|
+
const rotated = this.#store.rotateClientCredential({
|
|
204
|
+
clientId,
|
|
205
|
+
credentialId: randomUUID(),
|
|
206
|
+
credentialDigest: this.#digester.digest(secret, "client"),
|
|
207
|
+
});
|
|
208
|
+
if (!rotated)
|
|
209
|
+
throw operatorErrors.CLIENT_NOT_FOUND;
|
|
210
|
+
this.#registry.invalidate(clientId);
|
|
211
|
+
return secret;
|
|
212
|
+
}
|
|
213
|
+
revokeClient(clientId) {
|
|
214
|
+
if (!this.#store.clientExists(clientId))
|
|
215
|
+
throw operatorErrors.CLIENT_NOT_FOUND;
|
|
216
|
+
this.#store.revokeClientCredentials(clientId);
|
|
217
|
+
this.#registry.invalidate(clientId);
|
|
218
|
+
}
|
|
219
|
+
registerLiveSession(principal) {
|
|
220
|
+
if (typeof principal === "string")
|
|
221
|
+
return this.#registry.register(principal);
|
|
222
|
+
return this.#registry.register(principal.kind === "drone-session"
|
|
223
|
+
? [principal.id, principal.clientId]
|
|
224
|
+
: principal.id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
export function generateSecret() {
|
|
228
|
+
return randomBytes(32).toString("base64url");
|
|
229
|
+
}
|
|
230
|
+
function bearerSecret(authorization) {
|
|
231
|
+
if (authorization === undefined)
|
|
232
|
+
return "";
|
|
233
|
+
const match = /^Bearer ([A-Za-z0-9_-]{43,1024})$/u.exec(authorization);
|
|
234
|
+
return match?.[1] ?? "";
|
|
235
|
+
}
|
|
236
|
+
function safeDigest(digester, secret, purpose) {
|
|
237
|
+
try {
|
|
238
|
+
return digester.digest(secret, purpose);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return { lookup: Buffer.alloc(16), verifier: dummyVerifier };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CredentialAuthority } from "./credentials.js";
|
|
2
|
+
interface EnrollmentEnvelope {
|
|
3
|
+
readonly protocol_version: "1";
|
|
4
|
+
readonly request_id: string;
|
|
5
|
+
readonly payload: {
|
|
6
|
+
readonly invitation: string;
|
|
7
|
+
readonly retry_key: string;
|
|
8
|
+
readonly client_credential: string;
|
|
9
|
+
readonly client_name?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare function createEnrollmentExchange(authority: CredentialAuthority): (body: unknown) => Promise<{
|
|
13
|
+
readonly status: 201 | 400 | 401 | 507;
|
|
14
|
+
readonly body?: unknown;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function decodeEnrollmentEnvelope(value: unknown): EnrollmentEnvelope;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { StorageCapacityError } from "./store.js";
|
|
2
|
+
export function createEnrollmentExchange(authority) {
|
|
3
|
+
return async (body) => {
|
|
4
|
+
let envelope;
|
|
5
|
+
try {
|
|
6
|
+
envelope = decodeEnrollmentEnvelope(body);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return {
|
|
10
|
+
status: 400,
|
|
11
|
+
body: errorEnvelope("INVALID_INPUT", "Invalid enrollment request.", safeRequestId(body)),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
let response;
|
|
15
|
+
try {
|
|
16
|
+
response = authority.exchangeInvitation({
|
|
17
|
+
invitation: envelope.payload.invitation,
|
|
18
|
+
retryKey: envelope.payload.retry_key,
|
|
19
|
+
clientCredential: envelope.payload.client_credential,
|
|
20
|
+
...(envelope.payload.client_name === undefined
|
|
21
|
+
? {}
|
|
22
|
+
: { clientName: envelope.payload.client_name }),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (!(error instanceof StorageCapacityError))
|
|
27
|
+
throw error;
|
|
28
|
+
return {
|
|
29
|
+
status: 507,
|
|
30
|
+
body: errorEnvelope("CAPACITY_EXCEEDED", error.message, envelope.request_id),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (response === null) {
|
|
34
|
+
return {
|
|
35
|
+
status: 401,
|
|
36
|
+
body: errorEnvelope("AUTH_INVALID", "Enrollment authentication failed.", envelope.request_id),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
status: 201,
|
|
41
|
+
body: {
|
|
42
|
+
protocol_version: "1",
|
|
43
|
+
request_id: envelope.request_id,
|
|
44
|
+
payload: {
|
|
45
|
+
purpose: response.purpose,
|
|
46
|
+
client_id: response.clientId,
|
|
47
|
+
server_capabilities: response.serverCapabilities,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function errorEnvelope(code, message, requestId) {
|
|
54
|
+
return {
|
|
55
|
+
protocol_version: "1",
|
|
56
|
+
...(requestId === undefined ? {} : { request_id: requestId }),
|
|
57
|
+
error: { code, message },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function safeRequestId(value) {
|
|
61
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
62
|
+
return undefined;
|
|
63
|
+
const requestId = value["request_id"];
|
|
64
|
+
return typeof requestId === "string" && /^[A-Za-z0-9._-]{8,128}$/u.test(requestId)
|
|
65
|
+
? requestId
|
|
66
|
+
: undefined;
|
|
67
|
+
}
|
|
68
|
+
export function decodeEnrollmentEnvelope(value) {
|
|
69
|
+
const envelope = exactRecord(value, ["protocol_version", "request_id", "payload"]);
|
|
70
|
+
if (envelope["protocol_version"] !== "1")
|
|
71
|
+
throw new Error("Invalid enrollment request.");
|
|
72
|
+
const requestId = envelope["request_id"];
|
|
73
|
+
if (typeof requestId !== "string" || !/^[A-Za-z0-9._-]{8,128}$/u.test(requestId)) {
|
|
74
|
+
throw new Error("Invalid enrollment request.");
|
|
75
|
+
}
|
|
76
|
+
const payload = exactRecord(envelope["payload"], ["invitation", "retry_key", "client_credential"], ["client_name"]);
|
|
77
|
+
const invitation = payload["invitation"];
|
|
78
|
+
if (typeof invitation !== "string" || !/^[A-Za-z0-9_-]{43,1024}$/u.test(invitation)) {
|
|
79
|
+
throw new Error("Invalid enrollment request.");
|
|
80
|
+
}
|
|
81
|
+
const retryKey = payload["retry_key"];
|
|
82
|
+
if (typeof retryKey !== "string" ||
|
|
83
|
+
!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(retryKey)) {
|
|
84
|
+
throw new Error("Invalid enrollment request.");
|
|
85
|
+
}
|
|
86
|
+
const clientCredential = payload["client_credential"];
|
|
87
|
+
if (typeof clientCredential !== "string" ||
|
|
88
|
+
!/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/u.test(clientCredential)) {
|
|
89
|
+
throw new Error("Invalid enrollment request.");
|
|
90
|
+
}
|
|
91
|
+
const clientName = payload["client_name"];
|
|
92
|
+
if (clientName !== undefined &&
|
|
93
|
+
(typeof clientName !== "string" || Buffer.byteLength(clientName) > 120 ||
|
|
94
|
+
!/^[A-Za-z0-9][A-Za-z0-9 ._-]*$/u.test(clientName))) {
|
|
95
|
+
throw new Error("Invalid enrollment request.");
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
protocol_version: "1",
|
|
99
|
+
request_id: requestId,
|
|
100
|
+
payload: clientName === undefined
|
|
101
|
+
? { invitation, retry_key: retryKey.toLowerCase(), client_credential: clientCredential }
|
|
102
|
+
: {
|
|
103
|
+
invitation,
|
|
104
|
+
retry_key: retryKey.toLowerCase(),
|
|
105
|
+
client_credential: clientCredential,
|
|
106
|
+
client_name: clientName,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function exactRecord(value, required, optional = []) {
|
|
111
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
112
|
+
throw new Error("Invalid enrollment request.");
|
|
113
|
+
}
|
|
114
|
+
const record = value;
|
|
115
|
+
const allowed = new Set([...required, ...optional]);
|
|
116
|
+
if (Object.keys(record).some((key) => !allowed.has(key)) ||
|
|
117
|
+
required.some((key) => !Object.hasOwn(record, key))) {
|
|
118
|
+
throw new Error("Invalid enrollment request.");
|
|
119
|
+
}
|
|
120
|
+
return record;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=enrollment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../src/enrollment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Socket } from "node:net";
|
|
2
|
+
import { type BindOptionsInput } from "./network-policy.js";
|
|
3
|
+
import type { CoordinationRequest, CoordinationResponse } from "./coordination-api.js";
|
|
4
|
+
import type { Principal } from "./principal.js";
|
|
5
|
+
export interface ProtocolInfoDocument {
|
|
6
|
+
readonly protocol_version: string;
|
|
7
|
+
readonly package: {
|
|
8
|
+
readonly name: "borgmcp-shared";
|
|
9
|
+
readonly version: string;
|
|
10
|
+
};
|
|
11
|
+
readonly capabilities: readonly string[];
|
|
12
|
+
readonly limits: {
|
|
13
|
+
readonly max_request_bytes: number;
|
|
14
|
+
readonly max_log_message_bytes: number;
|
|
15
|
+
readonly max_read_page_size: number;
|
|
16
|
+
readonly max_replay_page_size: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface ServiceLimits {
|
|
20
|
+
readonly maxConnections: number;
|
|
21
|
+
readonly maxConnectionsPerAddress: number;
|
|
22
|
+
readonly maxRequestsPerWindow: number;
|
|
23
|
+
readonly maxRequestsPerAddressWindow: number;
|
|
24
|
+
readonly maxRequestsGlobalWindow: number;
|
|
25
|
+
readonly rateLimitWindowMs: number;
|
|
26
|
+
readonly maxRateLimitEntries: number;
|
|
27
|
+
readonly maxStreamsPerCredential: number;
|
|
28
|
+
readonly maxHeaderBytes: number;
|
|
29
|
+
readonly maxRequestBodyBytes: number;
|
|
30
|
+
readonly maxRequestsPerSocket: number;
|
|
31
|
+
readonly requestTimeoutMs: number;
|
|
32
|
+
readonly tlsHandshakeTimeoutMs: number;
|
|
33
|
+
readonly headersTimeoutMs: number;
|
|
34
|
+
readonly keepAliveTimeoutMs: number;
|
|
35
|
+
readonly handlerTimeoutMs: number;
|
|
36
|
+
}
|
|
37
|
+
export declare const DEFAULT_SERVICE_LIMITS: ServiceLimits;
|
|
38
|
+
export interface RequestHandlerContext {
|
|
39
|
+
readonly protocolInfo: ProtocolInfoDocument;
|
|
40
|
+
readonly authorizeProtocol: (authorization: string | undefined, signal: AbortSignal) => Promise<boolean | "missing" | "invalid" | "revoked">;
|
|
41
|
+
readonly exchangeEnrollment?: (body: unknown) => Promise<{
|
|
42
|
+
readonly status: 201 | 400 | 401 | 507;
|
|
43
|
+
readonly body?: unknown;
|
|
44
|
+
}>;
|
|
45
|
+
readonly authorizeCoordination?: (authorization: string | undefined, signal: AbortSignal) => Promise<Principal | "missing" | "invalid" | "revoked">;
|
|
46
|
+
readonly handleCoordination?: (request: CoordinationRequest) => Promise<CoordinationResponse>;
|
|
47
|
+
}
|
|
48
|
+
export interface HttpsServerOptions {
|
|
49
|
+
readonly bind?: BindOptionsInput;
|
|
50
|
+
readonly tls: {
|
|
51
|
+
readonly key: string | Buffer;
|
|
52
|
+
readonly cert: string | Buffer;
|
|
53
|
+
readonly ca?: string | Buffer;
|
|
54
|
+
};
|
|
55
|
+
readonly protocolInfo: ProtocolInfoDocument;
|
|
56
|
+
readonly authorizeProtocol: RequestHandlerContext["authorizeProtocol"];
|
|
57
|
+
readonly exchangeEnrollment?: RequestHandlerContext["exchangeEnrollment"];
|
|
58
|
+
readonly authorizeCoordination?: RequestHandlerContext["authorizeCoordination"];
|
|
59
|
+
readonly handleCoordination?: RequestHandlerContext["handleCoordination"];
|
|
60
|
+
readonly limits?: ServiceLimits;
|
|
61
|
+
readonly testHooks?: {
|
|
62
|
+
readonly identifyRemoteAddress?: (socket: Socket) => string;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export interface RunningServer {
|
|
66
|
+
readonly origin: string;
|
|
67
|
+
readonly limits: ServiceLimits;
|
|
68
|
+
readonly close: () => Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
export declare function startHttpsServer(options: HttpsServerOptions): Promise<RunningServer>;
|
|
71
|
+
export declare function createRequestHandlerContext(options: HttpsServerOptions): RequestHandlerContext;
|
|
72
|
+
export declare function validateTlsCertificate(certificate: string | Buffer, host: string, mode: "loopback" | "lan", caCertificate?: string | Buffer): void;
|
|
73
|
+
export declare class RequestRateLimiter {
|
|
74
|
+
#private;
|
|
75
|
+
constructor(limits: ServiceLimits, maxRequests?: number, clock?: () => number);
|
|
76
|
+
consume(identity: string): number | null;
|
|
77
|
+
reserve(identity: string): RateLimitReservation;
|
|
78
|
+
}
|
|
79
|
+
export declare class PreAuthAdmissionLimiter {
|
|
80
|
+
#private;
|
|
81
|
+
constructor(limits: ServiceLimits, clock?: () => number);
|
|
82
|
+
consume(addressIdentity: string): number | null;
|
|
83
|
+
}
|
|
84
|
+
interface RateLimitReservation {
|
|
85
|
+
readonly retryAfter: number | null;
|
|
86
|
+
readonly commit: () => void;
|
|
87
|
+
readonly rollback: () => void;
|
|
88
|
+
}
|
|
89
|
+
export declare class ConcurrentQuota {
|
|
90
|
+
#private;
|
|
91
|
+
constructor(limit: number);
|
|
92
|
+
acquire(identity: string): (() => void) | null;
|
|
93
|
+
}
|
|
94
|
+
export {};
|