@oxyhq/contracts 0.24.0 → 0.25.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.
@@ -2,26 +2,54 @@
2
2
  /**
3
3
  * Oxy-scoped signed-record types.
4
4
  *
5
- * The base `signedRecordEnvelopeSchema` (`./identity`) now treats `type` as an
6
- * OPEN, non-empty string so ANY Oxy app may define its own record categories
7
- * (e.g. `app.mention.*`'s `app_record`) on the shared envelope grammar. The Oxy
8
- * identity/civic/node STORE, however, accepts ONLY the closed set of categories
9
- * it knows how to verify and materialize — this module is that closed set.
5
+ * The base `signedRecordEnvelopeSchema` (`./identity`) treats `type` as an OPEN,
6
+ * non-empty string so ANY Oxy app may sign on the shared envelope grammar. The
7
+ * Oxy STORE re-narrows it to the closed set in this module — a `type` outside it
8
+ * is rejected as `invalid_envelope`.
10
9
  *
11
- * `oxySignedRecordTypeSchema` is the runtime gate the Oxy store re-narrows with
12
- * (the API's `verifyEnvelope` rejects any `type` outside it; the Mongoose
13
- * `SignedRecord.type` enum is derived from `.options`); `OxySignedRecordType` is
14
- * the matching compile-time union the SDK identity/civic mixins type against.
10
+ * `oxySignedRecordTypeSchema` is that runtime gate (the API's `verifyEnvelope`
11
+ * re-narrows with it; the Mongoose `SignedRecord.type` enum and the Postgres
12
+ * CHECK on `signed_records.type` are both derived from `.options`);
13
+ * `OxySignedRecordType` is the matching compile-time union the SDK
14
+ * identity/civic mixins type against.
15
15
  *
16
16
  * The signing input INCLUDES `type`, so this set is part of the signed bytes —
17
- * a record cannot have its category swapped after signing.
17
+ * a record cannot have its category swapped after signing, and a value once
18
+ * signed can never be renamed.
18
19
  *
19
20
  * v1 only ever carried `identity` / `profile` (already in production); v2 added
20
21
  * the civic record types (reputation attestations, real-life / peer validations,
21
22
  * personhood vouches, verifiable credentials) and the user-node registration
22
- * record. Every value here is an Oxy `app.oxy.*` (or legacy v1) category — an
23
- * app's own `type` (e.g. `app_record`) is intentionally NOT in this set and is
24
- * rejected by the Oxy store.
23
+ * record.
24
+ *
25
+ * ## Why `app_record` is here, when it deliberately was not
26
+ *
27
+ * This set used to hold Oxy's own categories only, and said so: an app's `type`
28
+ * was "intentionally NOT in this set". The reason given was that the store
29
+ * accepts only what it knows how to **verify and materialize**. Verification
30
+ * turned out not to argue for the exclusion — the engine verifies a signature
31
+ * against the subject's keys whatever the category says — and materialization
32
+ * is the app's job, not the store's: an app projects its own feed tables from
33
+ * records it reads back.
34
+ *
35
+ * What changed is the decision the exclusion blocked. One chain per PERSON, held
36
+ * by Oxy, is the ecosystem substrate: apps append their records to the subject's
37
+ * one chain instead of each keeping a private chain for the same person. A
38
+ * closed set that admits no app category makes that unrepresentable.
39
+ *
40
+ * `app_record` is ONE value rather than an open lane, and the lexicon lives in
41
+ * the envelope's `collection` (`app.mention.feed.post`, `app.syra.*`), which the
42
+ * store denormalizes to `signed_records.nsid` and indexes. So a new app needs no
43
+ * change here — it picks its own collection namespace and signs `app_record`,
44
+ * exactly as Mention already does in production. Keeping the set closed is what
45
+ * keeps the CHECK a real constraint.
46
+ *
47
+ * **Admitting the category is not the whole of that decision.** Two gates sit
48
+ * beside it and are unchanged: an app record must arrive as a v2 (chained)
49
+ * envelope, and `oxyVerificationResolver` accepts exactly one custodial issuer
50
+ * (`OXY_DID`). So a record a user signs themselves verifies here today, while
51
+ * one an app signs custodially under its OWN issuer DID does not — that needs a
52
+ * separate, deliberate answer about which issuers may write to a person's chain.
25
53
  *
26
54
  * Platform-agnostic — zod only, no react/react-native/expo, ESM-safe.
27
55
  */
@@ -37,4 +65,7 @@ exports.oxySignedRecordTypeSchema = zod_1.z.enum([
37
65
  'personhood_vouch',
38
66
  'credential',
39
67
  'node',
68
+ // Any Oxy app's own record. The LEXICON is the envelope's `collection`, not
69
+ // this value — see the header.
70
+ 'app_record',
40
71
  ]);