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
@@ -0,0 +1,171 @@
1
+ /**
2
+ * The HTTP glue. Thin on purpose: every rule lives in provider.ts and service.ts, and this only
3
+ * maps them onto requests and status codes.
4
+ *
5
+ * ROUTING IS BY sessionId, AND IT COSTS NOTHING EXTRA. Every message in SPEC.md 3 already carries
6
+ * one, because the specification needed it to stop a Reservation or Measurement being lifted from
7
+ * one session into another -- threat X1. A field that exists for a security reason turns
8
+ * out to be exactly the routing key a multi-session server needs, so there is no session cookie,
9
+ * no header, and no server-side handle to leak or confuse.
10
+ *
11
+ * An unpaid request gets x402's 402 with the signed Offer in `accepts`. A client that speaks x402
12
+ * but not this scheme reads the envelope, sees `scheme: "metered"` and declines cleanly.
13
+ */
14
+ import { createServer } from 'node:http';
15
+ import { digestHex, verifyState } from '../encoding.js';
16
+ import { SessionRejected } from './provider.js';
17
+ import { ReservationUnauthenticated } from '../reservation.js';
18
+ import { toPaymentRequired, errorBody, toBase64 } from './protocol.js';
19
+ /**
20
+ * The largest request this server will read. A Reservation, a Measurement and a State are all a
21
+ * few hundred bytes; a prompt is the only field with any size to it.
22
+ *
23
+ * BOUNDED ON PURPOSE. Reading an unbounded body means an unauthenticated client can hold the
24
+ * process open and grow a buffer until it dies -- which costs the attacker nothing and does not
25
+ * require knowing any session. This is the cheapest denial of service there is, and the only
26
+ * defence is to refuse to read past a limit.
27
+ */
28
+ export const MAX_BODY_BYTES = 256 * 1024;
29
+ export class BodyTooLarge extends Error {
30
+ }
31
+ async function readJson(req) {
32
+ // Cheapest refusal first: a declared length over the cap needs no reading at all.
33
+ const declared = Number(req.headers['content-length'] ?? NaN);
34
+ if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {
35
+ throw new BodyTooLarge(`declared body of ${declared} bytes exceeds ${MAX_BODY_BYTES}`);
36
+ }
37
+ const parts = [];
38
+ let size = 0;
39
+ let over = false;
40
+ for await (const c of req) {
41
+ const buf = c;
42
+ size += buf.length;
43
+ if (size > MAX_BODY_BYTES) {
44
+ // STOP BUFFERING, KEEP DRAINING. Destroying the socket here loses the response: the client
45
+ // sees a dropped connection rather than a refusal, and cannot tell a limit from a crash.
46
+ // Memory stays bounded because nothing further is kept.
47
+ over = true;
48
+ parts.length = 0;
49
+ continue;
50
+ }
51
+ parts.push(buf);
52
+ }
53
+ if (over)
54
+ throw new BodyTooLarge(`request body exceeds ${MAX_BODY_BYTES} bytes`);
55
+ return JSON.parse(Buffer.concat(parts).toString('utf8'));
56
+ }
57
+ const NO_SESSION = ['no such session', 'unknown, halted or evicted'];
58
+ async function handleOpen(svc, req, reply, resource) {
59
+ const body = await readJson(req);
60
+ if (!body.buyerPubkey)
61
+ return reply(400, errorBody('open requires buyerPubkey'));
62
+ // The 402 IS the answer here, not an error: it carries the terms.
63
+ return reply(402, toPaymentRequired(svc.open(body.buyerPubkey), resource, 'metered session available'));
64
+ }
65
+ /**
66
+ * ONLY AN AUTHENTICATED COUNTERPARTY MAY HALT A SESSION.
67
+ *
68
+ * SPEC.md 1's "the only remedy for disagreement is to stop" is about two parties who have
69
+ * authenticated themselves and cannot agree on a measurement. It is NOT about a malformed message
70
+ * from an unknown sender -- and `sessionId` is not a secret, it is carried in clear in every
71
+ * message on the wire. Halting on any failure meant anyone who could observe a session id could
72
+ * permanently kill that session with one junk request, which is a denial of service wearing the
73
+ * specification as a disguise.
74
+ *
75
+ * So a failure to AUTHENTICATE is answered with a refusal and no change of state; a failure to
76
+ * AGREE, which only an authenticated party can reach, halts.
77
+ */
78
+ const isUnauthenticated = (err) => err instanceof ReservationUnauthenticated || err instanceof BodyTooLarge || err instanceof SyntaxError;
79
+ async function handleBabel(svc, req, reply) {
80
+ const body = await readJson(req);
81
+ const id = body.reservation?.sessionId;
82
+ const session = id ? svc.get(id) : undefined;
83
+ if (!session || !id)
84
+ return reply(404, errorBody(...NO_SESSION));
85
+ try {
86
+ const { content, measurement } = session.chunk(body.reservation, body.prompt);
87
+ svc.persist(id);
88
+ return reply(200, { contentB64: toBase64(content), measurement });
89
+ }
90
+ catch (err) {
91
+ if (isUnauthenticated(err))
92
+ return reply(403, errorBody('rejected', String(err)));
93
+ svc.halt(id);
94
+ throw err;
95
+ }
96
+ }
97
+ async function handleState(svc, req, reply) {
98
+ const body = await readJson(req);
99
+ const id = body.measurement?.sessionId;
100
+ const session = id ? svc.get(id) : undefined;
101
+ if (!session || !id)
102
+ return reply(404, errorBody(...NO_SESSION));
103
+ try {
104
+ const settled = session.settle(body.measurement);
105
+ svc.persist(id);
106
+ return reply(200, settled);
107
+ }
108
+ catch (err) {
109
+ if (isUnauthenticated(err))
110
+ return reply(403, errorBody('rejected', String(err)));
111
+ // A disagreement stops the session for good, not just this request (SPEC.md 1).
112
+ svc.halt(id);
113
+ throw err;
114
+ }
115
+ }
116
+ async function handleCountersign(svc, req, reply) {
117
+ const body = await readJson(req);
118
+ const id = body.state?.sessionId;
119
+ const session = id ? svc.get(id) : undefined;
120
+ const offer = id ? svc.offerFor(id) : undefined;
121
+ if (!session || !offer || !id)
122
+ return reply(404, errorBody(...NO_SESSION));
123
+ if (!verifyState(body.state, body.buyerSig, offer.buyerPubkey)) {
124
+ // NOT a halt. An unverifiable signature means the sender is not the buyer, so this message is
125
+ // evidence about the sender and none at all about the session. See isUnauthenticated above.
126
+ return reply(403, errorBody('the buyer signature does not verify'));
127
+ }
128
+ session.chainTo(digestHex(body.state));
129
+ svc.persist(id);
130
+ return reply(200, { chained: digestHex(body.state) });
131
+ }
132
+ const ROUTES = {
133
+ '/metered/open': handleOpen,
134
+ '/metered/babel': (s, r, reply) => handleBabel(s, r, reply),
135
+ '/metered/state': (s, r, reply) => handleState(s, r, reply),
136
+ '/metered/countersign': (s, r, reply) => handleCountersign(s, r, reply),
137
+ };
138
+ export function meteredHandler(opts) {
139
+ const resource = opts.resource ?? '/metered';
140
+ return async (req, res) => {
141
+ const reply = (code, body) => {
142
+ const payload = JSON.stringify(body);
143
+ res.writeHead(code, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(payload) });
144
+ res.end(payload);
145
+ };
146
+ try {
147
+ if (req.method !== 'POST')
148
+ return reply(405, errorBody('POST only'));
149
+ const route = ROUTES[req.url ?? ''];
150
+ if (!route)
151
+ return reply(404, errorBody('no such endpoint'));
152
+ await route(opts.service, req, reply, resource);
153
+ }
154
+ catch (err) {
155
+ if (err instanceof SessionRejected) {
156
+ // 409: the session cannot continue. SPEC.md 1 -- the only remedy for disagreement is to stop.
157
+ return reply(409, errorBody('session halted', err.message));
158
+ }
159
+ if (err instanceof BodyTooLarge)
160
+ return reply(413, errorBody('request too large', err.message));
161
+ return reply(400, errorBody('bad request', err instanceof Error ? err.message : String(err)));
162
+ }
163
+ };
164
+ }
165
+ export function serveMetered(opts) {
166
+ const handler = meteredHandler(opts);
167
+ return createServer((req, res) => {
168
+ void handler(req, res);
169
+ });
170
+ }
171
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/http/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { ProviderSession, type Deliver, type Meter } from './provider.js';
2
+ import { Checkpointer, type Anchor } from '../checkpoint.js';
3
+ import { type SignerStore } from '../signer.js';
4
+ import type { SessionStore } from '../store.js';
5
+ import type { Offer } from '../types.js';
6
+ /** Everything about an Offer except who the buyer is, which is not known until one asks. */
7
+ export type OfferTerms = Omit<Offer, 'sessionId' | 'buyerPubkey' | 'partiesCommitment' | 'sig' | 'providerPubkey'>;
8
+ export interface ServiceOptions {
9
+ terms: OfferTerms;
10
+ providerSk: string;
11
+ providerPubkey: string;
12
+ meter: Meter;
13
+ deliver: Deliver;
14
+ /** How many sessions to keep. The oldest is evicted past this, halted ones first. */
15
+ maxSessions?: number | undefined;
16
+ /**
17
+ * SPEC.md 8. Each session gets its OWN Checkpointer over this one anchor, so a session's
18
+ * checkpoint records stay with the session and are evicted with it.
19
+ */
20
+ anchor?: Anchor | undefined;
21
+ /**
22
+ * SPEC.md 4. Shared across sessions deliberately: SignerRecord is keyed by sessionId, so one
23
+ * store holds every session this provider has ever signed for -- which is exactly what 4.4's
24
+ * restart rule needs to consult. Defaults to memory; src/store.ts survives a restart.
25
+ */
26
+ store?: SignerStore | undefined;
27
+ /**
28
+ * SPEC.md has nothing to say about this: it is an implementation quality. Without it a restart
29
+ * forgets what has been OFFERED and every buyer opens again, losing at most the babel in
30
+ * flight. With it, a buyer does not notice.
31
+ */
32
+ sessions?: SessionStore | undefined;
33
+ }
34
+ export declare class MeteredService {
35
+ private readonly opts;
36
+ private readonly sessions;
37
+ /** One store for every session, because SPEC.md 4.4 asks what this PARTY has signed. */
38
+ private readonly store;
39
+ constructor(opts: ServiceOptions);
40
+ get size(): number;
41
+ /** Mint a session for one buyer and return its signed Offer. */
42
+ open(buyerPubkey: string): Offer;
43
+ /**
44
+ * The session for an id, or undefined. Callers turn undefined into a 404, not a crash.
45
+ *
46
+ * REHYDRATES on a miss when a durable session store is configured, so a buyer whose provider
47
+ * restarted mid-session carries on rather than starting again.
48
+ */
49
+ get(sessionId: string): ProviderSession | undefined;
50
+ /** Rebuild a session from the durable store, if one is configured and holds it. */
51
+ private rehydrate;
52
+ /**
53
+ * Write a session down after it has moved. Called by the handlers, because only they know a
54
+ * message was accepted -- persisting before that would record a state nobody agreed to.
55
+ */
56
+ persist(sessionId: string): void;
57
+ offerFor(sessionId: string): Offer | undefined;
58
+ /** A session's checkpoint records, for an operator asking what has been anchored. */
59
+ checkpointsFor(sessionId: string): ReturnType<Checkpointer['all']>;
60
+ /**
61
+ * Wait for a finished session's anchors to land, so a REPORT can say what became of them.
62
+ * Never call this while a session is running -- see Checkpointer.settled.
63
+ */
64
+ checkpointsSettled(sessionId: string): Promise<void>;
65
+ /**
66
+ * Mark a session dead. SPEC.md 1: "the only remedy for disagreement is to stop", and stopping
67
+ * has to be sticky -- a halted session that answers the next request has not stopped.
68
+ */
69
+ halt(sessionId: string): void;
70
+ isHalted(sessionId: string): boolean;
71
+ /** Keep the map bounded: halted sessions go first, then the oldest. */
72
+ /**
73
+ * Keep the session map bounded, dropping the least valuable first.
74
+ *
75
+ * ORDER MATTERS UNDER ATTACK, and the obvious order is the wrong one. Opening a session is free
76
+ * and unauthenticated -- anyone may ask for terms -- so an attacker can open sessions as fast as
77
+ * it likes. Evicting purely by age hands it a weapon: its fresh sessions survive while real
78
+ * buyers, who are older by definition, get dropped mid-session.
79
+ *
80
+ * So progress is what is protected. Halted sessions go first, then sessions that have never
81
+ * delivered a babel -- which is exactly what a flood consists of -- and only then the oldest.
82
+ * A buyer partway through a session is the last thing to be thrown away.
83
+ */
84
+ private evict;
85
+ }
86
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/http/service.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,4FAA4F;AAC5F,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,GAAG,mBAAmB,GAAG,KAAK,GAAG,gBAAgB,CAAC,CAAC;AAEnH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAEhC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACrC;AAUD,qBAAa,cAAc;IAMb,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IAErD,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;gBAEP,IAAI,EAAE,cAAc;IAIjD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,gEAAgE;IAChE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,KAAK;IA+BhC;;;;;OAKG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAMnD,mFAAmF;IACnF,OAAO,CAAC,SAAS;IAiBjB;;;OAGG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKhC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAI9C,qFAAqF;IACrF,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAIlE;;;OAGG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D;;;OAGG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAO7B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIpC,uEAAuE;IACvE;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,KAAK;CAcd"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Many sessions at once, which is the difference between a demo and a server.
3
+ *
4
+ * WHY `/open` TAKES THE BUYER'S KEY. An Offer names `buyerPubkey` and commits to
5
+ * `blake3(buyer || provider)`, so it cannot be signed until the provider knows who is asking.
6
+ * The single-session version dodged this by having the Offer pre-made with both keys baked in,
7
+ * which quietly meant a server could only ever serve one buyer. Asking for the key at open is
8
+ * what makes the commitment honest AND makes many sessions possible; the two were the same
9
+ * problem.
10
+ *
11
+ * WHAT PERSISTS AND WHAT DOES NOT. Sessions live in memory, so a restart forgets what has been
12
+ * OFFERED and every buyer must open again. That is survivable because a Reservation authorises
13
+ * ONE chunk -- the most anyone loses to a forgotten session is that chunk.
14
+ *
15
+ * What has been SIGNED is a different matter, and forgetting it loses money. SPEC.md 4 is carried
16
+ * by a SignerStore threaded through every session here. Until 2026-09-10 it was not: src/signer.ts
17
+ * implemented all four obligations, src/session.ts used them, and NOTHING ELSE DID -- this server
18
+ * called `signState` directly, so the rules that exist to stop a party signing two States at one
19
+ * seq were running only in session.ts's own test. Passing a store from src/store.ts is what makes
20
+ * 4.4's restart rule true rather than available.
21
+ */
22
+ import { randomBytes } from 'node:crypto';
23
+ import { bytesToHex } from '@noble/hashes/utils';
24
+ import { signEnvelope, partiesCommitment } from '../encoding.js';
25
+ import { ProviderSession } from './provider.js';
26
+ import { Checkpointer } from '../checkpoint.js';
27
+ import { memoryStore } from '../signer.js';
28
+ export class MeteredService {
29
+ opts;
30
+ sessions = new Map();
31
+ /** One store for every session, because SPEC.md 4.4 asks what this PARTY has signed. */
32
+ store;
33
+ constructor(opts) {
34
+ this.opts = opts;
35
+ this.store = opts.store ?? memoryStore();
36
+ }
37
+ get size() {
38
+ return this.sessions.size;
39
+ }
40
+ /** Mint a session for one buyer and return its signed Offer. */
41
+ open(buyerPubkey) {
42
+ if (!/^[0-9a-f]{64}$/i.test(buyerPubkey))
43
+ throw new Error('buyerPubkey must be 32 hex bytes');
44
+ this.evict();
45
+ const sessionId = bytesToHex(randomBytes(16)); // SPEC.md 3.1: 16 bytes
46
+ const parties = partiesCommitment(buyerPubkey, this.opts.providerPubkey);
47
+ const offer = signEnvelope({
48
+ ...this.opts.terms,
49
+ sessionId,
50
+ buyerPubkey: buyerPubkey.toLowerCase(),
51
+ providerPubkey: this.opts.providerPubkey,
52
+ partiesCommitment: parties,
53
+ }, this.opts.providerSk);
54
+ const checkpointer = this.opts.anchor ? new Checkpointer(this.opts.anchor) : undefined;
55
+ this.sessions.set(sessionId, {
56
+ offer,
57
+ session: new ProviderSession(offer, this.opts.providerSk, this.opts.meter, this.opts.deliver, checkpointer, this.store),
58
+ checkpointer,
59
+ opened: Date.now(),
60
+ halted: false,
61
+ });
62
+ this.persist(sessionId);
63
+ return offer;
64
+ }
65
+ /**
66
+ * The session for an id, or undefined. Callers turn undefined into a 404, not a crash.
67
+ *
68
+ * REHYDRATES on a miss when a durable session store is configured, so a buyer whose provider
69
+ * restarted mid-session carries on rather than starting again.
70
+ */
71
+ get(sessionId) {
72
+ const entry = this.sessions.get(sessionId) ?? this.rehydrate(sessionId);
73
+ if (!entry || entry.halted)
74
+ return undefined;
75
+ return entry.session;
76
+ }
77
+ /** Rebuild a session from the durable store, if one is configured and holds it. */
78
+ rehydrate(sessionId) {
79
+ const snapshot = this.opts.sessions?.load(sessionId);
80
+ if (!snapshot)
81
+ return undefined;
82
+ const checkpointer = this.opts.anchor ? new Checkpointer(this.opts.anchor) : undefined;
83
+ const entry = {
84
+ offer: snapshot.offer,
85
+ session: ProviderSession.restore(snapshot, this.opts.providerSk, this.opts.meter, this.opts.deliver, checkpointer, this.store),
86
+ checkpointer,
87
+ opened: Date.now(),
88
+ halted: false,
89
+ };
90
+ this.sessions.set(sessionId, entry);
91
+ return entry;
92
+ }
93
+ /**
94
+ * Write a session down after it has moved. Called by the handlers, because only they know a
95
+ * message was accepted -- persisting before that would record a state nobody agreed to.
96
+ */
97
+ persist(sessionId) {
98
+ const entry = this.sessions.get(sessionId);
99
+ if (entry && this.opts.sessions)
100
+ this.opts.sessions.save(entry.session.snapshot());
101
+ }
102
+ offerFor(sessionId) {
103
+ return this.sessions.get(sessionId)?.offer;
104
+ }
105
+ /** A session's checkpoint records, for an operator asking what has been anchored. */
106
+ checkpointsFor(sessionId) {
107
+ return this.sessions.get(sessionId)?.checkpointer?.all() ?? [];
108
+ }
109
+ /**
110
+ * Wait for a finished session's anchors to land, so a REPORT can say what became of them.
111
+ * Never call this while a session is running -- see Checkpointer.settled.
112
+ */
113
+ async checkpointsSettled(sessionId) {
114
+ await this.sessions.get(sessionId)?.checkpointer?.settled();
115
+ }
116
+ /**
117
+ * Mark a session dead. SPEC.md 1: "the only remedy for disagreement is to stop", and stopping
118
+ * has to be sticky -- a halted session that answers the next request has not stopped.
119
+ */
120
+ halt(sessionId) {
121
+ const entry = this.sessions.get(sessionId);
122
+ if (entry)
123
+ entry.halted = true;
124
+ // A halt must survive a restart too, or the remedy stops working the moment a process does.
125
+ this.opts.sessions?.drop(sessionId);
126
+ }
127
+ isHalted(sessionId) {
128
+ return this.sessions.get(sessionId)?.halted ?? false;
129
+ }
130
+ /** Keep the map bounded: halted sessions go first, then the oldest. */
131
+ /**
132
+ * Keep the session map bounded, dropping the least valuable first.
133
+ *
134
+ * ORDER MATTERS UNDER ATTACK, and the obvious order is the wrong one. Opening a session is free
135
+ * and unauthenticated -- anyone may ask for terms -- so an attacker can open sessions as fast as
136
+ * it likes. Evicting purely by age hands it a weapon: its fresh sessions survive while real
137
+ * buyers, who are older by definition, get dropped mid-session.
138
+ *
139
+ * So progress is what is protected. Halted sessions go first, then sessions that have never
140
+ * delivered a babel -- which is exactly what a flood consists of -- and only then the oldest.
141
+ * A buyer partway through a session is the last thing to be thrown away.
142
+ */
143
+ evict() {
144
+ const max = this.opts.maxSessions ?? 1000;
145
+ if (this.sessions.size < max)
146
+ return;
147
+ const rank = (e) => {
148
+ if (e.halted)
149
+ return 0;
150
+ return e.session.pendingSeq < 0 ? 1 : 2;
151
+ };
152
+ const entries = [...this.sessions.entries()].sort((a, b) => {
153
+ const byRank = rank(a[1]) - rank(b[1]);
154
+ return byRank !== 0 ? byRank : a[1].opened - b[1].opened;
155
+ });
156
+ const drop = this.sessions.size - max + 1;
157
+ for (const [id] of entries.slice(0, drop))
158
+ this.sessions.delete(id);
159
+ }
160
+ }
161
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/http/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * What a program using this protocol is allowed to reach for.
3
+ *
4
+ * THIS FILE EXISTS BECAUSE SOMETHING TRIED TO USE THE PROTOCOL. Until then every import in the
5
+ * repository was relative and internal, so the question "what is the public surface?" had never
6
+ * been asked, and the honest answer was "all of it" -- which means any rearrangement inside `src/`
7
+ * is a breaking change for somebody.
8
+ *
9
+ * So this is the whole of what is promised. Anything not re-exported here is internal and may move
10
+ * without warning. Deliberately absent: the reconciliation internals, the bias detector, and the
11
+ * canonical encoder's private helpers.
12
+ *
13
+ * SETTLEMENT IS NOT ABSENT, IT IS ELSEWHERE -- `metered/chain`. An earlier version of this comment
14
+ * called the chain tooling "a demonstration of the covenant rather than part of the protocol a
15
+ * caller links against", and the first product built on this protocol falsified that within the
16
+ * day: the covenant is what makes the signed numbers enforceable, so of course a caller needs it.
17
+ * The split is about COST, not importance. Everything in this file is plain TypeScript over the
18
+ * messages; everything behind `metered/chain` needs a WASM SDK and a contract compiler, and a
19
+ * buyer that only speaks the protocol should not pay for those to do it.
20
+ *
21
+ * The list is short on purpose. A surface is a promise, and every name added here is one more
22
+ * thing that cannot be changed later without telling someone.
23
+ */
24
+ /** Building and running a provider. */
25
+ export { MeteredService, type OfferTerms, type ServiceOptions } from './http/service.js';
26
+ export { serveMetered, meteredHandler, BodyTooLarge } from './http/serve.js';
27
+ export { ProviderSession, SessionRejected, type Deliver, type Meter } from './http/provider.js';
28
+ /** Being a buyer. */
29
+ export { BuyerSession, BuyerRefused } from './http/buyer.js';
30
+ export { openSession, runBabel, readOffer, ProtocolError, type ChunkOutcome } from './http/client.js';
31
+ /** The wire, for anything speaking it directly rather than through the client. */
32
+ export { toBase64, fromBase64, toPaymentRequired, X402_VERSION, type BabelResponse, type ChunkRequest, type StateRequest, type PaymentRequiredBody, } from './http/protocol.js';
33
+ /** Units and meters (SPEC.md 6). */
34
+ export { resolveMeter, meterFor, minimumTolerance, available, encodeWith, MeterUnavailable } from './meter.js';
35
+ /** Durability. A provider that holds money must not forget what it has signed (SPEC.md 4). */
36
+ export { fileStore, fileHistory, fileSessionStore, type SessionStore } from './store.js';
37
+ export { memoryStore, SignerObligationError, type SignerStore, type SignerRecord } from './signer.js';
38
+ export { memoryHistory, type SessionHistory } from './history.js';
39
+ /**
40
+ * The types an implementable interface is made of.
41
+ *
42
+ * These are here because leaving them out made the surface LOOK complete and be unusable:
43
+ * `ServiceOptions` accepts an `anchor` and a `sessions` store, and neither the `Anchor` interface
44
+ * nor `SessionStore`'s snapshot type could be reached, so a caller could see where its own
45
+ * implementation was meant to go and had no way to write one. A type named in an exported
46
+ * signature is part of the surface whether or not anybody remembered to say so.
47
+ */
48
+ export { Checkpointer, isCheckpointBabel, type Anchor, type CheckpointRecord } from './checkpoint.js';
49
+ export type { SessionSnapshot } from './http/provider.js';
50
+ export type { BabelCursor } from './reservation.js';
51
+ export type { BiasState } from './bias.js';
52
+ export { ReservationRejected, ReservationUnauthenticated, acceptReservation } from './reservation.js';
53
+ export { MAX_BODY_BYTES, type ServeOptions } from './http/serve.js';
54
+ export type { StateResponse, ErrorBody } from './http/protocol.js';
55
+ /**
56
+ * SPEC.md 7.3a. The response window is the PROVIDER'S DEADLINE: once the covenant ages past it
57
+ * with no claim pending, the buyer can take back everything, including work already delivered.
58
+ * A provider that never asks itself when to settle will eventually deliver for free.
59
+ */
60
+ export { shouldSettle, acceptPolicy, worstCaseExposure, PolicyRejected, CONFIRM_MARGIN_DAA, type ExposurePolicy, type Exposure, } from './deadline.js';
61
+ /** Accepting an Offer, and what a session costs to fund (SPEC.md 3.1, 7.4b). */
62
+ export { acceptOffer, OfferRejected } from './offer.js';
63
+ export { priceOf, requiredFunding, CLOSE_FEE_SOMPI, MIN_COVENANT_SOMPI } from './reservation.js';
64
+ /** Signing and digesting, for a caller that builds messages itself. */
65
+ export { publicKeyHex, signEnvelope, signState, verify, verifyState, canonicalize, digestHex, blake3Hex, utf8, settlementPreimage, partiesCommitment, stateSigningPayload, settlementDigest, } from './encoding.js';
66
+ /** The messages themselves. */
67
+ export type { Offer, Reservation, Measurement, State, Halt, HaltReason, Reconciled } from './types.js';
68
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,uCAAuC;AACvC,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhG,qBAAqB;AACrB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEtG,kFAAkF;AAClF,OAAO,EACL,QAAQ,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,EACrD,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,mBAAmB,GACnF,MAAM,oBAAoB,CAAC;AAE5B,oCAAoC;AACpC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE/G,8FAA8F;AAC9F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,KAAK,MAAM,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACtG,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEnE;;;;GAIG;AACH,OAAO,EACL,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,EACjF,KAAK,cAAc,EAAE,KAAK,QAAQ,GACnC,MAAM,eAAe,CAAC;AAEvB,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEjG,uEAAuE;AACvE,OAAO,EACL,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAC1D,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAC5D,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,GACzD,MAAM,eAAe,CAAC;AAEvB,+BAA+B;AAC/B,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * What a program using this protocol is allowed to reach for.
3
+ *
4
+ * THIS FILE EXISTS BECAUSE SOMETHING TRIED TO USE THE PROTOCOL. Until then every import in the
5
+ * repository was relative and internal, so the question "what is the public surface?" had never
6
+ * been asked, and the honest answer was "all of it" -- which means any rearrangement inside `src/`
7
+ * is a breaking change for somebody.
8
+ *
9
+ * So this is the whole of what is promised. Anything not re-exported here is internal and may move
10
+ * without warning. Deliberately absent: the reconciliation internals, the bias detector, and the
11
+ * canonical encoder's private helpers.
12
+ *
13
+ * SETTLEMENT IS NOT ABSENT, IT IS ELSEWHERE -- `metered/chain`. An earlier version of this comment
14
+ * called the chain tooling "a demonstration of the covenant rather than part of the protocol a
15
+ * caller links against", and the first product built on this protocol falsified that within the
16
+ * day: the covenant is what makes the signed numbers enforceable, so of course a caller needs it.
17
+ * The split is about COST, not importance. Everything in this file is plain TypeScript over the
18
+ * messages; everything behind `metered/chain` needs a WASM SDK and a contract compiler, and a
19
+ * buyer that only speaks the protocol should not pay for those to do it.
20
+ *
21
+ * The list is short on purpose. A surface is a promise, and every name added here is one more
22
+ * thing that cannot be changed later without telling someone.
23
+ */
24
+ /** Building and running a provider. */
25
+ export { MeteredService } from './http/service.js';
26
+ export { serveMetered, meteredHandler, BodyTooLarge } from './http/serve.js';
27
+ export { ProviderSession, SessionRejected } from './http/provider.js';
28
+ /** Being a buyer. */
29
+ export { BuyerSession, BuyerRefused } from './http/buyer.js';
30
+ export { openSession, runBabel, readOffer, ProtocolError } from './http/client.js';
31
+ /** The wire, for anything speaking it directly rather than through the client. */
32
+ export { toBase64, fromBase64, toPaymentRequired, X402_VERSION, } from './http/protocol.js';
33
+ /** Units and meters (SPEC.md 6). */
34
+ export { resolveMeter, meterFor, minimumTolerance, available, encodeWith, MeterUnavailable } from './meter.js';
35
+ /** Durability. A provider that holds money must not forget what it has signed (SPEC.md 4). */
36
+ export { fileStore, fileHistory, fileSessionStore } from './store.js';
37
+ export { memoryStore, SignerObligationError } from './signer.js';
38
+ export { memoryHistory } from './history.js';
39
+ /**
40
+ * The types an implementable interface is made of.
41
+ *
42
+ * These are here because leaving them out made the surface LOOK complete and be unusable:
43
+ * `ServiceOptions` accepts an `anchor` and a `sessions` store, and neither the `Anchor` interface
44
+ * nor `SessionStore`'s snapshot type could be reached, so a caller could see where its own
45
+ * implementation was meant to go and had no way to write one. A type named in an exported
46
+ * signature is part of the surface whether or not anybody remembered to say so.
47
+ */
48
+ export { Checkpointer, isCheckpointBabel } from './checkpoint.js';
49
+ export { ReservationRejected, ReservationUnauthenticated, acceptReservation } from './reservation.js';
50
+ export { MAX_BODY_BYTES } from './http/serve.js';
51
+ /**
52
+ * SPEC.md 7.3a. The response window is the PROVIDER'S DEADLINE: once the covenant ages past it
53
+ * with no claim pending, the buyer can take back everything, including work already delivered.
54
+ * A provider that never asks itself when to settle will eventually deliver for free.
55
+ */
56
+ export { shouldSettle, acceptPolicy, worstCaseExposure, PolicyRejected, CONFIRM_MARGIN_DAA, } from './deadline.js';
57
+ /** Accepting an Offer, and what a session costs to fund (SPEC.md 3.1, 7.4b). */
58
+ export { acceptOffer, OfferRejected } from './offer.js';
59
+ export { priceOf, requiredFunding, CLOSE_FEE_SOMPI, MIN_COVENANT_SOMPI } from './reservation.js';
60
+ /** Signing and digesting, for a caller that builds messages itself. */
61
+ export { publicKeyHex, signEnvelope, signState, verify, verifyState, canonicalize, digestHex, blake3Hex, utf8, settlementPreimage, partiesCommitment, stateSigningPayload, settlementDigest, } from './encoding.js';
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,uCAAuC;AACvC,OAAO,EAAE,cAAc,EAAwC,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,eAAe,EAA4B,MAAM,oBAAoB,CAAC;AAEhG,qBAAqB;AACrB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAqB,MAAM,kBAAkB,CAAC;AAEtG,kFAAkF;AAClF,OAAO,EACL,QAAQ,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,GAEtD,MAAM,oBAAoB,CAAC;AAE5B,oCAAoC;AACpC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE/G,8FAA8F;AAC9F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAqB,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAuC,MAAM,aAAa,CAAC;AACtG,OAAO,EAAE,aAAa,EAAuB,MAAM,cAAc,CAAC;AAElE;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAsC,MAAM,iBAAiB,CAAC;AAItG,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,EAAE,cAAc,EAAqB,MAAM,iBAAiB,CAAC;AAGpE;;;;GAIG;AACH,OAAO,EACL,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,GAElF,MAAM,eAAe,CAAC;AAEvB,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEjG,uEAAuE;AACvE,OAAO,EACL,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAC1D,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAC5D,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,GACzD,MAAM,eAAe,CAAC"}
@@ -0,0 +1,39 @@
1
+ export declare class MeterUnavailable extends Error {
2
+ }
3
+ export interface Meter {
4
+ /** The name an Offer carries. */
5
+ name: string;
6
+ /** The unit this meter measures. An Offer naming a different unit is refused. */
7
+ unit: string;
8
+ /** Content in, units out. BYTES in: a meter measures what was delivered, and what is
9
+ * delivered is bytes. A meter for a text unit decodes them itself. */
10
+ count(content: Uint8Array): number;
11
+ /**
12
+ * Whether two correct implementations ALWAYS reach the same number for the same bytes.
13
+ * `true` permits `toleranceAbs` of 0; `false` requires at least 1.
14
+ */
15
+ exact: boolean;
16
+ }
17
+ /**
18
+ * The meter an Offer names, or throw.
19
+ *
20
+ * NO FALLBACK, DELIBERATELY. Counting with a meter other than the one agreed produces a number
21
+ * that looks like a measurement, settles like one, and is not one. §3.1 requires an Offer whose
22
+ * meter cannot be obtained to be refused, and this is where that refusal happens.
23
+ */
24
+ export declare function resolveMeter(name: string, unit?: string): Meter;
25
+ /**
26
+ * The smallest tolerance an Offer may carry for this meter.
27
+ *
28
+ * SPEC.md §3.1. An exact meter permits 0, and should use it: honest divergence is impossible, so
29
+ * any tolerance is pure shaving room. An inexact meter requires at least 1, because Study A
30
+ * measured a one-token disagreement between honest parties and a zero tolerance would halt them.
31
+ */
32
+ export declare const minimumTolerance: (meter: Meter) => number;
33
+ /** A counting function for the named meter, for callers that want only the number. */
34
+ export declare function meterFor(name: string, unit?: string): (content: Uint8Array) => number;
35
+ /** Token ids, for the conformance comparison that pins boundaries rather than totals. */
36
+ export declare function encodeWith(name: string, text: string): number[];
37
+ /** Every meter this implementation can serve, for a provider building an Offer. */
38
+ export declare const available: () => string[];
39
+ //# sourceMappingURL=meter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meter.d.ts","sourceRoot":"","sources":["../../src/meter.ts"],"names":[],"mappings":"AA6BA,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAE9C,MAAM,WAAW,KAAK;IACpB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb;0EACsE;IACtE,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAAC;IACnC;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAmCD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAc/D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,KAAG,MAA+B,CAAC;AAEhF,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,MAAM,CAGrF;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/D;AAED,mFAAmF;AACnF,eAAO,MAAM,SAAS,QAAO,MAAM,EAA2B,CAAC"}