Skip to main content

bitrouter_core/auth/
claims.rs

1//! JWT claims types for the BitRouter authentication protocol.
2//!
3//! These types define the payload of a BitRouter JWT. The `iss` claim carries
4//! the signer's CAIP-10 account identifier (e.g.
5//! `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:<base58_pubkey>`), which the
6//! server uses for both signature verification and account resolution.
7
8use serde::{Deserialize, Serialize};
9
10/// JWT claims for BitRouter authentication tokens.
11///
12/// Tokens are self-signed by the account holder's web3 wallet key. The
13/// CAIP-10 address in `iss` is the sole identity — the token has zero
14/// knowledge of the underlying account ID or server-side state.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct BitrouterClaims {
17    /// CAIP-10 account identifier of the signer.
18    ///
19    /// Examples:
20    /// - `"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:DRpb..."`
21    /// - `"eip155:8453:0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"`
22    pub iss: String,
23
24    /// CAIP-2 chain identifier indicating which chain was used to sign.
25    ///
26    /// Examples: `"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"`, `"eip155:8453"`.
27    pub chain: String,
28
29    /// Issued-at UNIX timestamp (seconds since epoch).
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub iat: Option<u64>,
32
33    /// Expiration UNIX timestamp. Required for admin-scope tokens.
34    /// Long-lived API tokens may omit this, relying on budget exhaustion
35    /// or key rotation for invalidation.
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub exp: Option<u64>,
38
39    /// Authorization scope granted by this token.
40    pub scope: TokenScope,
41
42    /// Optional allowlist of model name patterns this token may access.
43    /// When `None`, all models configured on the server are accessible.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub models: Option<Vec<String>>,
46
47    /// Budget limit in micro USD (1 USD = 1,000,000 μUSD).
48    /// Matches on-chain stablecoin precision (6 decimals).
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub budget: Option<u64>,
51
52    /// Whether the budget applies per-session or per-account.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub budget_scope: Option<BudgetScope>,
55
56    /// The range over which the budget is measured.
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub budget_range: Option<BudgetRange>,
59}
60
61/// Token authorization scope.
62///
63/// - `Admin`: Account management operations (rotate key, manage sessions).
64///   Scoped to the caller's own account — NOT global server admin.
65/// - `Api`: LLM inference endpoints only.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "lowercase")]
68pub enum TokenScope {
69    Admin,
70    Api,
71}
72
73/// Budget scope — determines what the budget limit applies to.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "lowercase")]
76pub enum BudgetScope {
77    /// Budget applies independently to each chat session.
78    Session,
79    /// Budget applies to the entire account across all sessions.
80    Account,
81}
82
83/// Budget range — the window over which the budget is measured.
84#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(tag = "type", rename_all = "lowercase")]
86pub enum BudgetRange {
87    /// Budget covers the next N conversation rounds.
88    Rounds { count: u32 },
89    /// Budget covers a time period (in seconds).
90    Duration { seconds: u64 },
91}