@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.
Files changed (282) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/HttpService.js +63 -19
  3. package/dist/cjs/api/accounts.js +10 -3
  4. package/dist/cjs/api/agency.js +3 -1
  5. package/dist/cjs/api/apps.js +15 -4
  6. package/dist/cjs/api/assets.js +39 -11
  7. package/dist/cjs/api/auth.js +28 -8
  8. package/dist/cjs/api/billing.js +24 -7
  9. package/dist/cjs/api/civic.js +14 -4
  10. package/dist/cjs/api/follows.js +9 -2
  11. package/dist/cjs/api/identity.js +31 -9
  12. package/dist/cjs/api/nodes.js +3 -1
  13. package/dist/cjs/api/privacy.js +8 -2
  14. package/dist/cjs/api/session.js +15 -4
  15. package/dist/cjs/api/store.js +15 -5
  16. package/dist/cjs/api/topics.js +10 -3
  17. package/dist/cjs/api/users.js +16 -4
  18. package/dist/cjs/boot/sessionColdBoot.js +40 -13
  19. package/dist/cjs/constants/version.js +6 -6
  20. package/dist/cjs/crypto/identityMarker.js +3 -1
  21. package/dist/cjs/crypto/identityProof.js +6 -1
  22. package/dist/cjs/crypto/keyManager.js +87 -32
  23. package/dist/cjs/crypto/recoveryPhrase.js +2 -2
  24. package/dist/cjs/crypto/signatureService.js +3 -4
  25. package/dist/cjs/i18n/index.js +16 -3
  26. package/dist/cjs/i18n/locales/ar-SA.js +366 -366
  27. package/dist/cjs/i18n/locales/ca-ES.js +366 -366
  28. package/dist/cjs/i18n/locales/de-DE.js +366 -366
  29. package/dist/cjs/i18n/locales/en-US.js +1994 -1994
  30. package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
  31. package/dist/cjs/i18n/locales/fr-FR.js +366 -366
  32. package/dist/cjs/i18n/locales/it-IT.js +366 -366
  33. package/dist/cjs/i18n/locales/ja-JP.js +366 -366
  34. package/dist/cjs/i18n/locales/ko-KR.js +366 -366
  35. package/dist/cjs/i18n/locales/pt-PT.js +366 -366
  36. package/dist/cjs/i18n/locales/zh-CN.js +366 -366
  37. package/dist/cjs/inference/OxyInferenceClient.js +12 -18
  38. package/dist/cjs/models/interfaces.js +11 -11
  39. package/dist/cjs/server/OxyServer.js +86 -28
  40. package/dist/cjs/server/agentAccount.js +6 -2
  41. package/dist/cjs/server/aliaMachineCredential.js +39 -14
  42. package/dist/cjs/server/auth.js +5 -4
  43. package/dist/cjs/server/capabilityTicket.js +5 -5
  44. package/dist/cjs/server/index.js +2 -1
  45. package/dist/cjs/server/liveCapabilityTicket.js +4 -1
  46. package/dist/cjs/server/middleware.js +61 -42
  47. package/dist/cjs/server/namespaces.js +28 -7
  48. package/dist/cjs/server/requesterAssertion.js +36 -30
  49. package/dist/cjs/server/serviceTokenLifetime.js +7 -4
  50. package/dist/cjs/server/traffic.js +53 -14
  51. package/dist/cjs/server/trafficNodeHttp.js +33 -8
  52. package/dist/cjs/server/workloadIdentity.client.js +4 -0
  53. package/dist/cjs/server/workloadIdentity.js +28 -1
  54. package/dist/cjs/session/SessionClient.js +54 -17
  55. package/dist/cjs/session/accountDelegationAccess.js +4 -6
  56. package/dist/cjs/session/accountDialogController.js +1 -1
  57. package/dist/cjs/session/authStateStore.js +18 -9
  58. package/dist/cjs/session/identityPin.js +6 -2
  59. package/dist/cjs/session/refresh.js +81 -23
  60. package/dist/cjs/session/sharedDeviceCredential.js +28 -15
  61. package/dist/cjs/shared/utils/errorUtils.js +1 -1
  62. package/dist/cjs/shared/utils/networkUtils.js +1 -1
  63. package/dist/cjs/shared/utils/themeUtils.js +1 -1
  64. package/dist/cjs/utils/accountCacheSweep.js +3 -1
  65. package/dist/cjs/utils/accountUtils.js +1 -3
  66. package/dist/cjs/utils/asyncUtils.js +1 -1
  67. package/dist/cjs/utils/authHelpers.js +2 -1
  68. package/dist/cjs/utils/avatarUtils.js +4 -2
  69. package/dist/cjs/utils/cache.js +1 -1
  70. package/dist/cjs/utils/cacheKey.js +3 -1
  71. package/dist/cjs/utils/deviceManager.js +4 -4
  72. package/dist/cjs/utils/errorUtils.js +6 -4
  73. package/dist/cjs/utils/languageUtils.js +404 -57
  74. package/dist/cjs/utils/profileLinks.js +1 -3
  75. package/dist/cjs/utils/requestUtils.js +7 -1
  76. package/dist/cjs/utils/userIdentity.js +1 -1
  77. package/dist/cjs/utils/validationUtils.js +5 -6
  78. package/dist/esm/HttpService.js +64 -20
  79. package/dist/esm/api/accounts.js +11 -4
  80. package/dist/esm/api/agency.js +3 -1
  81. package/dist/esm/api/apps.js +15 -4
  82. package/dist/esm/api/assets.js +39 -11
  83. package/dist/esm/api/auth.js +28 -8
  84. package/dist/esm/api/billing.js +25 -8
  85. package/dist/esm/api/civic.js +14 -4
  86. package/dist/esm/api/follows.js +9 -2
  87. package/dist/esm/api/identity.js +31 -9
  88. package/dist/esm/api/nodes.js +3 -1
  89. package/dist/esm/api/privacy.js +8 -2
  90. package/dist/esm/api/session.js +15 -4
  91. package/dist/esm/api/store.js +15 -5
  92. package/dist/esm/api/topics.js +10 -3
  93. package/dist/esm/api/users.js +16 -4
  94. package/dist/esm/boot/sessionColdBoot.js +41 -14
  95. package/dist/esm/constants/version.js +6 -6
  96. package/dist/esm/crypto/identityMarker.js +3 -1
  97. package/dist/esm/crypto/identityProof.js +6 -1
  98. package/dist/esm/crypto/internal.js +1 -1
  99. package/dist/esm/crypto/keyManager.js +87 -32
  100. package/dist/esm/crypto/recoveryPhrase.js +2 -2
  101. package/dist/esm/crypto/signatureService.js +3 -4
  102. package/dist/esm/i18n/index.js +16 -3
  103. package/dist/esm/i18n/locales/ar-SA.js +366 -366
  104. package/dist/esm/i18n/locales/ca-ES.js +366 -366
  105. package/dist/esm/i18n/locales/de-DE.js +366 -366
  106. package/dist/esm/i18n/locales/en-US.js +1994 -1994
  107. package/dist/esm/i18n/locales/es-ES.js +1994 -1994
  108. package/dist/esm/i18n/locales/fr-FR.js +366 -366
  109. package/dist/esm/i18n/locales/it-IT.js +366 -366
  110. package/dist/esm/i18n/locales/ja-JP.js +366 -366
  111. package/dist/esm/i18n/locales/ko-KR.js +366 -366
  112. package/dist/esm/i18n/locales/pt-PT.js +366 -366
  113. package/dist/esm/i18n/locales/zh-CN.js +366 -366
  114. package/dist/esm/index.js +1 -1
  115. package/dist/esm/inference/OxyInferenceClient.js +13 -19
  116. package/dist/esm/models/interfaces.js +11 -11
  117. package/dist/esm/server/OxyServer.js +87 -29
  118. package/dist/esm/server/agentAccount.js +6 -2
  119. package/dist/esm/server/aliaMachineCredential.js +39 -14
  120. package/dist/esm/server/auth.js +6 -5
  121. package/dist/esm/server/capabilityTicket.js +6 -6
  122. package/dist/esm/server/index.js +5 -5
  123. package/dist/esm/server/liveCapabilityTicket.js +6 -3
  124. package/dist/esm/server/middleware.js +62 -43
  125. package/dist/esm/server/namespaces.js +31 -10
  126. package/dist/esm/server/requesterAssertion.js +36 -30
  127. package/dist/esm/server/serviceTokenLifetime.js +7 -4
  128. package/dist/esm/server/traffic.js +55 -16
  129. package/dist/esm/server/trafficNodeHttp.js +34 -9
  130. package/dist/esm/server/userInvalidation.js +1 -1
  131. package/dist/esm/server/workloadIdentity.client.js +3 -0
  132. package/dist/esm/server/workloadIdentity.js +27 -1
  133. package/dist/esm/session/SessionClient.js +54 -17
  134. package/dist/esm/session/accountDelegationAccess.js +5 -7
  135. package/dist/esm/session/accountDialogController.js +1 -1
  136. package/dist/esm/session/authStateStore.js +18 -9
  137. package/dist/esm/session/identityPin.js +6 -2
  138. package/dist/esm/session/refresh.js +82 -24
  139. package/dist/esm/session/sharedDeviceCredential.js +28 -15
  140. package/dist/esm/shared/utils/errorUtils.js +1 -1
  141. package/dist/esm/shared/utils/networkUtils.js +1 -1
  142. package/dist/esm/shared/utils/themeUtils.js +1 -1
  143. package/dist/esm/utils/accountCacheSweep.js +3 -1
  144. package/dist/esm/utils/accountUtils.js +1 -3
  145. package/dist/esm/utils/asyncUtils.js +1 -1
  146. package/dist/esm/utils/authHelpers.js +2 -1
  147. package/dist/esm/utils/avatarUtils.js +4 -2
  148. package/dist/esm/utils/cache.js +1 -1
  149. package/dist/esm/utils/cacheKey.js +3 -1
  150. package/dist/esm/utils/deviceManager.js +4 -4
  151. package/dist/esm/utils/errorUtils.js +6 -4
  152. package/dist/esm/utils/languageUtils.js +404 -57
  153. package/dist/esm/utils/profileLinks.js +1 -3
  154. package/dist/esm/utils/requestUtils.js +7 -1
  155. package/dist/esm/utils/userIdentity.js +1 -1
  156. package/dist/esm/utils/validationUtils.js +5 -6
  157. package/dist/types/HttpService.d.ts +1 -1
  158. package/dist/types/api/accounts.d.ts +1 -1
  159. package/dist/types/api/apps.d.ts +4 -4
  160. package/dist/types/crypto/internal.d.ts +1 -1
  161. package/dist/types/index.d.ts +4 -4
  162. package/dist/types/server/index.d.ts +6 -6
  163. package/dist/types/server/workloadIdentity.client.d.ts +5 -1
  164. package/dist/types/server/workloadIdentity.d.ts +26 -0
  165. package/dist/types/session/accountDelegationAccess.d.ts +1 -1
  166. package/dist/types/session/index.d.ts +1 -1
  167. package/dist/types/utils/userIdentity.d.ts +1 -1
  168. package/dist/types/utils/validationUtils.d.ts +4 -5
  169. package/package.json +2 -3
  170. package/src/HttpService.ts +183 -53
  171. package/src/OxyServices.errors.ts +7 -3
  172. package/src/OxyServices.ts +6 -2
  173. package/src/api/accounts.ts +76 -26
  174. package/src/api/agency.ts +24 -9
  175. package/src/api/appData.ts +12 -2
  176. package/src/api/apps.ts +81 -31
  177. package/src/api/assets.ts +113 -43
  178. package/src/api/auth.ts +145 -77
  179. package/src/api/billing.ts +112 -65
  180. package/src/api/civic.ts +61 -19
  181. package/src/api/contacts.ts +4 -1
  182. package/src/api/devices.ts +54 -21
  183. package/src/api/follows.ts +88 -31
  184. package/src/api/identity.ts +101 -33
  185. package/src/api/linkedAccounts.ts +26 -8
  186. package/src/api/nodes.ts +18 -6
  187. package/src/api/notifications.ts +26 -6
  188. package/src/api/privacy.ts +42 -8
  189. package/src/api/reputation.ts +18 -7
  190. package/src/api/session.ts +23 -5
  191. package/src/api/store.ts +81 -31
  192. package/src/api/topics.ts +18 -6
  193. package/src/api/users.ts +98 -35
  194. package/src/boot/sessionColdBoot.ts +60 -19
  195. package/src/civic/payloads.ts +1 -4
  196. package/src/client/context.ts +6 -1
  197. package/src/constants/version.ts +7 -6
  198. package/src/crypto/aead.ts +1 -5
  199. package/src/crypto/ecdh.ts +1 -4
  200. package/src/crypto/identityDerivations.ts +5 -1
  201. package/src/crypto/identityMarker.ts +3 -1
  202. package/src/crypto/identityProof.ts +6 -1
  203. package/src/crypto/index.ts +11 -11
  204. package/src/crypto/internal.ts +6 -1
  205. package/src/crypto/keyManager.ts +348 -98
  206. package/src/crypto/recoveryPhrase.ts +14 -9
  207. package/src/crypto/registrationPow.ts +1 -1
  208. package/src/crypto/signatureService.ts +14 -6
  209. package/src/i18n/accountCategoryLabels.ts +1 -4
  210. package/src/i18n/accountRoleLabels.ts +1 -4
  211. package/src/i18n/index.ts +25 -7
  212. package/src/i18n/locales/ar-SA.ts +375 -366
  213. package/src/i18n/locales/ca-ES.ts +382 -366
  214. package/src/i18n/locales/de-DE.ts +387 -366
  215. package/src/i18n/locales/en-US.ts +2034 -1994
  216. package/src/i18n/locales/es-ES.ts +2043 -1994
  217. package/src/i18n/locales/fr-FR.ts +387 -366
  218. package/src/i18n/locales/it-IT.ts +380 -366
  219. package/src/i18n/locales/ja-JP.ts +380 -366
  220. package/src/i18n/locales/ko-KR.ts +377 -366
  221. package/src/i18n/locales/pt-PT.ts +383 -366
  222. package/src/i18n/locales/zh-CN.ts +373 -366
  223. package/src/i18n/trustTierLabels.ts +1 -4
  224. package/src/index.ts +371 -349
  225. package/src/inference/OxyInferenceClient.ts +784 -780
  226. package/src/models/interfaces.ts +30 -27
  227. package/src/server/OxyServer.ts +227 -68
  228. package/src/server/agentAccount.ts +8 -2
  229. package/src/server/aliaMachineCredential.ts +57 -16
  230. package/src/server/auth.ts +9 -5
  231. package/src/server/capabilityTicket.ts +30 -21
  232. package/src/server/index.ts +29 -6
  233. package/src/server/liveCapabilityTicket.ts +31 -9
  234. package/src/server/middleware.ts +212 -121
  235. package/src/server/namespaces.ts +247 -94
  236. package/src/server/rateLimit.ts +2 -4
  237. package/src/server/requesterAssertion.ts +93 -54
  238. package/src/server/serviceTokenLifetime.ts +9 -4
  239. package/src/server/traffic.ts +117 -23
  240. package/src/server/trafficNodeHttp.ts +61 -14
  241. package/src/server/userInvalidation.ts +1 -4
  242. package/src/server/workloadIdentity.client.ts +12 -1
  243. package/src/server/workloadIdentity.ts +52 -5
  244. package/src/session/SessionClient.ts +170 -49
  245. package/src/session/accountDelegationAccess.ts +24 -31
  246. package/src/session/accountDialogController.ts +59 -14
  247. package/src/session/accountSwitchTargets.ts +10 -15
  248. package/src/session/authStateStore.ts +121 -81
  249. package/src/session/deviceDirectory.ts +1 -3
  250. package/src/session/identityPin.ts +10 -11
  251. package/src/session/identitySession.ts +3 -1
  252. package/src/session/index.ts +87 -82
  253. package/src/session/refresh.ts +138 -44
  254. package/src/session/sessionClientHost.ts +1 -3
  255. package/src/session/sharedDeviceCredential.ts +35 -14
  256. package/src/session/socketLoader.ts +9 -2
  257. package/src/shared/utils/errorUtils.ts +5 -2
  258. package/src/shared/utils/networkUtils.ts +5 -8
  259. package/src/shared/utils/themeUtils.ts +2 -6
  260. package/src/types/buffer.d.ts +66 -21
  261. package/src/types/expo-secure-store.d.ts +5 -1
  262. package/src/utils/accountCacheSweep.ts +4 -5
  263. package/src/utils/accountUtils.ts +107 -108
  264. package/src/utils/apiUtils.ts +3 -3
  265. package/src/utils/asyncUtils.ts +2 -2
  266. package/src/utils/authHelpers.ts +10 -5
  267. package/src/utils/avatarUtils.ts +11 -6
  268. package/src/utils/cache.ts +9 -10
  269. package/src/utils/cacheKey.ts +5 -1
  270. package/src/utils/coldBoot.ts +1 -3
  271. package/src/utils/commonsDelivery.ts +1 -4
  272. package/src/utils/deviceManager.ts +15 -14
  273. package/src/utils/errorUtils.ts +68 -86
  274. package/src/utils/languageUtils.ts +407 -58
  275. package/src/utils/oauthPkce.ts +1 -4
  276. package/src/utils/platform.ts +0 -1
  277. package/src/utils/profileLinks.ts +2 -5
  278. package/src/utils/requestUtils.ts +18 -14
  279. package/src/utils/sessionUtils.ts +34 -33
  280. package/src/utils/userHandle.ts +3 -1
  281. package/src/utils/userIdentity.ts +1 -1
  282. package/src/utils/validationUtils.ts +11 -9
