@percayso-community/cayso-contracts 0.29.0 → 0.30.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/README.md CHANGED
@@ -21,7 +21,10 @@ Percayso Cloud is the second: its Contribution Store presents the settlement
21
21
  assertion to Cayso directly, and the Cloud generates its types from the `schema`
22
22
  entry point rather than typing Cayso's half by hand. The Cloud's two seams live in
23
23
  `cloud-settlement`, and the settlement path, which a registered issuer calls and
24
- no person does, is `CLOUD_SETTLEMENT_ROUTE`, published apart from `ROUTES`.
24
+ no person does, is `CLOUD_SETTLEMENT_ROUTE`, published apart from `ROUTES`. The
25
+ Cloud verifies Cayso's authorisation acknowledgement under a key it reads from
26
+ `ACKNOWLEDGEMENT_KEYS_ROUTE`, an unauthenticated read of public keys, published
27
+ apart from both.
25
28
 
26
29
  **The operator read surface, on its own entry point** (CAY-0026, Amendment 4). Three
27
30
  reads an operator app's *server* calls — the movement list, the purchase list and the
@@ -49,16 +52,34 @@ restated here.
49
52
  |---|---|
50
53
  | `@percayso-community/cayso-contracts` | Types and constants. No runtime. |
51
54
  | `@percayso-community/cayso-contracts/client` | The thin client — fetch, assertion header, idempotent retry. |
52
- | `@percayso-community/cayso-contracts/schema` | A machine-readable descriptor of the published surface. |
55
+ | `@percayso-community/cayso-contracts/schema` | A machine-readable descriptor of the published surface: the published, versioned surface a consumer in another language generates its types from. |
53
56
  | `@percayso-community/cayso-contracts/operator` | The three operator reads' types and route constants, for an operator app's server. |
54
57
 
55
58
  The split is the point: a consumer wanting only the types takes on no runtime at
56
59
  all. The client owns the fetch, the assertion header and the idempotent retry.
57
60
 
58
61
  The `schema` entry point serves a data description of the same surface —
59
- generated from these very types, never hand-written — for tooling that needs the
60
- shape at runtime (a diff, a doc generator, a compatibility check). It is a
61
- diagnostic, **not** a stand-in for depending on the package: see below.
62
+ generated from these very types, never hand-written. It is a published, versioned
63
+ surface in its own right, and there are two ways to depend on this package through
64
+ it, by language:
65
+
66
+ 1. **A TypeScript consumer imports the types**, and the compiler holds the surface.
67
+ Piper does this. For such a consumer the schema is a tool *about* the contract —
68
+ a diff, a doc generator, a compatibility check — and never a replacement for the
69
+ import.
70
+ 2. **A consumer in another language generates from the `schema` entry point of the
71
+ published tarball at a pinned version**, and treats a drift between its generated
72
+ types and the schema as a build failure. That *is* depending on the package: the
73
+ pin is the lockfile entry and the regeneration is the compile. The Percayso Cloud
74
+ is this consumer (`CAY-0026` Amendment 3; `CMTY-0018` §3 as amended): it is written
75
+ in Go, it cannot import a TypeScript type, and its generator reads the npm tarball
76
+ at a pinned version so that its Go types for the two Cloud seams in
77
+ `cloud-settlement` are never typed by hand. The package ships the schema as a JS
78
+ module only, which is why the generator reads the tarball and not a registry
79
+ endpoint.
80
+
81
+ What stays refused is reading the schema off the wire as a substitute for either:
82
+ see the version endpoint, below.
62
83
 
63
84
  ## Independent versioning
64
85
 
@@ -91,15 +112,18 @@ Alongside it, `GET /api/version/schema` serves the same machine-readable
91
112
  descriptor as `@percayso-community/cayso-contracts/schema` — the surface outside consumers read, with
92
113
  no operator, webhook or peer route. It exists for tooling and diagnostics.
93
114
 
94
- **It is not a substitute for depending on this package.** The descriptor is
95
- data; the guarantee is the *types*, and the types do not exist at runtime — they
96
- are erased when TypeScript compiles. A consumer that reads the schema off the
97
- wire and validates against it has re-created Piper's original mistake by another
98
- route: hand-rolling the shape instead of importing it, and losing every
99
- compile-time guarantee the moment the wire description and the code drift. Import
100
- `@percayso-community/cayso-contracts` and let the compiler hold the surface. Read the schema
101
- endpoint when you are building a tool *about* the contract, never to stand in for
102
- having it.
115
+ **The wire is not a substitute for depending on this package.** The descriptor
116
+ served here is data about *this deployment*; the guarantee is the published
117
+ package at a pinned version. A consumer that reads the schema off the wire and
118
+ validates or generates against it has re-created Piper's original mistake by
119
+ another route: it couples its build to a deployment's uptime, replaces a pinned
120
+ version with whatever happens to be deployed at that moment, and loses every
121
+ guarantee the moment the wire description and its code drift. Import
122
+ `@percayso-community/cayso-contracts` and let the compiler hold the surface, or —
123
+ in another language — generate from the `schema` entry point of the published
124
+ tarball at a pinned version, as the Cloud does. Read the schema *endpoint* when
125
+ you are building a tool *about* a running deployment, never to stand in for
126
+ having the package.
103
127
 
