@percayso-community/cayso-contracts 0.23.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 (41) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +114 -0
  3. package/dist/assertion.d.ts +867 -0
  4. package/dist/assertion.js +63 -0
  5. package/dist/assurance.d.ts +353 -0
  6. package/dist/assurance.js +551 -0
  7. package/dist/citizen-market.d.ts +468 -0
  8. package/dist/citizen-market.js +16 -0
  9. package/dist/citizenship.d.ts +215 -0
  10. package/dist/citizenship.js +26 -0
  11. package/dist/client.d.ts +288 -0
  12. package/dist/client.js +370 -0
  13. package/dist/commercial-payment.d.ts +243 -0
  14. package/dist/commercial-payment.js +16 -0
  15. package/dist/contract-schema.generated.d.ts +14 -0
  16. package/dist/contract-schema.generated.js +3107 -0
  17. package/dist/errors.d.ts +171 -0
  18. package/dist/errors.js +28 -0
  19. package/dist/index.d.ts +621 -0
  20. package/dist/index.js +624 -0
  21. package/dist/marketplace-receipt.d.ts +306 -0
  22. package/dist/marketplace-receipt.js +116 -0
  23. package/dist/marketplace-settlement.d.ts +298 -0
  24. package/dist/marketplace-settlement.js +16 -0
  25. package/dist/money.d.ts +84 -0
  26. package/dist/money.js +34 -0
  27. package/dist/organisation.d.ts +60 -0
  28. package/dist/organisation.js +16 -0
  29. package/dist/purchase.d.ts +531 -0
  30. package/dist/purchase.js +107 -0
  31. package/dist/restriction.d.ts +262 -0
  32. package/dist/restriction.js +316 -0
  33. package/dist/routes.d.ts +155 -0
  34. package/dist/routes.js +128 -0
  35. package/dist/schema-types.d.ts +101 -0
  36. package/dist/schema-types.js +16 -0
  37. package/dist/schema.d.ts +24 -0
  38. package/dist/schema.js +16 -0
  39. package/dist/wallet.d.ts +92 -0
  40. package/dist/wallet.js +59 -0
  41. package/package.json +41 -0
