@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.
- package/CHANGELOG.md +127 -0
- package/LICENSE +201 -0
- package/README.md +283 -0
- package/dist/app.module.js +55 -0
- package/dist/app.module.js.map +1 -0
- package/dist/auth.js +151 -0
- package/dist/auth.js.map +1 -0
- package/dist/chain/cost-models.js +81 -0
- package/dist/chain/cost-models.js.map +1 -0
- package/dist/chain/provider.js +269 -0
- package/dist/chain/provider.js.map +1 -0
- package/dist/chain/socket.js +48 -0
- package/dist/chain/socket.js.map +1 -0
- package/dist/cli/doctor.js +43 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/hexcore.js +435 -0
- package/dist/cli/hexcore.js.map +1 -0
- package/dist/common/auth.guard.js +66 -0
- package/dist/common/auth.guard.js.map +1 -0
- package/dist/common/http.js +171 -0
- package/dist/common/http.js.map +1 -0
- package/dist/common/zod-validation.pipe.js +47 -0
- package/dist/common/zod-validation.pipe.js.map +1 -0
- package/dist/config.js +354 -0
- package/dist/config.js.map +1 -0
- package/dist/config.module.js +35 -0
- package/dist/config.module.js.map +1 -0
- package/dist/controllers/cardano.controller.js +173 -0
- package/dist/controllers/cardano.controller.js.map +1 -0
- package/dist/controllers/heads.controller.js +225 -0
- package/dist/controllers/heads.controller.js.map +1 -0
- package/dist/controllers/setup.controller.js +127 -0
- package/dist/controllers/setup.controller.js.map +1 -0
- package/dist/controllers/system.controller.js +153 -0
- package/dist/controllers/system.controller.js.map +1 -0
- package/dist/db/database.module.js +73 -0
- package/dist/db/database.module.js.map +1 -0
- package/dist/db/index.js +135 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/repositories.js +220 -0
- package/dist/db/repositories.js.map +1 -0
- package/dist/docker/container-stats.js +134 -0
- package/dist/docker/container-stats.js.map +1 -0
- package/dist/docker/docker.service.js +335 -0
- package/dist/docker/docker.service.js.map +1 -0
- package/dist/hydra/argv.js +172 -0
- package/dist/hydra/argv.js.map +1 -0
- package/dist/hydra/blockfrost-keys.js +15 -0
- package/dist/hydra/blockfrost-keys.js.map +1 -0
- package/dist/hydra/boot-progress.js +165 -0
- package/dist/hydra/boot-progress.js.map +1 -0
- package/dist/hydra/capabilities.js +131 -0
- package/dist/hydra/capabilities.js.map +1 -0
- package/dist/hydra/head.service.js +1072 -0
- package/dist/hydra/head.service.js.map +1 -0
- package/dist/hydra/keys.js +301 -0
- package/dist/hydra/keys.js.map +1 -0
- package/dist/hydra/node-monitor.service.js +265 -0
- package/dist/hydra/node-monitor.service.js.map +1 -0
- package/dist/hydra/ws-client.js +177 -0
- package/dist/hydra/ws-client.js.map +1 -0
- package/dist/main.js +106 -0
- package/dist/main.js.map +1 -0
- package/dist/preflight.js +215 -0
- package/dist/preflight.js.map +1 -0
- package/dist/settings.js +243 -0
- package/dist/settings.js.map +1 -0
- package/dist/setup.service.js +257 -0
- package/dist/setup.service.js.map +1 -0
- package/dist/version.js +46 -0
- package/dist/version.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/api.d.ts +730 -0
- package/node_modules/@hexcore/shared/dist/cjs/api.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/api.js +358 -0
- package/node_modules/@hexcore/shared/dist/cjs/api.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/domain.d.ts +298 -0
- package/node_modules/@hexcore/shared/dist/cjs/domain.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/domain.js +49 -0
- package/node_modules/@hexcore/shared/dist/cjs/domain.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.d.ts +279 -0
- package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.js +453 -0
- package/node_modules/@hexcore/shared/dist/cjs/hydra-versions.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/index.d.ts +7 -0
- package/node_modules/@hexcore/shared/dist/cjs/index.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/index.js +23 -0
- package/node_modules/@hexcore/shared/dist/cjs/index.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/naming.d.ts +118 -0
- package/node_modules/@hexcore/shared/dist/cjs/naming.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/naming.js +103 -0
- package/node_modules/@hexcore/shared/dist/cjs/naming.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/offline.d.ts +73 -0
- package/node_modules/@hexcore/shared/dist/cjs/offline.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/offline.js +80 -0
- package/node_modules/@hexcore/shared/dist/cjs/offline.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/protocol-params.d.ts +79 -0
- package/node_modules/@hexcore/shared/dist/cjs/protocol-params.d.ts.map +1 -0
- package/node_modules/@hexcore/shared/dist/cjs/protocol-params.js +223 -0
- package/node_modules/@hexcore/shared/dist/cjs/protocol-params.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/api.js +355 -0
- package/node_modules/@hexcore/shared/dist/esm/api.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/domain.js +46 -0
- package/node_modules/@hexcore/shared/dist/esm/domain.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/hydra-versions.js +441 -0
- package/node_modules/@hexcore/shared/dist/esm/hydra-versions.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/index.js +7 -0
- package/node_modules/@hexcore/shared/dist/esm/index.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/naming.js +95 -0
- package/node_modules/@hexcore/shared/dist/esm/naming.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/offline.js +75 -0
- package/node_modules/@hexcore/shared/dist/esm/offline.js.map +1 -0
- package/node_modules/@hexcore/shared/dist/esm/package.json +3 -0
- package/node_modules/@hexcore/shared/dist/esm/protocol-params.js +217 -0
- package/node_modules/@hexcore/shared/dist/esm/protocol-params.js.map +1 -0
- package/node_modules/@hexcore/shared/package.json +15 -0
- package/package.json +67 -0
- package/web/200.html +23 -0
- package/web/404.html +23 -0
- package/web/_nuxt/2MxohCD-.js +1 -0
- package/web/_nuxt/4pFXjB_u.js +1 -0
- package/web/_nuxt/B-v0dEQX.js +1 -0
- package/web/_nuxt/B4yE3U25.js +1 -0
- package/web/_nuxt/BFh8Ai0L.js +1 -0
- package/web/_nuxt/BgPn4NJH.js +1 -0
- package/web/_nuxt/Bj1xviSh.js +1 -0
- package/web/_nuxt/BnotlrL7.js +1 -0
- package/web/_nuxt/C1YRg2ic.js +1 -0
- package/web/_nuxt/CGD7MYQH.js +1 -0
- package/web/_nuxt/CQcG7Bu4.js +1 -0
- package/web/_nuxt/CS3KqpH_.js +1 -0
- package/web/_nuxt/CT-yt694.js +1 -0
- package/web/_nuxt/CZK6xUH-.js +1 -0
- package/web/_nuxt/CcdyYRYz.js +1 -0
- package/web/_nuxt/CePNTU1S.js +1 -0
- package/web/_nuxt/CgPLsCeU.js +5 -0
- package/web/_nuxt/CkCrahXt.js +1 -0
- package/web/_nuxt/CnOqDT9k.js +3 -0
- package/web/_nuxt/CqsSss-D.js +1 -0
- package/web/_nuxt/Cr8dL64Q.js +1 -0
- package/web/_nuxt/D3CwzwU0.js +1 -0
- package/web/_nuxt/DDANn6-H.js +1 -0
- package/web/_nuxt/DDl9uKIA.js +1 -0
- package/web/_nuxt/DL9i0ayn.js +1 -0
- package/web/_nuxt/DLNoK9BC.js +1 -0
- package/web/_nuxt/DLztFeOQ.js +1 -0
- package/web/_nuxt/DPihKXdt.js +1 -0
- package/web/_nuxt/DXyr6Naa.js +1 -0
- package/web/_nuxt/DeDg5p0D.js +1 -0
- package/web/_nuxt/DgDlQiDU.js +1 -0
- package/web/_nuxt/DyPI7qJE.js +30 -0
- package/web/_nuxt/LGfPBi70.js +1 -0
- package/web/_nuxt/Onf26W-r.js +1 -0
- package/web/_nuxt/QZNQe_OX.js +1 -0
- package/web/_nuxt/Y8IJM-iz.js +1 -0
- package/web/_nuxt/YUGC0nt0.js +1 -0
- package/web/_nuxt/builds/latest.json +1 -0
- package/web/_nuxt/builds/meta/7e461629-93e2-4694-b237-6d5c6a85c97c.json +1 -0
- package/web/_nuxt/builds/meta/dev.json +1 -0
- package/web/_nuxt/entry.BxsAxZxf.css +1 -0
- package/web/_nuxt/error-404.XbtAw-qw.css +1 -0
- package/web/_nuxt/error-500.CNHVltWM.css +1 -0
- package/web/_nuxt/rS90pPZ_.js +1 -0
- package/web/_nuxt/rhEiP908.js +1 -0
- package/web/_nuxt/vSj9Ec0g.js +1 -0
- package/web/_nuxt/woNjBJMN.js +1 -0
- package/web/favicon.svg +28 -0
- package/web/heads/index.html +23 -0
- package/web/heads/new/index.html +23 -0
- package/web/index.html +23 -0
- package/web/login/index.html +23 -0
- package/web/logo.svg +56 -0
- package/web/settings/index.html +23 -0
- package/web/setup/index.html +23 -0
package/dist/settings.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SECRET_KEYS = exports.SETTINGS_KEYS = void 0;
|
|
4
|
+
exports.hexcoreHome = hexcoreHome;
|
|
5
|
+
exports.settingsPath = settingsPath;
|
|
6
|
+
exports.defaultStateDir = defaultStateDir;
|
|
7
|
+
exports.expandHome = expandHome;
|
|
8
|
+
exports.absolute = absolute;
|
|
9
|
+
exports.readSettings = readSettings;
|
|
10
|
+
exports.writeSettings = writeSettings;
|
|
11
|
+
exports.clearSettings = clearSettings;
|
|
12
|
+
exports.installId = installId;
|
|
13
|
+
exports.ensureSetupToken = ensureSetupToken;
|
|
14
|
+
exports.readSetupToken = readSetupToken;
|
|
15
|
+
exports.clearSetupToken = clearSetupToken;
|
|
16
|
+
exports.mergeLayers = mergeLayers;
|
|
17
|
+
exports.sourceOf = sourceOf;
|
|
18
|
+
/**
|
|
19
|
+
* Persisted settings, and where Hexcore keeps its things.
|
|
20
|
+
*
|
|
21
|
+
* `config.ts` answers "what is the running configuration". This answers the
|
|
22
|
+
* question underneath it: **where does that configuration come from when nobody
|
|
23
|
+
* has written a `.env`.** Standing Hexcore up used to mean editing 123 lines of
|
|
24
|
+
* `.env` by hand, which is where a non-technical operator stopped — and which a
|
|
25
|
+
* globally installed CLI cannot use at all, since `.env` is read from the
|
|
26
|
+
* current directory and `hexcore` is run from wherever you happen to be.
|
|
27
|
+
*
|
|
28
|
+
* ## Layering
|
|
29
|
+
*
|
|
30
|
+
* Four sources, highest first. Every one of them is reported by
|
|
31
|
+
* `describeProvenance`, because the failure mode of layered configuration is
|
|
32
|
+
* not a wrong value, it is a value nobody can explain.
|
|
33
|
+
*
|
|
34
|
+
* 1. **A real environment variable.** Containers and CI set these, and they
|
|
35
|
+
* must beat anything on disk.
|
|
36
|
+
* 2. **`.env` in the current directory**, loaded by dotenv, which does not
|
|
37
|
+
* overwrite an existing environment variable — so (1) still wins.
|
|
38
|
+
* 3. **`<home>/config.json`**, written by the setup wizard or `hexcore init`.
|
|
39
|
+
* 4. The schema defaults in `config.ts`.
|
|
40
|
+
*
|
|
41
|
+
* The settings file holds env-style keys deliberately: it IS an env layer that
|
|
42
|
+
* lives somewhere stable, and describing it as anything else would invite a
|
|
43
|
+
* second name for every setting.
|
|
44
|
+
*
|
|
45
|
+
* ## Where "here" is
|
|
46
|
+
*
|
|
47
|
+
* The home is `HEXCORE_HOME`, else `~/.hexcore`. The STATE_DIR default is more
|
|
48
|
+
* careful than that, because getting it wrong orphans key material that
|
|
49
|
+
* controls funded addresses — see `defaultStateDir`.
|
|
50
|
+
*/
|
|
51
|
+
const node_fs_1 = require("node:fs");
|
|
52
|
+
const node_os_1 = require("node:os");
|
|
53
|
+
const node_path_1 = require("node:path");
|
|
54
|
+
const node_crypto_1 = require("node:crypto");
|
|
55
|
+
/** Env keys the settings file is allowed to carry. Nothing else is written. */
|
|
56
|
+
exports.SETTINGS_KEYS = [
|
|
57
|
+
'CARDANO_NETWORK',
|
|
58
|
+
'CHAIN_BACKEND',
|
|
59
|
+
'BLOCKFROST_PROJECT_ID',
|
|
60
|
+
'CARDANO_NODE_SOCKET',
|
|
61
|
+
'HYDRA_NODE_IMAGE',
|
|
62
|
+
'ADMIN_USERNAME',
|
|
63
|
+
];
|
|
64
|
+
/** Keys whose value is never returned by an API or printed by the CLI. */
|
|
65
|
+
exports.SECRET_KEYS = new Set(['BLOCKFROST_PROJECT_ID', 'JWT_SECRET', 'ADMIN_PASSWORD']);
|
|
66
|
+
const EMPTY = { version: 1, env: {} };
|
|
67
|
+
function hexcoreHome(env = process.env) {
|
|
68
|
+
const configured = env.HEXCORE_HOME?.trim();
|
|
69
|
+
if (configured)
|
|
70
|
+
return expandHome(configured);
|
|
71
|
+
return (0, node_path_1.resolve)((0, node_os_1.homedir)(), '.hexcore');
|
|
72
|
+
}
|
|
73
|
+
function settingsPath(env = process.env) {
|
|
74
|
+
return (0, node_path_1.resolve)(hexcoreHome(env), 'config.json');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Where mutable state lives when nothing names it.
|
|
78
|
+
*
|
|
79
|
+
* The obvious answer — always `<home>/.hexcore` — would have moved an existing
|
|
80
|
+
* installation's state dir out from under it. Before this file existed the
|
|
81
|
+
* default was `./.hexcore`, resolved against the current directory, so anyone
|
|
82
|
+
* running the control-plane from its package directory has a populated
|
|
83
|
+
* `apps/control-plane/.hexcore` holding the Cardano signing keys for every head
|
|
84
|
+
* they have created. Those keys control addresses that may hold real funds and
|
|
85
|
+
* exist nowhere else. Silently pointing at a new empty directory would present
|
|
86
|
+
* as "all my heads are gone".
|
|
87
|
+
*
|
|
88
|
+
* So: adopt a `.hexcore` that is already in the current directory, and only
|
|
89
|
+
* fall back to the home when there is nothing to adopt. A fresh install has
|
|
90
|
+
* nothing to adopt and lands in `~/.hexcore`, which is what a CLI needs.
|
|
91
|
+
*/
|
|
92
|
+
function defaultStateDir(cwd = process.cwd(), env = process.env) {
|
|
93
|
+
const legacy = (0, node_path_1.resolve)(cwd, '.hexcore');
|
|
94
|
+
if ((0, node_fs_1.existsSync)(legacy))
|
|
95
|
+
return legacy;
|
|
96
|
+
return hexcoreHome(env);
|
|
97
|
+
}
|
|
98
|
+
function expandHome(p) {
|
|
99
|
+
return p.startsWith('~') ? (0, node_path_1.resolve)((0, node_os_1.homedir)(), p.slice(1).replace(/^\/+/, '')) : p;
|
|
100
|
+
}
|
|
101
|
+
function absolute(p, cwd = process.cwd()) {
|
|
102
|
+
const expanded = expandHome(p);
|
|
103
|
+
return (0, node_path_1.isAbsolute)(expanded) ? expanded : (0, node_path_1.resolve)(cwd, expanded);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Read the settings file, or an empty one.
|
|
107
|
+
*
|
|
108
|
+
* A file that cannot be parsed returns empty rather than throwing. Refusing to
|
|
109
|
+
* boot over a corrupt settings file would leave an operator with a process that
|
|
110
|
+
* will not start and no UI to fix it in — the wizard is the repair path, so it
|
|
111
|
+
* has to be reachable.
|
|
112
|
+
*/
|
|
113
|
+
function readSettings(env = process.env) {
|
|
114
|
+
const path = settingsPath(env);
|
|
115
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
116
|
+
return { ...EMPTY, env: {} };
|
|
117
|
+
try {
|
|
118
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
119
|
+
const values = {};
|
|
120
|
+
for (const key of exports.SETTINGS_KEYS) {
|
|
121
|
+
const value = parsed.env?.[key];
|
|
122
|
+
if (typeof value === 'string' && value !== '')
|
|
123
|
+
values[key] = value;
|
|
124
|
+
}
|
|
125
|
+
return { version: 1, setupCompletedAt: parsed.setupCompletedAt, env: values };
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { ...EMPTY, env: {} };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Write the settings file, 0600.
|
|
133
|
+
*
|
|
134
|
+
* It carries a Blockfrost project id, which is a credential — the same one
|
|
135
|
+
* `.env` has always held in plaintext, kept next to `secrets.json` under the
|
|
136
|
+
* same permissions rather than given a weaker home.
|
|
137
|
+
*/
|
|
138
|
+
function writeSettings(settings, env = process.env) {
|
|
139
|
+
const path = settingsPath(env);
|
|
140
|
+
(0, node_fs_1.mkdirSync)(hexcoreHome(env), { recursive: true });
|
|
141
|
+
(0, node_fs_1.writeFileSync)(path, `${JSON.stringify(settings, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
142
|
+
// writeFileSync only applies `mode` when it creates the file, so an
|
|
143
|
+
// existing world-readable one would keep its permissions forever.
|
|
144
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
145
|
+
}
|
|
146
|
+
function clearSettings(env = process.env) {
|
|
147
|
+
(0, node_fs_1.rmSync)(settingsPath(env), { force: true });
|
|
148
|
+
clearSetupToken(env);
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------- installation id
|
|
151
|
+
const INSTALL_FILE = 'install-id';
|
|
152
|
+
/**
|
|
153
|
+
* A stable id for THIS state directory.
|
|
154
|
+
*
|
|
155
|
+
* Stamped onto every container as `LABEL_INSTALL` so bulk operations can tell
|
|
156
|
+
* one installation's containers from another's. More than one installation on a
|
|
157
|
+
* machine is normal — a checkout being developed on and a globally installed
|
|
158
|
+
* `hexcore` share a Docker daemon — and the label that existed before this,
|
|
159
|
+
* `LABEL_MANAGED`, is true of both. Matching on it alone is what let a `clean`
|
|
160
|
+
* in one installation remove a running head belonging to the other.
|
|
161
|
+
*
|
|
162
|
+
* Tied to the state dir rather than the host or the user, because the state dir
|
|
163
|
+
* is what actually owns the heads: move it and the containers move with it.
|
|
164
|
+
*/
|
|
165
|
+
function installId(stateDir) {
|
|
166
|
+
const path = (0, node_path_1.resolve)(stateDir, INSTALL_FILE);
|
|
167
|
+
if ((0, node_fs_1.existsSync)(path)) {
|
|
168
|
+
const existing = (0, node_fs_1.readFileSync)(path, 'utf8').trim();
|
|
169
|
+
if (existing)
|
|
170
|
+
return existing;
|
|
171
|
+
}
|
|
172
|
+
const id = (0, node_crypto_1.randomBytes)(8).toString('hex');
|
|
173
|
+
(0, node_fs_1.mkdirSync)(stateDir, { recursive: true });
|
|
174
|
+
(0, node_fs_1.writeFileSync)(path, `${id}\n`, 'utf8');
|
|
175
|
+
return id;
|
|
176
|
+
}
|
|
177
|
+
// ------------------------------------------------------------- setup token
|
|
178
|
+
const TOKEN_FILE = 'setup-token';
|
|
179
|
+
/**
|
|
180
|
+
* A secret that proves the caller can see the terminal Hexcore was started in.
|
|
181
|
+
*
|
|
182
|
+
* The setup endpoint cannot require a login — it is where the login is created
|
|
183
|
+
* — so without this, on any interface but loopback, whoever reaches the port
|
|
184
|
+
* first chooses the admin password. That is a takeover on a shared network, and
|
|
185
|
+
* "bind to localhost" is not an answer for the person running this on a server
|
|
186
|
+
* to use from a laptop.
|
|
187
|
+
*
|
|
188
|
+
* The cost is one paste, and not even that: the CLI prints the URL with the
|
|
189
|
+
* token already in it.
|
|
190
|
+
*/
|
|
191
|
+
function ensureSetupToken(env = process.env) {
|
|
192
|
+
const path = (0, node_path_1.resolve)(hexcoreHome(env), TOKEN_FILE);
|
|
193
|
+
if ((0, node_fs_1.existsSync)(path)) {
|
|
194
|
+
const existing = (0, node_fs_1.readFileSync)(path, 'utf8').trim();
|
|
195
|
+
if (existing)
|
|
196
|
+
return existing;
|
|
197
|
+
}
|
|
198
|
+
const token = (0, node_crypto_1.randomBytes)(24).toString('base64url');
|
|
199
|
+
(0, node_fs_1.mkdirSync)(hexcoreHome(env), { recursive: true });
|
|
200
|
+
(0, node_fs_1.writeFileSync)(path, `${token}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
201
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
202
|
+
return token;
|
|
203
|
+
}
|
|
204
|
+
function readSetupToken(env = process.env) {
|
|
205
|
+
const path = (0, node_path_1.resolve)(hexcoreHome(env), TOKEN_FILE);
|
|
206
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
207
|
+
return null;
|
|
208
|
+
return (0, node_fs_1.readFileSync)(path, 'utf8').trim() || null;
|
|
209
|
+
}
|
|
210
|
+
function clearSetupToken(env = process.env) {
|
|
211
|
+
(0, node_fs_1.rmSync)((0, node_path_1.resolve)(hexcoreHome(env), TOKEN_FILE), { force: true });
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Flatten the layers into the object the zod schema parses.
|
|
215
|
+
*
|
|
216
|
+
* Empty strings are dropped from the higher layers on the way through. An
|
|
217
|
+
* exported-but-empty `BLOCKFROST_PROJECT_ID` — which is what `export VAR=` and
|
|
218
|
+
* several process managers produce — would otherwise beat a real saved value
|
|
219
|
+
* with nothing, and present as a credential that vanished on restart.
|
|
220
|
+
*/
|
|
221
|
+
function mergeLayers(layers) {
|
|
222
|
+
const merged = { ...layers.file };
|
|
223
|
+
for (const [key, value] of Object.entries(layers.dotenv)) {
|
|
224
|
+
if (value !== '')
|
|
225
|
+
merged[key] = value;
|
|
226
|
+
}
|
|
227
|
+
for (const [key, value] of Object.entries(layers.environment)) {
|
|
228
|
+
if (value !== undefined && value !== '')
|
|
229
|
+
merged[key] = value;
|
|
230
|
+
}
|
|
231
|
+
return merged;
|
|
232
|
+
}
|
|
233
|
+
/** Which layer a given key's value actually came from. */
|
|
234
|
+
function sourceOf(key, layers) {
|
|
235
|
+
if (layers.environment[key] !== undefined && layers.environment[key] !== '')
|
|
236
|
+
return 'environment';
|
|
237
|
+
if (layers.dotenv[key] !== undefined && layers.dotenv[key] !== '')
|
|
238
|
+
return 'env-file';
|
|
239
|
+
if (layers.file[key] !== undefined)
|
|
240
|
+
return 'settings-file';
|
|
241
|
+
return 'default';
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,257 @@
|
|
|
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 SetupService_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.SetupService = exports.SetupForbiddenError = exports.SettingsValidationError = void 0;
|
|
17
|
+
exports.wipeEverything = wipeEverything;
|
|
18
|
+
/**
|
|
19
|
+
* First-run setup, settings changes, and wiping.
|
|
20
|
+
*
|
|
21
|
+
* The problem this exists for: until now the only way to configure Hexcore was
|
|
22
|
+
* to edit a 123-line `.env`. That works for whoever wrote it and stops everyone
|
|
23
|
+
* else — and it cannot work at all for a globally installed `hexcore`, since
|
|
24
|
+
* `.env` is read from the current directory.
|
|
25
|
+
*
|
|
26
|
+
* Setup is reachable without a login, because it is where the login is created.
|
|
27
|
+
* That makes the guard rails here load-bearing rather than defensive:
|
|
28
|
+
*
|
|
29
|
+
* - `complete()` refuses once setup is done. Without that, anyone who can
|
|
30
|
+
* reach the port could re-run setup and choose a new admin password.
|
|
31
|
+
* - It requires the setup token, which is only readable from the terminal
|
|
32
|
+
* Hexcore was started in (or the file system it runs on).
|
|
33
|
+
* - The admin password passes through and is never written to the settings
|
|
34
|
+
* file — it goes to `AuthService`, which hashes it into `secrets.json`.
|
|
35
|
+
*/
|
|
36
|
+
const common_1 = require("@nestjs/common");
|
|
37
|
+
const node_os_1 = require("node:os");
|
|
38
|
+
const promises_1 = require("node:fs/promises");
|
|
39
|
+
const node_crypto_1 = require("node:crypto");
|
|
40
|
+
const node_crypto_2 = require("node:crypto");
|
|
41
|
+
const config_module_1 = require("./config.module");
|
|
42
|
+
const config_1 = require("./config");
|
|
43
|
+
const settings_1 = require("./settings");
|
|
44
|
+
const auth_1 = require("./auth");
|
|
45
|
+
const head_service_1 = require("./hydra/head.service");
|
|
46
|
+
/** A setting that is well-formed on its own but cannot work with the others. */
|
|
47
|
+
class SettingsValidationError extends Error {
|
|
48
|
+
field;
|
|
49
|
+
constructor(field, message) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.field = field;
|
|
52
|
+
this.name = 'SettingsValidationError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.SettingsValidationError = SettingsValidationError;
|
|
56
|
+
/** Setup attempted with the wrong token, or after it was already done. */
|
|
57
|
+
class SetupForbiddenError extends Error {
|
|
58
|
+
constructor(message) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = 'SetupForbiddenError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.SetupForbiddenError = SetupForbiddenError;
|
|
64
|
+
let SetupService = SetupService_1 = class SetupService {
|
|
65
|
+
config;
|
|
66
|
+
auth;
|
|
67
|
+
heads;
|
|
68
|
+
logger = new common_1.Logger(SetupService_1.name);
|
|
69
|
+
constructor(config, auth, heads) {
|
|
70
|
+
this.config = config;
|
|
71
|
+
this.auth = auth;
|
|
72
|
+
this.heads = heads;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether this installation still needs configuring.
|
|
76
|
+
*
|
|
77
|
+
* Three ways to be already configured, and the last two matter as much as
|
|
78
|
+
* the first: a `.env` in the working directory is the shape every existing
|
|
79
|
+
* deployment uses, and a container started with `-e CHAIN_BACKEND=…` was
|
|
80
|
+
* configured by whoever wrote the compose file. Nagging either with a
|
|
81
|
+
* wizard would be Hexcore failing to recognise its own configuration.
|
|
82
|
+
*/
|
|
83
|
+
status() {
|
|
84
|
+
const stored = (0, settings_1.readSettings)();
|
|
85
|
+
const via = this.completedVia(stored);
|
|
86
|
+
return {
|
|
87
|
+
required: via === undefined,
|
|
88
|
+
completedVia: via,
|
|
89
|
+
completedAt: stored.setupCompletedAt,
|
|
90
|
+
defaults: {
|
|
91
|
+
network: this.config.network,
|
|
92
|
+
chainBackend: this.config.chainBackend,
|
|
93
|
+
blockfrostProjectId: '',
|
|
94
|
+
cardanoNodeSocket: this.config.cardanoNodeSocket || '',
|
|
95
|
+
hydraNodeImage: this.config.hydraNodeImage,
|
|
96
|
+
adminUsername: this.config.adminUsername,
|
|
97
|
+
},
|
|
98
|
+
loopbackOnly: this.config.host === '127.0.0.1' || this.config.host === 'localhost',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
completedVia(stored) {
|
|
102
|
+
if (stored.setupCompletedAt)
|
|
103
|
+
return 'wizard';
|
|
104
|
+
if (config_1.dotenvFound)
|
|
105
|
+
return 'env-file';
|
|
106
|
+
/**
|
|
107
|
+
* Any one of these in the real environment means somebody configured
|
|
108
|
+
* this deliberately — a compose file, a CI job, a systemd unit.
|
|
109
|
+
* Checking the merged view instead would count our own defaults.
|
|
110
|
+
*/
|
|
111
|
+
const env = (0, config_1.currentLayers)().environment;
|
|
112
|
+
const configured = ['CHAIN_BACKEND', 'BLOCKFROST_PROJECT_ID', 'CARDANO_NODE_SOCKET', 'ADMIN_PASSWORD'];
|
|
113
|
+
if (configured.some(key => env[key]))
|
|
114
|
+
return 'environment';
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
/** The token the wizard must present, minted on demand while setup is due. */
|
|
118
|
+
setupToken() {
|
|
119
|
+
return this.status().required ? (0, settings_1.ensureSetupToken)() : null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Finish setup.
|
|
123
|
+
*
|
|
124
|
+
* Returns a generated password when the caller did not choose one, so
|
|
125
|
+
* `hexcore init` can print it. That is safe to return here and nowhere
|
|
126
|
+
* else: the caller has already proved it holds the setup token.
|
|
127
|
+
*/
|
|
128
|
+
complete(input) {
|
|
129
|
+
if (!this.status().required) {
|
|
130
|
+
throw new SetupForbiddenError('Hexcore is already configured. Change settings from the Settings screen, or run ' +
|
|
131
|
+
'`hexcore reset` to start over.');
|
|
132
|
+
}
|
|
133
|
+
const expected = (0, settings_1.readSetupToken)();
|
|
134
|
+
if (!expected || !constantTimeEquals(input.setupToken, expected)) {
|
|
135
|
+
throw new SetupForbiddenError('That setup link is not valid. Use the URL printed in the terminal where Hexcore was started.');
|
|
136
|
+
}
|
|
137
|
+
this.applySettings(input, { setupCompletedAt: new Date().toISOString() });
|
|
138
|
+
const password = input.adminPassword || (0, node_crypto_2.randomBytes)(9).toString('base64url');
|
|
139
|
+
this.auth.setCredentials(input.adminUsername ?? this.config.adminUsername, password);
|
|
140
|
+
(0, settings_1.clearSetupToken)();
|
|
141
|
+
this.logger.log(`setup completed — network=${this.config.network} backend=${this.config.chainBackend}`);
|
|
142
|
+
return input.adminPassword ? {} : { generatedPassword: password };
|
|
143
|
+
}
|
|
144
|
+
/** Change settings after setup. Authenticated; no token, no password. */
|
|
145
|
+
saveSettings(input) {
|
|
146
|
+
const stored = (0, settings_1.readSettings)();
|
|
147
|
+
this.applySettings(input, { setupCompletedAt: stored.setupCompletedAt ?? new Date().toISOString() });
|
|
148
|
+
this.logger.log(`settings updated — network=${this.config.network} backend=${this.config.chainBackend}`);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Validate, write, and make the running process agree.
|
|
152
|
+
*
|
|
153
|
+
* The reload mutates the injected config object rather than replacing it,
|
|
154
|
+
* so every service that captured a reference sees the change. Settings that
|
|
155
|
+
* cannot survive that — a port already bound, a Docker client already built
|
|
156
|
+
* — are not offered by the schema at all.
|
|
157
|
+
*/
|
|
158
|
+
applySettings(input, meta) {
|
|
159
|
+
this.validate(input);
|
|
160
|
+
const env = {
|
|
161
|
+
CARDANO_NETWORK: input.network,
|
|
162
|
+
CHAIN_BACKEND: input.chainBackend,
|
|
163
|
+
};
|
|
164
|
+
if (input.blockfrostProjectId)
|
|
165
|
+
env.BLOCKFROST_PROJECT_ID = input.blockfrostProjectId;
|
|
166
|
+
if (input.cardanoNodeSocket)
|
|
167
|
+
env.CARDANO_NODE_SOCKET = input.cardanoNodeSocket;
|
|
168
|
+
if (input.hydraNodeImage)
|
|
169
|
+
env.HYDRA_NODE_IMAGE = input.hydraNodeImage;
|
|
170
|
+
if (input.adminUsername)
|
|
171
|
+
env.ADMIN_USERNAME = input.adminUsername;
|
|
172
|
+
(0, settings_1.writeSettings)({ version: 1, setupCompletedAt: meta.setupCompletedAt, env });
|
|
173
|
+
(0, config_1.reloadConfigInto)(this.config);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The cross-field rules a per-field schema cannot express.
|
|
177
|
+
*
|
|
178
|
+
* Both are the difference between a setting that is merely wrong and one
|
|
179
|
+
* that produces a head which boots and then never observes its own init
|
|
180
|
+
* transaction — a failure that surfaces minutes later, somewhere else.
|
|
181
|
+
*/
|
|
182
|
+
validate(input) {
|
|
183
|
+
if (input.chainBackend === 'blockfrost') {
|
|
184
|
+
const key = input.blockfrostProjectId?.trim();
|
|
185
|
+
if (!key) {
|
|
186
|
+
throw new SettingsValidationError('blockfrostProjectId', 'The Blockfrost backend needs a project id. Get a free one at blockfrost.io, or choose ' +
|
|
187
|
+
'the offline backend, which needs no credential at all.');
|
|
188
|
+
}
|
|
189
|
+
if (!key.startsWith(input.network)) {
|
|
190
|
+
throw new SettingsValidationError('blockfrostProjectId', `This project id is for a different network — it must start with "${input.network}". ` +
|
|
191
|
+
'A mismatched key does not fail loudly; the head boots and never sees its own ' +
|
|
192
|
+
'transactions.');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (input.chainBackend === 'cardano-node' && !input.cardanoNodeSocket?.trim()) {
|
|
196
|
+
throw new SettingsValidationError('cardanoNodeSocket', 'The cardano-node backend needs the path to a running node socket.');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** What the operator must type to confirm a wipe. Their own machine's name. */
|
|
200
|
+
confirmationPhrase() {
|
|
201
|
+
return (0, node_os_1.hostname)();
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Wipe, as far as the scope says.
|
|
205
|
+
*
|
|
206
|
+
* `everything` is not reachable here on purpose — it deletes the signing
|
|
207
|
+
* keys behind funded addresses and the database this request is being
|
|
208
|
+
* served from. `hexcore clean --all` does it with the process stopped.
|
|
209
|
+
*/
|
|
210
|
+
async clean(scope, confirm) {
|
|
211
|
+
if (confirm.trim() !== this.confirmationPhrase()) {
|
|
212
|
+
throw new SettingsValidationError('confirm', `Type "${this.confirmationPhrase()}" to confirm. Nothing has been deleted.`);
|
|
213
|
+
}
|
|
214
|
+
let headsRemoved = 0;
|
|
215
|
+
let containersRemoved = 0;
|
|
216
|
+
if (scope === 'heads') {
|
|
217
|
+
for (const head of await this.heads.list()) {
|
|
218
|
+
containersRemoved += head.nodes.length;
|
|
219
|
+
await this.heads.delete(head.id);
|
|
220
|
+
headsRemoved += 1;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (scope === 'config') {
|
|
224
|
+
(0, settings_1.clearSettings)();
|
|
225
|
+
(0, config_1.reloadConfigInto)(this.config);
|
|
226
|
+
}
|
|
227
|
+
this.logger.warn(`clean(${scope}) removed ${headsRemoved} head(s)`);
|
|
228
|
+
return { scope, headsRemoved, containersRemoved, setupRequired: this.status().required };
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
exports.SetupService = SetupService;
|
|
232
|
+
exports.SetupService = SetupService = SetupService_1 = __decorate([
|
|
233
|
+
(0, common_1.Injectable)(),
|
|
234
|
+
__param(0, (0, common_1.Inject)(config_module_1.APP_CONFIG)),
|
|
235
|
+
__metadata("design:paramtypes", [Object, auth_1.AuthService,
|
|
236
|
+
head_service_1.HeadService])
|
|
237
|
+
], SetupService);
|
|
238
|
+
/**
|
|
239
|
+
* Compare without leaking the answer through timing.
|
|
240
|
+
*
|
|
241
|
+
* `timingSafeEqual` throws on a length mismatch, which would leak the length,
|
|
242
|
+
* so the lengths are compared first and the result folded in — a wrong-length
|
|
243
|
+
* token takes the same path as a wrong-value one.
|
|
244
|
+
*/
|
|
245
|
+
function constantTimeEquals(a, b) {
|
|
246
|
+
const left = Buffer.from(a);
|
|
247
|
+
const right = Buffer.from(b);
|
|
248
|
+
if (left.length !== right.length)
|
|
249
|
+
return false;
|
|
250
|
+
return (0, node_crypto_1.timingSafeEqual)(left, right);
|
|
251
|
+
}
|
|
252
|
+
/** Delete every trace of Hexcore. Used by `hexcore clean --all`, never by HTTP. */
|
|
253
|
+
async function wipeEverything(paths) {
|
|
254
|
+
await (0, promises_1.rm)(paths.stateDir, { recursive: true, force: true });
|
|
255
|
+
await (0, promises_1.rm)(paths.home, { recursive: true, force: true });
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=setup.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.service.js","sourceRoot":"","sources":["../src/setup.service.ts"],"names":[],"mappings":"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"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VERSION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The one place the running version comes from.
|
|
6
|
+
*
|
|
7
|
+
* It was written out by hand in three: the health payload, the Swagger
|
|
8
|
+
* document, and the CLI's `--version`. Two of them said `2.0.0` as a string
|
|
9
|
+
* literal, so a release bumps package.json and the Settings screen keeps
|
|
10
|
+
* reporting whatever the last person remembered to edit — a version that lies
|
|
11
|
+
* is worse than no version, because it is what someone quotes in a bug report.
|
|
12
|
+
*
|
|
13
|
+
* Read from package.json, which `npm version` and the pack script both already
|
|
14
|
+
* treat as the source of truth.
|
|
15
|
+
*/
|
|
16
|
+
const node_fs_1 = require("node:fs");
|
|
17
|
+
const node_path_1 = require("node:path");
|
|
18
|
+
function read() {
|
|
19
|
+
/**
|
|
20
|
+
* Two layouts, same as the web dist: installed from npm, `dist/version.js`
|
|
21
|
+
* sits one directory below package.json; in the monorepo it is the same
|
|
22
|
+
* shape. A missing or unreadable file yields '0.0.0-unknown' rather than
|
|
23
|
+
* throwing — a bad version string must never be the reason a server will
|
|
24
|
+
* not start.
|
|
25
|
+
*/
|
|
26
|
+
for (const candidate of ['../package.json', '../../package.json']) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.resolve)(__dirname, candidate), 'utf8'));
|
|
29
|
+
/**
|
|
30
|
+
* Matched by name so a stray package.json further up the tree
|
|
31
|
+
* cannot answer. The published name is scoped (`@vtechcom/hexcore`)
|
|
32
|
+
* and the workspace one is not, so both are accepted — and the
|
|
33
|
+
* suffix check keeps a fork's own scope working.
|
|
34
|
+
*/
|
|
35
|
+
if (parsed.version && (parsed.name?.endsWith('hexcore') || parsed.name === '@hexcore/control-plane')) {
|
|
36
|
+
return parsed.version;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Try the next candidate.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return '0.0.0-unknown';
|
|
44
|
+
}
|
|
45
|
+
exports.VERSION = read();
|
|
46
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,qCAAuC;AACvC,yCAAoC;AAEpC,SAAS,IAAI;IACT;;;;;;OAMG;IACH,KAAK,MAAM,SAAS,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,IAAA,mBAAO,EAAC,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,CAG5E,CAAC;YACF;;;;;eAKG;YACH,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,wBAAwB,CAAC,EAAE,CAAC;gBACnG,OAAO,MAAM,CAAC,OAAO,CAAC;YAC1B,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,0BAA0B;QAC9B,CAAC;IACL,CAAC;IACD,OAAO,eAAe,CAAC;AAC3B,CAAC;AAEY,QAAA,OAAO,GAAW,IAAI,EAAE,CAAC"}
|