@coffre/cli 0.1.2 → 0.1.3
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/dist/keys-RhB7ayOY.js +726 -0
- package/dist/main.js +6 -6
- package/dist/setup-3OJc_VCA.js +756 -0
- package/dist/templates/node/README.md +20 -17
- package/dist/templates/node/package.json +3 -3
- package/dist/templates/node/server.env.example +3 -3
- package/dist/templates/node/src/server.ts +2 -2
- package/dist/templates/node/src/vault.ts +7 -7
- package/dist/templates/node/vault.env.example +6 -5
- package/dist/templates/workers/README.md +28 -23
- package/dist/templates/workers/app/src/worker.ts +2 -2
- package/dist/templates/workers/app/wrangler.jsonc +4 -4
- package/dist/templates/workers/package.json +4 -4
- package/dist/templates/workers/vault/src/worker.ts +8 -8
- package/dist/templates/workers/vault/wrangler.jsonc +8 -7
- package/package.json +4 -4
- package/dist/keys-D9CG_wYV.js +0 -64
- package/dist/setup-DPHVyr2N.js +0 -521
|
@@ -9,7 +9,7 @@ Two processes, configured in code:
|
|
|
9
9
|
only the server, on a Unix socket.
|
|
10
10
|
|
|
11
11
|
Run them as two users that share a group, and the process facing the network
|
|
12
|
-
never holds the
|
|
12
|
+
never holds the vault key. For local development, `server.ts` can run the vault
|
|
13
13
|
in its own process instead; see the comment there. Everything below runs
|
|
14
14
|
from this directory, on Node 24 or later.
|
|
15
15
|
|
|
@@ -35,23 +35,26 @@ npx @coffre/cli setup
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Run it with the CLI you ran `coffre init` with. It asks for the database
|
|
38
|
-
administrator's connection string at a hidden prompt
|
|
39
|
-
in, or set `COFFRE_SETUP_DATABASE_URL`; never pass it as an argument
|
|
38
|
+
administrator's connection string at a hidden prompt. A script can pipe it
|
|
39
|
+
in, or set `COFFRE_SETUP_DATABASE_URL`; never pass it as an argument. It
|
|
40
40
|
makes the two logins coffre runs as, `coffre_runtime` for the server and
|
|
41
|
-
`coffre_vault_runtime` for the vault, migrates the database, checks that
|
|
42
|
-
each login holds only its rights
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
41
|
+
`coffre_vault_runtime` for the vault, migrates the database, and checks that
|
|
42
|
+
each login holds only its rights.
|
|
43
|
+
|
|
44
|
+
Then it shows five values on a screen of their own, which leaves nothing
|
|
45
|
+
behind in your scrollback. Copy each with `c` into your password manager,
|
|
46
|
+
beside the OAuth client secret, then into its file; `w` shows where each
|
|
47
|
+
goes. Nothing keeps a copy. There is one key for each process, so that the
|
|
48
|
+
server, which faces the network, never holds what decrypts a value.
|
|
49
|
+
|
|
50
|
+
- `server.env` takes the app key, `APP_KEY`, and the app's database URL, as
|
|
51
|
+
`DATABASE_URL`. The app key signs the server's log entries, sessions and
|
|
52
|
+
tokens. Lose it, and everyone is signed out and the log stops verifying.
|
|
53
|
+
- `vault.env` takes the vault ID, `VAULT_KEY_ID`, the vault key, `VAULT_KEY`,
|
|
54
|
+
and the vault's database URL, as `DATABASE_URL`. The vault key decrypts
|
|
55
|
+
every value, and the vault derives from it the key it signs its records
|
|
56
|
+
with. Lose it, and every value is lost. The vault ID only names it.
|
|
57
|
+
- Both URLs are the same database, each through its process's own login.
|
|
55
58
|
Neither process gets the administrator's URL.
|
|
56
59
|
|
|
57
60
|
With AWS KMS instead of a key of your own, the vault also needs a
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
"conformance": "coffre-conformance node"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@coffre/server": "0.1.
|
|
17
|
-
"@coffre/vault": "0.1.
|
|
16
|
+
"@coffre/server": "0.1.3",
|
|
17
|
+
"@coffre/vault": "0.1.3"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@coffre/conformance": "0.1.
|
|
20
|
+
"@coffre/conformance": "0.1.3",
|
|
21
21
|
"@types/node": "26.1.1",
|
|
22
22
|
"typescript": "5.9.3"
|
|
23
23
|
}
|
|
@@ -14,6 +14,6 @@ VAULT_SOCKET=vault.sock
|
|
|
14
14
|
GITHUB_CLIENT_ID=
|
|
15
15
|
GITHUB_CLIENT_SECRET=
|
|
16
16
|
|
|
17
|
-
# From `coffre setup`, saved in your password manager first:
|
|
18
|
-
# server's log entries, sessions and tokens.
|
|
19
|
-
|
|
17
|
+
# From `coffre setup`, saved in your password manager first: the app key,
|
|
18
|
+
# which signs the server's log entries, sessions and tokens.
|
|
19
|
+
APP_KEY=
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// coffre's server: the API, sign-in, pages and scheduled job, in one
|
|
2
|
-
// process. It holds no
|
|
2
|
+
// process. It holds no vault key: it asks the vault, a process of its own
|
|
3
3
|
// (src/vault.ts), over a Unix socket. Settings come from server.env.
|
|
4
4
|
import { github, serve, signin } from '@coffre/server/node';
|
|
5
5
|
import { connectVault } from '@coffre/vault/node';
|
|
@@ -28,7 +28,7 @@ const server = await serve({
|
|
|
28
28
|
}),
|
|
29
29
|
],
|
|
30
30
|
}),
|
|
31
|
-
auditChainKey: env('
|
|
31
|
+
auditChainKey: env('APP_KEY'),
|
|
32
32
|
});
|
|
33
33
|
console.log(`coffre is listening on ${server.url}`);
|
|
34
34
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The vault: the keys, and the members and grants, which it keeps in the
|
|
2
2
|
// server's database through a login of its own. It answers only on a Unix
|
|
3
3
|
// socket, which it makes 0660: run it as its own user, sharing a group with
|
|
4
|
-
// the server's, and nothing that faces the network can read the
|
|
4
|
+
// the server's, and nothing that faces the network can read the vault key.
|
|
5
5
|
// Settings come from vault.env.
|
|
6
6
|
import { serveVault } from '@coffre/vault/node';
|
|
7
7
|
|
|
@@ -14,13 +14,13 @@ function env(name: string): string {
|
|
|
14
14
|
const vault = await serveVault({
|
|
15
15
|
socket: env('VAULT_SOCKET'),
|
|
16
16
|
database: env('DATABASE_URL'),
|
|
17
|
-
kek: { id: env('
|
|
18
|
-
// After a rotation, the
|
|
19
|
-
// open and the records signed under them before it still verify:
|
|
20
|
-
// previousKeks: [{ id: '
|
|
17
|
+
kek: { id: env('VAULT_KEY_ID'), key: env('VAULT_KEY') },
|
|
18
|
+
// After a rotation, the vault keys before it, so the data keys they wrapped
|
|
19
|
+
// still open and the records signed under them before it still verify:
|
|
20
|
+
// previousKeks: [{ id: 'vault-2026-04-01-k7q2xm', key: env('OLD_VAULT_KEY') }],
|
|
21
21
|
rootAdmins: env('ROOT_ADMINS').split(',').map((email) => email.trim()),
|
|
22
|
-
// The vault derives its signing key from the
|
|
23
|
-
// holds, such as awsKms(…), it needs one of its own: signingKey: env('SIGNING_KEY').
|
|
22
|
+
// The vault derives its signing key from the vault key. With a key a key
|
|
23
|
+
// service holds, such as awsKms(…), it needs one of its own: signingKey: env('SIGNING_KEY').
|
|
24
24
|
});
|
|
25
25
|
console.log(`the vault is listening on ${vault.socket}`);
|
|
26
26
|
|
|
@@ -5,11 +5,12 @@ VAULT_SOCKET=vault.sock
|
|
|
5
5
|
# From `coffre setup`: the server's database, as the vault's own login.
|
|
6
6
|
DATABASE_URL=postgres://coffre_vault_runtime:CHANGE_ME@127.0.0.1:5432/coffre
|
|
7
7
|
|
|
8
|
-
# From `coffre setup`, saved in your password manager first: the
|
|
9
|
-
#
|
|
10
|
-
# The vault derives the key it signs its
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
# From `coffre setup`, saved in your password manager first: the vault key,
|
|
9
|
+
# which decrypts every value, and its ID, which is not secret. A rotation
|
|
10
|
+
# gives the new key a new ID. The vault derives the key it signs its
|
|
11
|
+
# records with from it.
|
|
12
|
+
VAULT_KEY_ID=vault-1
|
|
13
|
+
VAULT_KEY=
|
|
13
14
|
|
|
14
15
|
# Comma-separated emails: the first people in, whom nobody can remove.
|
|
15
16
|
ROOT_ADMINS=you@example.com
|
|
@@ -17,7 +17,7 @@ Worker secrets. Everything below runs from this directory.
|
|
|
17
17
|
- `app/wrangler.jsonc`: `PUBLIC_URL`, and `GITHUB_CLIENT_ID` from a GitHub
|
|
18
18
|
OAuth app whose callback is `<PUBLIC_URL>/auth/callback/github`.
|
|
19
19
|
- `vault/wrangler.jsonc`: `ROOT_ADMINS`, the emails of the first people in,
|
|
20
|
-
and `
|
|
20
|
+
and `VAULT_KEY_ID`, which `coffre setup` gives you below.
|
|
21
21
|
|
|
22
22
|
## 2. The database and keys
|
|
23
23
|
|
|
@@ -28,48 +28,53 @@ npx @coffre/cli setup
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Run it with the CLI you ran `coffre init` with. It asks for the database
|
|
31
|
-
administrator's connection string at a hidden prompt
|
|
32
|
-
in, or set `COFFRE_SETUP_DATABASE_URL`; never pass it as an argument
|
|
31
|
+
administrator's connection string at a hidden prompt. A script can pipe it
|
|
32
|
+
in, or set `COFFRE_SETUP_DATABASE_URL`; never pass it as an argument. It
|
|
33
33
|
makes the two logins coffre runs as, `coffre_runtime` for the app and
|
|
34
|
-
`coffre_vault_runtime` for the vault, migrates the database, checks that
|
|
35
|
-
each login holds only its rights
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
`coffre_vault_runtime` for the vault, migrates the database, and checks that
|
|
35
|
+
each login holds only its rights.
|
|
36
|
+
|
|
37
|
+
Then it shows five values on a screen of their own, which leaves nothing
|
|
38
|
+
behind in your scrollback: the app key and the app's database URL, and the
|
|
39
|
+
vault ID, the vault key and the vault's database URL. Copy each into your
|
|
40
|
+
password manager with `c`, beside the GitHub client secret; `w` shows where
|
|
41
|
+
each one goes. Nothing keeps a copy. There is one key for each Worker, so
|
|
39
42
|
that the app, which faces the network, never holds what decrypts a value:
|
|
40
43
|
|
|
41
|
-
- `
|
|
42
|
-
key it signs its records with. Lose it, and every value is
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
- `VAULT_KEY`, the vault key, decrypts every value, and the vault derives
|
|
45
|
+
from it the key it signs its records with. Lose it, and every value is
|
|
46
|
+
lost.
|
|
47
|
+
- `APP_KEY`, the app key, signs the app's log entries, sessions and tokens.
|
|
48
|
+
Lose it, and everyone is signed out and the log stops verifying.
|
|
49
|
+
- `VAULT_KEY_ID`, the vault ID, names the vault key. It is not secret.
|
|
45
50
|
|
|
46
51
|
To do the same by hand, see
|
|
47
52
|
[deploy.md](https://github.com/erwinkn/coffre/blob/main/docs/deploy.md#appendix-the-database-by-hand).
|
|
48
53
|
|
|
49
54
|
## 3. Hyperdrive and secrets
|
|
50
55
|
|
|
51
|
-
Run the two `wrangler hyperdrive create` commands setup
|
|
52
|
-
per login, each `--caching-disabled`. Hyperdrive otherwise caches
|
|
53
|
-
up to a minute, and a revoked token or a signed-out session could
|
|
54
|
-
working that long. `wrangler.jsonc` cannot set it, so for a config made
|
|
56
|
+
Run the two `wrangler hyperdrive create` commands from setup's screen: one
|
|
57
|
+
config per login, each `--caching-disabled`. Hyperdrive otherwise caches
|
|
58
|
+
reads for up to a minute, and a revoked token or a signed-out session could
|
|
59
|
+
keep working that long. `wrangler.jsonc` cannot set it, so for a config made
|
|
55
60
|
another way, check `caching` in `wrangler hyperdrive get <id>`.
|
|
56
61
|
|
|
57
62
|
Put the ids they print under `hyperdrive`, the first in
|
|
58
|
-
`app/wrangler.jsonc` and the second in `vault/wrangler.jsonc`, and
|
|
59
|
-
in `vault/wrangler.jsonc`. Then set the secrets; each command
|
|
60
|
-
the saved value:
|
|
63
|
+
`app/wrangler.jsonc` and the second in `vault/wrangler.jsonc`, and
|
|
64
|
+
`VAULT_KEY_ID` in `vault/wrangler.jsonc`. Then set the secrets; each command
|
|
65
|
+
prompts for the saved value:
|
|
61
66
|
|
|
62
67
|
```sh
|
|
63
|
-
pnpm exec wrangler secret put
|
|
64
|
-
pnpm exec wrangler secret put
|
|
68
|
+
pnpm exec wrangler secret put VAULT_KEY -c vault/wrangler.jsonc
|
|
69
|
+
pnpm exec wrangler secret put APP_KEY -c app/wrangler.jsonc
|
|
65
70
|
pnpm exec wrangler secret put GITHUB_CLIENT_SECRET -c app/wrangler.jsonc
|
|
66
71
|
```
|
|
67
72
|
|
|
68
73
|
Run `pnpm migrate`, with the administrator's URL in `DATABASE_URL`, after
|
|
69
74
|
every upgrade of `@coffre/server`, before deploying it.
|
|
70
75
|
|
|
71
|
-
Keep older
|
|
72
|
-
them, and so does what the vault signed under them before it. With AWS KMS
|
|
76
|
+
Keep older vault keys after a rotation, for good: what they wrapped still
|
|
77
|
+
needs them, and so does what the vault signed under them before it. With AWS KMS
|
|
73
78
|
instead of a key of your own, the vault also needs a `SIGNING_KEY`
|
|
74
79
|
([keys](https://github.com/erwinkn/coffre/blob/main/docs/keys.md#aws-kms)).
|
|
75
80
|
|
|
@@ -12,7 +12,7 @@ type Env = {
|
|
|
12
12
|
/** Set for GitHub Enterprise Server; github.com otherwise. */
|
|
13
13
|
GITHUB_URL?: string;
|
|
14
14
|
GITHUB_API_URL?: string;
|
|
15
|
-
|
|
15
|
+
APP_KEY: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
export default coffre((env: Env) => ({
|
|
@@ -29,5 +29,5 @@ export default coffre((env: Env) => ({
|
|
|
29
29
|
}),
|
|
30
30
|
],
|
|
31
31
|
}),
|
|
32
|
-
auditChainKey: env.
|
|
32
|
+
auditChainKey: env.APP_KEY,
|
|
33
33
|
}));
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"GITHUB_CLIENT_ID": "replace-with-your-client-id"
|
|
18
18
|
},
|
|
19
19
|
"secrets": {
|
|
20
|
-
//
|
|
21
|
-
"required": ["GITHUB_CLIENT_SECRET", "
|
|
20
|
+
// APP_KEY is from `coffre setup`; the app never gets the vault's keys.
|
|
21
|
+
"required": ["GITHUB_CLIENT_SECRET", "APP_KEY"]
|
|
22
22
|
},
|
|
23
|
-
// `wrangler hyperdrive create coffre --caching-disabled
|
|
24
|
-
// prints the id. Caching must stay off, or a revoked session keeps working
|
|
23
|
+
// `wrangler hyperdrive create coffre --caching-disabled`, as `coffre setup`
|
|
24
|
+
// shows it, prints the id. Caching must stay off, or a revoked session keeps working
|
|
25
25
|
// for about a minute, and this file cannot set it: see README.md. Locally,
|
|
26
26
|
// CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE stands in for
|
|
27
27
|
// the config.
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"conformance": "coffre-conformance workers"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@coffre/server": "0.1.
|
|
18
|
-
"@coffre/ui": "0.1.
|
|
19
|
-
"@coffre/vault": "0.1.
|
|
17
|
+
"@coffre/server": "0.1.3",
|
|
18
|
+
"@coffre/ui": "0.1.3",
|
|
19
|
+
"@coffre/vault": "0.1.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@cloudflare/workers-types": "5.20260920.1",
|
|
23
|
-
"@coffre/conformance": "0.1.
|
|
23
|
+
"@coffre/conformance": "0.1.3",
|
|
24
24
|
"typescript": "5.9.3",
|
|
25
25
|
"wrangler": "4.118.0"
|
|
26
26
|
}
|
|
@@ -5,18 +5,18 @@ import { postgres, vault } from '@coffre/vault/cloudflare';
|
|
|
5
5
|
|
|
6
6
|
type Env = {
|
|
7
7
|
VAULT_HYPERDRIVE: Hyperdrive;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
VAULT_KEY_ID: string;
|
|
9
|
+
VAULT_KEY: string;
|
|
10
10
|
ROOT_ADMINS: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export default vault((env: Env) => ({
|
|
14
14
|
database: postgres(env.VAULT_HYPERDRIVE),
|
|
15
|
-
kek: { id: env.
|
|
16
|
-
// After a rotation, the
|
|
17
|
-
// open and the records signed under them before it still verify:
|
|
18
|
-
// previousKeks: [{ id: '
|
|
15
|
+
kek: { id: env.VAULT_KEY_ID, key: env.VAULT_KEY },
|
|
16
|
+
// After a rotation, the vault keys before it, so the data keys they wrapped
|
|
17
|
+
// still open and the records signed under them before it still verify:
|
|
18
|
+
// previousKeks: [{ id: 'vault-2026-04-01-k7q2xm', key: env.OLD_VAULT_KEY }],
|
|
19
19
|
rootAdmins: env.ROOT_ADMINS.split(',').map((email) => email.trim()),
|
|
20
|
-
// The vault derives its signing key from the
|
|
21
|
-
// holds, such as awsKms(…), it needs one of its own: signingKey: env.SIGNING_KEY.
|
|
20
|
+
// The vault derives its signing key from the vault key. With a key a key
|
|
21
|
+
// service holds, such as awsKms(…), it needs one of its own: signingKey: env.SIGNING_KEY.
|
|
22
22
|
}));
|
|
@@ -11,19 +11,20 @@
|
|
|
11
11
|
"workers_dev": false,
|
|
12
12
|
"observability": { "enabled": true },
|
|
13
13
|
"vars": {
|
|
14
|
-
// Names the
|
|
15
|
-
|
|
14
|
+
// Names the vault key below, and is not secret: from `coffre setup`. A
|
|
15
|
+
// rotation gives the new key a new ID.
|
|
16
|
+
"VAULT_KEY_ID": "vault-1",
|
|
16
17
|
// Comma-separated emails: the first people in, whom nobody can remove.
|
|
17
18
|
"ROOT_ADMINS": "you@example.com"
|
|
18
19
|
},
|
|
19
20
|
"secrets": {
|
|
20
|
-
//
|
|
21
|
-
// it signs its records with from it.
|
|
22
|
-
"required": ["
|
|
21
|
+
// The vault key, from `coffre setup`: wrangler secret put VAULT_KEY. The
|
|
22
|
+
// vault derives the key it signs its records with from it.
|
|
23
|
+
"required": ["VAULT_KEY"]
|
|
23
24
|
},
|
|
24
25
|
// The app's database, as the vault's own login: `wrangler hyperdrive
|
|
25
|
-
// create coffre-vault --caching-disabled
|
|
26
|
-
//
|
|
26
|
+
// create coffre-vault --caching-disabled`, as `coffre setup` shows it,
|
|
27
|
+
// prints the id. Its own config, not the app's: the vault writes
|
|
27
28
|
// members and grants, which the app's login may only read. Locally,
|
|
28
29
|
// CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_VAULT_HYPERDRIVE stands in.
|
|
29
30
|
"hyperdrive": [{ "binding": "VAULT_HYPERDRIVE", "id": "replace-with-your-vault-hyperdrive-id" }]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffre/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The coffre command line: secrets in your shell, and `coffre init` for a new deployment.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"pg": "8.16.3",
|
|
28
28
|
"tsdown": "0.23.0",
|
|
29
29
|
"typescript": "5.9.3",
|
|
30
|
-
"@coffre/client": "0.1.
|
|
31
|
-
"@coffre/core": "0.1.
|
|
32
|
-
"@coffre/db": "0.1.
|
|
30
|
+
"@coffre/client": "0.1.3",
|
|
31
|
+
"@coffre/core": "0.1.3",
|
|
32
|
+
"@coffre/db": "0.1.3"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsdown && node scripts/copy-templates.ts && node scripts/copy-migrations.ts",
|
package/dist/keys-D9CG_wYV.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
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 };
|