@@ -0,0 +1,867 @@
1
+ /**
2
+ * `assertion` — the shape of the credential Piper mints for a signed-in holder
3
+ * and Cayso verifies before it answers (CAY-0021), and the trust envelope that
4
+ * says who minted it and under which key (CAY-0030).
5
+ *
6
+ * This file is the *shape alone*. Nothing here reads, parses, verifies or
7
+ * expires a credential: verification is the `PercaysoIdVerifier` seam in slice
8
+ * 18 and the middleware is slice 19. This is the contract between two
9
+ * repositories and two people, written down before either side builds against
10
+ * it. An `AssertionClaims` value is a claim to be *checked*, never a claim to be
11
+ * trusted: Cayso verifies a minted assertion and never takes a caller's word for
12
+ * who they are — not Piper's, not the admin app's, not a peer's (CLAUDE.md,
13
+ * Trust and authentication).
14
+ *
15
+ * ## An envelope is a HEADER and CLAIMS, and they are not the same thing
16
+ *
17
+ * {@link AssertionEnvelope} is a header ({@link AssertionHeader}) beside the
18
+ * claims ({@link AssertionClaims}), and the split is load-bearing rather than
19
+ * tidy.
20
+ *
21
+ * The header says *how to check this credential*: which algorithm it was signed
22
+ * with (`alg`) and which of the issuer's keys signed it (`kid`). The claims say
23
+ * *what the issuer asserts*: who the holder is, what was verified about them,
24
+ * what this credential may be used for. A verifier must read the header **before
25
+ * it can believe a single claim**, and it must read the header **without
26
+ * believing it** — the header is unauthenticated input that selects a check, not
27
+ * a statement to act on.
28
+ *
29
+ * Flattening `alg` and `kid` into the claims hides that ordering, and it invites
30
+ * the two failures CAY-0030 exists to close. If `alg` reads as a claim, somebody
31
+ * eventually *selects on it* — dispatching to the algorithm the credential names,
32
+ * which lets a caller name the algorithm their forgery is easiest under. If `kid`
33
+ * reads as a claim, somebody eventually treats it as a hint rather than a
34
+ * selector — trying every key until one verifies, which makes a revoked key as
35
+ * good as a live one. So the contract states the division here, in the type: the
36
+ * header is **read to choose a check**, the claims are **what the check
37
+ * establishes**.
38
+ *
39
+ * The obligations that follow are the verifier's, and they are stated so a
40
+ * consumer knows what it is being held to. `alg` is taken from the ISSUER'S
41
+ * REGISTRY ENTRY and the header's value is *checked against it*, never used to
42
+ * select a check. `kid` selects exactly ONE key, from that issuer's keys alone;
43
+ * an unknown `kid`, a `kid` belonging to a different issuer, and a revoked `kid`
44
+ * are each a refusal, and there is no fallback to a second key. Where the
45
+ * registry lives and what it contains is Cayso's own and is deliberately not on
46
+ * this surface.
47
+ *
48
+ * ## Lifetime is 60–120 seconds, single-use where the issuer can manage it
49
+ *
50
+ * These claims describe a *short-lived* credential. The band is **60 to 120
51
+ * seconds**, and it is single-use wherever the issuer can track that. This is
52
+ * the contract, stated here because it is the reason this slice exists as its
53
+ * own slice — not advice a consumer may weigh.
54
+ *
55
+ * Why so short: a long-lived credential that anybody holding it may present, and
56
+ * that moves money, is the admin-password objection wearing a different hat —
57
+ * "who has the admin password, select all records where this, into my wallet"
58
+ * (CAY-0008). Something you can copy once and replay for a month is that password
59
+ * again, however it was minted. The issuer's *own* sign-in sessions last 30 days
60
+ * (CAY-0008, and Piper's `identity` cookie), and this must emphatically **not** be
61
+ * one of those: an assertion is not a sign-in session handed across the boundary,
62
+ * it is a fresh, narrowly-scoped statement minted per request. The glossary says
63
+ * the same in one line — an assertion "never [carries] a password and never a
64
+ * Piper session" (CONTEXT.md).
65
+ *
66
+ * A note on where numbers live. CLAUDE.md keeps every *economic* figure —
67
+ * amounts, rates, tiers, schedules, thresholds — in the canon PRD and nowhere
68
+ * else, so that a number in two places cannot go stale. That rule governs the
69
+ * economy; the 60–120 second band, the clock constants below and the ceiling on
70
+ * `exp` are *security parameters of this credential*, and the briefs for these
71
+ * slices instruct they be recorded here **as the contract**. They are recorded
72
+ * here for that reason and cited to those briefs. No economic number appears
73
+ * anywhere in this file.
74
+ *
75
+ * ## The clock rules, and the direction skew is allowed to move in
76
+ *
77
+ * Three constants below state the whole of the timing contract:
78
+ * {@link MAX_ASSERTION_LIFETIME_SECONDS}, the furthest ahead of now an `exp` may
79
+ * sit before the credential is refused as over-long; {@link
80
+ * CLOCK_SKEW_TOLERANCE_SECONDS}, how far two honest clocks are assumed to
81
+ * disagree; and {@link MINIMUM_ASSERTION_USE_RETENTION_SECONDS}, derived from
82
+ * the two, which is the floor on how long a recorded use must be kept.
83
+ *
84
+ * **Skew tolerance may only ever refuse something, never admit something.** It
85
+ * is applied to `iat` — a credential issued further into the future than two
86
+ * honest clocks could disagree by is refused — and it is emphatically NOT applied
87
+ * to `exp`. Adding tolerance to `exp` would keep a credential alive past the
88
+ * moment its issuer said it died, which is to say it would lengthen a lifetime
89
+ * this contract exists to keep short. `exp` at or before now is expired, exactly,
90
+ * with no grace.
91
+ *
92
+ * ## `exp` and `iat` are SECONDS; every `Millis` on this surface is not
93
+ *
94
+ * The unit asymmetry is deliberate and it is the sharpest edge on this contract,
95
+ * so it is stated rather than left to be discovered. `exp`, `iat`,
96
+ * `recoveredAt` and `sessionAgeSeconds` are the **issuer's** fields and follow
97
+ * the issuer's convention: **seconds** since the Unix epoch (or, for
98
+ * `sessionAgeSeconds`, a duration in seconds). Every timestamp on the wallet and
99
+ * purchase contracts beside this one is `Millis` — milliseconds — and so is every
100
+ * clock in an implementation of this contract.
101
+ *
102
+ * The two do not meet implicitly anywhere. Reading an `exp` in seconds as if it
103
+ * were milliseconds is a credential that expires in the year 1970 — harmless; the
104
+ * reverse, reading a `Millis` as seconds, is a credential that expires fifty
105
+ * thousand years from now, which is the failure worth naming. A consumer crossing
106
+ * between them must do it through an explicit, named conversion, so that the
107
+ * crossing is visible at the point it happens.
108
+ *
109
+ * ## Rotation and revocation — the consumer's obligation
110
+ *
111
+ * The issuer's keys rotate, and this contract states what an issuer owes a
112
+ * verifier when they do. These are obligations on the party that mints, written
113
+ * here because they are the only half of CAY-0030 that is a consumer's business:
114
+ *
115
+ * 1. **A new key must be registered before it signs anything.** Cayso selects a
116
+ * key by `kid` from a configured registry; a `kid` that arrives before the
117
+ * registry knows it is an unknown key and is refused. There is no discovery,
118
+ * no fetch-on-miss and no "try the others" — an unregistered key does not
119
+ * become usable by being used.
120
+ * 2. **More than one key may be live at once, and that is how an overlap
121
+ * works.** An issuer registers the new key, waits for the registration to
122
+ * take effect, then starts signing with it, and only afterwards retires the
123
+ * old one. Both are live in between; both verify; nothing is refused mid-way.
124
+ * A rotation that skips the overlap refuses every credential minted between
125
+ * the last old-key mint and the new key's registration.
126
+ * 3. **Revocation is immediate and independent of `exp`.** A revoked key stops
127
+ * verifying the moment it is revoked, including for credentials minted
128
+ * before the revocation whose `exp` has not yet passed. A short lifetime is
129
+ * not a substitute for revocation: "it will expire in ninety seconds" is
130
+ * ninety seconds of a key somebody else holds. The same is true one level up
131
+ * — a revoked ISSUER's credentials stop verifying, whatever their `kid`.
132
+ *
133
+ * An issuer that cannot meet (1) cannot rotate without an outage, and that is the
134
+ * intended pressure: registration first is the only ordering under which a
135
+ * verifier never has to guess.
136
+ *
137
+ * ## `aud` must be checked, not merely present
138
+ *
139
+ * `aud` is the literal `'cayso'`, and a verifier must **check** it rather than
140
+ * observe that it is there. A credential minted for somebody else that merely
141
+ * *carries* an audience is a credential you accepted from the wrong recipient:
142
+ * audience binding only binds if the receiver refuses everything not addressed to
143
+ * it. The type pins the value; the check is slice 18's, and it is not optional.
144
+ *
145
+ * The same is true of `iss`, one level out. `aud` says who a credential was
146
+ * *for*; `iss` says who *made* it, and it is the field that decides which key may
147
+ * have signed it and which holders it may speak about. An `iss` a verifier merely
148
+ * records is an `iss` that binds nothing.
149
+ *
150
+ * ## `sub` is an issuer identity and must never be written to chain state
151
+ *
152
+ * `sub` is the holder's identity at the issuer (Percayso ID). It **must never
153
+ * reach chain state** (CAY-0006). On-chain accounts are pseudonymous, and the
154
+ * mapping from an issuer identity to a wallet lives in this repository's
155
+ * `holders` table — the Trust Network — and nowhere else. That mapping is the one
156
+ * thing in the system kept correctable, revocable and erasable, which is exactly
157
+ * what the ledger beside it is not (CONTEXT.md, Trust Network).
158
+ *
159
+ * The direction is one-way and that is the whole reason for the rule: you can
160
+ * always reveal a pseudonym later — publish the mapping, add a lookup — and you
161
+ * can never un-publish an identity once it has been committed to an append-only
162
+ * chain. Pseudonymity is the only choice that keeps the other one available
163
+ * (CAY-0006). So `sub` stops at the Trust Network; the `walletId` is the handle
164
+ * that travels onward.
165
+ *
166
+ * `sub` is also bound to `iss`, and the binding runs the other way from the one a
167
+ * reader expects: an issuer may only speak about the holders that are its own. A
168
+ * verifier holds each issuer's subject space and refuses a credential whose `sub`
169
+ * falls outside it, so an issuer cannot mint for another issuer's holder. Which
170
+ * space belongs to whom is configuration, and is not on this surface.
171
+ *
172
+ * ## Why `verificationMethod` is here, and why it is not noise
173
+ *
174
+ * `verificationMethod` is the field most easily mistaken for metadata, and it is
175
+ * the one that stops an unverified holder ending up with value. Piper's
176
+ * `PIPER_DEV_BYPASS` is on **unless** the environment holds the literal string
177
+ * `'false'`, and an identity marked verified that way keeps the method
178
+ * `'dev-bypass'` **permanently** (CAY-0021). So `verificationStatus === 'verified'`
179
+ * can be true of an identity that was checked by nothing at all, and a gate that
180
+ * asks only for "verified" inherits a check that checked nothing.
181
+ *
182
+ * That is why the method rides alongside the status. At any gate meaning
183
+ * "identity-verified", the code must require a method that is **explicitly not**
184
+ * the development bypass, rather than requiring "verified" and inheriting whatever
185
+ * that includes (CLAUDE.md, Trust and authentication). Where that gate is written
186
+ * is slices 18 and 24; *that it has to exist* is recorded here, on the field that
187
+ * makes it possible.
188
+ *
189
+ * ## The assurance profile version, and why it is required
190
+ *
191
+ * `verificationMethod` names HOW an identity was verified; it does not say what
192
+ * that method proves. What each method asserts, and which operations accept it,
193
+ * is the assurance profile in `assurance.ts` (CAY-0029) — and a profile that can
194
+ * change has to be versioned, or a claim minted last month silently comes to mean
195
+ * whatever the profile means today.
196
+ *
197
+ * So `assuranceProfileVersion` rides on every claim: the issuer stamps the profile
198
+ * it minted under, and a verifier refuses a version it does not recognise rather
199
+ * than reading the method as if it meant what this version means. It is REQUIRED,
200
+ * not optional — an unstamped claim is one whose method cannot be interpreted at
201
+ * all, and defaulting it to "presumably ours" is the assumption the field exists
202
+ * to remove.
203
+ *
204
+ * ## Assurance and scope answer different questions
205
+ *
206
+ * `assuranceProfileVersion`, `verificationStatus` and `verificationMethod` answer
207
+ * *how well do we know who this is*. {@link AssertionClaims.scope} answers *what
208
+ * did the issuer mint this credential to do*. Both must be satisfied and neither
209
+ * substitutes for the other: a document-verified identity presenting a credential
210
+ * scoped only to a wallet read may not authorise a purchase, and a credential
211
+ * scoped to a purchase minted for an unverified identity may not either.
212
+ *
213
+ * ## Personhood is a THIRD question, and it is not assurance
214
+ *
215
+ * `subjectPersonhood` answers neither *how well do we know who this is* nor *what
216
+ * was this minted to do*. It answers **is the subject one human being** — and
217
+ * nothing else on this surface did. It was added because an operator read's audit
218
+ * row promised to name a person while the credential behind it could perfectly
219
+ * well front a rota, a support tool or a reporting job (CAY-0041, CAY-0069), and a
220
+ * gate cannot refuse a fact nobody states.
221
+ *
222
+ * It is OPTIONAL and its absence is a refusal wherever a person is required, never
223
+ * a permission. The reasoning is on the field; read it there before assuming
224
+ * either half.
225
+ *
226
+ * ## The organisation a citizen acts for is a FOURTH question
227
+ *
228
+ * `organisationAuthority` answers none of the three above. It answers **which one
229
+ * Organisation this citizen may act for in this operation**, and it exists because
230
+ * an Organisation Wallet is keyed by the Organisation ID Percayso ID holds and
231
+ * attests, never by a Piper Instance (CAY-0059). A downloadable Instance is a
232
+ * public client and an identifier the caller supplies is a request rather than
233
+ * evidence (CAY-0057), so an organisation provisioning route reading an identifier
234
+ * from a path or a body would be a wallet-creation oracle: anybody who can name a
235
+ * UUID could bring an Organisation Wallet into being, or select one.
236
+ *
237
+ * It is SINGULAR and OPERATION-BOUND, it sits beside `sub` rather than replacing
238
+ * it, and it is emphatically not `mandateFor`. All four points are argued on the
239
+ * field; read them there.
240
+ *
241
+ * ## Which founder wallet a founder claims is a FIFTH question
242
+ *
243
+ * `founderClaimReference` answers **which founder wallet this citizen is claiming**
244
+ * (CAY-0081). It follows `organisationAuthority`'s pattern: one value, minted by
245
+ * the issuer for one operation, read by that operation alone. It goes one step
246
+ * further than that claim, because it is half of a claim on a founder's
247
+ * allocation. The reference travels on a `'claim-founder-wallet'` credential and
248
+ * on nothing else, and that credential does not verify without it. Both halves
249
+ * of that rule are argued on the field.
250
+ *
251
+ * ## The two reserved fields
252
+ *
253
+ * `recoveredAt` and `sessionAgeSeconds` are declared and depended on by nothing.
254
+ * They are here so the shape is fixed now, while it is free: a recovery is a
255
+ * second, shorter path to the signing key (CAY-0008), and a hold on the money
256
+ * path after one may eventually want to read them — but retrofitting a hold onto
257
+ * a live account is a migration over people's money, so the room is left in the
258
+ * shape today rather than added under pressure. Nothing in Phase A may branch on
259
+ * them; this file names no recovery window and suggests no value for either,
260
+ * because that would be defaulting a policy call in code.
261
+ */
262
+ /**
263
+ * An act a holder performs against this contract surface, named so that a policy
264
+ * can be written about it.
265
+ *
266
+ * ONE VOCABULARY, NOT TWO. These same keys are what an operation's acceptance
267
+ * policy is written against (`assurance.ts`, CAY-0029) and what an assertion's
268
+ * {@link AssertionClaims.scope} lists (CAY-0030). They are declared here, in the
269
+ * file that declares the claims, precisely so there is one list rather than a
270
+ * scope vocabulary drifting away from a policy vocabulary; `assurance.ts`
271
+ * re-exports this type rather than restating it, the same way it consumes
272
+ * {@link VerificationMethod} from here.
273
+ *
274
+ * These are OPERATIONS, not routes. Every route on the contract surface that
275
+ * carries an identity maps to one of these — `assurance.ts`'s
276
+ * `OPERATION_FOR_ROUTE` is that correspondence, and it is a compile error to add
277
+ * a route without stating which operation it exercises. The correspondence is not
278
+ * one-to-one in the other direction: an operation may be classified before it has
279
+ * a route, which is how `'provision-citizenship'` was decided in CAY-0029 and
280
+ * read, unchanged, by the slice that finally served it (CAY-0031).
281
+ *
282
+ * `'provision-citizenship'` and `'read-own-citizenship'` are two operations
283
+ * because they are two acts. Provisioning creates a wallet and grants cents;
284
+ * asking whether it has happened creates nothing at all. Naming them once and
285
+ * gating them alike would make the question as expensive to ask as the answer is
286
+ * to produce, and would put the write's assurance bar in front of a read of one's
287
+ * own record.
288
+ *
289
+ * `'cancel-purchase'` is a member of THIS vocabulary rather than a fourth
290
+ * vocabulary of its own (CAY-0032). Cancelling is an act a holder performs against
291
+ * this surface, so the question "which scope authorises this" already had an
292
+ * answer here and reaching for a new kind of answer would have given the gate two
293
+ * things to check instead of one. It is separate from `'authorise-purchase'`
294
+ * because the two are separate acts: a credential minted to let a confirmation
295
+ * page finish a purchase should not, by that fact alone, be able to terminate one.
296
+ *
297
+ * `'provision-organisation'` is its own operation beside `'provision-citizenship'`
298
+ * rather than a second use of it (CAY-0059). The two bind different holders from
299
+ * different facts: citizenship binds the authenticated `sub`, and organisation
300
+ * provisioning binds the one Organisation ID the issuer attested on
301
+ * {@link AssertionClaims.organisationAuthority}. Scope is checked per operation at
302
+ * the gate, so one key covering both acts would let a credential minted to
303
+ * provision its holder's own citizenship bring an Organisation Wallet into being.
304
+ *
305
+ * `'claim-founder-wallet'` is its own operation beside `'provision-citizenship'`
306
+ * for the same reason, and a stronger one (CAY-0081). Provisioning binds a
307
+ * wallet it creates and mints a sign-up grant into it. The founder claim binds a
308
+ * founder wallet that already exists, and a claimed founder must never receive a
309
+ * sign-up grant by any route. One key covering both would let a credential minted
310
+ * to claim provision, and the reverse. It is served at
311
+ * `ROUTES.citizenshipFounderClaim`, and it binds the wallet named by
312
+ * {@link AssertionClaims.founderClaimReference} and no other.
313
+ *
314
+ * `'make-commercial-payment'` is an Organisation paying a Citizen (CAY-0079). It
315
+ * is its own operation rather than a second use of `'authorise-purchase'`, which
316
+ * brings sterling in and moves nothing out of an organisation. A commercial payment
317
+ * moves value out of an organisation to an individual, so a credential minted to
318
+ * buy Cayso must not thereby be able to pay somebody. It was classified before it
319
+ * was served, as `'provision-citizenship'` was, and it is served at
320
+ * `ROUTES.commercialPayment`.
321
+ *
322
+ * `'place-quote'`, `'withdraw-quote'` and `'request-buy-back'` are the citizen
323
+ * market's three acts (CAY-0080, Open decision Q). They are three operations
324
+ * because they are three acts: a credential minted to withdraw a quote must not
325
+ * thereby place one, and selling to the Treasury is not a quote at all, since the
326
+ * Treasury rests no quote in the book (Open decision G). None of them is a second
327
+ * use of `'make-commercial-payment'` either, because an organisation takes no part
328
+ * in the citizen market. They were classified before they were served, and each is
329
+ * served at one route: `ROUTES.placeQuote`, `ROUTES.withdrawQuote` and
330
+ * `ROUTES.requestBuyBack`.
331
+ *
332
+ * `'read-citizen-market'` is reading the book (CAY-0080 §1.4). It is its own
333
+ * operation because it is a read beside two writes: a credential minted to look at
334
+ * the book must not thereby place or withdraw a quote. It is served at
335
+ * `ROUTES.citizenMarket`.
336
+ *
337
+ * `'read-marketplace-receipt'` is the marketplace status read (CAY-0083 §2): Piper,
338
+ * under the buyer's assertion, asking what an idempotency key landed after the
339
+ * answer to a sale or refund was lost. It is its own operation because it is a read
340
+ * beside the money-grade settlement it reports on, and a credential minted to ask
341
+ * what happened must not thereby be able to make something happen. It is the
342
+ * first of CAY-0083 §3's three keys to be published. It is classified before it is
343
+ * served, and no route serves it yet.
344
+ *
345
+ * `'settle-marketplace-order'` and `'settle-marketplace-refund'` are the two
346
+ * money-grade marketplace operations whose outcome that read reports (CAY-0083 §3):
347
+ * Piper presenting Store Platform's signed order, and Store Platform's signed refund
348
+ * decision, each under the buyer's assertion. They are two operations because they
349
+ * check two authorities. A sale moves the buyer's cents on the buyer's say; a refund
350
+ * moves a publisher's cents on Store Platform's decision, with the buyer as payee.
351
+ * A credential minted to buy must not thereby be able to draw back from a
352
+ * publisher, and one operation with a mode flag would blur which authority is
353
+ * checked. Neither is a second use of `'authorise-purchase'`, which brings sterling
354
+ * in, or of `'make-commercial-payment'`, which is organisation-to-Citizen only.
355
+ * Both are classified before they are served, and no route serves either yet.
356
+ */
357
+ export type ContractOperation = 'read-own-wallet' | 'read-purchase-result' | 'authorise-purchase' | 'cancel-purchase' | 'provision-citizenship' | 'read-own-citizenship' | 'provision-organisation' | 'claim-founder-wallet' | 'make-commercial-payment' | 'place-quote' | 'withdraw-quote' | 'request-buy-back' | 'read-citizen-market' | 'read-marketplace-receipt' | 'settle-marketplace-order' | 'settle-marketplace-refund';
358
+ /**
359
+ * An operation key belonging to a deployment's OWN surfaces rather than to this
360
+ * contract — operator work, above all. Deliberately not enumerated: this package
361
+ * is published and carries the Piper-facing surface only (CAY-0026), and naming
362
+ * an internal vocabulary here would publish a set of keys no consumer of this
363
+ * contract may ever present. Where the closed set is kept instead is stated on
364
+ * {@link ScopeKey}.
365
+ */
366
+ export type InternalOperation = string;
367
+ /**
368
+ * A key an assertion's {@link AssertionClaims.scope} may carry: one of this
369
+ * contract's {@link ContractOperation}s, or one of the deployment's own
370
+ * {@link InternalOperation}s.
371
+ *
372
+ * Two vocabularies meet in one array and only one of them is published. A
373
+ * consumer needs every {@link ContractOperation} named here in order to mint a
374
+ * credential this deployment will accept. The internal half it does not need and
375
+ * must not have.
376
+ *
377
+ * So this type is open where {@link ContractOperation} is closed — and the closed
378
+ * set MOVES rather than disappearing. It is kept in two places that know what this
379
+ * one cannot:
380
+ *
381
+ * - the VERIFIER is told the deployment's own vocabulary and refuses a scope key
382
+ * that is in neither list, so an unrecognised key is malformed rather than
383
+ * merely unauthorised. A deployment that declares no internal vocabulary
384
+ * admits none — empty is the default and empty is not permissive;
385
+ * - the SERVICE declares its internal keys as a closed union of its own, so a
386
+ * route cannot be classified against an operation that does not exist.
387
+ *
388
+ * An empty scope still authorises nothing, here as everywhere else.
389
+ */
390
+ export type ScopeKey = ContractOperation | InternalOperation;
391
+ /**
392
+ * The furthest ahead of now, in **seconds**, a verified assertion's `exp` may
393
+ * sit before it is refused as over-long.
394
+ *
395
+ * It is a ceiling, not the lifetime: the lifetime band is 60–120 seconds (see the
396
+ * file docblock) and this sits comfortably above it, so an issuer working inside
397
+ * the band is never near it. It exists because the issuer is ANOTHER TEAM and
398
+ * this is a check on somebody else's output: a verifier that trusts an `exp`
399
+ * because the issuer is friendly forbids nothing, and a correctly-signed
400
+ * credential whose `exp` sits a week out is the long-lived credential this
401
+ * contract exists to refuse (CAY-0008).
402
+ *
403
+ * It lives on the contract rather than inside one service because two things read
404
+ * it — the verifier that refuses an over-long credential, and the replay
405
+ * retention floor derived from it below — and because a consumer is entitled to
406
+ * know the ceiling its credentials are measured against.
407
+ */
408
+ export declare const MAX_ASSERTION_LIFETIME_SECONDS = 300;
409
+ /**
410
+ * How far apart, in **seconds**, two honest clocks are assumed to be able to
411
+ * drift.
412
+ *
413
+ * It is applied in ONE DIRECTION ONLY: to refuse a credential whose `iat` sits
414
+ * further into the future than this, which no honest issuer's clock explains. It
415
+ * is NOT added to `exp`. Tolerance that extends a credential's life is tolerance
416
+ * that defeats the short lifetime, so the rule is stated as a direction rather
417
+ * than as a number a reader might apply symmetrically: **skew may refuse; it may
418
+ * never admit.** `exp` at or before now is expired, exactly.
419
+ */
420
+ export declare const CLOCK_SKEW_TOLERANCE_SECONDS = 30;
421
+ /**
422
+ * The floor, in **seconds**, on how long a recorded assertion use must be kept so
423
+ * that replay is actually refused (CAY-0030).
424
+ *
425
+ * DERIVED, NEVER WRITTEN DOWN AS A NUMBER. A retention window shorter than the
426
+ * longest life a credential can have plus the clock disagreement around it is a
427
+ * window a replay simply waits out: the record of the first use is discarded
428
+ * while the credential itself is still within `exp`, and the second presentation
429
+ * looks like a first. Deriving the floor from the two constants above means the
430
+ * relationship cannot go stale when either moves, and means there is no second
431
+ * copy of it to disagree with this one.
432
+ *
433
+ * It is a FLOOR, not the window. A deployment may keep uses longer; it may not
434
+ * keep them for less, and an implementation must refuse a shorter configured
435
+ * window **in code** rather than note the requirement in a comment.
436
+ */
437
+ export declare const MINIMUM_ASSERTION_USE_RETENTION_SECONDS: number;
438
+ /**
439
+ * How an identity came to be verified. **What each member asserts is stated in
440
+ * `assurance.ts`'s `EVIDENCE_STATEMENTS`** (CAY-0029) — this union names the
441
+ * methods; that table says what evidence each one stands for, and which
442
+ * operations accept it.
443
+ *
444
+ * Not every method is acceptable at a money gate: `'dev-bypass'` is Piper's
445
+ * development shortcut, it asserts nothing at all, and it is explicitly *not*
446
+ * acceptable where "identity-verified" is meant (CAY-0021). It appears in no
447
+ * operation's accepted set.
448
+ */
449
+ export type VerificationMethod = 'dev-bypass' | 'document' | 'document-and-biometric';
450
+ /** Whether the issuer regards this identity as verified. Says nothing, on its own, about *how*. */
451
+ export type VerificationStatus = 'unverified' | 'verified';
452
+ /**
453
+ * Whether the credential's subject is ONE HUMAN BEING, or a credential standing
454
+ * in front of more than one of them — or of none (CAY-0069, amending CAY-0041 and
455
+ * CAY-0064).
456
+ *
457
+ * This is the fact CAY-0041 §3's Option B needed and the envelope did not have.
458
+ * `sub` is an opaque string, and nothing beside it said whether it named a person
459
+ * or a rota, a support tool or a reporting job. An operator read's audit row
460
+ * therefore answered "who looked at this wallet" with whatever the deployment
461
+ * happened to be configured with, and no gate could tell the two apart.
462
+ *
463
+ * ## It is a union of two named cases, not a boolean
464
+ *
465
+ * `subjectIsAPerson: boolean` would have made the negative case nameless — one
466
+ * value standing for a rota, a service credential and an automated job alike —
467
+ * and would have to be re-litigated the first time a third case arrives (a shared
468
+ * credential that names the human it was presented for is Option C, and it is not
469
+ * foreclosed). Two named cases carry their meaning on the wire, which is the same
470
+ * reason `AuditedVerificationMethod` is a union and the acting party on a money
471
+ * row is one (CAY-0054).
472
+ *
473
+ * ## What each case asserts — and it is an ISSUER's statement, not ours
474
+ *
475
+ * - `'natural-person'` — this credential was minted for one human being and is
476
+ * presented by them. It is the only value an operator route accepts.
477
+ * - `'shared-credential'` — it was not: it fronts several people, or nobody at
478
+ * all. A support tool minting on somebody's behalf, an on-call rota's shared
479
+ * identity, a reporting job. Well-formed, and refused wherever a person is
480
+ * required.
481
+ *
482
+ * Cayso cannot check either one, and this contract does not pretend otherwise:
483
+ * the issuer asserts it, exactly as the issuer asserts `verificationMethod`, and
484
+ * an issuer's own half of the binding is a stated limit rather than something a
485
+ * verifier can audit (CAY-0030 §8). Under a verifier that checks no signature it
486
+ * is as choosable as `sub` is — see `AssertionClaims.subjectPersonhood`.
487
+ *
488
+ * ## Why it is NOT part of the assurance profile
489
+ *
490
+ * `assurance.ts`'s `VerificationEvidence` describes IDENTITY PROOFING — a document
491
+ * examined, a biometric captured, a comparison recorded. This is a different kind
492
+ * of fact and is orthogonal to all three: a shared credential can be perfectly
493
+ * document-verified (that is the residual CAY-0064 §5 named), and one human being
494
+ * can be verified by nothing at all. Folding it into the evidence would make it
495
+ * versioned by the assurance profile, so a new personhood case would become a
496
+ * re-interpretation of what every method proves — which it is not — and it would
497
+ * have to be answered by `evidenceSupports` and `methodForEvidence`, where no
498
+ * amount of personhood makes `'document'` supportable. So it rides BESIDE the
499
+ * assurance fields on the claims, where `verificationStatus` and
500
+ * `verificationMethod` are, and the profile is left describing proofing alone.
501
+ */
502
+ export type SubjectPersonhood = 'natural-person' | 'shared-credential';
503
+ /**
504
+ * The header of a minted assertion: how to check it, read BEFORE anything in it
505
+ * is believed and never believed itself (CAY-0030).
506
+ *
507
+ * Both fields are unauthenticated at the moment they are read — they are, after
508
+ * all, part of the thing being checked. That is exactly why they are separated
509
+ * from the claims: a header field is an input to selecting a check, and a claim
510
+ * is an output of passing one.
511
+ */
512
+ export interface AssertionHeader {
513
+ /**
514
+ * The signature algorithm the issuer used.
515
+ *
516
+ * Typed `string`, deliberately NOT a closed union of the algorithms this
517
+ * deployment permits — the same reasoning
518
+ * {@link AssertionClaims.assuranceProfileVersion} is typed `string` for. A
519
+ * header naming an algorithm we do not permit must be REPRESENTABLE in order to
520
+ * be refused, and a type admitting only permitted values cannot describe the
521
+ * header a verifier most needs to reject.
522
+ *
523
+ * A VALUE TO CHECK, NEVER A VALUE TO SELECT ON. The permitted algorithm is
524
+ * fixed by the issuer's registry entry; this field is compared against it and a
525
+ * mismatch is a refusal. A verifier that dispatched on this field would let the
526
+ * presenter of a credential choose the algorithm their forgery is easiest
527
+ * under, which is the whole of the attack.
528
+ */
529
+ readonly alg: string;
530
+ /**
531
+ * Which of the issuer's keys signed this assertion.
532
+ *
533
+ * A SELECTOR, NOT A HINT. Exactly one key is chosen — from the keys registered
534
+ * to the `iss` on the claims, never from every key the deployment knows — and
535
+ * if that key is unknown, belongs to another issuer, or has been revoked, the
536
+ * credential is refused. There is no second attempt: a verifier that tries the
537
+ * remaining keys after one fails has made revocation meaningless, because a
538
+ * revoked key's credentials keep verifying under whichever key is tried next.
539
+ */
540
+ readonly kid: string;
541
+ }
542
+ /**
543
+ * A minted assertion as a whole: the header that says how to check it, and the
544
+ * claims it carries (CAY-0030).
545
+ *
546
+ * The two are separate types and are not merged, for the reason the file docblock
547
+ * gives at length: a verifier reads the header to CHOOSE a check and reads the
548
+ * claims as what that check ESTABLISHED. A single flat object loses that ordering
549
+ * and is how `alg` becomes something to dispatch on and `kid` becomes a hint.
550
+ *
551
+ * This contract does not fix the wire *encoding* — how a header and its claims are
552
+ * serialised and signed together is the issuer's and the verifier's business, and
553
+ * naming a serialisation here would pin a cryptographic dependency into a
554
+ * types-only published package. What it fixes is that a credential HAS these two
555
+ * parts and which fields belong to each.
556
+ */
557
+ export interface AssertionEnvelope {
558
+ /** How to check this credential. Read first, believed never. */
559
+ readonly header: AssertionHeader;
560
+ /** What the issuer asserts, established only once the header's check has passed. */
561
+ readonly claims: AssertionClaims;
562
+ }
563
+ /**
564
+ * The claims carried by a minted assertion (CAY-0021, CAY-0030). This is the
565
+ * shape a verifier checks; it is not a value a consumer constructs and trusts.
566
+ */
567
+ export interface AssertionClaims {
568
+ /**
569
+ * Who minted this assertion.
570
+ *
571
+ * It is the field every other trust decision hangs off: which keys may have
572
+ * signed the credential, which algorithm those keys use, which holders this
573
+ * issuer may speak for, and which economic universe it serves. An `iss` that is
574
+ * not in a verifier's configured registry is refused, and an empty registry
575
+ * therefore refuses everything — the fail-closed posture every adapter in this
576
+ * tree takes (CLAUDE.md, Trust and authentication). Recognising an issuer is a
577
+ * deliberate act of configuration, never something an issuer achieves by
578
+ * presenting a credential.
579
+ *
580
+ * Typed `string` rather than a literal for the reason the assurance profile
581
+ * version is: an unrecognised issuer must be representable in order to be
582
+ * refused.
583
+ */
584
+ readonly iss: string;
585
+ /**
586
+ * This assertion's unique identity, minted fresh by the issuer for each one.
587
+ *
588
+ * It is what makes a second presentation of the SAME credential detectable: a
589
+ * verifier records the pair (`iss`, `jti`) on the first successful verification
590
+ * and refuses any later presentation carrying it. The pair, not `jti` alone —
591
+ * two issuers may legitimately mint the same `jti` and neither is at fault, so
592
+ * a store keyed on `jti` alone would have one issuer's credential lock out
593
+ * another's.
594
+ *
595
+ * An issuer must not reuse one, and must not derive one from anything
596
+ * predictable: it is the issuer's obligation that two assertions never carry the
597
+ * same `jti`, and a verifier that sees a repeat treats it as a replay rather
598
+ * than investigating whose fault it was.
599
+ */
600
+ readonly jti: string;
601
+ /**
602
+ * The holder's identity id at the issuer. **NEVER reaches the chain** — it
603
+ * stops at the `holders` mapping in this repository (CAY-0006). Bound to
604
+ * {@link iss}: an issuer may only assert subjects in its own space, so one
605
+ * issuer cannot mint for another's holder.
606
+ */
607
+ readonly sub: string;
608
+ /** The intended recipient. Must be exactly `'cayso'`, and must be *checked*, not merely present. */
609
+ readonly aud: 'cayso';
610
+ /**
611
+ * What this assertion may be used for: the operations the issuer minted it to
612
+ * perform, named as {@link ScopeKey}s — this contract's own
613
+ * {@link ContractOperation}s, and any operation key the deployment declares for
614
+ * a surface that is not Piper-facing.
615
+ *
616
+ * **AN EMPTY SCOPE IS ACCEPTED FOR NOTHING.** `[]` does not mean "unrestricted"
617
+ * and it does not mean "the default"; it means the issuer authorised no
618
+ * operation, and every gated operation refuses it. That is the fail-closed
619
+ * reading and it is the only safe one: the alternative — an absent or empty
620
+ * scope meaning "everything" — turns the one field that narrows a credential
621
+ * into the one field whose omission widens it.
622
+ *
623
+ * It is REQUIRED and not optional, for the same reason. An optional scope has a
624
+ * missing case, and every missing case is eventually read as permission. A
625
+ * credential that authorises nothing is a well-formed credential that authorises
626
+ * nothing, and it is representable so that it can be refused.
627
+ *
628
+ * Scope is not assurance. It says what the credential is FOR; the assurance
629
+ * profile says how well the identity behind it is known. An operation needs
630
+ * both, and neither stands in for the other.
631
+ */
632
+ readonly scope: readonly ScopeKey[];
633
+ /**
634
+ * Expiry, in **seconds** since the Unix epoch. Short — 60–120 seconds; see the
635
+ * file docblock. This is seconds, not `Millis`: the other timestamps on this
636
+ * contract surface (`cayso.wallet`, `cayso.purchase`) are `Millis`, but `exp`
637
+ * and `iat` are the *issuer's* fields and follow the issuer's convention.
638
+ * Reading an `exp` in seconds as if it were milliseconds is a credential that
639
+ * never expires — so the unit is stated rather than assumed.
640
+ *
641
+ * At or before now is expired, with NO skew grace added (see
642
+ * {@link CLOCK_SKEW_TOLERANCE_SECONDS}), and further ahead than
643
+ * {@link MAX_ASSERTION_LIFETIME_SECONDS} is refused as over-long.
644
+ */
645
+ readonly exp: number;
646
+ /**
647
+ * Issued-at, in **seconds** since the Unix epoch — the issuer's convention, not
648
+ * `Millis`. See `exp`. An `iat` further ahead of now than
649
+ * {@link CLOCK_SKEW_TOLERANCE_SECONDS} is refused: that is the one place skew
650
+ * is read, and it is read to refuse.
651
+ */
652
+ readonly iat: number;
653
+ /** Whether the issuer regards this identity as verified. Insufficient alone — read it with `verificationMethod`. */
654
+ readonly verificationStatus: VerificationStatus;
655
+ /**
656
+ * How the identity was verified, or `null` when it was not. Nullable rather
657
+ * than optional: an unverified identity *has* no method, and the field is
658
+ * always present to carry that `null`. A money gate must require a method that
659
+ * is explicitly not `'dev-bypass'` (CAY-0021).
660
+ */
661
+ readonly verificationMethod: VerificationMethod | null;
662
+ /**
663
+ * The assurance profile this claim was minted under — `ASSURANCE_PROFILE_VERSION`
664
+ * for a claim minted against the current profile (CAY-0029).
665
+ *
666
+ * Typed `string`, deliberately NOT the literal: a claim carrying a version this
667
+ * deployment does not recognise must be REPRESENTABLE in order to be refused,
668
+ * and a type that only admits the current version cannot describe the claim a
669
+ * verifier most needs to reject. The refusal is
670
+ * `assessAssurance`'s, and its reason is a closed union member.
671
+ */
672
+ readonly assuranceProfileVersion: string;
673
+ /**
674
+ * Whether this credential's subject is one human being
675
+ * ({@link SubjectPersonhood}). OPTIONAL on the wire, and its absence is a
676
+ * refusal wherever a person is required — never a permission (CAY-0069).
677
+ *
678
+ * ## Why optional, and why that is not a default
679
+ *
680
+ * The dangerous shape would be an optional claim read as "yes, a person" when
681
+ * it is missing, which is worse than no claim at all: it would put a schema
682
+ * behind a guess. Nothing does that. What absence means is exactly **the issuer
683
+ * has not said**, and a consumer of this contract is told the consequence
684
+ * plainly — an operator route refuses a credential that does not carry
685
+ * `'natural-person'`, absence included, and nothing anywhere derives personhood
686
+ * from `sub`, from a naming convention, or from the verification method.
687
+ *
688
+ * It is optional rather than required because this deployment cannot make a
689
+ * claim on an issuer's behalf, and the decision that introduced it is scoped to
690
+ * OPERATOR routes (CAY-0069). A required claim would refuse every citizen
691
+ * credential minted against an issuer that has not yet been asked to assert it —
692
+ * a change to gates the decision deliberately does not touch. So the wire stays
693
+ * open, the gate that needs the fact refuses without it, and the day the
694
+ * programme wants personhood required of every credential it is a further
695
+ * decision rather than a field already forced.
696
+ *
697
+ * The comparison with `scope` is worth making, because `scope` IS required for a
698
+ * reason that does not apply here. An absent scope had to be refused because the
699
+ * only alternative reading — "unrestricted" — widens a credential. An absent
700
+ * personhood claim widens nothing: it narrows, everywhere it is read.
701
+ *
702
+ * ## It is as trustworthy as the verifier, and no more
703
+ *
704
+ * Under a verifier that checks no signature this claim is attacker-choosable
705
+ * exactly as `sub` is — a caller that can mint an envelope can mint one asserting
706
+ * `'natural-person'`. Declaring it makes the promise STRUCTURAL; a real signature
707
+ * check is what makes it TRUSTWORTHY, and the two are separate pieces of work.
708
+ */
709
+ readonly subjectPersonhood?: SubjectPersonhood;
710
+ /**
711
+ * The ONE Organisation ID for which the issuer says this citizen may perform
712
+ * THIS operation — the organisation-authority claim (CAY-0059 §3). OPTIONAL on
713
+ * the wire, and its absence provisions nothing.
714
+ *
715
+ * ## It is a second fact beside `sub`, and it replaces nothing
716
+ *
717
+ * `sub` stays what it has always been: the citizen Percayso ID authenticated
718
+ * directly. This says which Organisation that citizen is acting for. Collapsing
719
+ * the two — putting the Organisation ID in `sub` on organisation-scoped calls —
720
+ * was considered and rejected on the pillar owner's correction: the acting
721
+ * citizen and the organisation they act for are two facts, and one of them is
722
+ * the fact the audit trail and the acting party depend on (CAY-0054).
723
+ *
724
+ * ## SINGULAR and OPERATION-BOUND, never a list
725
+ *
726
+ * One Organisation ID, for one operation, minted for that operation. It is
727
+ * deliberately NOT the set of every Organisation the citizen is associated
728
+ * with: a list is a menu, and a credential carrying a menu lets whatever holds
729
+ * it choose from the menu. Piper may ASK Percayso ID for an assertion concerning
730
+ * a selected Organisation; Percayso ID verifies the relationship from its own
731
+ * records and mints the one value. Piper's submitted value is a request and
732
+ * never evidence (CAY-0057).
733
+ *
734
+ * ## THIS IS NOT `mandateFor`, AND THE TWO MUST NOT BE OVERLOADED
735
+ *
736
+ * CAY-0059 §7 is explicit, and the confusion is the mistake this claim most
737
+ * invites. {@link mandateFor} resolves Organisation **Wallet** IDs and is about
738
+ * SPENDING AUTHORITY through the existing mandate and allowance model
739
+ * (CAY-0024) — a wallet that already exists, and a limit already granted over
740
+ * it. This claim is about ESTABLISHING OR RESOLVING THE HOLDER, at a moment when
741
+ * the wallet does not exist yet and so cannot be named. A provisioning call has
742
+ * no wallet id to carry, which is the whole reason a second claim was needed
743
+ * rather than a second reading of the first. Nothing reads one where the other
744
+ * is meant, and a change that made either stand in for the other would undo
745
+ * CAY-0059 §7 in a line.
746
+ *
747
+ * ## Why the value is a plain `string` here
748
+ *
749
+ * The same reason `sub` is, and the same reason `Cents` is not branded
750
+ * (`./money`): this is the WIRE, JSON carries no brands, and a published
751
+ * Apache-2.0 type a consumer cannot build from a string it holds is a
752
+ * distinction that consumer cannot observe. The type that cannot express the
753
+ * violation — an Instance ID standing where an Organisation ID belongs (CAY-0059
754
+ * §5) — lives on the write path in `apps/api`, where the holder key is actually
755
+ * formed, exactly as the unforgeable money type lives beside the table rather
756
+ * than on the wire.
757
+ *
758
+ * ## It is as trustworthy as the verifier, and no more
759
+ *
760
+ * Under a verifier that checks no signature this claim is attacker-choosable
761
+ * exactly as `sub` is. Declaring it makes the promise STRUCTURAL — there is no
762
+ * path from a path, a body or a query to the holder key, so the wallet-creation
763
+ * oracle CAY-0057 names does not exist; a real signature check is what makes it
764
+ * TRUSTWORTHY, and the two are separate pieces of work.
765
+ */
766
+ readonly organisationAuthority?: string;
767
+ /**
768
+ * The founder claim reference: the opaque value naming the ONE founder wallet
769
+ * the issuer says this citizen may claim (CAY-0081, open decision 3). OPTIONAL
770
+ * on the wire, and read by `'claim-founder-wallet'` and by no other operation.
771
+ *
772
+ * ## It travels with its operation, in both directions
773
+ *
774
+ * A credential whose `scope` carries `'claim-founder-wallet'` must carry this
775
+ * claim, and a credential that carries this claim must be scoped to
776
+ * `'claim-founder-wallet'`. A verifier refuses either half missing as a
777
+ * malformed credential, before any gate reads it:
778
+ *
779
+ * - **scoped to claim, with no reference.** The credential names an act it
780
+ * cannot perform, because the founder claim takes the wallet from here and
781
+ * from nowhere else. Refusing it at the verifier means no route can ever
782
+ * be handed a founder claim credential with the reference missing and have
783
+ * to decide what that means.
784
+ * - **a reference, not scoped to claim.** The reference is half of a claim.
785
+ * Identity Platform holds nominated person → reference, and Cayso holds
786
+ * reference → wallet. A credential carrying it to any other operation puts
787
+ * that half in front of a route with no business reading it, so the
788
+ * credential is refused rather than the claim ignored.
789
+ *
790
+ * ## It is carried here and never in a request
791
+ *
792
+ * The reference is inside the signed assertion because a reference in a path,
793
+ * a body or a query would be a caller's claim about which allocation is theirs
794
+ * (CAY-0021). Piper may ask Percayso ID for a founder claim credential.
795
+ * Percayso ID decides from its own nomination record which reference, if any,
796
+ * this citizen is asserted for, and mints that one value. The founder claim
797
+ * route takes no field that names a Citizen, a wallet or an amount.
798
+ *
799
+ * ## What it is not
800
+ *
801
+ * It is NOT a wallet id. The wallet id is the operator-facing handle and never
802
+ * appears in an assertion, so the nomination list cannot become a directory of
803
+ * person → wallet. It is NOT derived from a Percayso identity. It carries no
804
+ * `sub`, no hash of one, no email and no name, and it is drawn from nothing by
805
+ * the Board's preparer. The founder schedule's own shape rule is the database's
806
+ * and is not restated here. This contract asks for a non-empty string and says
807
+ * nothing more about its shape.
808
+ *
809
+ * ## Why the value is a plain `string` here
810
+ *
811
+ * For the reason {@link organisationAuthority} is one: this is the wire, JSON
812
+ * carries no brands, and a published type a consumer cannot build from a string
813
+ * it holds is a distinction that consumer cannot observe.
814
+ *
815
+ * ## It is as trustworthy as the verifier, and no more
816
+ *
817
+ * Under a verifier that checks no signature this claim is attacker-choosable
818
+ * exactly as `sub` is. Declaring it makes the promise STRUCTURAL. A real
819
+ * signature check is what makes it TRUSTWORTHY, and the two are separate pieces
820
+ * of work.
821
+ */
822
+ readonly founderClaimReference?: string;
823
+ /**
824
+ * The Organisation **Wallet IDs** this identity holds the mandate for
825
+ * (CAY-0024, CAY-0059 §7). Each entry is a wallet id — the opaque identifier
826
+ * this repository gives a wallet, and the only handle an operator surface
827
+ * displays — never the Organisation ID of the holder behind it, and never an
828
+ * Instance ID. Present when the holder may act for one or more organisations.
829
+ *
830
+ * ## THIS IS NOT THE ORGANISATION-AUTHORITY CLAIM, AND THE TWO MUST NOT BE
831
+ * OVERLOADED
832
+ *
833
+ * {@link organisationAuthority} carries ONE Organisation ID and is about
834
+ * ESTABLISHING OR RESOLVING THE HOLDER. This claim is about SPENDING AUTHORITY,
835
+ * and it resolves through the existing wallet-based mandate and allowance model
836
+ * (CAY-0024) — a wallet that already exists, and a limit already granted over
837
+ * it. They cannot be one claim, and CAY-0059 §7 says so directly: at
838
+ * provisioning time the wallet does not yet exist, so a provisioning call has no
839
+ * wallet id to put here, and a claim about establishing a holder cannot be a
840
+ * claim about spending against a wallet that is already there. Reading one where
841
+ * the other is meant is the confusion §7 was written to prevent — and the
842
+ * confusion an earlier wording of this very docblock produced, by describing the
843
+ * field as a list of Instance ids (#208).
844
+ *
845
+ * The register of who may act for an organisation is Percayso ID's, not this
846
+ * economy's (CAY-0068). What arrives here is the issuer's attestation, accepted
847
+ * under a stated acceptance policy and never checked against a record in this
848
+ * tree.
849
+ *
850
+ * Declaring it sets no Signatory / Authorised-User threshold (a canon figure, read
851
+ * from configuration). The commercial payment's mandate route reads it to decide
852
+ * whether the caller may pay from the wallet they name (CAY-0079 §1), and nothing
853
+ * reads it as consent to anything else.
854
+ */
855
+ readonly mandateFor?: readonly string[];
856
+ /**
857
+ * RESERVED, declared and not yet depended on. When a key or passkey was last
858
+ * recovered, in seconds since the Unix epoch. Nothing in Phase A may branch on
859
+ * it (CAY-0008).
860
+ */
861
+ readonly recoveredAt?: number;
862
+ /**
863
+ * RESERVED, declared and not yet depended on. Age of the issuer's own sign-in
864
+ * session, in seconds. Nothing in Phase A may branch on it (CAY-0008).
865
+ */
866
+ readonly sessionAgeSeconds?: number;
867
+ }