feat: display DEFCON posture in banner + prompt
Reads BASCULE_DEFCON_LEVEL from env. At DEFCON <5: Banner: DEFCON level + label (RESTRICTED/CRITICAL/LOCKDOWN) + reason Prompt: [restricted] at DEFCON 3, [DEFCON] at ≤2 DEFCON 5 (peacetime): no DEFCON line in banner, normal prompt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3c4042ce8e
commit
02bcd58c99
2 changed files with 46 additions and 6 deletions
|
|
@ -162,6 +162,25 @@ fn print_banner(session: &SessionState) {
|
|||
_ => session.risk_level.clone(),
|
||||
},
|
||||
"║".bright_blue());
|
||||
|
||||
// DEFCON line — only shown when not peacetime
|
||||
if session.defcon_level < 5 {
|
||||
let defcon_label = match session.defcon_level {
|
||||
1 => "LOCKDOWN".red().to_string(),
|
||||
2 => "CRITICAL".red().to_string(),
|
||||
3 => "RESTRICTED".yellow().to_string(),
|
||||
4 => "ELEVATED".yellow().to_string(),
|
||||
_ => "PEACETIME".green().to_string(),
|
||||
};
|
||||
println!("{} DEFCON: {:<44}{}", "║".bright_blue(),
|
||||
format!("{} — {}", session.defcon_level, defcon_label),
|
||||
"║".bright_blue());
|
||||
if let Some(ref reason) = session.defcon_reason {
|
||||
let truncated = if reason.len() > 42 { format!("{}...", &reason[..39]) } else { reason.clone() };
|
||||
println!("{} Reason: {:<44}{}", "║".bright_blue(), truncated, "║".bright_blue());
|
||||
}
|
||||
}
|
||||
|
||||
println!("{}", "╚══════════════════════════════════════════════════════════╝".bright_blue());
|
||||
println!();
|
||||
}
|
||||
|
|
@ -193,11 +212,18 @@ fn print_summary(session: &SessionState) {
|
|||
}
|
||||
|
||||
fn build_prompt(session: &SessionState) -> DefaultPrompt {
|
||||
let risk_indicator = match session.risk_level.as_str() {
|
||||
// DEFCON overrides the risk indicator when elevated
|
||||
let risk_indicator = if session.defcon_level <= 2 {
|
||||
"[DEFCON]"
|
||||
} else if session.defcon_level == 3 {
|
||||
"[restricted]"
|
||||
} else {
|
||||
match session.risk_level.as_str() {
|
||||
"baseline" | "standard" | "ungoverned" => "[governed]",
|
||||
"elevated" => "[elevated]",
|
||||
"high" | "critical" => "[HIGH]",
|
||||
_ => "[governed]",
|
||||
}
|
||||
};
|
||||
|
||||
let short_name = session.display_name.split('@').next().unwrap_or(&session.display_name);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ pub struct SessionState {
|
|||
pub principal: String,
|
||||
pub display_name: String,
|
||||
pub risk_level: String,
|
||||
pub defcon_level: i32,
|
||||
pub defcon_reason: Option<String>,
|
||||
pub started_at: chrono::DateTime<chrono::Utc>,
|
||||
pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub governed_count: u32,
|
||||
|
|
@ -40,12 +42,18 @@ impl SessionState {
|
|||
let display_name = std::env::var("BASCULE_DISPLAY_NAME")
|
||||
.unwrap_or_else(|_| display_name_from_did(&principal));
|
||||
|
||||
let defcon_level = std::env::var("BASCULE_DEFCON_LEVEL")
|
||||
.ok().and_then(|v| v.parse().ok()).unwrap_or(5);
|
||||
let defcon_reason = std::env::var("BASCULE_DEFCON_REASON").ok();
|
||||
|
||||
Self {
|
||||
ac_id: ac.context_id.clone(),
|
||||
corpus_cid: corpus_cid.to_string(),
|
||||
principal,
|
||||
display_name,
|
||||
risk_level: "standard".to_string(), // TODO: read from AC when broker embeds it
|
||||
risk_level: "standard".to_string(),
|
||||
defcon_level,
|
||||
defcon_reason,
|
||||
started_at: chrono::Utc::now(),
|
||||
expires_at,
|
||||
governed_count: 0,
|
||||
|
|
@ -60,12 +68,18 @@ impl SessionState {
|
|||
let principal = whoami();
|
||||
let display_name = std::env::var("BASCULE_DISPLAY_NAME")
|
||||
.unwrap_or_else(|_| principal.clone());
|
||||
let defcon_level = std::env::var("BASCULE_DEFCON_LEVEL")
|
||||
.ok().and_then(|v| v.parse().ok()).unwrap_or(5);
|
||||
let defcon_reason = std::env::var("BASCULE_DEFCON_REASON").ok();
|
||||
|
||||
Self {
|
||||
ac_id: "ungoverned".to_string(),
|
||||
corpus_cid: corpus_cid.to_string(),
|
||||
principal,
|
||||
display_name,
|
||||
risk_level: "ungoverned".to_string(),
|
||||
defcon_level,
|
||||
defcon_reason,
|
||||
started_at: chrono::Utc::now(),
|
||||
expires_at: None,
|
||||
governed_count: 0,
|
||||
|
|
|
|||
Loading…
Reference in a new issue