@algovoi/substrate 0.3.0 → 0.4.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
@@ -66,8 +66,11 @@ verifyAuditChain([row0, row1]);
66
66
 
67
67
  ## Substrate discipline
68
68
 
69
- This package enforces the AlgoVoi-discipline rules formalised in PR #2436
70
- (`x402-foundation/x402`, three-voice coalition co-signed):
69
+ This package enforces the AlgoVoi-authored substrate-discipline rules
70
+ formalised in [IETF Internet-Draft `draft-hopley-x402-canonicalisation-jcs-v1`](https://datatracker.ietf.org/doc/draft-hopley-x402-canonicalisation-jcs-v1/)
71
+ (Independent Submission, Informational; sole AlgoVoi authorship) and
72
+ proposed for inclusion in `x402-foundation/x402` via [PR #2453](https://github.com/x402-foundation/x402/pull/2453)
73
+ (replaces closed [#2436](https://github.com/x402-foundation/x402/pull/2436)):
71
74
 
72
75
  - **Rule 1.** `timestamp_ms` is an epoch-millisecond integer. Floats, ISO
73
76
  8601 strings, and negative values are rejected at the source-side.
@@ -115,10 +118,21 @@ The reference exhibit for this substrate is AlgoVoi's
115
118
 
116
119
  ## Spec references
117
120
 
118
- - [PR #2436](https://github.com/x402-foundation/x402/pull/2436) -- canonicalisation discipline
119
- - [PR #2440](https://github.com/x402-foundation/x402/pull/2440) -- composite trust-query
120
- - [PR #2434](https://github.com/x402-foundation/x402/pull/2434) -- compliance-receipt-fixture
121
- - [draft-vauban-x402-stark-receipts](https://datatracker.ietf.org/doc/draft-vauban-x402-stark-receipts/)
121
+ - [draft-hopley-x402-canonicalisation-jcs-v1](https://datatracker.ietf.org/doc/draft-hopley-x402-canonicalisation-jcs-v1/) -- IETF I-D (Independent Submission, Informational, sole AlgoVoi authorship). Specifies `urn:x402:canonicalisation:jcs-rfc8785-v1`.
122
+ - [docs.algovoi.co.uk/canonicalisation-substrate](https://docs.algovoi.co.uk/canonicalisation-substrate) -- v1 discipline reference page.
123
+ - [docs.algovoi.co.uk/canonicalisation-substrate-v2](https://docs.algovoi.co.uk/canonicalisation-substrate-v2) -- v2 (PQC-aware) additive successor.
124
+ - [PR #2453](https://github.com/x402-foundation/x402/pull/2453) -- live upstream spec PR for the canonicalisation discipline (sole AlgoVoi authorship; replaces closed #2436).
125
+ - [draft-vauban-x402-stark-receipts](https://datatracker.ietf.org/doc/draft-vauban-x402-stark-receipts/) -- third-party adopter-authored receipt format that anchors to the AlgoVoi canonicalisation discipline.
126
+
127
+ ## Conformance to the canonicalisation discipline
128
+
129
+ This package emits receipts pinned to `canon_version: jcs-rfc8785-v1` in-band. Downstream verifiers (`algovoi-audit-verifier` and any conformant third-party verifier) read the pin to select the canonicalisation rule applied at emission.
130
+
131
+ The pin is the load-bearing primitive for the [Substrate Adopters Registry](https://docs.algovoi.co.uk/adopters): adopters anchoring to this discipline pin the same `canon_version` value in their own publicly-citable artefacts. AlgoVoi maintains the registry as a neutral observer; this package is recorded there as the AlgoVoi reference implementation.
132
+
133
+ ## Substrate adopters
134
+
135
+ AlgoVoi is recorded in the [Substrate Adopters Registry](https://docs.algovoi.co.uk/adopters) as the substrate author (v1 and v2). Parties anchoring their own services or specifications to `canon_version: jcs-rfc8785-v1` are recorded in the registry via the [submission process](https://docs.algovoi.co.uk/adopters#how-to-submit-an-adoption-entry). AlgoVoi validates submissions against the artefact's canonical bytes and adds qualifying entries.
122
136
 
123
137
  ## Licence
124
138
 
package/dist/index.d.ts CHANGED
@@ -22,4 +22,5 @@ export { CompositeTrustQueryError, type EmitterRow, compositeTrustQueryHash, } f
22
22
  export { SCREEN_RESULTS, type ScreenResult, ComplianceReceiptError, type ComplianceReceipt, type BuildComplianceReceiptInput, buildComplianceReceipt, } from './compliance-receipt.js';
23
23
  export { AuditChainError, type AuditChainRow, appendToChain, verifyAuditChain, } from './audit-chain.js';
24
24
  export { TransactionalError, type TransitionPreimage, type TransitionInput, type TransitionRecord, type TransactionalChain, type TransactionalChainInput, transitionPreimage, transitionHash, buildTransactionalActionChain, } from './transactional.js';
25
+ export { SettlementBindingError, type SettlementBindingPreimage, settlementBindingPreimage, settlementActionBinding, } from './settlement-binding.js';
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EACxB,KAAK,UAAU,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,sBAAsB,EACtB,KAAK,yBAAyB,EAC9B,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -22,4 +22,5 @@ export { CompositeTrustQueryError, compositeTrustQueryHash, } from './composite-
22
22
  export { SCREEN_RESULTS, ComplianceReceiptError, buildComplianceReceipt, } from './compliance-receipt.js';
23
23
  export { AuditChainError, appendToChain, verifyAuditChain, } from './audit-chain.js';
24
24
  export { TransactionalError, transitionPreimage, transitionHash, buildTransactionalActionChain, } from './transactional.js';
25
+ export { SettlementBindingError, settlementBindingPreimage, settlementActionBinding, } from './settlement-binding.js';
25
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAGd,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EAExB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EAEd,sBAAsB,EAGtB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EAEf,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAMlB,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,SAAS,GACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,cAAc,EAGd,SAAS,EACT,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,wBAAwB,EAExB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,cAAc,EAEd,sBAAsB,EAGtB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EAEf,aAAa,EACb,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,kBAAkB,EAMlB,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,sBAAsB,EAEtB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Settlement-action binding (non-normative substrate extension).
3
+ *
4
+ * An x402/AP2/A2A settlement attestation proves a *payment* occurred. It does
5
+ * not, on its own, prove *which verified agent action* the payment corresponds
6
+ * to, nor that the correspondence is recorded in a tamper-evident chain. This
7
+ * module closes that post-settlement accountability gap with a single canonical
8
+ * reference binding four already-published substrate artifacts into one record:
9
+ *
10
+ * binding_ref = "sha256:" + SHA-256(JCS({
11
+ * action_ref, // the verified agent-action identity
12
+ * transition_hash, // the COMMITTED lifecycle transition (the "once")
13
+ * settlement_ref, // the settlement attestation content_hash
14
+ * retention_chain_ref, // the tamper-evident chain position recording it
15
+ * }))
16
+ *
17
+ * No new hashing primitive is introduced: the binding is the substrate's
18
+ * existing JCS + SHA-256 over the four references. Because action_ref and
19
+ * transition_hash are themselves computed from epoch-millisecond-integer
20
+ * preimages (Substrate Rule 2), any upstream RFC 3339 string timestamp produces
21
+ * a different action_ref, hence a different binding -- a non-conformant lineage
22
+ * cannot reproduce the binding bytes.
23
+ *
24
+ * The output carries the "sha256:" algorithm prefix, consistent with
25
+ * retention_chain_ref, signalling a content-addressed reference.
26
+ */
27
+ export declare class SettlementBindingError extends Error {
28
+ constructor(message: string);
29
+ }
30
+ export interface SettlementBindingPreimage {
31
+ action_ref: string;
32
+ transition_hash: string;
33
+ settlement_ref: string;
34
+ retention_chain_ref: string;
35
+ }
36
+ /**
37
+ * Return the validated four-field binding preimage object.
38
+ *
39
+ * The shape is fixed at the substrate layer; each field cryptographically
40
+ * contributes to the binding so that no one component can be substituted
41
+ * without changing the binding_ref.
42
+ */
43
+ export declare function settlementBindingPreimage(input: {
44
+ action_ref: string;
45
+ transition_hash: string;
46
+ settlement_ref: string;
47
+ retention_chain_ref: string;
48
+ }): SettlementBindingPreimage;
49
+ /**
50
+ * Return the binding_ref as a "sha256:"-prefixed content-addressed reference.
51
+ *
52
+ * binding_ref = "sha256:" + SHA-256(JCS({action_ref, transition_hash,
53
+ * settlement_ref, retention_chain_ref}))
54
+ */
55
+ export declare function settlementActionBinding(input: {
56
+ action_ref: string;
57
+ transition_hash: string;
58
+ settlement_ref: string;
59
+ retention_chain_ref: string;
60
+ }): string;
61
+ //# sourceMappingURL=settlement-binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlement-binding.d.ts","sourceRoot":"","sources":["../src/settlement-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AA8BD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,GAAG,yBAAyB,CAO5B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,GAAG,MAAM,CAGT"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Settlement-action binding (non-normative substrate extension).
3
+ *
4
+ * An x402/AP2/A2A settlement attestation proves a *payment* occurred. It does
5
+ * not, on its own, prove *which verified agent action* the payment corresponds
6
+ * to, nor that the correspondence is recorded in a tamper-evident chain. This
7
+ * module closes that post-settlement accountability gap with a single canonical
8
+ * reference binding four already-published substrate artifacts into one record:
9
+ *
10
+ * binding_ref = "sha256:" + SHA-256(JCS({
11
+ * action_ref, // the verified agent-action identity
12
+ * transition_hash, // the COMMITTED lifecycle transition (the "once")
13
+ * settlement_ref, // the settlement attestation content_hash
14
+ * retention_chain_ref, // the tamper-evident chain position recording it
15
+ * }))
16
+ *
17
+ * No new hashing primitive is introduced: the binding is the substrate's
18
+ * existing JCS + SHA-256 over the four references. Because action_ref and
19
+ * transition_hash are themselves computed from epoch-millisecond-integer
20
+ * preimages (Substrate Rule 2), any upstream RFC 3339 string timestamp produces
21
+ * a different action_ref, hence a different binding -- a non-conformant lineage
22
+ * cannot reproduce the binding bytes.
23
+ *
24
+ * The output carries the "sha256:" algorithm prefix, consistent with
25
+ * retention_chain_ref, signalling a content-addressed reference.
26
+ */
27
+ import { sha256Jcs } from './canonicalize.js';
28
+ const HEX64_RE = /^[0-9a-f]{64}$/;
29
+ const SHA256_REF_RE = /^sha256:[0-9a-f]{64}$/;
30
+ export class SettlementBindingError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = 'SettlementBindingError';
34
+ }
35
+ }
36
+ function requireHex64(field, value) {
37
+ if (typeof value !== 'string') {
38
+ throw new SettlementBindingError(`${field} must be string, got ${typeof value}`);
39
+ }
40
+ if (!HEX64_RE.test(value)) {
41
+ throw new SettlementBindingError(`${field} must be a lowercase 64-character SHA-256 hex string`);
42
+ }
43
+ return value;
44
+ }
45
+ function requireChainRef(value) {
46
+ if (typeof value !== 'string') {
47
+ throw new SettlementBindingError(`retention_chain_ref must be string, got ${typeof value}`);
48
+ }
49
+ if (!SHA256_REF_RE.test(value)) {
50
+ throw new SettlementBindingError("retention_chain_ref must be of the form 'sha256:<lowercase 64-char hex>'");
51
+ }
52
+ return value;
53
+ }
54
+ /**
55
+ * Return the validated four-field binding preimage object.
56
+ *
57
+ * The shape is fixed at the substrate layer; each field cryptographically
58
+ * contributes to the binding so that no one component can be substituted
59
+ * without changing the binding_ref.
60
+ */
61
+ export function settlementBindingPreimage(input) {
62
+ return {
63
+ action_ref: requireHex64('action_ref', input.action_ref),
64
+ transition_hash: requireHex64('transition_hash', input.transition_hash),
65
+ settlement_ref: requireHex64('settlement_ref', input.settlement_ref),
66
+ retention_chain_ref: requireChainRef(input.retention_chain_ref),
67
+ };
68
+ }
69
+ /**
70
+ * Return the binding_ref as a "sha256:"-prefixed content-addressed reference.
71
+ *
72
+ * binding_ref = "sha256:" + SHA-256(JCS({action_ref, transition_hash,
73
+ * settlement_ref, retention_chain_ref}))
74
+ */
75
+ export function settlementActionBinding(input) {
76
+ const obj = settlementBindingPreimage(input);
77
+ return `sha256:${sha256Jcs(obj)}`;
78
+ }
79
+ //# sourceMappingURL=settlement-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlement-binding.js","sourceRoot":"","sources":["../src/settlement-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAClC,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAE9C,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AASD,SAAS,YAAY,CAAC,KAAa,EAAE,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,sBAAsB,CAC9B,GAAG,KAAK,wBAAwB,OAAO,KAAK,EAAE,CAC/C,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,sBAAsB,CAC9B,GAAG,KAAK,sDAAsD,CAC/D,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,sBAAsB,CAC9B,2CAA2C,OAAO,KAAK,EAAE,CAC1D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,sBAAsB,CAC9B,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAKzC;IACC,OAAO;QACL,UAAU,EAAE,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC;QACxD,eAAe,EAAE,YAAY,CAAC,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC;QACvE,cAAc,EAAE,YAAY,CAAC,gBAAgB,EAAE,KAAK,CAAC,cAAc,CAAC;QACpE,mBAAmB,EAAE,eAAe,CAAC,KAAK,CAAC,mBAAmB,CAAC;KAChE,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAKvC;IACC,MAAM,GAAG,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,UAAU,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;AACpC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@algovoi/substrate",
3
- "version": "0.3.0",
4
- "description": "AlgoVoi agentic-payments substrate -- JCS canonicalisation, action_ref, composite trust-query, compliance receipts, audit chain",
3
+ "version": "0.4.0",
4
+ "description": "AlgoVoi agentic-payments substrate -- JCS canonicalisation, action_ref, composite trust-query, compliance receipts, audit chain, settlement-action binding",
5
5
  "keywords": [
6
6
  "x402",
7
7
  "ap2",
package/src/index.ts CHANGED
@@ -66,3 +66,10 @@ export {
66
66
  transitionHash,
67
67
  buildTransactionalActionChain,
68
68
  } from './transactional.js';
69
+
70
+ export {
71
+ SettlementBindingError,
72
+ type SettlementBindingPreimage,
73
+ settlementBindingPreimage,
74
+ settlementActionBinding,
75
+ } from './settlement-binding.js';
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Settlement-action binding (non-normative substrate extension).
3
+ *
4
+ * An x402/AP2/A2A settlement attestation proves a *payment* occurred. It does
5
+ * not, on its own, prove *which verified agent action* the payment corresponds
6
+ * to, nor that the correspondence is recorded in a tamper-evident chain. This
7
+ * module closes that post-settlement accountability gap with a single canonical
8
+ * reference binding four already-published substrate artifacts into one record:
9
+ *
10
+ * binding_ref = "sha256:" + SHA-256(JCS({
11
+ * action_ref, // the verified agent-action identity
12
+ * transition_hash, // the COMMITTED lifecycle transition (the "once")
13
+ * settlement_ref, // the settlement attestation content_hash
14
+ * retention_chain_ref, // the tamper-evident chain position recording it
15
+ * }))
16
+ *
17
+ * No new hashing primitive is introduced: the binding is the substrate's
18
+ * existing JCS + SHA-256 over the four references. Because action_ref and
19
+ * transition_hash are themselves computed from epoch-millisecond-integer
20
+ * preimages (Substrate Rule 2), any upstream RFC 3339 string timestamp produces
21
+ * a different action_ref, hence a different binding -- a non-conformant lineage
22
+ * cannot reproduce the binding bytes.
23
+ *
24
+ * The output carries the "sha256:" algorithm prefix, consistent with
25
+ * retention_chain_ref, signalling a content-addressed reference.
26
+ */
27
+
28
+ import { sha256Jcs } from './canonicalize.js';
29
+
30
+ const HEX64_RE = /^[0-9a-f]{64}$/;
31
+ const SHA256_REF_RE = /^sha256:[0-9a-f]{64}$/;
32
+
33
+ export class SettlementBindingError extends Error {
34
+ constructor(message: string) {
35
+ super(message);
36
+ this.name = 'SettlementBindingError';
37
+ }
38
+ }
39
+
40
+ export interface SettlementBindingPreimage {
41
+ action_ref: string;
42
+ transition_hash: string;
43
+ settlement_ref: string;
44
+ retention_chain_ref: string;
45
+ }
46
+
47
+ function requireHex64(field: string, value: unknown): string {
48
+ if (typeof value !== 'string') {
49
+ throw new SettlementBindingError(
50
+ `${field} must be string, got ${typeof value}`,
51
+ );
52
+ }
53
+ if (!HEX64_RE.test(value)) {
54
+ throw new SettlementBindingError(
55
+ `${field} must be a lowercase 64-character SHA-256 hex string`,
56
+ );
57
+ }
58
+ return value;
59
+ }
60
+
61
+ function requireChainRef(value: unknown): string {
62
+ if (typeof value !== 'string') {
63
+ throw new SettlementBindingError(
64
+ `retention_chain_ref must be string, got ${typeof value}`,
65
+ );
66
+ }
67
+ if (!SHA256_REF_RE.test(value)) {
68
+ throw new SettlementBindingError(
69
+ "retention_chain_ref must be of the form 'sha256:<lowercase 64-char hex>'",
70
+ );
71
+ }
72
+ return value;
73
+ }
74
+
75
+ /**
76
+ * Return the validated four-field binding preimage object.
77
+ *
78
+ * The shape is fixed at the substrate layer; each field cryptographically
79
+ * contributes to the binding so that no one component can be substituted
80
+ * without changing the binding_ref.
81
+ */
82
+ export function settlementBindingPreimage(input: {
83
+ action_ref: string;
84
+ transition_hash: string;
85
+ settlement_ref: string;
86
+ retention_chain_ref: string;
87
+ }): SettlementBindingPreimage {
88
+ return {
89
+ action_ref: requireHex64('action_ref', input.action_ref),
90
+ transition_hash: requireHex64('transition_hash', input.transition_hash),
91
+ settlement_ref: requireHex64('settlement_ref', input.settlement_ref),
92
+ retention_chain_ref: requireChainRef(input.retention_chain_ref),
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Return the binding_ref as a "sha256:"-prefixed content-addressed reference.
98
+ *
99
+ * binding_ref = "sha256:" + SHA-256(JCS({action_ref, transition_hash,
100
+ * settlement_ref, retention_chain_ref}))
101
+ */
102
+ export function settlementActionBinding(input: {
103
+ action_ref: string;
104
+ transition_hash: string;
105
+ settlement_ref: string;
106
+ retention_chain_ref: string;
107
+ }): string {
108
+ const obj = settlementBindingPreimage(input);
109
+ return `sha256:${sha256Jcs(obj)}`;
110
+ }