axvault 1.13.0 → 1.14.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.
Files changed (61) hide show
  1. package/README.md +107 -14
  2. package/dist/cli.d.ts +2 -2
  3. package/dist/cli.js +21 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/init.d.ts +14 -0
  6. package/dist/commands/init.d.ts.map +1 -0
  7. package/dist/commands/init.js +88 -0
  8. package/dist/commands/init.js.map +1 -0
  9. package/dist/commands/serve.d.ts +1 -2
  10. package/dist/commands/serve.d.ts.map +1 -1
  11. package/dist/commands/serve.js +13 -20
  12. package/dist/commands/serve.js.map +1 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +1 -1
  15. package/dist/config.js.map +1 -1
  16. package/dist/db/bootstrap-api-key.d.ts +4 -4
  17. package/dist/db/bootstrap-api-key.d.ts.map +1 -1
  18. package/dist/db/bootstrap-api-key.js +39 -6
  19. package/dist/db/bootstrap-api-key.js.map +1 -1
  20. package/dist/db/repositories/api-key-utilities.d.ts +3 -3
  21. package/dist/db/repositories/api-key-utilities.d.ts.map +1 -1
  22. package/dist/db/repositories/api-key-utilities.js +12 -1
  23. package/dist/db/repositories/api-key-utilities.js.map +1 -1
  24. package/dist/db/repositories/api-keys.d.ts +4 -2
  25. package/dist/db/repositories/api-keys.d.ts.map +1 -1
  26. package/dist/db/repositories/api-keys.js +7 -2
  27. package/dist/db/repositories/api-keys.js.map +1 -1
  28. package/dist/db/repositories/create-api-key.d.ts +1 -0
  29. package/dist/db/repositories/create-api-key.d.ts.map +1 -1
  30. package/dist/db/repositories/create-api-key.js +2 -2
  31. package/dist/db/repositories/create-api-key.js.map +1 -1
  32. package/dist/db/verify-database-initialization.d.ts +4 -0
  33. package/dist/db/verify-database-initialization.d.ts.map +1 -0
  34. package/dist/db/verify-database-initialization.js +66 -0
  35. package/dist/db/verify-database-initialization.js.map +1 -0
  36. package/dist/index.d.ts +4 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +4 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/resolve-database-url.d.ts +4 -0
  41. package/dist/resolve-database-url.d.ts.map +1 -0
  42. package/dist/resolve-database-url.js +11 -0
  43. package/dist/resolve-database-url.js.map +1 -0
  44. package/dist/resolve-secret-source.d.ts +11 -0
  45. package/dist/resolve-secret-source.d.ts.map +1 -0
  46. package/dist/resolve-secret-source.js +36 -0
  47. package/dist/resolve-secret-source.js.map +1 -0
  48. package/dist/server/plugins/auth.d.ts.map +1 -1
  49. package/dist/server/plugins/auth.js +1 -3
  50. package/dist/server/plugins/auth.js.map +1 -1
  51. package/dist/server/routes/health.d.ts.map +1 -1
  52. package/dist/server/routes/health.js +7 -1
  53. package/dist/server/routes/health.js.map +1 -1
  54. package/dist/server/server.d.ts.map +1 -1
  55. package/dist/server/server.js +8 -7
  56. package/dist/server/server.js.map +1 -1
  57. package/package.json +18 -18
  58. package/dist/server/send-sensible-error.d.ts +0 -7
  59. package/dist/server/send-sensible-error.d.ts.map +0 -1
  60. package/dist/server/send-sensible-error.js +0 -40
  61. package/dist/server/send-sensible-error.js.map +0 -1
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Remote credential storage server for a╳kit.
4
4
 
5
+ Uses the standard a╳kit stateful server lifecycle: `init` for migrations and
6
+ admin bootstrap, `serve` for the long-running server.
7
+
5
8
  ## Prerequisites
6
9
 
7
10
  - Node.js 22.19+
@@ -29,26 +32,39 @@ set -a
29
32
  . ./.env
30
33
  set +a
31
34
 
32
- # Start server (runs migrations and creates bootstrap admin key on first startup)
35
+ # Initialize database and create the first admin key
36
+ npx -y axvault init --create-admin-key
37
+
38
+ # Or, instead of generating one, provision a deterministic admin key for deployment
39
+ export AXVAULT_ADMIN_KEY='axv_sk_0123456789abcdef0123456789abcdef'
40
+ npx -y axvault init --create-admin-key \
41
+ --admin-key-env AXVAULT_ADMIN_KEY
42
+
43
+ # Start server
33
44
  npx -y axvault serve
