@percayso-community/cayso-contracts 0.29.0 → 1.0.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,22 +21,37 @@ 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`.
25
-
26
- **The operator read surface, on its own entry point** (CAY-0026, Amendment 4). Three
27
- reads an operator app's *server* calls — the movement list, the purchase list and the
28
- quickening ladder — are published at `@percayso-community/cayso-contracts/operator`:
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.
28
+
29
+ **Percayso BizID's authority assertion** (CAY-0100) is published ahead of the
30
+ binding that accepts it. `AuthorityClaims` is its own type, under the identity
31
+ assertion's `AssertionHeader`, with no verification or assurance fields. It travels
32
+ in the `AUTHORITY_ASSERTION_HEADER` header, beside `Authorization`, and the thin
33
+ client sends it only on an Organisation operation given a per-request provider for
34
+ it. The binding's refusals are the closed `AuthorityBindingRefusal`.
35
+ `AssertionClaims` declares `auth_time`, which Percayso ID mints and the binding
36
+ compares. `organisationAuthority` and `mandateFor` are retired and still published,
37
+ until the breaking step that removes them.
38
+
39
+ **The operator read surface, on its own entry point** (CAY-0026, Amendments 4 and 5).
40
+ Four reads an operator app's *server* calls — the movement list, the purchase list, the
41
+ quickening ladder and the economy summary — are published at `@percayso-community/cayso-contracts/operator`:
29
42
  their paths and scopes, query inputs (wallet id, a half-open `[from, to)` window, a
30
43
  keyset cursor), rows and pages, and their closed refusal and not-configured bodies.
31
44
  Money is `Cents`, a wallet is named by its wallet id, and no Percayso identity
32
45
  appears on any row. It is not re-exported from the barrel, so Piper and the Cloud
33
46
  never import an operator vocabulary by accident, and the schema describes it under
34
47
  its own `operator` key rather than among the person-facing types. Cayso sends no
35
- CORS headers: these reads are for a server, never a browser. Widening the entry
36
- point beyond these three needs a further amendment.
48
+ CORS headers: these reads are for a server, never a browser. The economy summary
49
+ (Amendment 5) answers aggregates only: whole-number counts and cent totals, with no
50
+ wallet id, identity or row of any kind. Widening the entry point beyond these four
51
+ needs a further amendment.
37
52
 
38
53
  **Deliberately not published:** peer types, holdings and carves, and every operator
39
- route but those three reads. They are not a consumer's business, and putting them on
54
+ route but those four reads. They are not a consumer's business, and putting them on
40
55
  the public surface widens it for nothing.
41
56
 
42
57
  **This package carries no numbers.** Amounts, rates, tiers, schedules and
@@ -49,22 +64,42 @@ restated here.
49
64
  |---|---|
50
65
  | `@percayso-community/cayso-contracts` | Types and constants. No runtime. |
51
66
  | `@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. |
