Skip to content

Commit 84b6e09

Browse files
bb-connorclaude
andcommitted
feat!: hard cutover to clawdstrike naming and clean Decision API
BREAKING CHANGES: - Decision type now uses `status: 'allow' | 'warn' | 'deny'` only - Removed legacy boolean fields (allowed, denied, warn) - Removed toLegacyDecision/fromLegacyDecision helpers - Python package renamed from 'hush' to 'clawdstrike' - Rust env vars changed from HUSHD_* to CLAWDSTRIKE_* - Config paths changed from /etc/hushd to /etc/clawdstriked Changes: - Unified Decision type with single status field across all packages - Removed deprecated Python 'hush' shim module - Updated all test mocks to use new Decision format - Added clawdstriked deployment configs (Kubernetes, systemd, launchd) - Updated test env vars to CLAWDSTRIKE_* naming - Cleaned up adapter-core deprecation warnings All 424 tests passing: - TypeScript: 165 tests (SDK, adapter-core, all framework adapters) - Python: 133 tests - Rust: 258 tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b5092b9 commit 84b6e09

File tree

91 files changed

+1724
-300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1724
-300
lines changed

crates/hush-cli/src/hush_run.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ pub async fn cmd_run(
188188

189189
let forwarder = hushd_url.map(|url| {
190190
let token = hushd_token
191-
.or_else(|| std::env::var("HUSHD_ADMIN_KEY").ok())
192-
.or_else(|| std::env::var("HUSHD_API_KEY").ok());
191+
.or_else(|| std::env::var("CLAWDSTRIKE_ADMIN_KEY").ok())
192+
.or_else(|| std::env::var("CLAWDSTRIKE_API_KEY").ok());
193193
HushdForwarder::new(url, token)
194194
});
195195

crates/hush-cli/src/main.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ enum Commands {
168168
#[arg(long)]
169169
hushd_url: Option<String>,
170170

171-
/// Bearer token for hushd (if omitted, uses HUSHD_ADMIN_KEY or HUSHD_API_KEY when set)
171+
/// Bearer token for daemon (if omitted, uses CLAWDSTRIKE_ADMIN_KEY or CLAWDSTRIKE_API_KEY env vars)
172172
#[arg(long)]
173173
hushd_token: Option<String>,
174174

@@ -562,7 +562,7 @@ enum DaemonCommands {
562562
#[arg(default_value = "http://127.0.0.1:9876")]
563563
url: String,
564564

565-
/// Bearer token for authenticated daemons (if omitted, uses HUSHD_ADMIN_KEY or HUSHD_API_KEY when set)
565+
/// Bearer token for authenticated daemons (if omitted, uses CLAWDSTRIKE_ADMIN_KEY or CLAWDSTRIKE_API_KEY env vars)
566566
#[arg(long)]
567567
token: Option<String>,
568568
},
@@ -578,7 +578,7 @@ enum DaemonCommands {
578578
#[arg(default_value = "http://127.0.0.1:9876")]
579579
url: String,
580580

581-
/// Bearer token for authenticated daemons (if omitted, uses HUSHD_ADMIN_KEY or HUSHD_API_KEY when set)
581+
/// Bearer token for authenticated daemons (if omitted, uses CLAWDSTRIKE_ADMIN_KEY or CLAWDSTRIKE_API_KEY env vars)
582582
#[arg(long)]
583583
token: Option<String>,
584584
},
@@ -1797,8 +1797,8 @@ fn cmd_daemon(command: DaemonCommands, stdout: &mut dyn Write, stderr: &mut dyn
17971797
DaemonCommands::Stop { url, token } => {
17981798
let client = reqwest::blocking::Client::new();
17991799
let token = token
1800-
.or_else(|| std::env::var("HUSHD_ADMIN_KEY").ok())
1801-
.or_else(|| std::env::var("HUSHD_API_KEY").ok());
1800+
.or_else(|| std::env::var("CLAWDSTRIKE_ADMIN_KEY").ok())
1801+
.or_else(|| std::env::var("CLAWDSTRIKE_API_KEY").ok());
18021802

18031803
let _ = writeln!(stdout, "Requesting shutdown at {}...", url);
18041804

@@ -1902,8 +1902,8 @@ fn cmd_daemon(command: DaemonCommands, stdout: &mut dyn Write, stderr: &mut dyn
19021902
DaemonCommands::Reload { url, token } => {
19031903
let client = reqwest::blocking::Client::new();
19041904
let token = token
1905-
.or_else(|| std::env::var("HUSHD_ADMIN_KEY").ok())
1906-
.or_else(|| std::env::var("HUSHD_API_KEY").ok());
1905+
.or_else(|| std::env::var("CLAWDSTRIKE_ADMIN_KEY").ok())
1906+
.or_else(|| std::env::var("CLAWDSTRIKE_API_KEY").ok());
19071907

19081908
let mut req = client.post(format!("{}/api/v1/policy/reload", url));
19091909
if let Some(token) = token {
@@ -1976,7 +1976,7 @@ fn cmd_daemon(command: DaemonCommands, stdout: &mut dyn Write, stderr: &mut dyn
19761976
}
19771977

19781978
let _ = writeln!(stdout, "\nOr set environment variable:");
1979-
let _ = writeln!(stdout, " export HUSHD_API_KEY=\"{}\"", raw_key);
1979+
let _ = writeln!(stdout, " export CLAWDSTRIKE_API_KEY=\"{}\"", raw_key);
19801980
ExitCode::Ok
19811981
}
19821982
}

crates/hushd/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ path = "src/lib.rs"
1919
name = "hushd"
2020
path = "src/main.rs"
2121

22+
[[bin]]
23+
name = "clawdstriked"
24+
path = "src/main.rs"
25+
2226
[dependencies]
2327
hush-core.workspace = true
2428
hush-proxy.workspace = true

crates/hushd/src/auth/store.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ impl AuthStore {
3434

3535
/// Compute a stable hash of an API key token.
3636
///
37-
/// If `HUSHD_AUTH_PEPPER` is set (recommended), uses HMAC-SHA256 to make offline guessing
38-
/// attacks significantly harder if key hashes ever leak.
37+
/// If `CLAWDSTRIKE_AUTH_PEPPER` is set (recommended), uses HMAC-SHA256 to make
38+
/// offline guessing attacks significantly harder if key hashes ever leak.
3939
///
40-
/// If unset, falls back to raw SHA-256 for backward compatibility.
40+
/// If unset, falls back to raw SHA-256.
4141
pub fn hash_key(key: &str) -> String {
42-
let pepper = std::env::var("HUSHD_AUTH_PEPPER").ok();
42+
let pepper = std::env::var("CLAWDSTRIKE_AUTH_PEPPER").ok();
4343
let pepper = pepper.as_deref().filter(|s| !s.is_empty());
4444
hash_key_with_pepper(key, pepper.map(|p| p.as_bytes()))
4545
}

crates/hushd/src/config.rs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Configuration for hushd daemon
1+
//! Configuration for clawdstriked daemon
22
33
use serde::{Deserialize, Serialize};
44
use std::net::IpAddr;
@@ -892,10 +892,8 @@ fn default_ruleset() -> String {
892892
}
893893

894894
fn default_audit_db() -> PathBuf {
895-
dirs::data_local_dir()
896-
.unwrap_or_else(|| PathBuf::from("."))
897-
.join("hushd")
898-
.join("audit.db")
895+
let base = dirs::data_local_dir().unwrap_or_else(|| PathBuf::from("."));
896+
base.join("clawdstriked").join("audit.db")
899897
}
900898

901899
fn default_log_level() -> String {
@@ -1252,18 +1250,17 @@ impl Config {
12521250

12531251
/// Load from default locations or create default
12541252
pub fn load_default() -> anyhow::Result<Self> {
1255-
// Try standard config locations
12561253
let paths = [
1257-
PathBuf::from("/etc/hushd/config.yaml"),
1258-
PathBuf::from("/etc/hushd/config.toml"),
1254+
PathBuf::from("/etc/clawdstriked/config.yaml"),
1255+
PathBuf::from("/etc/clawdstriked/config.toml"),
12591256
dirs::config_dir()
1260-
.map(|d| d.join("hushd/config.yaml"))
1257+
.map(|d| d.join("clawdstriked/config.yaml"))
12611258
.unwrap_or_default(),
12621259
dirs::config_dir()
1263-
.map(|d| d.join("hushd/config.toml"))
1260+
.map(|d| d.join("clawdstriked/config.toml"))
12641261
.unwrap_or_default(),
1265-
PathBuf::from("./hushd.yaml"),
1266-
PathBuf::from("./hushd.toml"),
1262+
PathBuf::from("./clawdstriked.yaml"),
1263+
PathBuf::from("./clawdstriked.toml"),
12671264
];
12681265

12691266
let mut errors: Vec<(PathBuf, anyhow::Error)> = Vec::new();
@@ -1286,7 +1283,7 @@ impl Config {
12861283
}
12871284

12881285
if !errors.is_empty() {
1289-
let mut msg = String::from("Failed to load hushd config from existing file(s):\n");
1286+
let mut msg = String::from("Failed to load clawdstriked config from existing file(s):\n");
12901287
for (path, err) in errors {
12911288
msg.push_str(&format!(" - {}: {err}\n", path.display()));
12921289
}
@@ -1314,13 +1311,12 @@ impl Config {
13141311
pub async fn load_auth_store(&self) -> anyhow::Result<AuthStore> {
13151312
let store = AuthStore::new();
13161313

1317-
let has_pepper = std::env::var("HUSHD_AUTH_PEPPER")
1318-
.ok()
1319-
.as_deref()
1320-
.is_some_and(|v| !v.is_empty());
1314+
let has_pepper = std::env::var("CLAWDSTRIKE_AUTH_PEPPER")
1315+
.map(|v| !v.is_empty())
1316+
.unwrap_or(false);
13211317
if self.auth.enabled && !has_pepper {
13221318
tracing::warn!(
1323-
"Auth is enabled but HUSHD_AUTH_PEPPER is not set; API key hashing will use raw SHA-256"
1319+
"Auth is enabled but CLAWDSTRIKE_AUTH_PEPPER is not set; API key hashing will use raw SHA-256"
13241320
);
13251321
}
13261322

@@ -1504,15 +1500,15 @@ scopes = []
15041500

15051501
#[tokio::test]
15061502
async fn test_load_auth_store_expands_env_refs() -> anyhow::Result<()> {
1507-
std::env::set_var("HUSHD_TEST_API_KEY", "secret-from-env");
1503+
std::env::set_var("CLAWDSTRIKE_TEST_API_KEY", "secret-from-env");
15081504

15091505
let yaml = r#"
15101506
listen: "127.0.0.1:9876"
15111507
auth:
15121508
enabled: true
15131509
api_keys:
15141510
- name: "env"
1515-
key: "${HUSHD_TEST_API_KEY}"
1511+
key: "${CLAWDSTRIKE_TEST_API_KEY}"
15161512
scopes: ["check"]
15171513
"#;
15181514

crates/hushd/tests/common/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Common test utilities for hushd integration tests
1+
//! Common test utilities for clawdstriked integration tests
22
33
use std::net::TcpListener;
44
use std::net::{SocketAddr, TcpStream};
@@ -12,7 +12,7 @@ static TEST_DAEMON: OnceLock<Mutex<TestDaemon>> = OnceLock::new();
1212

1313
/// Get the daemon URL from environment or use default
1414
pub fn daemon_url() -> String {
15-
if let Ok(url) = std::env::var("HUSHD_TEST_URL") {
15+
if let Ok(url) = std::env::var("CLAWDSTRIKE_TEST_URL") {
1616
return url;
1717
}
1818

@@ -58,23 +58,23 @@ impl TestDaemon {
5858
let port = find_available_port();
5959
let url = format!("http://127.0.0.1:{}", port);
6060

61-
let test_dir = std::env::temp_dir().join(format!("hushd-test-{}", port));
61+
let test_dir = std::env::temp_dir().join(format!("clawdstriked-test-{}", port));
6262
std::fs::create_dir_all(&test_dir).expect("Failed to create test directory");
6363

6464
// Always isolate storage to the temp dir, regardless of caller config.
6565
config.listen = format!("127.0.0.1:{}", port);
6666
config.audit_db = test_dir.join("audit.db");
6767

68-
let config_path = test_dir.join("hushd.yaml");
68+
let config_path = test_dir.join("clawdstriked.yaml");
6969
let yaml = serde_yaml::to_string(&config).expect("Failed to serialize config");
7070
std::fs::write(&config_path, yaml).expect("Failed to write test config");
7171

72-
let daemon_path = std::env::var("HUSHD_BIN")
73-
.or_else(|_| std::env::var("CARGO_BIN_EXE_hushd"))
72+
let daemon_path = std::env::var("CLAWDSTRIKE_BIN")
73+
.or_else(|_| std::env::var("CARGO_BIN_EXE_clawdstriked"))
7474
.unwrap_or_else(|_| {
75-
option_env!("CARGO_BIN_EXE_hushd")
75+
option_env!("CARGO_BIN_EXE_clawdstriked")
7676
.map(ToString::to_string)
77-
.unwrap_or_else(|| "target/debug/hushd".to_string())
77+
.unwrap_or_else(|| "target/debug/clawdstriked".to_string())
7878
});
7979

8080
let process = Command::new(&daemon_path)

crates/hushd/tests/integration.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
//! Integration tests for hushd HTTP API
1+
//! Integration tests for clawdstriked HTTP API
22
//!
33
//! These tests require either:
4-
//! 1. A running daemon at HUSHD_TEST_URL (for local development)
5-
//! 2. The daemon binary at HUSHD_BIN (for CI, will spawn automatically)
4+
//! 1. A running daemon at CLAWDSTRIKE_TEST_URL (for local development)
5+
//! 2. The daemon binary at CLAWDSTRIKE_BIN (for CI, will spawn automatically)
66
77
#![allow(clippy::expect_used, clippy::unwrap_used)]
88

@@ -639,7 +639,7 @@ fn test_config_tracing_level() {
639639
}
640640

641641
// Auth tests - these require auth to be enabled on the daemon
642-
// Run with: HUSHD_TEST_AUTH_ENABLED=1 cargo test -p hushd --test integration
642+
// Run with: CLAWDSTRIKE_TEST_AUTH_ENABLED=1 cargo test -p hushd --test integration
643643

644644
#[tokio::test]
645645
#[ignore = "requires running daemon with auth enabled"]
@@ -662,7 +662,7 @@ async fn test_auth_required_without_token() {
662662
#[ignore = "requires running daemon with auth enabled"]
663663
async fn test_auth_with_valid_token() {
664664
let (client, url) = test_setup();
665-
let api_key = std::env::var("HUSHD_API_KEY").expect("HUSHD_API_KEY not set");
665+
let api_key = std::env::var("CLAWDSTRIKE_API_KEY").expect("CLAWDSTRIKE_API_KEY not set");
666666

667667
let resp = client
668668
.post(format!("{}/api/v1/check", url))
@@ -701,7 +701,7 @@ async fn test_auth_with_invalid_token() {
701701
#[ignore = "requires running daemon with auth enabled"]
702702
async fn test_admin_endpoint_requires_admin_scope() {
703703
let (client, url) = test_setup();
704-
let api_key = std::env::var("HUSHD_API_KEY").expect("HUSHD_API_KEY not set");
704+
let api_key = std::env::var("CLAWDSTRIKE_API_KEY").expect("CLAWDSTRIKE_API_KEY not set");
705705

706706
let resp = client
707707
.post(format!("{}/api/v1/policy/reload", url))

crates/hushd/tests/tls.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ PgOEzGSV8wm/fPgrYyiEqrM=
6161
"#;
6262

6363
fn daemon_path() -> String {
64-
std::env::var("HUSHD_BIN")
65-
.or_else(|_| std::env::var("CARGO_BIN_EXE_hushd"))
64+
std::env::var("CLAWDSTRIKE_BIN")
65+
.or_else(|_| std::env::var("CARGO_BIN_EXE_clawdstriked"))
6666
.unwrap_or_else(|_| {
67-
option_env!("CARGO_BIN_EXE_hushd")
67+
option_env!("CARGO_BIN_EXE_clawdstriked")
6868
.map(ToString::to_string)
69-
.unwrap_or_else(|| "target/debug/hushd".to_string())
69+
.unwrap_or_else(|| "target/debug/clawdstriked".to_string())
7070
})
7171
}
7272

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Kubernetes Deployment (hushd)
2+
3+
Minimal Kustomize manifests for running `hushd` in Kubernetes.
4+
5+
This is a **reference deployment**, not an operator.
6+
7+
## Apply (example)
8+
9+
```bash
10+
kubectl apply -k deploy/kubernetes/hushd
11+
kubectl -n hushd-system get pods
12+
kubectl -n hushd-system port-forward svc/hushd 9876:9876
13+
curl http://127.0.0.1:9876/health
14+
curl http://127.0.0.1:9876/metrics
15+
```
16+
17+
## Secrets
18+
19+
Edit `deploy/kubernetes/hushd/secret.yaml` before applying (demo values are placeholders).
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: clawdstriked-config
5+
data:
6+
config.yaml: |
7+
listen: "0.0.0.0:9876"
8+
ruleset: "default"
9+
audit_db: "/var/lib/clawdstriked/audit.db"
10+
log_level: "info"
11+
12+
auth:
13+
enabled: true
14+
api_keys:
15+
- name: "default"
16+
key: "${CLAWDSTRIKE_API_KEY}"
17+
scopes: ["check", "read"]
18+
- name: "admin"
19+
key: "${CLAWDSTRIKE_ADMIN_KEY}"
20+
scopes: ["*"]

0 commit comments

Comments
 (0)