34
45
  ```
35
46
 
36
- On first startup, axvault runs database migrations and creates a bootstrap admin API key with full access. The secret is printed to stderr — **save it immediately**, it cannot be retrieved later.
47
+ Generated and provisioned admin keys use the same format: `axv_sk_` followed by
48
+ 32 lowercase hexadecimal characters.
37
49
 
38
50
  Keep the `.env` file and reuse the same encryption key between restarts to avoid losing access to existing credentials.
39
51
 
40
52
  ## Architecture
41
53
 
42
- axvault is a server-only tool. The CLI has a single command (`serve`) that starts the HTTP server. All key and credential management is done through the HTTP API.
54
+ axvault is a server-only tool with an explicit `init` / `serve` lifecycle.
55
+ Initialization and long-running serving are separate operations.
43
56
 
44
- On startup, the server:
57
+ The CLI provides:
45
58
 
46
- 1. Runs database migrations (idempotent, so they are safe to re-run on every startup)
47
- 2. Creates a bootstrap admin API key if no keys exist (serialized with an advisory lock, prints secret to stderr)
48
- 3. Starts listening for HTTP requests
59
+ 1. `axvault init` for database migrations and first admin key bootstrap
60
+ 2. `axvault serve` for starting the HTTP server
61
+
62
+ `axvault init --create-admin-key` is idempotent with respect to bootstrap: it
63
+ uses an advisory lock and creates the first bootstrap-capable key only when no
64
+ existing key has `grantAccess: ["*"]`.
49
65
 
50
66
  Migration files must remain replay-safe because axvault re-runs them on every
51
- startup instead of tracking applied versions. Use guarded SQL such as
67
+ `init` instead of tracking applied versions. Use guarded SQL such as
52
68
  `CREATE ... IF NOT EXISTS` or `ALTER ... ADD COLUMN IF NOT EXISTS`. If a future
53
69
  schema change cannot be written safely that way, switch back to tracked
54
70
  migrations rather than adding a one-shot file to this runner.
@@ -62,9 +78,9 @@ Add to your `CLAUDE.md` or `AGENTS.md`:
62
78
 
63
79
  Run `npx -y axvault --help` to learn available options.
64
80
 
65
- Use `axvault serve` to start the credential vault server. All key and credential
66
- management is done via the HTTP API. On first startup, a bootstrap admin API key
67
- is created and printed to stderr.
81
+ Use `axvault init` for migrations and admin bootstrap, then `axvault serve` to
82
+ start the credential vault server. Ongoing key and credential management is done
83
+ via the HTTP API.
68
84
  ```
69
85
 
70
86
  ## Configuration
@@ -83,9 +99,14 @@ is created and printed to stderr.
83
99
 
84
100
  ### CLI Flags
85
101
 
86
- The `serve` command accepts flags that override environment variables:
102
+ The `init` and `serve` commands accept `--database-url`. The `serve` command
103
+ also accepts runtime flags that override environment variables:
87
104
 
88
105
  ```bash
106
+ npx -y axvault init \
107
+ --database-url postgresql://localhost:5432/axvault \
108
+ --create-admin-key
109
+
89
110
  npx -y axvault serve \
90
111
  --port 8080 \
91
112
  --host 0.0.0.0 \
@@ -107,7 +128,13 @@ API keys control access to the credential API. Each key has configurable permiss
107
128
 
108
129
  ### Bootstrap Key
109
130
 
110
- On first startup (when no keys exist), axvault creates a "Bootstrap Admin" key with full access (`*` for read, write, and grant). The secret is printed to stderr.
131
+ `axvault init --create-admin-key` creates a "Bootstrap Admin" key with full
132
+ access (`*` for read, write, and grant) only when no full-admin key exists yet.
133
+ Omit `--admin-key`, `--admin-key-env`, and `--admin-key-file` to generate a
134
+ one-time secret. For production, prefer `--admin-key-env` or
135
+ `--admin-key-file` over raw `--admin-key`: command-line arguments are visible
136
+ in process listings. Generated secrets are printed once to stdout; provided
137
+ secrets are used without being echoed back.
111
138
 
112
139
  ### Managing Keys via API
113
140
 
@@ -183,19 +210,84 @@ podman run -d \
183
210
 
184
211
  **Note:** `AXVAULT_DATABASE_URL` must point to an accessible PostgreSQL instance. The Containerfile default (`postgresql://localhost:5432/axvault`) refers to the container itself, so standalone `docker run` users must provide this variable. For a batteries-included setup, use `docker-compose.yml` which includes a PostgreSQL service.
185
212
 
