@oxy.so/core 4.5.0-oxy-one.1 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/HttpService.js +63 -19
- package/dist/cjs/api/accounts.js +10 -3
- package/dist/cjs/api/agency.js +3 -1
- package/dist/cjs/api/apps.js +15 -4
- package/dist/cjs/api/assets.js +39 -11
- package/dist/cjs/api/auth.js +28 -8
- package/dist/cjs/api/billing.js +24 -7
- package/dist/cjs/api/civic.js +14 -4
- package/dist/cjs/api/follows.js +9 -2
- package/dist/cjs/api/identity.js +31 -9
- package/dist/cjs/api/nodes.js +3 -1
- package/dist/cjs/api/privacy.js +8 -2
- package/dist/cjs/api/session.js +15 -4
- package/dist/cjs/api/store.js +15 -5
- package/dist/cjs/api/topics.js +10 -3
- package/dist/cjs/api/users.js +16 -4
- package/dist/cjs/boot/sessionColdBoot.js +40 -13
- package/dist/cjs/constants/version.js +6 -6
- package/dist/cjs/crypto/identityMarker.js +3 -1
- package/dist/cjs/crypto/identityProof.js +6 -1
- package/dist/cjs/crypto/keyManager.js +87 -32
- package/dist/cjs/crypto/recoveryPhrase.js +2 -2
- package/dist/cjs/crypto/signatureService.js +3 -4
- package/dist/cjs/i18n/index.js +16 -3
- package/dist/cjs/i18n/locales/ar-SA.js +366 -366
- package/dist/cjs/i18n/locales/ca-ES.js +366 -366
- package/dist/cjs/i18n/locales/de-DE.js +366 -366
- package/dist/cjs/i18n/locales/en-US.js +1994 -1994
- package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
- package/dist/cjs/i18n/locales/fr-FR.js +366 -366
- package/dist/cjs/i18n/locales/it-IT.js +366 -366
- package/dist/cjs/i18n/locales/ja-JP.js +366 -366
- package/dist/cjs/i18n/locales/ko-KR.js +366 -366
- package/dist/cjs/i18n/locales/pt-PT.js +366 -366
- package/dist/cjs/i18n/locales/zh-CN.js +366 -366
- package/dist/cjs/inference/OxyInferenceClient.js +12 -18
- package/dist/cjs/models/interfaces.js +11 -11
- package/dist/cjs/server/OxyServer.js +86 -28
- package/dist/cjs/server/agentAccount.js +6 -2
- package/dist/cjs/server/aliaMachineCredential.js +39 -14
- package/dist/cjs/server/auth.js +5 -4
- package/dist/cjs/server/capabilityTicket.js +5 -5
- package/dist/cjs/server/index.js +2 -1
- package/dist/cjs/server/liveCapabilityTicket.js +4 -1
- package/dist/cjs/server/middleware.js +61 -42
- package/dist/cjs/server/namespaces.js +28 -7
- package/dist/cjs/server/requesterAssertion.js +36 -30
- package/dist/cjs/server/serviceTokenLifetime.js +7 -4
- package/dist/cjs/server/traffic.js +53 -14
- package/dist/cjs/server/trafficNodeHttp.js +33 -8
- package/dist/cjs/server/workloadIdentity.client.js +4 -0
- package/dist/cjs/server/workloadIdentity.js +28 -1
- package/dist/cjs/session/SessionClient.js +54 -17
- package/dist/cjs/session/accountDelegationAccess.js +4 -6
- package/dist/cjs/session/accountDialogController.js +1 -1
- package/dist/cjs/session/authStateStore.js +18 -9
- package/dist/cjs/session/identityPin.js +6 -2
- package/dist/cjs/session/refresh.js +81 -23
- package/dist/cjs/session/sharedDeviceCredential.js +28 -15
- package/dist/cjs/shared/utils/errorUtils.js +1 -1
- package/dist/cjs/shared/utils/networkUtils.js +1 -1
- package/dist/cjs/shared/utils/themeUtils.js +1 -1
- package/dist/cjs/utils/accountCacheSweep.js +3 -1
- package/dist/cjs/utils/accountUtils.js +1 -3
- package/dist/cjs/utils/asyncUtils.js +1 -1
- package/dist/cjs/utils/authHelpers.js +2 -1
- package/dist/cjs/utils/avatarUtils.js +4 -2
- package/dist/cjs/utils/cache.js +1 -1
- package/dist/cjs/utils/cacheKey.js +3 -1
- package/dist/cjs/utils/deviceManager.js +4 -4
- package/dist/cjs/utils/errorUtils.js +6 -4
- package/dist/cjs/utils/languageUtils.js +404 -57
- package/dist/cjs/utils/profileLinks.js +1 -3
- package/dist/cjs/utils/requestUtils.js +7 -1
- package/dist/cjs/utils/userIdentity.js +1 -1
- package/dist/cjs/utils/validationUtils.js +5 -6
- package/dist/esm/HttpService.js +64 -20
- package/dist/esm/api/accounts.js +11 -4
- package/dist/esm/api/agency.js +3 -1
- package/dist/esm/api/apps.js +15 -4
- package/dist/esm/api/assets.js +39 -11
- package/dist/esm/api/auth.js +28 -8
- package/dist/esm/api/billing.js +25 -8
- package/dist/esm/api/civic.js +14 -4
- package/dist/esm/api/follows.js +9 -2
- package/dist/esm/api/identity.js +31 -9
- package/dist/esm/api/nodes.js +3 -1
- package/dist/esm/api/privacy.js +8 -2
- package/dist/esm/api/session.js +15 -4
- package/dist/esm/api/store.js +15 -5
- package/dist/esm/api/topics.js +10 -3
- package/dist/esm/api/users.js +16 -4
- package/dist/esm/boot/sessionColdBoot.js +41 -14
- package/dist/esm/constants/version.js +6 -6
- package/dist/esm/crypto/identityMarker.js +3 -1
- package/dist/esm/crypto/identityProof.js +6 -1
- package/dist/esm/crypto/internal.js +1 -1
- package/dist/esm/crypto/keyManager.js +87 -32
- package/dist/esm/crypto/recoveryPhrase.js +2 -2
- package/dist/esm/crypto/signatureService.js +3 -4
- package/dist/esm/i18n/index.js +16 -3
- package/dist/esm/i18n/locales/ar-SA.js +366 -366
- package/dist/esm/i18n/locales/ca-ES.js +366 -366
- package/dist/esm/i18n/locales/de-DE.js +366 -366
- package/dist/esm/i18n/locales/en-US.js +1994 -1994
- package/dist/esm/i18n/locales/es-ES.js +1994 -1994
- package/dist/esm/i18n/locales/fr-FR.js +366 -366
- package/dist/esm/i18n/locales/it-IT.js +366 -366
- package/dist/esm/i18n/locales/ja-JP.js +366 -366
- package/dist/esm/i18n/locales/ko-KR.js +366 -366
- package/dist/esm/i18n/locales/pt-PT.js +366 -366
- package/dist/esm/i18n/locales/zh-CN.js +366 -366
- package/dist/esm/index.js +1 -1
- package/dist/esm/inference/OxyInferenceClient.js +13 -19
- package/dist/esm/models/interfaces.js +11 -11
- package/dist/esm/server/OxyServer.js +87 -29
- package/dist/esm/server/agentAccount.js +6 -2
- package/dist/esm/server/aliaMachineCredential.js +39 -14
- package/dist/esm/server/auth.js +6 -5
- package/dist/esm/server/capabilityTicket.js +6 -6
- package/dist/esm/server/index.js +5 -5
- package/dist/esm/server/liveCapabilityTicket.js +6 -3
- package/dist/esm/server/middleware.js +62 -43
- package/dist/esm/server/namespaces.js +31 -10
- package/dist/esm/server/requesterAssertion.js +36 -30
- package/dist/esm/server/serviceTokenLifetime.js +7 -4
- package/dist/esm/server/traffic.js +55 -16
- package/dist/esm/server/trafficNodeHttp.js +34 -9
- package/dist/esm/server/userInvalidation.js +1 -1
- package/dist/esm/server/workloadIdentity.client.js +3 -0
- package/dist/esm/server/workloadIdentity.js +27 -1
- package/dist/esm/session/SessionClient.js +54 -17
- package/dist/esm/session/accountDelegationAccess.js +5 -7
- package/dist/esm/session/accountDialogController.js +1 -1
- package/dist/esm/session/authStateStore.js +18 -9
- package/dist/esm/session/identityPin.js +6 -2
- package/dist/esm/session/refresh.js +82 -24
- package/dist/esm/session/sharedDeviceCredential.js +28 -15
- package/dist/esm/shared/utils/errorUtils.js +1 -1
- package/dist/esm/shared/utils/networkUtils.js +1 -1
- package/dist/esm/shared/utils/themeUtils.js +1 -1
- package/dist/esm/utils/accountCacheSweep.js +3 -1
- package/dist/esm/utils/accountUtils.js +1 -3
- package/dist/esm/utils/asyncUtils.js +1 -1
- package/dist/esm/utils/authHelpers.js +2 -1
- package/dist/esm/utils/avatarUtils.js +4 -2
- package/dist/esm/utils/cache.js +1 -1
- package/dist/esm/utils/cacheKey.js +3 -1
- package/dist/esm/utils/deviceManager.js +4 -4
- package/dist/esm/utils/errorUtils.js +6 -4
- package/dist/esm/utils/languageUtils.js +404 -57
- package/dist/esm/utils/profileLinks.js +1 -3
- package/dist/esm/utils/requestUtils.js +7 -1
- package/dist/esm/utils/userIdentity.js +1 -1
- package/dist/esm/utils/validationUtils.js +5 -6
- package/dist/types/HttpService.d.ts +1 -1
- package/dist/types/api/accounts.d.ts +1 -1
- package/dist/types/api/apps.d.ts +4 -4
- package/dist/types/crypto/internal.d.ts +1 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/server/index.d.ts +6 -6
- package/dist/types/server/workloadIdentity.client.d.ts +5 -1
- package/dist/types/server/workloadIdentity.d.ts +26 -0
- package/dist/types/session/accountDelegationAccess.d.ts +1 -1
- package/dist/types/session/index.d.ts +1 -1
- package/dist/types/utils/userIdentity.d.ts +1 -1
- package/dist/types/utils/validationUtils.d.ts +4 -5
- package/package.json +2 -3
- package/src/HttpService.ts +183 -53
- package/src/OxyServices.errors.ts +7 -3
- package/src/OxyServices.ts +6 -2
- package/src/api/accounts.ts +76 -26
- package/src/api/agency.ts +24 -9
- package/src/api/appData.ts +12 -2
- package/src/api/apps.ts +81 -31
- package/src/api/assets.ts +113 -43
- package/src/api/auth.ts +145 -77
- package/src/api/billing.ts +112 -65
- package/src/api/civic.ts +61 -19
- package/src/api/contacts.ts +4 -1
- package/src/api/devices.ts +54 -21
- package/src/api/follows.ts +88 -31
- package/src/api/identity.ts +101 -33
- package/src/api/linkedAccounts.ts +26 -8
- package/src/api/nodes.ts +18 -6
- package/src/api/notifications.ts +26 -6
- package/src/api/privacy.ts +42 -8
- package/src/api/reputation.ts +18 -7
- package/src/api/session.ts +23 -5
- package/src/api/store.ts +81 -31
- package/src/api/topics.ts +18 -6
- package/src/api/users.ts +98 -35
- package/src/boot/sessionColdBoot.ts +60 -19
- package/src/civic/payloads.ts +1 -4
- package/src/client/context.ts +6 -1
- package/src/constants/version.ts +7 -6
- package/src/crypto/aead.ts +1 -5
- package/src/crypto/ecdh.ts +1 -4
- package/src/crypto/identityDerivations.ts +5 -1
- package/src/crypto/identityMarker.ts +3 -1
- package/src/crypto/identityProof.ts +6 -1
- package/src/crypto/index.ts +11 -11
- package/src/crypto/internal.ts +6 -1
- package/src/crypto/keyManager.ts +348 -98
- package/src/crypto/recoveryPhrase.ts +14 -9
- package/src/crypto/registrationPow.ts +1 -1
- package/src/crypto/signatureService.ts +14 -6
- package/src/i18n/accountCategoryLabels.ts +1 -4
- package/src/i18n/accountRoleLabels.ts +1 -4
- package/src/i18n/index.ts +25 -7
- package/src/i18n/locales/ar-SA.ts +375 -366
- package/src/i18n/locales/ca-ES.ts +382 -366
- package/src/i18n/locales/de-DE.ts +387 -366
- package/src/i18n/locales/en-US.ts +2034 -1994
- package/src/i18n/locales/es-ES.ts +2043 -1994
- package/src/i18n/locales/fr-FR.ts +387 -366
- package/src/i18n/locales/it-IT.ts +380 -366
- package/src/i18n/locales/ja-JP.ts +380 -366
- package/src/i18n/locales/ko-KR.ts +377 -366
- package/src/i18n/locales/pt-PT.ts +383 -366
- package/src/i18n/locales/zh-CN.ts +373 -366
- package/src/i18n/trustTierLabels.ts +1 -4
- package/src/index.ts +371 -349
- package/src/inference/OxyInferenceClient.ts +784 -780
- package/src/models/interfaces.ts +30 -27
- package/src/server/OxyServer.ts +227 -68
- package/src/server/agentAccount.ts +8 -2
- package/src/server/aliaMachineCredential.ts +57 -16
- package/src/server/auth.ts +9 -5
- package/src/server/capabilityTicket.ts +30 -21
- package/src/server/index.ts +29 -6
- package/src/server/liveCapabilityTicket.ts +31 -9
- package/src/server/middleware.ts +212 -121
- package/src/server/namespaces.ts +247 -94
- package/src/server/rateLimit.ts +2 -4
- package/src/server/requesterAssertion.ts +93 -54
- package/src/server/serviceTokenLifetime.ts +9 -4
- package/src/server/traffic.ts +117 -23
- package/src/server/trafficNodeHttp.ts +61 -14
- package/src/server/userInvalidation.ts +1 -4
- package/src/server/workloadIdentity.client.ts +12 -1
- package/src/server/workloadIdentity.ts +52 -5
- package/src/session/SessionClient.ts +170 -49
- package/src/session/accountDelegationAccess.ts +24 -31
- package/src/session/accountDialogController.ts +59 -14
- package/src/session/accountSwitchTargets.ts +10 -15
- package/src/session/authStateStore.ts +121 -81
- package/src/session/deviceDirectory.ts +1 -3
- package/src/session/identityPin.ts +10 -11
- package/src/session/identitySession.ts +3 -1
- package/src/session/index.ts +87 -82
- package/src/session/refresh.ts +138 -44
- package/src/session/sessionClientHost.ts +1 -3
- package/src/session/sharedDeviceCredential.ts +35 -14
- package/src/session/socketLoader.ts +9 -2
- package/src/shared/utils/errorUtils.ts +5 -2
- package/src/shared/utils/networkUtils.ts +5 -8
- package/src/shared/utils/themeUtils.ts +2 -6
- package/src/types/buffer.d.ts +66 -21
- package/src/types/expo-secure-store.d.ts +5 -1
- package/src/utils/accountCacheSweep.ts +4 -5
- package/src/utils/accountUtils.ts +107 -108
- package/src/utils/apiUtils.ts +3 -3
- package/src/utils/asyncUtils.ts +2 -2
- package/src/utils/authHelpers.ts +10 -5
- package/src/utils/avatarUtils.ts +11 -6
- package/src/utils/cache.ts +9 -10
- package/src/utils/cacheKey.ts +5 -1
- package/src/utils/coldBoot.ts +1 -3
- package/src/utils/commonsDelivery.ts +1 -4
- package/src/utils/deviceManager.ts +15 -14
- package/src/utils/errorUtils.ts +68 -86
- package/src/utils/languageUtils.ts +407 -58
- package/src/utils/oauthPkce.ts +1 -4
- package/src/utils/platform.ts +0 -1
- package/src/utils/profileLinks.ts +2 -5
- package/src/utils/requestUtils.ts +18 -14
- package/src/utils/sessionUtils.ts +34 -33
- package/src/utils/userHandle.ts +3 -1
- package/src/utils/userIdentity.ts +1 -1
- package/src/utils/validationUtils.ts +11 -9
|
@@ -4,7 +4,7 @@ import { jwtDecode } from 'jwt-decode';
|
|
|
4
4
|
import { loadNodeCrypto } from '@oxy.so/protocol';
|
|
5
5
|
import { logger } from '../logger/index.js';
|
|
6
6
|
import { toOxyApiError } from '../OxyServices.errors.js';
|
|
7
|
-
import { OXY_SERVICE_ENVIRONMENTS } from '../utils/oxyServiceEnvironment.js';
|
|
7
|
+
import { OXY_SERVICE_ENVIRONMENTS, } from '../utils/oxyServiceEnvironment.js';
|
|
8
8
|
/** The SET `events` member Oxy uses for an account deletion. */
|
|
9
9
|
export const OXY_ACCOUNT_DELETED_EVENT_URI = 'https://oxy.so/events/account.deleted';
|
|
10
10
|
/** Why an account event token was refused. `message` never contains the token. */
|
|
@@ -46,8 +46,7 @@ class ServiceTokenClaimError extends Error {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
function isOxyServiceEnvironment(value) {
|
|
49
|
-
return (typeof value === 'string' &&
|
|
50
|
-
OXY_SERVICE_ENVIRONMENTS.includes(value));
|
|
49
|
+
return (typeof value === 'string' && OXY_SERVICE_ENVIRONMENTS.includes(value));
|
|
51
50
|
}
|
|
52
51
|
/** Build the middleware set for one server. See `OxyServer.middleware`. */
|
|
53
52
|
export function createOxyMiddleware(host) {
|
|
@@ -195,7 +194,7 @@ export function createOxyMiddleware(host) {
|
|
|
195
194
|
error: 'MISSING_TOKEN',
|
|
196
195
|
message: 'Access token required',
|
|
197
196
|
code: 'MISSING_TOKEN',
|
|
198
|
-
status: 401
|
|
197
|
+
status: 401,
|
|
199
198
|
};
|
|
200
199
|
if (onError)
|
|
201
200
|
return onError(error);
|
|
@@ -228,7 +227,7 @@ export function createOxyMiddleware(host) {
|
|
|
228
227
|
error: 'INVALID_TOKEN_FORMAT',
|
|
229
228
|
message: 'Invalid token format',
|
|
230
229
|
code: 'INVALID_TOKEN_FORMAT',
|
|
231
|
-
status: 401
|
|
230
|
+
status: 401,
|
|
232
231
|
};
|
|
233
232
|
if (onError)
|
|
234
233
|
return onError(error);
|
|
@@ -327,7 +326,12 @@ export function createOxyMiddleware(host) {
|
|
|
327
326
|
req.user = null;
|
|
328
327
|
return next();
|
|
329
328
|
}
|
|
330
|
-
const error = {
|
|
329
|
+
const error = {
|
|
330
|
+
error: 'TOKEN_EXPIRED',
|
|
331
|
+
message: 'Service token expired',
|
|
332
|
+
code: 'TOKEN_EXPIRED',
|
|
333
|
+
status: 401,
|
|
334
|
+
};
|
|
331
335
|
if (onError)
|
|
332
336
|
return onError(error);
|
|
333
337
|
return res.status(401).json(error);
|
|
@@ -339,7 +343,12 @@ export function createOxyMiddleware(host) {
|
|
|
339
343
|
reason: 'Service token is not yet valid (nbf)',
|
|
340
344
|
status: 401,
|
|
341
345
|
});
|
|
342
|
-
const error = {
|
|
346
|
+
const error = {
|
|
347
|
+
error: 'INVALID_SERVICE_TOKEN_CLAIMS',
|
|
348
|
+
message: 'Service token is not yet valid',
|
|
349
|
+
code: 'INVALID_SERVICE_TOKEN_CLAIMS',
|
|
350
|
+
status: 401,
|
|
351
|
+
};
|
|
343
352
|
if (onError)
|
|
344
353
|
return onError(error);
|
|
345
354
|
return res.status(401).json(error);
|
|
@@ -356,10 +365,10 @@ export function createOxyMiddleware(host) {
|
|
|
356
365
|
!isExactNonEmptyServiceClaim(decoded.appName) ||
|
|
357
366
|
!isExactNonEmptyServiceClaim(credentialId) ||
|
|
358
367
|
!isExactNonEmptyServiceClaim(ownerAccountId) ||
|
|
359
|
-
!isOxyServiceEnvironment(environment)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
368
|
+
!isOxyServiceEnvironment(environment) ||
|
|
369
|
+
!Array.isArray(decoded.scopes) ||
|
|
370
|
+
!decoded.scopes.every((scope) => typeof scope === 'string' && scope.length > 0 && scope === scope.trim()) ||
|
|
371
|
+
new Set(decoded.scopes).size !== decoded.scopes.length) {
|
|
363
372
|
recordRefusal(req, {
|
|
364
373
|
code: 'INVALID_SERVICE_TOKEN',
|
|
365
374
|
stage: 'service-token',
|
|
@@ -373,7 +382,9 @@ export function createOxyMiddleware(host) {
|
|
|
373
382
|
!isExactNonEmptyServiceClaim(ownerAccountId) ? 'ownerAccountId' : null,
|
|
374
383
|
!isOxyServiceEnvironment(environment) ? 'environment' : null,
|
|
375
384
|
!Array.isArray(decoded.scopes) ? 'scopes' : null,
|
|
376
|
-
]
|
|
385
|
+
]
|
|
386
|
+
.filter((field) => field !== null)
|
|
387
|
+
.join(', ') || 'scopes'})`,
|
|
377
388
|
status: 401,
|
|
378
389
|
});
|
|
379
390
|
if (optional) {
|
|
@@ -381,7 +392,12 @@ export function createOxyMiddleware(host) {
|
|
|
381
392
|
req.user = null;
|
|
382
393
|
return next();
|
|
383
394
|
}
|
|
384
|
-
const error = {
|
|
395
|
+
const error = {
|
|
396
|
+
error: 'INVALID_SERVICE_TOKEN',
|
|
397
|
+
message: 'Invalid service token: missing required claims',
|
|
398
|
+
code: 'INVALID_SERVICE_TOKEN',
|
|
399
|
+
status: 401,
|
|
400
|
+
};
|
|
385
401
|
if (onError)
|
|
386
402
|
return onError(error);
|
|
387
403
|
return res.status(401).json(error);
|
|
@@ -416,8 +432,10 @@ export function createOxyMiddleware(host) {
|
|
|
416
432
|
// `req.userId = oxyUserId` would let any service impersonate
|
|
417
433
|
// any user simply by setting the header.
|
|
418
434
|
const grant = await host.verifyActingAs(appId, oxyUserId, {
|
|
419
|
-
cache: false,
|
|
420
|
-
|
|
435
|
+
cache: false,
|
|
436
|
+
credentialId: decoded.credentialId,
|
|
437
|
+
ownerAccountId: decoded.ownerAccountId,
|
|
438
|
+
environment: decoded.environment,
|
|
421
439
|
});
|
|
422
440
|
if (!grant || !grant.authorized) {
|
|
423
441
|
logger.warn('[oxy.auth] Service token rejected — no delegation grant', {
|
|
@@ -497,7 +515,7 @@ export function createOxyMiddleware(host) {
|
|
|
497
515
|
error: 'INVALID_TOKEN_PAYLOAD',
|
|
498
516
|
message: 'Token missing user ID',
|
|
499
517
|
code: 'INVALID_TOKEN_PAYLOAD',
|
|
500
|
-
status: 401
|
|
518
|
+
status: 401,
|
|
501
519
|
};
|
|
502
520
|
if (onError)
|
|
503
521
|
return onError(error);
|
|
@@ -521,7 +539,7 @@ export function createOxyMiddleware(host) {
|
|
|
521
539
|
error: 'TOKEN_EXPIRED',
|
|
522
540
|
message: 'Token expired',
|
|
523
541
|
code: 'TOKEN_EXPIRED',
|
|
524
|
-
status: 401
|
|
542
|
+
status: 401,
|
|
525
543
|
};
|
|
526
544
|
if (onError)
|
|
527
545
|
return onError(error);
|
|
@@ -549,7 +567,7 @@ export function createOxyMiddleware(host) {
|
|
|
549
567
|
error: 'SESSION_REQUIRED',
|
|
550
568
|
message: 'Access token is not bound to a session',
|
|
551
569
|
code: 'SESSION_REQUIRED',
|
|
552
|
-
status: 401
|
|
570
|
+
status: 401,
|
|
553
571
|
};
|
|
554
572
|
if (onError)
|
|
555
573
|
return onError(error);
|
|
@@ -577,7 +595,7 @@ export function createOxyMiddleware(host) {
|
|
|
577
595
|
error: 'INVALID_SESSION',
|
|
578
596
|
message: 'Session invalid or expired',
|
|
579
597
|
code: 'INVALID_SESSION',
|
|
580
|
-
status: 401
|
|
598
|
+
status: 401,
|
|
581
599
|
};
|
|
582
600
|
if (onError)
|
|
583
601
|
return onError(error);
|
|
@@ -601,7 +619,7 @@ export function createOxyMiddleware(host) {
|
|
|
601
619
|
error: 'INVALID_SESSION',
|
|
602
620
|
message: 'Session did not resolve to a usable identity',
|
|
603
621
|
code: 'INVALID_SESSION',
|
|
604
|
-
status: 401
|
|
622
|
+
status: 401,
|
|
605
623
|
};
|
|
606
624
|
if (onError)
|
|
607
625
|
return onError(error);
|
|
@@ -632,7 +650,7 @@ export function createOxyMiddleware(host) {
|
|
|
632
650
|
error: 'SESSION_USER_MISMATCH',
|
|
633
651
|
message: 'Token user does not match the session',
|
|
634
652
|
code: 'SESSION_USER_MISMATCH',
|
|
635
|
-
status: 401
|
|
653
|
+
status: 401,
|
|
636
654
|
};
|
|
637
655
|
if (onError)
|
|
638
656
|
return onError(error);
|
|
@@ -667,7 +685,7 @@ export function createOxyMiddleware(host) {
|
|
|
667
685
|
error: 'SESSION_ACTOR_MISMATCH',
|
|
668
686
|
message: 'Session actor does not match the session',
|
|
669
687
|
code: 'SESSION_ACTOR_MISMATCH',
|
|
670
|
-
status: 401
|
|
688
|
+
status: 401,
|
|
671
689
|
};
|
|
672
690
|
if (onError)
|
|
673
691
|
return onError(error);
|
|
@@ -680,7 +698,9 @@ export function createOxyMiddleware(host) {
|
|
|
680
698
|
// Session validation already returned the full user, so `loadUser`
|
|
681
699
|
// costs no extra round-trip.
|
|
682
700
|
// One id source: the one `getUserIdentityId` validated (`id`, else `_id`).
|
|
683
|
-
req.user = loadUser
|
|
701
|
+
req.user = loadUser
|
|
702
|
+
? { ...validationResult.user, id: validatedUserId }
|
|
703
|
+
: { id: validatedUserId };
|
|
684
704
|
if (debug) {
|
|
685
705
|
logger.debug(`[oxy.auth] OK user=${validatedUserId} session=${sessionId}`, {
|
|
686
706
|
component: 'auth',
|
|
@@ -714,7 +734,7 @@ export function createOxyMiddleware(host) {
|
|
|
714
734
|
error: 'SESSION_VALIDATION_ERROR',
|
|
715
735
|
message: 'Session validation failed',
|
|
716
736
|
code: 'SESSION_VALIDATION_ERROR',
|
|
717
|
-
status: 401
|
|
737
|
+
status: 401,
|
|
718
738
|
};
|
|
719
739
|
if (onError)
|
|
720
740
|
return onError(error);
|
|
@@ -996,16 +1016,16 @@ function parseServiceTokenJwks(value) {
|
|
|
996
1016
|
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
997
1017
|
throw new ServiceTokenSignatureError('Oxy JWKS contains a malformed key');
|
|
998
1018
|
const key = value;
|
|
999
|
-
if (key.kty !== 'OKP'
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1019
|
+
if (key.kty !== 'OKP' ||
|
|
1020
|
+
key.crv !== 'Ed25519' ||
|
|
1021
|
+
typeof key.x !== 'string' ||
|
|
1022
|
+
key.x.length === 0 ||
|
|
1023
|
+
key.use !== 'sig' ||
|
|
1024
|
+
key.alg !== 'EdDSA' ||
|
|
1025
|
+
typeof key.kid !== 'string' ||
|
|
1026
|
+
!/^[A-Za-z0-9._-]{1,128}$/.test(key.kid) ||
|
|
1027
|
+
Object.prototype.hasOwnProperty.call(key, 'd') ||
|
|
1028
|
+
result.has(key.kid))
|
|
1009
1029
|
throw new ServiceTokenSignatureError('Oxy JWKS contains an unsupported or duplicate key');
|
|
1010
1030
|
const x = Buffer.from(key.x, 'base64url');
|
|
1011
1031
|
if (x.length !== 32 || x.toString('base64url') !== key.x) {
|
|
@@ -1086,11 +1106,11 @@ async function verifyServiceTokenSignature(token, options) {
|
|
|
1086
1106
|
throw new ServiceTokenStructureError('Service token has empty segment');
|
|
1087
1107
|
}
|
|
1088
1108
|
const header = parseJsonSegment(headerB64);
|
|
1089
|
-
if (Object.keys(header).length !== 3
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1109
|
+
if (Object.keys(header).length !== 3 ||
|
|
1110
|
+
header.alg !== 'EdDSA' ||
|
|
1111
|
+
header.typ !== 'JWT' ||
|
|
1112
|
+
typeof header.kid !== 'string' ||
|
|
1113
|
+
!/^[A-Za-z0-9._-]{1,128}$/.test(header.kid))
|
|
1094
1114
|
throw new ServiceTokenStructureError('Service token JOSE header is not supported');
|
|
1095
1115
|
const jwk = await resolveServiceTokenPublicKey(header.kid, options.jwksUrl, options.cache);
|
|
1096
1116
|
let publicKey;
|
|
@@ -1134,13 +1154,12 @@ function readStringClaim(value) {
|
|
|
1134
1154
|
/**
|
|
1135
1155
|
* Resolve the canonical user id from a validated session's user object.
|
|
1136
1156
|
*
|
|
1137
|
-
* The API serializer emits `id`, but some upstream shapes carry
|
|
1138
|
-
*
|
|
1157
|
+
* The API serializer emits `id`, but some upstream shapes carry a raw `_id`
|
|
1158
|
+
* instead. We accept either, but only a non-empty string — anything else
|
|
1139
1159
|
* means the validated identity is unusable and the caller must reject.
|
|
1140
1160
|
*/
|
|
1141
1161
|
function getUserIdentityId(user) {
|
|
1142
|
-
const candidate = user.id
|
|
1143
|
-
?? user._id;
|
|
1162
|
+
const candidate = user.id ?? user._id;
|
|
1144
1163
|
return typeof candidate === 'string' && candidate.length > 0 ? candidate : null;
|
|
1145
1164
|
}
|
|
1146
1165
|
/**
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* `server.assets.metadataByIds(...)` sits beside every client method of
|
|
5
5
|
* `oxy.assets`.
|
|
6
6
|
*/
|
|
7
|
-
import { capabilityTicketRequestSchema, foregroundExecutionAuthorizationInputSchema } from '@oxy.so/contracts';
|
|
7
|
+
import { capabilityTicketRequestSchema, foregroundExecutionAuthorizationInputSchema, } from '@oxy.so/contracts';
|
|
8
8
|
import { AssetsApi } from '../api/assets.js';
|
|
9
9
|
import { NotificationsApi } from '../api/notifications.js';
|
|
10
10
|
import { LinkedAccountsApi } from '../api/linkedAccounts.js';
|
|
11
|
-
import { AgencyApi } from '../api/agency.js';
|
|
11
|
+
import { AgencyApi, } from '../api/agency.js';
|
|
12
12
|
import { ReputationApi } from '../api/reputation.js';
|
|
13
13
|
import { AppsApi } from '../api/apps.js';
|
|
14
|
-
import { isOxyAliaMachinePrincipal } from './aliaMachineCredential.js';
|
|
14
|
+
import { isOxyAliaMachinePrincipal, } from './aliaMachineCredential.js';
|
|
15
15
|
import { ServiceAssetMetadataError, ServiceLinkedDownloadUrlError } from '../OxyServices.errors.js';
|
|
16
16
|
import { extractErrorStatus } from '../utils/errorUtils.js';
|
|
17
17
|
import { logger } from '../logger/index.js';
|
|
@@ -44,7 +44,9 @@ export class ServerAppsApi extends AppsApi {
|
|
|
44
44
|
const record = answer;
|
|
45
45
|
if (record.active === false && Object.keys(record).length === 1)
|
|
46
46
|
return { active: false };
|
|
47
|
-
if (record.active !== true ||
|
|
47
|
+
if (record.active !== true ||
|
|
48
|
+
Object.keys(record).length !== 2 ||
|
|
49
|
+
!isOxyAliaMachinePrincipal(record.principal)) {
|
|
48
50
|
throw new Error('Invalid machine credential verdict');
|
|
49
51
|
}
|
|
50
52
|
return { active: true, principal: record.principal };
|
|
@@ -171,7 +173,9 @@ export class ServerAssetsApi extends AssetsApi {
|
|
|
171
173
|
return [];
|
|
172
174
|
const settled = await Promise.all(chunk(unique, SERVICE_ASSET_METADATA_BY_SHA_CHUNK_SIZE).map(async (part) => {
|
|
173
175
|
try {
|
|
174
|
-
const body = options.ownerUserId
|
|
176
|
+
const body = options.ownerUserId
|
|
177
|
+
? { sha256s: part, ownerUserId: options.ownerUserId }
|
|
178
|
+
: { sha256s: part };
|
|
175
179
|
const entries = await lane(this.ctx).request('POST', '/assets/service/by-sha256', body);
|
|
176
180
|
return Array.isArray(entries) ? entries : [];
|
|
177
181
|
}
|
|
@@ -211,7 +215,14 @@ export class ServerLinkedAccountsApi extends LinkedAccountsApi {
|
|
|
211
215
|
}
|
|
212
216
|
function agencyAuthorityOptions(signal) {
|
|
213
217
|
signal?.throwIfAborted();
|
|
214
|
-
return {
|
|
218
|
+
return {
|
|
219
|
+
cache: false,
|
|
220
|
+
deduplicate: false,
|
|
221
|
+
retry: false,
|
|
222
|
+
skipAuth: true,
|
|
223
|
+
timeout: 5000,
|
|
224
|
+
...(signal ? { signal } : {}),
|
|
225
|
+
};
|
|
215
226
|
}
|
|
216
227
|
export class ServerAgencyApi extends AgencyApi {
|
|
217
228
|
/** Live registry discovery with this service's existing capability scopes. */
|
|
@@ -249,15 +260,25 @@ export class ServerAgencyApi extends AgencyApi {
|
|
|
249
260
|
options.signal?.throwIfAborted();
|
|
250
261
|
if (!authorizationId || authorizationId.length > 255 || /\s/.test(authorizationId))
|
|
251
262
|
throw new Error('An execution authorization id is required');
|
|
252
|
-
if (!options.requesterToken ||
|
|
263
|
+
if (!options.requesterToken ||
|
|
264
|
+
options.requesterToken.length > 16384 ||
|
|
265
|
+
/\s/.test(options.requesterToken))
|
|
253
266
|
throw new Error('A requester bearer is required');
|
|
254
|
-
await this.ctx.request('DELETE', `/capabilities/execution-authorizations/${encodeURIComponent(authorizationId)}`, undefined, {
|
|
267
|
+
await this.ctx.request('DELETE', `/capabilities/execution-authorizations/${encodeURIComponent(authorizationId)}`, undefined, {
|
|
268
|
+
...agencyAuthorityOptions(options.signal),
|
|
269
|
+
headers: { Authorization: `Bearer ${options.requesterToken}` },
|
|
270
|
+
});
|
|
255
271
|
}
|
|
256
272
|
/** Independent service proof plus present requester bearer, sent only to Oxy. */
|
|
257
273
|
async createForegroundExecutionAuthorization(input, options) {
|
|
258
|
-
if (options.requesterToken === '' ||
|
|
274
|
+
if (options.requesterToken === '' ||
|
|
275
|
+
options.requesterToken.length > 16384 ||
|
|
276
|
+
/\s/.test(options.requesterToken))
|
|
259
277
|
throw new Error('A requester bearer is required');
|
|
260
|
-
const result = await lane(this.ctx).request('POST', '/capabilities/foreground-execution-authorizations', {
|
|
278
|
+
const result = await lane(this.ctx).request('POST', '/capabilities/foreground-execution-authorizations', {
|
|
279
|
+
...foregroundExecutionAuthorizationInputSchema.parse(input),
|
|
280
|
+
subjectToken: options.requesterToken,
|
|
281
|
+
}, agencyAuthorityOptions(options.signal));
|
|
261
282
|
return result.authorization;
|
|
262
283
|
}
|
|
263
284
|
/**
|
|
@@ -25,7 +25,8 @@ const KEY_ID_PATTERN = /^[A-Za-z0-9._-]{1,128}$/;
|
|
|
25
25
|
const BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
26
26
|
const MAX_ASSERTION_BYTES = 4096;
|
|
27
27
|
const identifier = z.string().min(1).max(128);
|
|
28
|
-
export const oxyRequesterAssertionClaimsSchema = z
|
|
28
|
+
export const oxyRequesterAssertionClaimsSchema = z
|
|
29
|
+
.object({
|
|
29
30
|
iss: z.string().min(1).max(256),
|
|
30
31
|
aud: identifier,
|
|
31
32
|
/** The requester account. */
|
|
@@ -39,7 +40,8 @@ export const oxyRequesterAssertionClaimsSchema = z.object({
|
|
|
39
40
|
cid: identifier,
|
|
40
41
|
/** The one native agent this requester may reach with it. */
|
|
41
42
|
agentId: identifier,
|
|
42
|
-
})
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
43
45
|
export class OxyRequesterAssertionError extends Error {
|
|
44
46
|
constructor(code) {
|
|
45
47
|
super(`Requester assertion rejected: ${code}`);
|
|
@@ -71,7 +73,8 @@ function decodeObject(segment) {
|
|
|
71
73
|
/** Signs an assertion. Oxy is the only intended caller. */
|
|
72
74
|
export function signOxyRequesterAssertion(claims, signing) {
|
|
73
75
|
const parsed = oxyRequesterAssertionClaimsSchema.parse(claims);
|
|
74
|
-
if (parsed.exp <= parsed.iat ||
|
|
76
|
+
if (parsed.exp <= parsed.iat ||
|
|
77
|
+
parsed.exp - parsed.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS) {
|
|
75
78
|
throw new OxyRequesterAssertionError('ttl_exceeded');
|
|
76
79
|
}
|
|
77
80
|
if (!KEY_ID_PATTERN.test(signing.keyId))
|
|
@@ -92,11 +95,11 @@ export function readOxyRequesterAssertionKeyId(token) {
|
|
|
92
95
|
throw new OxyRequesterAssertionError('malformed');
|
|
93
96
|
}
|
|
94
97
|
const header = decodeObject(segments[0]);
|
|
95
|
-
if (Object.keys(header).length !== 3
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
if (Object.keys(header).length !== 3 ||
|
|
99
|
+
header.alg !== ALGORITHM ||
|
|
100
|
+
header.typ !== OXY_REQUESTER_ASSERTION_TYPE ||
|
|
101
|
+
typeof header.kid !== 'string' ||
|
|
102
|
+
!KEY_ID_PATTERN.test(header.kid)) {
|
|
100
103
|
throw new OxyRequesterAssertionError('malformed');
|
|
101
104
|
}
|
|
102
105
|
return header.kid;
|
|
@@ -135,7 +138,8 @@ export function verifyOxyRequesterAssertion(token, options) {
|
|
|
135
138
|
throw new OxyRequesterAssertionError('not_yet_valid');
|
|
136
139
|
if (claims.exp <= now)
|
|
137
140
|
throw new OxyRequesterAssertionError('expired');
|
|
138
|
-
if (claims.exp <= claims.iat ||
|
|
141
|
+
if (claims.exp <= claims.iat ||
|
|
142
|
+
claims.exp - claims.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS) {
|
|
139
143
|
throw new OxyRequesterAssertionError('ttl_exceeded');
|
|
140
144
|
}
|
|
141
145
|
return claims;
|
|
@@ -159,15 +163,15 @@ function parseJwks(value) {
|
|
|
159
163
|
if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
|
|
160
164
|
throw new Error('JWKS key is malformed');
|
|
161
165
|
const key = entry;
|
|
162
|
-
if (key.kty !== 'OKP'
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
if (key.kty !== 'OKP' ||
|
|
167
|
+
key.crv !== 'Ed25519' ||
|
|
168
|
+
key.alg !== 'EdDSA' ||
|
|
169
|
+
key.use !== 'sig' ||
|
|
170
|
+
typeof key.x !== 'string' ||
|
|
171
|
+
typeof key.kid !== 'string' ||
|
|
172
|
+
!KEY_ID_PATTERN.test(key.kid) ||
|
|
173
|
+
Object.prototype.hasOwnProperty.call(key, 'd') ||
|
|
174
|
+
result.has(key.kid))
|
|
171
175
|
throw new Error('JWKS key is unsupported');
|
|
172
176
|
const x = Buffer.from(key.x, 'base64url');
|
|
173
177
|
if (x.length !== 32 || x.toString('base64url') !== key.x)
|
|
@@ -243,7 +247,9 @@ function reject(req, res, options, status, error, code) {
|
|
|
243
247
|
status,
|
|
244
248
|
applicationId: req.serviceApp?.appId ?? null,
|
|
245
249
|
});
|
|
246
|
-
res
|
|
250
|
+
res
|
|
251
|
+
.status(status)
|
|
252
|
+
.json({ error, code, message: 'The requester assertion was not accepted', status });
|
|
247
253
|
}
|
|
248
254
|
function singleHeader(req, name) {
|
|
249
255
|
const value = req.headers[name];
|
|
@@ -267,8 +273,8 @@ export function createOxyRequesterAssertionAuth(introspector, options) {
|
|
|
267
273
|
let resolvePublicKey = options.resolvePublicKey;
|
|
268
274
|
const resolver = () => {
|
|
269
275
|
resolvePublicKey ?? (resolvePublicKey = createOxyJwksKeyResolver({
|
|
270
|
-
jwksUrl: options.jwksUrl
|
|
271
|
-
|
|
276
|
+
jwksUrl: options.jwksUrl ??
|
|
277
|
+
new URL('/capabilities/.well-known/jwks.json', introspector.baseURL).toString(),
|
|
272
278
|
}));
|
|
273
279
|
return resolvePublicKey;
|
|
274
280
|
};
|
|
@@ -291,9 +297,9 @@ export function createOxyRequesterAssertionAuth(introspector, options) {
|
|
|
291
297
|
// One identity channel per request. An offline delegation header or an
|
|
292
298
|
// identity some earlier middleware attached must never be combined with, or
|
|
293
299
|
// silently replaced by, a present-requester assertion.
|
|
294
|
-
if (req.headers['x-oxy-user-id'] !== undefined
|
|
295
|
-
|
|
296
|
-
|
|
300
|
+
if (req.headers['x-oxy-user-id'] !== undefined ||
|
|
301
|
+
request.serviceActingAs !== undefined ||
|
|
302
|
+
(request.userId !== undefined && request.userId !== null)) {
|
|
297
303
|
reject(req, res, options, 400, 'REQUESTER_ASSERTION_CONFLICT', 'identity_conflict');
|
|
298
304
|
return;
|
|
299
305
|
}
|
|
@@ -329,12 +335,12 @@ export function createOxyRequesterAssertionAuth(introspector, options) {
|
|
|
329
335
|
reject(req, res, options, 503, 'REQUESTER_ASSERTION_UNAVAILABLE', 'introspection_unavailable');
|
|
330
336
|
return;
|
|
331
337
|
}
|
|
332
|
-
if (introspection.active !== true
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
if (introspection.active !== true ||
|
|
339
|
+
introspection.requesterAccountId !== claims.sub ||
|
|
340
|
+
introspection.agentId !== claims.agentId ||
|
|
341
|
+
introspection.applicationId !== claims.azp ||
|
|
342
|
+
introspection.credentialId !== claims.cid ||
|
|
343
|
+
introspection.jti !== claims.jti) {
|
|
338
344
|
reject(req, res, options, 401, 'REQUESTER_ASSERTION_INACTIVE', 'inactive');
|
|
339
345
|
return;
|
|
340
346
|
}
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
export const OXY_SERVICE_TOKEN_MAX_LIFETIME_SECONDS = 300;
|
|
3
3
|
/** Call only after signature verification; cache checks alone confer no authority. */
|
|
4
4
|
export function hasBoundedServiceTokenLifetime(claims, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
5
|
-
return typeof claims.iat === 'number' &&
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
return (typeof claims.iat === 'number' &&
|
|
6
|
+
Number.isSafeInteger(claims.iat) &&
|
|
7
|
+
typeof claims.exp === 'number' &&
|
|
8
|
+
Number.isSafeInteger(claims.exp) &&
|
|
9
|
+
claims.iat <= nowSeconds &&
|
|
10
|
+
claims.exp > claims.iat &&
|
|
11
|
+
claims.exp - claims.iat <= OXY_SERVICE_TOKEN_MAX_LIFETIME_SECONDS);
|
|
9
12
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { observeNodeHttp, withoutNodeHttpObservation } from './trafficNodeHttp.js';
|
|
2
|
-
import { observeTrafficSocket, observeTrafficWebSocket } from '@oxy.so/telemetry/socket';
|
|
2
|
+
import { observeTrafficSocket, observeTrafficWebSocket, } from '@oxy.so/telemetry/socket';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { OxyServer } from './OxyServer.js';
|
|
5
5
|
// Static, like `OxyServer`'s own import: this module is
|
|
@@ -8,7 +8,7 @@ import { OxyServer } from './OxyServer.js';
|
|
|
8
8
|
// server-only and the server barrel already exports `./workloadIdentity`, so
|
|
9
9
|
// there is nothing here for a phone to load.
|
|
10
10
|
import { canAttestWorkloadIdentity } from './workloadIdentity.js';
|
|
11
|
-
import { createTrafficCollector, instrumentTrafficFetch, trafficMiddleware, resolveOxyServiceEndpoint, infrastructureLocation } from '@oxy.so/telemetry/collector';
|
|
11
|
+
import { createTrafficCollector, instrumentTrafficFetch, trafficMiddleware, resolveOxyServiceEndpoint, infrastructureLocation, } from '@oxy.so/telemetry/collector';
|
|
12
12
|
/** Install once per service process, before registering HTTP routes. */
|
|
13
13
|
export function createEcosystemTraffic(options) {
|
|
14
14
|
const region = options.region ?? process.env.AWS_REGION ?? 'unknown';
|
|
@@ -55,13 +55,15 @@ export function createEcosystemTraffic(options) {
|
|
|
55
55
|
const post = async (path, data) => {
|
|
56
56
|
const token = await credential();
|
|
57
57
|
const response = await fetcher(new URL(path, baseURL), {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
60
|
+
body: JSON.stringify(data),
|
|
61
|
+
signal: AbortSignal.timeout(4000),
|
|
60
62
|
});
|
|
61
63
|
if (!response.ok)
|
|
62
64
|
throw new Error('Activity collection failed');
|
|
63
65
|
};
|
|
64
|
-
const collector = createTrafficCollector(events => post('/internal/activity', events));
|
|
66
|
+
const collector = createTrafficCollector((events) => post('/internal/activity', events));
|
|
65
67
|
let stopped = false;
|
|
66
68
|
let heartbeatWork = Promise.resolve();
|
|
67
69
|
let heartbeatPending = false;
|
|
@@ -70,7 +72,19 @@ export function createEcosystemTraffic(options) {
|
|
|
70
72
|
if (!removed && (stopped || heartbeatPending))
|
|
71
73
|
return heartbeatWork;
|
|
72
74
|
heartbeatPending = true;
|
|
73
|
-
heartbeatWork = heartbeatWork
|
|
75
|
+
heartbeatWork = heartbeatWork
|
|
76
|
+
.then(() => post('/internal/activity/infrastructure', {
|
|
77
|
+
instanceId,
|
|
78
|
+
service: options.service,
|
|
79
|
+
region,
|
|
80
|
+
...location,
|
|
81
|
+
status: options.ready?.() ? 'online' : 'unknown',
|
|
82
|
+
removed,
|
|
83
|
+
}))
|
|
84
|
+
.catch(reportError)
|
|
85
|
+
.finally(() => {
|
|
86
|
+
heartbeatPending = false;
|
|
87
|
+
});
|
|
74
88
|
return heartbeatWork;
|
|
75
89
|
};
|
|
76
90
|
const flush = () => collector.flush().catch(reportError);
|
|
@@ -82,13 +96,17 @@ export function createEcosystemTraffic(options) {
|
|
|
82
96
|
return Object.assign((...args) => withoutNodeHttpObservation(() => wrapped(...args)), fetcher);
|
|
83
97
|
};
|
|
84
98
|
const observeHttp = trafficMiddleware(collector, options.service, region);
|
|
85
|
-
const timer = setInterval(() => {
|
|
99
|
+
const timer = setInterval(() => {
|
|
100
|
+
void flush();
|
|
101
|
+
}, 2000);
|
|
86
102
|
timer.unref?.();
|
|
87
103
|
void heartbeat();
|
|
88
|
-
const heartbeatTimer = setInterval(() => {
|
|
104
|
+
const heartbeatTimer = setInterval(() => {
|
|
105
|
+
void heartbeat();
|
|
106
|
+
}, 10000);
|
|
89
107
|
heartbeatTimer.unref?.();
|
|
90
108
|
return {
|
|
91
|
-
record: flow => collector.record({ ...flow, service: options.service, region }),
|
|
109
|
+
record: (flow) => collector.record({ ...flow, service: options.service, region }),
|
|
92
110
|
middleware: observeHttp,
|
|
93
111
|
observeHttp,
|
|
94
112
|
wrapFetch,
|
|
@@ -99,8 +117,10 @@ export function createEcosystemTraffic(options) {
|
|
|
99
117
|
const original = globalThis.fetch;
|
|
100
118
|
const wrapped = wrapFetch(original);
|
|
101
119
|
globalThis.fetch = wrapped;
|
|
102
|
-
cleanups.add(() => {
|
|
103
|
-
globalThis.fetch
|
|
120
|
+
cleanups.add(() => {
|
|
121
|
+
if (globalThis.fetch === wrapped)
|
|
122
|
+
globalThis.fetch = original;
|
|
123
|
+
});
|
|
104
124
|
cleanups.add(observeNodeHttp(collector, options.service, region, options.resolveEndpoint ?? resolveOxyServiceEndpoint));
|
|
105
125
|
},
|
|
106
126
|
observeWebSocket(socket, socketOptions) {
|
|
@@ -108,7 +128,12 @@ export function createEcosystemTraffic(options) {
|
|
|
108
128
|
if (existing)
|
|
109
129
|
return existing;
|
|
110
130
|
const unobserve = observeTrafficWebSocket(socket, collector, options.service, region, socketOptions);
|
|
111
|
-
const cleanup = () => {
|
|
131
|
+
const cleanup = () => {
|
|
132
|
+
unobserve();
|
|
133
|
+
socket.off('close', cleanup);
|
|
134
|
+
cleanups.delete(cleanup);
|
|
135
|
+
sockets.delete(socket);
|
|
136
|
+
};
|
|
112
137
|
sockets.set(socket, cleanup);
|
|
113
138
|
socket.once('close', cleanup);
|
|
114
139
|
cleanups.add(cleanup);
|
|
@@ -119,14 +144,28 @@ export function createEcosystemTraffic(options) {
|
|
|
119
144
|
if (existing)
|
|
120
145
|
return existing;
|
|
121
146
|
const unobserve = observeTrafficSocket(socket, collector, options.service, region, socketOptions);
|
|
122
|
-
const cleanup = () => {
|
|
147
|
+
const cleanup = () => {
|
|
148
|
+
unobserve();
|
|
149
|
+
socket.off('disconnect', cleanup);
|
|
150
|
+
cleanups.delete(cleanup);
|
|
151
|
+
sockets.delete(socket);
|
|
152
|
+
};
|
|
123
153
|
sockets.set(socket, cleanup);
|
|
124
154
|
socket.once('disconnect', cleanup);
|
|
125
155
|
cleanups.add(cleanup);
|
|
126
156
|
return cleanup;
|
|
127
157
|
},
|
|
128
|
-
async stop() {
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
async stop() {
|
|
159
|
+
if (stopped)
|
|
160
|
+
return;
|
|
161
|
+
stopped = true;
|
|
162
|
+
for (const cleanup of cleanups)
|
|
163
|
+
cleanup();
|
|
164
|
+
cleanups.clear();
|
|
165
|
+
clearInterval(timer);
|
|
166
|
+
clearInterval(heartbeatTimer);
|
|
167
|
+
await flush();
|
|
168
|
+
await heartbeat(true);
|
|
169
|
+
},
|
|
131
170
|
};
|
|
132
171
|
}
|