@oxy.so/core 1.5.1 → 1.6.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 (126) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +72 -9
  3. package/dist/cjs/crypto/identityMove.js +53 -34
  4. package/dist/cjs/crypto/identityProof.js +28 -0
  5. package/dist/cjs/crypto/webIdentityCarrier.js +145 -82
  6. package/dist/cjs/i18n/locales/ar-SA.json +1 -0
  7. package/dist/cjs/i18n/locales/ca-ES.json +1 -0
  8. package/dist/cjs/i18n/locales/de-DE.json +1 -0
  9. package/dist/cjs/i18n/locales/en-US.json +1 -0
  10. package/dist/cjs/i18n/locales/es-ES.json +1 -0
  11. package/dist/cjs/i18n/locales/fr-FR.json +1 -0
  12. package/dist/cjs/i18n/locales/it-IT.json +1 -0
  13. package/dist/cjs/i18n/locales/ja-JP.json +1 -0
  14. package/dist/cjs/i18n/locales/ko-KR.json +1 -0
  15. package/dist/cjs/i18n/locales/locales/ar-SA.json +1 -0
  16. package/dist/cjs/i18n/locales/locales/ca-ES.json +1 -0
  17. package/dist/cjs/i18n/locales/locales/de-DE.json +1 -0
  18. package/dist/cjs/i18n/locales/locales/en-US.json +1 -0
  19. package/dist/cjs/i18n/locales/locales/es-ES.json +1 -0
  20. package/dist/cjs/i18n/locales/locales/fr-FR.json +1 -0
  21. package/dist/cjs/i18n/locales/locales/it-IT.json +1 -0
  22. package/dist/cjs/i18n/locales/locales/ja-JP.json +1 -0
  23. package/dist/cjs/i18n/locales/locales/ko-KR.json +1 -0
  24. package/dist/cjs/i18n/locales/locales/pt-PT.json +1 -0
  25. package/dist/cjs/i18n/locales/locales/zh-CN.json +1 -0
  26. package/dist/cjs/i18n/locales/pt-PT.json +1 -0
  27. package/dist/cjs/i18n/locales/zh-CN.json +1 -0
  28. package/dist/cjs/index.js +22 -14
  29. package/dist/cjs/inference/OxyInferenceClient.js +62 -5
  30. package/dist/cjs/mixins/OxyServices.auth.js +129 -0
  31. package/dist/cjs/mixins/OxyServices.identity.js +11 -44
  32. package/dist/cjs/mixins/index.js +0 -2
  33. package/dist/cjs/server/index.js +10 -1
  34. package/dist/cjs/server/securityHeaders.js +9 -2
  35. package/dist/cjs/server/workloadIdentity.js +170 -0
  36. package/dist/cjs/session/refresh.js +12 -0
  37. package/dist/esm/.tsbuildinfo +1 -1
  38. package/dist/esm/HttpService.js +72 -9
  39. package/dist/esm/crypto/identityMove.js +51 -33
  40. package/dist/esm/crypto/identityProof.js +24 -0
  41. package/dist/esm/crypto/webIdentityCarrier.js +137 -77
  42. package/dist/esm/i18n/locales/ar-SA.json +1 -0
  43. package/dist/esm/i18n/locales/ca-ES.json +1 -0
  44. package/dist/esm/i18n/locales/de-DE.json +1 -0
  45. package/dist/esm/i18n/locales/en-US.json +1 -0
  46. package/dist/esm/i18n/locales/es-ES.json +1 -0
  47. package/dist/esm/i18n/locales/fr-FR.json +1 -0
  48. package/dist/esm/i18n/locales/it-IT.json +1 -0
  49. package/dist/esm/i18n/locales/ja-JP.json +1 -0
  50. package/dist/esm/i18n/locales/ko-KR.json +1 -0
  51. package/dist/esm/i18n/locales/locales/ar-SA.json +1 -0
  52. package/dist/esm/i18n/locales/locales/ca-ES.json +1 -0
  53. package/dist/esm/i18n/locales/locales/de-DE.json +1 -0
  54. package/dist/esm/i18n/locales/locales/en-US.json +1 -0
  55. package/dist/esm/i18n/locales/locales/es-ES.json +1 -0
  56. package/dist/esm/i18n/locales/locales/fr-FR.json +1 -0
  57. package/dist/esm/i18n/locales/locales/it-IT.json +1 -0
  58. package/dist/esm/i18n/locales/locales/ja-JP.json +1 -0
  59. package/dist/esm/i18n/locales/locales/ko-KR.json +1 -0
  60. package/dist/esm/i18n/locales/locales/pt-PT.json +1 -0
  61. package/dist/esm/i18n/locales/locales/zh-CN.json +1 -0
  62. package/dist/esm/i18n/locales/pt-PT.json +1 -0
  63. package/dist/esm/i18n/locales/zh-CN.json +1 -0
  64. package/dist/esm/index.js +5 -4
  65. package/dist/esm/inference/OxyInferenceClient.js +63 -6
  66. package/dist/esm/mixins/OxyServices.auth.js +96 -0
  67. package/dist/esm/mixins/OxyServices.identity.js +11 -44
  68. package/dist/esm/mixins/index.js +0 -2
  69. package/dist/esm/server/index.js +7 -0
  70. package/dist/esm/server/securityHeaders.js +9 -2
  71. package/dist/esm/server/workloadIdentity.js +166 -0
  72. package/dist/esm/session/refresh.js +12 -0
  73. package/dist/types/.tsbuildinfo +1 -1
  74. package/dist/types/HttpService.d.ts +24 -0
  75. package/dist/types/crypto/identityMove.d.ts +48 -32
  76. package/dist/types/crypto/identityProof.d.ts +16 -0
  77. package/dist/types/crypto/webIdentityCarrier.d.ts +74 -47
  78. package/dist/types/index.d.ts +7 -6
  79. package/dist/types/inference/OxyInferenceClient.d.ts +21 -1
  80. package/dist/types/mixins/OxyServices.auth.d.ts +32 -0
  81. package/dist/types/mixins/OxyServices.identity.d.ts +16 -30
  82. package/dist/types/mixins/index.d.ts +1 -2
  83. package/dist/types/server/index.d.ts +8 -0
  84. package/dist/types/server/securityHeaders.d.ts +8 -0
  85. package/dist/types/server/workloadIdentity.d.ts +32 -0
  86. package/package.json +1 -1
  87. package/src/HttpService.ts +74 -9
  88. package/src/__tests__/httpServiceRateLimitedRefresh.test.ts +160 -0
  89. package/src/__tests__/serviceTokenWorkloadFallback.test.ts +85 -0
  90. package/src/crypto/__tests__/identityMove.test.ts +93 -51
  91. package/src/crypto/__tests__/identityProof.test.ts +81 -0
  92. package/src/crypto/__tests__/webIdentityCarrier.test.ts +21 -63
  93. package/src/crypto/__tests__/webIdentityRecoveryMaterial.test.ts +117 -0
  94. package/src/crypto/identityMove.ts +77 -43
  95. package/src/crypto/identityProof.ts +36 -0
  96. package/src/crypto/webIdentityCarrier.ts +185 -117
  97. package/src/i18n/locales/ar-SA.json +1 -0
  98. package/src/i18n/locales/ca-ES.json +1 -0
  99. package/src/i18n/locales/de-DE.json +1 -0
  100. package/src/i18n/locales/en-US.json +1 -0
  101. package/src/i18n/locales/es-ES.json +1 -0
  102. package/src/i18n/locales/fr-FR.json +1 -0
  103. package/src/i18n/locales/it-IT.json +1 -0
  104. package/src/i18n/locales/ja-JP.json +1 -0
  105. package/src/i18n/locales/ko-KR.json +1 -0
  106. package/src/i18n/locales/pt-PT.json +1 -0
  107. package/src/i18n/locales/zh-CN.json +1 -0
  108. package/src/index.ts +26 -15
  109. package/src/inference/OxyInferenceClient.ts +84 -7
  110. package/src/inference/__tests__/OxyInferenceClient.test.ts +48 -0
  111. package/src/mixins/OxyServices.auth.ts +100 -1
  112. package/src/mixins/OxyServices.identity.ts +17 -67
  113. package/src/mixins/__tests__/OxyServices.identity.test.ts +1 -76
  114. package/src/mixins/index.ts +0 -3
  115. package/src/server/__tests__/securityHeaders.test.ts +15 -0
  116. package/src/server/__tests__/workloadIdentity.test.ts +128 -0
  117. package/src/server/index.ts +9 -0
  118. package/src/server/securityHeaders.ts +19 -6
  119. package/src/server/workloadIdentity.ts +207 -0
  120. package/src/session/__tests__/refresh.test.ts +53 -3
  121. package/src/session/refresh.ts +12 -0
  122. package/dist/cjs/mixins/OxyServices.deviceTransfer.js +0 -314
  123. package/dist/esm/mixins/OxyServices.deviceTransfer.js +0 -311
  124. package/dist/types/mixins/OxyServices.deviceTransfer.d.ts +0 -149
  125. package/src/mixins/OxyServices.deviceTransfer.ts +0 -393
  126. package/src/mixins/__tests__/OxyServices.deviceTransfer.test.ts +0 -271
