@privos_ai/app-server 0.5.0 → 0.6.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/README.md +169 -2
- package/dist/app-descriptor.d.ts.map +1 -1
- package/dist/app-descriptor.js +2 -0
- package/dist/app-descriptor.js.map +1 -1
- package/dist/auth/user-token.d.ts.map +1 -1
- package/dist/auth/user-token.js +21 -3
- package/dist/auth/user-token.js.map +1 -1
- package/dist/context/tool-call-context.d.ts +15 -2
- package/dist/context/tool-call-context.d.ts.map +1 -1
- package/dist/direct/express-router.d.ts +3 -0
- package/dist/direct/express-router.d.ts.map +1 -1
- package/dist/direct/express-router.js +70 -10
- package/dist/direct/express-router.js.map +1 -1
- package/dist/direct/http-ingress.js +1 -1
- package/dist/direct/http-ingress.js.map +1 -1
- package/dist/index.d.ts +16 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/relay/hub-user-token-actor.d.ts +62 -0
- package/dist/relay/hub-user-token-actor.d.ts.map +1 -0
- package/dist/relay/hub-user-token-actor.js +81 -0
- package/dist/relay/hub-user-token-actor.js.map +1 -0
- package/dist/relay/relay-client.d.ts +66 -11
- package/dist/relay/relay-client.d.ts.map +1 -1
- package/dist/relay/relay-client.js +134 -15
- package/dist/relay/relay-client.js.map +1 -1
- package/dist/relay/standalone-control.d.ts +45 -0
- package/dist/relay/standalone-control.d.ts.map +1 -0
- package/dist/relay/standalone-control.js +338 -0
- package/dist/relay/standalone-control.js.map +1 -0
- package/dist/relay/standalone-identity.d.ts +69 -0
- package/dist/relay/standalone-identity.d.ts.map +1 -0
- package/dist/relay/standalone-identity.js +204 -0
- package/dist/relay/standalone-identity.js.map +1 -0
- package/dist/relay/standalone-readiness.d.ts +33 -0
- package/dist/relay/standalone-readiness.d.ts.map +1 -0
- package/dist/relay/standalone-readiness.js +80 -0
- package/dist/relay/standalone-readiness.js.map +1 -0
- package/dist/runtime-mode.d.ts +25 -0
- package/dist/runtime-mode.d.ts.map +1 -0
- package/dist/runtime-mode.js +72 -0
- package/dist/runtime-mode.js.map +1 -0
- package/dist/runtime.d.ts +9 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +9 -7
- package/dist/runtime.js.map +1 -1
- package/dist/workload/dispatch-assertion.d.ts +20 -8
- package/dist/workload/dispatch-assertion.d.ts.map +1 -1
- package/dist/workload/dispatch-assertion.js +30 -23
- package/dist/workload/dispatch-assertion.js.map +1 -1
- package/dist/workload/index.d.ts +3 -3
- package/dist/workload/index.d.ts.map +1 -1
- package/dist/workload/index.js +1 -1
- package/dist/workload/index.js.map +1 -1
- package/dist/workload/managed-ingress-authority.d.ts +26 -0
- package/dist/workload/managed-ingress-authority.d.ts.map +1 -0
- package/dist/workload/managed-ingress-authority.js +45 -0
- package/dist/workload/managed-ingress-authority.js.map +1 -0
- package/dist/workload/workload-identity.d.ts +26 -2
- package/dist/workload/workload-identity.d.ts.map +1 -1
- package/dist/workload/workload-identity.js +377 -19
- package/dist/workload/workload-identity.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone-production control channel: secret rotation, trust rotation
|
|
3
|
+
* (Hub re-key or generation/manifest update), and capabilities push — all
|
|
4
|
+
* delivered as ES256-signed control assertions over the already-authenticated
|
|
5
|
+
* Relay WebSocket. A control message is applied only when its signature
|
|
6
|
+
* verifies against the identity file's CURRENTLY pinned Hub key: the Relay
|
|
7
|
+
* transport being authenticated is not sufficient on its own, because a
|
|
8
|
+
* compromised relay hop must not be able to redirect credentials or trust.
|
|
9
|
+
*
|
|
10
|
+
* Wire contract (Hub side must match): a JSON-RPC notification over the Relay
|
|
11
|
+
* WebSocket with one of the reserved methods below and
|
|
12
|
+
* `params: { assertion: <compact ES256 JWS> }`. The compact JWS header is
|
|
13
|
+
* `{ alg:'ES256', kid, privos_protocol:3, typ:'privos-hub-standalone-control+jws' }`
|
|
14
|
+
* and its payload is
|
|
15
|
+
* `{ protocolVersion:3, type, iss:'hub:<deploymentId>', aud:'mcp-runtime:<mcpAppId>',
|
|
16
|
+
* jti, nonce, iat, exp, data }` with `exp - iat <= 30` (same zero-headroom
|
|
17
|
+
* budget as dispatch assertions — NTP on both sides is a hard requirement).
|
|
18
|
+
* `data` is `{ clientId, clientSecret }` for secret rotation,
|
|
19
|
+
* `{ trust: RuntimeDispatchTrustV3 }` for trust rotation, and
|
|
20
|
+
* `{ scopes: string[], grantEpoch: number }` for a capabilities push.
|
|
21
|
+
*/
|
|
22
|
+
import crypto from 'node:crypto';
|
|
23
|
+
import { assertRuntimeDispatchTrustConfigurationV3, } from '../workload/dispatch-assertion.js';
|
|
24
|
+
import { loadStandaloneIdentity, rotateStandaloneIdentity, standaloneHubFingerprint, } from './standalone-identity.js';
|
|
25
|
+
export const STANDALONE_SECRET_ROTATE_METHOD = 'notifications/privos.standaloneSecretRotate';
|
|
26
|
+
export const STANDALONE_TRUST_ROTATE_METHOD = 'notifications/privos.standaloneTrustRotate';
|
|
27
|
+
export const STANDALONE_CAPABILITIES_CHANGED_METHOD = 'notifications/privos.standaloneCapabilitiesChanged';
|
|
28
|
+
const CONTROL_METHODS = new Set([
|
|
29
|
+
STANDALONE_SECRET_ROTATE_METHOD,
|
|
30
|
+
STANDALONE_TRUST_ROTATE_METHOD,
|
|
31
|
+
STANDALONE_CAPABILITIES_CHANGED_METHOD,
|
|
32
|
+
]);
|
|
33
|
+
export function isStandaloneControlMethod(method) {
|
|
34
|
+
return typeof method === 'string' && CONTROL_METHODS.has(method);
|
|
35
|
+
}
|
|
36
|
+
const CONTROL_HEADER_KEYS = ['alg', 'kid', 'privos_protocol', 'typ'];
|
|
37
|
+
const CONTROL_PAYLOAD_KEYS = ['aud', 'data', 'exp', 'iat', 'iss', 'jti', 'nonce', 'protocolVersion', 'type'];
|
|
38
|
+
const CONTROL_TYPE_BY_METHOD = {
|
|
39
|
+
[STANDALONE_SECRET_ROTATE_METHOD]: 'standalone-secret-rotate',
|
|
40
|
+
[STANDALONE_TRUST_ROTATE_METHOD]: 'standalone-trust-rotate',
|
|
41
|
+
[STANDALONE_CAPABILITIES_CHANGED_METHOD]: 'standalone-capabilities-changed',
|
|
42
|
+
};
|
|
43
|
+
const MAX_CONTROL_COMPACT_BYTES = 32_768;
|
|
44
|
+
const DEFAULT_REPLAY_CAPACITY = 64;
|
|
45
|
+
export class StandaloneControlError extends Error {
|
|
46
|
+
constructor(message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = 'StandaloneControlError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isRecord(value) {
|
|
52
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
53
|
+
}
|
|
54
|
+
function exactKeys(value, expected) {
|
|
55
|
+
const actual = Object.keys(value).sort();
|
|
56
|
+
const sortedExpected = [...expected].sort();
|
|
57
|
+
return actual.length === sortedExpected.length && actual.every((key, index) => key === sortedExpected[index]);
|
|
58
|
+
}
|
|
59
|
+
function canonicalize(value) {
|
|
60
|
+
if (Array.isArray(value))
|
|
61
|
+
return value.map(canonicalize);
|
|
62
|
+
if (value && typeof value === 'object') {
|
|
63
|
+
return Object.fromEntries(Object.entries(value)
|
|
64
|
+
.filter(([, child]) => child !== undefined)
|
|
65
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
66
|
+
.map(([key, child]) => [key, canonicalize(child)]));
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
function canonical(value) {
|
|
71
|
+
return JSON.stringify(canonicalize(value));
|
|
72
|
+
}
|
|
73
|
+
function strictBase64urlDecode(value) {
|
|
74
|
+
if (!value || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
75
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
76
|
+
const decoded = Buffer.from(value, 'base64url');
|
|
77
|
+
if (decoded.toString('base64url') !== value)
|
|
78
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
79
|
+
return decoded;
|
|
80
|
+
}
|
|
81
|
+
/** Bounded replay protection for the rare (operator-triggered) control channel. */
|
|
82
|
+
class BoundedControlReplayConsumer {
|
|
83
|
+
capacity;
|
|
84
|
+
seen = new Map();
|
|
85
|
+
constructor(capacity = DEFAULT_REPLAY_CAPACITY) {
|
|
86
|
+
this.capacity = capacity;
|
|
87
|
+
}
|
|
88
|
+
consume(key, expiresAt, now) {
|
|
89
|
+
for (const [existingKey, exp] of this.seen)
|
|
90
|
+
if (exp <= now)
|
|
91
|
+
this.seen.delete(existingKey);
|
|
92
|
+
if (this.seen.has(key))
|
|
93
|
+
return false;
|
|
94
|
+
if (this.seen.size >= this.capacity) {
|
|
95
|
+
const oldest = this.seen.keys().next().value;
|
|
96
|
+
if (oldest !== undefined)
|
|
97
|
+
this.seen.delete(oldest);
|
|
98
|
+
}
|
|
99
|
+
this.seen.set(key, expiresAt);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function verifyStandaloneControlAssertion(input) {
|
|
104
|
+
const expectedType = CONTROL_TYPE_BY_METHOD[input.method];
|
|
105
|
+
if (!expectedType)
|
|
106
|
+
throw new StandaloneControlError('standalone_control_method_unknown');
|
|
107
|
+
if (typeof input.compact !== 'string' || Buffer.byteLength(input.compact, 'utf8') > MAX_CONTROL_COMPACT_BYTES) {
|
|
108
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
109
|
+
}
|
|
110
|
+
const parts = input.compact.split('.');
|
|
111
|
+
if (parts.length !== 3 || parts.some((part) => !part))
|
|
112
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
113
|
+
const headerBytes = strictBase64urlDecode(parts[0]);
|
|
114
|
+
const payloadBytes = strictBase64urlDecode(parts[1]);
|
|
115
|
+
const signature = strictBase64urlDecode(parts[2]);
|
|
116
|
+
let header;
|
|
117
|
+
let payload;
|
|
118
|
+
try {
|
|
119
|
+
header = JSON.parse(headerBytes.toString('utf8'));
|
|
120
|
+
payload = JSON.parse(payloadBytes.toString('utf8'));
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
124
|
+
}
|
|
125
|
+
if (!isRecord(header) || !isRecord(payload))
|
|
126
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
127
|
+
if (parts[0] !== Buffer.from(canonical(header), 'utf8').toString('base64url') ||
|
|
128
|
+
parts[1] !== Buffer.from(canonical(payload), 'utf8').toString('base64url')) {
|
|
129
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
130
|
+
}
|
|
131
|
+
if (!exactKeys(header, CONTROL_HEADER_KEYS) ||
|
|
132
|
+
header.alg !== 'ES256' ||
|
|
133
|
+
header.typ !== 'privos-hub-standalone-control+jws' ||
|
|
134
|
+
header.privos_protocol !== 3 ||
|
|
135
|
+
header.kid !== input.trust.hubKid) {
|
|
136
|
+
// A kid mismatch here is the cold-app case: the message was signed by a
|
|
137
|
+
// key this app never accepted a trust rotation for. Refuse; the operator
|
|
138
|
+
// must deliver the rotation live or re-pair.
|
|
139
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
140
|
+
}
|
|
141
|
+
if (!exactKeys(payload, CONTROL_PAYLOAD_KEYS) ||
|
|
142
|
+
payload.protocolVersion !== 3 ||
|
|
143
|
+
payload.type !== expectedType ||
|
|
144
|
+
payload.iss !== `hub:${input.trust.affinity.deploymentId}` ||
|
|
145
|
+
payload.aud !== `mcp-runtime:${input.trust.affinity.mcpAppId}` ||
|
|
146
|
+
typeof payload.jti !== 'string' ||
|
|
147
|
+
!payload.jti ||
|
|
148
|
+
typeof payload.nonce !== 'string' ||
|
|
149
|
+
!payload.nonce ||
|
|
150
|
+
!Number.isSafeInteger(payload.iat) ||
|
|
151
|
+
!Number.isSafeInteger(payload.exp) ||
|
|
152
|
+
!isRecord(payload.data)) {
|
|
153
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
154
|
+
}
|
|
155
|
+
let key;
|
|
156
|
+
try {
|
|
157
|
+
key = crypto.createPublicKey({ key: input.trust.hubPublicJwk, format: 'jwk' });
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
161
|
+
}
|
|
162
|
+
if (signature.byteLength !== 64 ||
|
|
163
|
+
!crypto.verify('sha256', Buffer.from(`${parts[0]}.${parts[1]}`, 'ascii'), { key, dsaEncoding: 'ieee-p1363' }, signature)) {
|
|
164
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
165
|
+
}
|
|
166
|
+
const iat = Number(payload.iat);
|
|
167
|
+
const exp = Number(payload.exp);
|
|
168
|
+
if (iat > input.now + input.clockSkewSeconds ||
|
|
169
|
+
exp <= input.now ||
|
|
170
|
+
exp <= iat ||
|
|
171
|
+
exp - iat > 30) {
|
|
172
|
+
throw new StandaloneControlError('standalone_control_assertion_time_invalid');
|
|
173
|
+
}
|
|
174
|
+
if (!input.replay.consume(`${payload.iss}:${payload.jti}:${payload.nonce}`, exp, input.now)) {
|
|
175
|
+
throw new StandaloneControlError('standalone_control_assertion_replayed');
|
|
176
|
+
}
|
|
177
|
+
return { data: payload.data };
|
|
178
|
+
}
|
|
179
|
+
function assertSecretRotateData(value) {
|
|
180
|
+
if (!isRecord(value) ||
|
|
181
|
+
!exactKeys(value, ['clientId', 'clientSecret']) ||
|
|
182
|
+
typeof value.clientId !== 'string' ||
|
|
183
|
+
!value.clientId ||
|
|
184
|
+
typeof value.clientSecret !== 'string' ||
|
|
185
|
+
!value.clientSecret) {
|
|
186
|
+
throw new StandaloneControlError('standalone_control_secret_rotate_data_invalid');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function assertTrustRotateData(value, current) {
|
|
190
|
+
if (!isRecord(value) || !exactKeys(value, ['trust'])) {
|
|
191
|
+
throw new StandaloneControlError('standalone_control_trust_rotate_data_invalid');
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
assertRuntimeDispatchTrustConfigurationV3(value.trust);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
throw new StandaloneControlError('standalone_control_trust_rotate_data_invalid');
|
|
198
|
+
}
|
|
199
|
+
const next = value.trust;
|
|
200
|
+
if (next.affinity.workspaceId !== current.affinity.workspaceId ||
|
|
201
|
+
next.affinity.deploymentId !== current.affinity.deploymentId ||
|
|
202
|
+
next.affinity.mcpAppId !== current.affinity.mcpAppId ||
|
|
203
|
+
next.affinity.runtimeInstallationId !== current.affinity.runtimeInstallationId) {
|
|
204
|
+
// Re-key/generation-update rotation may change the key, generation, and
|
|
205
|
+
// manifest — it must never change which installation this identity is.
|
|
206
|
+
throw new StandaloneControlError('standalone_control_trust_rotate_identity_mismatch');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function assertCapabilitiesChangedData(value) {
|
|
210
|
+
if (!isRecord(value) ||
|
|
211
|
+
!exactKeys(value, ['scopes', 'grantEpoch']) ||
|
|
212
|
+
!Array.isArray(value.scopes) ||
|
|
213
|
+
value.scopes.some((scope) => typeof scope !== 'string') ||
|
|
214
|
+
!Number.isSafeInteger(value.grantEpoch) ||
|
|
215
|
+
Number(value.grantEpoch) < 0) {
|
|
216
|
+
throw new StandaloneControlError('standalone_control_capabilities_data_invalid');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Builds the mutable, in-process bridge between the persisted identity file
|
|
221
|
+
* and a live Relay connection: current credentials/trust for outbound OAuth
|
|
222
|
+
* and dispatch verification, plus the capabilities event surface, all kept in
|
|
223
|
+
* sync as verified rotation/capabilities control messages arrive.
|
|
224
|
+
*/
|
|
225
|
+
export function createStandaloneRelayIdentityController(loaded, options = {}) {
|
|
226
|
+
const now = options.now ?? (() => Math.floor(Date.now() / 1_000));
|
|
227
|
+
const clockSkewSeconds = options.clockSkewSeconds ?? 5;
|
|
228
|
+
const replay = new BoundedControlReplayConsumer();
|
|
229
|
+
let current = loaded;
|
|
230
|
+
let capabilities = Object.freeze({
|
|
231
|
+
scopes: Object.freeze([]),
|
|
232
|
+
grantEpoch: current.trust.affinity.runtimeAuthorizationEpoch ?? 0,
|
|
233
|
+
updatedAt: Date.now(),
|
|
234
|
+
});
|
|
235
|
+
const listeners = new Set();
|
|
236
|
+
async function applySecretRotate(assertionCompact) {
|
|
237
|
+
if (typeof assertionCompact !== 'string')
|
|
238
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
239
|
+
const verified = verifyStandaloneControlAssertion({
|
|
240
|
+
method: STANDALONE_SECRET_ROTATE_METHOD,
|
|
241
|
+
compact: assertionCompact,
|
|
242
|
+
trust: current.trust,
|
|
243
|
+
replay,
|
|
244
|
+
now: now(),
|
|
245
|
+
clockSkewSeconds,
|
|
246
|
+
});
|
|
247
|
+
assertSecretRotateData(verified.data);
|
|
248
|
+
const { clientId, clientSecret } = verified.data;
|
|
249
|
+
const rotated = await rotateStandaloneIdentity((identity) => ({ ...identity, clientId, clientSecret, rotatedAt: Date.now() }), { filePath: current.filePath });
|
|
250
|
+
current = rotated;
|
|
251
|
+
options.logger?.('standalone.identity.secret_rotated', { filePath: current.filePath });
|
|
252
|
+
options.onRotated?.(current);
|
|
253
|
+
}
|
|
254
|
+
async function applyTrustRotate(assertionCompact) {
|
|
255
|
+
if (typeof assertionCompact !== 'string')
|
|
256
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
257
|
+
const verified = verifyStandaloneControlAssertion({
|
|
258
|
+
method: STANDALONE_TRUST_ROTATE_METHOD,
|
|
259
|
+
compact: assertionCompact,
|
|
260
|
+
trust: current.trust,
|
|
261
|
+
replay,
|
|
262
|
+
now: now(),
|
|
263
|
+
clockSkewSeconds,
|
|
264
|
+
});
|
|
265
|
+
assertTrustRotateData(verified.data, current.trust);
|
|
266
|
+
const nextTrust = verified.data.trust;
|
|
267
|
+
const rotated = await rotateStandaloneIdentity((identity) => ({
|
|
268
|
+
...identity,
|
|
269
|
+
trust: nextTrust,
|
|
270
|
+
fingerprint: standaloneHubFingerprint(nextTrust.hubKid),
|
|
271
|
+
rotatedAt: Date.now(),
|
|
272
|
+
}), { filePath: current.filePath });
|
|
273
|
+
current = rotated;
|
|
274
|
+
options.logger?.('standalone.identity.trust_rotated', {
|
|
275
|
+
filePath: current.filePath,
|
|
276
|
+
fingerprint: current.fingerprint,
|
|
277
|
+
});
|
|
278
|
+
options.onRotated?.(current);
|
|
279
|
+
}
|
|
280
|
+
async function applyCapabilitiesChanged(assertionCompact) {
|
|
281
|
+
if (typeof assertionCompact !== 'string')
|
|
282
|
+
throw new StandaloneControlError('standalone_control_assertion_invalid');
|
|
283
|
+
const verified = verifyStandaloneControlAssertion({
|
|
284
|
+
method: STANDALONE_CAPABILITIES_CHANGED_METHOD,
|
|
285
|
+
compact: assertionCompact,
|
|
286
|
+
trust: current.trust,
|
|
287
|
+
replay,
|
|
288
|
+
now: now(),
|
|
289
|
+
clockSkewSeconds,
|
|
290
|
+
});
|
|
291
|
+
assertCapabilitiesChangedData(verified.data);
|
|
292
|
+
const next = Object.freeze({
|
|
293
|
+
scopes: Object.freeze([...new Set(verified.data.scopes)].sort()),
|
|
294
|
+
grantEpoch: verified.data.grantEpoch,
|
|
295
|
+
updatedAt: Date.now(),
|
|
296
|
+
});
|
|
297
|
+
if (next.grantEpoch < capabilities.grantEpoch) {
|
|
298
|
+
// A control channel may reorder; never regress to a stale epoch.
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
capabilities = next;
|
|
302
|
+
for (const listener of listeners) {
|
|
303
|
+
try {
|
|
304
|
+
listener(next);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// A consumer callback must never break the control channel.
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
getCredentials: () => current.relay,
|
|
313
|
+
getTrust: () => current.trust,
|
|
314
|
+
peekEffectiveCapabilities: () => capabilities,
|
|
315
|
+
onCapabilitiesChanged: (listener) => {
|
|
316
|
+
listeners.add(listener);
|
|
317
|
+
return () => listeners.delete(listener);
|
|
318
|
+
},
|
|
319
|
+
async handleControlNotification(method, params) {
|
|
320
|
+
if (!isStandaloneControlMethod(method))
|
|
321
|
+
return 'ignored';
|
|
322
|
+
const assertion = isRecord(params) ? params.assertion : undefined;
|
|
323
|
+
if (method === STANDALONE_SECRET_ROTATE_METHOD)
|
|
324
|
+
await applySecretRotate(assertion);
|
|
325
|
+
else if (method === STANDALONE_TRUST_ROTATE_METHOD)
|
|
326
|
+
await applyTrustRotate(assertion);
|
|
327
|
+
else
|
|
328
|
+
await applyCapabilitiesChanged(assertion);
|
|
329
|
+
return 'handled';
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/** Convenience: load the identity file and build its controller in one call. */
|
|
334
|
+
export function loadStandaloneRelayIdentityController(options = {}) {
|
|
335
|
+
const loaded = loadStandaloneIdentity({ filePath: options.filePath });
|
|
336
|
+
return createStandaloneRelayIdentityController(loaded, options);
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=standalone-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone-control.js","sourceRoot":"","sources":["../../src/relay/standalone-control.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type RuntimeDispatchTrustV3 } from '../workload/dispatch-assertion.js';
|
|
2
|
+
export declare const DEFAULT_STANDALONE_IDENTITY_FILE = "./privos-standalone-identity.json";
|
|
3
|
+
export type StandaloneIdentityV2 = Readonly<{
|
|
4
|
+
pairingVersion: 2;
|
|
5
|
+
/** Origin the OAuth token + Relay WebSocket are reached at (http/https, no trailing slash). */
|
|
6
|
+
relayUrl: string;
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret: string;
|
|
9
|
+
/** Hub dispatch trust delivered at pairing; the sole input to `RuntimeDispatchTrustV3`. */
|
|
10
|
+
trust: RuntimeDispatchTrustV3;
|
|
11
|
+
/** `SHA256:<hubKid>` — operator-verifiable, SSH-host-key style. */
|
|
12
|
+
fingerprint: string;
|
|
13
|
+
mcpAppId?: string;
|
|
14
|
+
/** Unix ms when this file was first written by pairing. */
|
|
15
|
+
pairedAt: number;
|
|
16
|
+
/** Unix ms of the most recent secret or trust rotation, if any. */
|
|
17
|
+
rotatedAt?: number;
|
|
18
|
+
}>;
|
|
19
|
+
export type LoadedStandaloneIdentity = Readonly<{
|
|
20
|
+
filePath: string;
|
|
21
|
+
identity: StandaloneIdentityV2;
|
|
22
|
+
relay: Readonly<{
|
|
23
|
+
privosUrl: string;
|
|
24
|
+
clientId: string;
|
|
25
|
+
clientSecret: string;
|
|
26
|
+
}>;
|
|
27
|
+
trust: RuntimeDispatchTrustV3;
|
|
28
|
+
fingerprint: string;
|
|
29
|
+
}>;
|
|
30
|
+
export type StandaloneIdentityErrorCode = 'IDENTITY_FILE_MISSING' | 'IDENTITY_FILE_MODE_INVALID' | 'IDENTITY_FILE_UNPARSEABLE' | 'IDENTITY_FILE_INVALID' | 'IDENTITY_FILE_ALREADY_EXISTS';
|
|
31
|
+
export declare class StandaloneIdentityError extends Error {
|
|
32
|
+
readonly code: StandaloneIdentityErrorCode;
|
|
33
|
+
constructor(code: StandaloneIdentityErrorCode, message: string);
|
|
34
|
+
}
|
|
35
|
+
/** Renders an SSH-host-key-style fingerprint for out-of-band operator verification. */
|
|
36
|
+
export declare function standaloneHubFingerprint(hubKid: string): string;
|
|
37
|
+
/** Throws {@link StandaloneIdentityError} `IDENTITY_FILE_INVALID` describing exactly what failed. */
|
|
38
|
+
export declare function assertStandaloneIdentityShape(value: unknown): asserts value is StandaloneIdentityV2;
|
|
39
|
+
/**
|
|
40
|
+
* Loads, validates, and returns the standalone identity file. Refuses to load
|
|
41
|
+
* (with a specific reason) a missing file, a file with unsafe permissions, or
|
|
42
|
+
* unparseable/invalid content — never falls back to a relaxed mode.
|
|
43
|
+
*/
|
|
44
|
+
export declare function loadStandaloneIdentity(options?: {
|
|
45
|
+
filePath?: string;
|
|
46
|
+
}): LoadedStandaloneIdentity;
|
|
47
|
+
/** True when a standalone identity file exists at the resolved path, without loading/validating it. */
|
|
48
|
+
export declare function standaloneIdentityFileExists(options?: {
|
|
49
|
+
filePath?: string;
|
|
50
|
+
}): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Persists a freshly-paired identity. Creates the file with `wx` (fails if it
|
|
53
|
+
* already exists) at mode `0600` — the same discipline as the Hub's own
|
|
54
|
+
* self-generated identity file. Re-pairing over an existing file requires the
|
|
55
|
+
* operator to remove it first; rotation uses {@link rotateStandaloneIdentity}.
|
|
56
|
+
*/
|
|
57
|
+
export declare function saveStandaloneIdentity(identity: StandaloneIdentityV2, options?: {
|
|
58
|
+
filePath?: string;
|
|
59
|
+
}): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Atomically rewrites the identity file (temp file + rename) — the durable
|
|
62
|
+
* half of secret/trust rotation. `mutate` receives the currently persisted,
|
|
63
|
+
* validated identity and must return the complete next identity; the result
|
|
64
|
+
* is re-validated before it is written.
|
|
65
|
+
*/
|
|
66
|
+
export declare function rotateStandaloneIdentity(mutate: (current: StandaloneIdentityV2) => StandaloneIdentityV2, options?: {
|
|
67
|
+
filePath?: string;
|
|
68
|
+
}): Promise<LoadedStandaloneIdentity>;
|
|
69
|
+
//# sourceMappingURL=standalone-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone-identity.d.ts","sourceRoot":"","sources":["../../src/relay/standalone-identity.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,gCAAgC,sCAAsC,CAAC;AAapF,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC3C,cAAc,EAAE,CAAC,CAAC;IAClB,+FAA+F;IAC/F,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,KAAK,EAAE,sBAAsB,CAAC;IAC9B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,EAAE,QAAQ,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/E,KAAK,EAAE,sBAAsB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,2BAA2B,GACpC,uBAAuB,GACvB,4BAA4B,GAC5B,2BAA2B,GAC3B,uBAAuB,GACvB,8BAA8B,CAAC;AAElC,qBAAa,uBAAwB,SAAQ,KAAK;aAEhC,IAAI,EAAE,2BAA2B;gBAAjC,IAAI,EAAE,2BAA2B,EACjD,OAAO,EAAE,MAAM;CAKhB;AAaD,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/D;AAMD,qGAAqG;AACrG,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,oBAAoB,CAqCnG;AAeD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,wBAAwB,CAgChG;AAED,uGAAuG;AACvG,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAErF;AAMD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC3C,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,MAAM,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,oBAAoB,EAC/D,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,wBAAwB,CAAC,CAuBnC"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone production identity: the file a self-hosted app persists after
|
|
3
|
+
* pairing over the Relay WebSocket. It is the sole source of relay
|
|
4
|
+
* credentials and Hub dispatch trust for `standalone-production` runtime
|
|
5
|
+
* mode — apps must load it through {@link loadStandaloneIdentity} and never
|
|
6
|
+
* hand-assemble `RuntimeDispatchTrustV3` from raw environment input.
|
|
7
|
+
*
|
|
8
|
+
* File discipline mirrors the Hub's own self-generated identity file: mode
|
|
9
|
+
* `0600`, created with `wx` so an existing file is never silently
|
|
10
|
+
* overwritten, and rotations rewrite it atomically (temp file + rename).
|
|
11
|
+
*/
|
|
12
|
+
import crypto from 'node:crypto';
|
|
13
|
+
import fsSync from 'node:fs';
|
|
14
|
+
import fs from 'node:fs/promises';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { assertRuntimeDispatchTrustConfigurationV3, } from '../workload/dispatch-assertion.js';
|
|
17
|
+
export const DEFAULT_STANDALONE_IDENTITY_FILE = './privos-standalone-identity.json';
|
|
18
|
+
const IDENTITY_REQUIRED_KEYS = [
|
|
19
|
+
'pairingVersion',
|
|
20
|
+
'relayUrl',
|
|
21
|
+
'clientId',
|
|
22
|
+
'clientSecret',
|
|
23
|
+
'trust',
|
|
24
|
+
'fingerprint',
|
|
25
|
+
'pairedAt',
|
|
26
|
+
];
|
|
27
|
+
const IDENTITY_OPTIONAL_KEYS = ['mcpAppId', 'rotatedAt'];
|
|
28
|
+
export class StandaloneIdentityError extends Error {
|
|
29
|
+
code;
|
|
30
|
+
constructor(code, message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
this.name = 'StandaloneIdentityError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function isRecord(value) {
|
|
37
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
function exactKeys(value, required, optional) {
|
|
40
|
+
const present = Object.keys(value);
|
|
41
|
+
const allowed = new Set([...required, ...optional]);
|
|
42
|
+
if (present.some((key) => !allowed.has(key)))
|
|
43
|
+
return false;
|
|
44
|
+
return required.every((key) => Object.prototype.hasOwnProperty.call(value, key));
|
|
45
|
+
}
|
|
46
|
+
/** Renders an SSH-host-key-style fingerprint for out-of-band operator verification. */
|
|
47
|
+
export function standaloneHubFingerprint(hubKid) {
|
|
48
|
+
return `SHA256:${hubKid}`;
|
|
49
|
+
}
|
|
50
|
+
function resolveIdentityFilePath(explicit) {
|
|
51
|
+
return path.resolve(explicit ?? process.env.PRIVOS_STANDALONE_IDENTITY_FILE ?? DEFAULT_STANDALONE_IDENTITY_FILE);
|
|
52
|
+
}
|
|
53
|
+
/** Throws {@link StandaloneIdentityError} `IDENTITY_FILE_INVALID` describing exactly what failed. */
|
|
54
|
+
export function assertStandaloneIdentityShape(value) {
|
|
55
|
+
if (!isRecord(value) || !exactKeys(value, IDENTITY_REQUIRED_KEYS, IDENTITY_OPTIONAL_KEYS)) {
|
|
56
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_INVALID', 'Standalone identity file has an unexpected shape.');
|
|
57
|
+
}
|
|
58
|
+
if (value.pairingVersion !== 2 ||
|
|
59
|
+
typeof value.relayUrl !== 'string' ||
|
|
60
|
+
!value.relayUrl ||
|
|
61
|
+
typeof value.clientId !== 'string' ||
|
|
62
|
+
!value.clientId ||
|
|
63
|
+
typeof value.clientSecret !== 'string' ||
|
|
64
|
+
!value.clientSecret ||
|
|
65
|
+
typeof value.fingerprint !== 'string' ||
|
|
66
|
+
!value.fingerprint ||
|
|
67
|
+
!Number.isFinite(value.pairedAt) ||
|
|
68
|
+
(value.mcpAppId !== undefined && typeof value.mcpAppId !== 'string') ||
|
|
69
|
+
(value.rotatedAt !== undefined && !Number.isFinite(value.rotatedAt))) {
|
|
70
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_INVALID', 'Standalone identity file has an invalid field.');
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
new URL(value.relayUrl);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_INVALID', 'Standalone identity relayUrl is not a valid URL.');
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
assertRuntimeDispatchTrustConfigurationV3(value.trust);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_INVALID', `Standalone identity trust is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
const trust = value.trust;
|
|
85
|
+
if (value.fingerprint !== standaloneHubFingerprint(trust.hubKid)) {
|
|
86
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_INVALID', 'Standalone identity fingerprint does not match the pinned hub key.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function assertSafeFileStat(stat, filePath) {
|
|
90
|
+
const uid = typeof process.getuid === 'function' ? process.getuid() : stat.uid;
|
|
91
|
+
const gid = typeof process.getgid === 'function' ? process.getgid() : stat.gid;
|
|
92
|
+
if (!stat.isFile() || stat.uid !== uid || stat.gid !== gid || (stat.mode & 0o777) !== 0o600) {
|
|
93
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_MODE_INVALID', `Standalone identity file ${filePath} must be an owner-only file at mode 0600 ` +
|
|
94
|
+
`(found mode ${(stat.mode & 0o777).toString(8)}, uid ${stat.uid}, gid ${stat.gid}). ` +
|
|
95
|
+
'Refusing to load a credential file that could be read or replaced by another user.');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Loads, validates, and returns the standalone identity file. Refuses to load
|
|
100
|
+
* (with a specific reason) a missing file, a file with unsafe permissions, or
|
|
101
|
+
* unparseable/invalid content — never falls back to a relaxed mode.
|
|
102
|
+
*/
|
|
103
|
+
export function loadStandaloneIdentity(options) {
|
|
104
|
+
const filePath = resolveIdentityFilePath(options?.filePath);
|
|
105
|
+
let stat;
|
|
106
|
+
try {
|
|
107
|
+
stat = fsSync.lstatSync(filePath);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_MISSING', `No standalone identity file at ${filePath}. Pair with pairOverWebSocket first, or set PRIVOS_STANDALONE_IDENTITY_FILE.`);
|
|
111
|
+
}
|
|
112
|
+
assertSafeFileStat(stat, filePath);
|
|
113
|
+
let raw;
|
|
114
|
+
try {
|
|
115
|
+
raw = fsSync.readFileSync(filePath, 'utf8');
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_UNPARSEABLE', `Failed to read standalone identity file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
119
|
+
}
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = JSON.parse(raw);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_UNPARSEABLE', `Standalone identity file ${filePath} is not valid JSON.`);
|
|
126
|
+
}
|
|
127
|
+
assertStandaloneIdentityShape(parsed);
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
filePath,
|
|
130
|
+
identity: parsed,
|
|
131
|
+
relay: Object.freeze({ privosUrl: parsed.relayUrl, clientId: parsed.clientId, clientSecret: parsed.clientSecret }),
|
|
132
|
+
trust: parsed.trust,
|
|
133
|
+
fingerprint: parsed.fingerprint,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/** True when a standalone identity file exists at the resolved path, without loading/validating it. */
|
|
137
|
+
export function standaloneIdentityFileExists(options) {
|
|
138
|
+
return fsSync.existsSync(resolveIdentityFilePath(options?.filePath));
|
|
139
|
+
}
|
|
140
|
+
async function assertSafeParentDirectory(directory) {
|
|
141
|
+
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Persists a freshly-paired identity. Creates the file with `wx` (fails if it
|
|
145
|
+
* already exists) at mode `0600` — the same discipline as the Hub's own
|
|
146
|
+
* self-generated identity file. Re-pairing over an existing file requires the
|
|
147
|
+
* operator to remove it first; rotation uses {@link rotateStandaloneIdentity}.
|
|
148
|
+
*/
|
|
149
|
+
export async function saveStandaloneIdentity(identity, options) {
|
|
150
|
+
assertStandaloneIdentityShape(identity);
|
|
151
|
+
const filePath = resolveIdentityFilePath(options?.filePath);
|
|
152
|
+
await assertSafeParentDirectory(path.dirname(filePath));
|
|
153
|
+
let handle;
|
|
154
|
+
try {
|
|
155
|
+
handle = await fs.open(filePath, 'wx', 0o600);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error.code === 'EEXIST') {
|
|
159
|
+
throw new StandaloneIdentityError('IDENTITY_FILE_ALREADY_EXISTS', `Standalone identity file ${filePath} already exists. Remove it before re-pairing, or use rotation for an in-place credential change.`);
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
await handle.writeFile(`${JSON.stringify(identity, null, 2)}\n`, 'utf8');
|
|
165
|
+
await handle.sync();
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await handle.close();
|
|
169
|
+
}
|
|
170
|
+
return filePath;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Atomically rewrites the identity file (temp file + rename) — the durable
|
|
174
|
+
* half of secret/trust rotation. `mutate` receives the currently persisted,
|
|
175
|
+
* validated identity and must return the complete next identity; the result
|
|
176
|
+
* is re-validated before it is written.
|
|
177
|
+
*/
|
|
178
|
+
export async function rotateStandaloneIdentity(mutate, options) {
|
|
179
|
+
const filePath = resolveIdentityFilePath(options?.filePath);
|
|
180
|
+
const current = loadStandaloneIdentity({ filePath });
|
|
181
|
+
const next = mutate(current.identity);
|
|
182
|
+
assertStandaloneIdentityShape(next);
|
|
183
|
+
const directory = path.dirname(filePath);
|
|
184
|
+
await assertSafeParentDirectory(directory);
|
|
185
|
+
const temporary = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
186
|
+
const handle = await fs.open(temporary, 'wx', 0o600);
|
|
187
|
+
try {
|
|
188
|
+
await handle.writeFile(`${JSON.stringify(next, null, 2)}\n`, 'utf8');
|
|
189
|
+
await handle.sync();
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
await handle.close();
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
await fs.rename(temporary, filePath);
|
|
196
|
+
await fs.chmod(filePath, 0o600);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
await fs.rm(temporary, { force: true }).catch(() => undefined);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
return loadStandaloneIdentity({ filePath });
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=standalone-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone-identity.js","sourceRoot":"","sources":["../../src/relay/standalone-identity.ts"],"names":[],"mappings":"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"}
|