metered-protocol 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.
Files changed (206) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/contracts/metered.ag +135 -0
  4. package/contracts/metered_ag.tests.json +1226 -0
  5. package/contracts/metered_session.sil +237 -0
  6. package/contracts/metered_session.tests.json +1113 -0
  7. package/dist/contracts/metered.ag +135 -0
  8. package/dist/contracts/metered_ag.tests.json +1226 -0
  9. package/dist/contracts/metered_session.sil +237 -0
  10. package/dist/contracts/metered_session.tests.json +1113 -0
  11. package/dist/src/bias.d.ts +48 -0
  12. package/dist/src/bias.d.ts.map +1 -0
  13. package/dist/src/bias.js +43 -0
  14. package/dist/src/bias.js.map +1 -0
  15. package/dist/src/checkpoint.d.ts +57 -0
  16. package/dist/src/checkpoint.d.ts.map +1 -0
  17. package/dist/src/checkpoint.js +84 -0
  18. package/dist/src/checkpoint.js.map +1 -0
  19. package/dist/src/deadline.d.ts +71 -0
  20. package/dist/src/deadline.d.ts.map +1 -0
  21. package/dist/src/deadline.js +77 -0
  22. package/dist/src/deadline.js.map +1 -0
  23. package/dist/src/encoding.d.ts +72 -0
  24. package/dist/src/encoding.d.ts.map +1 -0
  25. package/dist/src/encoding.js +187 -0
  26. package/dist/src/encoding.js.map +1 -0
  27. package/dist/src/history.d.ts +33 -0
  28. package/dist/src/history.d.ts.map +1 -0
  29. package/dist/src/history.js +38 -0
  30. package/dist/src/history.js.map +1 -0
  31. package/dist/src/http/buyer.d.ts +57 -0
  32. package/dist/src/http/buyer.d.ts.map +1 -0
  33. package/dist/src/http/buyer.js +151 -0
  34. package/dist/src/http/buyer.js.map +1 -0
  35. package/dist/src/http/client.d.ts +55 -0
  36. package/dist/src/http/client.d.ts.map +1 -0
  37. package/dist/src/http/client.js +83 -0
  38. package/dist/src/http/client.js.map +1 -0
  39. package/dist/src/http/harness.d.ts +18 -0
  40. package/dist/src/http/harness.d.ts.map +1 -0
  41. package/dist/src/http/harness.js +16 -0
  42. package/dist/src/http/harness.js.map +1 -0
  43. package/dist/src/http/protocol.d.ts +84 -0
  44. package/dist/src/http/protocol.d.ts.map +1 -0
  45. package/dist/src/http/protocol.js +22 -0
  46. package/dist/src/http/protocol.js.map +1 -0
  47. package/dist/src/http/provider.d.ts +123 -0
  48. package/dist/src/http/provider.d.ts.map +1 -0
  49. package/dist/src/http/provider.js +197 -0
  50. package/dist/src/http/provider.js.map +1 -0
  51. package/dist/src/http/serve.d.ts +34 -0
  52. package/dist/src/http/serve.d.ts.map +1 -0
  53. package/dist/src/http/serve.js +171 -0
  54. package/dist/src/http/serve.js.map +1 -0
  55. package/dist/src/http/service.d.ts +86 -0
  56. package/dist/src/http/service.d.ts.map +1 -0
  57. package/dist/src/http/service.js +161 -0
  58. package/dist/src/http/service.js.map +1 -0
  59. package/dist/src/index.d.ts +68 -0
  60. package/dist/src/index.d.ts.map +1 -0
  61. package/dist/src/index.js +62 -0
  62. package/dist/src/index.js.map +1 -0
  63. package/dist/src/meter.d.ts +39 -0
  64. package/dist/src/meter.d.ts.map +1 -0
  65. package/dist/src/meter.js +100 -0
  66. package/dist/src/meter.js.map +1 -0
  67. package/dist/src/offer.d.ts +6 -0
  68. package/dist/src/offer.d.ts.map +1 -0
  69. package/dist/src/offer.js +92 -0
  70. package/dist/src/offer.js.map +1 -0
  71. package/dist/src/reconcile.d.ts +18 -0
  72. package/dist/src/reconcile.d.ts.map +1 -0
  73. package/dist/src/reconcile.js +101 -0
  74. package/dist/src/reconcile.js.map +1 -0
  75. package/dist/src/reservation.d.ts +51 -0
  76. package/dist/src/reservation.d.ts.map +1 -0
  77. package/dist/src/reservation.js +124 -0
  78. package/dist/src/reservation.js.map +1 -0
  79. package/dist/src/session.d.ts +40 -0
  80. package/dist/src/session.d.ts.map +1 -0
  81. package/dist/src/session.js +92 -0
  82. package/dist/src/session.js.map +1 -0
  83. package/dist/src/signer.d.ts +31 -0
  84. package/dist/src/signer.d.ts.map +1 -0
  85. package/dist/src/signer.js +57 -0
  86. package/dist/src/signer.js.map +1 -0
  87. package/dist/src/store.d.ts +30 -0
  88. package/dist/src/store.d.ts.map +1 -0
  89. package/dist/src/store.js +120 -0
  90. package/dist/src/store.js.map +1 -0
  91. package/dist/src/types.d.ts +80 -0
  92. package/dist/src/types.d.ts.map +1 -0
  93. package/dist/src/types.js +9 -0
  94. package/dist/src/types.js.map +1 -0
  95. package/dist/tools/anchor-cli.d.ts +2 -0
  96. package/dist/tools/anchor-cli.d.ts.map +1 -0
  97. package/dist/tools/anchor-cli.js +101 -0
  98. package/dist/tools/anchor-cli.js.map +1 -0
  99. package/dist/tools/anchor.d.ts +68 -0
  100. package/dist/tools/anchor.d.ts.map +1 -0
  101. package/dist/tools/anchor.js +136 -0
  102. package/dist/tools/anchor.js.map +1 -0
  103. package/dist/tools/chain.d.ts +18 -0
  104. package/dist/tools/chain.d.ts.map +1 -0
  105. package/dist/tools/chain.js +18 -0
  106. package/dist/tools/chain.js.map +1 -0
  107. package/dist/tools/conformance.d.ts +2 -0
  108. package/dist/tools/conformance.d.ts.map +1 -0
  109. package/dist/tools/conformance.js +220 -0
  110. package/dist/tools/conformance.js.map +1 -0
  111. package/dist/tools/contracts.d.ts +2 -0
  112. package/dist/tools/contracts.d.ts.map +1 -0
  113. package/dist/tools/contracts.js +68 -0
  114. package/dist/tools/contracts.js.map +1 -0
  115. package/dist/tools/covenant-binding.d.ts +40 -0
  116. package/dist/tools/covenant-binding.d.ts.map +1 -0
  117. package/dist/tools/covenant-binding.js +60 -0
  118. package/dist/tools/covenant-binding.js.map +1 -0
  119. package/dist/tools/covenant-profile.d.ts +41 -0
  120. package/dist/tools/covenant-profile.d.ts.map +1 -0
  121. package/dist/tools/covenant-profile.js +74 -0
  122. package/dist/tools/covenant-profile.js.map +1 -0
  123. package/dist/tools/covenant.d.ts +30 -0
  124. package/dist/tools/covenant.d.ts.map +1 -0
  125. package/dist/tools/covenant.js +54 -0
  126. package/dist/tools/covenant.js.map +1 -0
  127. package/dist/tools/demo-report.d.ts +24 -0
  128. package/dist/tools/demo-report.d.ts.map +1 -0
  129. package/dist/tools/demo-report.js +40 -0
  130. package/dist/tools/demo-report.js.map +1 -0
  131. package/dist/tools/demo.d.ts +2 -0
  132. package/dist/tools/demo.d.ts.map +1 -0
  133. package/dist/tools/demo.js +204 -0
  134. package/dist/tools/demo.js.map +1 -0
  135. package/dist/tools/dust-map.d.ts +2 -0
  136. package/dist/tools/dust-map.d.ts.map +1 -0
  137. package/dist/tools/dust-map.js +63 -0
  138. package/dist/tools/dust-map.js.map +1 -0
  139. package/dist/tools/expire-cases.d.ts +4 -0
  140. package/dist/tools/expire-cases.d.ts.map +1 -0
  141. package/dist/tools/expire-cases.js +211 -0
  142. package/dist/tools/expire-cases.js.map +1 -0
  143. package/dist/tools/fee-check.d.ts +2 -0
  144. package/dist/tools/fee-check.d.ts.map +1 -0
  145. package/dist/tools/fee-check.js +127 -0
  146. package/dist/tools/fee-check.js.map +1 -0
  147. package/dist/tools/fixtures.d.ts +39 -0
  148. package/dist/tools/fixtures.d.ts.map +1 -0
  149. package/dist/tools/fixtures.js +31 -0
  150. package/dist/tools/fixtures.js.map +1 -0
  151. package/dist/tools/gen-contract-tests.d.ts +2 -0
  152. package/dist/tools/gen-contract-tests.d.ts.map +1 -0
  153. package/dist/tools/gen-contract-tests.js +170 -0
  154. package/dist/tools/gen-contract-tests.js.map +1 -0
  155. package/dist/tools/kaspa.d.ts +13 -0
  156. package/dist/tools/kaspa.d.ts.map +1 -0
  157. package/dist/tools/kaspa.js +76 -0
  158. package/dist/tools/kaspa.js.map +1 -0
  159. package/dist/tools/live-deadline.d.ts +2 -0
  160. package/dist/tools/live-deadline.d.ts.map +1 -0
  161. package/dist/tools/live-deadline.js +122 -0
  162. package/dist/tools/live-deadline.js.map +1 -0
  163. package/dist/tools/live-expire.d.ts +2 -0
  164. package/dist/tools/live-expire.d.ts.map +1 -0
  165. package/dist/tools/live-expire.js +127 -0
  166. package/dist/tools/live-expire.js.map +1 -0
  167. package/dist/tools/live-settle.d.ts +2 -0
  168. package/dist/tools/live-settle.d.ts.map +1 -0
  169. package/dist/tools/live-settle.js +226 -0
  170. package/dist/tools/live-settle.js.map +1 -0
  171. package/dist/tools/live-steps.d.ts +63 -0
  172. package/dist/tools/live-steps.d.ts.map +1 -0
  173. package/dist/tools/live-steps.js +155 -0
  174. package/dist/tools/live-steps.js.map +1 -0
  175. package/dist/tools/model.d.ts +32 -0
  176. package/dist/tools/model.d.ts.map +1 -0
  177. package/dist/tools/model.js +99 -0
  178. package/dist/tools/model.js.map +1 -0
  179. package/dist/tools/send.d.ts +2 -0
  180. package/dist/tools/send.d.ts.map +1 -0
  181. package/dist/tools/send.js +41 -0
  182. package/dist/tools/send.js.map +1 -0
  183. package/dist/tools/session-chain.d.ts +69 -0
  184. package/dist/tools/session-chain.d.ts.map +1 -0
  185. package/dist/tools/session-chain.js +113 -0
  186. package/dist/tools/session-chain.js.map +1 -0
  187. package/dist/tools/sighash.d.ts +9 -0
  188. package/dist/tools/sighash.d.ts.map +1 -0
  189. package/dist/tools/sighash.js +68 -0
  190. package/dist/tools/sighash.js.map +1 -0
  191. package/dist/tools/sigscript-check.d.ts +2 -0
  192. package/dist/tools/sigscript-check.d.ts.map +1 -0
  193. package/dist/tools/sigscript-check.js +113 -0
  194. package/dist/tools/sigscript-check.js.map +1 -0
  195. package/dist/tools/sigscript.d.ts +61 -0
  196. package/dist/tools/sigscript.d.ts.map +1 -0
  197. package/dist/tools/sigscript.js +70 -0
  198. package/dist/tools/sigscript.js.map +1 -0
  199. package/dist/tools/state-layout.d.ts +2 -0
  200. package/dist/tools/state-layout.d.ts.map +1 -0
  201. package/dist/tools/state-layout.js +57 -0
  202. package/dist/tools/state-layout.js.map +1 -0
  203. package/package.json +59 -0
  204. package/spec/CONFORMANCE.md +80 -0
  205. package/spec/SPEC.md +821 -0
  206. package/spec/conformance-vectors.json +677 -0
