@oxy.so/contracts 2.2.0 → 3.0.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 (39) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/accountEmail.js +21 -25
  3. package/dist/cjs/deviceBoot.js +2 -2
  4. package/dist/cjs/deviceSession.js +73 -16
  5. package/dist/cjs/identity.js +1 -3
  6. package/dist/cjs/identityLink.js +22 -19
  7. package/dist/cjs/identityProof.js +2 -2
  8. package/dist/cjs/index.js +59 -28
  9. package/dist/cjs/reputation.js +10 -55
  10. package/dist/cjs/signIn.js +304 -0
  11. package/dist/esm/.tsbuildinfo +1 -1
  12. package/dist/esm/accountEmail.js +21 -25
  13. package/dist/esm/deviceBoot.js +2 -2
  14. package/dist/esm/deviceSession.js +72 -15
  15. package/dist/esm/identity.js +1 -3
  16. package/dist/esm/identityLink.js +21 -18
  17. package/dist/esm/identityProof.js +2 -2
  18. package/dist/esm/index.js +11 -11
  19. package/dist/esm/reputation.js +9 -54
  20. package/dist/esm/signIn.js +299 -0
  21. package/dist/types/.tsbuildinfo +1 -1
  22. package/dist/types/accountEmail.d.ts +20 -28
  23. package/dist/types/accountGraph.d.ts +4 -4
  24. package/dist/types/deviceBoot.d.ts +2 -2
  25. package/dist/types/deviceSession.d.ts +127 -15
  26. package/dist/types/externalIdentity.d.ts +4 -4
  27. package/dist/types/identity.d.ts +4 -8
  28. package/dist/types/identityLink.d.ts +54 -118
  29. package/dist/types/identityProof.d.ts +3 -3
  30. package/dist/types/index.d.ts +8 -8
  31. package/dist/types/inference/entitlement.d.ts +2 -2
  32. package/dist/types/oauth.d.ts +16 -16
  33. package/dist/types/reputation.d.ts +32 -127
  34. package/dist/types/signIn.d.ts +717 -0
  35. package/dist/types/userResponse.d.ts +2 -2
  36. package/package.json +1 -1
  37. package/dist/cjs/webauthn.js +0 -114
  38. package/dist/esm/webauthn.js +0 -111
  39. package/dist/types/webauthn.d.ts +0 -173
@@ -1,22 +1,26 @@
1
1
  /**
2
- * Recovery email contracts (ADR 0029 D3).
2
+ * Email codes and tickets (ADR 0030).
3
3
  *
4
- * A web account is a username, a passkey and a recovery email. The email is
5
- * proven by a 6-digit code sent to it, and the proof is a short-lived one-use
6
- * ticket the next step spends:
4
+ * A web account is a username and an email; a password and an authenticator
5
+ * app are optional and added later. The email is proven by a 6-digit code sent
6
+ * to it, and the proof is a short-lived one-use ticket the next step spends:
7
7
  *
8
- * - `signup`: the ticket lets `POST /webauthn/register/verify` create the
9
- * account with that email;
10
- * - `recovery`: the person names their username or email, the code goes to the
11
- * account's recovery email, and the ticket lets them register a new passkey
12
- * for that account.
8
+ * - `signup`: the ticket lets `POST /auth/signup` create the account with that
9
+ * email. `start` answers the same whether or not the address already has an
10
+ * account, so it never tells anyone which emails have an Oxy account.
13
11
  *
14
- * `start` answers the same whether or not an account exists, so neither purpose
15
- * tells anyone which emails or usernames have an Oxy account. An account with a
16
- * Commons key has no recovery email: it recovers in Commons.
12
+ * Signing in by email (`signin`) and re-verifying before a sensitive change
13
+ * (`reauth`) use the same code, through their own routes (`signIn.ts`).
17
14
  */
18
15
  import { z } from 'zod';
19
- export const EMAIL_VERIFICATION_PURPOSES = ['signup', 'recovery'];
16
+ /**
17
+ * - `signup`: above.
18
+ * - `signin`: the code (and link) of an email sign-in (`POST /auth/signin/email/start`).
19
+ * - `reauth`: a signed-in person proving it is them before a sensitive step
20
+ * (`POST /users/me/reauth/email`): a password, an authenticator, deleting the
21
+ * account, linking Commons.
22
+ */
23
+ export const EMAIL_VERIFICATION_PURPOSES = ['signup', 'signin', 'reauth'];
20
24
  /** Digits in a code. */