@@ -37,21 +37,23 @@ const MAX_ASSERTION_BYTES = 4096;
37
37
 
38
38
  const identifier = z.string().min(1).max(128);
39
39
 
40
- export const oxyRequesterAssertionClaimsSchema = z.object({
41
- iss: z.string().min(1).max(256),
42
- aud: identifier,
43
- /** The requester account. */
44
- sub: identifier,
45
- jti: z.string().uuid(),
46
- iat: z.number().int().nonnegative(),
47
- exp: z.number().int().positive(),
48
- /** The application the assertion was minted for, and the only valid presenter. */
49
- azp: identifier,
50
- /** The exact credential of that application. */
51
- cid: identifier,
52
- /** The one native agent this requester may reach with it. */
53
- agentId: identifier,
54
- }).strict();
40
+ export const oxyRequesterAssertionClaimsSchema = z
41
+ .object({
42
+ iss: z.string().min(1).max(256),
43
+ aud: identifier,
44
+ /** The requester account. */
45
+ sub: identifier,
46
+ jti: z.string().uuid(),
47
+ iat: z.number().int().nonnegative(),
48
+ exp: z.number().int().positive(),
49
+ /** The application the assertion was minted for, and the only valid presenter. */
50
+ azp: identifier,
51
+ /** The exact credential of that application. */
52
+ cid: identifier,
53
+ /** The one native agent this requester may reach with it. */
54
+ agentId: identifier,
55
+ })
56
+ .strict();
55
57
 
