Skip to main content

systemprompt_cli/commands/admin/bridge/
mod.rs

1//! `admin bridge` subcommand: operator tools for the bridge helper.
2//!
3//! Exposes [`BridgeCommands`] for enrolling device-certificate fingerprints,
4//! issuing one-shot session exchange codes, listing active bridge sessions,
5//! and rotating the ed25519 manifest signing seed.
6//!
7//! Copyright (c) systemprompt.io — Business Source License 1.1.
8//! See <https://systemprompt.io> for licensing details.
9
10mod enroll_cert;
11mod issue_code;
12mod list;
13mod rotate_signing_key;
14mod types;
15
16use crate::context::CommandContext;
17use crate::shared::render_result;
18use anyhow::{Result, anyhow};
19use clap::Subcommand;
20use systemprompt_database::DbPool;
21use systemprompt_identifiers::UserId;
22use systemprompt_users::{UserAdminService, UserService};
23
24pub(super) async fn resolve_user_id(pool: &DbPool, reference: &UserId) -> Result<UserId> {
25    let reference = reference.as_str().trim();
26    if reference.is_empty() {
27        return Err(anyhow!("user_id cannot be empty"));
28    }
29
30    let admin_service = UserAdminService::new(UserService::new(pool)?);
31    admin_service
32        .find_user(reference)
33        .await?
34        .map(|user| user.id)
35        .ok_or_else(|| anyhow!("no user with id, email, or name '{reference}'"))
36}
37
38#[derive(Debug, Subcommand)]
39pub enum BridgeCommands {
40    #[command(about = "Enroll a device certificate fingerprint for a user")]
41    EnrollCert(enroll_cert::EnrollCertArgs),
42
43    #[command(about = "Issue a one-shot session exchange code for the bridge helper")]
44    IssueCode(issue_code::IssueCodeArgs),
45
46    #[command(about = "List active bridge sessions (recent heartbeats)")]
47    List(list::ListArgs),
48
49    #[command(
50        about = "Generate a fresh ed25519 manifest signing seed and persist it to the secrets file"
51    )]
52    RotateSigningKey(rotate_signing_key::RotateSigningKeyArgs),
53}
54
55pub async fn execute(cmd: BridgeCommands, ctx: &CommandContext) -> Result<()> {
56    match cmd {
57        BridgeCommands::EnrollCert(args) => {
58            let result = enroll_cert::execute(args, ctx).await?;
59            render_result(&result, &ctx.cli);
60            Ok(())
61        },
62        BridgeCommands::IssueCode(args) => {
63            let result = issue_code::execute(args, ctx).await?;
64            render_result(&result, &ctx.cli);
65            Ok(())
66        },
67        BridgeCommands::List(args) => {
68            let result = list::execute(args, ctx).await?;
69            render_result(&result, &ctx.cli);
70            Ok(())
71        },
72        BridgeCommands::RotateSigningKey(args) => {
73            let result = rotate_signing_key::execute(args, &ctx.cli)?;
74            render_result(&result, &ctx.cli);
75            Ok(())
76        },
77    }
78}