@@ -111,6 +111,17 @@ export declare class HttpService {
111
111
  * fixed deadline that could not shrink once the token expired mid-cooldown.
112
112
  */
113
113
  private lastRefreshFailureAt;
114
+ /**
115
+ * Whether the last refresh failure was the server answering 429. Selects
116
+ * {@link RATE_LIMITED_REFRESH_COOLDOWN_MS} over the two token-state cooldowns
117
+ * for the NEXT attempt, and is cleared by any success or any other failure.
118
+ *
119
+ * A boolean beside {@link lastRefreshFailureAt} rather than a stored deadline,
120
+ * for the same reason that field gives: the cooldown stays a function of the
121
+ * current state, so it can still shorten if the situation changes underneath
122
+ * it, and there is exactly one timestamp to reason about.
123
+ */
124
+ private lastRefreshWasRateLimited;
114
125
  private authRefreshHandler;
115
126
  private accessTokenProvider;
116
127
  private deviceSecretMintInFlight;
@@ -266,6 +277,19 @@ export declare class HttpService {
266
277
  */
267
278
  private getAuthHeader;
268
279
  refreshAccessToken(reason: AuthRefreshReason): Promise<string | null>;
280
+ /**
281
+ * Report that the refresh/mint endpoint answered 429 Too Many Requests, so the
282
+ * next attempt waits {@link RATE_LIMITED_REFRESH_COOLDOWN_MS} instead of one of
283
+ * the token-state cooldowns.
284
+ *
285
+ * Called by the refresh handler (`refreshDeviceSecretArm`) from inside the
286
+ * `authRefreshHandler` call, because the handler's `Promise<string | null>`
287
+ * contract cannot carry WHY a refresh failed and 429 is the one failure whose
288
+ * correct retry interval is set by the server rather than by the token.
289
+ *
290
+ * Public because the handler lives in `session/refresh.ts`, not on this class.
291
+ */
292
+ noteRefreshRateLimited(): void;
269
293
  /**
270
294
  * Whether the CURRENT stored access token is already past its `exp`. Drives
271
295
  * the shorter post-failure refresh cooldown ({@link EXPIRED_TOKEN_REFRESH_COOLDOWN_MS}):
@@ -1,20 +1,18 @@
1
1
  /**
2
- * Identity move — the crypto of taking a web identity into Commons.
2
+ * Identity move — the crypto of giving a web root to Commons (ADR 0024 D6).
3
3
  *
4
- * Both carriers run this: `id.oxy.so` (the initiator, holding the identity) and
5
- * Commons (the responder, receiving it). The relay in between sees two ephemeral
6
- * public keys and ciphertext; the 6-digit SAS both screens show is what makes a
7
- * relay that swapped a key visible to the person.
4
+ * Both sides run this: the web holder (the initiator, holding the root) and
5
+ * Commons (the responder, receiving it). The relay in between sees a commitment,
6
+ * two ephemeral public keys and ciphertext. The initiator commits to its key
7
+ * before the responder chooses one and reveals it only afterwards, so the 6-digit
8
+ * code both screens show exposes a relay that substituted a key — it cannot grind
9
+ * one until the codes agree. The receipt binds the move, the root, both keys and
10
+ * the ciphertext actually relayed.
8
11
  *
9
12
  * PURE: no storage, no network. Contract: `@oxy.so/contracts` `identityMove`.
10
13
  */
11
14
  import './polyfill';
12
- import { type OpenedWebIdentity } from './webIdentityCarrier';
13
- /** Actions the identity key signs during a move. */
14
- export declare const IDENTITY_MOVE_ACTIONS: {
15
- readonly seal: "identity_move_seal";
16
- readonly received: "identity_move_received";
17
- };
15
+ import { type OpenedMnemonicIdentity } from './webIdentityCarrier';
18
16
  /** A fresh ephemeral key pair for one move. Keep the private key in memory only. */
19
17
  export declare function generateMoveEphemeralKeyPair(): {
20
18
  privateKey: string;
@@ -22,10 +20,44 @@ export declare function generateMoveEphemeralKeyPair(): {
22
20
  };
23
21
  /** The symmetric key both sides derive: HKDF(ECDH, salt = moveId). */
24
22
  export declare function deriveMoveKey(ownEphemeralPrivateKey: string, otherEphemeralPublicKey: string, moveId: string): Uint8Array;
25
- /** The 6-digit code both screens show, bound to the move and both ephemeral keys in their roles. */
26
- export declare function deriveMoveSas(moveId: string, initiatorEphemeralPublicKey: string, responderEphemeralPublicKey: string): string;
27
- /** Initiator: seal the identity's entropy for the responder. */
28
- export declare function sealIdentityForMove(identity: Pick<OpenedWebIdentity, 'mnemonic' | 'publicKey'>, moveKey: Uint8Array, moveId: string): {
23
+ /** Initiator: commit to the ephemeral key before anyone else's key is known. The nonce stays in memory until the reveal. */
24
+ export declare function createMoveCommitment(initiatorEphemeralPublicKey: string): {
25
+ commitment: string;
26
+ nonce: string;
27
+ };
28
+ /** Responder: does the revealed key match the commitment read before joining? */
29
+ export declare function verifyMoveCommitment(initiatorEphemeralPublicKey: string, nonce: string, commitment: string): boolean;
30
+ /** The 6-digit code, bound to the move, both keys in their roles, and the commitment. */
31
+ export declare function deriveMoveSas(input: {
32
+ moveId: string;
33
+ initiatorEphemeralPublicKey: string;
34
+ responderEphemeralPublicKey: string;
35
+ initiatorCommitment: string;
36
+ }): string;
37
+ /** The digest a receipt binds: what was actually relayed. */
38
+ export declare function digestMoveCiphertext(sealed: {
39
+ nonce: string;
40
+ ciphertext: string;
41
+ }): string;
42
+ export interface MoveReceiptClaims {
43
+ moveId: string;
44
+ rootPublicKey: string;
45
+ initiatorEphemeralPublicKey: string;
46
+ responderEphemeralPublicKey: string;
47
+ ciphertextDigest: string;
48
+ }
49
+ /**
50
+ * Responder: sign the receipt with a signer that reads the root back from durable
51
+ * storage (Commons passes its keychain signer), so a receipt exists only for a
52
+ * root that was actually stored.
53
+ */
54
+ export declare function signMoveReceipt(sign: (message: string) => Promise<string>, claims: MoveReceiptClaims): Promise<{
55
+ signature: string;
56
+ }>;
57
+ /** Initiator: is this receipt the root's, for exactly this move and ciphertext? */
58
+ export declare function verifyMoveReceipt(claims: MoveReceiptClaims, signature: string): Promise<boolean>;
59
+ /** Initiator: seal the phrase entropy (12–24 words) for the responder. */
60
+ export declare function sealIdentityForMove(identity: Pick<OpenedMnemonicIdentity, 'mnemonic' | 'publicKey'>, moveKey: Uint8Array, moveId: string): {
29
61
  nonce: string;
30
62
  ciphertext: string;
31
63
  };
@@ -36,23 +68,7 @@ export declare function sealIdentityForMove(identity: Pick<OpenedWebIdentity, 'm
36
68
  export declare function openMovedIdentity(sealed: {
37
69
  nonce: string;
38
70
  ciphertext: string;
39
- }, moveKey: Uint8Array, moveId: string, expectedPublicKey: string): OpenedWebIdentity;
40
- /** The exact bytes a move proof or receipt signs. */
41
- export declare function buildMoveMessage(action: string, moveId: string, timestamp: number): string;
42
- /** Sign a move action with the identity key. */
43
- export declare function signMoveAction(identity: Pick<OpenedWebIdentity, 'privateKey'>, action: string, moveId: string, timestamp?: number): Promise<{
44
- signature: string;
45
- timestamp: number;
46
- }>;
47
- /**
48
- * Initiator: is this receipt really from the identity? Checked locally with the
49
- * identity's public key, so a server that claims "completed" without Commons
50
- * having the key cannot make the web destroy its copy.
51
- */
52
- export declare function verifyMoveReceipt(publicKey: string, moveId: string, receipt: {
53
- signature: string;
54
- timestamp: number;
55
- }): Promise<boolean>;
71
+ }, moveKey: Uint8Array, moveId: string, expectedPublicKey: string): OpenedMnemonicIdentity;
56
72
  /** The QR payload for a move. */
57
73
  export declare function buildMoveQrPayload(moveId: string): string;
58
74
  /** The move id in a scanned payload, or `null` when it is not a move QR. */
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Signing the one identity-proof format (ADR 0024 D7).
3
+ *
4
+ * The bytes come from `@oxy.so/contracts` `buildIdentityProofMessage`, which the
5
+ * API verifies against; payload digests hash `canonicalJson`. Nothing here builds
6
+ * its own JSON.
7
+ */
8
+ import './polyfill';
9
+ import { type IdentityProof, type IdentityProofClaims } from '@oxy.so/contracts';
10
+ /** SHA-256 hex of the canonical JSON of `payload` — the `payloadDigest` claim. */
11
+ export declare function digestIdentityPayload(payload: unknown): string;
12
+ /** Sign `claims` with the root, producing the proof as it travels. */
13
+ export declare function signIdentityProof(identity: {
14
+ privateKey: string;
15
+ publicKey: string;
16
+ }, claims: IdentityProofClaims): Promise<IdentityProof>;
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Web identity carrier — the crypto of "one identity, two carriers".
2
+ * Web identity holder — the crypto of a browser holding a personal root (ADR 0024).
3
3
  *
4
- * An Oxy identity is a BIP-39 mnemonic; its seed's first 32 bytes are the
5
- * secp256k1 key (`recoveryPhrase.ts`). Commons keeps that key in the device
6
- * keychain. This module lets a browser carry the SAME identity without Oxy ever
7
- * being able to use it:
4
+ * An Oxy root is usually a BIP-39 mnemonic whose seed's first 32 bytes are the
5
+ * secp256k1 key (`recoveryPhrase.ts`), and for a few imported identities a raw
6
+ * private key with no phrase. Commons keeps it in the device keychain. This
7
+ * module lets a browser hold the SAME root without Oxy ever being able to use it:
8
8
  *
9
- * entropy ──AEAD(DEK)──▶ sealedEntropy
9
+ secret ──AEAD(DEK)──▶ sealed secret (phrase entropy or raw key)
10
10
  * DEK ──AEAD(KEK_i)─▶ wraps[i] KEK_i = HKDF(PRF output of passkey i)
11
11
  *
12
12
  * The PRF output is produced inside the user's authenticator, behind user
13
13
  * verification, and never leaves the page that asked for it. The envelope is
14
- * therefore safe to store anywhere — the identity origin's IndexedDB and the
15
- * server copy alike — and opens only with a registered passkey or the phrase.
14
+ * therefore safe to store anywhere — the holder host's IndexedDB and the server
15
+ * copy alike — and opens only with a registered passkey or the recovery material.
16
16
  *
17
17
  * PURE: no storage, no network, no WebAuthn call, no platform globals beyond the
18
18
  * CSPRNG the AEAD polyfill guarantees. The caller runs the ceremony and hands the
@@ -22,7 +22,7 @@
22
22
  * Contract: `WebIdentityEnvelope` in `@oxy.so/contracts`.
23
23
  */
24
24
  import './polyfill';
25
- import { type WebIdentityEnvelope, type WebIdentityWrap } from '@oxy.so/contracts';
25
+ import { type WebIdentityEnvelope } from '@oxy.so/contracts';
26
26
  /**
27
27
  * The PRF input every identity-carrier ceremony evaluates (`prf.eval.first`).
28
28
  *
@@ -31,6 +31,8 @@ import { type WebIdentityEnvelope, type WebIdentityWrap } from '@oxy.so/contract
31
31
  * scheme change bumps the label — and with it every KEK — instead of mutating it.
32
32
  */
33
33
  export declare const WEB_IDENTITY_PRF_INPUT: Uint8Array;
34
+ /** A WebAuthn PRF output is 32 bytes (HMAC-SHA-256). Nothing shorter or longer is usable. */
35
+ export declare const WEB_IDENTITY_PRF_OUTPUT_LENGTH = 32;
34
36
  /** Why an envelope did not open. Every value is a normal outcome a UI can explain. */
35
37
  export type WebIdentityUnlockFailure =
36
38
  /** The passkey used is not one of the envelope's wraps. */
@@ -38,7 +40,7 @@ export type WebIdentityUnlockFailure =
38
40
  /**
39
41
  * The passkey IS listed but its PRF output does not open its wrap — a synced
40
42
  * copy returning a different PRF value, or a provider that silently changed it.
41
- * Recoverable with another passkey or the phrase.
43
+ * Recoverable with another passkey or the recovery material.
42
44
  */
43
45
  | 'prf-mismatch'
44
46
  /** The envelope itself is malformed or tampered with. */
@@ -47,39 +49,81 @@ export declare class WebIdentityUnlockError extends Error {
47
49
  readonly failure: WebIdentityUnlockFailure;
48
50
  constructor(failure: WebIdentityUnlockFailure, message: string);
49
51
  }
50
- /** The identity in usable form. Hold it for one operation, then {@link wipeBytes}. */
51
- export interface OpenedWebIdentity {
52
+ /** A root that came from a BIP-39 phrase (12–24 words). Hold it for one operation, then wipe. */
53
+ export interface OpenedMnemonicIdentity {
54
+ kind: 'mnemonic';
52
55
  mnemonic: string;
53
- /** 32-byte secp256k1 private key, lowercase hex. */
56
+ /** 32-byte secp256k1 private key, lowercase hex (`seed[0:32]`). */
54
57
  privateKey: string;
55
58
  /** Uncompressed SEC1 public key, lowercase hex (Oxy's canonical form). */
56
59
  publicKey: string;
57
60
  }
61
+ /**
62
+ * A root that was imported as a raw private key and never had a phrase. It has
63
+ * no mnemonic, and nothing may ever derive or display one for it (ADR 0024 D5).
64
+ */
65
+ export interface OpenedRawKeyIdentity {
66
+ kind: 'raw-key';
67
+ mnemonic: null;
68
+ privateKey: string;
69
+ publicKey: string;
70
+ }
71
+ /** The identity in usable form. Hold it for one operation, then wipe it. */
72
+ export type OpenedWebIdentity = OpenedMnemonicIdentity | OpenedRawKeyIdentity;
73
+ /** Material a person can recover a root from. */
74
+ export type WebIdentityRecoveryMaterial = {
75
+ kind: 'mnemonic';
76
+ mnemonic: string;
77
+ } | {
78
+ kind: 'raw-key';
79
+ privateKey: string;
80
+ };
58
81
  /** Overwrite a buffer holding secret material. Best effort — JS gives no stronger guarantee. */
59
82
  export declare function wipeBytes(bytes: Uint8Array): void;
83
+ /** Best-effort removal of secret strings from an opened identity. */
84
+ export declare function wipeOpenedIdentity(identity: OpenedWebIdentity): void;
85
+ /** Normalize a typed phrase: trimmed, lowercase, single spaces. */
86
+ export declare function normalizeMnemonic(mnemonic: string): string;
60
87
  /**
61
88
  * Derive the identity from a mnemonic exactly as Commons does
62
- * (`RecoveryPhraseService`): seed → first 32 bytes → secp256k1.
89
+ * (`RecoveryPhraseService`): seed → first 32 bytes → secp256k1. Any standard
90
+ * BIP-39 length (12, 15, 18, 21, 24 words) is accepted; the derivation does not
91
+ * depend on the length.
63
92
  */
64
- export declare function deriveIdentityFromMnemonic(mnemonic: string): OpenedWebIdentity;
93
+ export declare function deriveIdentityFromMnemonic(mnemonic: string): OpenedMnemonicIdentity;
94
+ /** A raw 32-byte private key (hex, optional `0x`) as an identity. Never gains a phrase. */
95
+ export declare function deriveIdentityFromPrivateKey(privateKey: string): OpenedRawKeyIdentity;
96
+ /**
97
+ * Read what a person typed as recovery material: a BIP-39 phrase, or a 64-hex
98
+ * private key. Throws `Invalid recovery material` for anything else.
99
+ */
100
+ export declare function parseRecoveryMaterial(input: string): WebIdentityRecoveryMaterial;
101
+ /** Derive the identity recovery material names. */
102
+ export declare function deriveIdentityFromRecoveryMaterial(material: WebIdentityRecoveryMaterial): OpenedWebIdentity;
65
103
  /** A brand-new 12-word identity. Nothing is persisted. */
66
- export declare function generateWebIdentity(): OpenedWebIdentity;
104
+ export declare function generateWebIdentity(): OpenedMnemonicIdentity;
67
105
  /** A fresh random data key for a new envelope. */
68
106
  export declare function generateDataKey(): Uint8Array;
107
+ /** Whether `bytes` is a usable PRF output: exactly 32 bytes. */
108
+ export declare function isUsablePrfOutput(bytes: Uint8Array | null | undefined): bytes is Uint8Array;
69
109
  /** The key-encryption key a passkey's PRF output yields for its own wrap. */
70
110
  export declare function deriveKeyEncryptionKey(prfOutput: Uint8Array, credentialId: string): Uint8Array;
71
- /** Wrap `dataKey` for one passkey. */
72
- export declare function wrapDataKey(dataKey: Uint8Array, prfOutput: Uint8Array, credentialId: string, publicKey: string, now?: Date): WebIdentityWrap;
111
+ /** One passkey's contribution to an envelope. */
112
+ export interface WrapInput {
113
+ prfOutput: Uint8Array;
114
+ credentialId: string;
115
+ /** The RP ID the passkey lives under; bound into the wrap. */
116
+ rpId: string;
117
+ /** Set when this PRF output came from a ceremony separate from the one that created the wrap. */
118
+ verifiedAt?: string;
119
+ }
73
120
  /**
74
121
  * Seal an identity into a new envelope that opens with the given passkey.
75
122
  *
76
123
  * Returns the data key too, so the caller can add further wraps in the same
77
124
  * session; wipe it when done.
78
125
  */
79
- export declare function sealWebIdentity(identity: Pick<OpenedWebIdentity, 'mnemonic' | 'publicKey'>, firstWrap: {
80
- prfOutput: Uint8Array;
81
- credentialId: string;
82
- }, now?: Date): {
126
+ export declare function sealWebIdentity(identity: OpenedWebIdentity, firstWrap: WrapInput, now?: Date): {
83
127
  envelope: WebIdentityEnvelope;
84
128
  dataKey: Uint8Array;
85
129
  };
@@ -88,36 +132,19 @@ export declare function unwrapDataKey(envelope: WebIdentityEnvelope, prfOutput:
88
132
  /**
89
133
  * Open an envelope with its data key.
90
134
  *
91
- * The derived public key is checked against the envelope's own: a sealed entropy
135
+ * The derived public key is checked against the envelope's own: a sealed secret
92
136
  * that decrypts into a different identity is treated as corruption, never used.
93
137
  */
94
138
  export declare function openWebIdentity(envelope: WebIdentityEnvelope, dataKey: Uint8Array): OpenedWebIdentity;
95
139
  /** Unwrap and open in one step. */
96
140
  export declare function unlockWebIdentity(envelope: WebIdentityEnvelope, prfOutput: Uint8Array, credentialId: string): OpenedWebIdentity;
97
141
  /** Add (or replace) one passkey's wrap. Requires the data key of an unlocked envelope. */
98
- export declare function addWrap(envelope: WebIdentityEnvelope, dataKey: Uint8Array, prfOutput: Uint8Array, credentialId: string, now?: Date): WebIdentityEnvelope;
99
- /** Remove one passkey's wrap. The last wrap can never be removed — an envelope nobody can open is a loss. */
100
- export declare function removeWrap(envelope: WebIdentityEnvelope, credentialId: string): WebIdentityEnvelope;
142
+ export declare function addWrap(envelope: WebIdentityEnvelope, dataKey: Uint8Array, wrapInput: WrapInput, now?: Date): WebIdentityEnvelope;
101
143
  /**
102
- * The message an identity key signs to authorize an account action
103
- * (`link_identity`, `web_envelope_delete`, …): `JSON.stringify({ action, userId,
104
- * timestamp })`, byte-identical to what the API reconstructs.
144
+ * Record that a wrap's passkey opened the envelope in a ceremony of its own. The
145
+ * AEAD does not cover `verifiedAt`; it is holder metadata the owner writes with a
146
+ * root proof, not a cryptographic claim.
105
147
  */
106
- export declare function buildIdentityActionMessage(action: string, userId: string, timestamp: number): string;
107
- /** Sign an identity-action message with an opened identity's key. */
108
- export declare function signIdentityAction(identity: Pick<OpenedWebIdentity, 'privateKey'>, action: string, userId: string, timestamp?: number): Promise<{
109
- signature: string;
110
- timestamp: number;
111
- }>;
112
- /**
113
- * The 6-digit short authentication string both sides of an identity transfer show.
114
- *
115
- * Bound to the pairing and to BOTH ephemeral public keys in their roles, so a relay
116
- * that substitutes either key produces a different code on each screen. The user
117
- * comparing the two codes is what makes the transfer safe against the relay itself.
118
- */
119
- export declare function deriveTransferSas(input: {
120
- pairingId: string;
121
- initiatorEphemeralPublicKey: string;
122
- responderEphemeralPublicKey: string;
123
- }): string;
148
+ export declare function markWrapVerified(envelope: WebIdentityEnvelope, credentialId: string, now?: Date): WebIdentityEnvelope;
149
+ /** Remove one passkey's wrap. The last wrap can never be removed — an envelope nobody can open is a loss. */
150
+ export declare function removeWrap(envelope: WebIdentityEnvelope, credentialId: string): WebIdentityEnvelope;
@@ -30,7 +30,6 @@ export type { CommonsSignInHandle, CommonsSignInStatus, CommonsSignInPurpose, Co
30
30
  export type { PushTokenPlatform, RegisterPushTokenInput, } from './mixins/OxyServices.notifications';
31
31
  export type { ServiceApp, ServiceActingAsVerification, OxyAuthRefusal } from './mixins/OxyServices.utility';
32
32
  export type { ContactDiscoveryMatch, ContactDiscoveryResponse, } from './mixins/OxyServices.contacts';
33
- export type { InitDeviceTransferResult, DeviceTransferOutcome, } from './mixins/OxyServices.deviceTransfer';
34
33
  export type { BulkFollowEntry, BulkFollowResult, BulkUnfollowEntry, BulkUnfollowResult, FollowMutationResult, ViewerGraph, } from './mixins/OxyServices.user';
35
34
  export { OxyAppDataIdentifierError } from './mixins/OxyServices.appData';
36
35
  export { getNormalizedUserId, normalizeUserIdentity, normalizeUserIdentityOrNull, } from './utils/userIdentity';
@@ -44,7 +43,7 @@ export type { StoreCategory, StoreRating, StoreListingSummary, StoreListingDetai
44
43
  export type { AccountKind, AccountCategoryId, AccountRelationship, AccountRole, AccountMemberStatus, AccountMemberSource, AccountMember, AccountNode, ListAccountsOptions, CreateAccountInput, UpdateAccountInput, ProvisionChannelInput, ProvisionChannelMemberInput, ProvisionChannelResult, InviteAccountMemberInput, UpdateAccountMemberInput, TransferAccountOwnershipInput, AccountSuccessResult, SwitchAccountResult, Application, ApplicationType, ApplicationStatus, ApplicationCredential, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, CreateApplicationCredentialInput, RotateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, } from './mixins/OxyServices.accounts';
45
44
  export { ACCOUNT_CATEGORY_IDS, MAX_ACCOUNT_CATEGORIES, SELECTABLE_ACCOUNT_CATEGORY_IDS, isSelectableAccountCategoryId, kindAcceptsAccountCategories, } from './mixins/OxyServices.accounts';
46
45
  export { buildUserDid } from './mixins/OxyServices.identity';
47
- export type { IdentityRecordType, UnlinkableAuthMethodType, LinkAuthMethodResult, PublishRecordResult, VerifyRecordResult, VerifyDomainResult, RemoveDomainResult, RotateKeyProof, RotateKeyOptions, RotateKeyResult, } from './mixins/OxyServices.identity';
46
+ export type { IdentityRecordType, LinkAuthMethodResult, PublishRecordResult, VerifyRecordResult, VerifyDomainResult, RemoveDomainResult, RotateKeyProof, RotateKeyOptions, RotateKeyResult, } from './mixins/OxyServices.identity';
48
47
  export { parseIdPayload, parseAttestPayload, verifyPublicCardAttestation, } from './mixins/OxyServices.civic';
49
48
  export type { CivicCardResult, IdCardRef, AttestQrPayload, ParsedAttestPayload, SubmitRealLifeAttestationInput, DenyValidationResult, VouchForPersonInput, WithdrawVouchResult, IssueCredentialInput, RevokeCredentialResult, } from './mixins/OxyServices.civic';
50
49
  export type { UserNodeStatus, UserNodeMode, UserNodeController, UserNodeLivenessStatus, RegisterNodeInput, RemoveNodeResult } from './mixins/OxyServices.nodes';
@@ -70,9 +69,11 @@ export { hkdfSha256 } from './crypto/kdf';
70
69
  export { encryptAead, decryptAead, AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, } from './crypto/aead';
71
70
  export type { AeadResult } from './crypto/aead';
72
71
  export { deriveSharedSecret } from './crypto/ecdh';
73
- export { WEB_IDENTITY_PRF_INPUT, WebIdentityUnlockError, addWrap, buildIdentityActionMessage, deriveIdentityFromMnemonic, deriveKeyEncryptionKey, deriveTransferSas, generateDataKey, generateWebIdentity, openWebIdentity, removeWrap, sealWebIdentity, signIdentityAction, unlockWebIdentity, unwrapDataKey, wipeBytes, wrapDataKey, } from './crypto/webIdentityCarrier';
74
- export type { OpenedWebIdentity, WebIdentityUnlockFailure } from './crypto/webIdentityCarrier';
75
- export { IDENTITY_MOVE_ACTIONS, buildMoveMessage, buildMoveQrPayload, deriveMoveKey, deriveMoveSas, generateMoveEphemeralKeyPair, openMovedIdentity, parseMoveQrPayload, sealIdentityForMove, signMoveAction, verifyMoveReceipt, } from './crypto/identityMove';
72
+ export { WEB_IDENTITY_PRF_INPUT, WebIdentityUnlockError, WEB_IDENTITY_PRF_OUTPUT_LENGTH, addWrap, deriveIdentityFromMnemonic, deriveIdentityFromPrivateKey, deriveIdentityFromRecoveryMaterial, deriveKeyEncryptionKey, generateDataKey, generateWebIdentity, isUsablePrfOutput, markWrapVerified, normalizeMnemonic, openWebIdentity, parseRecoveryMaterial, removeWrap, sealWebIdentity, unlockWebIdentity, unwrapDataKey, wipeBytes, wipeOpenedIdentity, } from './crypto/webIdentityCarrier';
73
+ export type { OpenedMnemonicIdentity, OpenedRawKeyIdentity, OpenedWebIdentity, WebIdentityRecoveryMaterial, WebIdentityUnlockFailure, WrapInput, } from './crypto/webIdentityCarrier';
74
+ export { digestIdentityPayload, signIdentityProof } from './crypto/identityProof';
75
+ export { buildMoveQrPayload, createMoveCommitment, deriveMoveKey, deriveMoveSas, digestMoveCiphertext, generateMoveEphemeralKeyPair, openMovedIdentity, parseMoveQrPayload, sealIdentityForMove, signMoveReceipt, verifyMoveCommitment, verifyMoveReceipt, } from './crypto/identityMove';
76
+ export type { MoveReceiptClaims } from './crypto/identityMove';
76
77
  export { DeviceManager } from './utils/deviceManager';
77
78
  export type { DeviceFingerprint, StoredDeviceInfo } from './utils/deviceManager';
78
79
  export type { OxyConfig, PrivacySettings, NotificationPreferences, UserPreferences, User, LoginResponse, Notification, Wallet, Transaction, BlockedUser, RestrictedUser, TransferFundsRequest, PurchaseRequest, WithdrawalRequest, TransactionResponse, PaginationInfo, SearchProfilesResponse, ApiError, PaymentMethod, PaymentRequest, PaymentResponse, AnalyticsData, FollowerDetails, ContentViewer, FileMetadata, FileUploadResponse, FileListResponse, FileUpdateRequest, FileDeleteResponse, RNFileDescriptor, AssetUploadInput, FileVisibility, AssetLink, AssetMetadata, AssetVariant, Asset, AssetInitRequest, AssetInitResponse, AssetCompleteRequest, AssetLinkRequest, AssetUnlinkRequest, AssetUrlResponse, BatchFileAccessEntry, BatchFileAccessResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceLinkedSession, DeviceLinkedSessionsResponse, DeviceLinkedSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
@@ -141,7 +142,7 @@ export { AccountNotOnDeviceError } from './mixins/OxyServices.deviceBoot';
141
142
  export { refreshPersistedSession, refreshDeviceSecretArm, createAuthRefreshHandler, installAuthRefreshHandler, startTokenRefreshScheduler, TOKEN_REFRESH_LEAD_MS, } from './session/refresh';
142
143
  export type { RefreshDeps, TokenRefreshSchedulerHandle, DeviceSecretMintOutcome } from './session/refresh';
143
144
  export { OxyInferenceClient, OxyInferenceError, OxyInferenceProtocolError, OXY_INFERENCE_BASE_URL, } from './inference/OxyInferenceClient';
144
- export type { OxyInferenceClientOptions, OxyInferenceCredential, OxyInferenceFetch, OxyInferenceRequestOptions, OxyInferenceResponse, OxyGenerationReceipt, OxyResponsesRequest, } from './inference/OxyInferenceClient';
145
+ export type { OxyInferenceClientOptions, OxyInferenceCredential, OxyInferenceFetch, OxyInferenceRequestOptions, OxyInferenceResponse, OxyGenerationReceipt, OxyResponsesRequest, OxySpeechRequest, OxySpeechResponse, } from './inference/OxyInferenceClient';
145
146
  export { runSessionColdBoot } from './boot/sessionColdBoot';
146
147
  export type { RunSessionColdBootOptions, SessionMode, SignedOutReason, DeviceBootSession, } from './boot/sessionColdBoot';
147
148
  export { packageInfo } from './constants/version';
@@ -49,7 +49,7 @@
49
49
  * schema, so a rename on either side fails a build rather than a customer's
50
50
  * request.
51
51
  */
52
- import type { CurrencyCode, ExactDecimal, InferenceEnvironment, InferenceErrorCode, InferenceFinishReason, InferenceMessage, InferenceRequestOutcome, InferenceStreamEvent, ModelCatalogueEntry, ResponseFormat, RoutingPolicyReference, RoutingProfile, ToolChoice, ToolDefinition, UnitPrice, UsageQuantity, UsageSource } from '@oxy.so/contracts';
52
+ import type { CurrencyCode, ExactDecimal, InferenceEnvironment, InferenceErrorCode, InferenceFinishReason, InferenceMessage, InferenceRequestOutcome, InferenceAudioMediaType, InferenceStreamEvent, InferenceSpeechParameters, ModelCatalogueEntry, ResponseFormat, RoutingPolicyReference, RoutingProfile, ToolChoice, ToolDefinition, UnitPrice, UsageQuantity, UsageSource } from '@oxy.so/contracts';
53
53
  /** The base URL of the Oxy API, when a caller names none. */
54
54
  export declare const OXY_INFERENCE_BASE_URL = "https://api.oxy.so";
55
55
  /**
@@ -110,6 +110,24 @@ export interface OxyResponsesRequest {
110
110
  /** Your own correlation id, echoed on the response. */
111
111
  readonly clientRequestId?: string;
112
112
  }
113
+ /** Speech uses the same credential and delegation lane as responses. */
114
+ export type OxySpeechRequest = ({
115
+ readonly model: string;
116
+ readonly routingProfileId?: never;
117
+ } | {
118
+ readonly routingProfileId: string;
119
+ readonly model?: never;
120
+ }) & {
121
+ readonly input: string;
122
+ readonly voice: string;
123
+ readonly response_format?: InferenceSpeechParameters['responseFormat'];
124
+ readonly speed?: number;
125
+ };
126
+ export interface OxySpeechResponse {
127
+ readonly audio: Uint8Array;
128
+ readonly mediaType: InferenceAudioMediaType;
129
+ readonly requestId: string;
130
+ }
113
131
  export interface OxyInferenceRequestOptions {
114
132
  /**
115
133
  * Abort the request. The edge treats a client disconnect as a cancellation:
@@ -307,6 +325,8 @@ export declare class OxyInferenceClient {
307
325
  * `code`, `retryable` and `requestId`.
308
326
  */
309
327
  respond(request: OxyResponsesRequest, options?: OxyInferenceRequestOptions): Promise<OxyInferenceResponse>;
328
+ /** Generate audio bytes through Oxy's authenticated inference edge. */
329
+ speech(request: OxySpeechRequest, options?: OxyInferenceRequestOptions): Promise<OxySpeechResponse>;
310
330
  /**
311
331
  * Stream one inference request from `POST /v1/responses`.
312
332
  *
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { User } from '../models/interfaces';
7
7
  import type { LoginResult, CommonsDenyReason } from '@oxy.so/contracts';
8
+ import { type IdentityProof, type WebIdentityEnvelope } from '@oxy.so/contracts';
8
9
  import type { SessionLoginResponse } from '../models/session';
9
10
  import type { OxyServicesBase } from '../OxyServices.base';
10
11
  import type { PublicApplication } from './OxyServices.connectedApps';
@@ -387,6 +388,28 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
387
388
  * @param apiSecret - Application credential secret (optional if configureServiceAuth was called)
388
389
  */
389
390
  getServiceToken(apiKey?: string, apiSecret?: string): Promise<string>;
391
+ /**
392
+ * Whether this process can prove what it is (ADR 0026).
393
+ *
394
+ * The check and the module that performs it are loaded LAZILY and only on a
395
+ * Node host. `@oxy.so/core`'s root barrel reaches React Native and Expo, and
396
+ * `server/workloadIdentity` imports `node:crypto` — a static import here
397
+ * would pull it into every mobile bundle for a path a phone can never take.
398
+ *
399
+ * @internal
400
+ */
401
+ _canUseWorkloadIdentity(): Promise<boolean>;
402
+ /**
403
+ * A service token obtained by attestation, cached like a credential's.
404
+ *
405
+ * The cache key is a constant, not a credential hash: a process has exactly
406
+ * one identity, so there is nothing to key on and nothing to keep separate.
407
+ * The secret-comparison guard the credential path applies has nothing to
408
+ * compare here, which is the point — there is no secret.
409
+ *
410
+ * @internal
411
+ */
412
+ _getWorkloadServiceToken(): Promise<string>;
390
413
  /**
391
414
  * Perform the actual /auth/service-token request and cache the result.
392
415
  * Separated so getServiceToken() can deduplicate concurrent calls.
@@ -799,6 +822,15 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
799
822
  username?: string;
800
823
  deviceName?: string;
801
824
  deviceFingerprint?: string;
825
+ /**
826
+ * Sign-up only: the root created on the holder before this call, sealed
827
+ * under the passkey being registered, plus its `enroll_identity` proof
828
+ * (ADR 0024 D4). The account is created WITH it or not at all.
829
+ */
830
+ identity?: {
831
+ envelope: WebIdentityEnvelope;
832
+ proof: IdentityProof;
833
+ };
802
834
  }): Promise<{
803
835
  success: true;
804
836
  message: string;
@@ -4,9 +4,9 @@
4
4
  * Provides typed access to Oxy's AtProto/Bluesky-flavoured identity &
5
5
  * portability layer:
6
6
  * - DID resolution (`did:web:oxy.so:u:<userId>`, derived on demand by the API).
7
- * - The auth-method ↔ DID verification-method mapping and its reversibility
8
- * (link/unlink an identity key, link a password) via the existing
9
- * `/auth/link` surface.
7
+ * - The auth-method ↔ DID verification-method mapping (`GET /auth/methods`) and
8
+ * passkey removal. A root is linked by the holder flows (ADR 0024) and never
9
+ * unlinked; it is replaced by rotation.
10
10
  * - Signed records: clients sign an envelope with their own cryptographic key
11
11
  * (`SignatureService.signRecord` + the shared `canonicalize`) and publish it;
12
12
  * anyone can fetch and verify it.
@@ -19,11 +19,11 @@
19
19
  * the API validates its output against, so producer and consumer cannot drift.
20
20
  *
21
21
  * Identity signing is NATIVE-ONLY: the private key lives in native secure
22
- * storage, so `linkIdentityKey`, `signRecord`, and `publishRecord` require an
22
+ * storage, so `signRecord` and `publishRecord` require an
23
23
  * on-device identity and throw on web (where `KeyManager.getPublicKey()` is
24
24
  * always `null`).
25
25
  */
26
- import type { AuthMethodsResponse, DidDocument, DomainVerificationInstructions, ExportBundle, OxySignedRecordType, SignedRecordEnvelope, VerifiedDomain } from '@oxy.so/contracts';
26
+ import type { AuthMethodsResponse, IdentityRootStatus, DidDocument, DomainVerificationInstructions, ExportBundle, OxySignedRecordType, SignedRecordEnvelope, VerifiedDomain } from '@oxy.so/contracts';
27
27
  import type { OxyServicesBase } from '../OxyServices.base';
28
28
  import { type PendingIdentityResult } from '../crypto/recoveryPhrase';
29
29
  /**
@@ -33,12 +33,7 @@ import { type PendingIdentityResult } from '../crypto/recoveryPhrase';
33
33
  * record set.
34
34
  */
35
35
  export type IdentityRecordType = OxySignedRecordType;
36
- /** Auth-method types that can be unlinked via {@link OxyServicesIdentityMixin}. */
37
- export type UnlinkableAuthMethodType = 'identity' | 'webauthn';
38
- /**
39
- * Result of a link/unlink auth-method mutation (`POST /auth/link`,
40
- * `DELETE /auth/link/:type`).
41
- */
36
+ /** Result of an auth-method mutation (`DELETE /auth/link/webauthn/:id`). */
42
37
  export interface LinkAuthMethodResult {
43
38
  success: boolean;
44
39
  message: string;
@@ -145,31 +140,22 @@ export declare function OxyServicesIdentityMixin<T extends typeof OxyServicesBas
145
140
  */
146
141
  listAuthMethods(): Promise<AuthMethodsResponse>;
147
142
  /**
148
- * Link the on-device cryptographic identity to the current account,
149
- * upgrading it from custodial to self-sovereign. Signs a proof of private
150
- * key ownership and posts it to `POST /auth/link`.
151
- *
152
- * NATIVE-ONLY: requires a stored identity (throws if `KeyManager` has no key
153
- * or no user is authenticated). The signed payload is
154
- * `JSON.stringify({ action: 'link_identity', userId, timestamp })` — the
155
- * exact bytes the server reconstructs and verifies.
156
- */
157
- linkIdentityKey(): Promise<LinkAuthMethodResult>;
158
- /**
159
- * Unlink an authentication method from the current account. The server
160
- * refuses to remove the last remaining method (the account would become
161
- * inaccessible). Unlinking `identity` downgrades the account to custodial.
162
- *
163
- * @param type - The auth-method type to remove.
143
+ * The signed-in account's root readiness (ADR 0024 D5): whether a root is
144
+ * linked, how many passkeys can open the web holder, whether the root has a
145
+ * phrase and whether it is saved and was shown to recover it. Metadata only —
146
+ * nothing here can open the root — so any first-party surface may show a
147
+ * "save your recovery phrase" reminder from it.
164
148
  */
165
- unlinkAuthMethod(type: UnlinkableAuthMethodType): Promise<LinkAuthMethodResult>;
149
+ getIdentityRootStatus(): Promise<IdentityRootStatus>;
166
150
  /**
167
151
  * Remove ONE passkey (WebAuthn credential) from the current account.
168
152
  *
169
- * Passkeys are per-credential, so unlike {@link unlinkAuthMethod} (which
170
- * removes an auth method by type) this targets a specific credential id.
153
+ * Passkeys are per-credential, so this targets a specific credential id.
171
154
  * The server refuses to remove the last remaining auth method (the account
172
155
  * would become inaccessible) and deletes the stored `WebauthnCredential`.
156
+ * A passkey that also opens the account's root on the web takes its wrap with
157
+ * it, and the ONLY such passkey is refused with
158
+ * `code: 'IDENTITY_LAST_WEB_HOLDER'` (ADR 0024 D6).
173
159
  *
174
160
  * @param credentialId - The passkey's public credential id
175
161
  * (`AuthMethodEntry.credentialId`).
@@ -34,7 +34,6 @@ import { OxyServicesNodesMixin } from './OxyServices.nodes';
34
34
  import { OxyServicesFollowGraphMixin } from './OxyServices.followGraph';
35
35
  import { OxyServicesInferenceMixin } from './OxyServices.inference';
36
36
  import { OxyServicesDeviceBootMixin } from './OxyServices.deviceBoot';
37
- import { OxyServicesDeviceTransferMixin } from './OxyServices.deviceTransfer';
38
37
  /**
39
38
  * Instance shape of every mixin in the pipeline, intersected. The runtime
40
39
  * `composeOxyServices()` produces a class whose instances expose all of
@@ -44,7 +43,7 @@ import { OxyServicesDeviceTransferMixin } from './OxyServices.deviceTransfer';
44
43
  * If you add a new mixin to `MIXIN_PIPELINE`, add it here too so its methods
45
44
  * are visible without a cast.
46
45
  */
47
- type AllMixinInstances = InstanceType<ReturnType<typeof OxyServicesAuthMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesUserMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesIdentityMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesIdentityBackupMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesPrivacyMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesLanguageMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesPaymentMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesReputationMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAssetsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAccountsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesConnectedAppsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAgencyMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesStoreMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesLocationMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAnalyticsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesDevicesMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesSecurityMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesFeaturesMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesTopicsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesContactsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesNotificationsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAppDataMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesCivicMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesChainsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesNodesMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesFollowGraphMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesInferenceMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesDeviceBootMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesDeviceTransferMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesUtilityMixin<typeof OxyServicesBase>>>;
46
+ type AllMixinInstances = InstanceType<ReturnType<typeof OxyServicesAuthMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesUserMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesIdentityMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesIdentityBackupMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesPrivacyMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesLanguageMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesPaymentMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesReputationMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAssetsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAccountsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesConnectedAppsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAgencyMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesStoreMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesLocationMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAnalyticsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesDevicesMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesSecurityMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesFeaturesMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesTopicsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesContactsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesNotificationsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesAppDataMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesCivicMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesChainsMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesNodesMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesFollowGraphMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesInferenceMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesDeviceBootMixin<typeof OxyServicesBase>>> & InstanceType<ReturnType<typeof OxyServicesUtilityMixin<typeof OxyServicesBase>>>;
48
47
  /**
49
48
  * Constructor type for the fully composed mixin pipeline. Each mixin returns
50
49
  * a new constructor that augments its input; reducing across the pipeline
@@ -38,3 +38,11 @@ export { isOfficialWebOrigin } from '../utils/officialOrigins';
38
38
  export { createEcosystemTraffic } from './traffic';
39
39
  export type { EcosystemTrafficOptions } from './traffic';
40
40
  export { observeNodeHttp, withoutNodeHttpObservation } from './trafficNodeHttp';
41
+ /**
42
+ * Workload identity (ADR 0026): a first-party service asks Oxy for a service
43
+ * token by proving what it IS, with no api key and no secret. Server-only, and
44
+ * exported from this subpath rather than the root barrel because it reaches the
45
+ * container credentials endpoint.
46
+ */
47
+ export { canAttestWorkloadIdentity, requestWorkloadServiceToken } from './workloadIdentity';
48
+ export type { WorkloadServiceToken, WorkloadServiceTokenOptions } from './workloadIdentity';