53
- | `@percayso-community/cayso-contracts/operator` | The three operator reads' types and route constants, for an operator app's server. |
67
+ | `@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. |
68
+ | `@percayso-community/cayso-contracts/operator` | The four operator reads' types and route constants, for an operator app's server. |
54
69
 
55
70
  The split is the point: a consumer wanting only the types takes on no runtime at
56
71
  all. The client owns the fetch, the assertion header and the idempotent retry.
57
72
 
58
73
  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.
74
+ generated from these very types, never hand-written. It is a published, versioned
75
+ surface in its own right, and there are two ways to depend on this package through
76
+ it, by language:
77
+
78
+ 1. **A TypeScript consumer imports the types**, and the compiler holds the surface.
79
+ Piper does this. For such a consumer the schema is a tool *about* the contract —
80
+ a diff, a doc generator, a compatibility check — and never a replacement for the
81
+ import.
82
+ 2. **A consumer in another language generates from the `schema` entry point of the
83
+ published tarball at a pinned version**, and treats a drift between its generated
84
+ types and the schema as a build failure. That *is* depending on the package: the
85
+ pin is the lockfile entry and the regeneration is the compile. The Percayso Cloud
86
+ is this consumer (`CAY-0026` Amendment 3; `CMTY-0018` §3 as amended): it is written
87
+ in Go, it cannot import a TypeScript type, and its generator reads the npm tarball
88
+ at a pinned version so that its Go types for the two Cloud seams in
89
+ `cloud-settlement` are never typed by hand. The package ships the schema as a JS
90
+ module only, which is why the generator reads the tarball and not a registry
91
+ endpoint.
92
+
93
+ What stays refused is reading the schema off the wire as a substitute for either:
94
+ see the version endpoint, below.
62
95
 
63
96
  ## Independent versioning
64
97
 
65
98
  `CONTRACT_VERSION` is exported from the barrel and is **independent** — its own
66
- semantic version, starting `0.1.0`, not a repo-wide constant shared with anything
67
- else. Piper's own contracts share one global version bumped across all of them
99
+ semantic version, not a repo-wide constant shared with anything else. It started at
100
+ `0.1.0`, and from `1.0.0` it follows ordinary semantic versioning, Cayso's adoption of CMTY-0032
101
+ ([CAY-0101](../../docs/architectural-decision-record/CAY-0101-the-contracts-follow-ordinary-semantic-versioning-from-1-0-0.md)):
102
+ a minor is additive and drops nothing, and a major is the only breaking kind. Piper's own contracts share one global version bumped across all of them
68
103
  together; independent versioning across a service boundary is exactly what this
69
104
  money surface gains that theirs lack (CAY-0026). The constant and the package's
70
105
  `version` field must agree, and a test fails when they drift.
@@ -82,24 +117,33 @@ describes. The thin client's `getVersion()` reads it and warns once, per client
82
117
  instance, when the server's contract *major* differs from the one the client was
83
118
  built against.
84
119
 
85
- After a minor bump that is additive on the wire, a deployment keeps the previous
86
- minor in `supportedContractVersions` for one release, so a consumer still pinned
87
- to it keeps working while it moves. A breaking bump drops it, and says so in the
88
- bump's changelog.
120
+ A deployment keeps serving every earlier minor of its major and lists them in
121
+ `supportedContractVersions`, so a consumer pinned to any of them keeps working; a
122
+ consumer's handshake accepts a Cayso if `contractVersion` or any entry of
123
+ `supportedContractVersions` is on its own line: from `1.0.0` the same major, below it the
124
+ same minor. Only a
125
+ major breaks, and its first release lists nothing from an older major and says so in
126
+ the bump's changelog. An earlier major is listed only during a declared transition:
127
+ `1.0.0` keeps `0.32.0`, `0.31.0`, `0.30.0` and `0.29.0`, and the next major drops them
128
+ ([CAY-0101](../../docs/architectural-decision-record/CAY-0101-the-contracts-follow-ordinary-semantic-versioning-from-1-0-0.md),
129
+ amending CAY-0086).
89
130
 
90
131
  Alongside it, `GET /api/version/schema` serves the same machine-readable
91
132
  descriptor as `@percayso-community/cayso-contracts/schema` — the surface outside consumers read, with
92
133
  no operator, webhook or peer route. It exists for tooling and diagnostics.
93
134
 
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.
135
+ **The wire is not a substitute for depending on this package.** The descriptor
136
+ served here is data about *this deployment*; the guarantee is the published
137
+ package at a pinned version. A consumer that reads the schema off the wire and
138
+ validates or generates against it has re-created Piper's original mistake by
139
+ another route: it couples its build to a deployment's uptime, replaces a pinned
140
+ version with whatever happens to be deployed at that moment, and loses every
141
+ guarantee the moment the wire description and its code drift. Import
142
+ `@percayso-community/cayso-contracts` and let the compiler hold the surface, or —
143
+ in another language — generate from the `schema` entry point of the published
144
+ tarball at a pinned version, as the Cloud does. Read the schema *endpoint* when
145
+ you are building a tool *about* a running deployment, never to stand in for
146
+ having the package.
103
147
 
104
148
  ## No dependencies
105
149
 
@@ -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
@@ -746,6 +756,15 @@ export interface AssertionClaims {
746
756
  * THIS operation — the organisation-authority claim (CAY-0059 §3). OPTIONAL on
747
757
  * the wire, and its absence provisions nothing.
748
758
  *
759
+ * ## RETIRED by CAY-0100, and kept until #419
760
+ *
761
+ * BizID holds the Organisation, so the evidence for an act on an Organisation
762
+ * Wallet becomes BizID's `AuthorityClaims.organisationId`, and Percayso ID stops
763
+ * minting this claim. It stays declared, and read, until the previous `cayso-api`
764
+ * pin already runs the binding, because a revert must not bring back a reader of a
765
+ * claim nobody mints (CAY-0100 §7, Consequences). Its removal is the breaking
766
+ * contract step, #419. Nothing new may read it.
767
+ *
749
768
  * ## It is a second fact beside `sub`, and it replaces nothing
750
769
  *
751
770
  * `sub` stays what it has always been: the citizen Percayso ID authenticated
@@ -897,6 +916,14 @@ export interface AssertionClaims {
897
916
  * displays — never the Organisation ID of the holder behind it, and never an
898
917
  * Instance ID. Present when the holder may act for one or more organisations.
899
918
  *
919
+ * ## RETIRED by CAY-0100 §4a, and kept until #419
920
+ *
921
+ * The commercial payment's mandate route, the delegated purchase and an allowance
922
+ * grant on an Organisation Wallet authorise from BizID's `AuthorityClaims`, not from
923
+ * this claim. Percayso ID stops sending it only once no reader of it is left at the
924
+ * previous `cayso-api` pin (CAY-0100, Consequences), and its removal is the breaking
925
+ * contract step, #419. Nothing new may read it.
926
+ *
900
927
  * ## THIS IS NOT THE ORGANISATION-AUTHORITY CLAIM, AND THE TWO MUST NOT BE
901
928
  * OVERLOADED
902
929
  *
@@ -934,4 +961,30 @@ export interface AssertionClaims {
934
961
  * session, in seconds. Nothing in Phase A may branch on it (CAY-0008).
935
962
  */
936
963
  readonly sessionAgeSeconds?: number;
964
+ /**
965
+ * When the Citizen tapped their passkey for this assertion, in **seconds** since the
966
+ * Unix epoch: the issuer's convention, not `Millis`, as `exp` and `iat` are.
967
+ * OPTIONAL on the wire, declared ahead of its first reader (CAY-0100).
968
+ *
969
+ * Percayso ID mints it. BizID's authority assertion carries the tap it saw as
970
+ * `AuthorityClaims.auth_time`, and the binding (#417) refuses the two unless they
971
+ * are equal, so one tap serves both (CAY-0100 §4). Spelled `auth_time`, as OpenID
972
+ * Connect and that claim spell it, rather than in this surface's camel case, so the
973
+ * two sides of the comparison carry one name.
974
+ *
975
+ * ## Declared before Percayso ID mints it, and why that order is the point
976
+ *
977
+ * A verifier refuses any claim key it does not know. If Percayso ID minted
978
+ * `auth_time` before the `cayso-api` pin declared it, a Cloud revert to the previous
979
+ * pin would refuse every identity assertion. So Cayso declares it first, and
980
+ * Percayso ID mints it only once the previous pin already does (CAY-0100,
981
+ * Consequences). A verifier admits a non-negative integer here and refuses any other
982
+ * value as malformed.
983
+ *
984
+ * ## Nothing may branch on it yet
985
+ *
986
+ * Like {@link sessionAgeSeconds}, it is declared and depended on by nothing until the
987
+ * binding is built. Its absence refuses nothing, and its presence admits nothing.
988
+ */
989
+ readonly auth_time?: number;
937
990
  }
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
  /**
@@ -0,0 +1,221 @@
1
+ /**
2
+ * `authority` — the shape of Percayso BizID's authority assertion, and the closed
3
+ * reasons the binding refuses one for (CAY-0100 §2, §4).
4
+ *
5
+ * BizID holds the Organisation and Percayso ID holds people (CMTY-0031, BIZ-0010).
6
+ * So the evidence that a Citizen may act for an Organisation comes from BizID, a
7
+ * second issuer, in a credential of its own: {@link AuthorityClaims}. It says that
8
+ * this Citizen may do this operation for this Organisation, under its formed
9
+ * mandate. It is not a second identity assertion and shares no type with
10
+ * `AssertionClaims`.
11
+ *
12
+ * This file is the SHAPE ALONE, published so that Piper can build against it
13
+ * before Cayso accepts it. Nothing here reads, verifies or binds a credential.
14
+ * BizID's registration as an authority issuer, its verifier and the binding to
15
+ * Percayso ID's assertion are later slices (#415–#417), and until they are served
16
+ * an Organisation operation answers exactly as it did.
17
+ *
18
+ * ## The envelope is the identity assertion's
19
+ *
20
+ * Compact JWS, `EdDSA` over Ed25519 (CAY-0072), with a header of exactly
21
+ * `{alg, kid}`: the same {@link AssertionHeader}, not a second header type, so the
22
+ * one rule about what a header may say is written once. `iss` is BizID, `aud` is
23
+ * `'cayso'` and `jti` is fresh. `iat` and `exp` are in seconds, and `exp` sits
24
+ * within `MAX_ASSERTION_LIFETIME_SECONDS` (`assertion.ts`), the ceiling an identity
25
+ * assertion is held to. That ceiling is not restated here. Replay protection is on
26
+ * `(iss, jti)`, exactly as for identity assertions (CAY-0030).
27
+ *
28
+ * ## No verification or assurance fields
29
+ *
30
+ * BizID verifies no people. Money-grade assurance comes from Percayso ID's
31
+ * assertion, under the per-operation acceptance policy (CAY-0029), and nothing
32
+ * here can stand in for it. An `AuthorityClaims` alone authenticates no route: it
33
+ * is accepted only beside a Percayso ID assertion it binds to (CAY-0100 §3, §4).
34
+ *
35
+ * ## It travels in its own header, beside the identity assertion
36
+ *
37
+ * See {@link AUTHORITY_ASSERTION_HEADER}. The thin client sends it on the
38
+ * Organisation operations alone, fetched for each request and never cached, under
39
+ * the discipline the identity assertion is fetched under.
40
+ */
41
+ import type { AssertionHeader, ContractOperation } from './assertion.js';
42
+ /**
43
+ * The request header an authority assertion travels in, beside the identity
44
+ * assertion's `Authorization: Bearer` (CAY-0100 §4).
45
+ *
46
+ * ## Why this name
47
+ *
48
+ * - **Not `Authorization`.** That header carries Percayso ID's assertion and only
49
+ * it. A request has one `Authorization`, and the two credentials are verified by
50
+ * two verifiers against two registrations, so neither may be mistaken for the
51
+ * other by where it arrived.
52
+ * - **No `X-` prefix.** RFC 6648 retired it, and the one header this contract
53
+ * already names beside `Authorization` is `Idempotency-Key`, which has none. This
54
+ * follows that spelling: two words, each capitalised, hyphenated.
55
+ * - **The glossary's noun.** CAY-0100 calls the credential BizID's *authority
56
+ * assertion*, so the header says that and nothing more. It does not name BizID,
57
+ * for the reason `Authorization` does not name Percayso ID: who minted it is the
58
+ * credential's `iss`, checked against a registration, never a header's say-so.
59
+ *
60
+ * The value is the compact JWS alone, with no `Bearer` scheme: the scheme belongs to
61
+ * `Authorization`'s grammar, and this header has no other value to tell it from.
62
+ *
63
+ * Published as a value so a consumer that does not use the thin client spells it
64
+ * from the contract rather than from a retyped copy, and so the service that reads
65
+ * it reads the same string.
66
+ */
67
+ export declare const AUTHORITY_ASSERTION_HEADER: "Authority-Assertion";
68
+ /**
69
+ * A minted authority assertion as a whole: the identity assertion's header, and the
70
+ * claims BizID asserts (CAY-0100 §2).
71
+ *
72
+ * The header is {@link AssertionHeader} itself, exactly `{alg, kid}`, read first to
73
+ * choose a check and believed never. The split is the one `AssertionEnvelope` makes,
74
+ * for the same reason.
75
+ */
76
+ export interface AuthorityEnvelope {
77
+ /** How to check this credential. Read first, believed never. */
78
+ readonly header: AssertionHeader;
79
+ /** What BizID asserts, established only once the header's check has passed. */
80
+ readonly claims: AuthorityClaims;
81
+ }
82
+ /**
83
+ * The claims carried by BizID's authority assertion (CAY-0100 §2): this Citizen may
84
+ * perform this operation for this Organisation, under this version of its formed
85
+ * mandate.
86
+ *
87
+ * CLOSED. These ten keys and no others: a verifier refuses a key it does not know, as
88
+ * it does for an identity assertion. There are no verification or assurance fields,
89
+ * because BizID does not verify people.
90
+ *
91
+ * A claim to be CHECKED, never a claim to be trusted. Cayso verifies a minted
92
+ * assertion and never takes a caller's word for who they are, nor for whom they act
93
+ * (CLAUDE.md, Trust and authentication).
94
+ */
95
+ export interface AuthorityClaims {
96
+ /**
97
+ * Who minted this: Percayso BizID, registered as an authority issuer for this type
98
+ * alone (CAY-0100 §3). Typed `string` rather than a literal for the reason
99
+ * `AssertionClaims.iss` is: an unrecognised issuer must be representable in order
100
+ * to be refused.
101
+ */
102
+ readonly iss: string;
103
+ /** The intended recipient. Exactly `'cayso'`, and checked, not merely present. */
104
+ readonly aud: 'cayso';
105
+ /**
106
+ * This assertion's unique identity, fresh for each one. Replay is refused on
107
+ * `(iss, jti)`, exactly as for identity assertions (CAY-0030). It never names the
108
+ * identity assertion's `jti`, and the identity assertion never names it (CAY-0100
109
+ * §4): each is consumed as its own single use.
110
+ */
111
+ readonly jti: string;
112
+ /**
113
+ * Issued-at, in **seconds** since the Unix epoch, the issuer's convention and not
114
+ * `Millis`, as on `AssertionClaims`.
115
+ */
116
+ readonly iat: number;
117
+ /**
118
+ * Expiry, in **seconds** since the Unix epoch. No further ahead than
119
+ * `MAX_ASSERTION_LIFETIME_SECONDS`, the ceiling an identity assertion is held to
120
+ * (CAY-0100 §2). At or before now is expired, with no skew grace added.
121
+ */
122
+ readonly exp: number;
123
+ /**
124
+ * The Citizen this authority is for: the subject Percayso ID's assertion already
125
+ * named. **NEVER taken as an identity** (CAY-0100 §3). BizID attests no person, so
126
+ * this is read only as the value the binding compares with the identity assertion's
127
+ * `sub`, and a mismatch is `subject_mismatch`. Like that `sub`, it never reaches
128
+ * the chain (CAY-0006).
129
+ */
130
+ readonly sub: string;
131
+ /**
132
+ * The ONE operation this authority was minted for. SINGULAR, never a list: a list
133
+ * would be a menu, and a credential carrying a menu lets whatever holds it choose.
134
+ * It must be in Percayso ID's `scope` and be the operation of the route it is
135
+ * presented on, so an authority for one act cannot be spent on another (CAY-0100
136
+ * §4).
137
+ */
138
+ readonly operation: ContractOperation;
139
+ /**
140
+ * The Organisation ID Percayso BizID mints: the permanent external identity of the
141
+ * Organisation Wallet's holder (CAY-0100 §1).
142
+ *
143
+ * It reaches Cayso from THIS claim and from nowhere else: not a path, a body or a
144
+ * query, and not Percayso ID's assertion, which does not echo it because Percayso
145
+ * ID holds people, not Organisations (CAY-0100 §4). Nothing compares it against a
146
+ * Percayso ID claim.
147
+ *
148
+ * A plain `string`, for the reason `AssertionClaims.organisationAuthority` is one:
149
+ * this is the wire, JSON carries no brands, and a published type a consumer cannot
150
+ * build from a string it holds is a distinction that consumer cannot observe. The
151
+ * branded `OrganisationId`, which refuses an Instance ID at compile time, lives on
152
+ * the write path in `apps/api`, where the holder key is formed (CAY-0059 §5).
153
+ */
154
+ readonly organisationId: string;
155
+ /**
156
+ * The version of the Organisation's formed mandate BizID decided this authority
157
+ * under (CAY-0100 §5). Cayso records it with the movement, or in the act's audit
158
+ * row where the act writes no movement, and refuses an `AuthorityClaims` without
159
+ * one. It checks only that it is present and well formed: what the mandate allows is
160
+ * BizID's to decide, and Cayso does not interpret it.
161
+ *
162
+ * A JSON **integer, at least 1**: the mandate's version counter, which starts at 1 and
163
+ * rises by one each time the mandate is re-formed. The JWS payload carries a bare
164
+ * number (`"mandateVersion":1`), as `bizid-contracts` declares it and BIZ-0010 records
165
+ * it. {@link isMandateVersion} is the one check of that shape.
166
+ */
167
+ readonly mandateVersion: number;
168
+ /**
169
+ * When the Citizen tapped their Percayso ID passkey, in **seconds** since the Unix
170
+ * epoch: the tap BizID saw (CAY-0100 §2). Percayso ID's assertion carries the same
171
+ * `auth_time`, so one tap serves both, and the binding refuses two that differ as
172
+ * `auth_time_mismatch` (§4). Spelled `auth_time`, as OpenID Connect spells it and as
173
+ * BIZ-0010 does, rather than in this surface's camel case.
174
+ */
175
+ readonly auth_time: number;
176
+ }
177
+ /**
178
+ * Why the binding refused an Organisation operation: CAY-0100 §4's working shape,
179
+ * closed and spelled here.
180
+ *
181
+ * Published before anything produces it, as `founder_wallet_lapsed` was, so a
182
+ * consumer exhausting `CaysoApiErrorCode` renders it from the version that declared
183
+ * it. A new reason is a new member and a contract version, never a free-text field.
184
+ * Every member means the same thing: the operation did not happen.
185
+ *
186
+ * It is the BINDING's refusal, not the operation's. Each operation keeps its own
187
+ * union for what it refuses once the binding holds, and the identity assertion
188
+ * keeps the gate's codes.
189
+ */
190
+ export type AuthorityBindingRefusal =
191
+ /**
192
+ * An Organisation operation was presented with no authority assertion. Refused,
193
+ * and never read as "act as the Citizen alone" (CAY-0100 §4).
194
+ */
195
+ 'authority_assertion_missing'
196
+ /**
197
+ * The authority assertion did not verify: malformed, an issuer or key not
198
+ * registered for this type, a bad signature, expired, or already used. One member
199
+ * for all of them, so the refusal describes no part of the check to whoever is
200
+ * probing it.
201
+ */
202
+ | 'authority_assertion_invalid'
203
+ /** The two assertions name different subjects. */
204
+ | 'subject_mismatch'
205
+ /** The authority's `operation` is not in Percayso ID's `scope`. */
206
+ | 'operation_not_in_scope'
207
+ /**
208
+ * The authority's `operation` is not the operation of the route it was presented
209
+ * on, so an authority minted for one act cannot be spent on another.
210
+ */
211
+ | 'operation_not_the_routes'
212
+ /** The two assertions carry different `auth_time`s, so no one passkey tap serves both. */
213
+ | 'auth_time_mismatch'
214
+ /** The two assertions are not both unexpired at the same moment. */
215
+ | 'not_jointly_current';
216
+ /**
217
+ * Whether `value` is a well-formed {@link AuthorityClaims.mandateVersion}: a JSON integer
218
+ * of at least 1. The one statement of the shape, so the verifier (#416) and any consumer
219
+ * check it the same way. It says nothing about what the mandate allows.
220
+ */
221
+ export declare function isMandateVersion(value: unknown): value is number;
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Copyright 2026 Percayso Community
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * The request header an authority assertion travels in, beside the identity
18
+ * assertion's `Authorization: Bearer` (CAY-0100 §4).
19
+ *
20
+ * ## Why this name
21
+ *
22
+ * - **Not `Authorization`.** That header carries Percayso ID's assertion and only
23
+ * it. A request has one `Authorization`, and the two credentials are verified by
24
+ * two verifiers against two registrations, so neither may be mistaken for the
25
+ * other by where it arrived.
26
+ * - **No `X-` prefix.** RFC 6648 retired it, and the one header this contract
27
+ * already names beside `Authorization` is `Idempotency-Key`, which has none. This
28
+ * follows that spelling: two words, each capitalised, hyphenated.
29
+ * - **The glossary's noun.** CAY-0100 calls the credential BizID's *authority
30
+ * assertion*, so the header says that and nothing more. It does not name BizID,
31
+ * for the reason `Authorization` does not name Percayso ID: who minted it is the
32
+ * credential's `iss`, checked against a registration, never a header's say-so.
33
+ *
34
+ * The value is the compact JWS alone, with no `Bearer` scheme: the scheme belongs to
35
+ * `Authorization`'s grammar, and this header has no other value to tell it from.
36
+ *
37
+ * Published as a value so a consumer that does not use the thin client spells it
38
+ * from the contract rather than from a retyped copy, and so the service that reads
39
+ * it reads the same string.
40
+ */
41
+ export const AUTHORITY_ASSERTION_HEADER = 'Authority-Assertion';
42
+ /**
43
+ * Whether `value` is a well-formed {@link AuthorityClaims.mandateVersion}: a JSON integer
44
+ * of at least 1. The one statement of the shape, so the verifier (#416) and any consumer
45
+ * check it the same way. It says nothing about what the mandate allows.
46
+ */
47
+ export function isMandateVersion(value) {
48
+ return typeof value === 'number' && Number.isInteger(value) && value >= 1;
49
+ }