@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
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * Reading Docker's stats payload, without Docker.
4
+ *
5
+ * Kept a pure function for the same reason `hydra/argv.ts` is: the interesting
6
+ * part is arithmetic that is wrong in obvious ways and right in non-obvious
7
+ * ones, and it can be tested against captured samples instead of against a
8
+ * daemon. Two pieces of that arithmetic are worth knowing before changing it.
9
+ *
10
+ * **CPU is a delta between two samples, not a reading.** The cgroup counter is
11
+ * cumulative nanoseconds, so a percentage only exists relative to a previous
12
+ * sample. Docker takes that second sample for us when the stats request asks
13
+ * for `stream=false` without `one-shot`; ask for `one-shot=true` and `precpu`
14
+ * comes back zeroed, every delta becomes the whole lifetime of the container,
15
+ * and the number reads as a plausible few percent while meaning nothing.
16
+ *
17
+ * **Memory has to have the page cache subtracted.** The raw cgroup `usage`
18
+ * includes file cache, and a hydra-node reads a ~426 KB trusted setup and maps
19
+ * its persistence — that cache is memory the kernel gives back the moment
20
+ * anything else wants it. `docker stats` subtracts it and so do we, otherwise
21
+ * the number that the ~500 MB-per-node budget is checked against is inflated by
22
+ * whatever the node last touched.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.emptyUsage = emptyUsage;
26
+ exports.readContainerStats = readContainerStats;
27
+ const EMPTY = {
28
+ cpuPercent: null,
29
+ cpuCores: null,
30
+ memoryBytes: null,
31
+ memoryLimitBytes: null,
32
+ memoryPercent: null,
33
+ netRxBytes: null,
34
+ netTxBytes: null,
35
+ blockReadBytes: null,
36
+ blockWriteBytes: null,
37
+ pids: null,
38
+ };
39
+ function emptyUsage() {
40
+ return { ...EMPTY };
41
+ }
42
+ function readContainerStats(raw) {
43
+ if (!raw || typeof raw !== 'object')
44
+ return emptyUsage();
45
+ const stats = raw;
46
+ return {
47
+ ...cpuUsage(stats),
48
+ ...memoryUsage(stats),
49
+ ...networkUsage(stats),
50
+ ...blockIoUsage(stats),
51
+ pids: numberOrNull(stats.pids_stats?.current),
52
+ };
53
+ }
54
+ function cpuUsage(stats) {
55
+ const cores = stats.cpu_stats?.online_cpus ?? stats.cpu_stats?.cpu_usage?.percpu_usage?.length ?? null;
56
+ /**
57
+ * There must BE a previous sample before a delta can be taken from it.
58
+ *
59
+ * `system_cpu_usage` is the host's cumulative CPU time — on any real second
60
+ * reading it is an enormous positive number, and it is zero in exactly one
61
+ * situation: `precpu_stats` was never filled in, which is what a `one-shot`
62
+ * request returns. Checking the deltas is not enough to catch that, because
63
+ * subtracting zero leaves both of them positive and the ratio comes out as
64
+ * a believable few percent that is actually "container lifetime over host
65
+ * uptime". This is the check that rejects it.
66
+ */
67
+ const priorSystem = stats.precpu_stats?.system_cpu_usage ?? 0;
68
+ if (priorSystem <= 0 || !cores)
69
+ return { cpuPercent: null, cpuCores: cores };
70
+ const cpuDelta = (stats.cpu_stats?.cpu_usage?.total_usage ?? 0) - (stats.precpu_stats?.cpu_usage?.total_usage ?? 0);
71
+ const systemDelta = (stats.cpu_stats?.system_cpu_usage ?? 0) - priorSystem;
72
+ /**
73
+ * Both deltas must be positive for the ratio to mean anything. A zero
74
+ * system delta means two reads landed inside the same clock tick; a
75
+ * negative one means the container restarted between them and the counters
76
+ * reset. Null in both cases — "we cannot say" is a fact the UI can render,
77
+ * 0% is a lie.
78
+ */
79
+ if (cpuDelta <= 0 || systemDelta <= 0)
80
+ return { cpuPercent: null, cpuCores: cores };
81
+ return { cpuPercent: round2((cpuDelta / systemDelta) * cores * 100), cpuCores: cores };
82
+ }
83
+ function memoryUsage(stats) {
84
+ const usage = numberOrNull(stats.memory_stats?.usage);
85
+ const limit = numberOrNull(stats.memory_stats?.limit);
86
+ if (usage === null)
87
+ return { memoryBytes: null, memoryLimitBytes: limit, memoryPercent: null };
88
+ /**
89
+ * cgroup v2 names it `inactive_file`, v1 `total_inactive_file`, and older
90
+ * daemons only report `cache`. Docker Desktop is v2 today and the Linux
91
+ * hosts this also runs on may be either, so take whichever is present
92
+ * rather than picking one and being silently wrong on the other.
93
+ */
94
+ const cache = stats.memory_stats?.stats ?? {};
95
+ const cached = cache.inactive_file ?? cache.total_inactive_file ?? cache.cache ?? 0;
96
+ const used = Math.max(0, usage - cached);
97
+ return {
98
+ memoryBytes: used,
99
+ memoryLimitBytes: limit,
100
+ memoryPercent: limit && limit > 0 ? round2((used / limit) * 100) : null,
101
+ };
102
+ }
103
+ function networkUsage(stats) {
104
+ const interfaces = Object.values(stats.networks ?? {});
105
+ if (interfaces.length === 0)
106
+ return { netRxBytes: null, netTxBytes: null };
107
+ return {
108
+ netRxBytes: interfaces.reduce((sum, i) => sum + (i.rx_bytes ?? 0), 0),
109
+ netTxBytes: interfaces.reduce((sum, i) => sum + (i.tx_bytes ?? 0), 0),
110
+ };
111
+ }
112
+ function blockIoUsage(stats) {
113
+ const entries = stats.blkio_stats?.io_service_bytes_recursive;
114
+ // Docker Desktop's VM reports this as null rather than an empty array.
115
+ if (!Array.isArray(entries) || entries.length === 0)
116
+ return { blockReadBytes: null, blockWriteBytes: null };
117
+ let read = 0;
118
+ let write = 0;
119
+ for (const entry of entries) {
120
+ const op = (entry.op ?? '').toLowerCase();
121
+ if (op === 'read')
122
+ read += entry.value ?? 0;
123
+ if (op === 'write')
124
+ write += entry.value ?? 0;
125
+ }
126
+ return { blockReadBytes: read, blockWriteBytes: write };
127
+ }
128
+ function numberOrNull(value) {
129
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
130
+ }
131
+ function round2(value) {
132
+ return Math.round(value * 100) / 100;
133
+ }
134
+ //# sourceMappingURL=container-stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-stats.js","sourceRoot":"","sources":["../../src/docker/container-stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,335 @@
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
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ var DockerService_1;
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.DockerService = exports.ImagePullError = exports.DockerUnavailableError = void 0;
20
+ exports.demuxDockerLog = demuxDockerLog;
21
+ /**
22
+ * Docker orchestration.
23
+ *
24
+ * Wraps dockerode with the few operations the head lifecycle needs, plus
25
+ * preflight checks. v1 assumed the daemon was reachable and the image present;
26
+ * when either was false you got a raw ENOENT or a 404 from deep inside a
27
+ * container create. Here every failure is translated into an actionable message.
28
+ */
29
+ const common_1 = require("@nestjs/common");
30
+ const dockerode_1 = __importDefault(require("dockerode"));
31
+ const node_stream_1 = require("node:stream");
32
+ const shared_1 = require("@hexcore/shared");
33
+ const config_module_1 = require("../config.module");
34
+ const settings_1 = require("../settings");
35
+ class DockerUnavailableError extends Error {
36
+ constructor(cause) {
37
+ /**
38
+ * The daemon's own words, and nothing else.
39
+ *
40
+ * This message is rendered in the UI, and it used to wrap the cause in a
41
+ * second pair of parentheses and then append both the macOS and the
42
+ * Linux remedy — around 250 characters of which most were wrong for
43
+ * whoever was reading them. Docker says why itself ("Docker Desktop is
44
+ * manually paused", "connect ENOENT ..."), so the remedy belongs where
45
+ * there is room for it: preflight adds a platform-specific hint, the UI
46
+ * banner says what is blocked.
47
+ */
48
+ super(`Cannot reach the Docker daemon: ${cause.replace(/\s+/g, ' ').trim()}`);
49
+ this.name = 'DockerUnavailableError';
50
+ }
51
+ }
52
+ exports.DockerUnavailableError = DockerUnavailableError;
53
+ /**
54
+ * A pull that could not produce the image.
55
+ *
56
+ * Docker's own words name the *repository* and never the tag, so a reference
57
+ * with one character wrong ("aniadev" for "aniadev9") reports a repository the
58
+ * operator has never heard of and does not obviously match what they typed.
59
+ * Since the image is chosen once and only checked at the first start, that is
60
+ * exactly the moment they need to see their own string back.
61
+ */
62
+ class ImagePullError extends Error {
63
+ constructor(image, cause) {
64
+ const said = cause.replace(/\s+/g, ' ').trim();
65
+ const missing = /pull access denied|repository does not exist|manifest unknown|not found/i.test(said);
66
+ super(missing
67
+ ? `Cannot pull "${image}". No such image in the registry, or it is private — check the ` +
68
+ `account and repository spelling and the tag, and that the image is public or the ` +
69
+ `Docker daemon is logged in. Docker said: ${said}`
70
+ : `Cannot pull "${image}". Docker said: ${said}`);
71
+ this.name = 'ImagePullError';
72
+ }
73
+ }
74
+ exports.ImagePullError = ImagePullError;
75
+ let DockerService = DockerService_1 = class DockerService {
76
+ config;
77
+ logger = new common_1.Logger(DockerService_1.name);
78
+ docker;
79
+ constructor(config) {
80
+ this.config = config;
81
+ this.docker = new dockerode_1.default({ socketPath: config.dockerSocket });
82
+ }
83
+ async ping() {
84
+ try {
85
+ await this.docker.ping();
86
+ }
87
+ catch (err) {
88
+ throw new DockerUnavailableError(err.message);
89
+ }
90
+ }
91
+ async version() {
92
+ const v = (await this.docker.version());
93
+ return v.Version ?? 'unknown';
94
+ }
95
+ async hasImage(image) {
96
+ try {
97
+ await this.docker.getImage(image).inspect();
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ /** Pull an image, streaming progress lines to onProgress. */
105
+ async pullImage(image, onProgress) {
106
+ const stream = (await this.docker.pull(image));
107
+ await new Promise((resolvePull, rejectPull) => {
108
+ this.docker.modem.followProgress(stream, (err) => (err ? rejectPull(err) : resolvePull()), (event) => {
109
+ if (onProgress && event.status) {
110
+ onProgress(`${event.status}${event.progress ? ' ' + event.progress : ''}`);
111
+ }
112
+ });
113
+ });
114
+ }
115
+ async ensureImage(image, onProgress) {
116
+ if (await this.hasImage(image))
117
+ return;
118
+ this.logger.log(`pulling hydra-node image ${image} (first run only)`);
119
+ try {
120
+ await this.pullImage(image, onProgress);
121
+ }
122
+ catch (err) {
123
+ // A pull failure is about the reference, not about Docker being
124
+ // unreachable — say which image, since Docker will not.
125
+ throw new ImagePullError(image, err.message);
126
+ }
127
+ }
128
+ /** Create the shared user-defined bridge so nodes resolve each other by name. */
129
+ async ensureNetwork() {
130
+ const networks = await this.docker.listNetworks();
131
+ if (networks.some(n => n.Name === shared_1.DOCKER_NETWORK))
132
+ return;
133
+ await this.docker.createNetwork({ Name: shared_1.DOCKER_NETWORK, Driver: 'bridge' });
134
+ this.logger.log(`created docker network ${shared_1.DOCKER_NETWORK}`);
135
+ }
136
+ async removeContainer(name) {
137
+ try {
138
+ const container = this.docker.getContainer(name);
139
+ const info = await container.inspect();
140
+ if (info.State.Running)
141
+ await container.stop({ t: 10 }).catch(() => undefined);
142
+ await container.remove({ force: true });
143
+ this.logger.debug(`container ${name} removed`);
144
+ }
145
+ catch (err) {
146
+ const code = err.statusCode;
147
+ if (code !== 404)
148
+ this.logger.warn(`failed removing container ${name}: ${err.message}`);
149
+ }
150
+ }
151
+ async createAndStart(spec) {
152
+ // Always clear a stale container with the same name first: names are
153
+ // deterministic in v2, so a leftover from a crashed boot would
154
+ // otherwise make every subsequent start fail with 409 Conflict.
155
+ await this.removeContainer(spec.name);
156
+ const exposed = {};
157
+ const bindings = {};
158
+ for (const p of spec.ports) {
159
+ exposed[`${p}/tcp`] = {};
160
+ bindings[`${p}/tcp`] = [{ HostPort: String(p) }];
161
+ }
162
+ const container = await this.docker.createContainer({
163
+ name: spec.name,
164
+ Image: spec.image,
165
+ Cmd: spec.cmd,
166
+ Labels: { [shared_1.LABEL_MANAGED]: 'true', ...spec.labels },
167
+ Env: spec.env,
168
+ User: spec.user,
169
+ ExposedPorts: exposed,
170
+ HostConfig: {
171
+ NetworkMode: shared_1.DOCKER_NETWORK,
172
+ Binds: spec.binds,
173
+ PortBindings: bindings,
174
+ RestartPolicy: { Name: 'unless-stopped' },
175
+ ...(spec.logRotation
176
+ ? {
177
+ LogConfig: {
178
+ Type: 'json-file',
179
+ Config: {
180
+ 'max-size': `${spec.logRotation.maxSizeMb}m`,
181
+ 'max-file': String(spec.logRotation.maxFiles),
182
+ },
183
+ },
184
+ }
185
+ : {}),
186
+ },
187
+ });
188
+ await container.start();
189
+ this.logger.log(`container ${spec.name} started (${container.id.slice(0, 12)})`);
190
+ return container;
191
+ }
192
+ /** All containers this control-plane manages, optionally filtered by head. */
193
+ async listManaged(headId) {
194
+ const label = [`${shared_1.LABEL_MANAGED}=true`];
195
+ if (headId !== undefined)
196
+ label.push(`${shared_1.LABEL_HEAD_ID}=${headId}`);
197
+ return this.docker.listContainers({ all: true, filters: { label } });
198
+ }
199
+ async isRunning(name) {
200
+ try {
201
+ const info = await this.docker.getContainer(name).inspect();
202
+ return info.State.Running;
203
+ }
204
+ catch {
205
+ return false;
206
+ }
207
+ }
208
+ async restart(name) {
209
+ await this.docker.getContainer(name).restart();
210
+ }
211
+ /**
212
+ * Run a throwaway container and return its combined output.
213
+ *
214
+ * Used to ask an image what it supports (`--help`, `--version`) instead of
215
+ * maintaining a version→flags table that goes stale and cannot describe a
216
+ * fork or a `master` tag. The container is removed on exit; a non-zero exit
217
+ * is still a useful answer, since `--help` exits 1 on some builds, so the
218
+ * output is returned rather than treated as failure.
219
+ */
220
+ async runOnce(image, cmd, timeoutMs = 60_000) {
221
+ await this.ensureImage(image);
222
+ const chunks = [];
223
+ const sink = new node_stream_1.Writable({
224
+ write(chunk, _enc, done) {
225
+ chunks.push(chunk);
226
+ done();
227
+ },
228
+ });
229
+ const run = this.docker.run(image, cmd, sink, {
230
+ HostConfig: { AutoRemove: true, NetworkMode: 'none' },
231
+ Tty: false,
232
+ });
233
+ // dockerode's run() resolves only when the container exits; a hung
234
+ // image would otherwise stall boot indefinitely.
235
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`timed out after ${timeoutMs}ms`)), timeoutMs).unref?.());
236
+ try {
237
+ await Promise.race([run, timeout]);
238
+ }
239
+ catch (err) {
240
+ // Output collected before the failure is still worth returning:
241
+ // `--help` on some builds writes to stderr and exits non-zero.
242
+ if (chunks.length === 0)
243
+ throw err;
244
+ }
245
+ return demuxDockerLog(Buffer.concat(chunks));
246
+ }
247
+ /** Tail container logs — used by the UI log viewer. */
248
+ async logs(name, tail = 200) {
249
+ try {
250
+ const buf = (await this.docker.getContainer(name).logs({
251
+ stdout: true,
252
+ stderr: true,
253
+ tail,
254
+ timestamps: false,
255
+ }));
256
+ return demuxDockerLog(buf);
257
+ }
258
+ catch (err) {
259
+ const code = err.statusCode;
260
+ if (code === 404)
261
+ return '';
262
+ throw err;
263
+ }
264
+ }
265
+ /**
266
+ * One resource sample for a container, or null when it is not there.
267
+ *
268
+ * `stream: false` and NOT `one-shot`: Docker then takes two readings itself
269
+ * and returns the second with `precpu_stats` filled in, which is the only
270
+ * way a CPU percentage exists at all — see docker/container-stats.ts. The
271
+ * cost is that this call blocks for about a second inside the daemon, so it
272
+ * belongs on a request the operator asked for, never on a polling path that
273
+ * runs for every head.
274
+ */
275
+ async stats(name) {
276
+ try {
277
+ return await this.docker.getContainer(name).stats({ stream: false });
278
+ }
279
+ catch (err) {
280
+ const code = err.statusCode;
281
+ if (code === 404)
282
+ return null;
283
+ throw err;
284
+ }
285
+ }
286
+ /** Follow logs as a stream (websocket log tailing). */
287
+ async logStream(name, tail = 50) {
288
+ return (await this.docker.getContainer(name).logs({
289
+ stdout: true,
290
+ stderr: true,
291
+ tail,
292
+ follow: true,
293
+ }));
294
+ }
295
+ labelsFor(headId, nodeId, nodeIndex) {
296
+ return {
297
+ [shared_1.LABEL_HEAD_ID]: String(headId),
298
+ [shared_1.LABEL_NODE_ID]: String(nodeId),
299
+ [shared_1.LABEL_NODE_INDEX]: String(nodeIndex),
300
+ // Which installation owns this. See LABEL_INSTALL — without it a
301
+ // bulk operation cannot tell two Hexcores on one daemon apart.
302
+ [shared_1.LABEL_INSTALL]: this.installId,
303
+ };
304
+ }
305
+ /** This installation's id, derived from its state dir. */
306
+ get installId() {
307
+ return (0, settings_1.installId)(this.config.stateDir);
308
+ }
309
+ };
310
+ exports.DockerService = DockerService;
311
+ exports.DockerService = DockerService = DockerService_1 = __decorate([
312
+ (0, common_1.Injectable)(),
313
+ __param(0, (0, common_1.Inject)(config_module_1.APP_CONFIG)),
314
+ __metadata("design:paramtypes", [Object])
315
+ ], DockerService);
316
+ /**
317
+ * Docker multiplexes stdout/stderr into 8-byte-framed chunks when the container
318
+ * has no TTY. Strip the frames so the UI shows clean lines.
319
+ */
320
+ function demuxDockerLog(buf) {
321
+ const out = [];
322
+ let offset = 0;
323
+ while (offset + 8 <= buf.length) {
324
+ const type = buf[offset];
325
+ // A valid frame header starts with stream type 0/1/2 and 3 zero bytes.
326
+ if (type > 2 || buf[offset + 1] !== 0 || buf[offset + 2] !== 0 || buf[offset + 3] !== 0) {
327
+ return buf.toString('utf8');
328
+ }
329
+ const len = buf.readUInt32BE(offset + 4);
330
+ out.push(buf.toString('utf8', offset + 8, offset + 8 + len));
331
+ offset += 8 + len;
332
+ }
333
+ return out.join('');
334
+ }
335
+ //# sourceMappingURL=docker.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.service.js","sourceRoot":"","sources":["../../src/docker/docker.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isOffline = isOffline;
4
+ exports.backendArgs = backendArgs;
5
+ exports.scriptArgs = scriptArgs;
6
+ exports.buildNodeArgv = buildNodeArgv;
7
+ /**
8
+ * hydra-node argv construction.
9
+ *
10
+ * Pure functions so the exact flags a head boots with can be asserted in tests
11
+ * without Docker, a Blockfrost key, or a running Nest app. Getting these wrong
12
+ * is expensive: an unknown or conflicting flag aborts hydra-node during option
13
+ * parsing, the container dies instantly, and the head reports a generic
14
+ * "node never became ready" that points nowhere near the real cause.
15
+ */
16
+ const shared_1 = require("@hexcore/shared");
17
+ const keys_1 = require("./keys");
18
+ /** True for heads whose layer-1 interface is disabled entirely. See offline.ts. */
19
+ function isOffline(head) {
20
+ return head.chain_backend === 'offline';
21
+ }
22
+ /**
23
+ * Chain backend flags.
24
+ *
25
+ * The backends are mutually exclusive in hydra-node's option parser:
26
+ * --blockfrost cannot be combined with --testnet-magic / --node-socket.
27
+ */
28
+ function backendArgs(head, nodeIndex, opts) {
29
+ const cp = (0, shared_1.containerHeadPaths)(head.id);
30
+ /**
31
+ * Offline mode is a different branch of the option parser, not a variant of
32
+ * the online backends: no network magic, no socket, no Blockfrost, no
33
+ * scripts. `--initial-utxo` is the L2 ledger's whole starting state.
34
+ *
35
+ * `--ledger-genesis` is emitted only when a genesis file was actually
36
+ * written. hydra-node's own default is to have none, and pointing the flag
37
+ * at a missing file is a hard startup failure.
38
+ */
39
+ if (isOffline(head)) {
40
+ const args = ['--offline-head-seed', head.offline_head_seed || (0, shared_1.defaultOfflineHeadSeed)(head.id)];
41
+ args.push('--initial-utxo', cp.initialUtxo);
42
+ if (head.offline_ledger_genesis)
43
+ args.push('--ledger-genesis', cp.ledgerGenesis);
44
+ return args;
45
+ }
46
+ if (head.chain_backend === 'cardano-node') {
47
+ return [...(0, keys_1.networkArgs)(head.network), '--node-socket', '/cardano-node/node.socket'];
48
+ }
49
+ /**
50
+ * Each node reads its own token file. hydra-node takes a path, not a key,
51
+ * so per-node keys can only be expressed as per-node files — and pointing
52
+ * every node at one shared file would silently put N nodes on a single
53
+ * Blockfrost rate-limit budget.
54
+ */
55
+ const args = ['--blockfrost', cp.nodeBlockfrostToken(nodeIndex)];
56
+ /**
57
+ * Blockfrost polls over HTTP rather than following the chain, so a node's
58
+ * tip lags — upstream measured 50-80s on preprod against ~0s for a local
59
+ * socket. hydra-node defaults --unsynced-period to HALF the contestation
60
+ * period, so a common 120s contestation yields 60s, squarely inside that
61
+ * drift band.
62
+ *
63
+ * The consequence is not cosmetic: past the unsynced period hydra-node
64
+ * stops signing snapshots AND rejects client input, so a deposit can
65
+ * silently expire unsigned while the head looks healthy.
66
+ *
67
+ * Raise it only when the node's own default would be tighter, and never
68
+ * lower a value the operator deliberately set higher.
69
+ */
70
+ if (opts.caps.unsyncedPeriod) {
71
+ const nodeDefault = Math.floor(head.contestation_period / 2);
72
+ args.push('--unsynced-period', `${Math.max(opts.unsyncedPeriod, nodeDefault)}s`);
73
+ }
74
+ if (opts.caps.blockfrostTimeouts) {
75
+ args.push('--blockfrost-query-timeout', String(opts.blockfrostQueryTimeout), '--blockfrost-retry-timeout', String(opts.blockfrostRetryTimeout));
76
+ }
77
+ return args;
78
+ }
79
+ /**
80
+ * Script selection.
81
+ *
82
+ * An explicit tx id wins; otherwise --network picks the last pre-published
83
+ * scripts for that network, which is what lets a head boot with no setup. The
84
+ * two are mutually exclusive, and older images may support neither.
85
+ */
86
+ function scriptArgs(head, opts) {
87
+ // Offline heads never touch a chain, so there are no on-chain scripts to
88
+ // select. Both flags belong to the online branch and would abort parsing.
89
+ if (isOffline(head))
90
+ return [];
91
+ if (opts.hydraScriptsTxId)
92
+ return ['--hydra-scripts-tx-id', opts.hydraScriptsTxId];
93
+ return opts.caps.network ? ['--network', head.network] : [];
94
+ }
95
+ /**
96
+ * Full argv for one node. Peers are addressed by container name on the shared
97
+ * bridge network, so a head behaves identically on a laptop and on a server
98
+ * without host-network workarounds.
99
+ */
100
+ function buildNodeArgv(head, node, peers, opts) {
101
+ const cp = (0, shared_1.containerHeadPaths)(head.id);
102
+ const self = cp.nodeKeys(node.node_index);
103
+ const offline = isOffline(head);
104
+ /**
105
+ * Peer wiring works in both modes, but not identically.
106
+ *
107
+ * `--peer` and `--hydra-verification-key` sit OUTSIDE hydra-node's
108
+ * chain-config group, so an offline head forms a multi-party Head over the
109
+ * same network layer as an online one. `--cardano-verification-key` does
110
+ * not: it is inside the online branch, and passing it alongside
111
+ * `--offline-head-seed` aborts option parsing. It is dropped rather than
112
+ * substituted, because offline peers authorise nothing on chain.
113
+ */
114
+ const peerArgs = peers.flatMap(peer => {
115
+ const pk = cp.nodeKeys(peer.node_index);
116
+ return [
117
+ '--peer',
118
+ `${peer.name}:${peer.p2p_port}`,
119
+ '--hydra-verification-key',
120
+ pk.hydraVk,
121
+ ...(offline ? [] : ['--cardano-verification-key', pk.cardanoVk]),
122
+ ];
123
+ });
124
+ return [
125
+ // --quiet is a top-level flag; keeping it first also keeps it clear of
126
+ // the mutually exclusive backend groups below.
127
+ ...(head.log_quiet ? ['--quiet'] : []),
128
+ '--node-id',
129
+ node.name,
130
+ '--listen',
131
+ `0.0.0.0:${node.p2p_port}`,
132
+ '--advertise',
133
+ `${node.name}:${node.p2p_port}`,
134
+ '--api-host',
135
+ '0.0.0.0',
136
+ '--api-port',
137
+ String(node.api_port),
138
+ '--monitoring-port',
139
+ String(node.monitoring_port),
140
+ '--hydra-signing-key',
141
+ self.hydraSk,
142
+ /**
143
+ * Everything below the Hydra key belongs to the online branch of the
144
+ * option parser.
145
+ *
146
+ * The Cardano key authorises on-chain Hydra transactions and pays their
147
+ * fuel; the two periods bound on-chain close and deposit deadlines.
148
+ * Offline mode has no chain for any of it, and hydra-node rejects the
149
+ * flags outright rather than ignoring them — which is why they are
150
+ * dropped here instead of being passed with harmless values.
151
+ */
152
+ ...(offline ? [] : ['--cardano-signing-key', self.cardanoSk]),
153
+ '--persistence-dir',
154
+ cp.nodePersistence(node.node_index),
155
+ '--persistence-rotate-after',
156
+ String(head.persistence_rotate_after),
157
+ ...(offline
158
+ ? []
159
+ : ['--contestation-period', `${head.contestation_period}s`, '--deposit-period', `${head.deposit_period}s`]),
160
+ '--ledger-protocol-parameters',
161
+ cp.protocolParameters,
162
+ ...peerArgs,
163
+ ...scriptArgs(head, opts),
164
+ ...backendArgs(head, node.node_index, opts),
165
+ // Appended, never merged: these must be flags nothing above emits.
166
+ // hydra-node's parser rejects a repeated option rather than letting the
167
+ // last one win, so a duplicate here kills the container at startup —
168
+ // hence the test asserting no profile collides with the base argv.
169
+ ...(opts.extraArgs ?? []),
170
+ ];
171
+ }
172
+ //# sourceMappingURL=argv.js.map