186
- The bootstrap admin key is created on first startup and printed to the container logs. Retrieve it with `docker logs axvault` or `podman logs axvault`.
213
+ Initialize the database and first admin key with a one-shot container before
214
+ starting the long-running service:
215
+
216
+ ```bash
217
+ # Docker
218
+ docker run --rm \
219
+ -u 1000:1000 \
220
+ -e AXVAULT_ENCRYPTION_KEY="your-secret-key-minimum-32-chars!" \
221
+ -e AXVAULT_DATABASE_URL="postgresql://user:pass@db-host:5432/axvault" \
222
+ registry.j4k.dev/axvault:latest init --create-admin-key
223
+
224
+ # Podman
225
+ podman run --rm \
226
+ --user 1000:1000 \
227
+ -e AXVAULT_ENCRYPTION_KEY="your-secret-key-minimum-32-chars!" \
228
+ -e AXVAULT_DATABASE_URL="postgresql://user:pass@db-host:5432/axvault" \
229
+ registry.j4k.dev/axvault:latest init --create-admin-key
230
+ ```
231
+
232
+ When using `docker-compose.yml` or `podman-compose`, initialize against the
233
+ bundled PostgreSQL service, then start the long-running server:
234
+
235
+ ```bash
236
+ # Docker Compose
237
+ docker compose up -d db
238
+ docker compose run --rm axvault init --create-admin-key
239
+ docker compose up -d axvault
240
+
241
+ # Podman Compose
242
+ podman-compose up -d db
243
+ podman-compose run --rm axvault init --create-admin-key
244
+ podman-compose up -d axvault
245
+ ```
246
+
247
+ For production, prefer provisioning a deterministic admin key rather than
248
+ capturing a one-time generated secret from logs:
249
+
250
+ ```bash
251
+ docker compose run --rm \
252
+ -e AXVAULT_ADMIN_KEY='axv_sk_0123456789abcdef0123456789abcdef' \
253
+ axvault init --create-admin-key \
254
+ --admin-key-env AXVAULT_ADMIN_KEY
255
+ ```
187
256
 
188
257
  #### Quadlet (systemd)
189
258
 
190
259
  This example references `axvault-db.service`, which is a PostgreSQL container you must provide separately as a companion Quadlet (`axvault-db.container`). Alternatively, point `AXVAULT_DATABASE_URL` at an existing PostgreSQL instance and remove the `Requires`/`After` lines.
191
260
 
261
+ Create `/etc/containers/systemd/axvault-init.container`:
262
+
263
+ ```ini
264
+ [Unit]
265
+ Description=axvault bootstrap initialization
266
+ Requires=axvault-db.service
267
+ After=axvault-db.service
268
+
269
+ [Container]
270
+ Image=registry.j4k.dev/axvault:latest
271
+ User=1000
272
+ Group=1000
273
+ Environment=AXVAULT_ENCRYPTION_KEY=your-secret-key-minimum-32-chars!
274
+ Environment=AXVAULT_DATABASE_URL=postgresql://axvault:axvault@axvault-db:5432/axvault
275
+ Exec=init --create-admin-key
276
+
277
+ [Service]
278
+ Type=oneshot
279
+ RemainAfterExit=yes
280
+ ```
281
+
192
282
  Create `/etc/containers/systemd/axvault.container`:
193
283
 
194
284
  ```ini
195
285
  [Unit]
196
286
  Description=axvault credential server
197
287
  Requires=axvault-db.service
288
+ Requires=axvault-init.service
198
289
  After=axvault-db.service
290
+ After=axvault-init.service
199
291
 
200
292
  [Container]
201
293
  Image=registry.j4k.dev/axvault:latest
@@ -216,6 +308,7 @@ Then reload and start:
216
308
 
217
309
  ```bash
218
310
  sudo systemctl daemon-reload
311
+ sudo systemctl start axvault-init
219
312
  sudo systemctl start axvault
220
313
  ```
221
314
 
package/dist/cli.d.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * axvault - Remote credential storage server for axkit.
4
4
  *
5
- * Starts the vault server. All key and credential management is done
6
- * via the HTTP API once the server is running.
5
+ * Initializes and starts the vault server. Key and credential management is
6
+ * done via explicit bootstrap plus the HTTP API once the server is running.
7
7
  */
8
8
  export {};
9
9
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js CHANGED
@@ -2,11 +2,12 @@
2
2
  /**
3
3
  * axvault - Remote credential storage server for axkit.
4
4
  *
5
- * Starts the vault server. All key and credential management is done
6
- * via the HTTP API once the server is running.
5
+ * Initializes and starts the vault server. Key and credential management is
6
+ * done via explicit bootstrap plus the HTTP API once the server is running.
7
7
  */
8
8
  import { Command } from "@commander-js/extra-typings";
9
9
  import packageJson from "../package.json" with { type: "json" };
10
+ import { handleInit } from "./commands/init.js";
10
11
  import { handleServe } from "./commands/serve.js";
11
12
  const program = new Command()
12
13
  .name(packageJson.name)
@@ -17,7 +18,14 @@ const program = new Command()
17
18
  .helpCommand(false)
18
19
  .addHelpText("after", String.raw `
19
20
  Examples:
20
- # Start server (runs migrations and creates bootstrap key on first run)
21
+ # Initialize database and create the first admin key
22
+ axvault init --create-admin-key
23
+
24
+ # Initialize database and provision a deterministic admin key from an env var
25
+ AXVAULT_ADMIN_KEY=axv_sk_0123456789abcdef0123456789abcdef \
26
+ axvault init --create-admin-key --admin-key-env AXVAULT_ADMIN_KEY
27
+
28
+ # Start server
21
29
  axvault serve