56
58
  export type OxyRequesterAssertionClaims = z.infer<typeof oxyRequesterAssertionClaimsSchema>;
57
59
 
@@ -99,7 +101,10 @@ export function signOxyRequesterAssertion(
99
101
  signing: { readonly keyId: string; readonly privateKey: KeyObject },
100
102
  ): string {
101
103
  const parsed = oxyRequesterAssertionClaimsSchema.parse(claims);
102
- if (parsed.exp <= parsed.iat || parsed.exp - parsed.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS) {
104
+ if (
105
+ parsed.exp <= parsed.iat ||
106
+ parsed.exp - parsed.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS
107
+ ) {
103
108
  throw new OxyRequesterAssertionError('ttl_exceeded');
104
109
  }
105
110
  if (!KEY_ID_PATTERN.test(signing.keyId)) throw new OxyRequesterAssertionError('malformed');
@@ -120,11 +125,11 @@ export function readOxyRequesterAssertionKeyId(token: string): string {
120
125
  }
121
126
  const header = decodeObject(segments[0] as string);
122
127
  if (
123
- Object.keys(header).length !== 3
124
- || header.alg !== ALGORITHM
125
- || header.typ !== OXY_REQUESTER_ASSERTION_TYPE
126
- || typeof header.kid !== 'string'
127
- || !KEY_ID_PATTERN.test(header.kid)
128
+ Object.keys(header).length !== 3 ||
129
+ header.alg !== ALGORITHM ||
130
+ header.typ !== OXY_REQUESTER_ASSERTION_TYPE ||
131
+ typeof header.kid !== 'string' ||
132
+ !KEY_ID_PATTERN.test(header.kid)
128
133
  ) {
129
134
  throw new OxyRequesterAssertionError('malformed');
130
135
  }
@@ -148,7 +153,11 @@ export function verifyOxyRequesterAssertion(
148
153
  options: OxyRequesterAssertionVerificationOptions,
149
154
  ): OxyRequesterAssertionClaims {
150
155
  readOxyRequesterAssertionKeyId(token);
151
- const [encodedHeader, encodedPayload, encodedSignature] = token.split('.') as [string, string, string];
156
+ const [encodedHeader, encodedPayload, encodedSignature] = token.split('.') as [
157
+ string,
158
+ string,
159
+ string,
160
+ ];
152
161
  const publicKey = options.publicKey;
153
162
  if (!publicKey || publicKey.asymmetricKeyType !== 'ed25519') {
154
163
  throw new OxyRequesterAssertionError('unknown_key');
@@ -170,7 +179,10 @@ export function verifyOxyRequesterAssertion(
170
179
  if (claims.aud !== options.audience) throw new OxyRequesterAssertionError('wrong_audience');
171
180
  if (claims.iat > now + CLOCK_SKEW_SECONDS) throw new OxyRequesterAssertionError('not_yet_valid');
172
181
  if (claims.exp <= now) throw new OxyRequesterAssertionError('expired');
173
- if (claims.exp <= claims.iat || claims.exp - claims.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS) {
182
+ if (
183
+ claims.exp <= claims.iat ||
184
+ claims.exp - claims.iat > OXY_REQUESTER_ASSERTION_MAX_TTL_SECONDS
185
+ ) {
174
186
  throw new OxyRequesterAssertionError('ttl_exceeded');
175
187
  }
176
188
  return claims;
@@ -187,27 +199,35 @@ const JWKS_MAX_BYTES = 64 * 1024;
187
199
  const JWKS_MAX_KEYS = 20;
188
200
 
189
201
  function parseJwks(value: unknown): Map<string, KeyObject> {
190
- if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('JWKS is malformed');
202
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
203
+ throw new Error('JWKS is malformed');
191
204
  const keys = (value as { keys?: unknown }).keys;
192
- if (!Array.isArray(keys) || keys.length === 0 || keys.length > JWKS_MAX_KEYS) throw new Error('JWKS has an invalid key set');
205
+ if (!Array.isArray(keys) || keys.length === 0 || keys.length > JWKS_MAX_KEYS)
206
+ throw new Error('JWKS has an invalid key set');
193
207
  const result = new Map<string, KeyObject>();
194
208
  for (const entry of keys) {
195
- if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) throw new Error('JWKS key is malformed');
209
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
210
+ throw new Error('JWKS key is malformed');
196
211
  const key = entry as Record<string, unknown>;
197
212
  if (
198
- key.kty !== 'OKP'
199
- || key.crv !== 'Ed25519'
200
- || key.alg !== 'EdDSA'
201
- || key.use !== 'sig'
202
- || typeof key.x !== 'string'
203
- || typeof key.kid !== 'string'
204
- || !KEY_ID_PATTERN.test(key.kid)
205
- || Object.prototype.hasOwnProperty.call(key, 'd')
206
- || result.has(key.kid)
207
- ) throw new Error('JWKS key is unsupported');
213
+ key.kty !== 'OKP' ||
214
+ key.crv !== 'Ed25519' ||
215
+ key.alg !== 'EdDSA' ||
216
+ key.use !== 'sig' ||
217
+ typeof key.x !== 'string' ||
218
+ typeof key.kid !== 'string' ||
219
+ !KEY_ID_PATTERN.test(key.kid) ||
220
+ Object.prototype.hasOwnProperty.call(key, 'd') ||
221
+ result.has(key.kid)
222
+ )
223
+ throw new Error('JWKS key is unsupported');
208
224
  const x = Buffer.from(key.x, 'base64url');
209
- if (x.length !== 32 || x.toString('base64url') !== key.x) throw new Error('JWKS key is not Ed25519');
210
- result.set(key.kid, createPublicKey({ key: { kty: 'OKP', crv: 'Ed25519', x: key.x }, format: 'jwk' }));
225
+ if (x.length !== 32 || x.toString('base64url') !== key.x)
226
+ throw new Error('JWKS key is not Ed25519');
227
+ result.set(
228
+ key.kid,
229
+ createPublicKey({ key: { kty: 'OKP', crv: 'Ed25519', x: key.x }, format: 'jwk' }),
230
+ );
211
231
  }
212
232
  return result;
213
233
  }
@@ -248,7 +268,8 @@ export function createOxyJwksKeyResolver(
248
268
  });
249
269
  if (!response.ok) throw new Error(`JWKS returned HTTP ${response.status}`);
250
270
  const body = await response.text();
251
- if (Buffer.byteLength(body, 'utf8') > JWKS_MAX_BYTES) throw new Error('JWKS exceeds the size limit');
271
+ if (Buffer.byteLength(body, 'utf8') > JWKS_MAX_BYTES)
272
+ throw new Error('JWKS exceeds the size limit');
252
273
  keys = parseJwks(JSON.parse(body) as unknown);
253
274
  expiresAt = clock() + JWKS_CACHE_MS;
254
275
  } finally {
@@ -346,13 +367,16 @@ function reject(
346
367
  status,
347
368
  applicationId: (req as OxyAuthRequest).serviceApp?.appId ?? null,
348
369
  });
349
- res.status(status).json({ error, code, message: 'The requester assertion was not accepted', status });
370
+ res
371
+ .status(status)
372
+ .json({ error, code, message: 'The requester assertion was not accepted', status });
350
373
  }
351
374
 
352
375
  function singleHeader(req: Request, name: string): string | null | 'invalid' {
353
376
  const value = req.headers[name];
354
377
  if (value === undefined) return null;
355
- if (typeof value !== 'string' || value.length === 0 || value.length > MAX_ASSERTION_BYTES) return 'invalid';
378
+ if (typeof value !== 'string' || value.length === 0 || value.length > MAX_ASSERTION_BYTES)
379
+ return 'invalid';
356
380
  return value;
357
381
  }
358
382
 
@@ -373,8 +397,9 @@ export function createOxyRequesterAssertionAuth(
373
397
  let resolvePublicKey = options.resolvePublicKey;
374
398
  const resolver = (): ((keyId: string) => Promise<KeyObject | undefined>) => {
375
399
  resolvePublicKey ??= createOxyJwksKeyResolver({
376
- jwksUrl: options.jwksUrl
377
- ?? new URL('/capabilities/.well-known/jwks.json', introspector.baseURL).toString(),
400
+ jwksUrl:
401
+ options.jwksUrl ??
402
+ new URL('/capabilities/.well-known/jwks.json', introspector.baseURL).toString(),
378
403
  });
379
404
  return resolvePublicKey;
380
405
  };
@@ -393,16 +418,23 @@ export function createOxyRequesterAssertionAuth(
393
418
  const request = req as OxyRequesterAssertionRequest;
394
419
  const serviceApp: OxyServiceAppContext | undefined = request.serviceApp;
395
420
  if (!serviceApp) {
396
- reject(req, res, options, 401, 'REQUESTER_ASSERTION_REQUIRES_SERVICE_TOKEN', 'service_token_required');
421
+ reject(
422
+ req,
423
+ res,
424
+ options,
425
+ 401,
426
+ 'REQUESTER_ASSERTION_REQUIRES_SERVICE_TOKEN',
427
+ 'service_token_required',
428
+ );
397
429
  return;
398
430
  }
399
431
  // One identity channel per request. An offline delegation header or an
400
432
  // identity some earlier middleware attached must never be combined with, or
401
433
  // silently replaced by, a present-requester assertion.
402
434
  if (
403
- req.headers['x-oxy-user-id'] !== undefined
404
- || request.serviceActingAs !== undefined
405
- || (request.userId !== undefined && request.userId !== null)
435
+ req.headers['x-oxy-user-id'] !== undefined ||
436
+ request.serviceActingAs !== undefined ||
437
+ (request.userId !== undefined && request.userId !== null)
406
438
  ) {
407
439
  reject(req, res, options, 400, 'REQUESTER_ASSERTION_CONFLICT', 'identity_conflict');
408
440
  return;
@@ -437,17 +469,24 @@ export function createOxyRequesterAssertionAuth(
437
469
  presenter: { applicationId: serviceApp.appId, credentialId: serviceApp.credentialId },
438
470
  });
439
471
  } catch {
440
- reject(req, res, options, 503, 'REQUESTER_ASSERTION_UNAVAILABLE', 'introspection_unavailable');
472
+ reject(
473
+ req,
474
+ res,
475
+ options,
476
+ 503,
477
+ 'REQUESTER_ASSERTION_UNAVAILABLE',
478
+ 'introspection_unavailable',
479
+ );
441
480
  return;
442
481
  }
443
482
 
444
483
  if (
445
- introspection.active !== true
446
- || introspection.requesterAccountId !== claims.sub
447
- || introspection.agentId !== claims.agentId
448
- || introspection.applicationId !== claims.azp
449
- || introspection.credentialId !== claims.cid
450
- || introspection.jti !== claims.jti
484
+ introspection.active !== true ||
485
+ introspection.requesterAccountId !== claims.sub ||
486
+ introspection.agentId !== claims.agentId ||
487
+ introspection.applicationId !== claims.azp ||
488
+ introspection.credentialId !== claims.cid ||
489
+ introspection.jti !== claims.jti
451
490
  ) {
452
491
  reject(req, res, options, 401, 'REQUESTER_ASSERTION_INACTIVE', 'inactive');
453
492
  return;
@@ -6,8 +6,13 @@ export function hasBoundedServiceTokenLifetime(
6
6
  claims: { iat?: unknown; exp?: unknown },
7
7
  nowSeconds = Math.floor(Date.now() / 1000),
8
8
  ): boolean {
9
- return typeof claims.iat === 'number' && Number.isSafeInteger(claims.iat)
10
- && typeof claims.exp === 'number' && Number.isSafeInteger(claims.exp)
11
- && claims.iat <= nowSeconds && claims.exp > claims.iat
12
- && claims.exp - claims.iat <= OXY_SERVICE_TOKEN_MAX_LIFETIME_SECONDS;
9
+ return (
10
+ typeof claims.iat === 'number' &&
11
+ Number.isSafeInteger(claims.iat) &&
12
+ typeof claims.exp === 'number' &&
13
+ Number.isSafeInteger(claims.exp) &&
14
+ claims.iat <= nowSeconds &&
15
+ claims.exp > claims.iat &&
16
+ claims.exp - claims.iat <= OXY_SERVICE_TOKEN_MAX_LIFETIME_SECONDS
17
+ );
13
18
  }
@@ -1,5 +1,10 @@
1
1
  import { observeNodeHttp, withoutNodeHttpObservation } from './trafficNodeHttp';
2
- import { observeTrafficSocket, observeTrafficWebSocket, type TrafficWebSocket, type TrafficSocket } from '@oxy.so/telemetry/socket';
2
+ import {
3
+ observeTrafficSocket,
4
+ observeTrafficWebSocket,
5
+ type TrafficWebSocket,
6
+ type TrafficSocket,
7
+ } from '@oxy.so/telemetry/socket';
3
8
  import { randomUUID } from 'node:crypto';
4
9
  import type { RequestHandler } from 'express';
5
10
  import { OxyServer } from './OxyServer';
@@ -9,7 +14,15 @@ import { OxyServer } from './OxyServer';
9
14
  // server-only and the server barrel already exports `./workloadIdentity`, so
10
15
  // there is nothing here for a phone to load.
11
16
  import { canAttestWorkloadIdentity } from './workloadIdentity';
12
- import { createTrafficCollector, instrumentTrafficFetch, trafficMiddleware, type TrafficFlow, type ServiceEndpoint, resolveOxyServiceEndpoint, infrastructureLocation } from '@oxy.so/telemetry/collector';
17
+ import {
18
+ createTrafficCollector,
19
+ instrumentTrafficFetch,
20
+ trafficMiddleware,
21
+ type TrafficFlow,
22
+ type ServiceEndpoint,
23
+ resolveOxyServiceEndpoint,
24
+ infrastructureLocation,
25
+ } from '@oxy.so/telemetry/collector';
13
26
 
14
27
  export interface EcosystemTrafficOptions {
15
28
  service: string;
@@ -29,8 +42,14 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
29
42
  wrapFetch(fetcher: typeof fetch): typeof fetch;
30
43
  installFetch(): void;
31
44
  observeHttp: ReturnType<typeof trafficMiddleware>;
32
- observeSocket(socket: TrafficSocket, options?: Parameters<typeof observeTrafficSocket>[4]): () => void;
33
- observeWebSocket(socket: TrafficWebSocket, options?: Parameters<typeof observeTrafficWebSocket>[4]): () => void;
45
+ observeSocket(
46
+ socket: TrafficSocket,
47
+ options?: Parameters<typeof observeTrafficSocket>[4],
48
+ ): () => void;
49
+ observeWebSocket(
50
+ socket: TrafficWebSocket,
51
+ options?: Parameters<typeof observeTrafficWebSocket>[4],
52
+ ): () => void;
34
53
  stop(): Promise<void>;
35
54
  } {
36
55
  const region = options.region ?? process.env.AWS_REGION ?? 'unknown';
@@ -38,7 +57,9 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
38
57
  const activityKey = process.env.OXY_ACTIVITY_API_KEY;
39
58
  const activitySecret = process.env.OXY_ACTIVITY_API_SECRET;
40
59
  if (!options.credential && Boolean(activityKey) !== Boolean(activitySecret)) {
41
- throw new Error('Ecosystem activity requires a complete OXY_ACTIVITY_API_KEY and OXY_ACTIVITY_API_SECRET pair');
60
+ throw new Error(
61
+ 'Ecosystem activity requires a complete OXY_ACTIVITY_API_KEY and OXY_ACTIVITY_API_SECRET pair',
62
+ );
42
63
  }
43
64
  const apiKey = activityKey || process.env.OXY_SERVICE_API_KEY;
44
65
  const apiSecret = activitySecret || process.env.OXY_SERVICE_API_SECRET;
@@ -73,24 +94,44 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
73
94
  const fetcher = globalThis.fetch.bind(globalThis);
74
95
  const instanceId = randomUUID();
75
96
  const location = options.location ?? infrastructureLocation(region);
76
- if (!location) throw new Error('Ecosystem activity requires a known infrastructure region or explicit location');
97
+ if (!location)
98
+ throw new Error(
99
+ 'Ecosystem activity requires a known infrastructure region or explicit location',
100
+ );
77
101
  const post = async (path: string, data: unknown) => {
78
102
  const token = await credential();
79
103
  const response = await fetcher(new URL(path, baseURL), {
80
- method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
81
- body: JSON.stringify(data), signal: AbortSignal.timeout(4_000),
104
+ method: 'POST',
105
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
106
+ body: JSON.stringify(data),
107
+ signal: AbortSignal.timeout(4_000),
82
108
  });
83
109
  if (!response.ok) throw new Error('Activity collection failed');
84
110
  };
85
- const collector = createTrafficCollector(events => post('/internal/activity', events));
111
+ const collector = createTrafficCollector((events) => post('/internal/activity', events));
86
112
  let stopped = false;
87
113
  let heartbeatWork = Promise.resolve();
88
114
  let heartbeatPending = false;
89
- const reportError = options.onError ?? (() => console.warn('Ecosystem activity publisher unavailable'));
115
+ const reportError =
116
+ options.onError ?? (() => console.warn('Ecosystem activity publisher unavailable'));
90
117
  const heartbeat = (removed = false) => {
91
118
  if (!removed && (stopped || heartbeatPending)) return heartbeatWork;
92
119
  heartbeatPending = true;
93
- heartbeatWork = heartbeatWork.then(() => post('/internal/activity/infrastructure', { instanceId, service: options.service, region, ...location, status: options.ready?.() ? 'online' : 'unknown', removed })).catch(reportError).finally(() => { heartbeatPending = false; });
120
+ heartbeatWork = heartbeatWork
121
+ .then(() =>
122
+ post('/internal/activity/infrastructure', {
123
+ instanceId,
124
+ service: options.service,
125
+ region,
126
+ ...location,
127
+ status: options.ready?.() ? 'online' : 'unknown',
128
+ removed,
129
+ }),
130
+ )
131
+ .catch(reportError)
132
+ .finally(() => {
133
+ heartbeatPending = false;
134
+ });
94
135
  return heartbeatWork;
95
136
  };
96
137
  const flush = () => collector.flush().catch(reportError);
@@ -98,17 +139,30 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
98
139
  const sockets = new WeakMap<object, () => void>();
99
140
  let fetchInstalled = false;
100
141
  const wrapFetch = (fetcher: typeof fetch): typeof fetch => {
101
- const wrapped = instrumentTrafficFetch(fetcher, collector, options.service, region, options.resolveEndpoint ?? resolveOxyServiceEndpoint);
102
- return Object.assign((...args: Parameters<typeof fetch>) => withoutNodeHttpObservation(() => wrapped(...args)), fetcher) as typeof fetch;
142
+ const wrapped = instrumentTrafficFetch(
143
+ fetcher,
144
+ collector,
145
+ options.service,
146
+ region,
147
+ options.resolveEndpoint ?? resolveOxyServiceEndpoint,
148
+ );
149
+ return Object.assign(
150
+ (...args: Parameters<typeof fetch>) => withoutNodeHttpObservation(() => wrapped(...args)),
151
+ fetcher,
152
+ ) as typeof fetch;
103
153
  };
104
154
  const observeHttp = trafficMiddleware(collector, options.service, region);
105
- const timer = setInterval(() => { void flush(); }, 2_000);
155
+ const timer = setInterval(() => {
156
+ void flush();
157
+ }, 2_000);
106
158
  timer.unref?.();
107
159
  void heartbeat();
108
- const heartbeatTimer = setInterval(() => { void heartbeat(); }, 10_000);
160
+ const heartbeatTimer = setInterval(() => {
161
+ void heartbeat();
162
+ }, 10_000);
109
163
  heartbeatTimer.unref?.();
110
164
  return {
111
- record: flow => collector.record({ ...flow, service: options.service, region }),
165
+ record: (flow) => collector.record({ ...flow, service: options.service, region }),
112
166
  middleware: observeHttp as RequestHandler,
113
167
  observeHttp,
114
168
  wrapFetch,
@@ -118,14 +172,34 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
118
172
  const original = globalThis.fetch;
119
173
  const wrapped = wrapFetch(original);
120
174
  globalThis.fetch = wrapped;
121
- cleanups.add(() => { if (globalThis.fetch === wrapped) globalThis.fetch = original; });
122
- cleanups.add(observeNodeHttp(collector, options.service, region, options.resolveEndpoint ?? resolveOxyServiceEndpoint));
175
+ cleanups.add(() => {
176
+ if (globalThis.fetch === wrapped) globalThis.fetch = original;
177
+ });
178
+ cleanups.add(
179
+ observeNodeHttp(
180
+ collector,
181
+ options.service,
182
+ region,
183
+ options.resolveEndpoint ?? resolveOxyServiceEndpoint,
184
+ ),
185
+ );
123
186
  },
124
187
  observeWebSocket(socket, socketOptions) {
125
188
  const existing = sockets.get(socket);
126
189
  if (existing) return existing;
127
- const unobserve = observeTrafficWebSocket(socket, collector, options.service, region, socketOptions);
128
- const cleanup = () => { unobserve(); socket.off('close', cleanup); cleanups.delete(cleanup); sockets.delete(socket); };
190
+ const unobserve = observeTrafficWebSocket(
191
+ socket,
192
+ collector,
193
+ options.service,
194
+ region,
195
+ socketOptions,
196
+ );
197
+ const cleanup = () => {
198
+ unobserve();
199
+ socket.off('close', cleanup);
200
+ cleanups.delete(cleanup);
201
+ sockets.delete(socket);
202
+ };
129
203
  sockets.set(socket, cleanup);
130
204
  socket.once('close', cleanup);
131
205
  cleanups.add(cleanup);
@@ -134,13 +208,33 @@ export function createEcosystemTraffic(options: EcosystemTrafficOptions): {
134
208
  observeSocket(socket, socketOptions) {
135
209
  const existing = sockets.get(socket);
136
210
  if (existing) return existing;
137
- const unobserve = observeTrafficSocket(socket, collector, options.service, region, socketOptions);
138
- const cleanup = () => { unobserve(); socket.off('disconnect', cleanup); cleanups.delete(cleanup); sockets.delete(socket); };
211
+ const unobserve = observeTrafficSocket(
212
+ socket,
213
+ collector,
214
+ options.service,
215
+ region,
216
+ socketOptions,
217
+ );
218
+ const cleanup = () => {
219
+ unobserve();
220
+ socket.off('disconnect', cleanup);
221
+ cleanups.delete(cleanup);
222
+ sockets.delete(socket);
223
+ };
139
224
  sockets.set(socket, cleanup);
140
225
  socket.once('disconnect', cleanup);
141
226
  cleanups.add(cleanup);
142
227
  return cleanup;
143
228
  },
144
- async stop() { if (stopped) return; stopped = true; for (const cleanup of cleanups) cleanup(); cleanups.clear(); clearInterval(timer); clearInterval(heartbeatTimer); await flush(); await heartbeat(true); },
229
+ async stop() {
230
+ if (stopped) return;
231
+ stopped = true;
232
+ for (const cleanup of cleanups) cleanup();
233
+ cleanups.clear();
234
+ clearInterval(timer);
235
+ clearInterval(heartbeatTimer);
236
+ await flush();
237
+ await heartbeat(true);
238
+ },
145
239
  };
146
240
  }
@@ -2,14 +2,23 @@ import http from 'node:http';
2
2
  import https from 'node:https';
3
3
  import { AsyncLocalStorage } from 'node:async_hooks';
4
4
  import { syncBuiltinESMExports } from 'node:module';
5
- import { normalizeInfrastructureRegion, trafficType, type TrafficFlow, type ServiceEndpoint } from '@oxy.so/telemetry/collector';
5
+ import {
6
+ normalizeInfrastructureRegion,
7
+ trafficType,
8
+ type TrafficFlow,
9
+ type ServiceEndpoint,
10
+ } from '@oxy.so/telemetry/collector';
6
11
 
7
12
  const suppressed = new AsyncLocalStorage<boolean>();
8
- export function withoutNodeHttpObservation<T>(callback: () => T): T { return suppressed.run(true, callback); }
13
+ export function withoutNodeHttpObservation<T>(callback: () => T): T {
14
+ return suppressed.run(true, callback);
15
+ }
9
16
 
10
17
  /** Node and Bun share the HTTP facade; Bun does not emit Node's HTTP diagnostic channels. */
11
18
  export function observeNodeHttp(
12
- collector: { record(flow: TrafficFlow): void }, service: string, region: string,
19
+ collector: { record(flow: TrafficFlow): void },
20
+ service: string,
21
+ region: string,
13
22
  resolveEndpoint: (url: URL) => ServiceEndpoint | undefined,
14
23
  ): () => void {
15
24
  const seen = new WeakSet<http.ClientRequest>();
@@ -20,28 +29,60 @@ export function observeNodeHttp(
20
29
  const host = request.getHeader('host') ?? request.host;
21
30
  if (typeof host !== 'string') return request;
22
31
  const url = new URL(request.path || '/', `${request.protocol || 'http:'}//${host}`);
23
- if (/^\/(?:internal\/activity|auth\/service-token|platform-infrastructure)(?:\/|$)/.test(url.pathname)) return request;
32
+ if (
33
+ /^\/(?:internal\/activity|auth\/service-token|platform-infrastructure)(?:\/|$)/.test(
34
+ url.pathname,
35
+ )
36
+ )
37
+ return request;
24
38
  const peer = resolveEndpoint(url);
25
39
  if (peer && !request.headersSent) request.setHeader('X-Oxy-Source-Region', region);
26
- const flow: TrafficFlow = { service, region, activityType: trafficType(url.pathname), scope: peer ? 'internal' : 'external', direction: 'outbound', sourceRegion: region, sourceService: service, targetService: peer?.service };
40
+ const flow: TrafficFlow = {
41
+ service,
42
+ region,
43
+ activityType: trafficType(url.pathname),
44
+ scope: peer ? 'internal' : 'external',
45
+ direction: 'outbound',
46
+ sourceRegion: region,
47
+ sourceService: service,
48
+ targetService: peer?.service,
49
+ };
27
50
  const emit = request.emit;
28
51
  let recorded = false;
29
- const restore = () => { if (request.emit === observedEmit) request.emit = emit; restoreRequests.delete(restore); };
52
+ const restore = () => {
53
+ if (request.emit === observedEmit) request.emit = emit;
54
+ restoreRequests.delete(restore);
55
+ };
30
56
  // Intercept notifications without adding an 'error' listener: observation
31
57
  // must not turn an unhandled application error into a swallowed error.
32
- const observedEmit: typeof request.emit = function (this: http.ClientRequest, event: string | symbol, ...args: unknown[]) {
58
+ const observedEmit: typeof request.emit = function (
59
+ this: http.ClientRequest,
60
+ event: string | symbol,
61
+ ...args: unknown[]
62
+ ) {
33
63
  try {
34
64
  if (!recorded && event === 'response') {
35
65
  recorded = true;
36
66
  const response = args[0] as http.IncomingMessage;
37
- const targetRegion = peer ? normalizeInfrastructureRegion(response.headers['x-oxy-region']) : undefined;
67
+ const targetRegion = peer
68
+ ? normalizeInfrastructureRegion(response.headers['x-oxy-region'])
69
+ : undefined;
38
70
  collector.record({ ...flow, targetRegion });
39
- collector.record({ ...flow, direction: 'inbound', sourceRegion: targetRegion, targetRegion: region, sourceService: peer?.service, targetService: service });
71
+ collector.record({
72
+ ...flow,
73
+ direction: 'inbound',
74
+ sourceRegion: targetRegion,
75
+ targetRegion: region,
76
+ sourceService: peer?.service,
77
+ targetService: service,
78
+ });
40
79
  } else if (!recorded && (event === 'error' || event === 'close')) {
41
80
  recorded = true;
42
81
  collector.record(flow);
43
82
  }
44
- } catch { /* A metrics failure cannot change a request's behavior. */ }
83
+ } catch {
84
+ /* A metrics failure cannot change a request's behavior. */
85
+ }
45
86
  if (event === 'close') restore();
46
87
  return emit.call(this, event, ...args);
47
88
  };
@@ -50,13 +91,19 @@ export function observeNodeHttp(
50
91
  return request;
51
92
  };
52
93
  const safelyObserve = (request: http.ClientRequest) => {
53
- try { return observe(request); } catch { return request; }
94
+ try {
95
+ return observe(request);
96
+ } catch {
97
+ return request;
98
+ }
54
99
  };
55
- const patches = [http, https].map(module => {
100
+ const patches = [http, https].map((module) => {
56
101
  const request = module.request;
57
102
  const get = module.get;
58
- const wrappedRequest = ((...args: Parameters<typeof request>) => safelyObserve(request.apply(module, args))) as typeof request;
59
- const wrappedGet = ((...args: Parameters<typeof get>) => safelyObserve(get.apply(module, args))) as typeof get;
103
+ const wrappedRequest = ((...args: Parameters<typeof request>) =>
104
+ safelyObserve(request.apply(module, args))) as typeof request;
105
+ const wrappedGet = ((...args: Parameters<typeof get>) =>
106
+ safelyObserve(get.apply(module, args))) as typeof get;
60
107
  module.request = wrappedRequest;
61
108
  module.get = wrappedGet;
62
109
  return () => {
@@ -58,10 +58,7 @@ import {
58
58
  type OxyUserChangeReason,
59
59
  type OxyUserInvalidationEvent,
60
60
  } from '@oxy.so/contracts';
61
- import {
62
- evictOxyIdentityCache,
63
- type OxyIdentityCacheEvictor,
64
- } from '../utils/identityCacheSweep';
61
+ import { evictOxyIdentityCache, type OxyIdentityCacheEvictor } from '../utils/identityCacheSweep';
65
62
 
66
63
  /**
67
64
  * The publish surface of a Redis client. Both `ioredis` and `node-redis`
@@ -1,4 +1,8 @@
1
- import type { WorkloadServiceToken, WorkloadServiceTokenOptions } from './workloadIdentity';
1
+ import type {
2
+ WorkloadAttestation,
3
+ WorkloadServiceToken,
4
+ WorkloadServiceTokenOptions,
5
+ } from './workloadIdentity';
2
6
 
3
7
  /** Workload attestation belongs to a Node host, never a browser or device. */
4
8
  export function canAttestWorkloadIdentity(): boolean {
@@ -10,3 +14,10 @@ export async function requestWorkloadServiceToken(
10
14
  ): Promise<WorkloadServiceToken> {
11
15
  throw new Error('Workload identity is only available on a Node host');
12
16
  }
17
+
18
+ export async function attestWorkloadIdentity(_options: {
19
+ nonce: string;
20
+ fetch?: typeof fetch;
21
+ }): Promise<WorkloadAttestation> {
22
+ throw new Error('Workload identity is only available on a Node host');
23
+ }