@vtechcom/hexcore 2.0.0-beta.1

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 (173) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/LICENSE +201 -0
  3. package/README.md +283 -0
  4. package/dist/app.module.js +55 -0
  5. package/dist/app.module.js.map +1 -0
  6. package/dist/auth.js +151 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/chain/cost-models.js +81 -0
  9. package/dist/chain/cost-models.js.map +1 -0
  10. package/dist/chain/provider.js +269 -0
  11. package/dist/chain/provider.js.map +1 -0
  12. package/dist/chain/socket.js +48 -0
  13. package/dist/chain/socket.js.map +1 -0
  14. package/dist/cli/doctor.js +43 -0
  15. package/dist/cli/doctor.js.map +1 -0
  16. package/dist/cli/hexcore.js +435 -0
  17. package/dist/cli/hexcore.js.map +1 -0
  18. package/dist/common/auth.guard.js +66 -0
  19. package/dist/common/auth.guard.js.map +1 -0
  20. package/dist/common/http.js +171 -0
  21. package/dist/common/http.js.map +1 -0
  22. package/dist/common/zod-validation.pipe.js +47 -0
  23. package/dist/common/zod-validation.pipe.js.map +1 -0
  24. package/dist/config.js +354 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/config.module.js +35 -0
  27. package/dist/config.module.js.map +1 -0
  28. package/dist/controllers/cardano.controller.js +173 -0
  29. package/dist/controllers/cardano.controller.js.map +1 -0
  30. package/dist/controllers/heads.controller.js +225 -0
  31. package/dist/controllers/heads.controller.js.map +1 -0
  32. package/dist/controllers/setup.controller.js +127 -0
  33. package/dist/controllers/setup.controller.js.map +1 -0
  34. package/dist/controllers/system.controller.js +153 -0
  35. package/dist/controllers/system.controller.js.map +1 -0
  36. package/dist/db/database.module.js +73 -0
  37. package/dist/db/database.module.js.map +1 -0
  38. package/dist/db/index.js +135 -0
  39. package/dist/db/index.js.map +1 -0
  40. package/dist/db/repositories.js +220 -0
  41. package/dist/db/repositories.js.map +1 -0
  42. package/dist/docker/container-stats.js +134 -0
  43. package/dist/docker/container-stats.js.map +1 -0
  44. package/dist/docker/docker.service.js +335 -0
  45. package/dist/docker/docker.service.js.map +1 -0
  46. package/dist/hydra/argv.js +172 -0
  47. package/dist/hydra/argv.js.map +1 -0
  48. package/dist/hydra/blockfrost-keys.js +15 -0
  49. package/dist/hydra/blockfrost-keys.js.map +1 -0
  50. package/dist/hydra/boot-progress.js +165 -0
  51. package/dist/hydra/boot-progress.js.map +1 -0
  52. package/dist/hydra/capabilities.js +131 -0
  53. package/dist/hydra/capabilities.js.map +1 -0
  54. package/dist/hydra/head.service.js +1072 -0
  55. package/dist/hydra/head.service.js.map +1 -0
  56. package/dist/hydra/keys.js +301 -0
  57. package/dist/hydra/keys.js.map +1 -0
  58. package/dist/hydra/node-monitor.service.js +265 -0
  59. package/dist/hydra/node-monitor.service.js.map +1 -0
  60. package/dist/hydra/ws-client.js +177 -0
  61. package/dist/hydra/ws-client.js.map +1 -0
  62. package/dist/main.js +106 -0
  63. package/dist/main.js.map +1 -0
  64. package/dist/preflight.js +215 -0
  65. package/dist/preflight.js.map +1 -0
  66. package/dist/settings.js +243 -0
  67. package/dist/settings.js.map +1 -0
  68. package/dist/setup.service.js +257 -0
  69. package/dist/setup.service.js.map +1 -0
  70. package/dist/version.js +46 -0
  71. package/dist/version.js.map +1 -0
  72. package/node_modules/@hexcore/shared/dist/cjs/api.d.ts +730 -0
  73. package/node_modules/@hexcore/shared/dist/cjs/api.d.ts.map +1 -0
  74. package/node_modules/@hexcore/shared/dist/cjs/api.js +358 -0
  75. package/node_modules/@hexcore/shared/dist/cjs/api.js.map +1 -0
  76. package/node_modules/@hexcore/shared/dist/cjs/domain.d.ts +298 -0
  77. package/node_modules/@hexcore/shared/dist/cjs/domain.d.ts.map +1 -0
  78. package/node_modules/@hexcore/shared/dist/cjs/domain.js +49 -0
  79. package/node_modules/@hexcore/shared/dist/cjs/domain.js.map +1 -0
  80. package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.d.ts +279 -0
  81. package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.d.ts.map +1 -0
  82. package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.js +453 -0
  83. package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.js.map +1 -0
  84. package/node_modules/@hexcore/shared/dist/cjs/index.d.ts +7 -0
  85. package/node_modules/@hexcore/shared/dist/cjs/index.d.ts.map +1 -0
  86. package/node_modules/@hexcore/shared/dist/cjs/index.js +23 -0
  87. package/node_modules/@hexcore/shared/dist/cjs/index.js.map +1 -0
  88. package/node_modules/@hexcore/shared/dist/cjs/naming.d.ts +118 -0
  89. package/node_modules/@hexcore/shared/dist/cjs/naming.d.ts.map +1 -0
  90. package/node_modules/@hexcore/shared/dist/cjs/naming.js +103 -0
  91. package/node_modules/@hexcore/shared/dist/cjs/naming.js.map +1 -0
  92. package/node_modules/@hexcore/shared/dist/cjs/offline.d.ts +73 -0
  93. package/node_modules/@hexcore/shared/dist/cjs/offline.d.ts.map +1 -0
  94. package/node_modules/@hexcore/shared/dist/cjs/offline.js +80 -0
  95. package/node_modules/@hexcore/shared/dist/cjs/offline.js.map +1 -0
  96. package/node_modules/@hexcore/shared/dist/cjs/protocol-params.d.ts +79 -0
  97. package/node_modules/@hexcore/shared/dist/cjs/protocol-params.d.ts.map +1 -0
  98. package/node_modules/@hexcore/shared/dist/cjs/protocol-params.js +223 -0
  99. package/node_modules/@hexcore/shared/dist/cjs/protocol-params.js.map +1 -0
  100. package/node_modules/@hexcore/shared/dist/esm/api.js +355 -0
  101. package/node_modules/@hexcore/shared/dist/esm/api.js.map +1 -0
  102. package/node_modules/@hexcore/shared/dist/esm/domain.js +46 -0
  103. package/node_modules/@hexcore/shared/dist/esm/domain.js.map +1 -0
  104. package/node_modules/@hexcore/shared/dist/esm/hydra-versions.js +441 -0
  105. package/node_modules/@hexcore/shared/dist/esm/hydra-versions.js.map +1 -0
  106. package/node_modules/@hexcore/shared/dist/esm/index.js +7 -0
  107. package/node_modules/@hexcore/shared/dist/esm/index.js.map +1 -0
  108. package/node_modules/@hexcore/shared/dist/esm/naming.js +95 -0
  109. package/node_modules/@hexcore/shared/dist/esm/naming.js.map +1 -0
  110. package/node_modules/@hexcore/shared/dist/esm/offline.js +75 -0
  111. package/node_modules/@hexcore/shared/dist/esm/offline.js.map +1 -0
  112. package/node_modules/@hexcore/shared/dist/esm/package.json +3 -0
  113. package/node_modules/@hexcore/shared/dist/esm/protocol-params.js +217 -0
  114. package/node_modules/@hexcore/shared/dist/esm/protocol-params.js.map +1 -0
  115. package/node_modules/@hexcore/shared/package.json +15 -0
  116. package/package.json +67 -0
  117. package/web/200.html +23 -0
  118. package/web/404.html +23 -0
  119. package/web/_nuxt/2MxohCD-.js +1 -0
  120. package/web/_nuxt/4pFXjB_u.js +1 -0
  121. package/web/_nuxt/B-v0dEQX.js +1 -0
  122. package/web/_nuxt/B4yE3U25.js +1 -0
  123. package/web/_nuxt/BFh8Ai0L.js +1 -0
  124. package/web/_nuxt/BgPn4NJH.js +1 -0
  125. package/web/_nuxt/Bj1xviSh.js +1 -0
  126. package/web/_nuxt/BnotlrL7.js +1 -0
  127. package/web/_nuxt/C1YRg2ic.js +1 -0
  128. package/web/_nuxt/CGD7MYQH.js +1 -0
  129. package/web/_nuxt/CQcG7Bu4.js +1 -0
  130. package/web/_nuxt/CS3KqpH_.js +1 -0
  131. package/web/_nuxt/CT-yt694.js +1 -0
  132. package/web/_nuxt/CZK6xUH-.js +1 -0
  133. package/web/_nuxt/CcdyYRYz.js +1 -0
  134. package/web/_nuxt/CePNTU1S.js +1 -0
  135. package/web/_nuxt/CgPLsCeU.js +5 -0
  136. package/web/_nuxt/CkCrahXt.js +1 -0
  137. package/web/_nuxt/CnOqDT9k.js +3 -0
  138. package/web/_nuxt/CqsSss-D.js +1 -0
  139. package/web/_nuxt/Cr8dL64Q.js +1 -0
  140. package/web/_nuxt/D3CwzwU0.js +1 -0
  141. package/web/_nuxt/DDANn6-H.js +1 -0
  142. package/web/_nuxt/DDl9uKIA.js +1 -0
  143. package/web/_nuxt/DL9i0ayn.js +1 -0
  144. package/web/_nuxt/DLNoK9BC.js +1 -0
  145. package/web/_nuxt/DLztFeOQ.js +1 -0
  146. package/web/_nuxt/DPihKXdt.js +1 -0
  147. package/web/_nuxt/DXyr6Naa.js +1 -0
  148. package/web/_nuxt/DeDg5p0D.js +1 -0
  149. package/web/_nuxt/DgDlQiDU.js +1 -0
  150. package/web/_nuxt/DyPI7qJE.js +30 -0
  151. package/web/_nuxt/LGfPBi70.js +1 -0
  152. package/web/_nuxt/Onf26W-r.js +1 -0
  153. package/web/_nuxt/QZNQe_OX.js +1 -0
  154. package/web/_nuxt/Y8IJM-iz.js +1 -0
  155. package/web/_nuxt/YUGC0nt0.js +1 -0
  156. package/web/_nuxt/builds/latest.json +1 -0
  157. package/web/_nuxt/builds/meta/7e461629-93e2-4694-b237-6d5c6a85c97c.json +1 -0
  158. package/web/_nuxt/builds/meta/dev.json +1 -0
  159. package/web/_nuxt/entry.BxsAxZxf.css +1 -0
  160. package/web/_nuxt/error-404.XbtAw-qw.css +1 -0
  161. package/web/_nuxt/error-500.CNHVltWM.css +1 -0
  162. package/web/_nuxt/rS90pPZ_.js +1 -0
  163. package/web/_nuxt/rhEiP908.js +1 -0
  164. package/web/_nuxt/vSj9Ec0g.js +1 -0
  165. package/web/_nuxt/woNjBJMN.js +1 -0
  166. package/web/favicon.svg +28 -0
  167. package/web/heads/index.html +23 -0
  168. package/web/heads/new/index.html +23 -0
  169. package/web/index.html +23 -0
  170. package/web/login/index.html +23 -0
  171. package/web/logo.svg +56 -0
  172. package/web/settings/index.html +23 -0
  173. package/web/setup/index.html +23 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,127 @@