104
128
  ## No dependencies
105
129
 
@@ -383,9 +383,19 @@
383
383
  * It is not a second use of `'make-commercial-payment'`: nothing moves when it is
384
384
  * presented, and what it authorises is spending in later periods, settled by the
385
385
  * Contribution Store's assertion, which no person presents and which therefore has
386
- * no operation here. It is classified before it is served.
386
+ * no operation here.
387
+ *
388
+ * `'revoke-standing-authorisation'` is the owner withdrawing that delegation from
389
+ * their Instance (CAY-0094). It is its own operation rather than a mode of
390
+ * presenting, because one path carries one scope at CAY-0030's gate: a credential
391
+ * minted to revoke must not thereby delegate, nor the reverse.
392
+ *
393
+ * `'bind-node'` is the owner's Instance binding a Node to the wallet its Instance key
394
+ * is already bound to (CMTY-0018 §4, CAY-0095). Its own operation, because it decides
395
+ * where a Node's earnings land and binds no key: a credential minted to delegate
396
+ * spending must not thereby direct earnings, nor the reverse.
387
397
  */
388
- 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' | 'make-citizen-transfer' | 'change-handle' | 'read-handle-change-receipt' | 'place-quote' | 'withdraw-quote' | 'request-buy-back' | 'read-citizen-market' | 'read-marketplace-receipt' | 'settle-marketplace-order' | 'settle-marketplace-refund' | 'present-standing-authorisation';
398
+ 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' | 'make-citizen-transfer' | 'change-handle' | 'read-handle-change-receipt' | 'place-quote' | 'withdraw-quote' | 'request-buy-back' | 'read-citizen-market' | 'read-marketplace-receipt' | 'settle-marketplace-order' | 'settle-marketplace-refund' | 'present-standing-authorisation' | 'revoke-standing-authorisation' | 'bind-node';
389
399
  /**
390
400
  * An operation key belonging to a deployment's OWN surfaces rather than to this
391
401
  * contract — operator work, above all. Deliberately not enumerated: this package
package/dist/assurance.js CHANGED
@@ -518,6 +518,41 @@ export const ASSURANCE_POLICY = {
518
518
  'bypass. Nothing moves when it is presented; what it authorises moves later, without the ' +
519
519
  'holder present, which is why the bar is applied here.',
520
520
  },
521
+ // The owner withdrawing that delegation from their Instance (CAY-0094). Held to the
522
+ // bar of the act it undoes: a credential that could not have delegated must not be
523
+ // able to withdraw a delegation either, or a weaker assertion becomes a way to stop
524
+ // an owner's Instance spending what they authorised.
525
+ 'revoke-standing-authorisation': {
526
+ operation: 'revoke-standing-authorisation',
527
+ assurance: 'required',
528
+ decisions: {
529
+ // Asserts nothing; permanently refused wherever "identity-verified" is meant.
530
+ 'dev-bypass': 'refused',
531
+ document: 'accepted',
532
+ 'document-and-biometric': 'accepted',
533
+ },
534
+ rationale: 'Revoking a standing authorisation withdraws a delegation of spending from the ' +
535
+ "holder's wallet, so it is held to the bar of presenting one: anything but the " +
536
+ 'development bypass. A credential that could not delegate cannot undo a delegation.',
537
+ },
538
+ // The owner's Instance binding a Node to their wallet (CMTY-0018 §4, CAY-0095). Held to
539
+ // the bar of presenting a standing authorisation: a bound Node's earnings are paid into
540
+ // the wallet at every later settlement, without the holder present, so choosing where
541
+ // they land is a decision about somebody's money.
542
+ 'bind-node': {
543
+ operation: 'bind-node',
544
+ assurance: 'required',
545
+ decisions: {
546
+ // Asserts nothing; permanently refused wherever "identity-verified" is meant.
547
+ 'dev-bypass': 'refused',
548
+ document: 'accepted',
549
+ 'document-and-biometric': 'accepted',
550
+ },
551
+ rationale: "Binding a Node decides which wallet the Node's earnings are paid into at every later " +
552
+ 'settlement, without the holder present, and a binding is never re-pointed, so it is ' +
553
+ 'held to the bar of presenting a standing authorisation: anything but the development ' +
554
+ 'bypass.',
555
+ },
521
556
  };
522
557
  /**
523
558
  * Every operation, as a value. Derived from {@link ASSURANCE_POLICY} for the same
@@ -559,6 +594,8 @@ export const OPERATION_FOR_ROUTE = {
559
594
  requestBuyBack: 'request-buy-back',
560
595
  marketplaceOrder: 'settle-marketplace-order',
561
596
  standingAuthorisation: 'present-standing-authorisation',
597
+ revokeStandingAuthorisation: 'revoke-standing-authorisation',
598
+ bindNode: 'bind-node',
562
599
  version: null,
563
600
  };
564
601
  /**
package/dist/client.d.ts CHANGED
@@ -69,7 +69,7 @@
69
69
  */
