@drm3/sdk 0.1.2

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 (228) hide show
  1. package/LICENSE +75 -0
  2. package/README.md +426 -0
  3. package/bin/drm3.js +255 -0
  4. package/dist/alerts-bell.d.ts +7 -0
  5. package/dist/alerts-bell.d.ts.map +1 -0
  6. package/dist/alerts-bell.js +118 -0
  7. package/dist/alerts-bell.js.map +1 -0
  8. package/dist/alerts-fetch.d.ts +41 -0
  9. package/dist/alerts-fetch.d.ts.map +1 -0
  10. package/dist/alerts-fetch.js +60 -0
  11. package/dist/alerts-fetch.js.map +1 -0
  12. package/dist/alerts-styles.d.ts +4 -0
  13. package/dist/alerts-styles.d.ts.map +1 -0
  14. package/dist/alerts-styles.js +49 -0
  15. package/dist/alerts-styles.js.map +1 -0
  16. package/dist/alerts.d.ts +3 -0
  17. package/dist/alerts.d.ts.map +1 -0
  18. package/dist/alerts.global.js +206 -0
  19. package/dist/alerts.global.text.js +2 -0
  20. package/dist/alerts.js +21 -0
  21. package/dist/alerts.js.map +1 -0
  22. package/dist/index.d.ts +64 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +117 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/keys-canonical.d.ts +7 -0
  27. package/dist/keys-canonical.d.ts.map +1 -0
  28. package/dist/keys-canonical.js +57 -0
  29. package/dist/keys-canonical.js.map +1 -0
  30. package/dist/keys-generate.d.ts +39 -0
  31. package/dist/keys-generate.d.ts.map +1 -0
  32. package/dist/keys-generate.js +39 -0
  33. package/dist/keys-generate.js.map +1 -0
  34. package/dist/keys-verify.d.ts +62 -0
  35. package/dist/keys-verify.d.ts.map +1 -0
  36. package/dist/keys-verify.js +68 -0
  37. package/dist/keys-verify.js.map +1 -0
  38. package/dist/keys.d.ts +4 -0
  39. package/dist/keys.d.ts.map +1 -0
  40. package/dist/keys.js +22 -0
  41. package/dist/keys.js.map +1 -0
  42. package/dist/meter/adapter.d.ts +35 -0
  43. package/dist/meter/adapter.d.ts.map +1 -0
  44. package/dist/meter/adapter.js +0 -0
  45. package/dist/meter/adapter.js.map +1 -0
  46. package/dist/meter/cascade.d.ts +56 -0
  47. package/dist/meter/cascade.d.ts.map +1 -0
  48. package/dist/meter/cascade.js +64 -0
  49. package/dist/meter/cascade.js.map +1 -0
  50. package/dist/meter/client.d.ts +78 -0
  51. package/dist/meter/client.d.ts.map +1 -0
  52. package/dist/meter/client.js +84 -0
  53. package/dist/meter/client.js.map +1 -0
  54. package/dist/meter/core.d.ts +37 -0
  55. package/dist/meter/core.d.ts.map +1 -0
  56. package/dist/meter/core.js +49 -0
  57. package/dist/meter/core.js.map +1 -0
  58. package/dist/meter/gate-client.d.ts +94 -0
  59. package/dist/meter/gate-client.d.ts.map +1 -0
  60. package/dist/meter/gate-client.js +77 -0
  61. package/dist/meter/gate-client.js.map +1 -0
  62. package/dist/meter/gate-decide.d.ts +61 -0
  63. package/dist/meter/gate-decide.d.ts.map +1 -0
  64. package/dist/meter/gate-decide.js +54 -0
  65. package/dist/meter/gate-decide.js.map +1 -0
  66. package/dist/meter/gate.d.ts +10 -0
  67. package/dist/meter/gate.d.ts.map +1 -0
  68. package/dist/meter/gate.js +10 -0
  69. package/dist/meter/gate.js.map +1 -0
  70. package/dist/meter/index.d.ts +28 -0
  71. package/dist/meter/index.d.ts.map +1 -0
  72. package/dist/meter/index.js +28 -0
  73. package/dist/meter/index.js.map +1 -0
  74. package/dist/meter/meters-buckets.d.ts +56 -0
  75. package/dist/meter/meters-buckets.d.ts.map +1 -0
  76. package/dist/meter/meters-buckets.js +111 -0
  77. package/dist/meter/meters-buckets.js.map +1 -0
  78. package/dist/meter/meters-charge.d.ts +13 -0
  79. package/dist/meter/meters-charge.d.ts.map +1 -0
  80. package/dist/meter/meters-charge.js +76 -0
  81. package/dist/meter/meters-charge.js.map +1 -0
  82. package/dist/meter/meters.d.ts +63 -0
  83. package/dist/meter/meters.d.ts.map +1 -0
  84. package/dist/meter/meters.js +44 -0
  85. package/dist/meter/meters.js.map +1 -0
  86. package/dist/meter/plans.d.ts +63 -0
  87. package/dist/meter/plans.d.ts.map +1 -0
  88. package/dist/meter/plans.js +114 -0
  89. package/dist/meter/plans.js.map +1 -0
  90. package/dist/meter/pricebook.d.ts +32 -0
  91. package/dist/meter/pricebook.d.ts.map +1 -0
  92. package/dist/meter/pricebook.js +38 -0
  93. package/dist/meter/pricebook.js.map +1 -0
  94. package/dist/meter/resolve.d.ts +103 -0
  95. package/dist/meter/resolve.d.ts.map +1 -0
  96. package/dist/meter/resolve.js +100 -0
  97. package/dist/meter/resolve.js.map +1 -0
  98. package/dist/meter/sink.d.ts +26 -0
  99. package/dist/meter/sink.d.ts.map +1 -0
  100. package/dist/meter/sink.js +9 -0
  101. package/dist/meter/sink.js.map +1 -0
  102. package/dist/meter/sql/d1.d.ts +32 -0
  103. package/dist/meter/sql/d1.d.ts.map +1 -0
  104. package/dist/meter/sql/d1.js +41 -0
  105. package/dist/meter/sql/d1.js.map +1 -0
  106. package/dist/meter/sql/driver.d.ts +34 -0
  107. package/dist/meter/sql/driver.d.ts.map +1 -0
  108. package/dist/meter/sql/driver.js +17 -0
  109. package/dist/meter/sql/driver.js.map +1 -0
  110. package/dist/meter/sql/postgres.d.ts +23 -0
  111. package/dist/meter/sql/postgres.d.ts.map +1 -0
  112. package/dist/meter/sql/postgres.js +36 -0
  113. package/dist/meter/sql/postgres.js.map +1 -0
  114. package/dist/meter/sql/schema.d.ts +13 -0
  115. package/dist/meter/sql/schema.d.ts.map +1 -0
  116. package/dist/meter/sql/schema.js +32 -0
  117. package/dist/meter/sql/schema.js.map +1 -0
  118. package/dist/meter/sql/sql-adapter.d.ts +36 -0
  119. package/dist/meter/sql/sql-adapter.d.ts.map +1 -0
  120. package/dist/meter/sql/sql-adapter.js +124 -0
  121. package/dist/meter/sql/sql-adapter.js.map +1 -0
  122. package/dist/meter/sql/statements.d.ts +30 -0
  123. package/dist/meter/sql/statements.d.ts.map +1 -0
  124. package/dist/meter/sql/statements.js +24 -0
  125. package/dist/meter/sql/statements.js.map +1 -0
  126. package/dist/meter/types.d.ts +48 -0
  127. package/dist/meter/types.d.ts.map +1 -0
  128. package/dist/meter/types.js +8 -0
  129. package/dist/meter/types.js.map +1 -0
  130. package/dist/meter/units.d.ts +33 -0
  131. package/dist/meter/units.d.ts.map +1 -0
  132. package/dist/meter/units.js +45 -0
  133. package/dist/meter/units.js.map +1 -0
  134. package/dist/meter/wallet-buckets.d.ts +63 -0
  135. package/dist/meter/wallet-buckets.d.ts.map +1 -0
  136. package/dist/meter/wallet-buckets.js +131 -0
  137. package/dist/meter/wallet-buckets.js.map +1 -0
  138. package/dist/meter/wallet-charge.d.ts +15 -0
  139. package/dist/meter/wallet-charge.d.ts.map +1 -0
  140. package/dist/meter/wallet-charge.js +64 -0
  141. package/dist/meter/wallet-charge.js.map +1 -0
  142. package/dist/meter/wallet.d.ts +68 -0
  143. package/dist/meter/wallet.d.ts.map +1 -0
  144. package/dist/meter/wallet.js +47 -0
  145. package/dist/meter/wallet.js.map +1 -0
  146. package/dist/receipts-api.d.ts +5 -0
  147. package/dist/receipts-api.d.ts.map +1 -0
  148. package/dist/receipts-api.js +117 -0
  149. package/dist/receipts-api.js.map +1 -0
  150. package/dist/receipts-chain.d.ts +3 -0
  151. package/dist/receipts-chain.d.ts.map +1 -0
  152. package/dist/receipts-chain.js +121 -0
  153. package/dist/receipts-chain.js.map +1 -0
  154. package/dist/receipts-modal.d.ts +14 -0
  155. package/dist/receipts-modal.d.ts.map +1 -0
  156. package/dist/receipts-modal.js +100 -0
  157. package/dist/receipts-modal.js.map +1 -0
  158. package/dist/receipts-registry.d.ts +4 -0
  159. package/dist/receipts-registry.d.ts.map +1 -0
  160. package/dist/receipts-registry.js +54 -0
  161. package/dist/receipts-registry.js.map +1 -0
  162. package/dist/receipts-render.d.ts +9 -0
  163. package/dist/receipts-render.d.ts.map +1 -0
  164. package/dist/receipts-render.js +83 -0
  165. package/dist/receipts-render.js.map +1 -0
  166. package/dist/receipts-types.d.ts +61 -0
  167. package/dist/receipts-types.d.ts.map +1 -0
  168. package/dist/receipts-types.js +12 -0
  169. package/dist/receipts-types.js.map +1 -0
  170. package/dist/receipts-ui.d.ts +10 -0
  171. package/dist/receipts-ui.d.ts.map +1 -0
  172. package/dist/receipts-ui.js +103 -0
  173. package/dist/receipts-ui.js.map +1 -0
  174. package/dist/receipts-verify.d.ts +8 -0
  175. package/dist/receipts-verify.d.ts.map +1 -0
  176. package/dist/receipts-verify.js +113 -0
  177. package/dist/receipts-verify.js.map +1 -0
  178. package/dist/receipts.d.ts +9 -0
  179. package/dist/receipts.d.ts.map +1 -0
  180. package/dist/receipts.global.js +587 -0
  181. package/dist/receipts.global.text.js +2 -0
  182. package/dist/receipts.js +28 -0
  183. package/dist/receipts.js.map +1 -0
  184. package/dist/sso/index.d.ts +4 -0
  185. package/dist/sso/index.d.ts.map +1 -0
  186. package/dist/sso/index.js +10 -0
  187. package/dist/sso/index.js.map +1 -0
  188. package/dist/sso/jwt.d.ts +29 -0
  189. package/dist/sso/jwt.d.ts.map +1 -0
  190. package/dist/sso/jwt.js +73 -0
  191. package/dist/sso/jwt.js.map +1 -0
  192. package/dist/sso/launch.d.ts +70 -0
  193. package/dist/sso/launch.d.ts.map +1 -0
  194. package/dist/sso/launch.js +75 -0
  195. package/dist/sso/launch.js.map +1 -0
  196. package/dist/sso/revocation.d.ts +23 -0
  197. package/dist/sso/revocation.d.ts.map +1 -0
  198. package/dist/sso/revocation.js +53 -0
  199. package/dist/sso/revocation.js.map +1 -0
  200. package/dist/switcher.d.ts +22 -0
  201. package/dist/switcher.d.ts.map +1 -0
  202. package/dist/switcher.js +174 -0
  203. package/dist/switcher.js.map +1 -0
  204. package/dist/x402/chain.d.ts +49 -0
  205. package/dist/x402/chain.d.ts.map +1 -0
  206. package/dist/x402/chain.js +33 -0
  207. package/dist/x402/chain.js.map +1 -0
  208. package/dist/x402/crypto.d.ts +25 -0
  209. package/dist/x402/crypto.d.ts.map +1 -0
  210. package/dist/x402/crypto.js +74 -0
  211. package/dist/x402/crypto.js.map +1 -0
  212. package/dist/x402/eip712.d.ts +20 -0
  213. package/dist/x402/eip712.d.ts.map +1 -0
  214. package/dist/x402/eip712.js +49 -0
  215. package/dist/x402/eip712.js.map +1 -0
  216. package/dist/x402/envelope.d.ts +45 -0
  217. package/dist/x402/envelope.d.ts.map +1 -0
  218. package/dist/x402/envelope.js +50 -0
  219. package/dist/x402/envelope.js.map +1 -0
  220. package/dist/x402/index.d.ts +41 -0
  221. package/dist/x402/index.d.ts.map +1 -0
  222. package/dist/x402/index.js +41 -0
  223. package/dist/x402/index.js.map +1 -0
  224. package/dist/x402/verify.d.ts +51 -0
  225. package/dist/x402/verify.d.ts.map +1 -0
  226. package/dist/x402/verify.js +110 -0
  227. package/dist/x402/verify.js.map +1 -0
  228. package/package.json +100 -0
