@@ -25,6 +25,12 @@ pub struct WasmModuleMetadata {
2525 /// Network policy for WASM execution
2626 #[ serde( default ) ]
2727 pub network_policy : NetworkPolicy ,
28+ /// Restartable configuration identifier
29+ #[ serde( default ) ]
30+ pub restart_id : Option < String > ,
31+ /// Configuration version for hot-restarts
32+ #[ serde( default ) ]
33+ pub config_version : u64 ,
2834}
2935
3036impl WasmModuleMetadata {
@@ -39,6 +45,8 @@ impl WasmModuleMetadata {
3945 module_location,
4046 entrypoint,
4147 network_policy,
48+ restart_id : None ,
49+ config_version : 0 ,
4250 }
4351 }
4452}
@@ -59,6 +67,12 @@ pub struct ChallengeEntry {
5967 /// WASM module metadata
6068 #[ serde( default ) ]
6169 pub wasm_module : Option < WasmModuleMetadata > ,
70+ /// Restartable configuration identifier
71+ #[ serde( default ) ]
72+ pub restart_id : Option < String > ,
73+ /// Configuration version for hot-restarts
74+ #[ serde( default ) ]
75+ pub config_version : u64 ,
6276 /// Current lifecycle state
6377 pub lifecycle_state : LifecycleState ,
6478 /// Health status
@@ -81,6 +95,8 @@ impl ChallengeEntry {
8195 docker_image,
8296 endpoint : None ,
8397 wasm_module : None ,
98+ restart_id : None ,
99+ config_version : 0 ,
84100 lifecycle_state : LifecycleState :: Registered ,
85101 health_status : HealthStatus :: Unknown ,
86102 registered_at : now,
@@ -297,6 +313,58 @@ impl ChallengeRegistry {
297313 Ok ( old_version)
298314 }
299315
316+ /// Update restart configuration metadata
317+ pub fn update_restart_config (
318+ & self ,
319+ id : & ChallengeId ,
320+ restart_id : Option < String > ,
321+ config_version : u64 ,
322+ ) -> RegistryResult < ( Option < String > , u64 ) > {
323+ let mut challenges = self . challenges . write ( ) ;
324+ let registered = challenges
325+ . get_mut ( id)
326+ . ok_or_else ( || RegistryError :: ChallengeNotFound ( id. to_string ( ) ) ) ?;
327+
328+ let previous_restart_id = registered. entry . restart_id . clone ( ) ;
329+ let previous_config_version = registered. entry . config_version ;
330+
331+ let restart_required = self . lifecycle . restart_required (
332+ previous_restart_id. as_deref ( ) ,
333+ restart_id. as_deref ( ) ,
334+ previous_config_version,
335+ config_version,
336+ ) ;
337+
338+ registered. entry . restart_id = restart_id. clone ( ) ;
339+ registered. entry . config_version = config_version;
340+ registered. entry . updated_at = chrono:: Utc :: now ( ) . timestamp_millis ( ) ;
341+
342+ if let Some ( wasm_module) = registered. entry . wasm_module . as_mut ( ) {
343+ wasm_module. restart_id = restart_id. clone ( ) ;
344+ wasm_module. config_version = config_version;
345+ }
346+
347+ if restart_required {
348+ info ! (
349+ challenge_id = %id,
350+ previous_restart_id = ?previous_restart_id,
351+ new_restart_id = ?restart_id,
352+ previous_config_version = previous_config_version,
353+ new_config_version = config_version,
354+ "Challenge restart configuration updated"
355+ ) ;
356+ self . emit_event ( LifecycleEvent :: Restarted {
357+ challenge_id : * id,
358+ previous_restart_id : previous_restart_id. clone ( ) ,
359+ new_restart_id : restart_id,
360+ previous_config_version,
361+ new_config_version : config_version,
362+ } ) ;
363+ }
364+
365+ Ok ( ( previous_restart_id, previous_config_version) )
366+ }
367+
300368 /// Get state store for a challenge
301369 pub fn state_store ( & self , id : & ChallengeId ) -> Option < Arc < StateStore > > {
302370 self . challenges
@@ -345,7 +413,6 @@ impl Default for ChallengeRegistry {
345413#[ cfg( test) ]
346414mod tests {
347415 use super :: * ;
348-
349416 #[ test]
350417 fn test_register_challenge ( ) {
351418 let registry = ChallengeRegistry :: new ( ) ;
@@ -446,11 +513,40 @@ mod tests {
446513 assert_eq ! ( challenge. entry. version, ChallengeVersion :: new( 1 , 1 , 0 ) ) ;
447514 }
448515
516+ #[ test]
517+ fn test_update_restart_config ( ) {
518+ let registry = ChallengeRegistry :: new ( ) ;
519+ let entry = ChallengeEntry :: new (
520+ "test" . to_string ( ) ,
521+ ChallengeVersion :: new ( 1 , 0 , 0 ) ,
522+ "test:latest" . to_string ( ) ,
523+ )
524+ . with_wasm_module ( WasmModuleMetadata :: new (
525+ "hash" . to_string ( ) ,
526+ "module.wasm" . to_string ( ) ,
527+ "evaluate" . to_string ( ) ,
528+ NetworkPolicy :: default ( ) ,
529+ ) ) ;
530+
531+ let id = registry. register ( entry) . unwrap ( ) ;
532+ let previous = registry
533+ . update_restart_config ( & id, Some ( "restart-1" . to_string ( ) ) , 1 )
534+ . unwrap ( ) ;
535+
536+ assert_eq ! ( previous, ( None , 0 ) ) ;
537+
538+ let challenge = registry. get ( & id) . unwrap ( ) ;
539+ assert_eq ! ( challenge. entry. restart_id, Some ( "restart-1" . to_string( ) ) ) ;
540+ assert_eq ! ( challenge. entry. config_version, 1 ) ;
541+ let wasm_module = challenge. entry . wasm_module . unwrap ( ) ;
542+ assert_eq ! ( wasm_module. restart_id, Some ( "restart-1" . to_string( ) ) ) ;
543+ assert_eq ! ( wasm_module. config_version, 1 ) ;
544+ }
545+
449546 #[ test]
450547 fn test_list_active ( ) {
451548 let registry = ChallengeRegistry :: new ( ) ;
452549
453- // Register two challenges
454550 let entry1 = ChallengeEntry :: new (
455551 "active" . to_string ( ) ,
456552 ChallengeVersion :: new ( 1 , 0 , 0 ) ,
@@ -465,7 +561,6 @@ mod tests {
465561 let id1 = registry. register ( entry1) . unwrap ( ) ;
466562 registry. register ( entry2) . unwrap ( ) ;
467563
468- // Make first one active
469564 registry
470565 . update_state ( & id1, LifecycleState :: Running )
471566 . unwrap ( ) ;
0 commit comments