70
70
  import type { BuyBackMade, BuyBackRequest, CitizenMarketViewed, QuotePlaced, QuoteRequest, QuoteWithdrawalRequest, QuoteWithdrawn } from './citizen-market.js';
71
71
  import type { CitizenshipFounderClaimed, CitizenshipView } from './citizenship.js';
72
- import type { StandingAuthorisationAcknowledged, StandingAuthorisationRequest } from './cloud-settlement.js';
72
+ import type { StandingAuthorisationAcknowledged, StandingAuthorisationRequest, StandingAuthorisationRevocationRequest, StandingAuthorisationRevoked, NodeBindingRequest, NodeBound } from './cloud-settlement.js';
73
73
  import type { CitizenTransferMade, CitizenTransferRequest } from './citizen-transfer.js';
74
74
  import type { CommercialPaymentMade, CommercialPaymentRequest } from './commercial-payment.js';
75
75
  import type { HandleChangeCharged, HandleChangeReceiptReadResponse } from './handle-change.js';
@@ -304,10 +304,31 @@ export interface CaysoClient {
304
304
  * Safe to repeat: the authorisation's `jti` is its own key, and presenting the same
305
305
  * authorisation again binds nothing twice. A refusal arrives as a thrown
306
306
  * {@link CaysoApiError} whose `code` is a `StandingAuthorisationRefusal` member, one
307
- * of the gate's codes, or a seam code, and means nothing was bound. Published before
308
- * the route is served.
307
+ * of the gate's codes, or a seam code, and means nothing was bound. Each
308
+ * presentation answers a freshly signed acknowledgement.
309
309
  */
310
310
  presentStandingAuthorisation(req: StandingAuthorisationRequest): Promise<StandingAuthorisationAcknowledged>;
311
+ /**
312
+ * Revoke a standing authorisation the caller's wallet holds, named by its `jti`,
313
+ * under the owner's assertion (`revoke-standing-authorisation`, CAY-0094).
314
+ *
315
+ * Safe to repeat: revoking a revoked authorisation answers `revoked` again and
316
+ * writes nothing. A refusal arrives as a thrown {@link CaysoApiError} whose `code`
317
+ * is a `StandingAuthorisationRevocationRefusal` member, one of the gate's codes, or
318
+ * a seam code, and means nothing was written.
319
+ */
320
+ revokeStandingAuthorisation(req: StandingAuthorisationRevocationRequest): Promise<StandingAuthorisationRevoked>;
321
+ /**
322
+ * Bind a Node to the wallet the presenting Instance key is already bound to, under
323
+ * the owner's assertion (`bind-node`, CAY-0095). The key must have been bound to the
324
+ * caller's wallet by a standing authorisation it presented; this binds no key.
325
+ *
326
+ * Safe to repeat: binding a bound Node to the same wallet answers `bound` again and
327
+ * writes nothing. A refusal arrives as a thrown {@link CaysoApiError} whose `code` is
328
+ * a `NodeBindingRefusal` member, one of the gate's codes, or a seam code, and means
329
+ * nothing was bound.
330
+ */
331
+ bindNode(req: NodeBindingRequest): Promise<NodeBound>;
311
332
  getVersion(): Promise<CaysoVersion>;
312
333
  }
