@oxyhq/contracts 0.6.0 → 0.8.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,22 @@
1
+ import { z } from 'zod';
2
+ export const sessionAccountSchema = z.object({
3
+ accountId: z.string(),
4
+ sessionId: z.string(),
5
+ authuser: z.number().int().nonnegative(),
6
+ operatedByUserId: z.string().optional(),
7
+ });
8
+ export const deviceSessionStateSchema = z.object({
9
+ deviceId: z.string(),
10
+ accounts: z.array(sessionAccountSchema),
11
+ activeAccountId: z.string().nullable(),
12
+ revision: z.number().int().nonnegative(),
13
+ updatedAt: z.number(),
14
+ });
15
+ export const activeTokenSchema = z.object({
16
+ accessToken: z.string(),
17
+ expiresAt: z.string(),
18
+ });
19
+ export const deviceSessionSyncSchema = z.object({
20
+ state: deviceSessionStateSchema,
21
+ activeToken: activeTokenSchema.nullable(),
22
+ });
@@ -42,5 +42,12 @@ export const fedcmTokenPayloadSchema = z
42
42
  exp: z.number().optional(),
43
43
  iat: z.number().optional(),
44
44
  nonce: z.string().optional(),
45
+ /**
46
+ * An explicit central deviceId minted by the IdP, threaded through so the
47
+ * RP session can inherit a unified device id instead of deriving one from
48
+ * the (userId, RP origin) stableDeviceKey. Optional and additive — omitted
49
+ * tokens fall back to the existing stableDeviceKey/UA-IP derivation.
50
+ */
51
+ deviceId: z.string().optional(),
45
52
  })
46
53
  .passthrough();
@@ -38,12 +38,27 @@
38
38
  * `require()`).
39
39
  */
40
40
  import { z } from 'zod';
