axvault 1.6.0 → 1.7.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.
package/README.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  Remote credential storage server for a╳kit.
4
4
 
5
+ ## Quick start
6
+
7
+ ```bash
8
+ export AXVAULT_ENCRYPTION_KEY="your-secret-key-minimum-32-chars!"
9
+ axvault init
10
+ axvault serve
11
+ ```
12
+
13
+ In another shell, create an API key:
14
+
15
+ ```bash
16
+ axvault key create --name "Admin" --read "*" --write "*" --grant "*"
17
+ ```
18
+
19
+ Add `--verbose` to commands like `init`, `serve`, `key revoke`, `key update`, and
20
+ `credential delete` to see status output.
21
+
22
+ ## Pipeline examples
23
+
24
+ ### Extract key IDs
25
+
26
+ ```bash
27
+ axvault key list | tail -n +2 | cut -f1
28
+ ```
29
+
30
+ ### Count credentials by type
31
+
32
+ ```bash
33
+ axvault credential list | tail -n +2 | cut -f3 | sort | uniq -c | sort -rn
34
+ ```
35
+
36
+ ### List credential paths for a single agent
37
+
38
+ ```bash
39
+ axvault credential list | tail -n +2 | awk -F'\t' '$1 == "claude" {print $1 "/" $2}'
40
+ ```
41
+
42
+ ## Agent Rule
43
+
44
+ Add to your `CLAUDE.md` or `AGENTS.md`:
45
+
46
+ ```markdown
47
+ # Rule: `axvault` Usage
48
+
49
+ Run `npx -y axvault --help` to learn available options.
50
+
51
+ Use `axvault` when you need to initialize the vault database, manage API keys,
52
+ or list/delete stored credentials. It outputs TSV tables for list commands, so
53
+ you can pipe them into standard Unix tools.
54
+ ```
55
+
5
56
  ## Configuration
6
57
 
7
58
  ### Environment Variables
@@ -100,9 +151,11 @@ axvault key update k_a1b2c3d4e5f6 --add-read "codex/ci" --remove-write "claude/d
100
151
  ### Revoke a Key
101
152
 
102
153
  ```bash
103
- axvault key revoke k_a1b2c3d4e5f6
154
+ axvault key revoke k_a1b2c3d4e5f6 --force
104
155
  ```
105
156
 
157
+ This command requires `--force` or `--yes` to confirm.
158
+
106
159
  ### Container Deployments
107
160
 
108
161
  #### Running the Container
package/dist/cli.js CHANGED
@@ -40,21 +40,25 @@ Examples:
40
40
  # List all API keys
41
41
  axvault key list
42
42
 
43
+ # Extract key IDs for scripting (pipeline example)
44
+ axvault key list | tail -n +2 | cut -f1
45
+
43
46
  # Update an API key's permissions
44
47
  axvault key update k_abc123def456 --add-read "claude/new"
45
48
 
46
49
  # Revoke an API key
47
- axvault key revoke k_abc123def456
50
+ axvault key revoke k_abc123def456 --force
48
51
 
49
52
  # List all stored credentials
50
53
  axvault credential list
51
54
 
52
55
  # Delete a credential
53
- axvault credential delete claude/work`);
56
+ axvault credential delete claude/work --force`);
54
57
  program
55
58
  .command("init")
56
59
  .description("Initialize database and configuration")
57
60
  .option("--db-path <path>", "Database file path")
61
+ .option("-v, --verbose", "Enable verbose output")
58
62
  .action(handleInit);
59
63
  program
60
64
  .command("serve")
@@ -64,6 +68,7 @@ program
64
68
  .option("--db-path <path>", "Database file path")
65
69
  .option("--refresh-threshold <seconds>", "Refresh credentials expiring within this many seconds (0 to disable)")
66
70
  .option("--refresh-timeout <ms>", "Timeout for refresh operations in milliseconds")
71
+ .option("-v, --verbose", "Enable verbose output")
67
72
  .action(handleServe);
68
73
  // API key management commands
69
74
  const keyCommand = program
@@ -90,6 +95,9 @@ keyCommand
90
95
  .command("revoke")
91
96
  .description("Revoke an API key")
92
97
  .argument("<id>", "API key ID (e.g., k_abc123def456)")
98
+ .option("-f, --force", "Confirm destructive action")
99
+ .option("-y, --yes", "Alias for --force")
100
+ .option("-v, --verbose", "Enable verbose output")
93
101
  .option("--db-path <path>", "Database file path")
94
102
  .action(handleKeyRevoke);
95
103
  keyCommand
@@ -103,6 +111,7 @@ keyCommand
103
111
  .option("--remove-write <access>", "Remove write access entries")
104
112
  .option("--remove-grant <access>", "Remove grant access entries")
105
113
  .option("--json", "Output as JSON")
114
+ .option("-v, --verbose", "Enable verbose output")
106
115
  .option("--db-path <path>", "Database file path")
107
116
  .action(handleKeyUpdate);
108
117
  // Credential management commands