313
334
  /**
package/dist/client.js CHANGED
@@ -309,6 +309,24 @@ function buildClient(options, internals) {
309
309
  body: req,
310
310
  });
311
311
  },
312
+ async revokeStandingAuthorisation(req) {
313
+ return requestJson({
314
+ method: ROUTES.revokeStandingAuthorisation.method,
315
+ path: ROUTES.revokeStandingAuthorisation.path,
316
+ // Safe to repeat: a revoked authorisation revoked again answers the same.
317
+ idempotent: true,
318
+ body: req,
319
+ });
320
+ },
321
+ async bindNode(req) {
322
+ return requestJson({
323
+ method: ROUTES.bindNode.method,
324
+ path: ROUTES.bindNode.path,
325
+ // Safe to repeat: the same Node bound to the same wallet answers the same.
326
+ idempotent: true,
327
+ body: req,
328
+ });
329
+ },
312
330
  async getPurchase(purchaseId) {
313
331
  return requestJson({
314
332
  method: ROUTES.purchaseResult.method,
@@ -4,11 +4,16 @@
4
4
  * assertion** the Contribution Store signs once per period and Cayso fails closed
5
5
  * on (CMTY-0018, as Cayso amended it on 5 October 2026).
6
6
  *
7
- * **This is the contract surface only.** Nothing here is served, settled or
8
- * stored: no route answers either seam yet, no movement kind exists for either
9
- * leg, and nothing binds a Node or an Instance key to a wallet. Those are later
10
- * slices. What is published is what the Cloud generates its own types from
11
- * (CMTY-0018 §3), so that neither side redeclares the other's half.
7
+ * **Both seams are served.** `present-standing-authorisation` binds an Instance key
8
+ * to the presenting holder's wallet and answers a signed acknowledgement, and
9
+ * `revoke-standing-authorisation` withdraws one (CAY-0094). `bind-node` binds a
10
+ * Node to the wallet its Instance key is already bound to, and the Store asks
11
+ * whether a Node is bound at {@link NODE_BINDING_QUERY_ROUTE} (CAY-0095). The Store
12
+ * presents each period's settlement assertion at {@link CLOUD_SETTLEMENT_ROUTE},
13
+ * and Cayso settles it whole, as one `contribution_settlement` movement per paid
14
+ * contribution row and one `consumption_settlement` movement per consumption row,
15
+ * or refuses it whole (CAY-0096). What is published is what the Cloud generates its
16
+ * own types from (CMTY-0018 §3), so that neither side redeclares the other's half.
12
17
  *
13
18
  * ## Two seams, two authorities
14
19
  *
@@ -21,6 +26,10 @@
21
26
  * Cayso issuer key, which the Instance hands to the Cloud. An Instance mediates and
22
27
  * does not vouch (CAY-0057): the acknowledgement is how Cayso vouches for an
23
28
  * Instance key the Cloud has never seen, without the Cloud learning whose it is.
29
+ * The owner withdraws it from the Instance the same way, under their money-grade
30
+ * assertion (`revoke-standing-authorisation`,
31
+ * `ROUTES.revokeStandingAuthorisation`); the Cloud learns of it when the last
32
+ * acknowledgement it holds expires, at the end of the next period.
24
33
  *
25
34
  * **The settlement assertion** is the Contribution Store's statement of one
26
35
  * period: contribution by Node, consumption by authorisation. It is presented by
@@ -54,6 +63,24 @@
54
63
  * package, not the record, is the shape the Cloud generates from (CAY-0026,
55
64
  * Amendment 3).
56
65
  *
66
+ * ## A Node is bound to the wallet its Instance key is bound to (CMTY-0018 §4)
67
+ *
68
+ * A Contributor's Node earns for its owner. The owner's Instance presents the Node
69
+ * under the owner's money-grade assertion (`bind-node`, `ROUTES.bindNode`), signed by
70
+ * the Instance key, and Cayso binds the Node to the wallet that key is already bound
71
+ * to — which must be the verified holder's own. Binding a Node binds no Instance key:
72
+ * a key is bound only by the first standing authorisation it presents (CAY-0094). A
73
+ * Node is bound once and never re-pointed, and there is no unbinding.
74
+ *
75
+ * A Core Node's owner is the Operator, so it is never bound to a Citizen Wallet
76
+ * (`core_node_not_bound`). Nothing in a Node ID says it is a Core Node; a deployment
77
+ * is told its Core Node IDs by configuration.
78
+ *
79
+ * The Store learns only whether a Node is bound ({@link NodeBindingStatus}), at
80
+ * {@link NODE_BINDING_QUERY_ROUTE}, authenticated by the registered Store issuer
81
+ * exactly as the settlement assertion is, and never by a person. The answer cannot
82
+ * name a wallet.
83
+ *
57
84
  * ## Fail closed on the whole assertion
58
85
  *
59
86
  * A settlement assertion is settled whole or not at all (CAY-0021, CMTY-0018 §2).
@@ -68,6 +95,13 @@
68
95
  * period's id and the idempotency key, and a replay answers what the first
69
96
  * presentation answered.
70
97
  *
98
+ * A **withheld** contribution row is carried and settles nothing: it writes no
99
+ * movement and does not refuse the period. How a later period pays what was withheld
100
+ * is open (CAY-0096 §5). A row of nought cents writes no movement either, though every
101
+ * check on it still runs. A **Core Node**'s qualified row is refused with its own
102
+ * member, `contribution_node_is_a_core_node`: a Core Node's owner is the Operator, and
103
+ * where its contribution settles, if anywhere, is not designed (CAY-0096 §4).
104
+ *
71
105
  * ## What is not here
72
106
  *
73
107
  * - **No Instance ID**, in any claim, row, request, answer or refusal (CAY-0059 §6).
@@ -79,14 +113,22 @@
79
113
  * - **No figure.** The cap's ceiling, the authorisation's longest life, the
80
114
  * period's length and rule, and every rate are canon figures read from one
81
115
  * place. `period` is an opaque identifier; this package sets no value for it.
82
- * - **No unconfigured adapter in either union.** A seam this deployment has not
116
+ * - **No unconfigured adapter in any union.** A seam this deployment has not
83
117
  * been told about — no Store issuer registered, no Cayso acknowledgement key, no
84
- * ceiling configured — is a `503` in the refusal envelope's own code, spelled by
85
- * the slice that serves the route, never a member here.
86
- * - **No movement kind.** CMTY-0018 §2 proposes one per leg (CAY-0082's
87
- * pattern). The reasons a movement can be refused for — a short wallet, a
88
- * contended carve — arrive with the slice that designs those kinds, as a new
89
- * contract version.
118
+ * ceiling configured — is a `503` with a `CaysoApiErrorCode` of its own, never a
119
+ * member here: `acknowledgement_signing_not_configured`,
120
+ * `standing_authorisation_cap_ceiling_not_configured`,
121
+ * `standing_authorisation_life_ceiling_not_configured` and
122
+ * `standing_authorisation_store_not_configured` for the standing authorisation;
123
+ * `node_binding_store_not_configured` and `core_node_ids_not_configured` for
124
+ * binding a Node; `node_binding_store_not_configured`,
125
+ * `core_node_ids_not_configured` and `contribution_store_issuer_not_configured`
126
+ * for the Store's read; and, for the settlement assertion,
127
+ * `cloud_settlement_store_not_configured` — no Treasury, so nowhere to settle —
128
+ * `core_node_ids_not_configured` and `contribution_store_issuer_not_configured`.
129
+ * - **No movement kind in this package's unions.** The two kinds a settlement
130
+ * writes are `MovementKind` members like every other (CAY-0096); what crosses
131
+ * here is only whether the period settled, and why it did not.
90
132
  * - **No chain height.** A period is bounded in UTC seconds until there is a chain
91
133
  * to read (CAY-0016); the height a Store read is a later version's member.
92
134
  */