41
- export const verificationMethodSchema = z.object({
41
+ // The option schemas are left UN-annotated so they keep their concrete
42
+ // `ZodObject` type — `z.discriminatedUnion` requires object options and an
43
+ // explicit `z.ZodType<>` annotation would erase the shape it discriminates on.
44
+ // `z.object` already infers each option's type exactly (id/type/controller +
45
+ // the key field), so the union is structurally `VerificationMethod`.
46
+ const secp256k1VerificationMethodSchema = z.object({
42
47
  id: z.string(),
43
48
  type: z.literal('EcdsaSecp256k1VerificationKey2019'),
44
49
  controller: z.string(),
45
50
  publicKeyHex: z.string(),
46
51
  });
52
+ const multikeyVerificationMethodSchema = z.object({
53
+ id: z.string(),
54
+ type: z.literal('Multikey'),
55
+ controller: z.string(),
56
+ publicKeyMultibase: z.string(),
57
+ });
58
+ export const verificationMethodSchema = z.discriminatedUnion('type', [
59
+ secp256k1VerificationMethodSchema,
60
+ multikeyVerificationMethodSchema,
61
+ ]);
47
62
  export const didServiceSchema = z.object({
48
63
  id: z.string(),
49
64
  type: z.string(),
@@ -62,16 +77,9 @@ export const didDocumentSchema = z.object({
62
77
  export const signedRecordEnvelopeSchema = z
63
78
  .object({
64
79
  version: z.union([z.literal(1), z.literal(2)]),
65
- type: z.enum([
66
- 'identity',
67
- 'profile',
68
- 'reputation_attestation',
69
- 'real_life_attestation',
70
- 'validation_verdict',
71
- 'personhood_vouch',
72
- 'credential',
73
- 'node',
74
- ]),
80
+ // Open, app-defined category (see the `type` doc above). The Oxy STORE
81
+ // re-narrows to `oxySignedRecordTypeSchema`; an app to its own constant.
82
+ type: z.string().min(1),
75
83
  subject: z.string(),
76
84
  issuer: z.string(),
77
85
  record: z.record(z.unknown()),
package/dist/esm/index.js CHANGED
@@ -28,9 +28,16 @@ export {
28
28
  verificationMethodSchema, didServiceSchema, didDocumentSchema, signedRecordEnvelopeSchema, verifiedDomainSchema, domainVerificationRequestSchema, domainVerificationInstructionsSchema, authMethodEntrySchema, authMethodsResponseSchema, exportAttestationSchema, exportBundleSchema, } from './identity.js';
29
29
  export {
30
30
  // Schemas
31
+ oxySignedRecordTypeSchema, } from './oxyRecordTypes.js';
32
+ export {
33
+ // Schemas
34
+ chainHeadResponseSchema, logPageResponseSchema, } from './protocol.js';
35
+ export {
36
+ // Schemas
31
37
  publicCardSchema, signedPublicCardSchema, realLifeAttestationRecordSchema, realLifeAttestationResultSchema, validationVerdictRecordSchema, validationOpenRequestSchema, validationOpenResultSchema, validationRequestSummarySchema, validationVoteResultSchema, personhoodVouchRecordSchema, personhoodBreakdownSchema, personhoodStatusResultSchema, vouchResultSchema,
32
38
  // Verifiable Credentials (Fase 4 — NEW)
33
39
  credentialRecordSchema, verifiableCredentialResponseSchema, credentialIssueResultSchema, credentialListResultSchema, credentialVerifyResultSchema, } from './civic.js';
34
40
  export {
35
41
  // Schemas
36
42
  linkPreviewSchema, linkPreviewBatchRequestSchema, linkPreviewBatchResponseSchema, linkPreviewResponseSchema, } from './links.js';
43
+ export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, } from './deviceSession.js';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Oxy-scoped signed-record types.
3
+ *
4
+ * The base `signedRecordEnvelopeSchema` (`./identity`) now treats `type` as an
5
+ * OPEN, non-empty string so ANY Oxy app may define its own record categories
6
+ * (e.g. `app.mention.*`'s `app_record`) on the shared envelope grammar. The Oxy
7
+ * identity/civic/node STORE, however, accepts ONLY the closed set of categories
8
+ * it knows how to verify and materialize — this module is that closed set.
9
+ *
10
+ * `oxySignedRecordTypeSchema` is the runtime gate the Oxy store re-narrows with
11
+ * (the API's `verifyEnvelope` rejects any `type` outside it; the Mongoose
12
+ * `SignedRecord.type` enum is derived from `.options`); `OxySignedRecordType` is
13
+ * the matching compile-time union the SDK identity/civic mixins type against.
14
+ *
15
+ * The signing input INCLUDES `type`, so this set is part of the signed bytes —
16
+ * a record cannot have its category swapped after signing.
17
+ *
18
+ * v1 only ever carried `identity` / `profile` (already in production); v2 added
19
+ * the civic record types (reputation attestations, real-life / peer validations,
20
+ * personhood vouches, verifiable credentials) and the user-node registration
21
+ * record. Every value here is an Oxy `app.oxy.*` (or legacy v1) category — an
22
+ * app's own `type` (e.g. `app_record`) is intentionally NOT in this set and is
23
+ * rejected by the Oxy store.
24
+ *
25
+ * Platform-agnostic — zod only, no react/react-native/expo, ESM-safe.
26
+ */
27
+ import { z } from 'zod';
28
+ export const oxySignedRecordTypeSchema = z.enum([
29
+ 'identity',
30
+ 'profile',
31
+ 'reputation_attestation',
32
+ 'real_life_attestation',
33
+ 'validation_verdict',
34
+ 'personhood_vouch',
35
+ 'credential',
36
+ 'node',
37
+ ]);
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Generic "Oxy Protocol" record surface — the app-agnostic conventions every app
3
+ * follows to decentralize its own content on the shared signed-record substrate.
4
+ *
5
+ * The base `signedRecordEnvelopeSchema` (`./identity`) is the WIRE grammar: a
6
+ * signed envelope whose `type` is an open string and whose `record` is an opaque
7
+ * `Record<string, unknown>`. An app layers its own LEXICON on top of that
8
+ * grammar — a typed projection of the `record` payload, addressed by an
9
+ * AtProto-style `(collection, rkey)` key — WITHOUT forking the envelope schema.
10
+ *
11
+ * ## Recipe — defining an app lexicon record
12
+ *
13
+ * For each record kind an app wants to publish:
14
+ *
15
+ * 1. Define the `record` PAYLOAD schema as a `z.ZodType<XPayload>` (e.g.
16
+ * `app.mention.feed.post` → `mentionPostRecordSchema: z.ZodType<MentionPost>`).
17
+ * This validates ONLY the inner `record`, not the envelope.
18
+ * 2. Declare the `collection` NSID as a constant (e.g.
19
+ * `export const MENTION_POST_COLLECTION = 'app.mention.feed.post'`).
20
+ * 3. Reuse the UNCHANGED {@link signedRecordEnvelopeSchema} for the envelope. The
21
+ * base treats `record` as `z.record(z.unknown())`, so the app validates the
22
+ * envelope with the base schema first, then parses `envelope.record` with its
23
+ * own payload schema. {@link LexiconRecord} is the typed projection that pairs
24
+ * the `(collection, rkey)` key with the parsed payload.
25
+ *
26
+ * The Oxy civic contracts (`./civic`) already follow this convention implicitly:
27
+ * each civic record (`real_life_attestation`, `personhood_vouch`, `credential`,
28
+ * …) ships a `record`-payload schema and is carried by the base envelope.
29
+ *
30
+ * ## Chain-wire shapes
31
+ *
32
+ * {@link ChainHeadResponse} and {@link LogPageResponse} are the shared response
33
+ * shapes every chain store exposes (Oxy's `GET /identity/records/:userId/chain/head`,
34
+ * `GET /identity/head/:userId`, `GET /identity/log/:userId`, and any app node's
35
+ * equivalents). They are defined ONCE here so producers and consumers (the API
36
+ * handlers, the SDK identity/nodes mixins, app nodes) cannot drift.
37
+ *
38
+ * Platform-agnostic — zod only, no react/react-native/expo, ESM-safe.
39
+ */
40
+ import { z } from 'zod';
41
+ import { signedRecordEnvelopeSchema } from './identity.js';
42
+ export const chainHeadResponseSchema = z.object({
43
+ headRecordId: z.string().nullable(),
44
+ seq: z.number().int(),
45
+ recordCount: z.number().int().nonnegative(),
46
+ });
47
+ export const logPageResponseSchema = z.object({
48
+ records: z.array(signedRecordEnvelopeSchema),
49
+ count: z.number().int().nonnegative(),
50
+ });