@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 +72 -28
- package/dist/assertion.d.ts +55 -2
- package/dist/assurance.js +37 -0
- package/dist/authority.d.ts +221 -0
- package/dist/authority.js +49 -0
- package/dist/client.d.ts +68 -6
- package/dist/client.js +35 -4
- package/dist/cloud-settlement.d.ts +339 -20
- package/dist/contract-schema.generated.js +1092 -38
- package/dist/errors.d.ts +67 -3
- package/dist/index.d.ts +129 -5
- package/dist/index.js +127 -4
- package/dist/operator.d.ts +142 -6
- package/dist/operator.js +8 -1
- package/dist/routes.d.ts +68 -3
- package/dist/routes.js +65 -3
- package/dist/schema-types.d.ts +15 -5
- package/dist/schema.d.ts +20 -8
- package/package.json +1 -1
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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.
|
|
36
|
-
|
|
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
|
|
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
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
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,
|
|
67
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
route:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
package/dist/assertion.d.ts
CHANGED
|
@@ -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.
|
|
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
|
+
}
|