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 std::sync::Arc;
21use systemprompt_database::DbPool;
22use systemprompt_identifiers::UserId;
23use systemprompt_users::{UserAdminService, UserRepository, UserService};
24
25pub(super) async fn resolve_user_id(pool: &DbPool, reference: &UserId) -> Result<UserId> {
26    let reference = reference.as_str().trim();
27    if reference.is_empty() {
28        return Err(anyhow!("user_id cannot be empty"));
29    }
30
31    let admin_service =
32        UserAdminService::new(UserService::new(Arc::new(UserRepository::new(pool)?)));
33    admin_service
34        .find_user(reference)
35        .await?
36        .map(|user| user.id)
37        .ok_or_else(|| anyhow!("no user with id, email, or name '{reference}'"))
38}
39
40#[derive(Debug, Subcommand)]
41pub enum BridgeCommands {
42    #[command(about = "Enroll a device certificate fingerprint for a user")]
43    EnrollCert(enroll_cert::EnrollCertArgs),
44
45    #[command(about = "Issue a one-shot session exchange code for the bridge helper")]
46    IssueCode(issue_code::IssueCodeArgs),
47
48    #[command(about = "List active bridge sessions (recent heartbeats)")]
49    List(list::ListArgs),
50
51    #[command(
52        about = "Generate a fresh ed25519 manifest signing seed and persist it to the secrets file"
53    )]
54    RotateSigningKey(rotate_signing_key::RotateSigningKeyArgs),
55}
56
57pub async fn execute(cmd: BridgeCommands, ctx: &CommandContext) -> Result<()> {
58    match cmd {
59        BridgeCommands::EnrollCert(args) => {
60            let result = enroll_cert::execute(args, ctx).await?;
61            render_result(&result, &ctx.cli);
62            Ok(())
63        },
64        BridgeCommands::IssueCode(args) => {
65            let result = issue_code::execute(args, ctx).await?;
66            render_result(&result, &ctx.cli);
67            Ok(())
68        },
69        BridgeCommands::List(args) => {
70            let result = list::execute(args, ctx).await?;
71            render_result(&result, &ctx.cli);
72            Ok(())
73        },
74        BridgeCommands::RotateSigningKey(args) => {
75            let result = rotate_signing_key::execute(args, &ctx.cli)?;
76            render_result(&result, &ctx.cli);
77            Ok(())
78        },
79    }
80}