22
30
 
23
31
  # Start server on custom port
@@ -25,6 +33,16 @@ Examples:
25
33
 
26
34
  # Start with debug logging
27
35
  axvault serve --log-level debug`);
36
+ program
37
+ .command("init")
38
+ .description("Initialize database")
39
+ .option("--database-url <url>", "PostgreSQL connection URL")
40
+ .option("--create-admin-key", "Create an admin key with full access")
41
+ .option("--admin-key <key>", "Provision a specific admin API key (requires --create-admin-key; exposes the secret in the process list)")
42
+ .option("--admin-key-env <name>", "Read the admin API key from an environment variable (requires --create-admin-key)")
43
+ .option("--admin-key-file <path>", "Read the admin API key from a file (requires --create-admin-key)")
44
+ .option("-v, --verbose", "Enable verbose output")
45
+ .action(handleInit);
28
46
  program
29
47
  .command("serve")
30
48
  .description("Start the vault server")
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAEtD,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;KACtB,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC;KACpC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC;KAC7C,kBAAkB,CAAC,yCAAyC,CAAC;KAC7D,wBAAwB,EAAE;KAC1B,WAAW,CAAC,KAAK,CAAC;KAClB,WAAW,CACV,OAAO,EACP,MAAM,CAAC,GAAG,CAAA;;;;;;;;;kCASoB,CAC/B,CAAC;AAEJ,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wBAAwB,CAAC;KACrC,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;KAChD,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;KAC9C,MAAM,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;KAC3D,MAAM,CACL,+BAA+B,EAC/B,sEAAsE,CACvE;KACA,MAAM,CACL,wBAAwB,EACxB,gDAAgD,CACjD;KACA,MAAM,CACL,qBAAqB,EACrB,4DAA4D,CAC7D;KACA,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC;KAChD,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAEtD,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;KACtB,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC;KACpC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC;KAC7C,kBAAkB,CAAC,yCAAyC,CAAC;KAC7D,wBAAwB,EAAE;KAC1B,WAAW,CAAC,KAAK,CAAC;KAClB,WAAW,CACV,OAAO,EACP,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;kCAgBoB,CAC/B,CAAC;AAEJ,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,qBAAqB,CAAC;KAClC,MAAM,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;KAC3D,MAAM,CAAC,oBAAoB,EAAE,sCAAsC,CAAC;KACpE,MAAM,CACL,mBAAmB,EACnB,0GAA0G,CAC3G;KACA,MAAM,CACL,wBAAwB,EACxB,mFAAmF,CACpF;KACA,MAAM,CACL,yBAAyB,EACzB,kEAAkE,CACnE;KACA,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC;KAChD,MAAM,CAAC,UAAU,CAAC,CAAC;AAEtB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wBAAwB,CAAC;KACrC,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;KAChD,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;KAC9C,MAAM,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;KAC3D,MAAM,CACL,+BAA+B,EAC/B,sEAAsE,CACvE;KACA,MAAM,CACL,wBAAwB,EACxB,gDAAgD,CACjD;KACA,MAAM,CACL,qBAAqB,EACrB,4DAA4D,CAC7D;KACA,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC;KAChD,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Initialize database command handler.
3
+ */
4
+ interface InitOptions {
5
+ databaseUrl?: string;
6
+ createAdminKey?: boolean;
7
+ adminKey?: string;
8
+ adminKeyEnv?: string;
9
+ adminKeyFile?: string;
10
+ verbose?: boolean;
11
+ }
12
+ export declare function handleInit(options: InitOptions): Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgBH,UAAU,WAAW;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAmFpE"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Initialize database command handler.
3
+ */
4
+ import { fileURLToPath } from "node:url";
5
+ import { formatErrorMessage } from "../format-error-message.js";
6
+ import { bootstrapApiKey } from "../db/bootstrap-api-key.js";
7
+ import { closePool, createPool } from "../db/create-pool.js";
8
+ import { resolveApiKeySecret } from "../db/repositories/api-keys.js";
9
+ import { runMigrations } from "../db/run-migrations.js";
10
+ import { resolveSecretSource } from "../resolve-secret-source.js";
11
+ import { resolveDatabaseUrl } from "../resolve-database-url.js";
12
+ const migrationsDirectory = fileURLToPath(new URL("../db/migrations", import.meta.url));
13
+ export async function handleInit(options) {
14
+ const verbose = options.verbose === true;
15
+ if ((options.adminKey || options.adminKeyEnv || options.adminKeyFile) &&
16
+ options.createAdminKey !== true) {
17
+ console.error("Invalid option: --admin-key, --admin-key-env, and --admin-key-file require --create-admin-key.");
18
+ process.exitCode = 2;
19
+ return;
20
+ }
21
+ let adminKey;
22
+ try {
23
+ adminKey = await resolveSecretSource({
24
+ directValue: options.adminKey,
25
+ envName: options.adminKeyEnv,
26
+ filePath: options.adminKeyFile,
27
+ directOption: "--admin-key",
28
+ envOption: "--admin-key-env",
29
+ fileOption: "--admin-key-file",
30
+ });
31
+ adminKey =
32
+ adminKey === undefined ? undefined : resolveApiKeySecret(adminKey);
33
+ }
34
+ catch (error) {
35
+ const message = formatErrorMessage(error);
36
+ console.error(message);
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+ let pool;
41
+ try {
42
+ const databaseUrl = resolveDatabaseUrl(options.databaseUrl);
43
+ const initializedPool = createPool(databaseUrl);
44
+ pool = initializedPool;
45
+ await runMigrations(initializedPool, migrationsDirectory);
46
+ if (verbose) {
47
+ try {
48
+ const sanitizedUrl = new URL(databaseUrl);
49
+ if (sanitizedUrl.password) {
50
+ sanitizedUrl.password = "***";
51
+ }
52
+ console.error(`Database initialized at: ${sanitizedUrl.toString()}`);
53
+ }
54
+ catch {
55
+ console.error("Database initialized.");
56
+ }
57
+ }
58
+ if (options.createAdminKey === true) {
59
+ const key = await bootstrapApiKey(initializedPool, {
60
+ key: adminKey,
61
+ });
62
+ if (!key) {
63
+ console.error("Admin API key already exists; bootstrap skipped.");
64
+ return;
65
+ }
66
+ console.error("");
67
+ console.error(`Created admin API key: ${key.name}`);
68
+ console.error(`ID: ${key.id}`);
69
+ console.error("");
70
+ if (adminKey === undefined) {
71
+ console.error("Save this key securely - it cannot be retrieved later.");
72
+ console.error("");
73
+ console.log(key.key);
74
+ }
75
+ }
76
+ }
77
+ catch (error) {
78
+ const message = formatErrorMessage(error);
79
+ console.error(`Failed to initialize database: ${message}`);
80
+ process.exitCode = 1;
81
+ }
82
+ finally {
83
+ if (pool) {
84
+ await closePool(pool);
85
+ }
86
+ }
87
+ }
88
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,7 @@
2
2
  * Start server command handler.
3
3
  *
4
4
  * Builds the Fastify app, registers plugins in dependency order
5
- * (database → auth → routes), runs migrations, creates a bootstrap
6
- * API key on first startup, and starts listening.
5
+ * (database → auth → routes), verifies initialization, and starts listening.
7
6
  */
8
7
  interface ServeOptions {
9
8
  port?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkBH,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAMD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2GtE"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGtE"}
@@ -2,15 +2,13 @@
2
2
  * Start server command handler.
3
3
  *
4
4
  * Builds the Fastify app, registers plugins in dependency order
5
- * (database → auth → routes), runs migrations, creates a bootstrap
6
- * API key on first startup, and starts listening.
5
+ * (database → auth → routes), verifies initialization, and starts listening.
7
6
  */
8
- import { fileURLToPath } from "node:url";
9
7
  import closeWithGrace from "close-with-grace";
10
8
  import { formatErrorMessage } from "../format-error-message.js";
11
9
  import { resolveBuildAppConfig } from "../config.js";
12
- import { bootstrapApiKey } from "../db/bootstrap-api-key.js";
13
- import { runMigrations } from "../db/run-migrations.js";
10
+ import { countApiKeys } from "../db/repositories/api-keys.js";
11
+ import { verifyDatabaseInitialization } from "../db/verify-database-initialization.js";
14
12
  import configPlugin from "../server/plugins/config.js";
15
13
  import databasePlugin from "../server/plugins/database.js";
16
14
  import authPlugin from "../server/plugins/auth.js";
@@ -18,7 +16,6 @@ import credentialRoutes from "../server/routes/credentials.js";
18
16
  import healthRoutes from "../server/routes/health.js";
19
17
  import keyRoutes from "../server/routes/keys.js";
20
18
  import { buildApp } from "../server/server.js";
21
- const migrationsDirectory = fileURLToPath(new URL("../db/migrations", import.meta.url));
22
19
  export async function handleServe(options) {
23
20
  let buildConfig;
24
21
  try {
@@ -66,32 +63,28 @@ export async function handleServe(options) {
66
63
  // eslint-disable-next-line unicorn/no-process-exit -- CLI config/startup error
67
64
  process.exit(1);
68
65
  }
69
- // Run migrations using the pool created by @fastify/postgres
66
+ // `serve` assumes `init` already prepared the database schema.
70
67
  try {
71
- await runMigrations(app.pg.pool, migrationsDirectory);
68
+ await verifyDatabaseInitialization(app.pg);
72
69
  }
73
70
  catch (error) {
74
71
  const message = formatErrorMessage(error);
75
- app.log.error(`Failed to initialize database: ${message}`);
72
+ app.log.error(`Failed to verify database initialization: ${message}`);
73
+ console.error("Database is not initialized or the schema is outdated. Run `axvault init` first.");
76
74
  await app.close();
77
75
  // eslint-disable-next-line unicorn/no-process-exit -- CLI startup failure
78
76
  process.exit(1);
79
77
  }
80
- // Create bootstrap admin key on first startup (no keys exist)
78
+ let apiKeyCount;
81
79
  try {
82
- const bootstrapKey = await bootstrapApiKey(app.pg);
83
- if (bootstrapKey) {
84
- app.log.info({ keyId: bootstrapKey.id }, "Created bootstrap admin API key — save this secret, it cannot be retrieved later:");
85
- // Print the secret to stderr so it's visible but not mixed with structured logs
86
- console.error(`\n ${bootstrapKey.key}\n`);
87
- }
80
+ apiKeyCount = await countApiKeys(app.pg);
88
81
  }
89
82
  catch (error) {
90
83
  const message = formatErrorMessage(error);
91
- app.log.error(`Failed to create bootstrap key: ${message}`);
92
- await app.close();
93
- // eslint-disable-next-line unicorn/no-process-exit -- CLI startup failure
94
- process.exit(1);
84
+ app.log.warn(`Failed to check API key count: ${message}`);
85
+ }
86
+ if (apiKeyCount === 0) {
87
+ app.log.warn("No API keys configured. Run `axvault init --create-admin-key` before relying on the server.");
95
88
  }
96
89
  // Graceful shutdown
97
90
  closeWithGrace({ delay: 5000, logger: false }, async ({ signal, err }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,MAAM,EAAE,YAAY,CAAC;KACtB;CACF;AAED,KAAK,QAAQ,GACT,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,CAAC;AAEb,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB,EAAE,MAAM,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,cAAc;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAuBD,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Bd,CAAC;AAEX,iBAAS,gBAAgB,CACvB,SAAS,GAAE,eAAoB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB;AAED,iBAAS,qBAAqB,CAC5B,SAAS,GAAE,IAAI,CAAC,eAAe,EAAE,UAAU,CAAM,GAChD,cAAc,CAahB;AAED,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,MAAM,EAAE,YAAY,CAAC;KACtB;CACF;AAED,KAAK,QAAQ,GACT,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,CAAC;AAEb,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB,EAAE,MAAM,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,cAAc;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAsBD,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Bd,CAAC;AAEX,iBAAS,gBAAgB,CACvB,SAAS,GAAE,eAAoB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB;AAED,iBAAS,qBAAqB,CAC5B,SAAS,GAAE,IAAI,CAAC,eAAe,EAAE,UAAU,CAAM,GAChD,cAAc,CAahB;AAED,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC"}
package/dist/config.js CHANGED
@@ -5,9 +5,9 @@
5
5
  * `fastify.config`. Logger level is resolved separately because the logger is
6
6
  * configured before plugins are registered.
7
7
  */
8
+ import { DEFAULT_DATABASE_URL } from "./resolve-database-url.js";
8
9
  const DEFAULT_PORT = 3847;
9
10
  const DEFAULT_HOST = "127.0.0.1";
10
- const DEFAULT_DATABASE_URL = "postgresql://localhost:5432/axvault";
11
11
  const DEFAULT_REFRESH_THRESHOLD_SECONDS = 3600; // 1 hour
12
12
  const DEFAULT_REFRESH_TIMEOUT_MS = 30_000; // 30 seconds
13
13
  const DEFAULT_LOG_LEVEL = "info";
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyCH,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,CAAC;AACjC,MAAM,oBAAoB,GAAG,qCAAqC,CAAC;AACnE,MAAM,iCAAiC,GAAG,IAAI,CAAC,CAAC,SAAS;AACzD,MAAM,0BAA0B,GAAG,MAAM,CAAC,CAAC,aAAa;AACxD,MAAM,iBAAiB,GAAa,MAAM,CAAC;AAE3C,MAAM,gBAAgB,GAAG;IACvB,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;CACA,CAAC;AAEX,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAQ,gBAAsC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,kBAAkB,GAAG;IACzB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,eAAe,CAAC;IAC3B,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;QAC/C,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;QAC/C,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,oBAAoB,EAAE;QAC9D,uBAAuB,EAAE;YACvB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,iCAAiC;SAC3C;QACD,gBAAgB,EAAE;YAChB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,0BAA0B;SACpC;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,GAAG,gBAAgB,CAAC;YAC3B,OAAO,EAAE,iBAAiB;SAC3B;QACD,aAAa,EAAE;YACb,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,EAAE;SACd;KACF;CACO,CAAC;AAEX,SAAS,gBAAgB,CACvB,YAA6B,EAAE;IAE/B,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAChD,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAChD,WAAW,EAAE,SAAS,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB;QACtE,uBAAuB,EACrB,SAAS,CAAC,uBAAuB;YACjC,OAAO,CAAC,GAAG,CAAC,yBAAyB;QACvC,gBAAgB,EACd,SAAS,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B;QACtE,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7D,aAAa,EACX,SAAS,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB;KAChE,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,YAA+C,EAAE;IAEjD,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACxE,MAAM,QAAQ,GAAG,WAAW,IAAI,iBAAiB,CAAC;IAElD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ,sBAAsB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAyCjE,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,CAAC;AACjC,MAAM,iCAAiC,GAAG,IAAI,CAAC,CAAC,SAAS;AACzD,MAAM,0BAA0B,GAAG,MAAM,CAAC,CAAC,aAAa;AACxD,MAAM,iBAAiB,GAAa,MAAM,CAAC;AAE3C,MAAM,gBAAgB,GAAG;IACvB,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;CACA,CAAC;AAEX,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAQ,gBAAsC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,kBAAkB,GAAG;IACzB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,eAAe,CAAC;IAC3B,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;QAC/C,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;QAC/C,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,oBAAoB,EAAE;QAC9D,uBAAuB,EAAE;YACvB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,iCAAiC;SAC3C;QACD,gBAAgB,EAAE;YAChB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,0BAA0B;SACpC;QACD,QAAQ,EAAE;YACR,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,GAAG,gBAAgB,CAAC;YAC3B,OAAO,EAAE,iBAAiB;SAC3B;QACD,aAAa,EAAE;YACb,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,EAAE;SACd;KACF;CACO,CAAC;AAEX,SAAS,gBAAgB,CACvB,YAA6B,EAAE;IAE/B,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAChD,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAChD,WAAW,EAAE,SAAS,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB;QACtE,uBAAuB,EACrB,SAAS,CAAC,uBAAuB;YACjC,OAAO,CAAC,GAAG,CAAC,yBAAyB;QACvC,gBAAgB,EACd,SAAS,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B;QACtE,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7D,aAAa,EACX,SAAS,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB;KAChE,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,YAA+C,EAAE;IAEjD,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACxE,MAAM,QAAQ,GAAG,WAAW,IAAI,iBAAiB,CAAC;IAElD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ,sBAAsB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC"}
@@ -4,11 +4,11 @@
4
4
  * Serializes the bootstrap check inside a transaction-scoped advisory lock so
5
5
  * concurrent first-time startups cannot mint multiple root keys.
6
6
  */
7
- import type { PoolClient } from "pg";
7
+ import type { Pool } from "pg";
8
8
  import { type ApiKeyWithSecret } from "./repositories/api-keys.js";
9
- interface TransactionalDatabase {
10
- transact<TResult>(runInTransaction: (client: PoolClient) => Promise<TResult>): Promise<TResult>;
9
+ interface BootstrapApiKeyOptions {
10
+ key?: string;
11
11
  }
12
- declare function bootstrapApiKey(database: TransactionalDatabase): Promise<ApiKeyWithSecret | undefined>;
12
+ declare function bootstrapApiKey(pool: Pool, options?: BootstrapApiKeyOptions): Promise<ApiKeyWithSecret | undefined>;
13
13
  export { bootstrapApiKey };
14
14
  //# sourceMappingURL=bootstrap-api-key.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap-api-key.d.ts","sourceRoot":"","sources":["../../src/db/bootstrap-api-key.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACrC,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AAIpC,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,OAAO,EACd,gBAAgB,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,GACzD,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,iBAAe,eAAe,CAC5B,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAmBvC;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"bootstrap-api-key.d.ts","sourceRoot":"","sources":["../../src/db/bootstrap-api-key.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AASpC,UAAU,sBAAsB;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,iBAAe,eAAe,CAC5B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAsDvC;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -5,22 +5,55 @@
5
5
  * concurrent first-time startups cannot mint multiple root keys.
6
6
  */
7
7
  import { createApiKey, } from "./repositories/api-keys.js";
8
+ import { findApiKeyByKey, hasGrantAccess, listApiKeys, } from "./repositories/api-keys.js";
8
9
  const BOOTSTRAP_API_KEY_LOCK_ID = 980_641_073;
9
- async function bootstrapApiKey(database) {
10
- return database.transact(async (client) => {
10
+ async function bootstrapApiKey(pool, options = {}) {
11
+ const client = await pool.connect();
12
+ try {
13
+ await client.query("BEGIN");
11
14
  await client.query("SELECT pg_advisory_xact_lock($1)", [
12
15
  BOOTSTRAP_API_KEY_LOCK_ID,
13
16
  ]);
14
- const result = await client.query("SELECT EXISTS (SELECT 1 FROM api_keys) AS has_api_keys");
15
- if (result.rows[0]?.has_api_keys)
17
+ if (options.key !== undefined) {
18
+ const existingBootstrapKey = await findApiKeyByKey(client, options.key);
19
+ if (existingBootstrapKey) {
20
+ if (hasGrantAccess(existingBootstrapKey, "*")) {
21
+ await client.query("COMMIT");
22
+ return;
23
+ }
24
+ throw new Error("The requested bootstrap admin key does not match the existing database state.");
25
+ }
26
+ }
27
+ const existingKeys = await listApiKeys(client);
28
+ if (existingKeys.some((key) => hasGrantAccess(key, "*"))) {
29
+ if (options.key !== undefined) {
30
+ throw new Error("The requested bootstrap admin key does not match the existing database state.");
31
+ }
32
+ await client.query("COMMIT");
16
33
  return;
17
- return createApiKey(client, {
34
+ }
35
+ const createdKey = await createApiKey(client, {
18
36
  name: "Bootstrap Admin",
19
37
  readAccess: ["*"],
20
38
  writeAccess: ["*"],
21
39
  grantAccess: ["*"],
40
+ key: options.key,
22
41
  });
23
- });
42
+ await client.query("COMMIT");
43
+ return createdKey;
44
+ }
45
+ catch (error) {
46
+ try {
47
+ await client.query("ROLLBACK");
48
+ }
49
+ catch {
50
+ // PostgreSQL automatically rolls back on disconnect; preserve the cause.
51
+ }
52
+ throw error;
53
+ }
54
+ finally {
55
+ client.release();
56
+ }
24
57
  }
25
58
  export { bootstrapApiKey };
26
59
  //# sourceMappingURL=bootstrap-api-key.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap-api-key.js","sourceRoot":"","sources":["../../src/db/bootstrap-api-key.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,YAAY,GAEb,MAAM,4BAA4B,CAAC;AAEpC,MAAM,yBAAyB,GAAG,WAAW,CAAC;AAQ9C,KAAK,UAAU,eAAe,CAC5B,QAA+B;IAE/B,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;YACrD,yBAAyB;SAC1B,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,wDAAwD,CACzD,CAAC;QAEF,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,YAAY;YAAE,OAAO;QAEzC,OAAO,YAAY,CAAC,MAAM,EAAE;YAC1B,IAAI,EAAE,iBAAiB;YACvB,UAAU,EAAE,CAAC,GAAG,CAAC;YACjB,WAAW,EAAE,CAAC,GAAG,CAAC;YAClB,WAAW,EAAE,CAAC,GAAG,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"bootstrap-api-key.js","sourceRoot":"","sources":["../../src/db/bootstrap-api-key.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,YAAY,GAEb,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,GACZ,MAAM,4BAA4B,CAAC;AAEpC,MAAM,yBAAyB,GAAG,WAAW,CAAC;AAM9C,KAAK,UAAU,eAAe,CAC5B,IAAU,EACV,UAAkC,EAAE;IAEpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;YACrD,yBAAyB;SAC1B,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,oBAAoB,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YACxE,IAAI,oBAAoB,EAAE,CAAC;gBACzB,IAAI,cAAc,CAAC,oBAAoB,EAAE,GAAG,CAAC,EAAE,CAAC;oBAC9C,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAC7B,OAAO;gBACT,CAAC;gBAED,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACzD,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE;YAC5C,IAAI,EAAE,iBAAiB;YACvB,UAAU,EAAE,CAAC,GAAG,CAAC;YACjB,WAAW,EAAE,CAAC,GAAG,CAAC;YAClB,WAAW,EAAE,CAAC,GAAG,CAAC;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;QAC3E,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -5,8 +5,8 @@
5
5
  */
6
6
  /** Generate a new API key ID */
7
7
  declare function generateKeyId(): string;
8
- /** Generate a new API key secret */
9
- declare function generateKeySecret(): string;
8
+ /** Generate or validate an API key secret for deterministic provisioning. */
9
+ declare function resolveApiKeySecret(key?: string): string;
10
10
  /** Hash an API key for storage */
11
11
  declare function hashApiKey(key: string): string;
12
12
  /** Extract key prefix for display (axv_sk_ + first 8 hex chars of secret) */
@@ -23,5 +23,5 @@ declare function hasReadAccess(apiKey: AccessLists, name: string): boolean;
23
23
  declare function hasWriteAccess(apiKey: AccessLists, name: string): boolean;
24
24
  /** Check if API key has grant access to a credential */
25
25
  declare function hasGrantAccess(apiKey: AccessLists, name: string): boolean;
26
- export { extractKeyPrefix, generateKeyId, generateKeySecret, hashApiKey, hasGrantAccess, hasReadAccess, hasWriteAccess, };
26
+ export { extractKeyPrefix, generateKeyId, hashApiKey, hasGrantAccess, hasReadAccess, hasWriteAccess, resolveApiKeySecret, };
27
27
  //# sourceMappingURL=api-key-utilities.d.ts.map