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 +54 -1
- package/dist/cli.js +14 -2
- package/dist/commands/credential.d.ts +3 -0
- package/dist/commands/credential.js +9 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +15 -11
- package/dist/commands/key-revoke.d.ts +3 -0
- package/dist/commands/key-revoke.js +9 -1
- package/dist/commands/key-update.d.ts +1 -0
- package/dist/commands/key-update.js +3 -3
- package/dist/commands/serve.d.ts +1 -0
- package/dist/commands/serve.js +8 -3
- package/package.json +1 -1
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
|
-
|
|
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)}`);
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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) {
|
|
@@ -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
|
-
|
|
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)}`);
|
|
@@ -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))}`);
|
package/dist/commands/serve.d.ts
CHANGED
package/dist/commands/serve.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|