1
+ # Changelog
2
+
3
+ All notable changes to `hexcore`. Versions follow [semver](https://semver.org);
4
+ pre-releases are published under the `beta` npm tag, never `latest`.
5
+
6
+ ## 2.0.0-beta.1 — 2026-08-08
7
+
8
+ First release published to npm, as **`@vtechcom/hexcore`** — npm refused the
9
+ bare `hexcore` as too similar to an existing `hex-core`. The scope is only in
10
+ the install line; the command it puts on your PATH is `hexcore`.
11
+
12
+ ```bash
13
+ npm i -g @vtechcom/hexcore@beta && hexcore
14
+ ```
15
+
16
+ That gets you a running control-plane, a web UI on the same port, and a Hydra
17
+ Head.
18
+
19
+ Beta because the surface is new, not because it is untested: the head lifecycle
20
+ has been exercised against real hydra-node containers throughout. What has *not*
21
+ had wide exposure is this packaging — one global command, configured from a
22
+ browser, on machines other than the two it was built on. Treat the API and the
23
+ settings file as stable-ish and the CLI flags as still movable.
24
+
25
+ ### Added
26
+
27
+ - **`hexcore` CLI.** One command runs the control-plane and serves the built web
28
+ UI from the same port. `init`, `config`, `doctor`, `reset` and `clean`
29
+ alongside it. Commands that only touch files never load the application, so
30
+ they still work when the server will not start — which is when you reach for
31
+ them.
32
+ - **First-run setup in the browser.** An unconfigured installation prints a
33
+ setup link and the UI walks four questions. It replaces editing a 123-line
34
+ `.env`, which is where anyone who had not read the code stopped. `hexcore
35
+ init` asks the same questions in a terminal for machines with no browser
36
+ pointed at them.
37
+ - **Layered configuration.** Environment variable, then `.env` in the working
38
+ directory, then `~/.hexcore/config.json`, then the defaults. `hexcore config`
39
+ and the Settings screen print each value **and the layer that supplied it** —
40
+ layered configuration does not fail by producing a wrong value, it fails by
41
+ producing one nobody can explain.
42
+ - **Editable settings**, applied without a restart. Only settings that survive
43
+ that are offered: `PORT` and `STATE_DIR` are read once at boot, so they are
44
+ reported read-only rather than shown as fields that silently do nothing.
45
+ - **Reset and clean, in three scopes.** `reset` forgets configuration and keeps
46
+ heads; `clean` deletes heads, containers and persistence but keeps keys;
47
+ `clean --all` deletes everything and is CLI-only, because it destroys the keys
48
+ behind funded addresses *and* the database serving the request.
49
+ - **Node console.** A live WebSocket to any node of a running head, showing every
50
+ frame verbatim with what you sent interleaved, plus container CPU and memory
51
+ and copy-ready hydra-sdk / WebSocket / HTTP snippets built from that node's own
52
+ URL. Command templates load into an editor rather than firing on click;
53
+ `Close`, `SafeClose` and `Abort` confirm first, matched on the JSON about to be
54
+ sent so a hand-typed one is caught too.
55
+ - **`hydra-node 2.3.x (arm64)` profile** (`aniadev9/hydra-node:2.3.0-arm64`),
56
+ recommended on macOS with Apple Silicon. See *Known limitations*.
57
+ - **Edit a head that has not run**, and reset one that has. A head that failed to
58
+ start was previously stuck with whatever it was created with, and
59
+ delete-and-recreate throws away keys and any address already holding test ADA.
60
+ - `GET /api/heads/:id/resources` — CPU and memory per node container, plus the
61
+ head total, which is the level the ~500 MB-per-node budget is stated at.
62
+
63
+ ### Changed
64
+
65
+ - **`STATE_DIR` no longer defaults to `./.hexcore`.** It adopts a `.hexcore`
66
+ already in the working directory and otherwise uses `~/.hexcore`. The old
67
+ default was relative to wherever you happened to be, which for a global
68
+ command would have made `cd` look like every head had disappeared. Existing
69
+ installations keep their directory and need no migration.
70
+ - Container resource sampling, `docker stats` semantics: CPU is a delta between
71
+ two samples and memory has the page cache subtracted, the same figures
72
+ `docker stats` prints. Both report *nothing* rather than a plausible number
73
+ when they cannot be measured.
74
+ - The Settings screen is no longer read-only prose about `.env`.
75
+
76
+ ### Fixed
77
+
78
+ - **`hexcore clean` deleted containers belonging to other Hexcore installations.**
79
+ It matched on `io.hexcore.managed`, which is true of every Hexcore on the
80
+ Docker daemon, and more than one is normal — a checkout plus a global install
81
+ share one daemon. Containers now carry `io.hexcore.install` and bulk deletes
82
+ filter on it. This destroyed a running head during development; it is fixed and
83
+ tested.
84
+ - **The packaged tarball could not load its own configuration.** `zod` was
85
+ declared by both the package and its bundled `@hexcore/shared`; npm deduped the
86
+ pair and left `node_modules/zod` empty, so every command touching config died
87
+ while `--version` and `--help` kept working. Packing now installs the tarball
88
+ and runs a real command against it, and refuses to leave a broken one behind.
89
+ - The UI broke when Docker was unreachable — a fatal preflight check, plus a
90
+ banner that rendered as a 600px-tall column. Docker being down is now a
91
+ degraded state, not a refusal to start.
92
+ - An image pull failure reports the full reference you configured. Docker names
93
+ the repository and drops the tag, so one wrong character reported a repository
94
+ you had never heard of.
95
+
96
+ ### Licence
97
+
98
+ Apache-2.0. `hydra-node` itself is a separate Apache-2.0 project
99
+ ([cardano-scaling/hydra](https://github.com/cardano-scaling/hydra)); Hexcore runs
100
+ its published images and neither vendors nor modifies them.
101
+
102
+ ### Known limitations
103
+
104
+ - **Upstream hydra-node images are amd64-only.** On Apple Silicon the official
105
+ 2.2.0+ image runs under Rosetta, stalls inside a KZG warm-up before it installs
106
+ a tracer, and fails as a readiness timeout with nothing in the log. On arm64
107
+ Linux the daemon refuses it outright. Hexcore warns about this at the point of
108
+ choosing, and on macOS/arm64 recommends `aniadev9/hydra-node:2.3.0-arm64` — a
109
+ rebuild of the same source. **That is a community image on a personal Docker
110
+ Hub account, not one this project controls**, and it is offered because the
111
+ alternative on that platform is 2.1.x, which carries a double-spend defect. It
112
+ is reproducible: rebuild 2.3.0 yourself, push it as `you/hydra-node:2.3.0-arm64`
113
+ and it resolves to the same profile with the same flags. This will be replaced
114
+ the moment the Hydra core team publishes multi-arch images.
115
+ ([#7](https://github.com/Vtechcom/hexcore-v2/issues/7))
116
+ - **Container names collide between two installations on one Docker daemon.**
117
+ Names derive from `(headId, nodeIndex)` and head ids start at 1 everywhere, so
118
+ starting head 1 in one installation removes head 1's containers in another.
119
+ ([#6](https://github.com/Vtechcom/hexcore-v2/issues/6))
120
+ - **The node console cannot reach a node when Hexcore is served over HTTPS.**
121
+ The browser dials the node directly and browsers block `ws://` from a secure
122
+ page. The panel predicts this and says so rather than failing opaquely.
123
+ ([#5](https://github.com/Vtechcom/hexcore-v2/issues/5))
124
+ - No migration tool. The SQLite schema is idempotent and columns are only ever
125
+ added.
126
+ - The admin token lives in `localStorage`. Acceptable for a self-hosted operator
127
+ tool; revisit before any internet-exposed or multi-tenant deployment.
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Vtechcom
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # Hexcore v2
2
+
3
+ Monorepo for booting and operating **Hydra Heads** on Cardano — control-plane and
4
+ web UI in one repository.
5
+
6
+ > v2 is a rewrite of [hydra-hexcore](../hydra-hexcore) focused on one thing:
7
+ > going from `git clone` to a running Hydra Head without a checklist.
8
+
9
+ ---
10
+
11
+ ## What changed from v1
12
+
13
+ | | v1 | v2 |
14
+ |---|---|---|
15
+ | **Repos** | 3 separate (`hydra-hexcore`, `hexcore-ui`, `hexcore-cli`) | 1 monorepo |
16
+ | **Key material** | You generated Hydra + Cardano keys yourself and POSTed them in | Generated server-side on head creation |
17
+ | **Database** | MySQL + Redis, provisioned before first run | Embedded SQLite in the state dir |
18
+ | **Config** | ~30 env vars, unvalidated | zod-validated, works with a single key set |
19
+ | **Setup failures** | Surfaced as stack traces at random later points | `pnpm doctor` checks everything up front |
20
+ | **Container names** | Derived from DB ids (`hexcore-hydra-node-7`) | Deterministic (`hexcore-h1-n0`) |
21
+ | **Ports** | Scan upward from 10005 until free | Deterministic block per head |
22
+ | **Failed boot** | Left orphaned containers behind | Rolls back every container it created |
23
+ | **Head state** | Only "is the container up?" | Real Hydra state probed over WebSocket |
24
+ | **Types** | Duplicated between API and UI | Single zod schema in `packages/shared` |
25
+
26
+ ---
27
+
28
+ ## Requirements
29
+
30
+ - **Node** >= 22
31
+ - **Docker** running and reachable by your user
32
+ - Nothing else. The `offline` backend runs a real multi-node Hydra Head with no
33
+ chain, no credential and no waiting — a Blockfrost project id (free,
34
+ <https://blockfrost.io>) or a synced `cardano-node` socket is only needed when
35
+ you want heads on the real chain.
36
+
37
+ No MySQL. No Redis. No RabbitMQ.
38
+
39
+ ---
40
+
41
+ ## Quick start
42
+
43
+ ```bash
44
+ npm install -g @vtechcom/hexcore@beta
45
+ hexcore
46
+ ```
47
+
48
+ > **This is a beta**, published under the `beta` tag rather than `latest` — so
49
+ > `npm i -g @vtechcom/hexcore` without `@beta` will not resolve until 2.0.0 is final.
50
+ > The package is scoped; the command it installs is plain `hexcore`.
51
+ > See [CHANGELOG.md](CHANGELOG.md) for what that means and what is known broken.
52
+
53
+ Building it from this checkout gives the identical artefact:
54
+
55
+ ```bash
56
+ pnpm install
57
+ node scripts/pack-cli.mjs # builds, packs, and verifies the tarball installs
58
+ npm i -g ./vtechcom-hexcore-2.0.0-beta.1.tgz
59
+ ```
60
+
61
+ That starts the control-plane and serves the web UI from the same port, and
62
+ prints a setup link with a one-time token in it:
63
+
64
+ ```
65
+ Hexcore is not configured yet. Open this link to finish setup:
66
+ http://localhost:4000/setup?token=…
67
+ ```
68
+
69
+ Open it, answer four questions, and you have a working installation. Nothing to
70
+ edit by hand; there is no `.env` to copy.
71
+
72
+ > The token is not ceremony. Setup has to be reachable without a login, because
73
+ > it is where the login is created — so on any interface but loopback, whoever
74
+ > reaches the port first would otherwise choose the admin password.
75
+
76
+ Prefer a terminal, or setting up a machine with no browser pointed at it?
77
+
78
+ ```bash
79
+ hexcore init # the same questions, in the shell
80
+ ```
81
+
82
+ ### Everything the CLI does
83
+
84
+ ```bash
85
+ hexcore # start the control-plane and the web UI
86
+ hexcore init # configure from the terminal
87
+ hexcore config # every setting, and which layer it came from
88
+ hexcore doctor # check Docker, chain access and permissions
89
+ hexcore reset # forget the configuration; heads and keys are kept
90
+ hexcore clean # delete every head and its containers; keys are kept
91
+ hexcore clean --all # delete everything, including keys
92
+ ```
93
+
94
+ ### Where configuration comes from
95
+
96
+ Four layers, highest wins:
97
+
98
+ 1. a real environment variable — for containers and CI
99
+ 2. a `.env` in the current directory
100
+ 3. `~/.hexcore/config.json`, written by the setup wizard or `hexcore init`
101
+ 4. the built-in defaults
102
+
103
+ `hexcore config` prints the value **and the layer that supplied it**, which is
104
+ the only useful answer to "I changed that setting and nothing happened".
105
+
106
+ ### From a checkout
107
+
108
+ ```bash
109
+ pnpm install
110
+ pnpm build
111
+ pnpm start
112
+ ```
113
+
114
+ Same first-run experience — the setup link is printed on boot. `pnpm doctor`
115
+ runs the preflight checks on their own.
116
+
117
+ ### On Apple Silicon, read this before creating a head
118
+
119
+ Every hydra-node image the Hydra core team publishes is **amd64-only**. On a Mac
120
+ with an M-series chip the 2.2.0+ image runs under Rosetta, stalls inside a
121
+ cryptographic warm-up *before* it installs a logger, and fails as a plain
122
+ readiness timeout with nothing in the log to explain it.
123
+
124
+ Hexcore warns about this while you are still choosing, and on macOS/arm64 it
125
+ recommends `aniadev9/hydra-node:2.3.0-arm64` — the same 2.3.0 source rebuilt for
126
+ arm64, which starts natively in 0.47s.
127
+
128
+ **That is a community image on a personal Docker Hub account, not one this
129
+ project controls.** It is offered because the only upstream image that boots
130
+ there is 2.1.x, which carries a double-spend defect. If you would rather not
131
+ depend on someone else's registry, rebuild 2.3.0 yourself and push it as
132
+ `you/hydra-node:2.3.0-arm64` — it resolves to the same profile, with the same
133
+ flags and the same warnings. This recommendation goes away the moment upstream
134
+ publishes multi-arch images ([#7](https://github.com/Vtechcom/hexcore-v2/issues/7)).
135
+
136
+ ### Create and start a head
137
+
138
+ ```bash
139
+ TOKEN=$(curl -s -X POST localhost:4000/api/auth/login \
140
+ -H 'Content-Type: application/json' \
141
+ -d '{"username":"admin","password":"<your password>"}' | jq -r .data.token)
142
+
143
+ # Creates the head AND generates every key it needs.
144
+ curl -X POST localhost:4000/api/heads \
145
+ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
146
+ -d '{"name":"my-head","nodeCount":3}'
147
+ ```
148
+
149
+ The response contains one `cardanoAddress` per node. **Fund each of them with
150
+ test ADA** (https://docs.cardano.org/cardano-testnets/tools/faucet) — a Hydra
151
+ node cannot commit without UTxOs.
152
+
153
+ ```bash
154
+ curl -X POST localhost:4000/api/heads/1/start -H "Authorization: Bearer $TOKEN"
155
+ curl localhost:4000/api/heads/1/status -H "Authorization: Bearer $TOKEN"
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Layout
161
+
162
+ ```
163
+ apps/
164
+ control-plane/ NestJS + Fastify adapter
165
+ src/
166
+ config.ts zod-validated environment
167
+ preflight.ts startup checks (also `pnpm doctor`)
168
+ hydra/keys.ts Hydra + Cardano key generation
169
+ hydra/head.service.ts head lifecycle: create/start/stop/reset
170
+ hydra/ws-client.ts live Hydra state over WebSocket
171
+ docker/ container orchestration + rollback
172
+ db/ SQLite schema and repositories
173
+ controllers/ REST API (Swagger annotated)
174
+ web/ Nuxt SPA (ssr:false) — Tailwind v4 + shadcn-vue
175
+
176
+ packages/
177
+ shared/ zod schemas + types used by BOTH sides
178
+
179
+ docs/ UI specification and architecture notes
180
+ sketches/ HTML mockups the UI design was chosen from
181
+ ```
182
+
183
+ ### Web UI
184
+
185
+ Built, and served by the control-plane on the same port when `SERVE_WEB=true` —
186
+ which is what `hexcore` does. It builds to static files (`nuxt generate`), so
187
+ there is no second process and no second port.
188
+
189
+ - [`docs/UI_SPEC.md`](docs/UI_SPEC.md) — screens, states, actions, error handling
190
+ - [`docs/UI_ARCHITECTURE.md`](docs/UI_ARCHITECTURE.md) — stack and implementation notes
191
+
192
+ ### State directory
193
+
194
+ Everything mutable lives under `STATE_DIR`. Unset, that is `~/.hexcore` — unless
195
+ a `.hexcore` already exists in the working directory, which is adopted instead so
196
+ an existing checkout keeps the heads and keys it already has.
197
+
198
+ ```
199
+ ~/.hexcore/
200
+ config.json settings from the wizard (0600)
201
+ hexcore.db SQLite
202
+ secrets.json jwt secret + admin password hash (0600)
203
+ install-id distinguishes this installation's containers
204
+ heads/head-1/
205
+ keys/n0.hydra.sk 0600, text-envelope format
206
+ keys/n0.cardano.sk
207
+ persistence/n0/ hydra-node state
208
+ protocol-parameters.json
209
+ ```
210
+
211
+ `hexcore config` prints the resolved path. **Back this up to preserve funded
212
+ addresses** — the Cardano signing keys exist here and nowhere else. `hexcore
213
+ clean --all` deletes it, which is why that one is CLI-only and asks you to type
214
+ your machine's name.
215
+
216
+ ---
217
+
218
+ ## API
219
+
220
+ | Method | Path | Purpose |
221
+ |---|---|---|
222
+ | `GET` | `/health` | Liveness + Docker reachability (public) |
223
+ | `POST` | `/api/auth/login` | Get a bearer token (public) |
224
+ | `GET` | `/api/heads` | List heads with their nodes |
225
+ | `POST` | `/api/heads` | Create a head, generating all keys |
226
+ | `GET` | `/api/heads/:id` | Head detail |
227
+ | `PATCH` | `/api/heads/:id` | Update settings (applied on next start) |
228
+ | `POST` | `/api/heads/:id/start` | Boot every node, wait until ready |
229
+ | `POST` | `/api/heads/:id/stop` | Remove containers, keep keys + state |
230
+ | `POST` | `/api/heads/:id/restart` | Stop then start |
231
+ | `POST` | `/api/heads/:id/reset` | Wipe persistence (`includeKeys` for keys too) |
232
+ | `DELETE` | `/api/heads/:id` | Delete head, containers and state |
233
+ | `GET` | `/api/heads/:id/status` | Live state probed from the nodes |
234
+ | `GET` | `/api/heads/:id/nodes/:index/logs` | Tail a node's container log |
235
+ | `GET` | `/api/heads/:id/events` | Lifecycle audit trail |
236
+
237
+ Every response is `{ ok: true, data }` or `{ ok: false, error: { code, message } }`.
238
+
239
+ ---
240
+
241
+ ## Troubleshooting
242
+
243
+ Run `pnpm doctor` first — it diagnoses most problems with a concrete fix.
244
+
245
+ **`DOCKER_UNAVAILABLE`** — Docker is not running, or your user cannot use the
246
+ socket. On Linux: `sudo usermod -aG docker $USER`, then log out and back in.
247
+
248
+ **`CHAIN_PROVIDER_ERROR` / Blockfrost 403** — the key does not match
249
+ `CARDANO_NETWORK`. A key starting with `preprod` only works on preprod.
250
+
251
+ **Node never becomes ready** — check its log:
252
+ `GET /api/heads/:id/nodes/0/logs`. The usual cause is an unfunded Cardano
253
+ address; the head cannot proceed without UTxOs at each node's address.
254
+
255
+ **Port already in use** — change `PORT_BASE`; each node needs 10 ports.
256
+
257
+ ---
258
+
259
+ ## Development
260
+
261
+ ```bash
262
+ pnpm dev # control-plane + web in watch mode
263
+ pnpm dev:api # control-plane only
264
+ pnpm test # unit tests
265
+ pnpm doctor # environment diagnostics
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Licence
271
+
272
+ [Apache License 2.0](LICENSE). Use it, ship it, fork it, build a product on it —
273
+ the licence also grants you a patent licence from the contributors, which is the
274
+ main reason to prefer it over MIT for infrastructure.
275
+
276
+ Two things it does **not** cover, because they are not ours to license:
277
+
278
+ - **hydra-node** is a separate project ([cardano-scaling/hydra](https://github.com/cardano-scaling/hydra), Apache-2.0).
279
+ Hexcore runs its published container images; it does not vendor or modify them.
280
+ - `aniadev9/hydra-node:2.3.0-arm64`, the community rebuild recommended on Apple
281
+ Silicon, is a third-party image on a personal Docker Hub account. It is a
282
+ rebuild of upstream's Apache-2.0 source, but this project does not control that
283
+ registry — see the Apple Silicon section above.
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AppModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const core_1 = require("@nestjs/core");
12
+ const config_module_1 = require("./config.module");
13
+ const database_module_1 = require("./db/database.module");
14
+ const auth_1 = require("./auth");
15
+ const docker_service_1 = require("./docker/docker.service");
16
+ const head_service_1 = require("./hydra/head.service");
17
+ const capabilities_1 = require("./hydra/capabilities");
18
+ const node_monitor_service_1 = require("./hydra/node-monitor.service");
19
+ const boot_progress_1 = require("./hydra/boot-progress");
20
+ const setup_service_1 = require("./setup.service");
21
+ const heads_controller_1 = require("./controllers/heads.controller");
22
+ const system_controller_1 = require("./controllers/system.controller");
23
+ const cardano_controller_1 = require("./controllers/cardano.controller");
24
+ const setup_controller_1 = require("./controllers/setup.controller");
25
+ const auth_guard_1 = require("./common/auth.guard");
26
+ const http_1 = require("./common/http");
27
+ /**
28
+ * The whole control-plane is four modules deep on purpose. v1 grew to a dozen
29
+ * feature modules (hydra-main, hydra-heads, hydra-hub, monitoring-agent,
30
+ * rabbitmq, event-listener, blockfrost, axios, docker...) with circular
31
+ * dependencies between several of them; v2 keeps one service per concern.
32
+ */
33
+ let AppModule = class AppModule {
34
+ };
35
+ exports.AppModule = AppModule;
36
+ exports.AppModule = AppModule = __decorate([
37
+ (0, common_1.Module)({
38
+ imports: [config_module_1.ConfigModule, database_module_1.DatabaseModule],
39
+ controllers: [system_controller_1.SystemController, setup_controller_1.SetupController, heads_controller_1.HeadsController, cardano_controller_1.CardanoController],
40
+ providers: [
41
+ auth_1.AuthService,
42
+ docker_service_1.DockerService,
43
+ head_service_1.HeadService,
44
+ setup_service_1.SetupService,
45
+ capabilities_1.NodeCapabilitiesService,
46
+ node_monitor_service_1.NodeMonitorService,
47
+ boot_progress_1.BootTracker,
48
+ // Secure-by-default: every route needs a token unless marked @Public().
49
+ { provide: core_1.APP_GUARD, useClass: auth_guard_1.AuthGuard },
50
+ { provide: core_1.APP_INTERCEPTOR, useClass: http_1.ResponseEnvelopeInterceptor },
51
+ { provide: core_1.APP_FILTER, useClass: http_1.ApiExceptionFilter },
52
+ ],
53
+ })
54
+ ], AppModule);
55
+ //# sourceMappingURL=app.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uCAAsE;AACtE,mDAA+C;AAC/C,0DAAsD;AACtD,iCAAqC;AACrC,4DAAwD;AACxD,uDAAmD;AACnD,uDAA+D;AAC/D,uEAAkE;AAClE,yDAAoD;AACpD,mDAA+C;AAC/C,qEAAiE;AACjE,uEAAmE;AACnE,yEAAqE;AACrE,qEAAiE;AACjE,oDAAgD;AAChD,wCAAgF;AAEhF;;;;;GAKG;AAkBI,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAjBrB,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,4BAAY,EAAE,gCAAc,CAAC;QACvC,WAAW,EAAE,CAAC,oCAAgB,EAAE,kCAAe,EAAE,kCAAe,EAAE,sCAAiB,CAAC;QACpF,SAAS,EAAE;YACP,kBAAW;YACX,8BAAa;YACb,0BAAW;YACX,4BAAY;YACZ,sCAAuB;YACvB,yCAAkB;YAClB,2BAAW;YACX,wEAAwE;YACxE,EAAE,OAAO,EAAE,gBAAS,EAAE,QAAQ,EAAE,sBAAS,EAAE;YAC3C,EAAE,OAAO,EAAE,sBAAe,EAAE,QAAQ,EAAE,kCAA2B,EAAE;YACnE,EAAE,OAAO,EAAE,iBAAU,EAAE,QAAQ,EAAE,yBAAkB,EAAE;SACxD;KACJ,CAAC;GACW,SAAS,CAAG"}