1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
pub mod access;
pub mod agent;
pub mod check;
pub mod init;
pub mod recovery;
pub mod secret;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "agent-vault", about = "Zero-trust credential manager for AI agents")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Initialize a new vault in the current (or specified) directory
Init {
/// Directory to initialize (defaults to current directory)
directory: Option<String>,
},
/// Add a new agent to the vault
AddAgent {
/// Name of the agent
name: String,
},
/// Remove an agent from the vault
RemoveAgent {
/// Name of the agent
name: String,
},
/// List all agents in the vault
ListAgents {
/// Output as JSON
#[arg(long)]
json: bool,
},
/// Grant an agent access to a group
Grant {
/// Agent name
agent: String,
/// Group name
group: String,
},
/// Revoke an agent's access to a group
Revoke {
/// Agent name
agent: String,
/// Group name
group: String,
},
/// Set (create or update) a secret
Set {
/// Secret path (e.g. stripe/api-key)
path: String,
/// Secret value
value: Option<String>,
/// Read value from file
#[arg(long)]
from_file: Option<String>,
/// Group to assign the secret to (defaults to first component of path)
#[arg(long)]
group: Option<String>,
/// Expiration date (ISO 8601, e.g. 2026-12-31T00:00:00Z)
#[arg(long)]
expires: Option<String>,
/// Encrypt for specific agents (comma-separated, additive with group members)
#[arg(long, value_delimiter = ',')]
agents: Option<Vec<String>>,
},
/// Get (decrypt) a secret
Get {
/// Secret path (e.g. stripe/api-key)
path: String,
/// Path to private key file
#[arg(long)]
key: Option<String>,
},
/// List all secrets in the vault
List {
/// Filter by group
#[arg(long)]
group: Option<String>,
/// Output as JSON
#[arg(long)]
json: bool,
},
/// Audit the vault for issues
Check {
/// Output as JSON
#[arg(long)]
json: bool,
},
/// Recover an agent (generate new keypair, re-encrypt secrets)
RecoverAgent {
/// Agent name
name: String,
},
/// Restore an agent's original private key from escrow
RestoreAgent {
/// Agent name
name: String,
/// Path to write the restored key
#[arg(long = "to")]
to_path: String,
},
/// Generate shell completions
Completions {
/// Shell to generate completions for
shell: clap_complete::Shell,
},
}
pub fn dispatch(cli: Cli) -> anyhow::Result<()> {
match cli.command {
Commands::Init { directory } => init::run(directory),
Commands::AddAgent { name } => agent::run_add(&name),
Commands::RemoveAgent { name } => agent::run_remove(&name),
Commands::ListAgents { json } => agent::run_list(json),
Commands::Grant { agent, group } => access::run_grant(&agent, &group),
Commands::Revoke { agent, group } => access::run_revoke(&agent, &group),
Commands::Set {
path,
value,
from_file,
group,
expires,
agents,
} => secret::run_set(&path, value.as_deref(), from_file.as_deref(), group.as_deref(), expires.as_deref(), agents),
Commands::Get { path, key } => secret::run_get(&path, key.as_deref()),
Commands::List { group, json } => secret::run_list(group.as_deref(), json),
Commands::Check { json } => check::run(json),
Commands::RecoverAgent { name } => recovery::run_recover(&name),
Commands::RestoreAgent { name, to_path } => recovery::run_restore(&name, &to_path),
Commands::Completions { shell } => {
let mut cmd = <Cli as clap::CommandFactory>::command();
clap_complete::generate(shell, &mut cmd, "agent-vault", &mut std::io::stdout());
Ok(())
}
}
}