@formancy/data-server 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,69 @@
1
+ import { i as oneKeySet, n as openKeySetFile, s as isKeySetAlgorithm } from "./secrets-DhKHG-Y9.mjs";
2
+ import { n as oneKey, t as identityKeySource } from "./identity-settings-BlQY9gHy.mjs";
3
+ import { readFile } from "node:fs/promises";
4
+ //#region src/identity-keys-tool.ts
5
+ const USAGE = "Usage: identity-keys-cli.mjs check <file> <RS256|ES256|EdDSA|HS256>\n identity-keys-cli.mjs convert <kid>";
6
+ /** The server's own sentence: readKeySet's, resolveSecret's, jose's or the file system's, none of which repeats a key or a secret. */
7
+ function sentence(error) {
8
+ return error instanceof Error ? error.message : "the key could not be read";
9
+ }
10
+ async function check(path, algorithm, io) {
11
+ if (!isKeySetAlgorithm(algorithm)) {
12
+ io.stderr("The algorithm is RS256, ES256, EdDSA or HS256, as FORMANCY_DATA_IDENTITY_ALGORITHM will say.\n");
13
+ return 1;
14
+ }
15
+ try {
16
+ const set = (await openKeySetFile(path, algorithm)).current();
17
+ const leftOut = set.leftOut.map((key) => `${key.key} (${key.because})`).join(", ");
18
+ io.stdout(`in force: ${set.kids.join(", ")}\nleft out: ${leftOut === "" ? "none" : leftOut}\n`);
19
+ return 0;
20
+ } catch (error) {
21
+ io.stderr(`${sentence(error)}\n`);
22
+ return 1;
23
+ }
24
+ }
25
+ async function convert(kid, env, io) {
26
+ const setting = identityKeySource(env);
27
+ if (!setting.ok) {
28
+ io.stderr(`${setting.problem}\n`);
29
+ return 1;
30
+ }
31
+ const { source } = setting;
32
+ if (source.kind === "key-set") {
33
+ io.stderr("This environment already names a key set file (FORMANCY_DATA_IDENTITY_KEYS_FILE); convert writes one from FORMANCY_DATA_IDENTITY_SECRET or FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE.\n");
34
+ return 1;
35
+ }
36
+ try {
37
+ const set = await oneKeySet(await oneKey(source, {
38
+ env,
39
+ readFile: (path) => io.readFile(path)
40
+ }));
41
+ const document = await set.document(kid);
42
+ const was = source.kind === "secret" ? "FORMANCY_DATA_IDENTITY_SECRET" : "FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE";
43
+ io.stdout(document);
44
+ io.stderr(`Set FORMANCY_DATA_IDENTITY_KEYS_FILE to where this set is, FORMANCY_DATA_IDENTITY_ALGORITHM=${set.algorithm}, and unset ${was}.\n`);
45
+ return 0;
46
+ } catch (error) {
47
+ io.stderr(`${sentence(error)}\n`);
48
+ return 1;
49
+ }
50
+ }
51
+ /** The tool's exit status for `argv` (without node and the script). */
52
+ async function runKeysTool(argv, env, io) {
53
+ const [command, ...rest] = argv;
54
+ if (command === "check" && rest.length === 2) return check(rest[0], rest[1], io);
55
+ if (command === "convert" && rest.length === 1) return convert(rest[0], env, io);
56
+ io.stderr(`${USAGE}\n`);
57
+ return 1;
58
+ }
59
+ //#endregion
60
+ //#region src/identity-keys-cli.ts
61
+ process.exitCode = await runKeysTool(process.argv.slice(2), process.env, {
62
+ stdout: (text) => process.stdout.write(text),
63
+ stderr: (text) => process.stderr.write(text),
64
+ readFile: (path) => readFile(path, "utf8")
65
+ });
66
+ //#endregion
67
+ export {};
68
+
69
+ //# sourceMappingURL=identity-keys-cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-keys-cli.mjs","names":[],"sources":["../src/identity-keys-tool.ts","../src/identity-keys-cli.ts"],"sourcesContent":["import { oneKeySet } from './identity.js'\nimport { openKeySetFile } from './identity-file.js'\nimport { isKeySetAlgorithm } from './identity-keys.js'\nimport { identityKeySource, oneKey } from './identity-settings.js'\n\n/**\n * The key set file tool the image ships as `dist/identity-keys-cli.mjs`\n * (0059), over the server's own functions:\n *\n * check <file> <algorithm> readKeySet over the file: what is in force, what is left out; exit 1 with its sentence if refused\n * convert <kid> the key FORMANCY_DATA_IDENTITY_SECRET or _PUBLIC_KEY_FILE names, as a one-key set on stdout\n *\n * `check` is what an operator's job runs before it renames a file into place:\n * a refused file left there stops the server's next start. `convert` reads\n * the setting through identityKeySource and resolveSecret, as main.ts does,\n * so the key it writes is the key the server verifies with today.\n *\n * Nothing it prints to stderr holds key material; `convert`'s stdout is key\n * material, the file the operator asked for.\n */\n\nexport interface KeysToolIo {\n stdout(text: string): void\n stderr(text: string): void\n readFile(path: string): Promise<string>\n}\n\nconst USAGE = 'Usage: identity-keys-cli.mjs check <file> <RS256|ES256|EdDSA|HS256>\\n identity-keys-cli.mjs convert <kid>'\n\n/** The server's own sentence: readKeySet's, resolveSecret's, jose's or the file system's, none of which repeats a key or a secret. */\nfunction sentence(error: unknown): string {\n return error instanceof Error ? error.message : 'the key could not be read'\n}\n\nasync function check(path: string, algorithm: string, io: KeysToolIo): Promise<number> {\n if (!isKeySetAlgorithm(algorithm)) {\n io.stderr('The algorithm is RS256, ES256, EdDSA or HS256, as FORMANCY_DATA_IDENTITY_ALGORITHM will say.\\n')\n return 1\n }\n try {\n // Read as the server reads it, through readKeySetText: a path that is\n // not a regular file is refused here as it would be on SIGHUP.\n const set = (await openKeySetFile(path, algorithm)).current()\n const leftOut = set.leftOut.map((key) => `${key.key} (${key.because})`).join(', ')\n io.stdout(`in force: ${set.kids.join(', ')}\\nleft out: ${leftOut === '' ? 'none' : leftOut}\\n`)\n return 0\n } catch (error) {\n io.stderr(`${sentence(error)}\\n`)\n return 1\n }\n}\n\nasync function convert(kid: string, env: Readonly<Record<string, string | undefined>>, io: KeysToolIo): Promise<number> {\n const setting = identityKeySource(env)\n if (!setting.ok) {\n io.stderr(`${setting.problem}\\n`)\n return 1\n }\n const { source } = setting\n if (source.kind === 'key-set') {\n io.stderr('This environment already names a key set file (FORMANCY_DATA_IDENTITY_KEYS_FILE); convert writes one from FORMANCY_DATA_IDENTITY_SECRET or FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE.\\n')\n return 1\n }\n try {\n const set = await oneKeySet(await oneKey(source, { env, readFile: (path) => io.readFile(path) }))\n const document = await set.document(kid)\n const was = source.kind === 'secret' ? 'FORMANCY_DATA_IDENTITY_SECRET' : 'FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE'\n io.stdout(document)\n io.stderr(`Set FORMANCY_DATA_IDENTITY_KEYS_FILE to where this set is, FORMANCY_DATA_IDENTITY_ALGORITHM=${set.algorithm}, and unset ${was}.\\n`)\n return 0\n } catch (error) {\n io.stderr(`${sentence(error)}\\n`)\n return 1\n }\n}\n\n/** The tool's exit status for `argv` (without node and the script). */\nexport async function runKeysTool(argv: readonly string[], env: Readonly<Record<string, string | undefined>>, io: KeysToolIo): Promise<number> {\n const [command, ...rest] = argv\n if (command === 'check' && rest.length === 2) return check(rest[0] as string, rest[1] as string, io)\n if (command === 'convert' && rest.length === 1) return convert(rest[0] as string, env, io)\n io.stderr(`${USAGE}\\n`)\n return 1\n}\n","import { readFile } from 'node:fs/promises'\nimport { runKeysTool } from './identity-keys-tool.js'\n\n// `node dist/identity-keys-cli.mjs check <file> <algorithm> | convert <kid>`:\n// the image's key set file tool (0059), an entry beside main.mjs. What it\n// does is runKeysTool's, which its tests drive; this only hands it the process.\nprocess.exitCode = await runKeysTool(process.argv.slice(2), process.env, {\n stdout: (text) => process.stdout.write(text),\n stderr: (text) => process.stderr.write(text),\n readFile: (path) => readFile(path, 'utf8'),\n})\n"],"mappings":";;;;AA2BA,MAAM,QAAQ;;AAGd,SAAS,SAAS,OAAwB;CACxC,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAClD;AAEA,eAAe,MAAM,MAAc,WAAmB,IAAiC;CACrF,IAAI,CAAC,kBAAkB,SAAS,GAAG;EACjC,GAAG,OAAO,gGAAgG;EAC1G,OAAO;CACT;CACA,IAAI;EAGF,MAAM,OAAO,MAAM,eAAe,MAAM,SAAS,EAAA,CAAG,QAAQ;EAC5D,MAAM,UAAU,IAAI,QAAQ,KAAK,QAAQ,GAAG,IAAI,IAAI,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC,KAAK,IAAI;EACjF,GAAG,OAAO,aAAa,IAAI,KAAK,KAAK,IAAI,EAAE,cAAc,YAAY,KAAK,SAAS,QAAQ,GAAG;EAC9F,OAAO;CACT,SAAS,OAAO;EACd,GAAG,OAAO,GAAG,SAAS,KAAK,EAAE,GAAG;EAChC,OAAO;CACT;AACF;AAEA,eAAe,QAAQ,KAAa,KAAmD,IAAiC;CACtH,MAAM,UAAU,kBAAkB,GAAG;CACrC,IAAI,CAAC,QAAQ,IAAI;EACf,GAAG,OAAO,GAAG,QAAQ,QAAQ,GAAG;EAChC,OAAO;CACT;CACA,MAAM,EAAE,WAAW;CACnB,IAAI,OAAO,SAAS,WAAW;EAC7B,GAAG,OAAO,sLAAsL;EAChM,OAAO;CACT;CACA,IAAI;EACF,MAAM,MAAM,MAAM,UAAU,MAAM,OAAO,QAAQ;GAAE;GAAK,WAAW,SAAS,GAAG,SAAS,IAAI;EAAE,CAAC,CAAC;EAChG,MAAM,WAAW,MAAM,IAAI,SAAS,GAAG;EACvC,MAAM,MAAM,OAAO,SAAS,WAAW,kCAAkC;EACzE,GAAG,OAAO,QAAQ;EAClB,GAAG,OAAO,+FAA+F,IAAI,UAAU,cAAc,IAAI,IAAI;EAC7I,OAAO;CACT,SAAS,OAAO;EACd,GAAG,OAAO,GAAG,SAAS,KAAK,EAAE,GAAG;EAChC,OAAO;CACT;AACF;;AAGA,eAAsB,YAAY,MAAyB,KAAmD,IAAiC;CAC7I,MAAM,CAAC,SAAS,GAAG,QAAQ;CAC3B,IAAI,YAAY,WAAW,KAAK,WAAW,GAAG,OAAO,MAAM,KAAK,IAAc,KAAK,IAAc,EAAE;CACnG,IAAI,YAAY,aAAa,KAAK,WAAW,GAAG,OAAO,QAAQ,KAAK,IAAc,KAAK,EAAE;CACzF,GAAG,OAAO,GAAG,MAAM,GAAG;CACtB,OAAO;AACT;;;AC7EA,QAAQ,WAAW,MAAM,YAAY,QAAQ,KAAK,MAAM,CAAC,GAAG,QAAQ,KAAK;CACvE,SAAS,SAAS,QAAQ,OAAO,MAAM,IAAI;CAC3C,SAAS,SAAS,QAAQ,OAAO,MAAM,IAAI;CAC3C,WAAW,SAAS,SAAS,MAAM,MAAM;AAC3C,CAAC"}
@@ -0,0 +1,165 @@
1
+ import { n as openKeySetFile, r as createIdentityVerifier, s as isKeySetAlgorithm, t as resolveSecret } from "./secrets-DhKHG-Y9.mjs";
2
+ import { isAbsolute } from "node:path";
3
+ //#region src/identity-settings.ts
4
+ function identityKeySource(env) {
5
+ const reference = env["FORMANCY_DATA_IDENTITY_SECRET"];
6
+ const publicKeyFile = env["FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE"];
7
+ const keysFile = env["FORMANCY_DATA_IDENTITY_KEYS_FILE"];
8
+ const algorithm = env["FORMANCY_DATA_IDENTITY_ALGORITHM"];
9
+ if ([
10
+ reference,
11
+ publicKeyFile,
12
+ keysFile
13
+ ].filter((value) => value !== void 0).length !== 1) return {
14
+ ok: false,
15
+ problem: "Set exactly one of FORMANCY_DATA_IDENTITY_SECRET, FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE and FORMANCY_DATA_IDENTITY_KEYS_FILE."
16
+ };
17
+ if (reference !== void 0) return {
18
+ ok: true,
19
+ source: {
20
+ kind: "secret",
21
+ reference
22
+ }
23
+ };
24
+ if (publicKeyFile !== void 0) {
25
+ if (algorithm === void 0 || algorithm === "") return {
26
+ ok: false,
27
+ problem: "FORMANCY_DATA_IDENTITY_ALGORITHM is required."
28
+ };
29
+ if (algorithm === "HS256") return {
30
+ ok: false,
31
+ problem: "HS256 is a shared secret: it is FORMANCY_DATA_IDENTITY_SECRET or a key set, never a public key file."
32
+ };
33
+ if (algorithm !== "RS256" && algorithm !== "ES256" && algorithm !== "EdDSA") return {
34
+ ok: false,
35
+ problem: "FORMANCY_DATA_IDENTITY_ALGORITHM is RS256, ES256 or EdDSA."
36
+ };
37
+ return {
38
+ ok: true,
39
+ source: {
40
+ kind: "public-key",
41
+ path: publicKeyFile,
42
+ algorithm
43
+ }
44
+ };
45
+ }
46
+ const path = keysFile;
47
+ if (algorithm === void 0 || algorithm === "") return {
48
+ ok: false,
49
+ problem: "FORMANCY_DATA_IDENTITY_ALGORITHM is required with FORMANCY_DATA_IDENTITY_KEYS_FILE: RS256, ES256, EdDSA or HS256, for every key in the set."
50
+ };
51
+ if (!isKeySetAlgorithm(algorithm)) return {
52
+ ok: false,
53
+ problem: "FORMANCY_DATA_IDENTITY_ALGORITHM is RS256, ES256, EdDSA or HS256 for a key set."
54
+ };
55
+ if (!isAbsolute(path)) return {
56
+ ok: false,
57
+ problem: "FORMANCY_DATA_IDENTITY_KEYS_FILE is an absolute path: the file is read again, on SIGHUP, from wherever the server then runs."
58
+ };
59
+ return {
60
+ ok: true,
61
+ source: {
62
+ kind: "key-set",
63
+ path,
64
+ algorithm
65
+ }
66
+ };
67
+ }
68
+ /** The one key a secret or a public key file names, read as main.ts reads it: the secret through resolveSecret, the PEM as the file's text. */
69
+ async function oneKey(source, io) {
70
+ if (source.kind === "secret") return {
71
+ kind: "secret",
72
+ secret: await resolveSecret(source.reference, io)
73
+ };
74
+ let pem;
75
+ try {
76
+ pem = await io.readFile(source.path);
77
+ } catch {
78
+ throw new Error(`the identity public key file ${source.path} cannot be read`);
79
+ }
80
+ return {
81
+ kind: "public-key",
82
+ pem,
83
+ algorithm: source.algorithm
84
+ };
85
+ }
86
+ /**
87
+ * The verifier main.ts serves with, and the key set file it reads again on
88
+ * SIGHUP when the setting names one. Every refusal of the key at start -- a
89
+ * secret or a file that cannot be read, a secret under 32 bytes, a public key
90
+ * that does not import, a key set refused -- is the problem, one sentence
91
+ * main.ts prints before it exits 1, never an exception: a short secret and a
92
+ * PEM that did not import used to be thrown by createIdentityVerifier, outside
93
+ * main's handling, as a stack trace. The key set file is read through
94
+ * readKeySetText, never `io`, so a path that is not a regular file is refused
95
+ * at start as it is on SIGHUP.
96
+ */
97
+ async function openIdentity(source, io, claims) {
98
+ try {
99
+ let key;
100
+ let file;
101
+ if (source.kind === "key-set") {
102
+ file = await openKeySetFile(source.path, source.algorithm);
103
+ key = {
104
+ kind: "key-set",
105
+ keys: file.current
106
+ };
107
+ } else key = await oneKey(source, io);
108
+ return {
109
+ ok: true,
110
+ verifyIdentity: await createIdentityVerifier({
111
+ ...claims,
112
+ key
113
+ }),
114
+ file
115
+ };
116
+ } catch (error) {
117
+ return {
118
+ ok: false,
119
+ problem: error instanceof Error ? error.message : "the identity key could not be opened"
120
+ };
121
+ }
122
+ }
123
+ /**
124
+ * Reads the key set file again on every SIGHUP, and says what happened in
125
+ * one line on the server's log: the kids in force and what was added,
126
+ * removed and replaced; that nothing changed; or, at `error`, that nothing
127
+ * was read and why, the set in force unchanged. No line holds key material,
128
+ * and none a hash of the file, which for HS256 would let a reader of the log
129
+ * check a guess at the secret.
130
+ *
131
+ * The listener is installed whatever the setting, so a SIGHUP never stops
132
+ * the server -- Node's default for it is to terminate -- and with a one-key
133
+ * setting it says that nothing was read.
134
+ */
135
+ function readAgainOnSignal(signals, file, log) {
136
+ signals.on("SIGHUP", () => {
137
+ if (file === void 0) {
138
+ log.warn("SIGHUP: the identity key is not a key set file; nothing was read again");
139
+ return;
140
+ }
141
+ file.readAgain().then((outcome) => {
142
+ if (!outcome.read) log.error({ identityKeys: {
143
+ kids: outcome.kids,
144
+ problem: outcome.problem
145
+ } }, "identity keys not read again; the set in force is unchanged");
146
+ else if (outcome.changed) {
147
+ const { kids, added, removed, replaced, leftOut } = outcome;
148
+ log.info({ identityKeys: {
149
+ kids,
150
+ added,
151
+ removed,
152
+ replaced,
153
+ leftOut
154
+ } }, "identity keys read again");
155
+ } else log.info({ identityKeys: {
156
+ kids: outcome.kids,
157
+ leftOut: outcome.leftOut
158
+ } }, "identity keys read again; nothing changed");
159
+ });
160
+ });
161
+ }
162
+ //#endregion
163
+ export { readAgainOnSignal as i, oneKey as n, openIdentity as r, identityKeySource as t };
164
+
165
+ //# sourceMappingURL=identity-settings-BlQY9gHy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-settings-BlQY9gHy.mjs","names":[],"sources":["../src/identity-settings.ts"],"sourcesContent":["import { isAbsolute } from 'node:path'\nimport { createIdentityVerifier } from './identity.js'\nimport type { IdentityOptions, IdentityVerifier, OneKey } from './identity.js'\nimport { openKeySetFile } from './identity-file.js'\nimport type { KeySetFile } from './identity-file.js'\nimport { isKeySetAlgorithm } from './identity-keys.js'\nimport type { KeySetAlgorithm, PublicKeyAlgorithm } from './identity-keys.js'\nimport { resolveSecret } from './secrets.js'\nimport type { SecretSource } from './secrets.js'\n\n/**\n * Where the host identity's key comes from, as the environment names it, and\n * what a SIGHUP does with it (0014, 0059). Read by the composition root\n * (main.ts) and by `identity-keys-cli.mjs convert`, so the two never read the\n * settings differently, and parsed and opened here, so the rules and every\n * refusal at start are tested without starting a process.\n *\n * FORMANCY_DATA_IDENTITY_SECRET a secret reference, env:NAME or file:/path (HS256)\n * FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE or a PEM public key, with FORMANCY_DATA_IDENTITY_ALGORITHM RS256, ES256 or EdDSA\n * FORMANCY_DATA_IDENTITY_KEYS_FILE or the absolute path of a JSON Web Key Set, with FORMANCY_DATA_IDENTITY_ALGORITHM\n * RS256, ES256, EdDSA or HS256, pinned for the whole set and read again on SIGHUP\n *\n * Exactly one of the three; \"set\" means defined, as it always has. No\n * sentence repeats a setting's value.\n */\nexport type IdentityKeySource =\n | { kind: 'secret'; reference: string }\n | { kind: 'public-key'; path: string; algorithm: PublicKeyAlgorithm }\n | { kind: 'key-set'; path: string; algorithm: KeySetAlgorithm }\n\nexport type IdentityKeySetting = { ok: true; source: IdentityKeySource } | { ok: false; problem: string }\n\ntype Environment = Readonly<Record<string, string | undefined>>\n\nexport function identityKeySource(env: Environment): IdentityKeySetting {\n const reference = env['FORMANCY_DATA_IDENTITY_SECRET']\n const publicKeyFile = env['FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE']\n const keysFile = env['FORMANCY_DATA_IDENTITY_KEYS_FILE']\n const algorithm = env['FORMANCY_DATA_IDENTITY_ALGORITHM']\n if ([reference, publicKeyFile, keysFile].filter((value) => value !== undefined).length !== 1) {\n return { ok: false, problem: 'Set exactly one of FORMANCY_DATA_IDENTITY_SECRET, FORMANCY_DATA_IDENTITY_PUBLIC_KEY_FILE and FORMANCY_DATA_IDENTITY_KEYS_FILE.' }\n }\n if (reference !== undefined) return { ok: true, source: { kind: 'secret', reference } }\n\n if (publicKeyFile !== undefined) {\n if (algorithm === undefined || algorithm === '') return { ok: false, problem: 'FORMANCY_DATA_IDENTITY_ALGORITHM is required.' }\n // A PEM's text taken as an HMAC secret verifies any token its reader signs with it.\n if (algorithm === 'HS256') return { ok: false, problem: 'HS256 is a shared secret: it is FORMANCY_DATA_IDENTITY_SECRET or a key set, never a public key file.' }\n if (algorithm !== 'RS256' && algorithm !== 'ES256' && algorithm !== 'EdDSA') return { ok: false, problem: 'FORMANCY_DATA_IDENTITY_ALGORITHM is RS256, ES256 or EdDSA.' }\n return { ok: true, source: { kind: 'public-key', path: publicKeyFile, algorithm } }\n }\n\n const path = keysFile as string\n if (algorithm === undefined || algorithm === '') {\n return { ok: false, problem: 'FORMANCY_DATA_IDENTITY_ALGORITHM is required with FORMANCY_DATA_IDENTITY_KEYS_FILE: RS256, ES256, EdDSA or HS256, for every key in the set.' }\n }\n if (!isKeySetAlgorithm(algorithm)) return { ok: false, problem: 'FORMANCY_DATA_IDENTITY_ALGORITHM is RS256, ES256, EdDSA or HS256 for a key set.' }\n if (!isAbsolute(path)) return { ok: false, problem: 'FORMANCY_DATA_IDENTITY_KEYS_FILE is an absolute path: the file is read again, on SIGHUP, from wherever the server then runs.' }\n return { ok: true, source: { kind: 'key-set', path, algorithm } }\n}\n\n/** The one key a secret or a public key file names, read as main.ts reads it: the secret through resolveSecret, the PEM as the file's text. */\nexport async function oneKey(source: Exclude<IdentityKeySource, { kind: 'key-set' }>, io: SecretSource): Promise<OneKey> {\n if (source.kind === 'secret') return { kind: 'secret', secret: await resolveSecret(source.reference, io) }\n let pem: string\n try {\n pem = await io.readFile(source.path)\n } catch {\n // Named as resolveSecret names a secret file; the error's own text is not passed on.\n throw new Error(`the identity public key file ${source.path} cannot be read`)\n }\n return { kind: 'public-key', pem, algorithm: source.algorithm }\n}\n\nexport type OpenedIdentity = { ok: true; verifyIdentity: IdentityVerifier; file: KeySetFile | undefined } | { ok: false; problem: string }\n\n/**\n * The verifier main.ts serves with, and the key set file it reads again on\n * SIGHUP when the setting names one. Every refusal of the key at start -- a\n * secret or a file that cannot be read, a secret under 32 bytes, a public key\n * that does not import, a key set refused -- is the problem, one sentence\n * main.ts prints before it exits 1, never an exception: a short secret and a\n * PEM that did not import used to be thrown by createIdentityVerifier, outside\n * main's handling, as a stack trace. The key set file is read through\n * readKeySetText, never `io`, so a path that is not a regular file is refused\n * at start as it is on SIGHUP.\n */\nexport async function openIdentity(source: IdentityKeySource, io: SecretSource, claims: Omit<IdentityOptions, 'key'>): Promise<OpenedIdentity> {\n try {\n let key: IdentityOptions['key']\n let file: KeySetFile | undefined\n if (source.kind === 'key-set') {\n file = await openKeySetFile(source.path, source.algorithm)\n key = { kind: 'key-set', keys: file.current }\n } else {\n key = await oneKey(source, io)\n }\n return { ok: true, verifyIdentity: await createIdentityVerifier({ ...claims, key }), file }\n } catch (error) {\n // Each sentence above is resolveSecret's, readKeySet's or the builders',\n // none of which repeats a key or a secret.\n return { ok: false, problem: error instanceof Error ? error.message : 'the identity key could not be opened' }\n }\n}\n\n/** Where a signal comes from: `process` in the server, an EventEmitter in a test. */\nexport interface SignalSource {\n on(signal: 'SIGHUP', listener: () => void): unknown\n}\n\n/** The server's log, as much of it as this writes to. */\nexport interface KeysLog {\n info(fields: Record<string, unknown>, message: string): void\n warn(message: string): void\n error(fields: Record<string, unknown>, message: string): void\n}\n\n/**\n * Reads the key set file again on every SIGHUP, and says what happened in\n * one line on the server's log: the kids in force and what was added,\n * removed and replaced; that nothing changed; or, at `error`, that nothing\n * was read and why, the set in force unchanged. No line holds key material,\n * and none a hash of the file, which for HS256 would let a reader of the log\n * check a guess at the secret.\n *\n * The listener is installed whatever the setting, so a SIGHUP never stops\n * the server -- Node's default for it is to terminate -- and with a one-key\n * setting it says that nothing was read.\n */\nexport function readAgainOnSignal(signals: SignalSource, file: KeySetFile | undefined, log: KeysLog): void {\n signals.on('SIGHUP', () => {\n if (file === undefined) {\n log.warn('SIGHUP: the identity key is not a key set file; nothing was read again')\n return\n }\n void file.readAgain().then((outcome) => {\n if (!outcome.read) {\n log.error({ identityKeys: { kids: outcome.kids, problem: outcome.problem } }, 'identity keys not read again; the set in force is unchanged')\n } else if (outcome.changed) {\n const { kids, added, removed, replaced, leftOut } = outcome\n log.info({ identityKeys: { kids, added, removed, replaced, leftOut } }, 'identity keys read again')\n } else {\n log.info({ identityKeys: { kids: outcome.kids, leftOut: outcome.leftOut } }, 'identity keys read again; nothing changed')\n }\n })\n })\n}\n"],"mappings":"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"}