Skip to main content

systemprompt_cli/session/
api.rs

1//! Local session and JWT minting for CLI commands.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::{Context, Result};
7use chrono::Duration;
8use std::sync::Arc;
9use systemprompt_analytics::AnalyticsService;
10use systemprompt_analytics::repository::AnalyticsRepositories;
11use systemprompt_database::DbPool;
12use systemprompt_identifiers::{SessionId, SessionSource, UserId};
13use systemprompt_oauth::services::SessionCreationService;
14use systemprompt_traits::{AnalyticsProvider, SessionAnalytics, UserProvider};
15use systemprompt_users::{UserRepository, UserService};
16
17/// Lifetime of a CLI session row and of the admin token that names it. The two
18/// must agree, or the operator sees a mid-session 401.
19pub const DEFAULT_CLI_SESSION_HOURS: i64 = 24;
20
21// Why: the public `POST /oauth/session` endpoint must not accept a
22// caller-supplied `user_id` — doing so allows arbitrary admin-JWT issuance
23// against any known user UUID on a public route. The CLI is colocated with
24// the database and holds the JWT signing secret, so it mints session rows
25// (and the JWTs above) locally instead of round-tripping through the public
26// HTTP endpoint. It goes through `SessionCreationService` rather than the
27// repository so every `user_sessions` row in the deployment is written by one
28// code path, whatever minted it.
29pub async fn create_local_session_row(
30    db_pool: &DbPool,
31    user: &UserId,
32    ttl: Duration,
33) -> Result<SessionId> {
34    let repositories = AnalyticsRepositories::new(db_pool)
35        .context("Failed to construct analytics repositories")?;
36    let analytics: Arc<dyn AnalyticsProvider> =
37        Arc::new(AnalyticsService::new(None, None, &repositories));
38    let user_repository =
39        Arc::new(UserRepository::new(db_pool).context("Failed to construct user repository")?);
40    let users: Arc<dyn UserProvider> = Arc::new(UserService::new(user_repository));
41
42    SessionCreationService::new(analytics, users)
43        .create_authenticated_session_with_ttl(
44            user,
45            &SessionAnalytics::default(),
46            SessionSource::Cli,
47            ttl,
48        )
49        .await
50        .context("Failed to insert CLI session row")
51}