@oxyhq/contracts 0.17.0 → 0.19.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.
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * Canonical contract for the "Sign in with Oxy" approval handoff.
4
+ *
5
+ * SINGLE SOURCE OF TRUTH for the closed set of reasons an approver may attach
6
+ * when it DENIES a pending request via
7
+ * `POST /auth/session/deny/:authorizeCode`.
8
+ *
9
+ * That endpoint is UNAUTHENTICATED — the public `authorizeCode` is the only
10
+ * credential — so a free-form string from it is never stored: it would be an
11
+ * unauthenticated write of arbitrary text onto a record other surfaces read.
12
+ * The set is therefore deliberately tiny, and closed:
13
+ *
14
+ * - `'declined'` the approver rejected a request they recognised ("Not now").
15
+ * - `'not_me'` the approver did not start the request ("This wasn't me").
16
+ * The ONE value that records the denial as suspicious rather
17
+ * than an ordinary cancel, so a UI may only offer it where the
18
+ * user genuinely said so.
19
+ *
20
+ * Why this lives in `@oxyhq/contracts` rather than in either consumer: the same
21
+ * closed set is enforced in three places — the request schema of the API route,
22
+ * the `enum` of the persisted `AuthSession.deniedReason` field, and the client
23
+ * SDK's `denyCommonsSignIn` parameter. Two hand-maintained copies of a wire
24
+ * contract drift the moment a value is added on one side only, and the failure
25
+ * lands at runtime, in an auth path, as a generic validation error. One
26
+ * declaration makes that impossible.
27
+ *
28
+ * Platform-agnostic — zod only, no react/react-native/expo. ESM-safe (no
29
+ * `require()`).
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.IDENTITY_APPROVAL_PUSH_CHANNEL = exports.commonsDenyReasonSchema = exports.COMMONS_DENY_REASONS = void 0;
33
+ const zod_1 = require("zod");
34
+ /**
35
+ * The closed set, as a value — consumed directly where a runtime list is
36
+ * required (e.g. the Mongoose `enum` of `AuthSession.deniedReason`, which is
37
+ * the storage-level guarantee that an unauthenticated caller can never write
38
+ * free-form text into the field).
39
+ */
40
+ exports.COMMONS_DENY_REASONS = ['declined', 'not_me'];
41
+ /**
42
+ * The same set as a zod enum — the edge validator. Anything outside it
43
+ * (including free-form text) is rejected with 400 before any handler runs.
44
+ */
45
+ exports.commonsDenyReasonSchema = zod_1.z.enum(exports.COMMONS_DENY_REASONS);
46
+ /**
47
+ * Android notification channel id the identity-approval push is sent on.
48
+ *
49
+ * A wire contract for the same reason the deny set is: Android 8+ DROPS a
50
+ * notification whose channel id the app has not created, silently and with no
51
+ * client-side error. The API attaches this id when it sends, and the vault
52
+ * creates the channel with it before registering a push token — two hand-typed
53
+ * copies of that string would fail as "the notification never arrived", which
54
+ * is the single hardest push symptom to diagnose.
55
+ *
56
+ * The channel's user-visible NAME and description are deliberately NOT here:
57
+ * those are localized app copy, and the vault owns them.
58
+ */
59
+ exports.IDENTITY_APPROVAL_PUSH_CHANNEL = 'auth-approval';
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * Device-to-device identity transfer contracts (b3 Feature 2 — "add a device").
4
+ *
5
+ * SINGLE SOURCE OF TRUTH for the wire shape of the short-lived, unauthenticated
6
+ * relay that carries E2E-encrypted key material from an existing (old) device to
7
+ * a fresh (new) device so both end up holding the SAME secp256k1 private key
8
+ * (key cloning). The relay is E2E-encrypted via an ephemeral secp256k1 ECDH
9
+ * handshake: the server stores only the two ephemeral public keys plus an opaque
10
+ * AEAD ciphertext + nonce and NEVER holds a decryption key.
11
+ *
12
+ * Flow:
13
+ * 1. New device (no identity) generates an ephemeral pair and calls
14
+ * `POST /identity/device-transfer/init { newEphPub, newDeviceLabel? }` →
15
+ * `{ pairingId, expiresAt }`. The QR carries ONLY `pairingId` (not
16
+ * self-contained — mirrors the QR sign-in `approve-info` resolution).
17
+ * 2. Old device (has identity) scans, resolves the request via
18
+ * `GET /identity/device-transfer/:pairingId` (returns `newEphPub` + label),
19
+ * derives `transferKey = HKDF(ECDH(oldEphPriv, newEphPub), pairingId,
20
+ * 'oxy-device-transfer-v1')`, AEAD-encrypts `{ privateKey, publicKey }`, and
21
+ * calls `POST /identity/device-transfer/:pairingId/approve` with the
22
+ * ciphertext PLUS a fresh signature over
23
+ * `{ action:'approve_device_transfer', pairingId, timestamp }` made with the
24
+ * CURRENT identity key (dual-proof: a bearer alone cannot exfiltrate the key).
25
+ * 3. New device (socket push or poll fallback) re-derives the same
26
+ * `transferKey` from `ECDH(newEphPriv, oldEphPub)`, decrypts, and imports the
27
+ * private key, then completes a NORMAL challenge/verify sign-in.
28
+ *
29
+ * The load-bearing response shapes are declared as explicit `interface`s (same
30
+ * `moduleResolution: node` rationale as `UserNameResponse` / the identity/civic
31
+ * contracts: a nested `z.infer<>` can degrade to `{}` under a consumer's
32
+ * `moduleResolution: "node"`), with the runtime schemas annotated
33
+ * `z.ZodType<Interface>`.
34
+ *
35
+ * Platform-agnostic — zod only, no react/react-native/expo, ESM-safe.
36
+ */
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.deviceTransferDenyResponseSchema = exports.deviceTransferApproveResponseSchema = exports.deviceTransferApproveRequestSchema = exports.deviceTransferInfoResponseSchema = exports.deviceTransferInitResponseSchema = exports.deviceTransferInitRequestSchema = exports.devicePairingStatusSchema = void 0;
39
+ const zod_1 = require("zod");
40
+ /* -------------------------------------------------------------------------- */
41
+ /* Shared field validators */
42
+ /* -------------------------------------------------------------------------- */
43
+ /** Lowercase/uppercase hex string (no `0x` prefix). */
44
+ const hexString = zod_1.z
45
+ .string()
46
+ .trim()
47
+ .regex(/^[0-9a-fA-F]+$/, 'must be a hex string');
48
+ /**
49
+ * A secp256k1 public key, hex-encoded — compressed (`02`/`03` + 32 bytes = 66
50
+ * chars) or uncompressed (`04` + 64 bytes = 130 chars). The exact curve-point
51
+ * validity is re-checked server-side; this only bounds the shape/length.
52
+ */
53
+ const publicKeyHex = hexString.min(66).max(130);
54
+ /** DER-encoded ECDSA signature, hex (variable length, ~140–144 chars). */
55
+ const signatureHex = hexString.min(2).max(256);
56
+ /**
57
+ * The 24-byte XChaCha20-Poly1305 nonce, hex (exactly 48 chars). Matches
58
+ * `@oxyhq/core` `AEAD_NONCE_LENGTH` (24 bytes).
59
+ */
60
+ const nonceHex = hexString.length(48);
61
+ /**
62
+ * The AEAD ciphertext (Poly1305 tag appended), hex. The plaintext is the small
63
+ * JSON `{ privateKey, publicKey }` (~200 bytes), so the ciphertext stays well
64
+ * under the cap; the bound blunts relay-abuse via oversized blobs.
65
+ */
66
+ const ciphertextHex = hexString.min(2).max(8192);
67
+ /* -------------------------------------------------------------------------- */
68
+ /* Status */
69
+ /* -------------------------------------------------------------------------- */
70
+ /**
71
+ * Pairing lifecycle:
72
+ * - `pending` — created by the new device, awaiting the old device's approval.
73
+ * - `approved` — the old device sealed and posted the encrypted key material.
74
+ * - `denied` — the old device explicitly cancelled the transfer.
75
+ * - `expired` — the 3-minute TTL elapsed before approval.
76
+ */
77
+ exports.devicePairingStatusSchema = zod_1.z.enum([
78
+ 'pending',
79
+ 'approved',
80
+ 'denied',
81
+ 'expired',
82
+ ]);
83
+ /* -------------------------------------------------------------------------- */
84
+ /* POST /identity/device-transfer/init */
85
+ /* -------------------------------------------------------------------------- */
86
+ /** Request body for `POST /identity/device-transfer/init` (public). */
87
+ exports.deviceTransferInitRequestSchema = zod_1.z.object({
88
+ /** The new device's ephemeral secp256k1 public key (single-use). */
89
+ newEphPub: publicKeyHex,
90
+ /** Optional human-readable label for the new device (e.g. "iPhone 15"). */
91
+ newDeviceLabel: zod_1.z.string().trim().min(1).max(120).optional(),
92
+ });
93
+ exports.deviceTransferInitResponseSchema = zod_1.z.object({
94
+ pairingId: zod_1.z.string(),
95
+ expiresAt: zod_1.z.string(),
96
+ });
97
+ exports.deviceTransferInfoResponseSchema = zod_1.z.object({
98
+ pairingId: zod_1.z.string(),
99
+ newDeviceEphemeralPublicKey: zod_1.z.string(),
100
+ newDeviceLabel: zod_1.z.string().nullable(),
101
+ status: exports.devicePairingStatusSchema,
102
+ expiresAt: zod_1.z.string(),
103
+ oldDeviceEphemeralPublicKey: zod_1.z.string().nullable(),
104
+ ciphertext: zod_1.z.string().nullable(),
105
+ nonce: zod_1.z.string().nullable(),
106
+ });
107
+ /* -------------------------------------------------------------------------- */
108
+ /* POST /identity/device-transfer/:pairingId/approve */
109
+ /* -------------------------------------------------------------------------- */
110
+ /**
111
+ * Request body for `POST /identity/device-transfer/:pairingId/approve`
112
+ * (bearer-authenticated AND signature-proven). The `signature` covers
113
+ * `JSON.stringify({ action:'approve_device_transfer', pairingId, timestamp })`
114
+ * made with the caller's CURRENT identity key — dual-proof so a bearer token
115
+ * alone can never exfiltrate the private key.
116
+ */
117
+ exports.deviceTransferApproveRequestSchema = zod_1.z.object({
118
+ /** The old device's ephemeral secp256k1 public key (single-use). */
119
+ oldEphPub: publicKeyHex,
120
+ /** AEAD ciphertext of `{ privateKey, publicKey }`, hex. */
121
+ ciphertext: ciphertextHex,
122
+ /** AEAD nonce, hex (24 bytes). */
123
+ nonce: nonceHex,
124
+ /** ECDSA (DER, hex) signature proving control of the CURRENT identity key. */
125
+ signature: signatureHex,
126
+ /** Signing timestamp (ms since epoch) — freshness-checked server-side. */
127
+ timestamp: zod_1.z.number().int().positive(),
128
+ });
129
+ exports.deviceTransferApproveResponseSchema = zod_1.z.object({
130
+ success: zod_1.z.boolean(),
131
+ pairingId: zod_1.z.string(),
132
+ status: exports.devicePairingStatusSchema,
133
+ });
134
+ exports.deviceTransferDenyResponseSchema = zod_1.z.object({
135
+ success: zod_1.z.boolean(),
136
+ pairingId: zod_1.z.string(),
137
+ status: exports.devicePairingStatusSchema,
138
+ });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sessionAccountsChangedEventSchema = exports.sessionAccountsChangedReasonSchema = exports.SESSION_ACCOUNTS_CHANGED_EVENT = exports.deviceHubTicketRedeemResponseSchema = exports.deviceHubTicketRedeemRequestSchema = exports.deviceHubTicketIssueResponseSchema = exports.deviceHubTicketIssueRequestSchema = exports.deviceTokenMintResponseSchema = exports.deviceTokenMintRequestSchema = exports.deviceSessionSyncSchema = exports.activeTokenSchema = exports.deviceSessionStateSchema = exports.sessionAccountSchema = void 0;
3
+ exports.sessionAccountsChangedEventSchema = exports.sessionAccountsChangedReasonSchema = exports.SESSION_ACCOUNTS_CHANGED_EVENT = exports.deviceTokenMintResponseSchema = exports.deviceTokenMintRequestSchema = exports.deviceSessionSyncSchema = exports.activeTokenSchema = exports.deviceSessionStateSchema = exports.sessionAccountSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  exports.sessionAccountSchema = zod_1.z.object({
6
6
  accountId: zod_1.z.string(),
@@ -32,10 +32,19 @@ exports.deviceSessionSyncSchema = zod_1.z.object({
32
32
  * possession of the secret IS the proof of device ownership. The server matches
33
33
  * `sha256(deviceSecret)` against the device's stored `secretHash` (constant-time)
34
34
  * and mints a short access token for the device's active account.
35
+ *
36
+ * `accountId` pins the mint to ONE account of that device instead of whichever
37
+ * account is currently active. It exists for identity-bound clients (Commons),
38
+ * whose authenticated user is determined by a local cryptographic key and must
39
+ * never follow an account switch made by another app on the same device. The
40
+ * account must already be a member of the device session; the mint NEVER
41
+ * mutates `activeAccountId`, so pinning is read-only with respect to the device
42
+ * state every other app observes.
35
43
  */
36
44
  exports.deviceTokenMintRequestSchema = zod_1.z.object({
37
45
  deviceId: zod_1.z.string().min(1),
38
46
  deviceSecret: zod_1.z.string().min(1),
47
+ accountId: zod_1.z.string().min(1).optional(),
39
48
  });
40
49
  /**
41
50
  * Wire shape of a successful `POST /session/device/token`: the freshly-minted
@@ -51,28 +60,6 @@ exports.deviceTokenMintResponseSchema = zod_1.z.object({
51
60
  state: exports.deviceSessionStateSchema,
52
61
  });
53
62
  /* -------------------------------------------------------------------------- */
54
- /* Hub ticket — server-side cross-origin device credential sync */
55
- /* -------------------------------------------------------------------------- */
56
- /** Request body for `POST /session/device/hub-ticket`. */
57
- exports.deviceHubTicketIssueRequestSchema = zod_1.z.object({
58
- returnOrigin: zod_1.z.string().min(1),
59
- });
60
- /** Response from `POST /session/device/hub-ticket`. */
61
- exports.deviceHubTicketIssueResponseSchema = zod_1.z.object({
62
- ticket: zod_1.z.string().min(1),
63
- expiresIn: zod_1.z.number().int().positive(),
64
- });
65
- /** Request body for `POST /session/device/redeem-ticket`. */
66
- exports.deviceHubTicketRedeemRequestSchema = zod_1.z.object({
67
- ticket: zod_1.z.string().min(1),
68
- returnOrigin: zod_1.z.string().min(1),
69
- });
70
- /** Response from `POST /session/device/redeem-ticket`. */
71
- exports.deviceHubTicketRedeemResponseSchema = zod_1.z.object({
72
- deviceId: zod_1.z.string().min(1),
73
- deviceSecret: zod_1.z.string().min(1),
74
- });
75
- /* -------------------------------------------------------------------------- */
76
63
  /* Instant cross-app session sync (token-free socket signal) */
77
64
  /* -------------------------------------------------------------------------- */
78
65
  /**
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * Canonical contract for Inbox new-mail push notifications.
4
+ *
5
+ * The Android channel id and payload `type` are wire contracts: Android 8+
6
+ * drops a notification whose channel the app has not created, and the client
7
+ * only routes taps it recognises. Two hand-typed copies of either string fail as
8
+ * "the notification never arrived" or "tapping does nothing" — the hardest push
9
+ * symptoms to diagnose.
10
+ *
11
+ * Platform-agnostic — zod only, no react/react-native/expo.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.inboxEmailPushDataSchema = exports.INBOX_EMAIL_PUSH_TYPE = exports.INBOX_EMAIL_PUSH_CHANNEL = void 0;
15
+ const zod_1 = require("zod");
16
+ /** Android notification channel id the new-mail push is sent on. */
17
+ exports.INBOX_EMAIL_PUSH_CHANNEL = 'email';
18
+ /** Runtime type discriminator of the new-mail push payload. */
19
+ exports.INBOX_EMAIL_PUSH_TYPE = 'oxy_inbox_new_message';
20
+ exports.inboxEmailPushDataSchema = zod_1.z.object({
21
+ type: zod_1.z.literal(exports.INBOX_EMAIL_PUSH_TYPE),
22
+ messageId: zod_1.z.string().min(1),
23
+ mailboxId: zod_1.z.string().min(1),
24
+ });
package/dist/cjs/index.js CHANGED
@@ -11,9 +11,9 @@
11
11
  * expo, no `require()` in the ESM build.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.validationOpenResultSchema = exports.validationOpenRequestSchema = exports.validationVerdictRecordSchema = exports.realLifeAttestationResultSchema = exports.realLifeAttestationRecordSchema = exports.signedPublicCardSchema = exports.publicCardSchema = exports.logPageResponseSchema = exports.chainHeadResponseSchema = exports.oxySignedRecordTypeSchema = exports.exportBundleSchema = exports.exportAttestationSchema = exports.authMethodsResponseSchema = exports.authMethodEntrySchema = exports.domainVerificationInstructionsSchema = exports.domainVerificationRequestSchema = exports.verifiedDomainSchema = exports.signedRecordEnvelopeSchema = exports.didDocumentSchema = exports.didServiceSchema = exports.verificationMethodSchema = exports.appAffinityEventsIngestSchema = exports.appAffinityEventSchema = exports.appAffinityEventTypeSchema = exports.appUserSignalIngestSchema = exports.appInterestInputSchema = exports.appEndorsementInputSchema = exports.recommendationResponseSchema = exports.recommendationItemSchema = exports.recommendationCountSchema = exports.recommendationRequestSchema = exports.recommendationSignalWeightsSchema = exports.recommendationBoostSchema = exports.recommendationExcludeTypeSchema = exports.sessionStatusSchema = exports.publicApplicationSchema = exports.applicationTypeSchema = exports.safeParseContract = exports.resolveUserId = exports.deviceLinkedSessionsResponseSchema = exports.deviceLinkedSessionSchema = exports.currentUserResponseSchema = exports.userProfileUpdateSchema = exports.userResponseSchema = exports.themePreferenceSchema = exports.userRelationshipSchema = exports.userNameSchema = exports.createAccountRequestSchema = exports.organizationCategorySchema = exports.ORGANIZATION_CATEGORIES = void 0;
15
- exports.assetCompleteResponseSchema = exports.assetCompleteResultItemSchema = exports.assetCompleteRequestSchema = exports.assetInitResponseSchema = exports.assetUploadTicketSchema = exports.assetInitRequestSchema = exports.assetInitItemSchema = exports.rolloutPercentSchema = exports.runtimeVersionSchema = exports.channelNameSchema = exports.sha256HexSchema = exports.updateAssetStatusSchema = exports.updateStatusSchema = exports.updatePlatformSchema = exports.backupStatusResponseSchema = exports.backupUploadRequestSchema = exports.encryptedBackupEnvelopeSchema = exports.backupLookupIdSchema = exports.rotateKeyCompleteResponseSchema = exports.rotateKeyCompleteRequestSchema = exports.rotateKeyChallengeResponseSchema = exports.loginResultSchema = exports.sessionAccountsChangedEventSchema = exports.sessionAccountsChangedReasonSchema = exports.SESSION_ACCOUNTS_CHANGED_EVENT = exports.deviceHubTicketRedeemResponseSchema = exports.deviceHubTicketRedeemRequestSchema = exports.deviceHubTicketIssueResponseSchema = exports.deviceHubTicketIssueRequestSchema = exports.deviceTokenMintResponseSchema = exports.deviceTokenMintRequestSchema = exports.deviceSessionSyncSchema = exports.activeTokenSchema = exports.deviceSessionStateSchema = exports.sessionAccountSchema = exports.linkPreviewResponseSchema = exports.linkPreviewBatchResponseSchema = exports.linkPreviewBatchRequestSchema = exports.linkPreviewSchema = exports.credentialVerifyResultSchema = exports.credentialListResultSchema = exports.credentialIssueResultSchema = exports.verifiableCredentialResponseSchema = exports.credentialRecordSchema = exports.vouchResultSchema = exports.personhoodStatusResultSchema = exports.personhoodBreakdownSchema = exports.personhoodVouchRecordSchema = exports.validationVoteResultSchema = exports.validationRequestSummarySchema = void 0;
16
- exports.webauthnLoginVerifyRequestSchema = exports.webauthnRegisterVerifyRequestSchema = exports.webauthnLoginOptionsRequestSchema = exports.webauthnRegisterOptionsRequestSchema = exports.updateRolloutPatchSchema = exports.promoteRequestSchema = exports.rollbackToEmbeddedRequestSchema = exports.rollbackRequestSchema = exports.updateListResponseSchema = exports.channelListResponseSchema = exports.channelSchema = exports.rollbackToEmbeddedEntrySchema = exports.createUpdateResponseSchema = exports.updateSchema = exports.createUpdateRequestSchema = exports.updateAssetRefSchema = void 0;
14
+ exports.publicCardSchema = exports.logPageResponseSchema = exports.chainHeadResponseSchema = exports.oxySignedRecordTypeSchema = exports.exportBundleSchema = exports.exportAttestationSchema = exports.authMethodsResponseSchema = exports.authMethodEntrySchema = exports.domainVerificationInstructionsSchema = exports.domainVerificationRequestSchema = exports.verifiedDomainSchema = exports.signedRecordEnvelopeSchema = exports.didDocumentSchema = exports.didServiceSchema = exports.verificationMethodSchema = exports.appAffinityEventsIngestSchema = exports.appAffinityEventSchema = exports.appAffinityEventTypeSchema = exports.appUserSignalIngestSchema = exports.appInterestInputSchema = exports.appEndorsementInputSchema = exports.recommendationResponseSchema = exports.recommendationItemSchema = exports.recommendationCountSchema = exports.recommendationRequestSchema = exports.recommendationSignalWeightsSchema = exports.recommendationBoostSchema = exports.recommendationExcludeTypeSchema = exports.inboxEmailPushDataSchema = exports.INBOX_EMAIL_PUSH_TYPE = exports.INBOX_EMAIL_PUSH_CHANNEL = exports.IDENTITY_APPROVAL_PUSH_CHANNEL = exports.commonsDenyReasonSchema = exports.COMMONS_DENY_REASONS = exports.sessionStatusSchema = exports.publicApplicationSchema = exports.applicationTypeSchema = exports.safeParseContract = exports.resolveUserId = exports.deviceLinkedSessionsResponseSchema = exports.deviceLinkedSessionSchema = exports.currentUserResponseSchema = exports.userProfileUpdateSchema = exports.userResponseSchema = exports.themePreferenceSchema = exports.userRelationshipSchema = exports.userNameSchema = exports.createAccountRequestSchema = exports.organizationCategorySchema = exports.ORGANIZATION_CATEGORIES = void 0;
15
+ exports.assetCompleteRequestSchema = exports.assetInitResponseSchema = exports.assetUploadTicketSchema = exports.assetInitRequestSchema = exports.assetInitItemSchema = exports.rolloutPercentSchema = exports.runtimeVersionSchema = exports.channelNameSchema = exports.sha256HexSchema = exports.updateAssetStatusSchema = exports.updateStatusSchema = exports.updatePlatformSchema = exports.backupStatusResponseSchema = exports.backupUploadRequestSchema = exports.encryptedBackupEnvelopeSchema = exports.backupLookupIdSchema = exports.rotateKeyCompleteResponseSchema = exports.rotateKeyCompleteRequestSchema = exports.rotateKeyChallengeResponseSchema = exports.loginResultSchema = exports.sessionAccountsChangedEventSchema = exports.sessionAccountsChangedReasonSchema = exports.SESSION_ACCOUNTS_CHANGED_EVENT = exports.deviceTokenMintResponseSchema = exports.deviceTokenMintRequestSchema = exports.deviceSessionSyncSchema = exports.activeTokenSchema = exports.deviceSessionStateSchema = exports.sessionAccountSchema = exports.linkPreviewResponseSchema = exports.linkPreviewBatchResponseSchema = exports.linkPreviewBatchRequestSchema = exports.linkPreviewSchema = exports.credentialVerifyResultSchema = exports.credentialListResultSchema = exports.credentialIssueResultSchema = exports.verifiableCredentialResponseSchema = exports.credentialRecordSchema = exports.vouchResultSchema = exports.personhoodStatusResultSchema = exports.personhoodBreakdownSchema = exports.personhoodVouchRecordSchema = exports.validationVoteResultSchema = exports.validationRequestSummarySchema = exports.validationOpenResultSchema = exports.validationOpenRequestSchema = exports.validationVerdictRecordSchema = exports.realLifeAttestationResultSchema = exports.realLifeAttestationRecordSchema = exports.signedPublicCardSchema = void 0;
16
+ exports.transparencyCheckpointListSchema = exports.transparencyInclusionProofSchema = exports.transparencyCheckpointSchema = exports.transparencyAnchorSchema = exports.transparencyCheckpointSignatureSchema = exports.deviceTransferDenyResponseSchema = exports.deviceTransferApproveResponseSchema = exports.deviceTransferApproveRequestSchema = exports.deviceTransferInfoResponseSchema = exports.deviceTransferInitResponseSchema = exports.deviceTransferInitRequestSchema = exports.devicePairingStatusSchema = exports.webauthnLoginVerifyRequestSchema = exports.webauthnRegisterVerifyRequestSchema = exports.webauthnLoginOptionsRequestSchema = exports.webauthnRegisterOptionsRequestSchema = exports.updateRolloutPatchSchema = exports.promoteRequestSchema = exports.rollbackToEmbeddedRequestSchema = exports.rollbackRequestSchema = exports.updateListResponseSchema = exports.channelListResponseSchema = exports.channelSchema = exports.rollbackToEmbeddedEntrySchema = exports.createUpdateResponseSchema = exports.updateSchema = exports.createUpdateRequestSchema = exports.updateAssetRefSchema = exports.assetCompleteResponseSchema = exports.assetCompleteResultItemSchema = void 0;
17
17
  var accountGraph_1 = require("./accountGraph");
18
18
  Object.defineProperty(exports, "ORGANIZATION_CATEGORIES", { enumerable: true, get: function () { return accountGraph_1.ORGANIZATION_CATEGORIES; } });
19
19
  Object.defineProperty(exports, "organizationCategorySchema", { enumerable: true, get: function () { return accountGraph_1.organizationCategorySchema; } });
@@ -36,6 +36,17 @@ var sessionStatus_1 = require("./sessionStatus");
36
36
  Object.defineProperty(exports, "applicationTypeSchema", { enumerable: true, get: function () { return sessionStatus_1.applicationTypeSchema; } });
37
37
  Object.defineProperty(exports, "publicApplicationSchema", { enumerable: true, get: function () { return sessionStatus_1.publicApplicationSchema; } });
38
38
  Object.defineProperty(exports, "sessionStatusSchema", { enumerable: true, get: function () { return sessionStatus_1.sessionStatusSchema; } });
39
+ var commonsSignIn_1 = require("./commonsSignIn");
40
+ // Closed set of denial reasons for POST /auth/session/deny/:authorizeCode —
41
+ // shared by the API request schema, the persisted `AuthSession.deniedReason`
42
+ // enum, and the SDK's `denyCommonsSignIn`.
43
+ Object.defineProperty(exports, "COMMONS_DENY_REASONS", { enumerable: true, get: function () { return commonsSignIn_1.COMMONS_DENY_REASONS; } });
44
+ Object.defineProperty(exports, "commonsDenyReasonSchema", { enumerable: true, get: function () { return commonsSignIn_1.commonsDenyReasonSchema; } });
45
+ Object.defineProperty(exports, "IDENTITY_APPROVAL_PUSH_CHANNEL", { enumerable: true, get: function () { return commonsSignIn_1.IDENTITY_APPROVAL_PUSH_CHANNEL; } });
46
+ var inboxPush_1 = require("./inboxPush");
47
+ Object.defineProperty(exports, "INBOX_EMAIL_PUSH_CHANNEL", { enumerable: true, get: function () { return inboxPush_1.INBOX_EMAIL_PUSH_CHANNEL; } });
48
+ Object.defineProperty(exports, "INBOX_EMAIL_PUSH_TYPE", { enumerable: true, get: function () { return inboxPush_1.INBOX_EMAIL_PUSH_TYPE; } });
49
+ Object.defineProperty(exports, "inboxEmailPushDataSchema", { enumerable: true, get: function () { return inboxPush_1.inboxEmailPushDataSchema; } });
39
50
  var recommendations_1 = require("./recommendations");
40
51
  // Schemas
41
52
  Object.defineProperty(exports, "recommendationExcludeTypeSchema", { enumerable: true, get: function () { return recommendations_1.recommendationExcludeTypeSchema; } });
@@ -105,10 +116,6 @@ Object.defineProperty(exports, "activeTokenSchema", { enumerable: true, get: fun
105
116
  Object.defineProperty(exports, "deviceSessionSyncSchema", { enumerable: true, get: function () { return deviceSession_1.deviceSessionSyncSchema; } });
106
117
  Object.defineProperty(exports, "deviceTokenMintRequestSchema", { enumerable: true, get: function () { return deviceSession_1.deviceTokenMintRequestSchema; } });
107
118
  Object.defineProperty(exports, "deviceTokenMintResponseSchema", { enumerable: true, get: function () { return deviceSession_1.deviceTokenMintResponseSchema; } });
108
- Object.defineProperty(exports, "deviceHubTicketIssueRequestSchema", { enumerable: true, get: function () { return deviceSession_1.deviceHubTicketIssueRequestSchema; } });
109
- Object.defineProperty(exports, "deviceHubTicketIssueResponseSchema", { enumerable: true, get: function () { return deviceSession_1.deviceHubTicketIssueResponseSchema; } });
110
- Object.defineProperty(exports, "deviceHubTicketRedeemRequestSchema", { enumerable: true, get: function () { return deviceSession_1.deviceHubTicketRedeemRequestSchema; } });
111
- Object.defineProperty(exports, "deviceHubTicketRedeemResponseSchema", { enumerable: true, get: function () { return deviceSession_1.deviceHubTicketRedeemResponseSchema; } });
112
119
  Object.defineProperty(exports, "SESSION_ACCOUNTS_CHANGED_EVENT", { enumerable: true, get: function () { return deviceSession_1.SESSION_ACCOUNTS_CHANGED_EVENT; } });
113
120
  Object.defineProperty(exports, "sessionAccountsChangedReasonSchema", { enumerable: true, get: function () { return deviceSession_1.sessionAccountsChangedReasonSchema; } });
114
121
  Object.defineProperty(exports, "sessionAccountsChangedEventSchema", { enumerable: true, get: function () { return deviceSession_1.sessionAccountsChangedEventSchema; } });
@@ -164,3 +171,19 @@ Object.defineProperty(exports, "webauthnRegisterOptionsRequestSchema", { enumera
164
171
  Object.defineProperty(exports, "webauthnLoginOptionsRequestSchema", { enumerable: true, get: function () { return webauthn_1.webauthnLoginOptionsRequestSchema; } });
165
172
  Object.defineProperty(exports, "webauthnRegisterVerifyRequestSchema", { enumerable: true, get: function () { return webauthn_1.webauthnRegisterVerifyRequestSchema; } });
166
173
  Object.defineProperty(exports, "webauthnLoginVerifyRequestSchema", { enumerable: true, get: function () { return webauthn_1.webauthnLoginVerifyRequestSchema; } });
174
+ var devicePairing_1 = require("./devicePairing");
175
+ // Schemas
176
+ Object.defineProperty(exports, "devicePairingStatusSchema", { enumerable: true, get: function () { return devicePairing_1.devicePairingStatusSchema; } });
177
+ Object.defineProperty(exports, "deviceTransferInitRequestSchema", { enumerable: true, get: function () { return devicePairing_1.deviceTransferInitRequestSchema; } });
178
+ Object.defineProperty(exports, "deviceTransferInitResponseSchema", { enumerable: true, get: function () { return devicePairing_1.deviceTransferInitResponseSchema; } });
179
+ Object.defineProperty(exports, "deviceTransferInfoResponseSchema", { enumerable: true, get: function () { return devicePairing_1.deviceTransferInfoResponseSchema; } });
180
+ Object.defineProperty(exports, "deviceTransferApproveRequestSchema", { enumerable: true, get: function () { return devicePairing_1.deviceTransferApproveRequestSchema; } });
181
+ Object.defineProperty(exports, "deviceTransferApproveResponseSchema", { enumerable: true, get: function () { return devicePairing_1.deviceTransferApproveResponseSchema; } });
182
+ Object.defineProperty(exports, "deviceTransferDenyResponseSchema", { enumerable: true, get: function () { return devicePairing_1.deviceTransferDenyResponseSchema; } });
183
+ var transparency_1 = require("./transparency");
184
+ // Schemas — transparency log (checkpoints + inclusion proofs)
185
+ Object.defineProperty(exports, "transparencyCheckpointSignatureSchema", { enumerable: true, get: function () { return transparency_1.transparencyCheckpointSignatureSchema; } });
186
+ Object.defineProperty(exports, "transparencyAnchorSchema", { enumerable: true, get: function () { return transparency_1.transparencyAnchorSchema; } });
187
+ Object.defineProperty(exports, "transparencyCheckpointSchema", { enumerable: true, get: function () { return transparency_1.transparencyCheckpointSchema; } });
188
+ Object.defineProperty(exports, "transparencyInclusionProofSchema", { enumerable: true, get: function () { return transparency_1.transparencyInclusionProofSchema; } });
189
+ Object.defineProperty(exports, "transparencyCheckpointListSchema", { enumerable: true, get: function () { return transparency_1.transparencyCheckpointListSchema; } });
@@ -93,6 +93,14 @@ exports.publicApplicationSchema = zod_1.z.object({
93
93
  * current producer and are never `null`, but stay `.optional()` so the contract
94
94
  * tolerates leaner shapes from other producers of this same payload without a
95
95
  * coordinated bump.
96
+ *
97
+ * `pushSentAt` / `openedAt` are DELIVERY PROGRESS, not authorization state: they
98
+ * let a waiting surface render "Check Commons on your phone" → "Opened in
99
+ * Commons" without inventing competing statuses. `status` remains the only
100
+ * authority on whether the request is pending, authorized, cancelled or expired.
101
+ * Both are `.nullable().optional()` for the same reason as `sessionId` — the
102
+ * producer always emits the key with `null` until that step happens, and an
103
+ * older API that omits them entirely must degrade, not fail the parse.
96
104
  */
97
105
  exports.sessionStatusSchema = zod_1.z.object({
98
106
  status: zod_1.z.string(),
@@ -103,4 +111,11 @@ exports.sessionStatusSchema = zod_1.z.object({
103
111
  sessionId: zod_1.z.string().nullable().optional(),
104
112
  publicKey: zod_1.z.string().nullable().optional(),
105
113
  userId: zod_1.z.string().nullable().optional(),
114
+ /**
115
+ * What approving this request does. Legacy rows read as `device_sign_in`.
116
+ * OAuth-bound sessions finalize into an authorization code (no `sessionId`).
117
+ */
118
+ purpose: zod_1.z.enum(['device_sign_in', 'oauth_authorization']).optional(),
119
+ pushSentAt: zod_1.z.string().nullable().optional(),
120
+ openedAt: zod_1.z.string().nullable().optional(),
106
121
  });
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transparencyCheckpointListSchema = exports.transparencyInclusionProofSchema = exports.transparencyCheckpointSchema = exports.transparencyAnchorSchema = exports.transparencyCheckpointSignatureSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Transparency log — the public wire contract of the checkpoint surface.
7
+ *
8
+ * A checkpoint is the operator's signed commitment to EVERY subject's chain head
9
+ * at a point in time: "at `periodEnd` I committed to `root` over `treeSize`
10
+ * subjects, and the previous checkpoint hashed to `prevCheckpointHash`". Anyone
11
+ * can then ask for an inclusion proof of their own head and verify it against
12
+ * that root without trusting the server — which is what closes the one gap a
13
+ * per-subject hash chain cannot close on its own (the server serving two
14
+ * different histories, or quietly dropping a record).
15
+ *
16
+ * The Merkle math, the leaf/checkpoint signing bytes, and the proof verifier all
17
+ * live in `@oxyhq/protocol` (`src/transparency/`); this module only fixes the
18
+ * SHAPES that cross the wire, so a client and the API cannot drift on them.
19
+ *
20
+ * Digest fields are pinned to 64-char LOWERCASE hex on purpose: the digests are
21
+ * compared as strings against locally recomputed hashes, so accepting an
22
+ * upper-case or truncated variant would turn a real mismatch into a confusing
23
+ * verification failure at a distance.
24
+ */
25
+ /** A SHA-256 digest in the exact form the protocol emits: 64 lowercase hex chars. */
26
+ const hexDigestSchema = zod_1.z.string().regex(/^[0-9a-f]{64}$/, 'Expected a 64-char lowercase hex digest');
27
+ /**
28
+ * One signer's endorsement of a checkpoint's signed fields.
29
+ *
30
+ * The operator and every independent witness produce this same shape over the
31
+ * SAME bytes, so the array on a checkpoint can grow without coordination.
32
+ */
33
+ exports.transparencyCheckpointSignatureSchema = zod_1.z.object({
34
+ /** Uncompressed hex public key of the signer. */
35
+ publicKey: zod_1.z.string().min(1),
36
+ alg: zod_1.z.literal('ES256K-DER-SHA256'),
37
+ /** DER-encoded hex secp256k1 signature over the checkpoint signing input. */
38
+ signature: zod_1.z.string().min(1),
39
+ });
40
+ /** Where a checkpoint root was published on a public chain. */
41
+ exports.transparencyAnchorSchema = zod_1.z.object({
42
+ /** Chain/network identifier, e.g. `faircoin-main`. */
43
+ network: zod_1.z.string().min(1),
44
+ txid: zod_1.z.string().min(1),
45
+ confirmations: zod_1.z.number().int().nonnegative(),
46
+ /** When the anchoring transaction was broadcast (ms epoch). */
47
+ anchoredAt: zod_1.z.number().int().positive(),
48
+ });
49
+ /**
50
+ * A published checkpoint.
51
+ *
52
+ * `signatures` is non-empty by contract: an unsigned root commits nobody and
53
+ * must never be served as a checkpoint. `anchors` may be empty — a checkpoint is
54
+ * published immediately and anchored asynchronously, so "not yet anchored" is a
55
+ * normal, temporary state rather than an error.
56
+ */
57
+ exports.transparencyCheckpointSchema = zod_1.z.object({
58
+ index: zod_1.z.number().int().nonnegative(),
59
+ /** End of the committed period (ms epoch). */
60
+ periodEnd: zod_1.z.number().int().positive(),
61
+ /** Number of subjects (leaves) committed. */
62
+ treeSize: zod_1.z.number().int().nonnegative(),
63
+ root: hexDigestSchema,
64
+ /** Hash of the previous checkpoint; `null` only at genesis. */
65
+ prevCheckpointHash: hexDigestSchema.nullable(),
66
+ signatures: zod_1.z.array(exports.transparencyCheckpointSignatureSchema).min(1),
67
+ anchors: zod_1.z.array(exports.transparencyAnchorSchema),
68
+ });
69
+ /**
70
+ * An inclusion proof for one subject against one checkpoint.
71
+ *
72
+ * Carries the leaf PREIMAGE (`subjectDid`, `seq`, `headRecordId`) as well as the
73
+ * `leaf` digest so the verifier re-derives the leaf itself rather than trusting
74
+ * the server's hash, then walks `proof` up to the checkpoint's `root`.
75
+ */
76
+ exports.transparencyInclusionProofSchema = zod_1.z.object({
77
+ checkpoint: exports.transparencyCheckpointSchema,
78
+ subjectDid: zod_1.z.string().min(1),
79
+ seq: zod_1.z.number().int().nonnegative(),
80
+ headRecordId: hexDigestSchema,
81
+ leaf: hexDigestSchema,
82
+ leafIndex: zod_1.z.number().int().nonnegative(),
83
+ /** Audit path, leaf-adjacent sibling first; empty for a single-leaf tree. */
84
+ proof: zod_1.z.array(hexDigestSchema),
85
+ });
86
+ /** A page of the checkpoint chain, oldest first, for walking `prevCheckpointHash`. */
87
+ exports.transparencyCheckpointListSchema = zod_1.z.object({
88
+ checkpoints: zod_1.z.array(exports.transparencyCheckpointSchema),
89
+ });