@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.
- package/LICENSE +201 -0
- package/README.md +114 -0
- package/dist/assertion.d.ts +867 -0
- package/dist/assertion.js +63 -0
- package/dist/assurance.d.ts +353 -0
- package/dist/assurance.js +551 -0
- package/dist/citizen-market.d.ts +468 -0
- package/dist/citizen-market.js +16 -0
- package/dist/citizenship.d.ts +215 -0
- package/dist/citizenship.js +26 -0
- package/dist/client.d.ts +288 -0
- package/dist/client.js +370 -0
- package/dist/commercial-payment.d.ts +243 -0
- package/dist/commercial-payment.js +16 -0
- package/dist/contract-schema.generated.d.ts +14 -0
- package/dist/contract-schema.generated.js +3107 -0
- package/dist/errors.d.ts +171 -0
- package/dist/errors.js +28 -0
- package/dist/index.d.ts +621 -0
- package/dist/index.js +624 -0
- package/dist/marketplace-receipt.d.ts +306 -0
- package/dist/marketplace-receipt.js +116 -0
- package/dist/marketplace-settlement.d.ts +298 -0
- package/dist/marketplace-settlement.js +16 -0
- package/dist/money.d.ts +84 -0
- package/dist/money.js +34 -0
- package/dist/organisation.d.ts +60 -0
- package/dist/organisation.js +16 -0
- package/dist/purchase.d.ts +531 -0
- package/dist/purchase.js +107 -0
- package/dist/restriction.d.ts +262 -0
- package/dist/restriction.js +316 -0
- package/dist/routes.d.ts +155 -0
- package/dist/routes.js +128 -0
- package/dist/schema-types.d.ts +101 -0
- package/dist/schema-types.js +16 -0
- package/dist/schema.d.ts +24 -0
- package/dist/schema.js +16 -0
- package/dist/wallet.d.ts +92 -0
- package/dist/wallet.js +59 -0
- 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
|
+
}
|