package/spec/SPEC.md ADDED
@@ -0,0 +1,821 @@
1
+ # Metered — a metered session scheme for x402
2
+
3
+ **Status:** Phase 0 draft. Normative. Nothing implemented.
4
+ **Version:** 0.1.0-draft.2 · 2026-09-09
5
+ **Scheme identifier:** `metered`
6
+
7
+ The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
8
+
9
+ ---
10
+
11
+ ## 0. Why this exists
12
+
13
+ x402 ships two schemes. `exact` transfers a fixed amount. `upto` authorises a maximum and charges
14
+ actual consumption. The `upto` specification states three non-goals:
15
+
16
+ - *"Multi-settlement / streaming"* — out of scope
17
+ - *"Recurring payments"* — out of scope
18
+ - *"Settling the same authorization multiple times (e.g. pay-per-chunk streaming)"* — not supported
19
+
20
+ and settlement is unilateral: the resource server reports consumption, the facilitator checks only
21
+ that it does not exceed the cap. There is no client verification and no dispute mechanism.
22
+
23
+ `metered` adds the two missing pieces: **babel reservation**, so a session can span many
24
+ settlements, and **two-sided measurement**, so the buyer's own count is part of what settles.
25
+
26
+ ### 0.0 The babel
27
+
28
+ **A `babel` is one reserved slice of delivered work: `babelUnits` units of the Offer's `unit`.**
29
+ It is the quantum of this protocol. A Reservation authorises exactly one babel, a Measurement
30
+ reports one babel, and a State settles the running total of every babel agreed so far.
31
+
32
+ **The babel is the exposure bound.** It is the most either party can lose to the other turning
33
+ dishonest mid-session, it is fixed by the Offer before anything is spent, and every parameter in
34
+ this document is ultimately a statement about how small it can be made.
35
+
36
+ Named as the *bel* is -- a unit for a quantity that is otherwise awkward to measure -- and after
37
+ the tower, which is where two parties last stopped being able to agree on words.
38
+
39
+ ### 0.1 What decided the parameters
40
+
41
+ Two studies ran before this document was written. They are in `../evidence/`, seeded and reproducible.
42
+
43
+ | Finding | Consequence |
44
+ |---|---|
45
+ | Per-babel token summation diverges by exactly 1 token in 0.08% of sessions (3 counter-examples in 3,634 adversarial trials) | `toleranceAbs` MUST be ≥ 1 **for a tokeniser** — §6 |
46
+ | Re-encoding an *interior* token slice diverges 7.6% of the time; babels tiling from zero drop that to 0.08% | Babels MUST tile from sequence position zero |
47
+ | Per-frame tokenisation is wrong by up to 21.5% (code) and 21.2% (CJK), systematically upward | Implementations MUST reassemble before tokenising |
48
+ | The same text under `o200k_base` vs `cl100k_base` differs by 40.9% on CJK | The Offer MUST name the meter; an unnamed meter is not usable |
49
+ | Checkpoint fee 0.002 KAS, latency p50 1,053 ms / p90 1,879 ms over 25 live anchors | Checkpoints MUST be non-blocking |
50
+
51
+ ---
52
+
53
+ ## 1. Roles and trust
54
+
55
+ | Party | Trusted for |
56
+ |---|---|
57
+ | Buyer | **Nothing.** Its measurement is a claim the provider need not believe. |
58
+ | Provider | **Nothing.** Same. |
59
+ | Kaspa consensus | Settlement and ordering. The only trust assumption. |
60
+ | Facilitator | **Nothing.** Optional convenience. Either party MUST be able to settle without one. |
61
+
62
+ There is no arbiter, oracle or reputation service. The only remedy for disagreement is to stop.
63
+
64
+ ---
65
+
66
+ ## 2. Canonical encoding
67
+
68
+ Every signed object is encoded identically by both parties or nothing works. A field-order
69
+ divergence between two encoders is silent, and produces signatures that verify for neither.
70
+
71
+ 1. Objects are serialised as JSON with **keys sorted by UTF-16 code unit**, ascending — this is
72
+ what RFC 8785 (JCS) mandates and what JavaScript's default `Array.prototype.sort` does.
73
+ It is **not** the same as Unicode code-point order: they diverge above the BMP, where
74
+ surrogate pairs (U+D800–DFFF) sort below U+E000–FFFF. `�` and `U+10000` order
75
+ oppositely under the two rules. Field names in this version are ASCII, so nothing is
76
+ affected today — but an implementer following the wrong rule would diverge silently the
77
+ first time a key contained an astral character.
78
+ 2. **No insignificant whitespace.** No spaces after `:` or `,`, no trailing newline.
79
+ 3. Integers are JSON numbers and MUST be exactly representable — no value may exceed
80
+ 2^53−1. Sompi amounts and unit counts are integers. **Floats are forbidden. There are none in
81
+ this protocol** — the last of them, `toleranceRel`, was removed in this version (§5 rule 4), so
82
+ nothing settled here depends on binary floating point being reproduced identically by two
83
+ implementations.
84
+ 4. `null` is permitted only where this document names it (`prevState` at sequence 0).
85
+ 5. Byte strings are **lower-case hexadecimal**, unprefixed, of the exact length stated.
86
+ 6. **The signature is computed over the object with its own signature field absent**, not over a
87
+ field set to null or empty. **BIP340 receives the canonical bytes themselves, NOT a digest of
88
+ them.**
89
+
90
+ That last sentence exists because its absence was a real gap, found by writing a second
91
+ implementation. BIP340 accepts a message of any length, so signing `blake3(canonical)` is just
92
+ as implementable as signing `canonical` and looks just as correct -- and §3.4.1 explicitly
93
+ digests first for a State, which invites a reader to assume signing always digests first. It
94
+ does not. **A State is the exception**, and it is the exception because a Kaspa script must
95
+ reconstruct what it verifies and cannot build canonical JSON.
96
+ 7. Digests are **BLAKE3-256**, 32 bytes, hex — **everywhere, without exception.** The message
97
+ digest that chains States, the content digest, the session id, the parties commitment and
98
+ the checkpoint digest all use the same function over the same canonical bytes.
99
+
100
+ This is stated so emphatically because it was wrong. The borrowed encoder used BLAKE2b for
101
+ message digests and BLAKE3 for content addressing, while this document called both blake3.
102
+ An implementer following the sentence would have produced entirely different `prevState`
103
+ values and a different checkpoint digest — total incompatibility, from one word. The
104
+ encoder is now vendored in `src/encoding.ts` and uses BLAKE3 for all of it.
105
+ 8. **Signature bytes are not canonical and MUST NOT be compared for equality.** BIP340 signs with
106
+ random auxiliary data, so two correct signatures over the same message differ. A signature is
107
+ *verified*, never diffed. Conformance vectors therefore pin the signing payload and its digest,
108
+ and assert only that a signature verifies.
109
+
110
+ An implementation MUST reject an object whose re-encoding does not reproduce the bytes it verified.
111
+
112
+ **One object is signed over something other than its JSON form: the State.** Its two signatures
113
+ cover the fixed byte concatenation defined in §3.4.1, not its canonical JSON. This is not a
114
+ stylistic exception -- the covenant must reconstruct exactly what it verifies, and a Kaspa script
115
+ cannot build canonical JSON. Every rule above still governs how a State is *transmitted*; §3.4.1
116
+ governs what its signatures *cover*. An implementation that signs a State's JSON form produces a
117
+ signature the chain cannot check, and a session that cannot settle.
118
+
119
+ ---
120
+
121
+ ## 3. Messages
122
+
123
+ ### 3.1 Offer
124
+
125
+ Sent by the provider inside the HTTP 402 response as an x402 `PaymentRequirements` with
126
+ `scheme: "metered"`. Signed by the provider. It is a commitment, not a suggestion.
127
+
128
+ | Field | Type | Rule |
129
+ |---|---|---|
130
+ | `v` | int | MUST be `1`. |
131
+ | `scheme` | string | MUST be `"metered"`. |
132
+ | `network` | string | CAIP-2-style, e.g. `"kaspa:testnet-10"`. |
133
+ | `asset` | string | `"KAS"` in this version. |
134
+ | `sessionId` | hex[16] | Provider-chosen, unique. Binds every later message. See §3.1a. |
135
+ | `unit` | string | Versioned unit identifier. §6 defines two. |
136
+ | `meter` | string | **MUST be present and resolvable, and MUST measure `unit`.** See §6. |
137
+ | `unitPriceSompi` | int | Price of one unit. MUST be ≥ 1. |
138
+ | `babelUnits` | int | Units per reservation. **This is the exposure bound.** MUST be ≥ 1. |
139
+ | `maxBabels` | int | Session ceiling. MUST be ≥ 1. |
140
+ | `toleranceAbs` | int | **MUST be ≥ the meter's floor** (§6): 0 for an exact meter, 1 otherwise. |
141
+ | `checkpointEvery` | int | Chunks between checkpoints. `0` disables checkpointing. |
142
+ | `responseWindowDaa` | int | **Relative** sequence delay. MUST be in `1..=4294967295`. See §7.3. |
143
+ | `buyerPubkey` | hex[32] | BIP340 x-only. |
144
+ | `providerPubkey` | hex[32] | BIP340 x-only. |
145
+ | `partiesCommitment` | hex[32] | `blake3(buyerPubkey ‖ providerPubkey)`. |
146
+ | `sig` | hex[64] | Provider signature. |
147
+
148
+ A buyer MUST reject an Offer with a `toleranceAbs` below its meter's floor (§6), an absent or
149
+ unresolvable `meter`, a `meter` that does not measure the named `unit`, or a `responseWindowDaa`
150
+ outside the stated range.
151
+
152
+ ### 3.1a `sessionId` novelty is the buyer's obligation
153
+
154
+ **A buyer MUST reject an Offer carrying a `sessionId` it has already been offered by the same
155
+ provider, and MUST retain enough history to do so for as long as it will accept Offers from that
156
+ provider.**
157
+
158
+ `sessionId` is **provider-chosen**, and every threat that its width appears to address is one the
159
+ provider is not subject to. Sixteen bytes give 128 bits against accidental collision and against a
160
+ third party trying to guess or grind one -- both real, both handled. Neither is the interesting
161
+ case. A provider does not guess its own identifiers: it picks them, and picking a previous one
162
+ costs nothing.
163
+
164
+ The consequence is that the settlement preimage of §3.4.1 is no longer unique to a session. Two
165
+ sessions sharing a `sessionId` and a `(seq, cumulativeUnits, cumulativeSompi, prevState)` produce
166
+ the *same 72 bytes*, so the buyer's signature from the first session is a valid signature for the
167
+ second. That is exactly threat X1, cross-session replay, executed by the one party for
168
+ whom no choice of width is a defence.
169
+
170
+ No larger `sessionId` fixes this, and no on-chain rule can: the covenant is instantiated per
171
+ session and cannot see the other one. **The only party positioned to catch a reused `sessionId` is
172
+ the buyer, and only by remembering.** Hence the rule above, and hence the width was reduced to 16
173
+ bytes with nothing lost -- the bytes were never doing this job.
174
+
175
+ An implementation that keeps no such history MUST document that it does not, because a buyer
176
+ without it is unprotected against a provider that reuses identifiers deliberately.
177
+
178
+ **THIS RULE IS ALSO WHAT SEPARATES THE NETWORKS.** The §3.4.1 preimage does not commit to a
179
+ network, and a redeem script hashes to the same value on every one of them -- so a State signed
180
+ for a testnet session would, in principle, settle a mainnet covenant carrying the same parties and
181
+ `sessionId`. It cannot in practice, and the reason is that the history above is keyed by provider
182
+ and identifier, **deliberately not by network**: the second Offer is refused wherever it claims to
183
+ be. The buyer also funds the covenant, so the collision cannot be created without its help.
184
+
185
+ **Binding the network into the preimage instead was measured and rejected.** It costs 4 bytes in
186
+ the hand-written covenant, which has 16 spare, and **28 bytes in the Argent port, which has none**
187
+ -- the tag becomes state, and a continuation must re-emit it. Spending the only implementation
188
+ that a compiler checks, to duplicate a guarantee §3.1a already gives, is a bad trade. An
189
+ implementation that drops the novelty rule loses network separation along with everything else,
190
+ which is the other reason it is a MUST.
191
+
192
+ ### 3.2 Reservation
193
+
194
+ Signed by the buyer. Authorises **one babel**, never the session.
195
+
196
+ **A provider MUST verify that signature against the Offer's `buyerPubkey` before doing anything
197
+ else with the Reservation, and MUST refuse it otherwise.** An unverified Reservation authorises
198
+ nothing: it is not evidence the buyer asked for the work, so a provider acting on one is
199
+ delivering for free to whoever sent it, and holds no record of an authorisation to bill against.
200
+ Because `sessionId` travels in clear, "whoever sent it" includes anyone who has seen one message.
201
+
202
+ | Field | Type | Rule |
203
+ |---|---|---|
204
+ | `v` | int | MUST be `1`. |
205
+ | `sessionId` | hex[16] | MUST equal the Offer's. |
206
+ | `seq` | int | Chunk index from 0, strictly incrementing by exactly 1. |
207
+ | `units` | int | Units authorised. MUST be ≤ `babelUnits`. |
208
+ | `cumulativeUnits` | int | Running total including this babel. See below. |
209
+ | `cumulativeSompi` | int | Running amount. Recomputed and compared, never trusted. |
210
+ | `prevState` | hex[32] or null | Digest of the previous **doubly-signed** State. `null` at `seq` 0. |
211
+ | `sig` | hex[64] | Buyer signature. |
212
+
213
+ **Running from what.** Both running totals build on the previous **State**'s figures -- what was
214
+ actually settled -- plus this babel's *authorised* amount:
215
+
216
+ cumulativeUnits = prevState.cumulativeUnits + units
217
+ cumulativeSompi = prevState.cumulativeSompi + units x unitPriceSompi
218
+
219
+ This needs saying because §5 rule 6 bills the **lower** of the two counts, so the authorised and
220
+ settled figures diverge the first time the parties disagree by a token, and they never re-converge.
221
+ An implementation that instead accumulated its own authorised totals would drift a little further
222
+ from the settled chain on every divergent babel, and the two parties would disagree about
223
+ `cumulativeSompi` while both followed this document.
224
+
225
+ So: **a Reservation is a ceiling measured from the last settled point, and a State is a fact.** The
226
+ Reservation says *at most this much will be owed after this babel*; the State says what is owed.
227
+ `cumulativeSompi` in a Reservation is therefore an upper bound on the State that follows it, and
228
+ equal to it only when the two counts agreed exactly.
229
+
230
+ ### 3.3 Measurement
231
+
232
+ Both parties emit one per babel boundary. Signed by its author.
233
+
234
+ | Field | Type | Rule |
235
+ |---|---|---|
236
+ | `v` | int | MUST be `1`. |
237
+ | `sessionId` | hex[16] | |
238
+ | `seq` | int | The babel being reported. |
239
+ | `by` | string | `"buyer"` or `"provider"`. MUST match the signing key. |
240
+ | `units` | int | Units this party counted **for this babel alone**. |
241
+ | `cumulativeUnits` | int | This party's running total. Catches drift a per-babel check misses. |
242
+ | `contentDigest` | hex[32] | blake3 over the exact bytes of this babel. See §6.3. |
243
+ | `measurementId` | hex[16] | Idempotency key. A retransmission MUST be a no-op -- see below. |
244
+ | `sig` | hex[64] | |
245
+
246
+ ### 3.3a A retransmission is a no-op, not a disagreement
247
+
248
+ A Measurement carries `measurementId` so the same message can arrive twice without being counted
249
+ twice. **A party receiving a `measurementId` it has already settled MUST return the result it
250
+ returned the first time, unchanged.** It MUST NOT halt, and MUST NOT settle it again.
251
+
252
+ This is not a nicety. Retransmission is ordinary behaviour for anything carried over a network: a
253
+ client whose request times out will resend it. An implementation that treats the second copy as a
254
+ protocol violation converts a lost response -- nobody's fault, nobody's disagreement -- into a
255
+ permanently halted session, and does so most often to the honest party with the worst connection.
256
+
257
+ ### 3.4 State
258
+
259
+ The only object that can settle. Carries **two** signatures.
260
+
261
+ | Field | Type | Rule |
262
+ |---|---|---|
263
+ | `v` | int | MUST be `1`. |
264
+ | `sessionId` | hex[16] | |
265
+ | `seq` | int | |
266
+ | `cumulativeUnits` | int | Agreed total. |
267
+ | `cumulativeSompi` | int | Agreed amount owed the provider. |
268
+ | `prevState` | hex[32] or null | Digest of the previous State. The session is a hash chain. |
269
+ | `buyerSig` | hex[64] | Both REQUIRED. |
270
+ | `providerSig` | hex[64] | A State with one signature is not a State. |
271
+
272
+ Both signatures are computed over the **settlement preimage** of §3.4.1 -- **not** over the
273
+ State's canonical JSON with its signature fields absent, which is how every other signed object in
274
+ this document works. See §2.
275
+
276
+ ### 3.4.1 The settlement preimage
277
+
278
+ The only bytes a State's signatures ever cover. Fixed width, fixed order, no delimiters, no
279
+ length prefixes -- the covenant reconstructs this exact layout from values it already holds, so
280
+ anything variable-length would be unreconstructable.
281
+
282
+ | Offset | Width | Field |
283
+ |---|---|---|
284
+ | 0 | 16 | `sessionId` |
285
+ | 16 | 8 | `seq` |
286
+ | 24 | 8 | `cumulativeUnits` |
287
+ | 32 | 8 | `cumulativeSompi` |
288
+ | 40 | 32 | `prevState` |
289
+ | | **72** | total |
290
+
291
+ 1. Integers are **8-byte signed-magnitude**, the encoding `OpNum2Bin(x, 8)` produces. §2.3 already
292
+ caps every integer in this protocol at 2^53−1, which is comfortably inside 8 signed-magnitude
293
+ bytes, so the conversion is always defined. An implementation MUST still range-check before
294
+ converting: SilverScript's undefined behaviour is **fail-open**, and an out-of-range
295
+ `OpNum2Bin` is undefined, not a rejection.
296
+ 2. `prevState` is 32 bytes always. At `seq` 0, where the JSON carries `null`, the preimage carries
297
+ **32 zero bytes**. A `null` has no byte form and the covenant cannot branch on one cheaply.
298
+ 3. The signed digest is `blake3(preimage)`, 32 bytes, per §2.7. Both parties sign **that digest**
299
+ with BIP340. On-chain it is verified with `checkMsgSig`, which checks a signature over supplied
300
+ data rather than over the spending transaction.
301
+ 4. **`v` is deliberately absent.** A version field would cost 8 more bytes against a 520-byte
302
+ script limit, and it is not load-bearing here: a future version changes the covenant's
303
+ bytecode, which changes its P2SH address, which means the funds a v1 signature could be
304
+ replayed against do not exist at a v2 address. The contract's own bytecode is the version tag.
305
+ This is a deliberate omission with an argument, not an oversight.
306
+
307
+ ---
308
+
309
+ ## 4. Signer obligations
310
+
311
+ Adapted from Kurrent's stated signer policy. These are obligations on implementations, not on the
312
+ wire format, and violating them loses money without any message being malformed.
313
+
314
+ 1. A party MUST sign **at most one** State for a given `(sessionId, seq)`.
315
+ 2. A party MUST **durably record** the highest State it has signed **before transmitting it**.
316
+ Record-then-send. Never send-then-record.
317
+ 3. A party MUST NOT sign a State whose `prevState` does not match the digest of the State it last
318
+ agreed to.
319
+ 4. On restart, a party MUST load its highest recorded State before participating further, and MUST
320
+ refuse to sign at a `seq` at or below it.
321
+
322
+ ---
323
+
324
+ ## 5. The reconciliation rule
325
+
326
+ At each babel boundary, in order. Any failure halts the session.
327
+
328
+ 1. Both Measurement signatures verify, and each `by` matches the key that signed it.
329
+ 2. Both `seq` equal the babel being reconciled.
330
+ 3. **`contentDigest` values are equal.** If counts match but digests differ, the parties are
331
+ describing different bytes and counting cannot resolve it.
332
+ 4. `|buyerUnits − providerUnits| ≤ toleranceAbs`. **The bound is absolute and does not scale with
333
+ the babel.** An earlier draft added a relative term. It was removed because the evidence never
334
+ supported it: honest divergence is a boundary effect, Study A saw it three times in 3,634
335
+ adversarial trials and every occurrence was exactly one token, and Study C found none at all at
336
+ seven babel sizes from 3 to 550 units with 4,000 boundaries each. The magnitude does not grow
337
+ with the babel, so a bound that grows with the babel widens the room a counterparty can shave
338
+ in — §5.1's leak — in exchange for nothing measured.
339
+ 5. The same bound holds for `cumulativeUnits`.
340
+ 6. **The billed amount uses the lower of the two counts.**
341
+
342
+ ### 5.0 What reconciliation produces
343
+
344
+ The rules above say what must hold. This says what comes out, because a second implementation
345
+ cannot guess it and two implementations that disagree about the OUTPUT disagree about the bill.
346
+
347
+ On success:
348
+
349
+ | Field | Value |
350
+ |---|---|
351
+ | `billedUnits` | `min(buyerUnits, providerUnits)` — rule 6 |
352
+ | `billedCumulativeUnits` | `min(buyerCumulativeUnits, providerCumulativeUnits)` |
353
+ | `residual` | `providerUnits - buyerUnits`, **signed, in that order** |
354
+
355
+ `billedCumulativeUnits` is stated because rule 6 only names the per-babel amount. An implementation
356
+ that carries the provider's cumulative figure forward instead agrees about every babel and disagrees
357
+ about the session.
358
+
359
+ The **sign** of `residual` is stated because §5.1.1 feeds it to a one-sided CUSUM. Reversing it does
360
+ not produce a detector that fires late; it produces one that ignores a counterparty shaving and
361
+ alarms on an honest one.
362
+
363
+ On failure the outcome carries a `reason`, which is one of:
364
+
365
+ `signature`, `sequence`, `content-digest`, `tolerance-babel`, `tolerance-cumulative`, `bias`.
366
+
367
+ Rule 6 removes the incentive to sit at the top of the tolerance band. It also absorbs the one-token
368
+ divergence measured in Study A, since every counter-example found had the buyer counting fewer.
369
+ That second property was luck, and is recorded as luck.
370
+
371
+ ### 5.1 Bias detection — symmetric and normative
372
+
373
+ Rule 6 bills the lower count, which removes the provider's incentive to sit at the top of the
374
+ tolerance band. Phase 1 found it hands the identical trick to the **buyer**: under-reporting by
375
+ exactly `toleranceAbs` every babel pays less, systematically, and no single babel breaches
376
+ anything. At 1 token on a 550-token babel that is 0.18% — small, but a leak with no mechanism
377
+ against it.
378
+
379
+ Therefore **both parties MUST** track the signed residual `providerUnits − buyerUnits` across the
380
+ session. **The honest residual is zero to measurement** (Study C: 0 non-zero residuals in 10,013
381
+ honest babels at 50/200/550 units; 95% upper bound 0.075% at every babel size tested), so any
382
+ sustained positive drift is signal rather than noise. A party MUST halt when the test below fires.
383
+
384
+ This converts tolerance-riding from an unbounded slow leak into a bounded one: an adversary must
385
+ stay inside the tolerance *and* keep the residual centred, which means giving back what it takes.
386
+
387
+ #### 5.1.1 The test, measured rather than chosen
388
+
389
+ "A persistent one-sided residual" was normative and undefined -- no threshold, no window, no
390
+ statistic, so two implementers could not produce the same behaviour. Study C
391
+ (`evidence/results-c.txt`) measured what honest sessions actually do and sized a test against it.
392
+
393
+ **Both parties MUST run a one-sided CUSUM over the per-babel residual `providerUnits - buyerUnits`:**
394
+
395
+ S[0] = 0
396
+ S[i] = max(0, S[i-1] + residual[i] - k) with k = 0.5
397
+ halt when S[i] >= h with h = 5
398
+
399
+ One-sided, because the threat is directional and so is the only honest divergence ever observed:
400
+ both make the residual **positive**. Sign therefore carries no information and rate is the whole
401
+ signal -- which is what a CUSUM measures and what "persistent" was groping for.
402
+
403
+ | Buyer under-reports 1 token on | Detected | Median babels to alarm |
404
+ |---|---|---|
405
+ | every babel (the 0.18% leak §5.1 names) | 100% | 9 |
406
+ | 50% of babels | 100% | 80 |
407
+ | 25% of babels | 1% | — |
408
+ | 10% of babels | never | — |
409
+
410
+ False alarms: **zero in 800,000 honest babels** (400 sessions of 2,000).
411
+
412
+ **What this does and does not bound, stated because a reader will otherwise infer zero.** The test
413
+ catches the threat §5.1 was written for, quickly. It does **not** catch a patient buyer: below
414
+ about a quarter of babels the leak is invisible to it. So §5.1's promise of converting an
415
+ unbounded slow leak into a bounded one holds, and **the bound is roughly 0.045% per babel** -- an
416
+ order of magnitude under the named threat, and not zero. An implementation that needs a tighter
417
+ bound must reduce `toleranceAbs`, not tune `k` and `h`; below `k = 0.5` the honest rate is no
418
+ longer known to be small enough, because Study C bounds it at 0.075% rather than measuring it as
419
+ exactly zero.
420
+
421
+ ---
422
+
423
+ ### 5.2 Only an authenticated counterparty may halt a session
424
+
425
+ §1 says "the only remedy for disagreement is to stop". That applies to two parties who have
426
+ **authenticated themselves** and cannot agree on a measurement. It does not apply to a message
427
+ from an unknown sender.
428
+
429
+ **An implementation MUST NOT halt a session because a message failed to authenticate.** It MUST
430
+ refuse the message and leave the session untouched. Halting is reserved for failures that only an
431
+ authenticated counterparty can reach.
432
+
433
+ The distinction is load-bearing because `sessionId` is not a secret -- it is carried in clear in
434
+ every message, and it must be, since it is what binds them together (§3.1a). An implementation
435
+ that halts on any malformed input therefore lets anyone who has observed a single message destroy
436
+ the session permanently, at no cost and with no key. That is a denial of service wearing this
437
+ specification as its authorisation.
438
+
439
+ Concretely: a bad signature, an unparseable body and an oversized body are refusals. A measurement
440
+ outside tolerance, a broken State chain and a tripped bias detector are halts.
441
+
442
+ ---
443
+
444
+ ## 6. Units and meters
445
+
446
+ The protocol counts integers and does not know what they represent. A **unit** names what is being
447
+ sold; a **meter** names the procedure that turns delivered content into a number. The Offer carries
448
+ both, and a meter that does not measure the named unit MUST be refused -- counting tokens against a
449
+ byte price is not a rounding error, it is a different bill.
450
+
451
+ | Unit | Meter | Exact |
452
+ |---|---|---|
453
+ | `llm.output_tokens.v1` | `o200k_base` | no |
454
+ | `net.bytes_delivered.v1` | `octets` | yes |
455
+
456
+ ### 6.0 Exact and inexact meters, and where the tolerance comes from
457
+
458
+ **A meter is EXACT when two correct implementations always reach the same number for the same
459
+ bytes.** That property decides `toleranceAbs`, and it belongs to the meter rather than to the
460
+ protocol:
461
+
462
+ | | Minimum `toleranceAbs` | Because |
463
+ |---|---|---|
464
+ | exact | **0**, and it SHOULD be 0 | §5 rule 3 has already agreed `contentDigest`, so the bytes are identical and the counts cannot differ. A tolerance would absorb no honest divergence and would be pure room to shave in. |
465
+ | inexact | **1** | Tokenisation is a lossy map from bytes to a count. Two implementations can agree on every byte and still differ by one token, because the boundaries belong to the tokeniser. §0.1 measured it. |
466
+
467
+ An earlier version of this document required `toleranceAbs ≥ 1` of every Offer. That rule was
468
+ derived from tokenisation and stated as though it were a property of the protocol; adding a second
469
+ unit is what made the difference visible.
470
+
471
+ ### 6.1 `llm.output_tokens.v1`
472
+
473
+ Tokens in the assistant content **actually delivered to the buyer**, tokenised with the meter named
474
+ in the Offer.
475
+
476
+ **Not counted:** input tokens, system prompt, and anything the buyer does not receive.
477
+
478
+ #### Hidden reasoning tokens are out of scope
479
+
480
+ A model billing for internal reasoning the buyer never sees cannot be metered two-sided -- the
481
+ buyer cannot count what it was not given. Providers billing for hidden reasoning MUST either price
482
+ it into the delivered-token rate or use `exact` for that portion. This is a limitation of the
483
+ approach, not an omission, and it is the boundary of two-sided measurement rather than a gap in
484
+ this specification: no protocol can make a buyer able to count data it never receives.
485
+
486
+ #### Counting rules for `llm.output_tokens.v1`
487
+
488
+ 1. Implementations MUST **reassemble the full babel** before tokenising. Tokenising stream frames
489
+ separately is wrong by up to 21.5% (§0.1) and MUST NOT be done.
490
+ 2. Chunks MUST **tile from sequence position zero**. Cutting a session into interior slices
491
+ diverges 7.6% of the time; tiling from zero drops it to 0.08%. A future change to how babels are
492
+ cut would silently reintroduce this, which is why it is normative rather than advisory.
493
+ 3. `contentDigest` is blake3 over the **raw delivered bytes**, with **no normalisation** — no
494
+ whitespace collapsing, no unicode normalisation, no trimming.
495
+ 4. The `tokenizer` field MUST identify a specific, publicly obtainable tokeniser and version.
496
+ 5. An implementation that cannot obtain the named tokeniser MUST **refuse the Offer**. It MUST NOT
497
+ count with a different one. A substituted tokeniser produces a number that looks like a
498
+ measurement and settles like one; §0.1 measured 40.9% divergence on CJK between two real
499
+ tokenisers, so the substitute is not an approximation of the agreed unit but a different unit
500
+ wearing its name.
501
+ 6. Implementations of this specification in different languages MUST agree **token for token**,
502
+ not merely on totals. Two tokenisers can produce the same count for one input and disagree
503
+ about where the boundaries fall, which diverges on the next input rather than this one. This
504
+ reference implementation pins its JavaScript tokeniser against Python `tiktoken` over §0.1's
505
+ adversarial corpus -- CJK, emoji, combining marks, surrogate pairs, pathological whitespace --
506
+ and compares the token IDs.
507
+
508
+ ---
509
+
510
+
511
+ ### 6.2 `net.bytes_delivered.v1`
512
+
513
+ **Octets of the content actually delivered to the buyer, exactly as `contentDigest` covers them.**
514
+ The meter is `octets`, and it is exact.
515
+
516
+ 1. Count the bytes as delivered. **No re-encoding, no normalisation, no decompression.** If a
517
+ transport compressed the body, the unit is the bytes the buyer received and digested, not the
518
+ bytes before or after any transform the transport applied. `contentDigest` is computed over the
519
+ same bytes, which is what makes the two sides agree by construction.
520
+ 2. The rules of §6.1's counting section that concern *what* is counted apply unchanged:
521
+ reassemble the whole babel before counting, and tile babels from sequence position zero.
522
+ 3. `toleranceAbs` SHOULD be 0. A non-zero tolerance is permitted and is a decision to accept
523
+ shaving, since there is no honest divergence for it to absorb.
524
+
525
+ This unit is why the protocol names a meter rather than a tokeniser. Anything a buyer physically
526
+ receives and can count -- bytes, frames, records -- fits the same machinery, and only §6 changes.
527
+
528
+ **What does NOT fit:** a resource the buyer never receives. Storage at rest has nothing delivered
529
+ to count and needs proof of continued possession, which is a different mechanism with different
530
+ assumptions. **This protocol meters delivery, not possession.**
531
+
532
+ ### 6.4 Delivered content is bytes, and a transport encoding is not the content
533
+
534
+ Everything this protocol does to delivered content -- digest it under §6.3, count it under §6.1 or
535
+ §6.2, agree on it under §5 -- is defined over **bytes**. An implementation MUST NOT restrict
536
+ delivery to content expressible as text.
537
+
538
+ This is stated because it is easy to violate without noticing, and the reference implementation did
539
+ for as long as it had only ever sold tokens: every signature on its delivery path took a string, so
540
+ `net.bytes_delivered.v1` could be declared in an Offer and could not actually be served. The
541
+ specification was not wrong; the implementation was narrower than the specification, which is the
542
+ harder failure to see.
543
+
544
+ 1. A transport that cannot carry arbitrary bytes MAY encode them -- JSON bodies, for instance,
545
+ require it. This reference transport base64-encodes the babel body and names the field
546
+ `contentB64` so that the encoding is visible at every use.
547
+ 2. **The encoding is never what is metered or digested.** Both parties MUST decode before counting
548
+ and before computing `contentDigest`. Metering the encoded form bills the buyer for roughly a
549
+ third more than it received, and digesting it commits the parties to the encoding rather than to
550
+ the content.
551
+ 3. A lenient decoder is acceptable but MUST NOT be relied upon. Base64 decoders commonly discard
552
+ characters outside the alphabet rather than refusing, so a corrupted field decodes to different
553
+ bytes instead of raising. That cannot pass silently here: different bytes produce a different
554
+ digest, and §5 rule 3 halts the session on a digest mismatch before any count is consulted.
555
+
556
+ ## 7. Settlement
557
+
558
+ ### 7.1 Cooperative close — NOT AVAILABLE in this version
559
+
560
+ ~~Both parties sign an **ordinary spend** paying `cumulativeSompi` to the provider and the
561
+ remainder to the buyer. **The covenant is not executed.** This is what happens almost every time,
562
+ and keeping it off the covenant path removes nearly all the script-size pressure from the common
563
+ case.~~
564
+
565
+ **That is not implementable, and the reason is structural rather than a bug.** The session's funds
566
+ sit at a covenant P2SH address. A P2SH output cannot be spent without supplying its redeem script
567
+ and satisfying it, so **every** close executes the covenant. There is no "ordinary spend" available
568
+ to a UTXO locked by a script, and the sentence about removing script-size pressure has it exactly
569
+ backwards: a cooperative close must be a THIRD ENTRY POINT, and entry points are what script size
570
+ is made of.
571
+
572
+ **Measured, 2026-09-10.** A minimal cooperative entry -- bind the parties, check two signatures,
573
+ constrain no outputs because two consenting parties have already agreed the split -- takes the
574
+ contract from 516 bytes to **619**, against a limit of 520. It costs **103 bytes** and there are
575
+ **4** spare.
576
+
577
+ **Two custody models, and this version picked one.**
578
+
579
+ | | Funds live at | Cooperative close | Unilateral close |
580
+ |---|---|---|---|
581
+ | **Covenant** (this version) | a covenant P2SH | a third entry, +103 bytes | `settle` then `expire`, consensus-enforced |
582
+ | **Channel** (Lightning-shaped) | a plain 2-of-2 | an ordinary spend, free | pre-signed asymmetric commitments plus revocation |
583
+
584
+ They are alternatives, not complements: one UTXO cannot be both a bare 2-of-2 and a covenant. The
585
+ covenant model was chosen because consensus enforces the sequence ordering directly, which removes
586
+ revocation secrets and the entire class of bugs that comes with them. The price is that the common
587
+ case pays for a script, and §7.1 was written as though it did not.
588
+
589
+ **What a close costs today, plainly:** two transactions -- `settle`, then `expire` after
590
+ `responseWindowDaa` -- rather than one. A cooperative path would save one transaction fee and the
591
+ window. Whether a cooperative close is worth the 103 bytes it measures at is an open decision
592
+ rather than an oversight, and it is recorded here so a later version can take it up deliberately.
593
+
594
+ ### 7.2 Unilateral close
595
+
596
+ Used when a counterparty is unresponsive or a party refuses to co-sign. Three paths:
597
+
598
+ **Two entry points, not three.** Phase 1's script budget found that three paths plus a
599
+ continuation state does not plausibly fit in 520 bytes, and that `supersede` need not be its own
600
+ entry — it is a guard on `settle`.
601
+
602
+ | Path | Who | What consensus enforces |
603
+ |---|---|---|
604
+ | `settle` | Either party with a doubly-signed State | Both signatures verify against `partiesCommitment` over the §3.4.1 preimage. **If a State is already pending, this one MUST carry a strictly higher `seq`** — this is supersede, as a guard rather than a branch. **`settle` pays no one.** It posts the claim: the single output returns the funds to this same covenant carrying `(seq, cumulativeSompi)` as state, and the response window restarts. |
605
+ | `expire` | Either party after the response window | Pays out the pending claim — `cumulativeSompi` to the provider, the remainder to the buyer. **If no claim was ever posted, refunds the buyer entirely.** |
606
+
607
+ **Settlement is two-phase, and it has to be.** An earlier draft of this table said `settle` "pays
608
+ exactly `cumulativeSompi` and the remainder" *and* that a later `settle` supersedes it. Those
609
+ cannot both hold: once the money is paid the covenant is spent and there is nothing left to
610
+ supersede. §7.5's "a stale close that has already been **accepted**" was always describing a
611
+ posted claim, not a completed payout. Found by writing
612
+ the covenant.
613
+
614
+ ### 7.2a One covenant per transaction
615
+
616
+ **A closing transaction MUST spend exactly one covenant input.** `expire` enforces
617
+ `tx.inputs.length == 1`.
618
+
619
+ The reason is that `expire` pays to `P2PK(buyer)` and `P2PK(provider)`, and those scripts are not
620
+ session-specific. Two sessions between the same two parties therefore produce **identical output
621
+ scripts**, so one pair of outputs can satisfy both inputs' checks at once -- each input reads its
622
+ own `total` and each is separately satisfied, while only one payout exists.
623
+
624
+ Measured before the rule was added: two covenant inputs of 10,000,000 sompi each, closed with
625
+ outputs totalling 9,600,000, was **accepted**. 10,400,000 sompi -- more than half the money --
626
+ went to fee. Either party can sign such a transaction, so it is a griefing attack against whoever
627
+ has more at stake.
628
+
629
+ `settle` is not exposed the same way, because its single output is the continuation P2SH and that
630
+ script embeds the `sessionId`; two sessions cannot share one. The rule is nonetheless stated for
631
+ the whole scheme rather than for one entry, because the property being relied on is that **a
632
+ covenant accounts for every input carrying it**, and a future entry that pays to a
633
+ non-session-specific script would reintroduce this without warning.
634
+
635
+ Found by reading Argent's leader/delegate input-group invariants, whose Rule 3 requires exactly
636
+ this accounting.
637
+
638
+ ### 7.3 The response window is relative, not absolute
639
+
640
+ `this.age` in SilverScript lowers to `OpCheckSequenceVerify`, which reads the spending input's
641
+ `sequence` field — **not** a current-DAA context. Therefore:
642
+
643
+ - `responseWindowDaa` is a **relative** delay, satisfied by setting the spending input's `sequence`.
644
+ - It MUST fit the low-32-bit encoding: `1..=4294967295`.
645
+ - The disabled bit (`1 << 63`) MUST be unset. Mask `0x00000000ffffffff`.
646
+
647
+ ### 7.3a The window is the provider's DEADLINE
648
+
649
+ Because the delay is relative to the **covenant UTXO being spent**, the clock starts when the
650
+ covenant is funded and every `settle` restarts it by creating a fresh output. That has a
651
+ consequence §7.3's mechanics do not state, and it is the sharpest rule in this document:
652
+
653
+ **Once the covenant UTXO is older than `responseWindowDaa` and no claim is pending, the buyer can
654
+ `expire` and take back everything — including payment for work already delivered and already
655
+ agreed in doubly-signed States.**
656
+
657
+ **A provider MUST therefore post a claim before that deadline, and MUST choose
658
+ `responseWindowDaa` long enough to do so.** A provider that delivers for longer than the window
659
+ without settling is not protected by holding signed States; it is holding evidence of a debt the
660
+ chain will shortly release.
661
+
662
+ The provider is not otherwise exposed. It holds a doubly-signed State it can post at any moment,
663
+ and `seq > pendingSeq` is strict, so a stale claim can never overwrite a fresher one — the buyer's
664
+ only move is to get there **first**, and only while nothing is pending. The requirement is a
665
+ deadline, not a vigil.
666
+
667
+ **This is not the problem a Lightning watchtower solves, and MUST NOT be answered the same way.**
668
+ There, punishment is retrospective: an old state must be detected and answered with a justice
669
+ transaction, using per-update revocation secrets, by a wallet that is offline by nature — hence
670
+ delegation to a third party who must be trusted, can be bribed, and has to be paid. None of that
671
+ shape appears here. The party at risk is a server, online because serving is its business; it
672
+ needs only the latest State, which it already holds; there is no secret to store, no third party,
673
+ and nothing to delegate. **An implementation MUST NOT introduce one.**
674
+
675
+ Two settings bound the loss, and both are the provider's to choose:
676
+
677
+ | Setting | Bounds |
678
+ |---|---|
679
+ | post before the UTXO reaches a chosen age | how long an unposted claim may sit |
680
+ | post once unsettled value reaches a chosen amount | how much may accrue unposted |
681
+
682
+ The second is the provider's counterpart to the babel: with it, the worst a buyer can take by
683
+ waiting out the window is that amount. Without it, the bound is whatever the session can bill in
684
+ the time allowed, which is a choice too — just an implicit one.
685
+
686
+ **Each settle costs a transaction**, so a short window is safer and dearer. That trade is the
687
+ provider's, and it should be made with the numbers rather than by default.
688
+
689
+ ### 7.4 Mutable covenant state is integers only
690
+
691
+ There is a NUM2BIN size cap on `byte[32]` state writes in the current compiler. Mutable covenant
692
+ state is therefore restricted to `seq` and `cumulativeSompi`, both integers. `partiesCommitment` is
693
+ a **constructor constant**, which is a different mechanism and is unaffected. `prevState` lives in
694
+ the off-chain message and is never written on-chain.
695
+
696
+ ### 7.4a Dust, and the three shapes a close may take
697
+
698
+ Kaspa's KIP-9 prices an output by its reciprocal, and a transaction is refused when
699
+
700
+ 10^12 / out_1 + 10^12 / out_2 - 10^12 / in > 500,000
701
+
702
+ The formula is the node's own, confirmed to within one sompi against a real rejection.
703
+
704
+ **THE FLOOR IS NOT A CONSTANT, and treating it as one strands funds.** An output is priced by its
705
+ reciprocal and the input's is subtracted, so what is payable depends on the OTHER output and on
706
+ how much the covenant holds. 2,000,000 is only the limit a very large transaction approaches.
707
+ `tools/dust-map.ts` walks every possible claim against the formula above and reports which of the
708
+ three shapes below, if any, consensus would accept. Against a threshold of 2,000,000 there is an
709
+ **unclosable window at every balance** -- claims for which the covenant demands an output the
710
+ network will not create, and the whole balance is stranded:
711
+
712
+ | Covenant holds | Claims with no legal close |
713
+ |---|---|
714
+ | 1 KAS | 2,000,000 .. 2,000,980 |
715
+ | 0.5 KAS | 2,000,000 .. 2,004,044 |
716
+ | 0.2 KAS | 2,000,000 .. 2,028,014 |
717
+ | 0.1 KAS | 2,000,000 .. 2,146,692 |
718
+ | 0.05 KAS | 2,000,000 .. 2,599,999 |
719
+
720
+ **So the threshold is 2,600,000, and the fold threshold 3,000,000** -- measured as the smallest
721
+ values that close the window at every balance §7.4b admits.
722
+
723
+ That is not a pricing inconvenience; it decides whether a session can be closed. A close
724
+ therefore has **exactly three legal shapes**, and an implementation MUST choose between them
725
+ rather than always emitting two outputs:
726
+
727
+ | Condition | Shape |
728
+ |---|---|
729
+ | `cumulativeSompi` < 2,600,000 | ONE output, everything to the **buyer**. Includes the no-claim case. |
730
+ | refund < 2,600,000 | ONE output, everything to the **provider**. Requires `cumulativeSompi + 3,000,000 >= total`. |
731
+ | both payable | TWO outputs: `cumulativeSompi` to the provider, the remainder to the buyer. |
732
+
733
+ **Whichever side's share is dust, the other side takes the lot.** Dust cannot be paid to anyone,
734
+ so folding it costs that party at most 0.02 KAS, and the alternative is locking the entire
735
+ balance. A provider SHOULD still price a session so the ordinary two-output close is reachable,
736
+ and MAY close cooperatively (§7.1) at any size, where an ordinary spend can pay whatever both
737
+ parties agree.
738
+
739
+ **The fold threshold is 2,400,000 rather than 2,000,000, and the difference is the fee.** The
740
+ refund is `total - cumulativeSompi - fee`, so a bound of 2,000,000 would admit refunds only up to
741
+ 1,600,000 and leave everything between there and the dust floor with no legal shape at all --
742
+ another way to lock the balance. Adding the fee allowance closes that gap exactly.
743
+
744
+ This section is what a real session taught: three babels of ten words at 3,630 sompi earned
745
+ 108,900, the State settled on chain, and the close was then impossible. The same session now
746
+ closes.
747
+
748
+ ### 7.5 What settlement does not promise
749
+
750
+ A stale close that has already been **accepted** cannot be reversed by a later State. The response
751
+ window is the entire protection. A party offline for its duration loses. This is the honest state
752
+ of the art and matches Kurrent's stated non-claims; see §9.
753
+
754
+ ---
755
+
756
+ ### 7.4b The funding floor
757
+
758
+ **A buyer MUST fund the covenant with at least**
759
+
760
+ max( maxBabels x babelUnits x unitPriceSompi + closeFee , 10,000,000 sompi )
761
+
762
+ **The second term is a SHAPE rule, not a dust rule.** A two-output close needs both halves to
763
+ clear KIP-9 *together*, and for balances from 5,700,000 to 6,800,000 sompi they cannot -- at any
764
+ split, and whatever the dust threshold is set to. `tools/dust-map.ts` walks balances in
765
+ 100,000-sompi steps and finds claims with no legal close at every balance in that band, and none
766
+ at 6,850,000 or above. 10,000,000 is the next round number, and is what this rule requires.
767
+
768
+ **and MUST NOT countersign a State whose `cumulativeSompi` exceeds what the covenant holds less
769
+ that fee.**
770
+
771
+ §7.2's `expire` pays the provider an output of **exactly** `pendingSompi`. If the parties have
772
+ signed a total the covenant cannot cover -- including the fee for the very transaction that pays
773
+ it -- then no valid close transaction exists, and the entire balance is stranded. It is Finding G
774
+ again, reached by agreement rather than by dust.
775
+
776
+ **The covenant cannot enforce this and no covenant could.** It would have to know the fee of a
777
+ transaction that has not been built yet, which is not available to a script at validation time.
778
+ The buyer can, because the buyer chooses the funding amount, and it can do so before spending
779
+ anything at all. This is why the rule is normative on the buyer rather than a line of script.
780
+
781
+ ---
782
+
783
+ ## 8. Checkpoints
784
+
785
+ Every `checkpointEvery` babels, `digest(State)` is anchored in a Kaspa lane at a measured cost of
786
+ 0.002 KAS. Checkpointing MUST be **non-blocking** — the session continues while it confirms,
787
+ because measured p90 latency is 1,879 ms.
788
+
789
+ **Checkpoints are evidence, not safety.** They prove a State existed before a given block, which
790
+ makes a stale close provable and attributable. They cannot prevent one: the covenant cannot recover
791
+ a State from a digest, so it cannot enforce a minimum `seq`. Prevention is §7.2's job.
792
+
793
+ ---
794
+
795
+ ## 9. Non-goals
796
+
797
+ - **Hidden reasoning tokens.** Not two-sided measurable. Permanent.
798
+ - **Watchtowers.** A party offline for the response window can be closed against unfairly.
799
+ - **Quality.** The meter counts units delivered. Whether they were any good is verification.
800
+ - **Routing or multi-hop.** Bilateral only.
801
+ - **Discovery, reputation, marketplaces.**
802
+ - **Fiat, stablecoins, cross-chain.** Native KAS in this version.
803
+ - **Privacy.** Checkpoints are public; session size and cadence leak.
804
+
805
+ ---
806
+
807
+ ## 10. Worked example
808
+
809
+ `worked-example.txt` is generated by `vectors.ts` using the reference encoder
810
+ (`src/encoding.ts`), with real BLAKE3 digests and real BIP340 signatures. It is a **golden vector**: an implementation that produces different bytes for the same
811
+ inputs is wrong.
812
+
813
+ It runs a six-babel session in which babel 2 exhibits the one-token divergence Study A measured
814
+ (absorbed by `toleranceAbs`, billed at 549), a checkpoint fires after babel 2, and babel 4 diverges
815
+ by 48 units and halts. Settlement is at State 3: **2,199 units, 7,982,370 sompi, 3,996,630 sompi
816
+ refunded.**
817
+
818
+ Regenerate with `npm run vectors`. CI regenerates it on every push and requires a
819
+ byte-for-byte match.
820
+
821
+ ---