@@ -120,6 +129,9 @@ credentialCommand
120
129
  .command("delete")
121
130
  .description("Delete a credential")
122
131
  .argument("<path>", "Credential path (agent/name, e.g., claude/work)")
132
+ .option("-f, --force", "Confirm destructive action")
133
+ .option("-y, --yes", "Alias for --force")
134
+ .option("-v, --verbose", "Enable verbose output")
123
135
  .option("--db-path <path>", "Database file path")
124
136
  .action(handleCredentialDelete);
125
137
  await program.parseAsync(process.argv);
@@ -7,6 +7,9 @@ interface CredentialListOptions {
7
7
  }
8
8
  interface CredentialDeleteOptions {
9
9
  dbPath?: string;
10
+ force?: boolean;
11
+ yes?: boolean;
12
+ verbose?: boolean;
10
13
  }
11
14
  export declare function handleCredentialList(options: CredentialListOptions): void;
12
15
  export declare function handleCredentialDelete(path: string, options: CredentialDeleteOptions): void;
@@ -91,6 +91,12 @@ export function handleCredentialDelete(path, options) {
91
91
  process.exitCode = 2;
92
92
  return;
93
93
  }
94
+ if (!options.force && !options.yes) {
95
+ console.error("Error: Deleting a credential is destructive. Re-run with --force or --yes to confirm.");
96
+ console.error("Try 'axvault credential delete --help' for more information.");
97
+ process.exitCode = 2;
98
+ return;
99
+ }
94
100
  const { agent, name } = parsed;