@@ -189,6 +231,38 @@ export interface AuthorisationAcknowledgementClaims {
189
231
  /** Expiry, in **seconds** since the Unix epoch. */
190
232
  readonly exp: number;
191
233
  }
234
+ /**
235
+ * The id of one Cayso acknowledgement key: the RFC 7638 SHA-256 thumbprint of its
236
+ * public half, unpadded base64url, and the `kid` in an acknowledgement's header.
237
+ * Derived rather than minted, so a key id cannot be re-pointed at a different key.
238
+ * Opaque to a consumer: compare it, never parse it.
239
+ */
240
+ export type AcknowledgementKeyId = string;
241
+ /**
242
+ * The public half of one key Cayso signs an {@link AuthorisationAcknowledgementClaims}
243
+ * with: an Ed25519 public JSON Web Key (RFC 8037), and nothing private. Its `kid` is
244
+ * the thumbprint of `crv`, `kty` and `x`, so a verifier can check that the key it
245
+ * selected is the key the header named.
246
+ */
247
+ export interface AcknowledgementPublicKey {
248
+ readonly kty: 'OKP';
249
+ readonly crv: 'Ed25519';
250
+ /** The public key, unpadded base64url. */
251
+ readonly x: string;
252
+ readonly kid: AcknowledgementKeyId;
253
+ readonly alg: 'EdDSA';
254
+ readonly use: 'sig';
255
+ }
256
+ /**
257
+ * Every key a Cayso acknowledgement may be verified under, as a JSON Web Key Set
258
+ * (CAY-0093). A key is listed before it signs and stays listed after it stops
259
+ * signing, so a verifier selects by `kid` and never by position. Answered at
260
+ * `ACKNOWLEDGEMENT_KEYS_ROUTE`; a deployment holding no key answers
261
+ * `503 acknowledgement_signing_not_configured`, never an empty set.
262
+ */
263
+ export interface AcknowledgementKeySet {
264
+ readonly keys: readonly AcknowledgementPublicKey[];
265
+ }
192
266
  /** Cayso holds the authorisation, and here is its acknowledgement for the Cloud. */
