Skip to main content

systemprompt_cli/commands/admin/
mod.rs

1//! `admin` command tree: privileged platform administration.
2//!
3//! [`AdminCommands`] groups user, agent, configuration, session, bridge,
4//! access-control, and signing-key management plus the setup and bootstrap
5//! flows. On a `--database-url` invocation only the user-management subgroup
6//! is served; the rest require a full profile context.
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11pub mod access_control;
12pub mod agents;
13pub mod bootstrap;
14pub mod bridge;
15pub mod config;
16pub mod evals;
17pub mod identity;
18pub mod keys;
19pub mod session;
20pub mod setup;
21pub mod users;
22
23use anyhow::Result;
24use clap::Subcommand;
25
26use crate::context::CommandContext;
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(subcommand, about = "Evaluation runs over production AI traffic")]
41    Evals(evals::EvalsCommands),
42
43    #[command(about = "Interactive setup wizard for local development environment")]
44    Setup(setup::SetupArgs),
45
46    #[command(
47        about = "Idempotently ensure the system admin user exists with the admin role. Required \
48                 by every install recipe before services start. Note: the admin ROLE only — \
49                 deployments that derive platform admin from organization membership grant that \
50                 half themselves (at boot, or via their own tooling)."
51    )]
52    Bootstrap(bootstrap::BootstrapArgs),
53
54    #[command(subcommand, about = "Manage CLI session and profile switching")]
55    Session(session::SessionCommands),
56
57    #[command(
58        subcommand,
59        about = "Bridge helper enrollment (device certs, exchange codes)"
60    )]
61    Bridge(bridge::BridgeCommands),
62
63    #[command(
64        subcommand,
65        name = "access-control",
66        about = "Access-control baseline operations (DB → YAML export)"
67    )]
68    AccessControl(access_control::AccessControlCommands),
69
70    #[command(
71        subcommand,
72        about = "RSA signing-key generation for the federated JWT plane"
73    )]
74    Keys(keys::KeysCommands),
75
76    #[command(
77        subcommand,
78        about = "Replica identity secrets shared by every node of a deployment"
79    )]
80    Identity(identity::IdentityCommands),
81}
82
83pub async fn execute(cmd: AdminCommands, ctx: &CommandContext) -> Result<()> {
84    // Why: Session is exempt alongside Users — `session login` against a cloud
85    // profile with external_db_access runs exactly in this database-scoped
86    // mode (profile + secrets + external DB URL, no local /app paths), and it
87    // is the command that mints the token remote routing needs. Refusing it
88    // here made cloud login circular: the error told the operator to run the
89    // very command being refused.
90    if ctx.is_database_scoped()
91        && !matches!(cmd, AdminCommands::Users(_) | AdminCommands::Session(_))
92    {
93        return Err(crate::shared::database_scoped_command_error());
94    }
95
96    match cmd {
97        AdminCommands::Users(cmd) => users::execute(cmd, ctx).await,
98        AdminCommands::Agents(cmd) => Box::pin(agents::execute(cmd, ctx)).await,
99        AdminCommands::Config(cmd) => config::execute(cmd, ctx).await,
100        AdminCommands::Evals(cmd) => Box::pin(evals::execute(cmd, ctx)).await,
101        AdminCommands::Setup(args) => {
102            let result = Box::pin(setup::execute(args, ctx)).await?;
103            render_result(&result, &ctx.cli);
104            Ok(())
105        },
106        AdminCommands::Bootstrap(args) => {
107            let result = bootstrap::execute(args, &ctx.cli).await?;
108            render_result(&result, &ctx.cli);
109            Ok(())
110        },
111        AdminCommands::Session(cmd) => session::execute(cmd, ctx).await,
112        AdminCommands::Bridge(cmd) => bridge::execute(cmd, ctx).await,
113        AdminCommands::AccessControl(cmd) => access_control::execute(cmd, ctx).await,
114        AdminCommands::Keys(cmd) => keys::execute(cmd, ctx).await,
115        AdminCommands::Identity(cmd) => identity::execute(cmd, ctx),
116    }
117}