95
101
  try {
96
102
  const config = getServerConfig(options);
@@ -98,7 +104,9 @@ export function handleCredentialDelete(path, options) {
98
104
  runMigrations(database);
99
105
  const deleted = deleteCredential(database, agent, name);
100
106
  if (deleted) {
101
- console.error(`Deleted credential: ${sanitizeForTsv(agent)}/${sanitizeForTsv(name)}`);
107
+ if (options.verbose) {
108
+ console.error(`Deleted credential: ${sanitizeForTsv(agent)}/${sanitizeForTsv(name)}`);
109
+ }
102
110
  }
103
111
  else {
104
112
  console.error(`Error: Credential not found: ${sanitizeForTsv(agent)}/${sanitizeForTsv(name)}`);
@@ -3,6 +3,7 @@
3
3
  */
4
4
  interface InitOptions {
5
5
  dbPath?: string;
6
+ verbose?: boolean;
6
7
  }
7
8
  export declare function handleInit(options: InitOptions): void;
8
9
  export {};
@@ -7,13 +7,15 @@ import { getServerConfig } from "../config.js";
7
7
  import { closeDatabase, getDatabase } from "../db/client.js";
8
8
  import { CURRENT_VERSION, getSchemaVersion, runMigrations, } from "../db/migrations.js";
9
9
  export function handleInit(options) {
10
- const config = getServerConfig(options);
10
+ const config = getServerConfig({ dbPath: options.dbPath });
11
+ const verbose = options.verbose === true;
11
12
  // Ensure data directory exists
12
13
  const dataDirectory = path.dirname(config.databasePath);
13
14
  if (!existsSync(dataDirectory)) {
14
15
  try {
15
16
  mkdirSync(dataDirectory, { recursive: true });
16
- console.error(`Created data directory: ${dataDirectory}`);
17
+ if (verbose)
18
+ console.error(`Created data directory: ${dataDirectory}`);
17
19
  }
18
20
  catch (error) {
19
21
  const message = error instanceof Error ? error.message : String(error);
@@ -28,15 +30,17 @@ export function handleInit(options) {
28
30
  const versionBefore = getSchemaVersion(database);
29
31
  runMigrations(database);
30
32
  const versionAfter = getSchemaVersion(database);
31
- if (versionBefore === 0) {
32
- console.error(`Database initialized at: ${config.databasePath}`);
33
- console.error(`Schema version: ${versionAfter}`);
34
- }
35
- else if (versionBefore < versionAfter) {
36
- console.error(`Database migrated from v${versionBefore} to v${versionAfter}`);
37
- }
38
- else {
39
- console.error(`Database already at version ${versionAfter} (current: ${CURRENT_VERSION})`);
33
+ if (verbose) {
34
+ if (versionBefore === 0) {
35
+ console.error(`Database initialized at: ${config.databasePath}`);
36
+ console.error(`Schema version: ${versionAfter}`);
37
+ }
38
+ else if (versionBefore < versionAfter) {
39
+ console.error(`Database migrated from v${versionBefore} to v${versionAfter}`);
40
+ }
41
+ else {
42
+ console.error(`Database already at version ${versionAfter} (current: ${CURRENT_VERSION})`);
43
+ }
40
44
  }
41
45
  }
42
46
  catch (error) {
@@ -3,6 +3,9 @@
3
3
  */
4
4
  interface KeyRevokeOptions {
5
5
  dbPath?: string;
6
+ force?: boolean;
7
+ yes?: boolean;
8
+ verbose?: boolean;
6
9
  }
7
10
  export declare function handleKeyRevoke(id: string, options: KeyRevokeOptions): void;
8
11
  export {};
@@ -24,13 +24,21 @@ export function handleKeyRevoke(id, options) {
24
24
  process.exitCode = 2;
25
25
  return;
26
26
  }
27
+ if (!options.force && !options.yes) {
28
+ console.error("Error: Revoking an API key is destructive. Re-run with --force or --yes to confirm.");
29
+ console.error("Try 'axvault key revoke --help' for more information.");
30
+ process.exitCode = 2;
31
+ return;
32
+ }
27
33
  try {
28
34
  const config = getServerConfig(options);
29
35
  const database = getDatabase(config.databasePath);
30
36
  runMigrations(database);
31
37
  const deleted = deleteApiKey(database, trimmedId);
32
38
  if (deleted) {
33
- console.error(`Revoked API key: ${sanitizeForTsv(trimmedId)}`);
39
+ if (options.verbose) {
40
+ console.error(`Revoked API key: ${sanitizeForTsv(trimmedId)}`);
41
+ }
34
42
  }
35
43
  else {
36
44
  console.error(`Error: API key not found: ${sanitizeForTsv(trimmedId)}`);
@@ -10,6 +10,7 @@ interface KeyUpdateOptions {
10
10
  removeWrite?: string;
11
11
  removeGrant?: string;
12
12
  json?: boolean;
13
+ verbose?: boolean;
13
14
  }
14
15
  export declare function handleKeyUpdate(id: string, options: KeyUpdateOptions): void;
15
16
  export {};
@@ -76,7 +76,7 @@ export function handleKeyUpdate(id, options) {
76
76
  process.exitCode = 1;
77
77
  return;
78
78
  }
79
- outputKeyDetails(updatedKey, options.json ?? false);
79
+ outputKeyDetails(updatedKey, options.json ?? false, options.verbose ?? false);
80
80
  }
81
81
  catch (error) {
82
82
  console.error(`Error: Failed to update API key: ${getErrorMessage(error)}`);
@@ -87,7 +87,7 @@ export function handleKeyUpdate(id, options) {
87
87
  }
88
88
  }
89
89
  /** Output key details in JSON or human-readable format */
90
- function outputKeyDetails(key, json) {
90
+ function outputKeyDetails(key, json, verbose) {
91
91
  if (json) {
92
92
  console.log(JSON.stringify({
93
93
  id: key.id,
@@ -99,7 +99,7 @@ function outputKeyDetails(key, json) {
99
99
  lastUsedAt: formatDateForJson(key.lastUsedAt),
100
100
  }, undefined, 2));
101
101
  }
102
- else {
102
+ else if (verbose) {
103
103
  console.error(`Updated API key: ${sanitizeForTsv(key.name)}`);
104
104
  console.error(`ID: ${sanitizeForTsv(key.id)}`);
105
105
  console.error(`Read access: ${sanitizeForTsv(formatAccessList(key.readAccess))}`);
@@ -7,6 +7,7 @@ interface ServeOptions {
7
7
  dbPath?: string;
8
8
  refreshThreshold?: string;
9
9
  refreshTimeout?: string;
10
+ verbose?: boolean;
10
11
  }
11
12
  export declare function handleServe(options: ServeOptions): Promise<void>;
12
13
  export {};
@@ -10,9 +10,12 @@ import { createHealthRouter, createCredentialRouter, } from "../server/routes.js
10
10
  import { createServer } from "../server/server.js";
11
11
  export async function handleServe(options) {
12
12
  let config;
13
+ const verbose = options.verbose === true;
13
14
  try {
14
15
  config = getServerConfig({
15
- ...options,
16
+ port: options.port,
17
+ host: options.host,
18
+ dbPath: options.dbPath,
16
19
  refreshThresholdSeconds: options.refreshThreshold,
17
20
  refreshTimeoutMs: options.refreshTimeout,
18
21
  });
@@ -35,7 +38,8 @@ export async function handleServe(options) {
35
38
  if (!existsSync(dataDirectory)) {
36
39
  try {
37
40
  mkdirSync(dataDirectory, { recursive: true });
38
- console.error(`Created data directory: ${dataDirectory}`);
41
+ if (verbose)
42
+ console.error(`Created data directory: ${dataDirectory}`);
39
43
  }
40
44
  catch (error) {
41
45
  const message = error instanceof Error ? error.message : String(error);
@@ -66,7 +70,8 @@ export async function handleServe(options) {
66
70
  ]);
67
71
  // Graceful shutdown handler
68
72
  const shutdown = () => {
69
- console.error("Shutting down...");
73
+ if (verbose)
74
+ console.error("Shutting down...");
70
75
  server.stop().then(() => {
71
76
  closeDatabase();
72
77
  // eslint-disable-next-line unicorn/no-process-exit -- CLI graceful shutdown
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "axvault",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "1.6.0",
5
+ "version": "1.7.0",
6
6
  "description": "Remote credential storage server for axkit",
7
7
  "repository": {
8
8
  "type": "git",