ledgdex 1.0.20__tar.gz

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 (231) hide show
  1. ledgdex-1.0.20/LICENSE +121 -0
  2. ledgdex-1.0.20/MANIFEST.in +8 -0
  3. ledgdex-1.0.20/PKG-INFO +254 -0
  4. ledgdex-1.0.20/README.md +233 -0
  5. ledgdex-1.0.20/guide/dex/ledgdex.jsonl +5 -0
  6. ledgdex-1.0.20/guide/examples/01-first-trade.sh +37 -0
  7. ledgdex-1.0.20/guide/examples/02-marketplace.sh +51 -0
  8. ledgdex-1.0.20/guide/examples/03-auction.sh +39 -0
  9. ledgdex-1.0.20/guide/examples/04-disputes.sh +39 -0
  10. ledgdex-1.0.20/guide/examples/05-keys.sh +35 -0
  11. ledgdex-1.0.20/guide/examples/06-publish.sh +34 -0
  12. ledgdex-1.0.20/guide/examples/07-check.sh +37 -0
  13. ledgdex-1.0.20/guide/examples/08-python-api.py +40 -0
  14. ledgdex-1.0.20/guide/examples/09-bots.py +80 -0
  15. ledgdex-1.0.20/guide/examples/10-robot-cleaning.py +78 -0
  16. ledgdex-1.0.20/guide/examples/11-extending.py +55 -0
  17. ledgdex-1.0.20/guide/examples/12-javascript.mjs +28 -0
  18. ledgdex-1.0.20/guide/examples/13-js-verify.mjs +42 -0
  19. ledgdex-1.0.20/guide/examples/14-js-web-page.html +43 -0
  20. ledgdex-1.0.20/guide/examples/15-js-sign.mjs +49 -0
  21. ledgdex-1.0.20/guide/tutorial.md +977 -0
  22. ledgdex-1.0.20/js/canon.js +77 -0
  23. ledgdex-1.0.20/js/core.js +364 -0
  24. ledgdex-1.0.20/js/dexweb-template.json +14 -0
  25. ledgdex-1.0.20/js/dexweb.js +91 -0
  26. ledgdex-1.0.20/js/ed25519.js +117 -0
  27. ledgdex-1.0.20/js/fuzz.mjs +27 -0
  28. ledgdex-1.0.20/js/keystore.js +64 -0
  29. ledgdex-1.0.20/js/render.js +200 -0
  30. ledgdex-1.0.20/js/sha.js +113 -0
  31. ledgdex-1.0.20/js/sig.js +51 -0
  32. ledgdex-1.0.20/js/state.js +206 -0
  33. ledgdex-1.0.20/js/test.mjs +178 -0
  34. ledgdex-1.0.20/js/viewer.js +346 -0
  35. ledgdex-1.0.20/js/zip.js +36 -0
  36. ledgdex-1.0.20/py/ledgdex/__init__.py +2 -0
  37. ledgdex-1.0.20/py/ledgdex/__main__.py +3 -0
  38. ledgdex-1.0.20/py/ledgdex/canon.py +99 -0
  39. ledgdex-1.0.20/py/ledgdex/check.py +352 -0
  40. ledgdex-1.0.20/py/ledgdex/cli.py +559 -0
  41. ledgdex-1.0.20/py/ledgdex/core.py +535 -0
  42. ledgdex-1.0.20/py/ledgdex/dex.py +292 -0
  43. ledgdex-1.0.20/py/ledgdex/ed25519.py +135 -0
  44. ledgdex-1.0.20/py/ledgdex/publish.py +79 -0
  45. ledgdex-1.0.20/py/ledgdex/render.py +352 -0
  46. ledgdex-1.0.20/py/ledgdex/shop.py +217 -0
  47. ledgdex-1.0.20/py/ledgdex/sig.py +88 -0
  48. ledgdex-1.0.20/py/ledgdex/state.py +278 -0
  49. ledgdex-1.0.20/py/ledgdex/styles.css +123 -0
  50. ledgdex-1.0.20/py/ledgdex.egg-info/PKG-INFO +254 -0
  51. ledgdex-1.0.20/py/ledgdex.egg-info/SOURCES.txt +229 -0
  52. ledgdex-1.0.20/py/ledgdex.egg-info/dependency_links.txt +1 -0
  53. ledgdex-1.0.20/py/ledgdex.egg-info/entry_points.txt +2 -0
  54. ledgdex-1.0.20/py/ledgdex.egg-info/requires.txt +4 -0
  55. ledgdex-1.0.20/py/ledgdex.egg-info/top_level.txt +1 -0
  56. ledgdex-1.0.20/py/tests/fuzz.py +424 -0
  57. ledgdex-1.0.20/py/tests/helpers.py +52 -0
  58. ledgdex-1.0.20/py/tests/make_vectors.py +502 -0
  59. ledgdex-1.0.20/py/tests/offline.mjs +19 -0
  60. ledgdex-1.0.20/py/tests/page.mjs +22 -0
  61. ledgdex-1.0.20/py/tests/test_canon.py +45 -0
  62. ledgdex-1.0.20/py/tests/test_check.py +313 -0
  63. ledgdex-1.0.20/py/tests/test_cli_v2.py +216 -0
  64. ledgdex-1.0.20/py/tests/test_dex.py +283 -0
  65. ledgdex-1.0.20/py/tests/test_ed25519.py +31 -0
  66. ledgdex-1.0.20/py/tests/test_fast.py +158 -0
  67. ledgdex-1.0.20/py/tests/test_guide.py +95 -0
  68. ledgdex-1.0.20/py/tests/test_ledger.py +159 -0
  69. ledgdex-1.0.20/py/tests/test_safety.py +81 -0
  70. ledgdex-1.0.20/py/tests/test_shop.py +170 -0
  71. ledgdex-1.0.20/py/tests/test_state.py +117 -0
  72. ledgdex-1.0.20/py/tests/test_v2.py +274 -0
  73. ledgdex-1.0.20/py/tests/test_vectors.py +15 -0
  74. ledgdex-1.0.20/py/tests/tutorial.py +119 -0
  75. ledgdex-1.0.20/pyproject.toml +39 -0
  76. ledgdex-1.0.20/run_tests.py +79 -0
  77. ledgdex-1.0.20/setup.cfg +4 -0
  78. ledgdex-1.0.20/vectors/canon.json +138 -0
  79. ledgdex-1.0.20/vectors/dex/auctions/backup/data.json +92 -0
  80. ledgdex-1.0.20/vectors/dex/auctions/config.json +15 -0
  81. ledgdex-1.0.20/vectors/dex/auctions/data.json +92 -0
  82. ledgdex-1.0.20/vectors/dex/auctions/gen/assets/favicon.ico +0 -0
  83. ledgdex-1.0.20/vectors/dex/auctions/gen/farmledger.html +17 -0
  84. ledgdex-1.0.20/vectors/dex/auctions/gen/index.html +15 -0
  85. ledgdex-1.0.20/vectors/dex/auctions/gen/ledgdex.jsonl +29 -0
  86. ledgdex-1.0.20/vectors/dex/auctions/gen/oldmap.html +17 -0
  87. ledgdex-1.0.20/vectors/dex/auctions/gen/oldmapd3772d43.html +17 -0
  88. ledgdex-1.0.20/vectors/dex/auctions/gen/oldmapee9215e9.html +17 -0
  89. ledgdex-1.0.20/vectors/dex/auctions/gen/styles.css +123 -0
  90. ledgdex-1.0.20/vectors/dex/auctions/ledgdex.jsonl +29 -0
  91. ledgdex-1.0.20/vectors/dex/auctions/run.py +2 -0
  92. ledgdex-1.0.20/vectors/dex/auctions/styles.css +123 -0
  93. ledgdex-1.0.20/vectors/dex/buyer/backup/data.json +27 -0
  94. ledgdex-1.0.20/vectors/dex/buyer/config.json +15 -0
  95. ledgdex-1.0.20/vectors/dex/buyer/data.json +27 -0
  96. ledgdex-1.0.20/vectors/dex/buyer/gen/ashaledger.html +17 -0
  97. ledgdex-1.0.20/vectors/dex/buyer/gen/ashapurchases.html +17 -0
  98. ledgdex-1.0.20/vectors/dex/buyer/gen/assets/favicon.ico +0 -0
  99. ledgdex-1.0.20/vectors/dex/buyer/gen/index.html +15 -0
  100. ledgdex-1.0.20/vectors/dex/buyer/gen/ledgdex.jsonl +4 -0
  101. ledgdex-1.0.20/vectors/dex/buyer/gen/styles.css +123 -0
  102. ledgdex-1.0.20/vectors/dex/buyer/ledgdex.jsonl +4 -0
  103. ledgdex-1.0.20/vectors/dex/buyer/run.py +2 -0
  104. ledgdex-1.0.20/vectors/dex/buyer/styles.css +123 -0
  105. ledgdex-1.0.20/vectors/dex/hostile-text/backup/data.json +57 -0
  106. ledgdex-1.0.20/vectors/dex/hostile-text/config.json +15 -0
  107. ledgdex-1.0.20/vectors/dex/hostile-text/data.json +57 -0
  108. ledgdex-1.0.20/vectors/dex/hostile-text/gen/assets/favicon.ico +0 -0
  109. ledgdex-1.0.20/vectors/dex/hostile-text/gen/index.html +15 -0
  110. ledgdex-1.0.20/vectors/dex/hostile-text/gen/ledgdex.jsonl +12 -0
  111. ledgdex-1.0.20/vectors/dex/hostile-text/gen/ltscriptgtxltscriptgtltimgsrcxonerrorygtx27quotampampltahrefquotjavascriptzquotgt.html +17 -0
  112. ledgdex-1.0.20/vectors/dex/hostile-text/gen/ltscriptgtxltscriptgtltimgsrcxonerrorygtx27quotampampltahrefquotjavascriptzquotgtadmissions.html +17 -0
  113. ledgdex-1.0.20/vectors/dex/hostile-text/gen/ltscriptgtxltscriptgtltimgsrcxonerrorygtx27quotampampltahrefquotjavascriptzquotgtledger.html +17 -0
  114. ledgdex-1.0.20/vectors/dex/hostile-text/gen/styles.css +123 -0
  115. ledgdex-1.0.20/vectors/dex/hostile-text/ledgdex.jsonl +12 -0
  116. ledgdex-1.0.20/vectors/dex/hostile-text/run.py +2 -0
  117. ledgdex-1.0.20/vectors/dex/hostile-text/styles.css +123 -0
  118. ledgdex-1.0.20/vectors/dex/index/backup/data.json +21 -0
  119. ledgdex-1.0.20/vectors/dex/index/config.json +15 -0
  120. ledgdex-1.0.20/vectors/dex/index/data.json +21 -0
  121. ledgdex-1.0.20/vectors/dex/index/gen/assets/favicon.ico +0 -0
  122. ledgdex-1.0.20/vectors/dex/index/gen/index.html +15 -0
  123. ledgdex-1.0.20/vectors/dex/index/gen/ledgdex.jsonl +6 -0
  124. ledgdex-1.0.20/vectors/dex/index/gen/marketledger.html +17 -0
  125. ledgdex-1.0.20/vectors/dex/index/gen/styles.css +123 -0
  126. ledgdex-1.0.20/vectors/dex/index/ledgdex.jsonl +6 -0
  127. ledgdex-1.0.20/vectors/dex/index/run.py +2 -0
  128. ledgdex-1.0.20/vectors/dex/index/styles.css +123 -0
  129. ledgdex-1.0.20/vectors/dex/market/backup/data.json +96 -0
  130. ledgdex-1.0.20/vectors/dex/market/config.json +15 -0
  131. ledgdex-1.0.20/vectors/dex/market/data.json +96 -0
  132. ledgdex-1.0.20/vectors/dex/market/gen/assets/favicon.ico +0 -0
  133. ledgdex-1.0.20/vectors/dex/market/gen/farmadmissions.html +17 -0
  134. ledgdex-1.0.20/vectors/dex/market/gen/farmledger.html +17 -0
  135. ledgdex-1.0.20/vectors/dex/market/gen/index.html +15 -0
  136. ledgdex-1.0.20/vectors/dex/market/gen/ledgdex.jsonl +25 -0
  137. ledgdex-1.0.20/vectors/dex/market/gen/mangoes.html +17 -0
  138. ledgdex-1.0.20/vectors/dex/market/gen/mangoes/340/244/206/340/244/256.html +17 -0
  139. ledgdex-1.0.20/vectors/dex/market/gen/styles.css +123 -0
  140. ledgdex-1.0.20/vectors/dex/market/ledgdex.jsonl +25 -0
  141. ledgdex-1.0.20/vectors/dex/market/run.py +2 -0
  142. ledgdex-1.0.20/vectors/dex/market/styles.css +123 -0
  143. ledgdex-1.0.20/vectors/dex/market-variant/gen/assets/favicon.ico +0 -0
  144. ledgdex-1.0.20/vectors/dex/market-variant/gen/farmadmissions.html +17 -0
  145. ledgdex-1.0.20/vectors/dex/market-variant/gen/farmledger.html +17 -0
  146. ledgdex-1.0.20/vectors/dex/market-variant/gen/index.html +15 -0
  147. ledgdex-1.0.20/vectors/dex/market-variant/gen/ledgdex.jsonl +25 -0
  148. ledgdex-1.0.20/vectors/dex/market-variant/gen/mangoes.html +17 -0
  149. ledgdex-1.0.20/vectors/dex/market-variant/gen/mangoes/340/244/206/340/244/256.html +17 -0
  150. ledgdex-1.0.20/vectors/dex/market-variant/gen/styles.css +123 -0
  151. ledgdex-1.0.20/vectors/dexnames.json +42 -0
  152. ledgdex-1.0.20/vectors/ed25519.json +119 -0
  153. ledgdex-1.0.20/vectors/ledgers/admitted-backdated.json +9 -0
  154. ledgdex-1.0.20/vectors/ledgers/admitted-backdated.jsonl +5 -0
  155. ledgdex-1.0.20/vectors/ledgers/admitted-future-stamp.json +9 -0
  156. ledgdex-1.0.20/vectors/ledgers/admitted-future-stamp.jsonl +5 -0
  157. ledgdex-1.0.20/vectors/ledgers/admitted-then.json +9 -0
  158. ledgdex-1.0.20/vectors/ledgers/admitted-then.jsonl +7 -0
  159. ledgdex-1.0.20/vectors/ledgers/auctions-later.json +9 -0
  160. ledgdex-1.0.20/vectors/ledgers/auctions-revealing.json +9 -0
  161. ledgdex-1.0.20/vectors/ledgers/auctions-revealing.jsonl +23 -0
  162. ledgdex-1.0.20/vectors/ledgers/auctions.json +9 -0
  163. ledgdex-1.0.20/vectors/ledgers/auctions.jsonl +29 -0
  164. ledgdex-1.0.20/vectors/ledgers/broken-byte.json +9 -0
  165. ledgdex-1.0.20/vectors/ledgers/broken-byte.jsonl +25 -0
  166. ledgdex-1.0.20/vectors/ledgers/broken-device-copies-owner-message.json +9 -0
  167. ledgdex-1.0.20/vectors/ledgers/broken-device-copies-owner-message.jsonl +4 -0
  168. ledgdex-1.0.20/vectors/ledgers/broken-forged-receipt.json +9 -0
  169. ledgdex-1.0.20/vectors/ledgers/broken-forged-receipt.jsonl +3 -0
  170. ledgdex-1.0.20/vectors/ledgers/broken-fullwidth-time.json +9 -0
  171. ledgdex-1.0.20/vectors/ledgers/broken-fullwidth-time.jsonl +2 -0
  172. ledgdex-1.0.20/vectors/ledgers/broken-header-true.json +8 -0
  173. ledgdex-1.0.20/vectors/ledgers/broken-header-true.jsonl +1 -0
  174. ledgdex-1.0.20/vectors/ledgers/broken-header.json +8 -0
  175. ledgdex-1.0.20/vectors/ledgers/broken-header.jsonl +1 -0
  176. ledgdex-1.0.20/vectors/ledgers/broken-newline.json +9 -0
  177. ledgdex-1.0.20/vectors/ledgers/broken-newline.jsonl +25 -0
  178. ledgdex-1.0.20/vectors/ledgers/broken-noroot.json +9 -0
  179. ledgdex-1.0.20/vectors/ledgers/broken-noroot.jsonl +9 -0
  180. ledgdex-1.0.20/vectors/ledgers/broken-order.json +9 -0
  181. ledgdex-1.0.20/vectors/ledgers/broken-order.jsonl +25 -0
  182. ledgdex-1.0.20/vectors/ledgers/broken-receipt-replayed-device.json +9 -0
  183. ledgdex-1.0.20/vectors/ledgers/broken-receipt-replayed-device.jsonl +3 -0
  184. ledgdex-1.0.20/vectors/ledgers/broken-recovery-replay.json +9 -0
  185. ledgdex-1.0.20/vectors/ledgers/broken-recovery-replay.jsonl +10 -0
  186. ledgdex-1.0.20/vectors/ledgers/broken-seq-true.json +9 -0
  187. ledgdex-1.0.20/vectors/ledgers/broken-seq-true.jsonl +4 -0
  188. ledgdex-1.0.20/vectors/ledgers/broken-signer.json +9 -0
  189. ledgdex-1.0.20/vectors/ledgers/broken-signer.jsonl +25 -0
  190. ledgdex-1.0.20/vectors/ledgers/broken-small-order-key.json +9 -0
  191. ledgdex-1.0.20/vectors/ledgers/broken-small-order-key.jsonl +2 -0
  192. ledgdex-1.0.20/vectors/ledgers/broken-type-object.json +9 -0
  193. ledgdex-1.0.20/vectors/ledgers/broken-type-object.jsonl +4 -0
  194. ledgdex-1.0.20/vectors/ledgers/broken-version-true.json +9 -0
  195. ledgdex-1.0.20/vectors/ledgers/broken-version-true.jsonl +4 -0
  196. ledgdex-1.0.20/vectors/ledgers/buyer.json +9 -0
  197. ledgdex-1.0.20/vectors/ledgers/buyer.jsonl +4 -0
  198. ledgdex-1.0.20/vectors/ledgers/disputes.json +9 -0
  199. ledgdex-1.0.20/vectors/ledgers/disputes.jsonl +14 -0
  200. ledgdex-1.0.20/vectors/ledgers/duplicate-offer.json +9 -0
  201. ledgdex-1.0.20/vectors/ledgers/duplicate-offer.jsonl +6 -0
  202. ledgdex-1.0.20/vectors/ledgers/hostile-buyer.json +9 -0
  203. ledgdex-1.0.20/vectors/ledgers/hostile-buyer.jsonl +4 -0
  204. ledgdex-1.0.20/vectors/ledgers/hostile-text.json +9 -0
  205. ledgdex-1.0.20/vectors/ledgers/hostile-text.jsonl +12 -0
  206. ledgdex-1.0.20/vectors/ledgers/index.json +9 -0
  207. ledgdex-1.0.20/vectors/ledgers/index.jsonl +6 -0
  208. ledgdex-1.0.20/vectors/ledgers/keys.json +9 -0
  209. ledgdex-1.0.20/vectors/ledgers/keys.jsonl +9 -0
  210. ledgdex-1.0.20/vectors/ledgers/links.json +9 -0
  211. ledgdex-1.0.20/vectors/ledgers/links.jsonl +4 -0
  212. ledgdex-1.0.20/vectors/ledgers/market.json +9 -0
  213. ledgdex-1.0.20/vectors/ledgers/market.jsonl +25 -0
  214. ledgdex-1.0.20/vectors/ledgers/receipt-recovered.json +9 -0
  215. ledgdex-1.0.20/vectors/ledgers/receipt-recovered.jsonl +3 -0
  216. ledgdex-1.0.20/vectors/ledgers/recovered-from-thief.json +9 -0
  217. ledgdex-1.0.20/vectors/ledgers/recovered-from-thief.jsonl +4 -0
  218. ledgdex-1.0.20/vectors/ledgers/recovered.json +9 -0
  219. ledgdex-1.0.20/vectors/ledgers/recovered.jsonl +9 -0
  220. ledgdex-1.0.20/vectors/ledgers/replay.json +9 -0
  221. ledgdex-1.0.20/vectors/ledgers/replay.jsonl +8 -0
  222. ledgdex-1.0.20/vectors/ledgers/root-later.json +9 -0
  223. ledgdex-1.0.20/vectors/ledgers/root-later.jsonl +5 -0
  224. ledgdex-1.0.20/vectors/ledgers/root-thief.json +9 -0
  225. ledgdex-1.0.20/vectors/ledgers/root-thief.jsonl +3 -0
  226. ledgdex-1.0.20/vectors/ledgers/root.json +9 -0
  227. ledgdex-1.0.20/vectors/ledgers/root.jsonl +4 -0
  228. ledgdex-1.0.20/vectors/ledgers/year-99.json +9 -0
  229. ledgdex-1.0.20/vectors/ledgers/year-99.jsonl +3 -0
  230. ledgdex-1.0.20/vectors/pages.json +1065 -0
  231. ledgdex-1.0.20/vectors/signatures.json +101 -0
