@@ -34,7 +34,7 @@ use tracing_subscriber::FmtSubscriber;
3434use wharf_core:: config:: { ConfigLoader , YachtAgentConfig } ;
3535use wharf_core:: crypto:: {
3636 self , HybridKeypair , HybridPublicKey , generate_hybrid_keypair, hybrid_public_key,
37- verify_hybrid , serialize_public_key,
37+ verify_with_scheme , serialize_public_key, SignatureScheme ,
3838 serialize_keypair_raw, deserialize_keypair_raw,
3939} ;
4040use wharf_core:: db_policy:: { DatabasePolicy , PolicyEngine , QueryAction } ;
@@ -137,6 +137,10 @@ struct Args {
137137 #[ arg( long, default_value_t = true , env = "METRICS_ENABLED" ) ]
138138 metrics_enabled : bool ,
139139
140+ /// Signature scheme: ml-dsa-87-only (default, production-safe) or hybrid (requires ed448 audit)
141+ #[ arg( long, default_value = "ml-dsa-87-only" , env = "SIGNATURE_SCHEME" ) ]
142+ signature_scheme : String ,
143+
140144 /// Enable verbose logging
141145 #[ arg( short, long, action = clap:: ArgAction :: Count ) ]
142146 verbose : u8 ,
@@ -179,6 +183,9 @@ struct AgentState {
179183 /// Site root for integrity verification
180184 site_root : Option < String > ,
181185
186+ /// Signature scheme for mooring verification
187+ signature_scheme : SignatureScheme ,
188+
182189 /// Statistics
183190 queries_allowed : u64 ,
184191 queries_blocked : u64 ,
@@ -188,7 +195,7 @@ struct AgentState {
188195}
189196
190197impl AgentState {
191- fn new ( key_store_dir : Option < & str > ) -> Self {
198+ fn new ( key_store_dir : Option < & str > , signature_scheme : SignatureScheme ) -> Self {
192199 // Load or generate yacht keypair with persistence
193200 let ( keypair, pubkey) = Self :: load_or_generate_keypair ( key_store_dir) ;
194201
@@ -203,6 +210,7 @@ impl AgentState {
203210 yacht_pubkey : pubkey,
204211 last_mooring_time : None ,
205212 site_root : None ,
213+ signature_scheme,
206214 queries_allowed : 0 ,
207215 queries_blocked : 0 ,
208216 queries_audited : 0 ,
@@ -453,8 +461,15 @@ async fn main() -> anyhow::Result<()> {
453461 error ! ( "WARNING: No firewall protection active!" ) ;
454462 }
455463
464+ // Parse signature scheme from CLI arg
465+ let signature_scheme = match args. signature_scheme . as_str ( ) {
466+ "hybrid" => SignatureScheme :: Hybrid ,
467+ _ => SignatureScheme :: MlDsa87Only ,
468+ } ;
469+ info ! ( "Signature scheme: {:?}" , signature_scheme) ;
470+
456471 // Initialize shared state with persistent keypair
457- let mut agent_state = AgentState :: new ( config. key_store_dir . as_deref ( ) ) ;
472+ let mut agent_state = AgentState :: new ( config. key_store_dir . as_deref ( ) , signature_scheme ) ;
458473 agent_state. site_root = config. site_root . clone ( ) ;
459474 let state = Arc :: new ( RwLock :: new ( agent_state) ) ;
460475
@@ -793,8 +808,8 @@ async fn mooring_init(
793808 Ok ( wharf_pk) => {
794809 match crypto:: deserialize_signature ( & request. signature ) {
795810 Ok ( sig) => {
796- if let Err ( e) = verify_hybrid ( & wharf_pk, & canonical, & sig) {
797- warn ! ( "Hybrid signature verification failed: {}" , e) ;
811+ if let Err ( e) = verify_with_scheme ( & wharf_pk, & canonical, & sig, state_guard . signature_scheme ) {
812+ warn ! ( "Signature verification failed: {}" , e) ;
798813 return Json ( serde_json:: json!( {
799814 "error" : "invalid_signature" ,
800815 "message" : format!( "Signature verification failed: {}" , e)
@@ -1326,7 +1341,7 @@ mod tests {
13261341 #[ tokio:: test]
13271342 async fn test_mooring_e2e_flow ( ) {
13281343 // Build the yacht-agent API on a random port
1329- let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None ) ) ) ;
1344+ let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None , SignatureScheme :: default ( ) ) ) ) ;
13301345 let app = build_api_router ( state. clone ( ) , true ) ;
13311346
13321347 let listener = tokio:: net:: TcpListener :: bind ( "127.0.0.1:0" )
@@ -1395,7 +1410,7 @@ mod tests {
13951410 /// Test that the health endpoint responds
13961411 #[ tokio:: test]
13971412 async fn test_health_endpoint ( ) {
1398- let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None ) ) ) ;
1413+ let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None , SignatureScheme :: default ( ) ) ) ) ;
13991414 let app = build_api_router ( state, false ) ;
14001415
14011416 let listener = tokio:: net:: TcpListener :: bind ( "127.0.0.1:0" )
@@ -1420,7 +1435,7 @@ mod tests {
14201435 /// Test that metrics endpoint returns real counters
14211436 #[ tokio:: test]
14221437 async fn test_metrics_endpoint ( ) {
1423- let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None ) ) ) ;
1438+ let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None , SignatureScheme :: default ( ) ) ) ) ;
14241439 {
14251440 let mut s = state. write ( ) . await ;
14261441 s. queries_allowed = 42 ;
@@ -1451,7 +1466,7 @@ mod tests {
14511466 /// Test that stats endpoint returns real counters
14521467 #[ tokio:: test]
14531468 async fn test_stats_endpoint ( ) {
1454- let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None ) ) ) ;
1469+ let state = Arc :: new ( RwLock :: new ( AgentState :: new ( None , SignatureScheme :: default ( ) ) ) ) ;
14551470 {
14561471 let mut s = state. write ( ) . await ;
14571472 s. queries_allowed = 10 ;
0 commit comments