@@ -0,0 +1,62 @@
1
+ /** The certificate DRM3 issues (`drm3-issuer-attestation/1`, ROOT-CEREMONY.md §3.1). */
2
+ export interface IssuerAttestation {
3
+ schema: "drm3-issuer-attestation/1";
4
+ /** The DRM3 signing key that issued this, `ed25519:<hex>`. */
5
+ issuer: string;
6
+ issuer_path?: string;
7
+ /** The certified subject (service or partner name). */
8
+ service: string;
9
+ subject_keys: Array<{
10
+ key: string;
11
+ path?: string;
12
+ valid_from?: string;
13
+ expected_valid_until?: string;
14
+ }>;
15
+ issued_at?: string;
16
+ statement?: string;
17
+ signature: string;
18
+ }
19
+ /** Minimal receipt shape for chain verification (matches @drm3/sdk/receipts). */
20
+ export interface ChainReceipt {
21
+ id?: string;
22
+ action?: string;
23
+ timestamp?: string;
24
+ input_hash?: string;
25
+ output_hash?: string;
26
+ cost?: number | string | null;
27
+ duration_ms?: number | null;
28
+ parent_id?: string | null;
29
+ metadata?: unknown;
30
+ public_key?: string;
31
+ signature?: string;
32
+ }
33
+ /** The graded outcome of a chain verification. Never a bare boolean. */
34
+ export interface ChainVerdict {
35
+ ok: boolean;
36
+ /**
37
+ * root-anchored - receipt sig valid AND a valid issuer attestation covers the key AND the
38
+ * attestation verifies under the provided root/issuer public key.
39
+ * unanchored - receipt sig valid; no (or unverifiable-by-absence) attestation supplied.
40
+ * Signature proves authorship; authorization by DRM3 is not established.
41
+ * failed - signature math or the supplied attestation failed. Do not trust.
42
+ */
43
+ grade: "root-anchored" | "unanchored" | "failed";
44
+ notes: string[];
45
+ }
46
+ /** Sign an issuer attestation (the CA side; also used to build test fixtures). */
47
+ export declare function signIssuerAttestation(unsigned: Omit<IssuerAttestation, "signature">, issuerPrivateKey: CryptoKey): Promise<IssuerAttestation>;
48
+ /** Verify an attestation's signature; optionally pin the expected issuer public key. */
49
+ export declare function verifyAttestation(att: IssuerAttestation, expectedIssuer?: string): Promise<{
50
+ ok: boolean;
51
+ reason?: string;
52
+ }>;
53
+ /**
54
+ * Verify a receipt against its certificate chain. Grades, never booleans:
55
+ * receipt signature (always) -> attestation covers the key -> attestation verifies under
56
+ * the DRM3 root/issuer key you supply. No attestation supplied = honest `unanchored`
57
+ * (today's fleet state until per-subject attestations ship).
58
+ */
59
+ export declare function verifyChain(receipt: ChainReceipt, attestation?: IssuerAttestation | null, rootPublicKey?: string | null): Promise<ChainVerdict>;
60
+ /** Short display form: `ed25519:aabbccdd…eeff00`. */
61
+ export declare function shortKeyId(k: string): string;
62
+ //# sourceMappingURL=keys-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys-verify.d.ts","sourceRoot":"","sources":["../src/keys-verify.ts"],"names":[],"mappings":"AAKA,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,2BAA2B,CAAC;IACpC,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC,CAAC;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,EAAE,eAAe,GAAG,YAAY,GAAG,QAAQ,CAAC;IACjD,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,kFAAkF;AAClF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,EAAE,gBAAgB,EAAE,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAGnJ;AAED,wFAAwF;AACxF,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,iBAAiB,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAMlI;AAkBD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAoBrJ;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG5C"}
@@ -0,0 +1,68 @@
1
+ // DRM3 partner signing keys - issuer attestations + receipt certificate-chain grading
2
+ // (receipt -> issuer attestation -> DRM3 Root). MOVED verbatim from ./keys: do NOT
3
+ // change any byte order, constant, or check.
4
+ import { canonicalize, signPayload, verifySig } from "./keys-canonical.js";
5
+ /** Sign an issuer attestation (the CA side; also used to build test fixtures). */
6
+ export async function signIssuerAttestation(unsigned, issuerPrivateKey) {
7
+ const signature = await signPayload(issuerPrivateKey, canonicalize(unsigned));
8
+ return { ...unsigned, signature };
9
+ }
10
+ /** Verify an attestation's signature; optionally pin the expected issuer public key. */
11
+ export async function verifyAttestation(att, expectedIssuer) {
12
+ if (att.schema !== "drm3-issuer-attestation/1")
13
+ return { ok: false, reason: "unknown schema" };
14
+ if (expectedIssuer && att.issuer.toLowerCase() !== expectedIssuer.toLowerCase())
15
+ return { ok: false, reason: "issuer key does not match the expected root" };
16
+ const { signature, ...unsigned } = att;
17
+ const ok = await verifySig(att.issuer, signature, canonicalize(unsigned));
18
+ return ok ? { ok } : { ok: false, reason: "attestation signature failed" };
19
+ }
20
+ // The exact receipt field subset the signature covers (same discipline as ./receipts).
21
+ function receiptPayload(r) {
22
+ return canonicalize({
23
+ id: r.id,
24
+ action: r.action,
25
+ timestamp: r.timestamp,
26
+ input_hash: r.input_hash,
27
+ output_hash: r.output_hash,
28
+ cost: r.cost,
29
+ duration_ms: r.duration_ms,
30
+ parent_id: r.parent_id,
31
+ metadata: r.metadata,
32
+ public_key: r.public_key,
33
+ });
34
+ }
35
+ /**
36
+ * Verify a receipt against its certificate chain. Grades, never booleans:
37
+ * receipt signature (always) -> attestation covers the key -> attestation verifies under
38
+ * the DRM3 root/issuer key you supply. No attestation supplied = honest `unanchored`
39
+ * (today's fleet state until per-subject attestations ship).
40
+ */
41
+ export async function verifyChain(receipt, attestation, rootPublicKey) {
42
+ const notes = [];
43
+ if (!receipt.public_key || !receipt.signature)
44
+ return { ok: false, grade: "failed", notes: ["receipt is missing public_key or signature"] };
45
+ const sigOk = await verifySig(receipt.public_key, receipt.signature, receiptPayload(receipt));
46
+ if (!sigOk)
47
+ return { ok: false, grade: "failed", notes: ["receipt signature failed"] };
48
+ notes.push("receipt signature verified");
49
+ if (!attestation) {
50
+ notes.push("no issuer attestation supplied: signature proves authorship, DRM3 authorization not established");
51
+ return { ok: true, grade: "unanchored", notes };
52
+ }
53
+ const att = await verifyAttestation(attestation, rootPublicKey ?? undefined);
54
+ if (!att.ok)
55
+ return { ok: false, grade: "failed", notes: [...notes, `attestation: ${att.reason}`] };
56
+ notes.push("issuer attestation verified");
57
+ const covered = attestation.subject_keys.some((k) => k.key.toLowerCase() === receipt.public_key.toLowerCase());
58
+ if (!covered)
59
+ return { ok: false, grade: "failed", notes: [...notes, "receipt key is not in the attestation's subject_keys"] };
60
+ notes.push("receipt key is attested for this subject");
61
+ return { ok: true, grade: "root-anchored", notes };
62
+ }
63
+ /** Short display form: `ed25519:aabbccdd…eeff00`. */
64
+ export function shortKeyId(k) {
65
+ const h = k.replace(/^ed25519:/, "");
66
+ return h.length <= 18 ? `ed25519:${h}` : `ed25519:${h.slice(0, 8)}…${h.slice(-6)}`;
67
+ }
68
+ //# sourceMappingURL=keys-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys-verify.js","sourceRoot":"","sources":["../src/keys-verify.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,mFAAmF;AACnF,6CAA6C;AAC7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAkD3E,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAA8C,EAAE,gBAA2B;IACrH,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,gBAAgB,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9E,OAAO,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,CAAC;AACpC,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAsB,EAAE,cAAuB;IACrF,IAAI,GAAG,CAAC,MAAM,KAAK,2BAA2B;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC/F,IAAI,cAAc,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,WAAW,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,6CAA6C,EAAE,CAAC;IAC7J,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAC;IACvC,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1E,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;AAC7E,CAAC;AAED,uFAAuF;AACvF,SAAS,cAAc,CAAC,CAAe;IACrC,OAAO,YAAY,CAAC;QAClB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,CAAC,CAAC,UAAU;KACzB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAqB,EAAE,WAAsC,EAAE,aAA6B;IAC5H,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,4CAA4C,CAAC,EAAE,CAAC;IAC5I,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9F,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,0BAA0B,CAAC,EAAE,CAAC;IACvF,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAEzC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,iGAAiG,CAAC,CAAC;QAC9G,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAClD,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,WAAW,EAAE,aAAa,IAAI,SAAS,CAAC,CAAC;IAC7E,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,gBAAgB,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;IACpG,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,KAAM,OAAO,CAAC,UAAqB,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3H,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,sDAAsD,CAAC,EAAE,CAAC;IAC/H,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACvD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;AACrD,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACrF,CAAC"}
package/dist/keys.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { canonicalize } from "./keys-canonical.js";
2
+ export { type Csr, type GeneratedSigningKey, buildCSR, generateSigningKey, importPrivateKeyPkcs8, verifyCsr } from "./keys-generate.js";
3
+ export { type ChainReceipt, type ChainVerdict, type IssuerAttestation, shortKeyId, signIssuerAttestation, verifyAttestation, verifyChain, } from "./keys-verify.js";
4
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,mBAAmB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACxI,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,UAAU,EACV,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,GACZ,MAAM,kBAAkB,CAAC"}
package/dist/keys.js ADDED
@@ -0,0 +1,22 @@
1
+ // DRM3 partner signing keys - generate a keypair locally, build a CSR for the DRM3 CA,
2
+ // and verify a certificate chain (receipt -> issuer attestation -> DRM3 Root).
3
+ //
4
+ // import { generateSigningKey, buildCSR, verifyChain } from "@drm3/sdk/keys";
5
+ //
6
+ // const key = await generateSigningKey(); // private key never leaves you
7
+ // const csr = await buildCSR({ subject: "acme", keyPair: key.keyPair });
8
+ // // POST csr to DRM3 (a verified partner account) -> a drm3-issuer-attestation/1 back
9
+ // const verdict = await verifyChain(receipt, attestation, rootPublicKey);
10
+ //
11
+ // The CA model: the partner generates the key and keeps the private half, DRM3 only ever
12
+ // sees the public key and certifies it (docs: drm3-provenance CERTIFICATE-PRACTICE-STATEMENT
13
+ // + ROOT-CEREMONY.md §3). Signing discipline is identical to receipts: Ed25519 over
14
+ // canonicalize(object minus its signature field).
15
+ //
16
+ // Framework-agnostic, zero deps: stdlib WebCrypto (Ed25519) only. This is a thin barrel:
17
+ // canonicalization + low-level crypto live in ./keys-canonical, keygen + CSR in
18
+ // ./keys-generate, and attestation + chain grading in ./keys-verify.
19
+ export { canonicalize } from "./keys-canonical.js";
20
+ export { buildCSR, generateSigningKey, importPrivateKeyPkcs8, verifyCsr } from "./keys-generate.js";
21
+ export { shortKeyId, signIssuerAttestation, verifyAttestation, verifyChain, } from "./keys-verify.js";
22
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,+EAA+E;AAC/E,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,uFAAuF;AACvF,2EAA2E;AAC3E,yFAAyF;AACzF,4EAA4E;AAC5E,EAAE;AACF,yFAAyF;AACzF,6FAA6F;AAC7F,oFAAoF;AACpF,kDAAkD;AAClD,EAAE;AACF,yFAAyF;AACzF,gFAAgF;AAChF,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAsC,QAAQ,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACxI,OAAO,EAIL,UAAU,EACV,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,GACZ,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type App, type Balance, type Credits, GLOBAL, type Reservation, type Result, type Tenant } from "./types.js";
2
+ export interface StorageAdapter {
3
+ fund(tenant: Tenant, app: App, amount: Credits): Promise<void>;
4
+ /** Atomically place the hold if the scope can cover it. true = placed. */
5
+ hold(resv: Reservation): Promise<boolean>;
6
+ capture(id: string, quotedActual: Credits, idemKey?: string): Promise<Result<{
7
+ charged: Credits;
8
+ }>>;
9
+ voidHold(id: string): Promise<Result<Record<never, never>>>;
10
+ balance(tenant: Tenant, app: App): Promise<Balance>;
11
+ /** Release every hold past its TTL. Returns how many were reclaimed. */
12
+ reclaimExpired(now: number): Promise<number>;
13
+ getReservation(id: string): Promise<Reservation | undefined>;
14
+ }
15
+ export declare class InMemoryAdapter implements StorageAdapter {
16
+ private rows;
17
+ private resv;
18
+ /** capture_idem_key → the charge it produced, so a retry returns the same. */
19
+ private idem;
20
+ private locks;
21
+ /** Serialize all operations for one tenant (the atomicity boundary). */
22
+ private run;
23
+ private row;
24
+ fund(tenant: Tenant, app: App, amount: Credits): Promise<void>;
25
+ hold(r: Reservation): Promise<boolean>;
26
+ capture(id: string, quotedActual: Credits, idemKey?: string): Promise<Result<{
27
+ charged: Credits;
28
+ }>>;
29
+ voidHold(id: string): Promise<Result<Record<never, never>>>;
30
+ balance(tenant: Tenant, app: App): Promise<Balance>;
31
+ reclaimExpired(now: number): Promise<number>;
32
+ getReservation(id: string): Promise<Reservation | undefined>;
33
+ }
34
+ export { GLOBAL };
35
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/meter/adapter.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAO,MAAM,YAAY,CAAC;AAE3H,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,0EAA0E;IAC1E,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,CAAC;IACpG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,wEAAwE;IACxE,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;CAC9D;AASD,qBAAa,eAAgB,YAAW,cAAc;IACpD,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,IAAI,CAAkC;IAC9C,8EAA8E;IAC9E,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,KAAK,CAAuC;IAEpD,wEAAwE;IACxE,OAAO,CAAC,GAAG;IAaX,OAAO,CAAC,GAAG;IAUX,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D,IAAI,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAUtC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAqBnG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAc3D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAOnD,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB5C,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;CAG7D;AAED,OAAO,EAAE,MAAM,EAAE,CAAC"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/meter/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * cascade - the one recursive draw primitive ("a pattern in a pattern").
3
+ *
4
+ * A cascade is an ordered list of draw-nodes. To satisfy a demand, draw from each
5
+ * node in order; whatever a node can't cover SPILLS to the next. Every node FLOORS
6
+ * at zero (never goes negative) EXCEPT the terminal node, which may OVERDRAFT to
7
+ * absorb the remainder. That single rule describes both depths of the wallet:
8
+ *
9
+ * - a WALLET is a cascade of METERS (feature_1, feature_2, … → credits = terminal)
10
+ * - a METER is a cascade of BUCKETS (daily → monthly → buyup)
11
+ *
12
+ * This module is just the math - pure, integer (µcredit/`bigint`), no I/O, no
13
+ * storage, no unit conversion. The wallet/meter/bucket/plan layers compose it.
14
+ *
15
+ * `terminalOverdraft`:
16
+ * - true (default) → the last node absorbs all remaining demand and may go
17
+ * negative; `shortfall` is always 0. Use when this cascade
18
+ * is the buck-stops-here layer (the credits purse / a
19
+ * buyup that is the final fallback).
20
+ * - false → the last node also floors at zero; unmet demand is
21
+ * reported as `shortfall` so a parent cascade can spill it
22
+ * upward (a meter that is NOT the terminal meter, whose
23
+ * remainder converts to credits and continues).
24
+ */
25
+ export interface CascadeNode {
26
+ readonly key: string;
27
+ /** Current balance in µcredits (or any integer unit). May start negative. */
28
+ readonly balance: bigint;
29
+ }
30
+ export interface CascadeDraw {
31
+ readonly key: string;
32
+ /** Amount drawn from this node this pass (>= 0). */
33
+ readonly amount: bigint;
34
+ /** Resulting balance. >= 0 for every node except an overdrafting terminal. */
35
+ readonly balance: bigint;
36
+ }
37
+ export interface CascadeResult {
38
+ /** One entry per input node, in order (amount may be 0). */
39
+ readonly draws: readonly CascadeDraw[];
40
+ /** Total drawn = demand - shortfall. */
41
+ readonly applied: bigint;
42
+ /** Unmet demand. Always 0 when terminalOverdraft is true. */
43
+ readonly shortfall: bigint;
44
+ /** How much further below zero the terminal went this pass (>= 0). */
45
+ readonly overdraft: bigint;
46
+ }
47
+ /**
48
+ * Draw `demand` from `nodes` in order. Pure: input nodes are never mutated.
49
+ *
50
+ * @param nodes ordered draw-nodes (most-specific first; terminal last)
51
+ * @param demand units to satisfy; <= 0 draws nothing
52
+ */
53
+ export declare function cascade(nodes: readonly CascadeNode[], demand: bigint, opts?: {
54
+ terminalOverdraft?: boolean;
55
+ }): CascadeResult;
56
+ //# sourceMappingURL=cascade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../../src/meter/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAID;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,aAAa,CAmChI"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * cascade - the one recursive draw primitive ("a pattern in a pattern").
3
+ *
4
+ * A cascade is an ordered list of draw-nodes. To satisfy a demand, draw from each
5
+ * node in order; whatever a node can't cover SPILLS to the next. Every node FLOORS
6
+ * at zero (never goes negative) EXCEPT the terminal node, which may OVERDRAFT to
7
+ * absorb the remainder. That single rule describes both depths of the wallet:
8
+ *
9
+ * - a WALLET is a cascade of METERS (feature_1, feature_2, … → credits = terminal)
10
+ * - a METER is a cascade of BUCKETS (daily → monthly → buyup)
11
+ *
12
+ * This module is just the math - pure, integer (µcredit/`bigint`), no I/O, no
13
+ * storage, no unit conversion. The wallet/meter/bucket/plan layers compose it.
14
+ *
15
+ * `terminalOverdraft`:
16
+ * - true (default) → the last node absorbs all remaining demand and may go
17
+ * negative; `shortfall` is always 0. Use when this cascade
18
+ * is the buck-stops-here layer (the credits purse / a
19
+ * buyup that is the final fallback).
20
+ * - false → the last node also floors at zero; unmet demand is
21
+ * reported as `shortfall` so a parent cascade can spill it
22
+ * upward (a meter that is NOT the terminal meter, whose
23
+ * remainder converts to credits and continues).
24
+ */
25
+ const max0 = (x) => (x > 0n ? x : 0n);
26
+ /**
27
+ * Draw `demand` from `nodes` in order. Pure: input nodes are never mutated.
28
+ *
29
+ * @param nodes ordered draw-nodes (most-specific first; terminal last)
30
+ * @param demand units to satisfy; <= 0 draws nothing
31
+ */
32
+ export function cascade(nodes, demand, opts = {}) {
33
+ const terminalOverdraft = opts.terminalOverdraft ?? true;
34
+ const want = demand > 0n ? demand : 0n;
35
+ const draws = [];
36
+ let remaining = want;
37
+ let overdraft = 0n;
38
+ const lastIdx = nodes.length - 1;
39
+ for (let i = 0; i < nodes.length; i++) {
40
+ const node = nodes[i];
41
+ const isTerminal = i === lastIdx;
42
+ let take;
43
+ if (isTerminal && terminalOverdraft) {
44
+ // Terminal absorbs the rest, even past zero.
45
+ take = remaining;
46
+ const before = node.balance;
47
+ const after = before - take;
48
+ overdraft = max0(-after) - max0(-before); // new overdraft incurred this pass
49
+ }
50
+ else {
51
+ // Floor at zero: can only draw a positive balance.
52
+ take = remaining < max0(node.balance) ? remaining : max0(node.balance);
53
+ }
54
+ draws.push({ key: node.key, amount: take, balance: node.balance - take });
55
+ remaining -= take;
56
+ }
57
+ return {
58
+ draws,
59
+ applied: want - remaining,
60
+ shortfall: remaining,
61
+ overdraft,
62
+ };
63
+ }
64
+ //# sourceMappingURL=cascade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cascade.js","sourceRoot":"","sources":["../../src/meter/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA2BH,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,KAA6B,EAAE,MAAc,EAAE,OAAwC,EAAE;IAC/G,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvC,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAgB,CAAC;QACrC,MAAM,UAAU,GAAG,CAAC,KAAK,OAAO,CAAC;QAEjC,IAAI,IAAY,CAAC;QACjB,IAAI,UAAU,IAAI,iBAAiB,EAAE,CAAC;YACpC,6CAA6C;YAC7C,IAAI,GAAG,SAAS,CAAC;YACjB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;YAC5B,MAAM,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;YAC5B,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,mCAAmC;QAC/E,CAAC;aAAM,CAAC;YACN,mDAAmD;YACnD,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzE,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC,CAAC;QAC1E,SAAS,IAAI,IAAI,CAAC;IACpB,CAAC;IAED,OAAO;QACL,KAAK;QACL,OAAO,EAAE,IAAI,GAAG,SAAS;QACzB,SAAS,EAAE,SAAS;QACpB,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * CreditsClient - the app-facing surface. Orchestrates the pure core, the
3
+ * storage adapter, the price book, and (later) the settlement sink. Holds the
4
+ * operation, never a bucket: a hold exists only while a request is in flight and
5
+ * is released the instant it captures, voids, or expires.
6
+ *
7
+ * Spend precedence mirrors the contract: app-scoped balance first, then GLOBAL.
8
+ * (A hold resolves to a single scope in v1 - whichever covers it, app before
9
+ * global; a hold that must span both scopes is a documented v1 limitation.)
10
+ */
11
+ import type { StorageAdapter } from "./adapter.js";
12
+ import { PriceBook } from "./pricebook.js";
13
+ import { type App, type Balance, type Credits, type Reservation, type Result, type Tenant } from "./types.js";
14
+ export interface ClientOptions {
15
+ adapter: StorageAdapter;
16
+ priceBook?: PriceBook;
17
+ /** Injected clock - the core never reads time itself. Default Date.now. */
18
+ now?: () => number;
19
+ /** Injected id generator. Default crypto.randomUUID. */
20
+ genId?: () => string;
21
+ /** Default hold TTL in ms. Default 60_000. */
22
+ holdTtlMs?: number;
23
+ }
24
+ export interface AuthorizeInput {
25
+ tenant: Tenant;
26
+ app?: App;
27
+ /** ceiling in credits, OR give unit+maxUnits to quote it from the price book */
28
+ maxCredits?: Credits;
29
+ unit?: string;
30
+ maxUnits?: number;
31
+ ttlMs?: number;
32
+ }
33
+ export interface CaptureInput {
34
+ /** actual usage in credits, OR unit+units to quote it */
35
+ credits?: Credits;
36
+ unit?: string;
37
+ units?: number;
38
+ /** idempotency key - a retried capture with the same key charges once */
39
+ idemKey?: string;
40
+ }
41
+ export declare class CreditsClient {
42
+ private adapter;
43
+ private prices;
44
+ private now;
45
+ private genId;
46
+ private ttl;
47
+ constructor(opts: ClientOptions);
48
+ fund(tenant: Tenant, amount: Credits, app?: App): Promise<void>;
49
+ balance(tenant: Tenant, app?: App): Promise<Balance>;
50
+ private ceiling;
51
+ /** Pre-auth: reserve the worst-case ceiling. Bounds the most this can cost. */
52
+ authorize(input: AuthorizeInput): Promise<Result<{
53
+ reservation: Reservation;
54
+ }>>;
55
+ private actual;
56
+ /** Clearing: commit the actual amount (capped at the hold), release the rest. */
57
+ capture(reservationId: string, input: CaptureInput): Promise<Result<{
58
+ charged: Credits;
59
+ }>>;
60
+ /** Reversal: backend failed → release the whole hold, charge nothing. */
61
+ void(reservationId: string): Promise<Result<Record<never, never>>>;
62
+ /**
63
+ * Fixed-price fast path: authorize + capture the exact price in one call, with
64
+ * no lingering hold. For a query, a refinement run - anything priced up front.
65
+ */
66
+ charge(tenant: Tenant, input: {
67
+ app?: App;
68
+ credits?: Credits;
69
+ unit?: string;
70
+ units?: number;
71
+ idemKey?: string;
72
+ }): Promise<Result<{
73
+ charged: Credits;
74
+ }>>;
75
+ /** TTL sweep of abandoned holds - a cheap cron over your own DB, never RPC. */
76
+ reclaimExpired(): Promise<number>;
77
+ }
78
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/meter/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAO,MAAM,YAAY,CAAC;AAE3H,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IACrB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,GAAG,CAAe;IAC1B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,GAAG,CAAS;gBAER,IAAI,EAAE,aAAa;IAQ/B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAE,GAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,GAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5D,OAAO,CAAC,OAAO;IAMf,+EAA+E;IACzE,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,WAAW,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;IAuBrF,OAAO,CAAC,MAAM;IAMd,iFAAiF;IACjF,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAI1F,yEAAyE;IACzE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAIlE;;;OAGG;IACG,MAAM,CACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GACvF,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAOxC,+EAA+E;IAC/E,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;CAGlC"}
@@ -0,0 +1,84 @@
1
+ import { PriceBook } from "./pricebook.js";
2
+ import { GLOBAL, err } from "./types.js";
3
+ export class CreditsClient {
4
+ adapter;
5
+ prices;
6
+ now;
7
+ genId;
8
+ ttl;
9
+ constructor(opts) {
10
+ this.adapter = opts.adapter;
11
+ this.prices = opts.priceBook ?? new PriceBook();
12
+ this.now = opts.now ?? (() => Date.now());
13
+ this.genId = opts.genId ?? (() => crypto.randomUUID());
14
+ this.ttl = opts.holdTtlMs ?? 60_000;
15
+ }
16
+ fund(tenant, amount, app = GLOBAL) {
17
+ return this.adapter.fund(tenant, app, amount);
18
+ }
19
+ balance(tenant, app = GLOBAL) {
20
+ return this.adapter.balance(tenant, app);
21
+ }
22
+ ceiling(input) {
23
+ if (input.maxCredits !== undefined)
24
+ return input.maxCredits;
25
+ if (input.unit !== undefined && input.maxUnits !== undefined)
26
+ return this.prices.quote(input.unit, input.maxUnits);
27
+ return 0n;
28
+ }
29
+ /** Pre-auth: reserve the worst-case ceiling. Bounds the most this can cost. */
30
+ async authorize(input) {
31
+ const want = this.ceiling(input);
32
+ const ttlMs = input.ttlMs ?? this.ttl;
33
+ const at = this.now();
34
+ const base = {
35
+ id: this.genId(),
36
+ tenant: input.tenant,
37
+ held: want,
38
+ charged: 0n,
39
+ state: "held",
40
+ createdAt: at,
41
+ expiresAt: at + ttlMs,
42
+ };
43
+ const requested = input.app ?? GLOBAL;
44
+ // app-first, then fall back to GLOBAL
45
+ const scopes = requested === GLOBAL ? [GLOBAL] : [requested, GLOBAL];
46
+ for (const scope of scopes) {
47
+ const resv = { ...base, app: scope };
48
+ if (await this.adapter.hold(resv))
49
+ return { ok: true, reservation: resv };
50
+ }
51
+ return err("INSUFFICIENT_FUNDS", `tenant ${input.tenant} cannot reserve ${want} credits`);
52
+ }
53
+ actual(input) {
54
+ if (input.credits !== undefined)
55
+ return input.credits;
56
+ if (input.unit !== undefined && input.units !== undefined)
57
+ return this.prices.quote(input.unit, input.units);
58
+ return 0n;
59
+ }
60
+ /** Clearing: commit the actual amount (capped at the hold), release the rest. */
61
+ capture(reservationId, input) {
62
+ return this.adapter.capture(reservationId, this.actual(input), input.idemKey);
63
+ }
64
+ /** Reversal: backend failed → release the whole hold, charge nothing. */
65
+ void(reservationId) {
66
+ return this.adapter.voidHold(reservationId);
67
+ }
68
+ /**
69
+ * Fixed-price fast path: authorize + capture the exact price in one call, with
70
+ * no lingering hold. For a query, a refinement run - anything priced up front.
71
+ */
72
+ async charge(tenant, input) {
73
+ const price = input.credits ?? (input.unit !== undefined && input.units !== undefined ? this.prices.quote(input.unit, input.units) : 0n);
74
+ const authd = await this.authorize({ tenant, app: input.app, maxCredits: price, ttlMs: 1_000 });
75
+ if (!authd.ok)
76
+ return authd;
77
+ return this.capture(authd.reservation.id, { credits: price, idemKey: input.idemKey });
78
+ }
79
+ /** TTL sweep of abandoned holds - a cheap cron over your own DB, never RPC. */
80
+ reclaimExpired() {
81
+ return this.adapter.reclaimExpired(this.now());
82
+ }
83
+ }
84
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/meter/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The pure core. No I/O, no clock, no network - every input is an argument and
3
+ * every output is a value. This is what makes the engine provably correct and
4
+ * exhaustively testable (property + state×action). The StorageAdapter applies
5
+ * these decisions atomically; the core never touches state itself.
6
+ */
7
+ import type { Credits, Reservation, ReservationState } from "./types.js";
8
+ /** available = balance - held, floored at 0 (a hold never makes it negative). */
9
+ export declare function available(balance: Credits, held: Credits): Credits;
10
+ /** Can a hold of `amount` be placed against this scope? The Visa pre-auth check. */
11
+ export declare function canHold(balance: Credits, held: Credits, amount: Credits): boolean;
12
+ /**
13
+ * The Visa guarantee: you are never charged more than you reserved.
14
+ * charged = min(quotedActual, held). Always.
15
+ */
16
+ export declare function captureAmount(held: Credits, quotedActual: Credits): Credits;
17
+ /** Whether a reservation is in a state that can still be captured/voided. */
18
+ export declare function isOpen(state: ReservationState): boolean;
19
+ /**
20
+ * Decide the result of capturing a held reservation. Returns the next
21
+ * reservation shape + how much to debit balance + how much hold to release.
22
+ * Pure: caller persists. Capturing a non-open reservation is a no-op echo
23
+ * (idempotent at the state-machine level; storage enforces idem by key too).
24
+ */
25
+ export declare function applyCapture(r: Reservation, quotedActual: Credits, idemKey?: string): {
26
+ reservation: Reservation;
27
+ debit: Credits;
28
+ releaseHold: Credits;
29
+ };
30
+ /** Voiding/expiring an open reservation releases the entire hold, charges nothing. */
31
+ export declare function applyRelease(r: Reservation, state: "voided" | "expired"): {
32
+ reservation: Reservation;
33
+ releaseHold: Credits;
34
+ };
35
+ /** Has a reservation passed its TTL and is still holding credits? */
36
+ export declare function isReclaimable(r: Reservation, now: number): boolean;
37
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/meter/core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEzE,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAGlE;AAED,oFAAoF;AACpF,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAEjF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAG3E;AAED,6EAA6E;AAC7E,wBAAgB,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAUxJ;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG;IAAE,WAAW,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAG5H;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAElE"}