ledgdex-1.0.20/LICENSE ADDED
@@ -0,0 +1,121 @@
1
+ Creative Commons Legal Code
2
+
3
+ CC0 1.0 Universal
4
+
5
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
+ HEREUNDER.
13
+
14
+ Statement of Purpose
15
+
16
+ The laws of most jurisdictions throughout the world automatically confer
17
+ exclusive Copyright and Related Rights (defined below) upon the creator
18
+ and subsequent owner(s) (each and all, an "owner") of an original work of
19
+ authorship and/or a database (each, a "Work").
20
+
21
+ Certain owners wish to permanently relinquish those rights to a Work for
22
+ the purpose of contributing to a commons of creative, cultural and
23
+ scientific works ("Commons") that the public can reliably and without fear
24
+ of later claims of infringement build upon, modify, incorporate in other
25
+ works, reuse and redistribute as freely as possible in any form whatsoever
26
+ and for any purposes, including without limitation commercial purposes.
27
+ These owners may contribute to the Commons to promote the ideal of a free
28
+ culture and the further production of creative, cultural and scientific
29
+ works, or to gain reputation or greater distribution for their Work in
30
+ part through the use and efforts of others.
31
+
32
+ For these and/or other purposes and motivations, and without any
33
+ expectation of additional consideration or compensation, the person
34
+ associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
+ is an owner of Copyright and Related Rights in the Work, voluntarily
36
+ elects to apply CC0 to the Work and publicly distribute the Work under its
37
+ terms, with knowledge of his or her Copyright and Related Rights in the
38
+ Work and the meaning and intended legal effect of CC0 on those rights.
39
+
40
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
41
+ protected by copyright and related or neighboring rights ("Copyright and
42
+ Related Rights"). Copyright and Related Rights include, but are not
43
+ limited to, the following:
44
+
45
+ i. the right to reproduce, adapt, distribute, perform, display,
46
+ communicate, and translate a Work;
47
+ ii. moral rights retained by the original author(s) and/or performer(s);
48
+ iii. publicity and privacy rights pertaining to a person's image or
49
+ likeness depicted in a Work;
50
+ iv. rights protecting against unfair competition in regards to a Work,
51
+ subject to the limitations in paragraph 4(a), below;
52
+ v. rights protecting the extraction, dissemination, use and reuse of data
53
+ in a Work;
54
+ vi. database rights (such as those arising under Directive 96/9/EC of the
55
+ European Parliament and of the Council of 11 March 1996 on the legal
56
+ protection of databases, and under any national implementation
57
+ thereof, including any amended or successor version of such
58
+ directive); and
59
+ vii. other similar, equivalent or corresponding rights throughout the
60
+ world based on applicable law or treaty, and any national
61
+ implementations thereof.
62
+
63
+ 2. Waiver. To the greatest extent permitted by, but not in contravention
64
+ of, applicable law, Affirmer hereby overtly, fully, permanently,
65
+ irrevocably and unconditionally waives, abandons, and surrenders all of
66
+ Affirmer's Copyright and Related Rights and associated claims and causes
67
+ of action, whether now known or unknown (including existing as well as
68
+ future claims and causes of action), in the Work (i) in all territories
69
+ worldwide, (ii) for the maximum duration provided by applicable law or
70
+ treaty (including future time extensions), (iii) in any current or future
71
+ medium and for any number of copies, and (iv) for any purpose whatsoever,
72
+ including without limitation commercial, advertising or promotional
73
+ purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
+ member of the public at large and to the detriment of Affirmer's heirs and
75
+ successors, fully intending that such Waiver shall not be subject to
76
+ revocation, rescission, cancellation, termination, or any other legal or
77
+ equitable action to disrupt the quiet enjoyment of the Work by the public
78
+ as contemplated by Affirmer's express Statement of Purpose.
79
+
80
+ 3. Public License Fallback. Should any part of the Waiver for any reason
81
+ be judged legally invalid or ineffective under applicable law, then the
82
+ Waiver shall be preserved to the maximum extent permitted taking into
83
+ account Affirmer's express Statement of Purpose. In addition, to the
84
+ extent the Waiver is so judged Affirmer hereby grants to each affected
85
+ person a royalty-free, non transferable, non sublicensable, non exclusive,
86
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
87
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
+ maximum duration provided by applicable law or treaty (including future
89
+ time extensions), (iii) in any current or future medium and for any number
90
+ of copies, and (iv) for any purpose whatsoever, including without
91
+ limitation commercial, advertising or promotional purposes (the
92
+ "License"). The License shall be deemed effective as of the date CC0 was
93
+ applied by Affirmer to the Work. Should any part of the License for any
94
+ reason be judged legally invalid or ineffective under applicable law, such
95
+ partial invalidity or ineffectiveness shall not invalidate the remainder
96
+ of the License, and in such case Affirmer hereby affirms that he or she
97
+ will not (i) exercise any of his or her remaining Copyright and Related
98
+ Rights in the Work or (ii) assert any associated claims and causes of
99
+ action with respect to the Work, in either case contrary to Affirmer's
100
+ express Statement of Purpose.
101
+
102
+ 4. Limitations and Disclaimers.
103
+
104
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
+ surrendered, licensed or otherwise affected by this document.
106
+ b. Affirmer offers the Work as-is and makes no representations or
107
+ warranties of any kind concerning the Work, express, implied,
108
+ statutory or otherwise, including without limitation warranties of
109
+ title, merchantability, fitness for a particular purpose, non
110
+ infringement, or the absence of latent or other defects, accuracy, or
111
+ the present or absence of errors, whether or not discoverable, all to
112
+ the greatest extent permissible under applicable law.
113
+ c. Affirmer disclaims responsibility for clearing rights of other persons
114
+ that may apply to the Work or any use thereof, including without
115
+ limitation any person's Copyright and Related Rights in the Work.
116
+ Further, Affirmer disclaims responsibility for obtaining any necessary
117
+ consents, permissions or other rights required for any use of the
118
+ Work.
119
+ d. Affirmer understands and acknowledges that Creative Commons is not a
120
+ party to this document and has no duty or obligation with respect to
121
+ this CC0 or use of the Work.
@@ -0,0 +1,8 @@
1
+ # The source package (sdist) holds only what "python run_tests.py" needs. The wheel holds only py/ledgdex.
2
+ include README.md LICENSE run_tests.py
3
+ recursive-include py/tests *.py *.mjs
4
+ recursive-include vectors *
5
+ recursive-include js *.js *.mjs *.json
6
+ recursive-include guide/examples *
7
+ include guide/tutorial.md guide/dex/ledgdex.jsonl
8
+ global-exclude __pycache__ *.pyc
@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.4
2
+ Name: ledgdex
3
+ Version: 1.0.20
4
+ Summary: Simple market on a dex: signed, append-only ledgers with triple-entry accounting
5
+ Author: Noorul Ali
6
+ License: CC0-1.0
7
+ Project-URL: Homepage, https://ledgdex.github.io
8
+ Project-URL: Documentation, https://ledgdex.github.io/docs/
9
+ Project-URL: Source, https://github.com/ledgdex/ledgdex
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Environment :: Console
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: dexweb<5,>=4.2.5
18
+ Provides-Extra: fast
19
+ Requires-Dist: cryptography>=42; extra == "fast"
20
+ Dynamic: license-file
21
+
22
+ # Ledgdex
23
+
24
+ A simple market on a dex. Every seller and buyer keeps a signed, append-only ledger (`ledgdex.jsonl`) in their own dex, a freely shared website built with [dexweb](https://github.com/matrixdex/dexweb). A trade exists three times: the buyer's signed claim, the seller's signed record of it, and the buyer's copy of that record. No server, no blockchain: SHA-256, Ed25519 and plain files.
25
+
26
+ **Site: https://ledgdex.github.io** (viewer and live markets). **Docs: https://ledgdex.github.io/docs/** (getting started, setting up a marketplace, extending ledgdex, bots, the command line and API references, and tested examples). The docs are a dex built with dexweb, and a ledgdex too: dex is the core; dex + ledger is a ledgdex. Their source is [guide/](guide/).
27
+
28
+ The specification is [architecture/SPEC.md](architecture/SPEC.md). Everything in it is built: the market (offers, claims, payment, delivery, receipts), disputes, sealed-bid auctions, device keys, key rotation and recovery, indexes, the root, dex pages, publishing and tamper checks, in Python, plus a JavaScript version and a browser viewer.
29
+
30
+ ## Install
31
+
32
+ ```
33
+ pip install "ledgdex[fast]"
34
+ ```
35
+
36
+ From the source on GitHub: `pip install "ledgdex[fast] @ git+https://github.com/ledgdex/ledgdex@v1.0.20"`. From a clone,
37
+ to work on ledgdex: `pip install -e ".[fast]"`. Releases before 1.0.20 are on GitHub only, as tags; add
38
+ `#subdirectory=py` to their address.
39
+
40
+ Python 3.8 or later. The only dependency is dexweb (4.2.5 or later, before 5). Private keys live in `~/.ledgdex` (or `$LEDGDEX_HOME`), never in a dex, with mode 600: ledgdex refuses a key file others can read.
41
+
42
+ The `fast` extra adds the `cryptography` package: signing and verifying get about 20 to 25 times faster. Without it ledgdex uses its own pure-Python Ed25519, with the same results. Verified ledgers are also cached (in `~/.cache/ledgdex`, or `$LEDGDEX_CACHE`) by the exact bytes already verified, so a ledger that grew is only verified from where it was verified before; any changed byte is verified in full. `ledgdex verify --full` and `LEDGDEX_NO_CACHE=1` skip the cache, and `LEDGDEX_PURE=1` forces the pure-Python Ed25519.
43
+
44
+ ## Sell
45
+
46
+ ```
47
+ ledgdex init shop --name "Mango Farm" --about "Alphonso mangoes from Ratnagiri" --key farm \
48
+ --url https://farm.github.io --dest git@github.com:farm/farm.github.io.git --branch main
49
+ ledgdex offer shop offer.json
50
+ ledgdex publish shop
51
+ ```
52
+
53
+ `offer.json`:
54
+
55
+ ```json
56
+ {"item": {"title": "Alphonso mangoes", "text": "One dozen, ripe."},
57
+ "quantity": 10, "unit": "dozen", "currency": "INR", "price": 120000,
58
+ "pay": [{"method": "upi", "to": "farm@bank"}]}
59
+ ```
60
+
61
+ Prices are integers in the currency's smallest unit (120000 is INR 1,200.00). Left out: `allow` is `"any"`, `terms` is empty, and the arbiter is the seller.
62
+
63
+ ## A whole shop from a catalog
64
+
65
+ ```
66
+ ledgdex shop catalog.json --repo OWNER/NAME
67
+ ```
68
+
69
+ One command makes the shop from a JSON catalog: the ledgdex (if it is missing), one offer for each product, and the
70
+ publish with dexweb to GitHub. Run it again after the catalog changes: unchanged products keep their offers, new and
71
+ changed products get new offers, and the offers of changed and removed products are withdrawn. The same catalog again
72
+ signs nothing. In Python: `from ledgdex.shop import shop; shop("catalog.json", repo="OWNER/NAME")`.
73
+
74
+ ```json
75
+ {"shop": {"name": "Penguin Shop", "currency": "USD", "pay": [{"method": "card", "to": "https://pay.example/penguin"}]},
76
+ "products": [{"title": "Ice axe", "price": 4999, "quantity": 20, "text": "Steel, 70 cm"}]}
77
+ ```
78
+
79
+ `shop.name`, `shop.currency` and `shop.pay` (one or more methods) are necessary; each product needs `title`, `price`
80
+ and `quantity`. Unknown fields are refused, and a wrong product stops the run before anything is signed. The full
81
+ format is in spec 7.5. GitHub Pages must be on for the repository (`OWNER.github.io` always has it).
82
+
83
+ ## Buy
84
+
85
+ ```
86
+ ledgdex init me --name "Asha" --key asha --url https://asha.github.io --dest git@github.com:asha/asha.github.io.git
87
+ ledgdex claim me https://farm.github.io OFFER_ID --quantity 2
88
+ ledgdex publish me
89
+ ```
90
+
91
+ The claim is kept in your ledger as `sent`. Publishing your dex is sending it: the seller collects it with `ledgdex record shop --from https://asha.github.io`, or you send the claim file any other way and the seller runs `ledgdex record shop claim-....json`.
92
+
93
+ ## The rest of a trade
94
+
95
+ ```
96
+ ledgdex receipt me https://farm.github.io buyer keeps the seller's record: the receipt and the claim id
97
+ ledgdex pay me https://farm.github.io CLAIM_ID --method upi --ref UTR123
98
+ ledgdex record shop --from https://asha.github.io seller records the payment notice
99
+ ledgdex received shop CLAIM_ID 240000
100
+ ledgdex delivered shop CLAIM_ID --note "by courier"
101
+ ledgdex confirm me https://farm.github.io CLAIM_ID
102
+ ledgdex record shop --from https://asha.github.io the claim is now closed
103
+ ```
104
+
105
+ Publish after each step that others need to see. Anyone can check any ledger:
106
+
107
+ ```
108
+ ledgdex verify https://farm.github.io
109
+ ledgdex state https://farm.github.io
110
+ ```
111
+
112
+ ## Disputes
113
+
114
+ ```
115
+ ledgdex dispute me CLAIM_ID --seller https://farm.github.io --text "never arrived" buyer
116
+ ledgdex record shop --from https://asha.github.io
117
+ ledgdex ruling judge https://farm.github.io DISPUTE_ID --outcome refund the offer's arbiter
118
+ ledgdex record shop --from https://judge.github.io
119
+ ```
120
+
121
+ ## Sealed-bid auctions
122
+
123
+ ```
124
+ ledgdex auction shop auction.json {"item": {"title": "Old map"}, "currency": "INR", "close": "...", "reveal_until": "...", "reserve": 10000}
125
+ ledgdex bid me https://farm.github.io AUCTION_ID --amount 25000 only a commitment is sent; the amount stays in ~/.ledgdex/bids
126
+ ledgdex reveal me https://farm.github.io AUCTION_ID after the close, before reveal_until
127
+ ```
128
+
129
+ The seller records bids and reveals with `ledgdex record`. The best revealed bid that meets the reserve wins; payment and delivery then use the auction id in place of a claim id.
130
+
131
+ ## Keys
132
+
133
+ ```
134
+ ledgdex keygen phone on the phone: prints its public key
135
+ ledgdex device add shop ed25519:... --name phone with the owner key: the phone may now sign
136
+ ledgdex device revoke shop ed25519:... --reason lost
137
+ ledgdex rotate shop --key farm2 replace the owner key
138
+ ledgdex recover root shop ed25519:NEW the root names a new key for a lost one
139
+ ledgdex recovered shop --root ROOT --key farm-new the owner takes the ledger back
140
+ ```
141
+
142
+ On a machine with a device key, everyday commands sign with it. Messages to other ledgers (claims, payments, bids) are signed with the owner key, which is a self's identity in other ledgers.
143
+
144
+ ## Indexes and the root
145
+
146
+ ```
147
+ ledgdex list market https://farm.github.io --note "mangoes" an index lists ledgers
148
+ ledgdex discover https://market.github.io --offers what the listed ledgers sell
149
+ ledgdex admit root ed25519:... the root admits selves to The Matrix
150
+ ledgdex state shop --root https://root.github.io "allow": "admitted" then needs the root too
151
+ ```
152
+
153
+ ## Running the root
154
+
155
+ The root is the ledger whose admissions define who is in The Matrix, and its owner is the default arbiter. Today
156
+ Noorul Ali, founder of The Matrix, runs it. It is an ordinary ledgdex:
157
+
158
+ ```
159
+ ledgdex init root --name "The Matrix root" --about "Admission to The Matrix" --key matrix-root \
160
+ --url https://ROOT_ADDRESS --dest git@github.com:YOU/root.git --branch main
161
+ ledgdex publish root
162
+ ```
163
+
164
+ Keep the owner key (`~/.ledgdex/matrix-root.key`) backed up offline. For daily work, give the laptop a device key,
165
+ which may admit and revoke but not change keys. Recovery hands a ledger to a new key, so `ledgdex recover` needs the
166
+ owner key itself:
167
+
168
+ ```
169
+ ledgdex keygen root-laptop prints ed25519:...
170
+ ledgdex device add root ed25519:... --name laptop with the owner key, once
171
+ ```
172
+
173
+ Then, with `ledgdex publish root` after each change:
174
+
175
+ ```
176
+ ledgdex admit root ed25519:THEIR_KEY --name "Shop 12" --note "ground floor" admit a self
177
+ ledgdex revoke root ed25519:THEIR_KEY --reason "left the mall" take it back
178
+ ledgdex recover root https://THEIR_DEX ed25519:THEIR_NEW_KEY with the owner key, after checking who they are
179
+ ledgdex list root https://THEIR_DEX --note "Shop 12" optional: the root as an index too
180
+ ```
181
+
182
+ Every dex that trades in The Matrix names the root in its `config.json`, as `"ledgdex": {"root": "https://ROOT_ADDRESS"}`.
183
+ The first time ledgdex reads it, it adds `"root_id"`, the root's ledger id, and from then on refuses any other ledger
184
+ at that address (or a broken one): whoever controls the address cannot swap in a root of their own. Set `root_id`
185
+ yourself, from a source you trust, to pin it before the first read. The dex also keeps the last root it read, in `.ledgdex-root.jsonl`: when
186
+ the root's address is down, commands work on with that copy, and an address serving an older root is ignored.
187
+ Its "admitted" offers then need admission by the root too, and its offers and auctions default to the root's owner as
188
+ arbiter. Rulings are signed with the owner key, since that is the arbiter key offers name:
189
+
190
+ ```
191
+ ledgdex ruling root https://SELLER_DEX DISPUTE_ID --outcome refund --text "..."
192
+ ```
193
+
194
+ Run `ledgdex check` on the root (the GitHub workflow, or cron) so any change to its history is caught.
195
+
196
+ ## JavaScript and the viewer
197
+
198
+ `js/` holds the same core in plain browser JavaScript (no npm): canonical JSON, SHA-256/512, Ed25519, verification, the state function, the dex pages (`render.js`) and the dexweb build (`dexweb.js`). `node js/test.mjs` checks it reproduces the shared vectors in `vectors/` byte for byte: states, pages, and whole dexs that dexweb built. `python py/tests/make_vectors.py` regenerates them.
199
+
200
+ The site https://ledgdex.github.io comes from three dexes. Dexweb publishes them to [ledgdex/ledgdex.github.io](https://github.com/ledgdex/ledgdex.github.io), and GitHub Pages serves that repository.
201
+
202
+ | Source | Published to | Address | Publish with |
203
+ |---|---|---|---|
204
+ | `home/` (home page, Live Markets, About) | the root | https://ledgdex.github.io | `cd home && python run.py` |
205
+ | `viewer/` (the viewer and the JavaScript modules) | `viewer/` | https://ledgdex.github.io/viewer/index.html | `cd viewer && python run.py` |
206
+ | `guide/dex/` (the docs) | `docs/` | https://ledgdex.github.io/docs/ | `cd guide/dex && ledgdex publish .` |
207
+
208
+ After you change `viewer/` or `js/`, publish the viewer again. `python run.py --build-only` builds `gen/` and does not publish. The home pages run no scripts. Only the viewer runs scripts.
209
+
210
+ The viewer page loads any ledger by address or file, verifies it in the browser, and shows it as the same pages its dex has. It signs claims, payments, confirmations, disputes, bids and reveals with a key kept in the browser, using the browser's own constant-time Web Crypto Ed25519 when it has one, else ledgdex's built-in code. The key is stored only sealed with your passphrase ("Keep, sealed"), and "Download key backup" gives you a copy to keep offline. The viewer also keeps your own ledger: every message you sign is kept as `sent`, and "Collect receipts" keeps the seller's records of them. "Download your dex" gives you your ledger as a complete dex (a zip), file for file what `ledgdex init` and dexweb make; publish it like any dex. So a buyer with only a browser has a ledgdex too. Open a ledger directly with `https://ledgdex.github.io/viewer/index.html?ledger=https://farm.github.io`.
211
+
212
+ ## Checking for tampering
213
+
214
+ `ledgdex verify` checks one ledger once. `ledgdex check` checks ledgers over time:
215
+
216
+ ```
217
+ ledgdex check shop https://farm.github.io once; exit code 1 on tampering
218
+ ledgdex check shop --every 3600 as a worker, every hour
219
+ 17 6 * * * cd /srv/ledgdex && ledgdex check shop --json report.json from cron
220
+ ledgdex workflow seller/ledgdex.jsonl > .github/workflows/ledgdex-check.yml as a GitHub workflow
221
+ ```
222
+
223
+ It keeps the last copy of every ledger it has seen (in `.ledgdex-check/`) and reports an error, with signed proof in `.ledgdex-check/proofs/`, when a ledger stops extending that copy (two entries at one seq), when a receipt no longer matches the ledger it came from, or when an address starts serving a different ledger. Receipts in the ledgers you check are followed to the sellers' ledgers automatically. `--media` also checks offer pictures against their hashes; `--published` compares a dex folder with its published ledger. A copy that is only behind (a cache or an old snapshot) is a warning, not an error. The GitHub workflow runs on every push, daily and by hand, keeps its last-seen copies in the Actions cache, and attaches the report and proofs to each run.
224
+
225
+ ## Every ledgdex is a dex
226
+
227
+ Each command rewrites the ledgdex pages in `data.json` (pages whose first paragraph starts with `<!-- ledgdex -->`) and rebuilds the site with dexweb. Your own pages are never touched. `ledgdex publish` publishes with dexweb; `config.json` has `"append_only": ["ledgdex.jsonl"]`, so dexweb refuses to publish a ledger that does not extend the published one. Publishing from two devices is safe: the second one catches up and re-sequences its unpublished entries.
228
+
229
+ ## Production (v1.0)
230
+
231
+ Before you trade for real, read [SECURITY.md](SECURITY.md). In short:
232
+
233
+ - Install the `fast` extra (`pip install "ledgdex[fast]"`) on every machine that signs: it signs in constant time. ledgdex warns when it signs without it.
234
+ - Keep owner keys offline (backed up), and use device keys day to day. Revoke a lost device at once.
235
+ - Run `ledgdex check` on your ledger and the ledgers you trade with, daily (the workflow, cron or a worker). An error there is tampering, with proof.
236
+ - In the browser, keep your key sealed with a long passphrase and download a backup. Use the viewer at https://ledgdex.github.io/viewer/index.html, or host it on an address of its own: every page on one origin can read what pages there store.
237
+ - Generated workflows install ledgdex from the release tag and pin each action to a commit.
238
+
239
+ ## Tests
240
+
241
+ Two suites. `python run_tests.py` is the essential one (about 30 s): every Python test except the docs examples and
242
+ the tutorial, with the fast backend, and the JavaScript core against the shared vectors; run it after small changes.
243
+ `python run_tests.py --full` is the full one (about 10 min): every Python test on both Ed25519 backends, the
244
+ JavaScript vectors, and a round of differential fuzzing; run it after larger changes and before a release. CI runs
245
+ the essential suite on every push (on Linux), and the full suite (on Linux, macOS and Windows) for release tags, by
246
+ hand, weekly, and for a push whose commit message contains `[full tests]`. Both work in a clone and in
247
+ the source package on PyPI, which holds the tests and their data; the wheel holds only the code. Steps:
248
+ https://ledgdex.github.io/docs/testlocalledgdexinstallation.html. It needs git and node 20 or later. The parts one by one:
249
+
250
+ ```
251
+ cd py && python -m unittest discover -s tests
252
+ node js/test.mjs
253
+ cd py && python tests/fuzz.py --seed 7 --ledgers 1000 Python and JavaScript must agree on random and corrupted ledgers
254
+ ```
@@ -0,0 +1,233 @@
1
+ # Ledgdex
2
+
3
+ A simple market on a dex. Every seller and buyer keeps a signed, append-only ledger (`ledgdex.jsonl`) in their own dex, a freely shared website built with [dexweb](https://github.com/matrixdex/dexweb). A trade exists three times: the buyer's signed claim, the seller's signed record of it, and the buyer's copy of that record. No server, no blockchain: SHA-256, Ed25519 and plain files.
4
+
5
+ **Site: https://ledgdex.github.io** (viewer and live markets). **Docs: https://ledgdex.github.io/docs/** (getting started, setting up a marketplace, extending ledgdex, bots, the command line and API references, and tested examples). The docs are a dex built with dexweb, and a ledgdex too: dex is the core; dex + ledger is a ledgdex. Their source is [guide/](guide/).
6
+
7
+ The specification is [architecture/SPEC.md](architecture/SPEC.md). Everything in it is built: the market (offers, claims, payment, delivery, receipts), disputes, sealed-bid auctions, device keys, key rotation and recovery, indexes, the root, dex pages, publishing and tamper checks, in Python, plus a JavaScript version and a browser viewer.
8
+
9
+ ## Install
10
+
11
+ ```
12
+ pip install "ledgdex[fast]"
13
+ ```
14
+
15
+ From the source on GitHub: `pip install "ledgdex[fast] @ git+https://github.com/ledgdex/ledgdex@v1.0.20"`. From a clone,
16
+ to work on ledgdex: `pip install -e ".[fast]"`. Releases before 1.0.20 are on GitHub only, as tags; add
17
+ `#subdirectory=py` to their address.
18
+
19
+ Python 3.8 or later. The only dependency is dexweb (4.2.5 or later, before 5). Private keys live in `~/.ledgdex` (or `$LEDGDEX_HOME`), never in a dex, with mode 600: ledgdex refuses a key file others can read.
20
+
21
+ The `fast` extra adds the `cryptography` package: signing and verifying get about 20 to 25 times faster. Without it ledgdex uses its own pure-Python Ed25519, with the same results. Verified ledgers are also cached (in `~/.cache/ledgdex`, or `$LEDGDEX_CACHE`) by the exact bytes already verified, so a ledger that grew is only verified from where it was verified before; any changed byte is verified in full. `ledgdex verify --full` and `LEDGDEX_NO_CACHE=1` skip the cache, and `LEDGDEX_PURE=1` forces the pure-Python Ed25519.
22
+
23
+ ## Sell
24
+
25
+ ```
26
+ ledgdex init shop --name "Mango Farm" --about "Alphonso mangoes from Ratnagiri" --key farm \
27
+ --url https://farm.github.io --dest git@github.com:farm/farm.github.io.git --branch main
28
+ ledgdex offer shop offer.json
29
+ ledgdex publish shop
30
+ ```
31
+
32
+ `offer.json`:
33
+
34
+ ```json
35
+ {"item": {"title": "Alphonso mangoes", "text": "One dozen, ripe."},
36
+ "quantity": 10, "unit": "dozen", "currency": "INR", "price": 120000,
37
+ "pay": [{"method": "upi", "to": "farm@bank"}]}
38
+ ```
39
+
40
+ Prices are integers in the currency's smallest unit (120000 is INR 1,200.00). Left out: `allow` is `"any"`, `terms` is empty, and the arbiter is the seller.
41
+
42
+ ## A whole shop from a catalog
43
+
44
+ ```
45
+ ledgdex shop catalog.json --repo OWNER/NAME
46
+ ```
47
+
48
+ One command makes the shop from a JSON catalog: the ledgdex (if it is missing), one offer for each product, and the
49
+ publish with dexweb to GitHub. Run it again after the catalog changes: unchanged products keep their offers, new and
50
+ changed products get new offers, and the offers of changed and removed products are withdrawn. The same catalog again
51
+ signs nothing. In Python: `from ledgdex.shop import shop; shop("catalog.json", repo="OWNER/NAME")`.
52
+
53
+ ```json
54
+ {"shop": {"name": "Penguin Shop", "currency": "USD", "pay": [{"method": "card", "to": "https://pay.example/penguin"}]},
55
+ "products": [{"title": "Ice axe", "price": 4999, "quantity": 20, "text": "Steel, 70 cm"}]}
56
+ ```
57
+
58
+ `shop.name`, `shop.currency` and `shop.pay` (one or more methods) are necessary; each product needs `title`, `price`
59
+ and `quantity`. Unknown fields are refused, and a wrong product stops the run before anything is signed. The full
60
+ format is in spec 7.5. GitHub Pages must be on for the repository (`OWNER.github.io` always has it).
61
+
62
+ ## Buy
63
+
64
+ ```
65
+ ledgdex init me --name "Asha" --key asha --url https://asha.github.io --dest git@github.com:asha/asha.github.io.git
66
+ ledgdex claim me https://farm.github.io OFFER_ID --quantity 2
67
+ ledgdex publish me
68
+ ```
69
+
70
+ The claim is kept in your ledger as `sent`. Publishing your dex is sending it: the seller collects it with `ledgdex record shop --from https://asha.github.io`, or you send the claim file any other way and the seller runs `ledgdex record shop claim-....json`.
71
+
72
+ ## The rest of a trade
73
+
74
+ ```
75
+ ledgdex receipt me https://farm.github.io buyer keeps the seller's record: the receipt and the claim id
76
+ ledgdex pay me https://farm.github.io CLAIM_ID --method upi --ref UTR123
77
+ ledgdex record shop --from https://asha.github.io seller records the payment notice
78
+ ledgdex received shop CLAIM_ID 240000
79
+ ledgdex delivered shop CLAIM_ID --note "by courier"
80
+ ledgdex confirm me https://farm.github.io CLAIM_ID
81
+ ledgdex record shop --from https://asha.github.io the claim is now closed
82
+ ```
83
+
84
+ Publish after each step that others need to see. Anyone can check any ledger:
85
+
86
+ ```
87
+ ledgdex verify https://farm.github.io
88
+ ledgdex state https://farm.github.io
89
+ ```
90
+
91
+ ## Disputes
92
+
93
+ ```
94
+ ledgdex dispute me CLAIM_ID --seller https://farm.github.io --text "never arrived" buyer
95
+ ledgdex record shop --from https://asha.github.io
96
+ ledgdex ruling judge https://farm.github.io DISPUTE_ID --outcome refund the offer's arbiter
97
+ ledgdex record shop --from https://judge.github.io
98
+ ```
99
+
100
+ ## Sealed-bid auctions
101
+
102
+ ```
103
+ ledgdex auction shop auction.json {"item": {"title": "Old map"}, "currency": "INR", "close": "...", "reveal_until": "...", "reserve": 10000}
104
+ ledgdex bid me https://farm.github.io AUCTION_ID --amount 25000 only a commitment is sent; the amount stays in ~/.ledgdex/bids
105
+ ledgdex reveal me https://farm.github.io AUCTION_ID after the close, before reveal_until
106
+ ```
107
+
108
+ The seller records bids and reveals with `ledgdex record`. The best revealed bid that meets the reserve wins; payment and delivery then use the auction id in place of a claim id.
109
+
110
+ ## Keys
111
+
112
+ ```
113
+ ledgdex keygen phone on the phone: prints its public key
114
+ ledgdex device add shop ed25519:... --name phone with the owner key: the phone may now sign
115
+ ledgdex device revoke shop ed25519:... --reason lost
116
+ ledgdex rotate shop --key farm2 replace the owner key
117
+ ledgdex recover root shop ed25519:NEW the root names a new key for a lost one
118
+ ledgdex recovered shop --root ROOT --key farm-new the owner takes the ledger back
119
+ ```
120
+
121
+ On a machine with a device key, everyday commands sign with it. Messages to other ledgers (claims, payments, bids) are signed with the owner key, which is a self's identity in other ledgers.
122
+
123
+ ## Indexes and the root
124
+
125
+ ```
126
+ ledgdex list market https://farm.github.io --note "mangoes" an index lists ledgers
127
+ ledgdex discover https://market.github.io --offers what the listed ledgers sell
128
+ ledgdex admit root ed25519:... the root admits selves to The Matrix
129
+ ledgdex state shop --root https://root.github.io "allow": "admitted" then needs the root too
130
+ ```
131
+
132
+ ## Running the root
133
+
134
+ The root is the ledger whose admissions define who is in The Matrix, and its owner is the default arbiter. Today
135
+ Noorul Ali, founder of The Matrix, runs it. It is an ordinary ledgdex:
136
+
137
+ ```
138
+ ledgdex init root --name "The Matrix root" --about "Admission to The Matrix" --key matrix-root \
139
+ --url https://ROOT_ADDRESS --dest git@github.com:YOU/root.git --branch main
140
+ ledgdex publish root
141
+ ```
142
+
143
+ Keep the owner key (`~/.ledgdex/matrix-root.key`) backed up offline. For daily work, give the laptop a device key,
144
+ which may admit and revoke but not change keys. Recovery hands a ledger to a new key, so `ledgdex recover` needs the
145
+ owner key itself:
146
+
147
+ ```
148
+ ledgdex keygen root-laptop prints ed25519:...
149
+ ledgdex device add root ed25519:... --name laptop with the owner key, once
150
+ ```
151
+
152
+ Then, with `ledgdex publish root` after each change:
153
+
154
+ ```
155
+ ledgdex admit root ed25519:THEIR_KEY --name "Shop 12" --note "ground floor" admit a self
156
+ ledgdex revoke root ed25519:THEIR_KEY --reason "left the mall" take it back
157
+ ledgdex recover root https://THEIR_DEX ed25519:THEIR_NEW_KEY with the owner key, after checking who they are
158
+ ledgdex list root https://THEIR_DEX --note "Shop 12" optional: the root as an index too
159
+ ```
160
+
161
+ Every dex that trades in The Matrix names the root in its `config.json`, as `"ledgdex": {"root": "https://ROOT_ADDRESS"}`.
162
+ The first time ledgdex reads it, it adds `"root_id"`, the root's ledger id, and from then on refuses any other ledger
163
+ at that address (or a broken one): whoever controls the address cannot swap in a root of their own. Set `root_id`
164
+ yourself, from a source you trust, to pin it before the first read. The dex also keeps the last root it read, in `.ledgdex-root.jsonl`: when
165
+ the root's address is down, commands work on with that copy, and an address serving an older root is ignored.
166
+ Its "admitted" offers then need admission by the root too, and its offers and auctions default to the root's owner as
167
+ arbiter. Rulings are signed with the owner key, since that is the arbiter key offers name:
168
+
169
+ ```
170
+ ledgdex ruling root https://SELLER_DEX DISPUTE_ID --outcome refund --text "..."
171
+ ```
172
+
173
+ Run `ledgdex check` on the root (the GitHub workflow, or cron) so any change to its history is caught.
174
+
175
+ ## JavaScript and the viewer
176
+
177
+ `js/` holds the same core in plain browser JavaScript (no npm): canonical JSON, SHA-256/512, Ed25519, verification, the state function, the dex pages (`render.js`) and the dexweb build (`dexweb.js`). `node js/test.mjs` checks it reproduces the shared vectors in `vectors/` byte for byte: states, pages, and whole dexs that dexweb built. `python py/tests/make_vectors.py` regenerates them.
178
+
179
+ The site https://ledgdex.github.io comes from three dexes. Dexweb publishes them to [ledgdex/ledgdex.github.io](https://github.com/ledgdex/ledgdex.github.io), and GitHub Pages serves that repository.
180
+
181
+ | Source | Published to | Address | Publish with |
182
+ |---|---|---|---|
183
+ | `home/` (home page, Live Markets, About) | the root | https://ledgdex.github.io | `cd home && python run.py` |
184
+ | `viewer/` (the viewer and the JavaScript modules) | `viewer/` | https://ledgdex.github.io/viewer/index.html | `cd viewer && python run.py` |
185
+ | `guide/dex/` (the docs) | `docs/` | https://ledgdex.github.io/docs/ | `cd guide/dex && ledgdex publish .` |
186
+
187
+ After you change `viewer/` or `js/`, publish the viewer again. `python run.py --build-only` builds `gen/` and does not publish. The home pages run no scripts. Only the viewer runs scripts.
188
+
189
+ The viewer page loads any ledger by address or file, verifies it in the browser, and shows it as the same pages its dex has. It signs claims, payments, confirmations, disputes, bids and reveals with a key kept in the browser, using the browser's own constant-time Web Crypto Ed25519 when it has one, else ledgdex's built-in code. The key is stored only sealed with your passphrase ("Keep, sealed"), and "Download key backup" gives you a copy to keep offline. The viewer also keeps your own ledger: every message you sign is kept as `sent`, and "Collect receipts" keeps the seller's records of them. "Download your dex" gives you your ledger as a complete dex (a zip), file for file what `ledgdex init` and dexweb make; publish it like any dex. So a buyer with only a browser has a ledgdex too. Open a ledger directly with `https://ledgdex.github.io/viewer/index.html?ledger=https://farm.github.io`.
190
+
191
+ ## Checking for tampering
192
+
193
+ `ledgdex verify` checks one ledger once. `ledgdex check` checks ledgers over time:
194
+
195
+ ```
196
+ ledgdex check shop https://farm.github.io once; exit code 1 on tampering
197
+ ledgdex check shop --every 3600 as a worker, every hour
198
+ 17 6 * * * cd /srv/ledgdex && ledgdex check shop --json report.json from cron
199
+ ledgdex workflow seller/ledgdex.jsonl > .github/workflows/ledgdex-check.yml as a GitHub workflow
200
+ ```
201
+
202
+ It keeps the last copy of every ledger it has seen (in `.ledgdex-check/`) and reports an error, with signed proof in `.ledgdex-check/proofs/`, when a ledger stops extending that copy (two entries at one seq), when a receipt no longer matches the ledger it came from, or when an address starts serving a different ledger. Receipts in the ledgers you check are followed to the sellers' ledgers automatically. `--media` also checks offer pictures against their hashes; `--published` compares a dex folder with its published ledger. A copy that is only behind (a cache or an old snapshot) is a warning, not an error. The GitHub workflow runs on every push, daily and by hand, keeps its last-seen copies in the Actions cache, and attaches the report and proofs to each run.
203
+
204
+ ## Every ledgdex is a dex
205
+
206
+ Each command rewrites the ledgdex pages in `data.json` (pages whose first paragraph starts with `<!-- ledgdex -->`) and rebuilds the site with dexweb. Your own pages are never touched. `ledgdex publish` publishes with dexweb; `config.json` has `"append_only": ["ledgdex.jsonl"]`, so dexweb refuses to publish a ledger that does not extend the published one. Publishing from two devices is safe: the second one catches up and re-sequences its unpublished entries.
207
+
208
+ ## Production (v1.0)
209
+
210
+ Before you trade for real, read [SECURITY.md](SECURITY.md). In short:
211
+
212
+ - Install the `fast` extra (`pip install "ledgdex[fast]"`) on every machine that signs: it signs in constant time. ledgdex warns when it signs without it.
213
+ - Keep owner keys offline (backed up), and use device keys day to day. Revoke a lost device at once.
214
+ - Run `ledgdex check` on your ledger and the ledgers you trade with, daily (the workflow, cron or a worker). An error there is tampering, with proof.
215
+ - In the browser, keep your key sealed with a long passphrase and download a backup. Use the viewer at https://ledgdex.github.io/viewer/index.html, or host it on an address of its own: every page on one origin can read what pages there store.
216
+ - Generated workflows install ledgdex from the release tag and pin each action to a commit.
217
+
218
+ ## Tests
219
+
220
+ Two suites. `python run_tests.py` is the essential one (about 30 s): every Python test except the docs examples and
221
+ the tutorial, with the fast backend, and the JavaScript core against the shared vectors; run it after small changes.
222
+ `python run_tests.py --full` is the full one (about 10 min): every Python test on both Ed25519 backends, the
223
+ JavaScript vectors, and a round of differential fuzzing; run it after larger changes and before a release. CI runs
224
+ the essential suite on every push (on Linux), and the full suite (on Linux, macOS and Windows) for release tags, by
225
+ hand, weekly, and for a push whose commit message contains `[full tests]`. Both work in a clone and in
226
+ the source package on PyPI, which holds the tests and their data; the wheel holds only the code. Steps:
227
+ https://ledgdex.github.io/docs/testlocalledgdexinstallation.html. It needs git and node 20 or later. The parts one by one:
228
+
229
+ ```
230
+ cd py && python -m unittest discover -s tests
231
+ node js/test.mjs
232
+ cd py && python tests/fuzz.py --seed 7 --ledgers 1000 Python and JavaScript must agree on random and corrupted ledgers
233
+ ```
@@ -0,0 +1,5 @@
1
+ {"ledger":1,"name":"Ledgdex Docs","owner":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65"}
2
+ {"msg":{"at":"2026-10-20T09:00:00Z","body":{"about":"The ledger of the ledgdex docs: a demonstration. Its key was destroyed after signing, so nothing can be added to it, and nobody may buy from it.","dex":"https://ledgdex.github.io/docs"},"by":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65","sig":"48971bc58b4e5b55ff61660d5348fd0c7084443ebbaef1bb055ed36220930c21124dbd8304b828f8bd9518a6745deedc7fb14fed00b21c31f2e6551adc756c01","type":"open","v":1},"prev":"sha256:7b3d502db18a78b0c53a2c23d0ae2cf6281d27aa98708f9ae3edf0802d8f345c","seq":0,"sig":"608a79e5be4420a51b851629457143d7edeba04913687bdd06a0760fbd82bbefd57555fabe4e183a77c9eef07e6e3a1522bde05013a3c3657135e183b741840d","time":"2026-10-20T09:00:00Z"}
3
+ {"msg":{"at":"2026-10-20T09:00:01Z","body":{"allow":[],"arbiter":{"key":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65","url":""},"currency":"USD","item":{"media":[],"text":"A demonstration of the page ledgdex makes for an offer: the weights of Penjointu, a neural network that analyzes the bone density of knee joints. Nobody may buy it.","title":"Example offer: Penjointu v1 weights"},"pay":[{"method":"card","to":"https://pay.example/biobot"}],"price":250000,"quantity":10,"terms":"An example in the ledgdex docs: \"allow\" is an empty list, so every claim is refused.","unit":"license"},"by":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65","sig":"f15781fbad22189ca3d83b0f1e820e54033a5bd430443acc497c2434c8fe656475eb878ed97b60eceb592839ad671fe9ae65bda85f19b8c76ce1749ea8681c0c","type":"offer","v":1},"prev":"sha256:c934353ae5d46814eeb94844b46dbc31ae9af77b6fe4c70785d37e1a831c8e6b","seq":1,"sig":"fe42164cfea5a20fb3747c301029c4d4196d467f2f44f60a7963ad61d88f4713178eba4bb67b28685a3e1bbf8345050dad99ae2a194fab742bcf8cd2db216801","time":"2026-10-20T09:00:01Z"}
4
+ {"msg":{"at":"2026-10-20T09:00:02Z","body":{"allow":[],"arbiter":{"key":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65","url":""},"best":"highest","close":"2026-10-28T09:00:00Z","currency":"USD","item":{"media":[],"text":"A demonstration of the page ledgdex makes for a sealed-bid auction: the v2 weights, 90 days before all other clinics. Nobody may bid.","title":"Example auction: Penjointu v2 early access"},"reserve":1000000,"reveal_until":"2026-10-29T09:00:00Z","terms":"An example in the ledgdex docs."},"by":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65","sig":"cb188870d7b4189fd5591c7ae7e0b5776d89085594edde2b18475ba2eff2f7cc5daa5c7d393f7d30d5648a5a75af0b5191698669fefecaef910eb6240d11db07","type":"auction","v":1},"prev":"sha256:039bb4080d22c05d5f464e137c3f8ec720e64734bcd52fdf7677c3a72ca7aadc","seq":2,"sig":"598a418c8a10c57adc628ffc623f074857033e0e11a4d6ef561c941e6066f0fa876dbbe9957c4e84c103e2b5271823d2896c61c724a624a472d888b53041db08","time":"2026-10-20T09:00:02Z"}
5
+ {"msg":{"at":"2026-10-20T09:00:03Z","body":{"ref":"sha256:039bb4080d22c05d5f464e137c3f8ec720e64734bcd52fdf7677c3a72ca7aadc","text":"This ledger was signed once, for the docs."},"by":"ed25519:109fe19cccb5efede333f889bfedf3bb30ab39b0eeec7d6898dbb40de25c1a65","sig":"6593e48a66c33bb60f69854707f35cfaa7679bb3e10cc078f03c4132e2db7811798ee9383f38aa13dfc88461ed9ece4b37f0d8c7bea8f3c7193ed99beb3b2000","type":"note","v":1},"prev":"sha256:e78a0fa08f3f9c1fb85d69fc66e43e9ac5a04248f0921cc303f2fe302b2cc664","seq":3,"sig":"2ad71ad9ef800cade5e92a381fe7dd2f3cea86eea2a489ae16500429a8e3ee252afaa6a725d1aa3a205aa855b6c6a3797a1000aba126df04ad006c725095b50f","time":"2026-10-20T09:00:03Z"}