193
267
  export interface StandingAuthorisationAcknowledged {
194
268
  readonly outcome: 'acknowledged';
@@ -255,6 +329,208 @@ export type StandingAuthorisationRefusal =
255
329
  | 'cap_not_delegable'
256
330
  /** The Instance key is already bound to a different wallet, and a key spends from one. */
257
331
  | 'instance_key_bound_to_another_wallet';
332
+ /**
333
+ * The owner withdrawing a standing authorisation from their Instance, under the
334
+ * owner's assertion (`revoke-standing-authorisation`, CAY-0094). It names the
335
+ * authorisation by its `jti` and nothing else: whose wallet it is comes from the
336
+ * assertion, never from the request.
337
+ */
338
+ export interface StandingAuthorisationRevocationRequest {
339
+ /** The revoked authorisation's `jti`. */
340
+ readonly authorisation: string;
341
+ }
342
+ /**
343
+ * The authorisation is revoked: presented again, it is refused
344
+ * `authorisation_revoked`. Its Instance key stays bound to the wallet, so the key
345
+ * cannot be re-pointed at another. Revoking it again answers this again.
346
+ */
347
+ export interface StandingAuthorisationRevoked {
348
+ readonly outcome: 'revoked';
349
+ /** The `jti` the request named, echoed so a caller can match the answer to its act. */
350
+ readonly authorisation: string;
351
+ }
352
+ /** The revocation was refused, with one member of the closed union. Nothing was written. */
353
+ export interface StandingAuthorisationRevocationRefused {
354
+ readonly outcome: 'refused';
355
+ readonly error: StandingAuthorisationRevocationRefusal;
356
+ }
357
+ /** The ONE declared answer to `revoke-standing-authorisation`, discriminated by `outcome`. */
358
+ export type StandingAuthorisationRevocationResponse = StandingAuthorisationRevoked | StandingAuthorisationRevocationRefused;
359
+ /**
360
+ * Why a revocation was refused. Closed, like {@link StandingAuthorisationRefusal}:
361
+ * a new reason is a new member and a contract version.
362
+ */
363
+ export type StandingAuthorisationRevocationRefusal =
364
+ /**
365
+ * The verification method is outside the money-grade assurance profile
366
+ * (`ASSURANCE_POLICY['revoke-standing-authorisation']`). `dev-bypass` is never
367
+ * inside it.
368
+ */
369
+ 'assurance_not_money_grade'
370
+ /** No wallet is bound to the verified holder now, so there is nothing they hold to revoke. */
371
+ | 'wallet_has_no_live_binding'
372
+ /**
373
+ * No authorisation with this `jti` is held for the caller's wallet: it was never
374
+ * presented, or it was presented for another wallet. One member for both, so the
375
+ * route is no oracle for which `jti`s exist.
376
+ */
377
+ | 'authorisation_not_held_by_this_wallet';
378
+ /**
379
+ * The Instance key's statement naming one Node (CMTY-0018 §4, CAY-0095): signed by
380
+ * the Instance key in CAY-0072's format, whose header `kid` is `iss`. It says only
381
+ * "this key presents this Node". Which wallet the Node is bound to is not a claim:
382
+ * it is the wallet that Instance key is already bound to, on Cayso's side, which is
383
+ * the verified holder's own.
384
+ */
385
+ export interface NodePresentationClaims {
386
+ /** The Instance key that signed it. The same value as the header's `kid`. */
387
+ readonly iss: InstanceKeyId;
388
+ /** Exactly `'cayso'`, and checked, not merely present. */
389
+ readonly aud: 'cayso';
390
+ /** The Node being bound, as the Cloud's Node Registry holds it. */
391
+ readonly node_id: NodeId;
392
+ /** Issued-at, in **seconds** since the Unix epoch. */
393
+ readonly iat: number;
394
+ /** Expiry, in **seconds** since the Unix epoch. */
395
+ readonly exp: number;
396
+ }
397
+ /**
398
+ * The Instance binding a Node to its owner's wallet, under the owner's assertion
399
+ * (`bind-node`, CAY-0095). The Instance key must already be bound to the verified
400
+ * holder's wallet, by a standing authorisation it presented (CAY-0094): binding a
401
+ * Node binds no key.
402
+ */
403
+ export interface NodeBindingRequest {
404
+ /** The compact {@link NodePresentationClaims}, exactly as the Instance key signed it. */
405
+ readonly nodePresentation: string;
406
+ /**
407
+ * The Instance key's public half, as the JSON text of a public JSON Web Key. Its
408
+ * RFC 7638 thumbprint must be the presentation's `kid`, so the key that verifies
409
+ * the presentation is the key it names, and no other.
410
+ */
411
+ readonly instanceKey: string;
412
+ }
413
+ /**
414
+ * The Node is bound to the verified holder's wallet. Answered for the presentation
415
+ * that bound it and for every repeat of the same binding, in the same shape: a
416
+ * repeat writes nothing. It names the Node and no wallet.
417
+ */
418
+ export interface NodeBound {
419
+ readonly outcome: 'bound';
420
+ /** The Node the presentation named, echoed so a caller can match the answer to its act. */
421
+ readonly nodeId: NodeId;
422
+ }
423
+ /** The binding was refused, with one member of the closed union. Nothing was bound. */
424
+ export interface NodeBindingRefused {
425
+ readonly outcome: 'refused';
426
+ readonly error: NodeBindingRefusal;
427
+ }
428
+ /** The ONE declared answer to `bind-node`, discriminated by `outcome`. */
429
+ export type NodeBindingResponse = NodeBound | NodeBindingRefused;
430
+ /**
431
+ * Why a Node binding was refused. Closed, like {@link StandingAuthorisationRefusal}:
432
+ * a new reason is a new member and a contract version. Every member means the same
433
+ * thing: nothing was bound. There is no unbinding, so no member is about one.
434
+ */
435
+ export type NodeBindingRefusal =
436
+ /**
437
+ * The verification method is outside the money-grade assurance profile
438
+ * (`ASSURANCE_POLICY['bind-node']`). `dev-bypass` is never inside it.
439
+ */
440
+ 'assurance_not_money_grade'
441
+ /**
442
+ * Not a compact assertion in CAY-0072's format, or its claims are not exactly the
443
+ * closed set of {@link NodePresentationClaims} with `aud` `'cayso'`.
444
+ */
445
+ | 'node_presentation_malformed'
446
+ /** The Instance key presented is not a public JSON Web Key Cayso can verify with. */
447
+ | 'instance_key_malformed'
448
+ /**
449
+ * The presented key's thumbprint is not the presentation's `kid`, `iss` is not that
450
+ * `kid`, or the signature does not verify over the bytes presented. One member for
451
+ * all three: in each, the key presented did not sign this presentation.
452
+ */
453
+ | 'node_presentation_not_signed_by_its_instance_key'
454
+ /** `exp` is at or before now, or `iat` is further ahead of now than the clock-skew tolerance. */
455
+ | 'node_presentation_not_current'
456
+ /**
457
+ * The Instance key is not bound to the verified holder's wallet: it was never
458
+ * bound, it is bound to another wallet, or the holder has no wallet now. One member
459
+ * for all three, so the route is no oracle for whose a key is.
460
+ */
461
+ | 'instance_key_not_bound_to_holder'
462
+ /**
463
+ * The Node is a Core Node, which this deployment is told of by configuration. A
464
+ * Core Node's owner is the Operator (CMTY-0018 §4), so it is bound to no Citizen
465
+ * Wallet.
466
+ */
467
+ | 'core_node_not_bound'
468
+ /** The Node is already bound to a different wallet. A binding is never re-pointed. */
469
+ | 'node_bound_to_another_wallet';
470
+ /**
471
+ * The Contribution Store's question about one Node (CAY-0095): signed by a key of
472
+ * the registered Store issuer, in CAY-0072's format, whose header `kid` names that
473
+ * key. It asks whether the Node is bound, and nothing about to whom.
474
+ */
475
+ export interface NodeBindingQueryClaims {
476
+ readonly iss: ContributionStoreIssuer;
477
+ /** Exactly `'cayso'`. */
478
+ readonly aud: 'cayso';
479
+ /** The Node asked about. */
480
+ readonly node_id: NodeId;
481
+ /** Issued-at, in **seconds** since the Unix epoch. */
482
+ readonly iat: number;
483
+ /** Expiry, in **seconds** since the Unix epoch. */
484
+ readonly exp: number;
485
+ }
486
+ /**
487
+ * The Contribution Store asking whether a Node is bound, under its own registered
488
+ * issuer and no person's assertion, at `NODE_BINDING_QUERY_ROUTE`.
489
+ */
490
+ export interface NodeBindingQueryRequest {
491
+ /** The compact {@link NodeBindingQueryClaims}, exactly as the Store signed it. */
492
+ readonly nodeBindingQuery: string;
493
+ }
494
+ /**
495
+ * Cayso's answer: whether the Node is bound to a wallet, and **nothing else**. No
496
+ * wallet, no holder, no Citizen, no date, no Instance ID (CAY-0006, CAY-0059 §6). A
497
+ * Store that learns a Node is bound pays its rows; which wallet is paid is Cayso's
498
+ * to resolve at settlement, on its own side. A Node this deployment is told is a Core
499
+ * Node is answered `bound: false`, even if a Citizen bound it before it was named
500
+ * Core. Exactly two members, and the contract tests pin that no third can be added
501
+ * unnoticed.
502
+ */
503
+ export interface NodeBindingStatus {
504
+ readonly nodeId: NodeId;
505
+ readonly bound: boolean;
506
+ }
507
+ /** The query was refused, with one member of the closed union. Nothing was read. */
508
+ export interface NodeBindingQueryRefused {
509
+ readonly outcome: 'refused';
510
+ readonly error: NodeBindingQueryRefusal;
511
+ }
512
+ /**
513
+ * Why the Store's query was refused. Closed. A query signed by anybody but the
514
+ * registered Store issuer, a person issuer included, is `node_binding_query_issuer_not_recognised`.
515
+ */
516
+ export type NodeBindingQueryRefusal =
517
+ /**
518
+ * Not a compact assertion in CAY-0072's format, or its claims are not exactly the
519
+ * closed set of {@link NodeBindingQueryClaims} with `aud` `'cayso'`.
520
+ */
521
+ 'node_binding_query_malformed'
522
+ /**
523
+ * `iss` is not the Store issuer this deployment's registry holds, the issuer or the
524
+ * `kid` is revoked, or the `kid` is not one of its keys. A person issuer is never
525
+ * the Store issuer.
526
+ */
527
+ | 'node_binding_query_issuer_not_recognised'
528
+ /** `iss` names the Contribution Store of another environment. */
529
+ | 'node_binding_query_issuer_for_another_environment'
530
+ /** The signature does not verify over the bytes presented. */
531
+ | 'node_binding_query_signature_invalid'
532
+ /** `exp` is at or before now, or `iat` is further ahead of now than the clock-skew tolerance. */
533
+ | 'node_binding_query_not_current';
258
534
  /** The kind of contribution a row measures: the three of the Community map's #3. Closed. */
259
535
  export type ContributionType = 'storage' | 'compute' | 'full-node';
260
536
  /**
@@ -429,30 +705,70 @@ export type SettlementAssertionLevelRefusal =
429
705
  | 'settlement_issued_in_the_future'
430
706
  /** `jti` is not the period's id. */
431
707
  | 'settlement_jti_not_the_period'
708
+ /**
709
+ * The period is not one this deployment's period rules made: its id is not the id a
710
+ * recognised rule gives the period containing `starts_at`, or its instants are not
711
+ * that period's. A deployment that recognises no rule refuses every period.
712
+ */
713
+ | 'settlement_period_not_recognised'
714
+ /** The period's `ends_at` is still ahead of now, beyond the clock-skew tolerance. */
715
+ | 'settlement_period_not_ended'
432
716
  /**
433
717
  * A different assertion already settled this period. The two cannot both be the
434
718
  * period, so the second is refused, and the first stands.
435
719
  */
436
720
  | 'settlement_period_already_settled_differently'
437
721
  /** A leg's rows do not add to its total. Refused before any row is read. */
438
- | 'settlement_totals_do_not_add';
722
+ | 'settlement_totals_do_not_add'
723
+ /**
724
+ * Another movement kept changing a wallet the settlement draws from, and the carve
725
+ * gave up. Nothing was written; presenting the same assertion again is safe.
726
+ */
727
+ | 'settlement_carve_contended';
439
728
  /** Why the first failing contribution row failed. */
440
729
  export type ContributionRowRefusal =
441
730
  /**
442
- * A qualified row names a Node bound to no wallet. An unbound Node's row is
443
- * carried withheld (`node_not_bound`) and never settled to anybody.
731
+ * A qualified row names a Node bound to no wallet, or to a wallet that no longer
732
+ * has a holder. An unbound Node's row is carried withheld (`node_not_bound`) and
733
+ * never settled to anybody.
734
+ */
735
+ 'contribution_node_not_bound'
736
+ /**
737
+ * A qualified row names a Node this deployment is told is a Core Node. A Core
738
+ * Node's owner is the Operator (CMTY-0018 §4), so it settles to no Citizen Wallet.
444
739
  */
445
- 'contribution_node_not_bound';
740
+ | 'contribution_node_is_a_core_node'
741
+ /** The Treasury holds too few unrestricted cents to pay this row. */
742
+ | 'contribution_treasury_short';
446
743
  /** Why the first failing consumption row failed. */
447
744
  export type ConsumptionRowRefusal =
448
745
  /** The row cites an authorisation Cayso never acknowledged. */
449
746
  'consumption_authorisation_unknown'
450
- /** The row cites an authorisation its owner revoked before the period. */
747
+ /**
748
+ * The row cites an authorisation its owner revoked before the period: no
749
+ * acknowledgement Cayso signed before the revocation covers any of it.
750
+ */
451
751
  | 'consumption_authorisation_revoked'
452
- /** The row's cap is not the cap the acknowledgement stated. */
752
+ /**
753
+ * The row cites an authorisation that was not in force in the period: issued after
754
+ * the period ended, or expired at or before it began.
755
+ */
756
+ | 'consumption_authorisation_not_in_force'
757
+ /**
758
+ * The row's cap is not the cap the acknowledgement stated, or the authorisation's
759
+ * period rule is not the rule that made this period.
760
+ */
453
761
  | 'consumption_cap_not_the_acknowledged_cap'
454
- /** The authorisation's running total in this period passes its cap at this row. */
455
- | 'consumption_over_cap';
762
+ /**
763
+ * The authorisation's running total in this period passes its cap at this row. The
764
+ * Store clamps at the cap, so this is a Store defect, and it is never softened.
765
+ */
766
+ | 'consumption_over_cap'
767
+ /**
768
+ * The paying wallet holds too few unrestricted cents for this row. Restricted cents
769
+ * never pay a consumption settlement (CAY-0096 §2).
770
+ */
771
+ | 'consumption_wallet_short';
456
772
  /**
457
773
  * Every reason a settlement assertion can be refused: the assertion's own, and each
458
774
  * leg's. Closed, so a consumer's map over it is total.