21
25
  export const EMAIL_CODE_LENGTH = 6;
22
26
  /** How long a code can be confirmed. */
@@ -33,16 +37,9 @@ export const emailTicketSchema = z
33
37
  .trim()
34
38
  .regex(/^[A-Za-z0-9_-]{43}$/, 'ticket must be 32 bytes of base64url');
35
39
  /** `POST /auth/email/verify/start` */
36
- export const emailVerificationStartRequestSchema = z.discriminatedUnion('purpose', [
37
- z.object({ purpose: z.literal('signup'), email: emailAddressSchema }).strict(),
38
- z
39
- .object({
40
- purpose: z.literal('recovery'),
41
- /** The account's username, or its recovery email. */
42
- identifier: z.string().trim().min(1).max(254),
43
- })
44
- .strict(),
45
- ]);
40
+ export const emailVerificationStartRequestSchema = z
41
+ .object({ purpose: z.literal('signup'), email: emailAddressSchema })
42
+ .strict();
46
43
  export const emailVerificationStartResponseSchema = z.object({
47
44
  verificationId: z.string().min(1).max(64),
48
45
  expiresAt: z.number().int().positive(),
@@ -60,7 +57,6 @@ export const emailVerificationConfirmRequestSchema = z
60
57
  export const emailVerificationConfirmResponseSchema = z.object({
61
58
  ticket: emailTicketSchema,
62
59
  expiresAt: z.number().int().positive(),
63
- username: z.string().nullable(),
64
60
  });
65
61
  /**
66
62
  * Stable error codes (`error.code` in the API error body). Clients map these
@@ -73,7 +69,7 @@ export const EMAIL_VERIFICATION_ERROR_CODES = {
73
69
  tooManyAttempts: 'EMAIL_CODE_TOO_MANY_ATTEMPTS',
74
70
  /** The ticket is unknown, expired, spent, or for another email or purpose. */
75
71
  ticketInvalid: 'EMAIL_TICKET_INVALID',
76
- /** A sign-up without a confirmed recovery email. */
72
+ /** A sign-up without a confirmed email. */
77
73
  ticketRequired: 'EMAIL_TICKET_REQUIRED',
78
74
  /** This server cannot send mail. */
79
75
  unavailable: 'EMAIL_UNAVAILABLE',
@@ -4,8 +4,8 @@
4
4
  * SINGLE SOURCE OF TRUTH for the first-party login result (the session arm). The
5
5
  * API validates its OUTPUT against this schema; every consumer (`@oxy.so/core`'s
6
6
  * auth mixin) validates its INPUT against the same definition, so producer and
7
- * consumers cannot drift. Sign-in is passkey (WebAuthn) or Commons handoff —
8
- * password and 2FA were removed, so the only outcome is a completed session.
7
+ * consumers cannot drift. This is the session arm every sign-in
8
+ * ends in (email code or link, password, authenticator, Commons handoff).
9
9
  *
10
10
  * The device transport is `deviceId` + `deviceSecret` + `POST /session/device/token`
11
11
  * (see `deviceSession.ts`). The legacy cookie/bootstrap/refresh-family lanes were
@@ -26,9 +26,10 @@ export const deviceSessionSyncSchema = z.object({
26
26
  /**
27
27
  * Request body for `POST /session/device/token` — the client presents the
28
28
  * `deviceId` it stored first-party plus the opaque `deviceSecret`. NO bearer:
29
- * possession of the secret IS the proof of device ownership. The server matches
30
- * `sha256(deviceSecret)` against the device's stored `secretHash` (constant-time)
31
- * and mints a short access token for the device's active account.
29
+ * possession of the secret IS the proof of device ownership. The server looks
30
+ * `sha256(deviceSecret)` up among the device's holder credentials (one per app
31
+ * or origin that joined the shared DeviceSession) and mints a short access
32
+ * token for the device's active account.
32
33
  *
33
34
  * `accountId` pins the mint to ONE account of that device instead of whichever
34
35
  * account is currently active. It exists for identity-bound clients (Commons),
@@ -48,8 +49,8 @@ export const deviceTokenMintRequestSchema = z.object({
48
49
  * short access token for the active account, its expiry, the device secret the
49
50
  * client must persist (`nextDeviceSecret` — on mint this echoes the presented
50
51
  * secret unchanged so concurrent refreshes from multiple origins do not race),
51
- * and the projected device-session state. Sign-in rotates the secret via
52
- * `issueDeviceSecret`; mint does not.
52
+ * and the projected device-session state. Nothing rotates: each sign-in issues
53
+ * a NEW holder credential and leaves the others valid.
53
54
  */
54
55
  export const deviceTokenMintResponseSchema = z.object({
55
56
  accessToken: z.string(),
@@ -107,12 +108,11 @@ export const sessionAccountsChangedEventSchema = z.object({
107
108
  * derived server-side from it) and consumed afterwards only by native
108
109
  * background code, which has no JS runtime to mint a token for itself.
109
110
  *
110
- * Deliberately a SEPARATE credential from the rotating `deviceSecret`: that one
111
- * rotates on every mint, so background code presenting it would become a second
112
- * writer of a value the JS runtime depends on, and background code killed
113
- * mid-rotation would silently sign the user out on the next cold start. Against
114
- * this credential background code is the sole writer, and it can never rotate
115
- * anything JS reads.
111
+ * Deliberately a SEPARATE credential from the holder `deviceSecret`: that one
112
+ * is device-wide and mints for whichever account is active, while this one is
113
+ * bound to ONE account and expires, so a widget worker never holds a
114
+ * credential that reaches every account on the device. Background code is its
115
+ * sole writer and never touches anything JS reads.
116
116
  *
117
117
  * The raw `secret` is returned exactly once, at provision time — never stored
118
118
  * retrievably, never logged, never re-read. A caller that loses it provisions
@@ -135,10 +135,10 @@ export const deviceBackgroundCredentialResponseSchema = z.object({
135
135
  * native background code with NO bearer and NO cookies: possession of the
136
136
  * background `secret` IS the proof, as it is for the device-secret mint.
137
137
  *
138
- * Unlike that mint this one NEVER rotates the presented secret (hence no
139
- * `next…` field to persist in the response), so background code interrupted
140
- * anywhere between request and response leaves the credential intact and
141
- * usable on its next run.
138
+ * Like that mint this one NEVER rotates the presented secret, and it carries
139
+ * no `next…` field at all, so background code interrupted anywhere between
140
+ * request and response leaves the credential intact and usable on its next
141
+ * run.
142
142
  */
143
143
  export const deviceBackgroundTokenRequestSchema = z.object({
144
144
  deviceId: z.string().min(1),
@@ -159,3 +159,60 @@ export const deviceBackgroundTokenResponseSchema = z.object({
159
159
  expiresAt: z.string(),
160
160
  accountId: z.string().min(1),
161
161
  });
162
+ /* -------------------------------------------------------------------------- */
163
+ /* The browser bridge — joining the browser's DeviceSession (ADR 0029 D2) */
164
+ /* -------------------------------------------------------------------------- */
165
+ /**
166
+ * Proof that the caller holds a device: the `deviceId` it stored first-party and
167
+ * one of that device's holder secrets. Sent with `POST /session/device/join-code`
168
+ * and, optionally, with a sign-in (`POST /auth/session/claim`, the email,
169
+ * password and second-factor sign-ins, `POST /auth/signup`), where
170
+ * a valid proof puts the new session on THAT device so every app holding it sees
171
+ * the account. An invalid proof on a sign-in is ignored, never an error.
172
+ */
173
+ export const deviceProofSchema = z.object({
174
+ deviceId: z.string().min(1).max(128),
175
+ deviceSecret: z.string().min(1).max(256),
176
+ });
177
+ /**
178
+ * `POST /session/device/register` — auth.oxy.so only. No body. A new, empty
179
+ * DeviceSession with a server-chosen `deviceId` and ONE holder credential for
180
+ * auth.oxy.so. The raw secret is returned exactly once.
181
+ */
182
+ export const deviceRegisterResponseSchema = z.object({
183
+ deviceId: z.string().min(1),
184
+ deviceSecret: z.string().min(1),
185
+ });
186
+ /** PKCE S256 challenge: base64url of a SHA-256 digest (43 characters). */
187
+ const pkceS256ChallengeSchema = z.string().regex(/^[A-Za-z0-9_-]{43}$/);
188
+ /**
189
+ * `POST /session/device/join-code` — auth.oxy.so only (the bridge window). Proves
190
+ * auth.oxy.so's device and asks for a one-use code an official app redeems to
191
+ * join it. The code is bound to the application (`clientId`), its exact
192
+ * registered `redirectUri` and the app's PKCE challenge, and lives about a
193
+ * minute.
194
+ */
195
+ export const deviceJoinCodeRequestSchema = deviceProofSchema.extend({
196
+ clientId: z.string().min(1).max(256),
197
+ redirectUri: z.string().url().max(2048),
198
+ codeChallenge: pkceS256ChallengeSchema,
199
+ codeChallengeMethod: z.literal('S256'),
200
+ });
201
+ export const deviceJoinCodeResponseSchema = z.object({
202
+ code: z.string().min(1),
203
+ /** Seconds until the code expires. */
204
+ expiresIn: z.number().int().positive(),
205
+ });
206
+ /**
207
+ * `POST /session/device/join` — called by the app's own origin with the code the
208
+ * bridge window posted to it and the PKCE verifier only the app holds. Returns a
209
+ * NEW holder credential for the browser's device; the app then mints through the
210
+ * ordinary `POST /session/device/token`.
211
+ */
212
+ export const deviceJoinRequestSchema = z.object({
213
+ code: z.string().min(1).max(256),
214
+ codeVerifier: z.string().min(43).max(128).regex(/^[A-Za-z0-9._~-]+$/),
215
+ clientId: z.string().min(1).max(256),
216
+ redirectUri: z.string().url().max(2048),
217
+ });
218
+ export const deviceJoinResponseSchema = deviceRegisterResponseSchema;
@@ -207,11 +207,9 @@ export const domainVerificationInstructionsSchema = z.object({
207
207
  }),
208
208
  });
209
209
  export const authMethodEntrySchema = z.object({
210
- type: z.enum(['identity', 'webauthn']),
210
+ type: z.literal('identity'),
211
211
  linkedAt: z.union([z.string(), z.date()]),
212
212
  verificationMethodId: z.string().optional(),
213
- credentialId: z.string().optional(),
214
- name: z.string().optional(),
215
213
  });
216
214
  export const authMethodsResponseSchema = z.object({
217
215
  did: z.string(),
@@ -1,28 +1,31 @@
1
1
  /**
2
- * Linking Commons to a passkey account (ADR 0029 D3) — the two-device relay.
2
+ * Linking Commons to an account without a key (ADR 0029 D3) — the two-device
3
+ * relay.
3
4
  *
4
- * A passkey account links a Commons root once, and becomes self-custodied: its
5
- * recovery email is deleted and its phrase in Commons is how it gets back in.
6
- * The authority is the same as `POST /auth/link` (ADR 0024 D8): a root proof
7
- * (`link_identity`) by the key Commons holds, and a fresh assertion by one of
8
- * the account's passkeys over the SAME one-use challenge. Only the transport is
9
- * new, because the two factors live on two devices:
5
+ * An account without a key links a Commons root once, and becomes
6
+ * self-custodied: its email is deleted and its phrase in Commons is how it gets
7
+ * back in. The authority is the same as `POST /auth/link` (ADR 0024 D8): a root
8
+ * proof (`link_identity`) by the key Commons holds over a one-use challenge,
9
+ * and the account's own confirmation — a code just sent to its email (plus its
10
+ * authenticator code when it has one). Only the transport is new, because the
11
+ * two factors live on two devices:
10
12
  *
11
- * 1. auth.oxy.so (signed in) opens a link request → `{ linkId, challenge }`,
12
- * shown as a QR (`oxycommons://link?id=…&c=…`).
13
+ * 1. The signed-in account (the "Link Commons" panel of `@oxy.so/services`)
14
+ * opens a link request → `{ linkId, challenge }`, shown as a QR
15
+ * (`oxycommons://link?id=…&c=…`).
13
16
  * 2. Commons scans it, reads the request (the account's id and username),
14
17
  * signs the root proof over the challenge and posts it with its key.
15
18
  * 3. Both screens show the same 6-digit code, derived from the link id and
16
19
  * that key (`deriveIdentityLinkCode` in `@oxy.so/core`); the person checks
17
20
  * they match, so a photographed QR cannot slip another key in.
18
- * 4. auth.oxy.so asserts the passkey over the challenge and completes: the
19
- * account gains the root, loses the email, and Commons signs in with it.
21
+ * 4. The panel completes with the email code: the account gains the root,
22
+ * loses the email, and Commons signs in with it.
20
23
  *
21
24
  * The server stores only the challenge's hash; the challenge travels in the QR.
22
25
  */
23
26
  import { z } from 'zod';
24
27
  import { identityProofSchema } from './identityProof.js';
25
- import { webauthnAssertionResponseSchema } from './webauthn.js';
28
+ import { emailReauthProofSchema } from './signIn.js';
26
29
  export const IDENTITY_LINK_STATUSES = ['pending', 'signed', 'completed', 'cancelled'];
27
30
  /** The scheme and host Commons routes a link QR to. */
28
31
  export const IDENTITY_LINK_QR_PREFIX = 'oxycommons://link';
@@ -76,9 +79,9 @@ export const identityLinkProofRequestSchema = z
76
79
  proof: identityProofSchema,
77
80
  })
78
81
  .strict();
79
- /** `POST /identity/link/:linkId/options` — WebAuthn request options over the challenge. */
80
- export const identityLinkOptionsRequestSchema = z
81
- .object({ challenge: z.string().trim().regex(CHALLENGE, 'challenge must be 64 lowercase hex characters') })
82
- .strict();
83
- /** `POST /identity/link/:linkId/complete` — the passkey assertion over the challenge. */
84
- export const identityLinkCompleteRequestSchema = z.object({ assertion: webauthnAssertionResponseSchema }).strict();
82
+ /**
83
+ * `POST /identity/link/:linkId/complete` — the account's own confirmation: a
84
+ * code just sent to its email for this link (`reauth`, plus its authenticator
85
+ * code when it has one).
86
+ */
87
+ export const identityLinkCompleteRequestSchema = z.object({ reauth: emailReauthProofSchema }).strict();
@@ -28,8 +28,8 @@ export const IDENTITY_PROOF_CHALLENGE_TTL_MS = 5 * 60 * 1000;
28
28
  */
29
29
  export const IDENTITY_PROOF_ACTIONS = {
30
30
  /**
31
- * Link Commons' root to a passkey account that has none (`POST /auth/link`).
32
- * The account becomes self-custodied and its recovery email is deleted
31
+ * Link Commons' root to an account that has none (`POST /auth/link`).
32
+ * The account becomes self-custodied and its email is deleted
33
33
  * (ADR 0029 D3).
34
34
  */
35
35
  link: 'link_identity',
package/dist/esm/index.js CHANGED
@@ -47,13 +47,13 @@ export {
47
47
  // Closed value sets — shared by the API's mongoose enums, the API's request
48
48
  // validation, and the SDK's unions, so a new category/tier/status cannot be
49
49
  // added on one side only.
50
- REPUTATION_CATEGORIES, REPUTATION_TRANSACTION_STATUSES, TRUST_TIERS, REPUTATION_TARGET_ENTITY_TYPES, REPUTATION_DISPUTE_STATUSES, REPUTATION_INFLUENCE_CONTEXTS,
50
+ REPUTATION_CATEGORIES, REPUTATION_TRANSACTION_STATUSES, TRUST_TIERS, REPUTATION_TARGET_ENTITY_TYPES, REPUTATION_INFLUENCE_CONTEXTS,
51
51
  // Schemas — closed value sets
52
- reputationCategorySchema, reputationTransactionStatusSchema, trustTierSchema, reputationTargetEntityTypeSchema, reputationDisputeStatusSchema, reputationInfluenceContextSchema,
52
+ reputationCategorySchema, reputationTransactionStatusSchema, trustTierSchema, reputationTargetEntityTypeSchema, reputationInfluenceContextSchema,
53
53
  // Schemas — responses
54
- reputationTransactionSchema, reputationBalanceBreakdownSchema, reputationInfluenceSchema, reputationReliabilitySchema, reputationBalanceSummarySchema, reputationBalanceSchema, reputationDisputeSchema, reputationRuleSchema, reputationLeaderboardUserSchema, reputationLeaderboardEntrySchema, reputationInfluenceResultSchema, reverseReputationTransactionResultSchema,
54
+ reputationTransactionSchema, reputationBalanceBreakdownSchema, reputationInfluenceSchema, reputationReliabilitySchema, reputationBalanceSummarySchema, reputationBalanceSchema, reputationRuleSchema, reputationRulesResponseSchema, reputationLeaderboardUserSchema, reputationLeaderboardEntrySchema, reputationInfluenceResultSchema,
55
55
  // Schemas — request bodies
56
- awardReputationSchema, createReputationDisputeSchema, resolveReputationDisputeSchema, upsertReputationRuleSchema, reverseReputationTransactionSchema,
56
+ awardReputationSchema,
57
57
  // Narrows the two balance views apart at runtime.
58
58
  isFullReputationBalance, } from './reputation.js';
59
59
  export {
@@ -67,7 +67,7 @@ moderationFindingSchema, moderationDecisionEventSubjectSchema, moderationPolicyV
67
67
  registerIdentityBindingSchema, identityBindingSchema,
68
68
  // Schemas — the derived V2 axes
69
69
  reputationPersonhoodSchema, reputationContributionSchema, reputationConductSchema, reputationReportingSchema, reputationReviewingSchema, reputationContextualInfluenceSchema, applicationModerationTrustSchema, } from './moderationReputation.js';
70
- export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, deviceTokenMintRequestSchema, deviceTokenMintResponseSchema, deviceBackgroundCredentialResponseSchema, deviceBackgroundTokenRequestSchema, deviceBackgroundTokenResponseSchema, SESSION_ACCOUNTS_CHANGED_EVENT, sessionAccountsChangedReasonSchema, sessionAccountsChangedEventSchema, } from './deviceSession.js';
70
+ export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, deviceTokenMintRequestSchema, deviceTokenMintResponseSchema, deviceBackgroundCredentialResponseSchema, deviceBackgroundTokenRequestSchema, deviceBackgroundTokenResponseSchema, deviceProofSchema, deviceRegisterResponseSchema, deviceJoinCodeRequestSchema, deviceJoinCodeResponseSchema, deviceJoinRequestSchema, deviceJoinResponseSchema, SESSION_ACCOUNTS_CHANGED_EVENT, sessionAccountsChangedReasonSchema, sessionAccountsChangedEventSchema, } from './deviceSession.js';
71
71
  export { deviceContextRelationshipSchema, deviceDirectoryProfileSchema, deviceAccountContextSchema, devicePrincipalSchema, deviceDirectorySchema, deviceActivateRequestSchema, deviceActivateResponseSchema, deviceDirectorySyncSchema, } from './deviceDirectory.js';
72
72
  export { oauthConsentDecisionSchema, oauthAuthorizeCodeResponseSchema, mcpOAuthClientApplicationSchema, mcpOAuthWriteActionSchema, mcpOAuthConsentContextSchema, mcpOAuthClientInfoResponseSchema, mcpOAuthConsentResponseSchema, } from './oauth.js';
73
73
  export {
@@ -94,14 +94,14 @@ updateSchema, createUpdateResponseSchema, rollbackToEmbeddedEntrySchema, channel
94
94
  // Rollback / promote / rollout
95
95
  rollbackRequestSchema, rollbackToEmbeddedRequestSchema, promoteRequestSchema, updateRolloutPatchSchema, } from './updates.js';
96
96
  export {
97
- // Schemas
98
- webauthnRegisterOptionsRequestSchema, webauthnLoginOptionsRequestSchema, webauthnRegisterVerifyRequestSchema, webauthnLoginVerifyRequestSchema, webauthnCredentialIdSchema, webauthnAssertionResponseSchema, } from './webauthn.js';
99
- export {
100
- // Recovery email of a passkey account (ADR 0029 D3)
97
+ // Email codes and tickets for sign-up and re-verification
101
98
  EMAIL_VERIFICATION_PURPOSES, EMAIL_CODE_LENGTH, EMAIL_CODE_TTL_MS, EMAIL_CODE_MAX_ATTEMPTS, EMAIL_TICKET_TTL_MS, EMAIL_VERIFICATION_ERROR_CODES, emailAddressSchema, emailTicketSchema, emailVerificationStartRequestSchema, emailVerificationStartResponseSchema, emailVerificationConfirmRequestSchema, emailVerificationConfirmResponseSchema, } from './accountEmail.js';
102
99
  export {
103
- // Linking Commons to a passkey account (ADR 0029 D3)
104
- IDENTITY_LINK_STATUSES, IDENTITY_LINK_QR_PREFIX, identityLinkIdSchema, buildIdentityLinkQrPayload, parseIdentityLinkQrPayload, identityLinkCreateResponseSchema, identityLinkStateSchema, identityLinkProofRequestSchema, identityLinkOptionsRequestSchema, identityLinkCompleteRequestSchema, } from './identityLink.js';
100
+ // Email code/link, password and authenticator sign-in
101
+ EMAIL_SIGNIN_LINK_TTL_MS, SIGNIN_SECOND_FACTOR_TTL_MS, SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS, PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH, TOTP_DIGITS, TOTP_PERIOD_SECONDS, TOTP_BACKUP_CODE_COUNT, SIGN_IN_ERROR_CODES, signInIdentifierSchema, passwordInputSchema, newPasswordSchema, secondFactorCodeSchema, emailSignInStartRequestSchema, emailSignInStartResponseSchema, emailSignInConfirmRequestSchema, emailSignInCodeSchema, normalizeEmailSignInCode, EMAIL_SIGNIN_LONG_CODE_ALPHABET, EMAIL_SIGNIN_LONG_CODE_LENGTH, emailSignInCollectRequestSchema, emailSignInLinkRequestSchema, emailSignInLinkResponseSchema, emailSignInPendingSchema, passwordSignInRequestSchema, secondFactorSignInRequestSchema, secondFactorRequiredSchema, signUpRequestSchema, isSecondFactorRequired, reauthProofSchema, emailReauthProofSchema, REAUTH_ACTIONS, reauthEmailStartRequestSchema, passwordSetRequestSchema, signInMethodsSchema, totpEnrollResponseSchema, totpConfirmRequestSchema, totpReauthRequestSchema, totpBackupCodesResponseSchema, } from './signIn.js';
102
+ export {
103
+ // Linking Commons to an account without a key (ADR 0029 D3)
104
+ IDENTITY_LINK_STATUSES, IDENTITY_LINK_QR_PREFIX, identityLinkIdSchema, buildIdentityLinkQrPayload, parseIdentityLinkQrPayload, identityLinkCreateResponseSchema, identityLinkStateSchema, identityLinkProofRequestSchema, identityLinkCompleteRequestSchema, } from './identityLink.js';
105
105
  export {
106
106
  // Schemas — transparency log (checkpoints + inclusion proofs)
107
107
  transparencyCheckpointSignatureSchema, transparencyAnchorSchema, transparencyCheckpointSchema, transparencyInclusionProofSchema, transparencyCheckpointListSchema, } from './transparency.js';
@@ -3,9 +3,8 @@
3
3
  *
4
4
  * SINGLE SOURCE OF TRUTH for the reputation ledger's wire shapes: the closed
5
5
  * value sets (`REPUTATION_CATEGORIES`, `TRUST_TIERS`, …), the response entities
6
- * (`ReputationTransaction`, the two balance views, `ReputationDispute`,
7
- * `ReputationRule`, the leaderboard entry) and the request bodies the write
8
- * endpoints accept. The API validates its OUTPUT against these schemas and its
6
+ * (`ReputationTransaction`, the two balance views, `ReputationRule`, the
7
+ * leaderboard entry) and the award body. The API validates its OUTPUT against these schemas and its
9
8
  * INPUT with the same request schemas the SDK's input types are derived from;
10
9
  * `@oxy.so/core`'s reputation mixin imports every type from here rather than
11
10
  * declaring its own.
@@ -71,15 +70,13 @@ export const reputationCategorySchema = z.enum(REPUTATION_CATEGORIES);
71
70
  * Transaction lifecycle status.
72
71
  *
73
72
  * - `active` — counts toward the balance.
74
- * - `disputed` — under dispute; still counts until the dispute resolves.
75
- * - `reversed` — superseded by a compensating reversal transaction; excluded.
76
- * - `voided` — administratively excluded with no compensating entry.
73
+ * - `reversed` — superseded by a compensating reversal transaction (written
74
+ * only by policy-driven code, never by a person); the pair nets
75
+ * to zero.
77
76
  */
78
77
  export const REPUTATION_TRANSACTION_STATUSES = [
79
78
  'active',
80
- 'disputed',
81
79
  'reversed',
82
- 'voided',
83
80
  ];
84
81
  export const reputationTransactionStatusSchema = z.enum(REPUTATION_TRANSACTION_STATUSES);
85
82
  /**
@@ -104,15 +101,6 @@ export const REPUTATION_TARGET_ENTITY_TYPES = [
104
101
  'other',
105
102
  ];
106
103
  export const reputationTargetEntityTypeSchema = z.enum(REPUTATION_TARGET_ENTITY_TYPES);
107
- /** Dispute lifecycle status. */
108
- export const REPUTATION_DISPUTE_STATUSES = [
109
- 'open',
110
- 'accepted',
111
- 'rejected',
112
- 'needs_review',
113
- ];
114
- export const reputationDisputeStatusSchema = z.enum(REPUTATION_DISPUTE_STATUSES);
115
- /** Influence context selecting which capped weight axis to read. */
116
104
  export const REPUTATION_INFLUENCE_CONTEXTS = [
117
105
  'default',
118
106
  'report',
@@ -217,26 +205,16 @@ const FULL_BALANCE_FIELDS = [
217
205
  export function isFullReputationBalance(balance) {
218
206
  return FULL_BALANCE_FIELDS.every((field) => field in balance);
219
207
  }
220
- export const reputationDisputeSchema = z.object({
221
- id: z.string(),
222
- transactionId: z.string(),
223
- userId: z.string(),
224
- reason: z.string(),
225
- status: reputationDisputeStatusSchema,
226
- evidence: z.array(z.string()).optional(),
227
- resolvedAt: z.string().optional(),
228
- resolvedByUserId: z.string().optional(),
229
- createdAt: z.string(),
230
- updatedAt: z.string(),
231
- });
232
208
  export const reputationRuleSchema = z.object({
233
- id: z.string(),
234
209
  actionType: z.string(),
235
210
  points: z.number(),
236
211
  category: reputationCategorySchema,
237
212
  description: z.string(),
238
213
  cooldownInMinutes: z.number(),
239
- isEnabled: z.boolean(),
214
+ });
215
+ export const reputationRulesResponseSchema = z.object({
216
+ version: z.number().int(),
217
+ rules: z.array(reputationRuleSchema),
240
218
  });
241
219
  export const reputationLeaderboardUserSchema = z.object({
242
220
  id: z.string(),
@@ -256,10 +234,6 @@ export const reputationInfluenceResultSchema = z.object({
256
234
  weight: z.number(),
257
235
  influence: reputationInfluenceSchema,
258
236
  });
259
- export const reverseReputationTransactionResultSchema = z.object({
260
- original: reputationTransactionSchema,
261
- reversal: reputationTransactionSchema,
262
- });
263
237
  export const awardReputationSchema = z.object({
264
238
  userId: z.string().trim().min(1),
265
239
  actionType: z.string().trim().min(1),
@@ -272,22 +246,3 @@ export const awardReputationSchema = z.object({
272
246
  reason: z.string().trim().max(500).optional(),
273
247
  metadata: z.record(z.unknown()).optional(),
274
248
  });
275
- export const createReputationDisputeSchema = z.object({
276
- transactionId: z.string().trim().min(1),
277
- reason: z.string().trim().min(1).max(1000),
278
- evidence: z.array(z.string().trim().min(1)).max(20).optional(),
279
- });
280
- export const resolveReputationDisputeSchema = z.object({
281
- status: z.enum(['accepted', 'rejected']),
282
- });
283
- export const upsertReputationRuleSchema = z.object({
284
- actionType: z.string().trim().min(1),
285
- points: z.number(),
286
- category: reputationCategorySchema,
287
- description: z.string().trim().min(1).max(500),
288
- cooldownInMinutes: z.number().int().min(0).default(0),
289
- isEnabled: z.boolean().default(true),
290
- });
291
- export const reverseReputationTransactionSchema = z.object({
292
- reason: z.string().trim().max(500).optional(),
293
- });