systemprompt_cli/commands/admin/
mod.rs1pub mod access_control;
12pub mod agents;
13pub mod bootstrap;
14pub mod bridge;
15pub mod config;
16pub mod identity;
17pub mod keys;
18pub mod session;
19pub mod setup;
20pub mod users;
21
22use anyhow::Result;
23use clap::Subcommand;
24
25use crate::context::CommandContext;
26use crate::descriptor::DataImpact;
27use crate::shared::render_result;
28
29#[derive(Debug, Subcommand)]
30pub enum AdminCommands {
31 #[command(subcommand, about = "User management and IP banning")]
32 Users(users::UsersCommands),
33
34 #[command(subcommand, about = "Agent management")]
35 Agents(agents::AgentsCommands),
36
37 #[command(subcommand, about = "Configuration management and rate limits")]
38 Config(config::ConfigCommands),
39
40 #[command(about = "Interactive setup wizard for local development environment")]
41 Setup(setup::SetupArgs),
42
43 #[command(
44 about = "Idempotently ensure the system admin user exists with the admin role. Required \
45 by every install recipe before services start. Note: the admin ROLE only — \
46 deployments that derive platform admin from organization membership grant that \
47 half themselves (at boot, or via their own tooling)."
48 )]
49 Bootstrap(bootstrap::BootstrapArgs),
50
51 #[command(subcommand, about = "Manage CLI session and profile switching")]
52 Session(session::SessionCommands),
53
54 #[command(
55 subcommand,
56 about = "Bridge helper enrollment (device certs, exchange codes)"
57 )]
58 Bridge(bridge::BridgeCommands),
59
60 #[command(
61 subcommand,
62 name = "access-control",
63 about = "Access-control baseline operations (DB → YAML export)"
64 )]
65 AccessControl(access_control::AccessControlCommands),
66
67 #[command(
68 subcommand,
69 about = "RSA signing-key generation for the federated JWT plane"
70 )]
71 Keys(keys::KeysCommands),
72
73 #[command(
74 subcommand,
75 about = "Replica identity secrets shared by every node of a deployment"
76 )]
77 Identity(identity::IdentityCommands),
78}
79
80pub async fn execute(cmd: AdminCommands, ctx: &CommandContext) -> Result<()> {
81 if ctx.is_database_scoped()
82 && !matches!(cmd, AdminCommands::Users(_) | AdminCommands::Session(_))
83 {
84 return Err(crate::shared::database_scoped_command_error());
85 }
86
87 match cmd {
88 AdminCommands::Users(cmd) => users::execute(cmd, ctx).await,
89 AdminCommands::Agents(cmd) => Box::pin(agents::execute(cmd, ctx)).await,
90 AdminCommands::Config(cmd) => config::execute(cmd, ctx).await,
91 AdminCommands::Setup(args) => {
92 let result = Box::pin(setup::execute(args, ctx)).await?;
93 render_result(&result, &ctx.cli);
94 Ok(())
95 },
96 AdminCommands::Bootstrap(args) => {
97 let result = bootstrap::execute(args, &ctx.cli).await?;
98 render_result(&result, &ctx.cli);
99 Ok(())
100 },
101 AdminCommands::Session(cmd) => session::execute(cmd, ctx).await,
102 AdminCommands::Bridge(cmd) => bridge::execute(cmd, ctx).await,
103 AdminCommands::AccessControl(cmd) => access_control::execute(cmd, ctx).await,
104 AdminCommands::Keys(cmd) => keys::execute(cmd, ctx).await,
105 AdminCommands::Identity(cmd) => identity::execute(cmd, ctx),
106 }
107}
108
109impl AdminCommands {
110 pub const fn data_impact(&self) -> DataImpact {
111 match self {
112 Self::Users(cmd) => cmd.data_impact(),
113 Self::Agents(cmd) => cmd.data_impact(),
114 Self::Bootstrap(_) | Self::Bridge(bridge::BridgeCommands::RotateSigningKey(_)) => {
115 DataImpact::Destructive
116 },
117 Self::Bridge(
118 bridge::BridgeCommands::EnrollCert(_)
119 | bridge::BridgeCommands::IssueCode(_)
120 | bridge::BridgeCommands::List(_),
121 )
122 | Self::AccessControl(
123 access_control::AccessControlCommands::ExportYaml(_)
124 | access_control::AccessControlCommands::Lint(_),
125 )
126 | Self::Keys(
127 keys::KeysCommands::Generate(_) | keys::KeysCommands::IssuePluginToken(_),
128 )
129 | Self::Config(_)
130 | Self::Setup(_)
131 | Self::Session(_)
132 | Self::Identity(_) => DataImpact::Preserving,
133 }
134 }
135}