@@ -15,6 +15,8 @@ use crate::rpc_types::HealthStatus;
1515type JsonRpcResult = Result < serde_json:: Value , JsonRpcError > ;
1616
1717/// Returns health status with uptime and error count.
18+ ///
19+ /// Wire Standard L1: includes `"status": "alive"` for biomeOS liveness probes.
1820#[ expect(
1921 clippy:: unused_async,
2022 reason = "handler signature requires async for uniform dispatch"
@@ -39,8 +41,13 @@ pub(crate) async fn health(
3941 error_count : error_count_val,
4042 resource_utilization : 0.0 ,
4143 } ;
42- serde_json:: to_value ( status)
43- . map_err ( |e| JsonRpcError :: internal_error ( format ! ( "Serialization error: {e}" ) ) )
44+ let mut value = serde_json:: to_value ( status)
45+ . map_err ( |e| JsonRpcError :: internal_error ( format ! ( "Serialization error: {e}" ) ) ) ?;
46+ // Wire Standard L1: biomeOS probes expect "status": "alive"
47+ if let Some ( obj) = value. as_object_mut ( ) {
48+ obj. insert ( "status" . into ( ) , serde_json:: Value :: String ( "alive" . into ( ) ) ) ;
49+ }
50+ Ok ( value)
4451}
4552
4653/// Returns version and protocol information.
@@ -60,30 +67,31 @@ pub(crate) async fn version_info(version: &str) -> JsonRpcResult {
6067 Ok ( serde_json:: json!( info) )
6168}
6269
63- /// Returns discovered capabilities including semantic methods.
64- #[ expect(
65- clippy:: unused_async,
66- reason = "handler signature requires async for uniform dispatch"
67- ) ]
68- pub ( crate ) async fn discover_capabilities (
69- semantic_registry : & SemanticMethodRegistry ,
70- version : & str ,
71- ) -> JsonRpcResult {
70+ /// Builds the sorted flat methods list from direct routes + semantic registry.
71+ fn all_callable_methods ( semantic_registry : & SemanticMethodRegistry ) -> Vec < & str > {
7272 let semantic_methods: Vec < & str > = semantic_registry. semantic_names ( ) . into_iter ( ) . collect ( ) ;
7373
74- let mut direct_methods = vec ! [
74+ let mut methods = vec ! [
75+ "capabilities.list" ,
7576 "identity.get" ,
7677 "health.liveness" ,
7778 "health.readiness" ,
79+ "health.check" ,
7880 "toadstool.health" ,
7981 "toadstool.version" ,
82+ "toadstool.submit_workload" ,
83+ "toadstool.query_status" ,
84+ "toadstool.cancel_workload" ,
85+ "toadstool.list_workloads" ,
8086 "toadstool.query_capabilities" ,
8187 "toadstool.resources.estimate" ,
8288 "toadstool.resources.validate_availability" ,
8389 "toadstool.resources.suggest_optimizations" ,
8490 "resources.estimate" ,
8591 "resources.validate_availability" ,
8692 "resources.suggest_optimizations" ,
93+ "ai.local_inference" ,
94+ "ai.local_execute" ,
8795 "compute.health" ,
8896 "compute.version" ,
8997 "compute.capabilities" ,
@@ -98,10 +106,21 @@ pub(crate) async fn discover_capabilities(
98106 "compute.dispatch.result" ,
99107 "compute.dispatch.forward" ,
100108 "compute.dispatch.capabilities" ,
109+ "compute.hardware.observe" ,
110+ "compute.hardware.distill" ,
111+ "compute.hardware.apply" ,
112+ "compute.hardware.share_recipe" ,
113+ "compute.hardware.auto_init" ,
114+ "compute.hardware.auto_init_all" ,
115+ "compute.hardware.status" ,
116+ "compute.hardware.vfio_devices" ,
117+ "compute.performance_surface.report" ,
118+ "compute.performance_surface.query" ,
119+ "compute.performance_surface.list" ,
120+ "compute.route.multi_unit" ,
101121 "gpu.query_info" ,
102122 "gpu.query_memory" ,
103123 "gpu.query_telemetry" ,
104- "provenance.query" ,
105124 "gate.update" ,
106125 "gate.remove" ,
107126 "gate.list" ,
@@ -112,37 +131,130 @@ pub(crate) async fn discover_capabilities(
112131 "transport.open" ,
113132 "transport.stream" ,
114133 "transport.status" ,
115- "compute.hardware.observe" ,
116- "compute.hardware.distill" ,
117- "compute.hardware.apply" ,
118- "compute.hardware.share_recipe" ,
119- "compute.hardware.auto_init" ,
120- "compute.hardware.auto_init_all" ,
121- "compute.hardware.status" ,
122- "compute.hardware.vfio_devices" ,
134+ "shader.dispatch" ,
135+ "ember.list" ,
136+ "ember.status" ,
137+ "provenance.query" ,
123138 ] ;
124139
125140 for m in & semantic_methods {
126- if !direct_methods . contains ( m) {
127- direct_methods . push ( m) ;
141+ if !methods . contains ( m) {
142+ methods . push ( m) ;
128143 }
129144 }
130- direct_methods. sort_unstable ( ) ;
145+ methods. sort_unstable ( ) ;
146+ methods
147+ }
131148
132- let capabilities = serde_json:: json!( {
149+ /// Wire Standard L2 `capabilities.list` response.
150+ ///
151+ /// Returns `{primal, version, methods, provided_capabilities}` per
152+ /// `CAPABILITY_WIRE_STANDARD.md` v1.0.
153+ #[ expect(
154+ clippy:: unused_async,
155+ reason = "handler signature requires async for uniform dispatch"
156+ ) ]
157+ pub ( crate ) async fn capabilities_list (
158+ semantic_registry : & SemanticMethodRegistry ,
159+ version : & str ,
160+ ) -> JsonRpcResult {
161+ let methods = all_callable_methods ( semantic_registry) ;
162+
163+ Ok ( serde_json:: json!( {
164+ "primal" : toadstool_common:: constants:: PRIMAL_NAME ,
165+ "version" : version,
166+ "methods" : methods,
167+ "provided_capabilities" : [
168+ {
169+ "type" : "compute" ,
170+ "methods" : [ "submit" , "status" , "result" , "cancel" , "list" ,
171+ "dispatch.submit" , "dispatch.status" , "dispatch.result" ,
172+ "dispatch.forward" , "dispatch.capabilities" ,
173+ "hardware.observe" , "hardware.distill" , "hardware.apply" ,
174+ "hardware.share_recipe" , "hardware.auto_init" ,
175+ "hardware.auto_init_all" , "hardware.status" ,
176+ "hardware.vfio_devices" ,
177+ "performance_surface.report" , "performance_surface.query" ,
178+ "performance_surface.list" , "route.multi_unit" ,
179+ "health" , "version" , "capabilities" , "discover_capabilities" ] ,
180+ "version" : version,
181+ "description" : "GPU job queue, hardware dispatch, and performance routing"
182+ } ,
183+ {
184+ "type" : "toadstool" ,
185+ "methods" : [ "submit_workload" , "query_status" , "cancel_workload" ,
186+ "list_workloads" , "query_capabilities" , "health" , "version" ,
187+ "resources.estimate" , "resources.validate_availability" ,
188+ "resources.suggest_optimizations" ] ,
189+ "version" : version,
190+ "description" : "High-level workload executor (multi-runtime)"
191+ } ,
192+ {
193+ "type" : "gpu" ,
194+ "methods" : [ "query_info" , "query_memory" , "query_telemetry" ] ,
195+ "description" : "GPU hardware info and telemetry"
196+ } ,
197+ {
198+ "type" : "gate" ,
199+ "methods" : [ "update" , "remove" , "list" , "route" ] ,
200+ "description" : "Distributed cross-gate routing"
201+ } ,
202+ {
203+ "type" : "transport" ,
204+ "methods" : [ "discover" , "list" , "route" , "open" , "stream" , "status" ] ,
205+ "description" : "Hardware transport (DRM, V4L2, serial)"
206+ } ,
207+ {
208+ "type" : "shader" ,
209+ "methods" : [ "dispatch" ] ,
210+ "description" : "Sovereign shader dispatch (VFIO/DRM passthrough)"
211+ } ,
212+ {
213+ "type" : "ember" ,
214+ "methods" : [ "list" , "status" ] ,
215+ "description" : "glowPlug/ember GPU device lifecycle"
216+ }
217+ ] ,
218+ "consumed_capabilities" : [
219+ "security.sign" ,
220+ "security.verify" ,
221+ "storage.artifact.store" ,
222+ "storage.artifact.retrieve" ,
223+ "coordination.register" ,
224+ "coordination.discover"
225+ ] ,
226+ "protocol" : "jsonrpc-2.0" ,
227+ "transport" : [ "uds" , "tcp" ]
228+ } ) )
229+ }
230+
231+ /// Returns discovered capabilities including semantic methods.
232+ ///
233+ /// Legacy `compute.discover_capabilities` — returns node capabilities
234+ /// and merged method list.
235+ #[ expect(
236+ clippy:: unused_async,
237+ reason = "handler signature requires async for uniform dispatch"
238+ ) ]
239+ pub ( crate ) async fn discover_capabilities (
240+ semantic_registry : & SemanticMethodRegistry ,
241+ version : & str ,
242+ ) -> JsonRpcResult {
243+ let methods = all_callable_methods ( semantic_registry) ;
244+
245+ Ok ( serde_json:: json!( {
133246 "node_capabilities" : [
134247 "compute" , "workload" , "orchestration" ,
135248 "gpu" , "wasm" , "container" , "hardware_transport" ,
136249 "shader_dispatch" , "hardware_learning"
137250 ] ,
138- "methods" : direct_methods ,
251+ "methods" : methods ,
139252 "version" : version,
140253 "primal" : toadstool_common:: constants:: PRIMAL_NAME
141- } ) ;
142- Ok ( capabilities)
254+ } ) )
143255}
144256
145- /// Returns primal identity per `CAPABILITY_BASED_DISCOVERY_STANDARD.md`.
257+ /// Returns primal identity per Wire Standard L2 + `CAPABILITY_BASED_DISCOVERY_STANDARD.md`.
146258///
147259/// Every primal MUST implement `identity.get` so orchestrators and peers
148260/// can discover name, version, capabilities, and protocol.
@@ -170,6 +282,8 @@ pub(crate) async fn identity_get(
170282 Ok ( serde_json:: json!( {
171283 "primal" : toadstool_common:: constants:: PRIMAL_NAME ,
172284 "version" : version,
285+ "domain" : "compute" ,
286+ "license" : "AGPL-3.0-or-later" ,
173287 "protocol" : "JSON-RPC 2.0" ,
174288 "capabilities" : capabilities,
175289 "methods" : semantic_methods,
@@ -221,20 +335,25 @@ mod tests {
221335 use super :: * ;
222336
223337 #[ tokio:: test]
224- async fn health_includes_version_uptime_and_error_count ( ) {
338+ async fn health_includes_version_uptime_error_count_and_wire_status ( ) {
225339 let ver = "unit-test-9.9.9" ;
226340 let start = Instant :: now ( )
227341 . checked_sub ( Duration :: from_secs ( 2 ) )
228342 . expect ( "instant" ) ;
229343 let errors = AtomicU64 :: new ( 7 ) ;
230344 let v = health ( ver, start, & errors) . await . expect ( "health ok" ) ;
231345 assert_eq ! ( v[ "healthy" ] , true ) ;
346+ assert_eq ! (
347+ v[ "status" ] , "alive" ,
348+ "Wire Standard L1: status must be 'alive'"
349+ ) ;
232350 assert_eq ! ( v[ "version" ] , ver) ;
233351 assert ! ( v[ "uptime_secs" ] . as_u64( ) . unwrap( ) >= 2 ) ;
234352 assert_eq ! ( v[ "error_count" ] , 7 ) ;
235353 errors. fetch_add ( 1 , Ordering :: Relaxed ) ;
236354 let v2 = health ( ver, start, & errors) . await . expect ( "health ok" ) ;
237355 assert_eq ! ( v2[ "error_count" ] , 8 ) ;
356+ assert_eq ! ( v2[ "status" ] , "alive" ) ;
238357 }
239358
240359 #[ tokio:: test]
@@ -271,11 +390,69 @@ mod tests {
271390 }
272391
273392 #[ tokio:: test]
274- async fn identity_get_lists_core_capabilities_and_methods ( ) {
393+ async fn capabilities_list_returns_wire_standard_envelope ( ) {
394+ let reg = SemanticMethodRegistry :: new ( ) ;
395+ let cap = capabilities_list ( & reg, "wire-1.0" )
396+ . await
397+ . expect ( "capabilities_list" ) ;
398+ assert_eq ! ( cap[ "primal" ] , toadstool_common:: constants:: PRIMAL_NAME ) ;
399+ assert_eq ! ( cap[ "version" ] , "wire-1.0" ) ;
400+
401+ let methods = cap[ "methods" ] . as_array ( ) . expect ( "methods array" ) ;
402+ assert ! ( !methods. is_empty( ) ) ;
403+ let strs: Vec < & str > = methods. iter ( ) . map ( |m| m. as_str ( ) . unwrap ( ) ) . collect ( ) ;
404+ let mut sorted = strs. clone ( ) ;
405+ sorted. sort_unstable ( ) ;
406+ assert_eq ! ( strs, sorted, "methods must be sorted" ) ;
407+
408+ assert ! (
409+ strs. contains( & "capabilities.list" ) ,
410+ "must advertise capabilities.list"
411+ ) ;
412+ assert ! (
413+ strs. contains( & "health.liveness" ) ,
414+ "must advertise health.liveness"
415+ ) ;
416+ assert ! (
417+ strs. contains( & "identity.get" ) ,
418+ "must advertise identity.get"
419+ ) ;
420+ assert ! (
421+ strs. contains( & "compute.submit" ) ,
422+ "must advertise compute.submit"
423+ ) ;
424+ assert ! (
425+ strs. contains( & "shader.dispatch" ) ,
426+ "must advertise shader.dispatch"
427+ ) ;
428+ assert ! ( strs. contains( & "ember.list" ) , "must advertise ember.list" ) ;
429+
430+ let groups = cap[ "provided_capabilities" ]
431+ . as_array ( )
432+ . expect ( "provided_capabilities" ) ;
433+ assert ! ( groups. len( ) >= 5 , "should have multiple capability groups" ) ;
434+ let group_types: Vec < & str > = groups. iter ( ) . map ( |g| g[ "type" ] . as_str ( ) . unwrap ( ) ) . collect ( ) ;
435+ assert ! ( group_types. contains( & "compute" ) ) ;
436+ assert ! ( group_types. contains( & "toadstool" ) ) ;
437+ assert ! ( group_types. contains( & "gpu" ) ) ;
438+ assert ! ( group_types. contains( & "transport" ) ) ;
439+ assert ! ( group_types. contains( & "shader" ) ) ;
440+
441+ assert ! ( cap[ "consumed_capabilities" ] . as_array( ) . is_some( ) ) ;
442+ assert_eq ! ( cap[ "protocol" ] , "jsonrpc-2.0" ) ;
443+ }
444+
445+ #[ tokio:: test]
446+ async fn identity_get_includes_domain_and_license ( ) {
275447 let reg = SemanticMethodRegistry :: new ( ) ;
276448 let id = identity_get ( "id-ver" , & reg) . await . expect ( "identity_get" ) ;
277449 assert_eq ! ( id[ "primal" ] , toadstool_common:: constants:: PRIMAL_NAME ) ;
278450 assert_eq ! ( id[ "version" ] , "id-ver" ) ;
451+ assert_eq ! ( id[ "domain" ] , "compute" , "Wire Standard L2: domain field" ) ;
452+ assert_eq ! (
453+ id[ "license" ] , "AGPL-3.0-or-later" ,
454+ "Wire Standard L2: license field"
455+ ) ;
279456 assert_eq ! ( id[ "protocol" ] , "JSON-RPC 2.0" ) ;
280457 assert_eq ! ( id[ "transport" ] , "unix-socket" ) ;
281458 let sock = id[ "socket_name" ] . as_str ( ) . expect ( "socket_name" ) ;
0 commit comments