@coffre/cli 0.1.0 → 0.1.2

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,64 @@
1
+ import { parseArgs } from "node:util";
2
+ import { randomBytes } from "node:crypto";
3
+ //#region src/keys.ts
4
+ /**
5
+ * Two fresh keys, each 32 random bytes in base64, and an id for the KEK
6
+ * dated to the day, so that a rotation, even one the same month, gets a new
7
+ * one: the vault refuses two KEKs with the same id.
8
+ */
9
+ function generateKeys(now = /* @__PURE__ */ new Date()) {
10
+ const key = () => randomBytes(32).toString("base64");
11
+ return {
12
+ KEK_ID: `kek-${now.toISOString().slice(0, 10)}`,
13
+ KEK: key(),
14
+ AUDIT_CHAIN_KEY: key()
15
+ };
16
+ }
17
+ /** What each key is for, as comments: `coffre keys` and `coffre setup` both say it. */
18
+ const KEYS_EXPLAINED = `# Two keys, one for each component, so that the app, which faces the
19
+ # network, never holds what decrypts a value:
20
+ #
21
+ # KEK the vault's. Decrypts every value, and signs the vault's
22
+ # log entries, member rows and checkpoints. Lose it, and
23
+ # every value is lost for good.
24
+ # AUDIT_CHAIN_KEY the app's. Signs the app's log entries, sessions and
25
+ # tokens. Lose it, and everyone is signed out and the log
26
+ # stops verifying.
27
+ # KEK_ID names the KEK; not secret.
28
+ #
29
+ # Whoever has the KEK and a copy of the database has every value: keep it
30
+ # apart from the backups.
31
+ `;
32
+ /** The keys as a dotenv block, then, as comments, what each is for and where it goes. */
33
+ function formatKeys(keys) {
34
+ return `KEK_ID=${keys.KEK_ID}
35
+ KEK=${keys.KEK}
36
+ AUDIT_CHAIN_KEY=${keys.AUDIT_CHAIN_KEY}
37
+
38
+ # Save all three in your password manager now. They are shown once, and
39
+ # coffre keeps no copy.
40
+ #
41
+ ${KEYS_EXPLAINED}#
42
+ # On Workers, KEK_ID is a var in vault/wrangler.jsonc, and KEK and
43
+ # AUDIT_CHAIN_KEY are Worker secrets (wrangler secret put). On Node, KEK_ID
44
+ # and KEK go in vault.env, and AUDIT_CHAIN_KEY in server.env.
45
+ #
46
+ # These are for a new deployment. To rotate a deployment's KEK, take only
47
+ # KEK_ID and KEK, and keep the old pair in previousKeks, for what it wrapped
48
+ # and signed. AUDIT_CHAIN_KEY cannot be changed.
49
+ `;
50
+ }
51
+ function keys(args) {
52
+ const { values } = parseArgs({
53
+ args,
54
+ options: { json: {
55
+ type: "boolean",
56
+ default: false
57
+ } },
58
+ allowPositionals: false
59
+ });
60
+ const fresh = generateKeys();
61
+ process.stdout.write(values.json ? `${JSON.stringify(fresh)}\n` : formatKeys(fresh));
62
+ }
63
+ //#endregion
64
+ export { generateKeys as n, keys as r, KEYS_EXPLAINED as t };
package/dist/main.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { r as keys } from "./keys-D9CG_wYV.js";
3
+ import { i as isRole, r as assignableToEnvironment, t as ROLES } from "./access-CO1kGjWW.js";
2
4
  import { parseArgs, promisify } from "node:util";
3
5
  import { execFile, spawn } from "node:child_process";
4
6
  import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -466,79 +468,6 @@ async function planImport(coffre, path, entries) {
466
468
  };
467
469
  }
468
470
  //#endregion
469
- //#region ../core/dist/access.js
470
- /**
471
- * Who may do what: seven permissions and the six roles that bundle them.
472
- *
473
- * Deliberately constants and not tables or a policy language. A grant names
474
- * one role for one member on one place, and a check is a lookup in this file,
475
- * so authorisation stays something you can read end to end.
476
- */
477
- const PERMISSIONS = [
478
- "secret.read",
479
- "secret.write",
480
- "secret.archive",
481
- "audit.read",
482
- "environment.manage",
483
- "grant.manage",
484
- "project.manage"
485
- ];
486
- /**
487
- * Permissions that only make sense on a project. Granting
488
- * `environment.manage` on one environment would be incoherent: what it
489
- * authorises is creating that environment's siblings.
490
- */
491
- const PROJECT_ONLY_PERMISSIONS = [
492
- "environment.manage",
493
- "grant.manage",
494
- "project.manage"
495
- ];
496
- const ROLES = {
497
- viewer: {
498
- name: "Viewer",
499
- description: "Read secret values.",
500
- permissions: ["secret.read"]
501
- },
502
- developer: {
503
- name: "Developer",
504
- description: "Read and write secrets.",
505
- permissions: ["secret.read", "secret.write"]
506
- },
507
- maintainer: {
508
- name: "Maintainer",
509
- description: "Read, write and retire secrets, and manage environments.",
510
- permissions: [
511
- "secret.read",
512
- "secret.write",
513
- "secret.archive",
514
- "environment.manage"
515
- ]
516
- },
517
- "access-manager": {
518
- name: "Access manager",
519
- description: "Manage who has access. Cannot read secret values.",
520
- permissions: ["grant.manage"]
521
- },
522
- auditor: {
523
- name: "Auditor",
524
- description: "Read the audit log. Cannot read secret values.",
525
- permissions: ["audit.read"]
526
- },
527
- owner: {
528
- name: "Owner",
529
- description: "Everything, including reading secret values.",
530
- permissions: [...PERMISSIONS]
531
- }
532
- };
533
- Object.keys(ROLES);
534
- function isRole(value) {
535
- return Object.hasOwn(ROLES, value);
536
- }
537
- /** Roles that hold a project-only permission can only be granted on a project. */
538
- function assignableToEnvironment(role) {
539
- return !ROLES[role].permissions.some((permission) => PROJECT_ONLY_PERMISSIONS.includes(permission));
540
- }
541
- //#endregion
542
471
  //#region ../core/dist/dotenv.js
543
472
  const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
544
473
  function parseDotenv(input) {
@@ -1531,6 +1460,10 @@ const USAGE = `coffre - secrets, with an audit log
1531
1460
  New deployment
1532
1461
  coffre init --workers [<dir>] two Cloudflare Workers: the app and its vault
1533
1462
  coffre init --node [<dir>] a Node server, and its vault beside it
1463
+ coffre setup [--reset-passwords] [--json]
1464
+ its database logins, migrations and keys, in one go,
1465
+ shown once (the connection string is asked for)
1466
+ coffre keys [--json] its keys and the KEK's id alone, made here and shown once
1534
1467
 
1535
1468
  Session
1536
1469
  coffre login [<url>] [--no-browser] sign in, and make <url> the current instance
@@ -1579,6 +1512,12 @@ switch (command) {
1579
1512
  case "init":
1580
1513
  initProject(rest);
1581
1514
  break;
1515
+ case "keys":
1516
+ keys(rest);
1517
+ break;
1518
+ case "setup":
1519
+ await (await import("./setup-DPHVyr2N.js")).setup(rest);
1520
+ break;
1582
1521
  case "login":
1583
1522
  await login(rest);
1584
1523
  break;