openc3 7.3.0 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (499) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Guardfile +0 -0
  4. data/LICENSE.md +0 -0
  5. data/README.md +0 -0
  6. data/Rakefile +0 -0
  7. data/bin/openc3cli +138 -0
  8. data/bin/pipuninstall +0 -0
  9. data/bin/uvinstall +10 -3
  10. data/data/config/_array_params.yaml +0 -0
  11. data/data/config/_canvas_values.yaml +0 -0
  12. data/data/config/_graph_params.yaml +0 -0
  13. data/data/config/_id_items.yaml +0 -0
  14. data/data/config/_id_params.yaml +0 -0
  15. data/data/config/_interfaces.yaml +0 -0
  16. data/data/config/_items.yaml +0 -0
  17. data/data/config/_params.yaml +0 -0
  18. data/data/config/command.yaml +0 -0
  19. data/data/config/command_modifiers.yaml +2 -0
  20. data/data/config/command_telemetry.yaml +0 -0
  21. data/data/config/conversions.yaml +0 -0
  22. data/data/config/graph_settings.yaml +0 -0
  23. data/data/config/interface_modifiers.yaml +103 -1
  24. data/data/config/item_modifiers.yaml +0 -0
  25. data/data/config/microservice.yaml +5 -2
  26. data/data/config/param_item_modifiers.yaml +0 -0
  27. data/data/config/parameter_modifiers.yaml +0 -0
  28. data/data/config/plugins.yaml +0 -0
  29. data/data/config/processors.yaml +0 -0
  30. data/data/config/protocols.yaml +0 -0
  31. data/data/config/screen.yaml +0 -0
  32. data/data/config/settings.yaml +0 -0
  33. data/data/config/table_manager.yaml +0 -0
  34. data/data/config/table_parameter_modifiers.yaml +0 -0
  35. data/data/config/target.yaml +0 -0
  36. data/data/config/target_config.yaml +0 -0
  37. data/data/config/telemetry.yaml +0 -0
  38. data/data/config/telemetry_modifiers.yaml +2 -0
  39. data/data/config/tool.yaml +0 -0
  40. data/data/config/unknown.yaml +0 -0
  41. data/data/config/widgets.yaml +6 -0
  42. data/data/xtce_schemas/SpaceSystem_20180204.xsd +0 -0
  43. data/data/xtce_schemas/xml.xsd +0 -0
  44. data/ext/openc3/ext/array/array.c +0 -0
  45. data/ext/openc3/ext/array/extconf.rb +0 -0
  46. data/ext/openc3/ext/buffered_file/buffered_file.c +0 -0
  47. data/ext/openc3/ext/buffered_file/extconf.rb +0 -0
  48. data/ext/openc3/ext/burst_protocol/burst_protocol.c +6 -0
  49. data/ext/openc3/ext/burst_protocol/extconf.rb +0 -0
  50. data/ext/openc3/ext/config_parser/config_parser.c +0 -0
  51. data/ext/openc3/ext/config_parser/extconf.rb +0 -0
  52. data/ext/openc3/ext/crc/crc.c +0 -0
  53. data/ext/openc3/ext/crc/extconf.rb +0 -0
  54. data/ext/openc3/ext/openc3_io/extconf.rb +0 -0
  55. data/ext/openc3/ext/openc3_io/openc3_io.c +0 -0
  56. data/ext/openc3/ext/packet/extconf.rb +0 -0
  57. data/ext/openc3/ext/packet/packet.c +0 -0
  58. data/ext/openc3/ext/platform/extconf.rb +0 -0
  59. data/ext/openc3/ext/platform/platform.c +4 -1
  60. data/ext/openc3/ext/polynomial_conversion/extconf.rb +0 -0
  61. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +0 -0
  62. data/ext/openc3/ext/string/extconf.rb +0 -0
  63. data/ext/openc3/ext/string/string.c +0 -0
  64. data/ext/openc3/ext/structure/structure.c +49 -17
  65. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +0 -0
  66. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +0 -0
  67. data/ext/openc3/ext/telemetry/extconf.rb +0 -0
  68. data/ext/openc3/ext/telemetry/telemetry.c +0 -0
  69. data/lib/cosmos.rb +0 -0
  70. data/lib/cosmosc2.rb +0 -0
  71. data/lib/openc3/accessors/accessor.rb +0 -0
  72. data/lib/openc3/accessors/binary_accessor.rb +63 -9
  73. data/lib/openc3/accessors/cbor_accessor.rb +0 -0
  74. data/lib/openc3/accessors/form_accessor.rb +0 -0
  75. data/lib/openc3/accessors/html_accessor.rb +0 -0
  76. data/lib/openc3/accessors/http_accessor.rb +0 -0
  77. data/lib/openc3/accessors/json_accessor.rb +0 -0
  78. data/lib/openc3/accessors/template_accessor.rb +0 -0
  79. data/lib/openc3/accessors/xml_accessor.rb +0 -0
  80. data/lib/openc3/accessors.rb +0 -0
  81. data/lib/openc3/api/README.md +0 -0
  82. data/lib/openc3/api/api.rb +0 -0
  83. data/lib/openc3/api/authorized_api.rb +0 -0
  84. data/lib/openc3/api/calendar_api.rb +0 -0
  85. data/lib/openc3/api/cmd_api.rb +1 -1
  86. data/lib/openc3/api/config_api.rb +0 -0
  87. data/lib/openc3/api/interface_api.rb +0 -0
  88. data/lib/openc3/api/limits_api.rb +0 -0
  89. data/lib/openc3/api/metrics_api.rb +1 -1
  90. data/lib/openc3/api/offline_access_api.rb +1 -1
  91. data/lib/openc3/api/router_api.rb +0 -0
  92. data/lib/openc3/api/settings_api.rb +22 -18
  93. data/lib/openc3/api/stash_api.rb +1 -1
  94. data/lib/openc3/api/target_api.rb +0 -0
  95. data/lib/openc3/api/tlm_api.rb +4 -3
  96. data/lib/openc3/bridge/bridge.rb +0 -0
  97. data/lib/openc3/bridge/bridge_config.rb +0 -0
  98. data/lib/openc3/bridge/bridge_interface_thread.rb +0 -0
  99. data/lib/openc3/bridge/bridge_router_thread.rb +0 -0
  100. data/lib/openc3/ccsds/ccsds_packet.rb +0 -0
  101. data/lib/openc3/ccsds/ccsds_parser.rb +0 -0
  102. data/lib/openc3/config/config_parser.rb +78 -17
  103. data/lib/openc3/config/meta_config_parser.rb +0 -0
  104. data/lib/openc3/conversions/bit_reverse_conversion.rb +0 -0
  105. data/lib/openc3/conversions/conversion.rb +0 -0
  106. data/lib/openc3/conversions/generic_conversion.rb +0 -0
  107. data/lib/openc3/conversions/ip_read_conversion.rb +0 -0
  108. data/lib/openc3/conversions/ip_write_conversion.rb +0 -0
  109. data/lib/openc3/conversions/object_read_conversion.rb +0 -0
  110. data/lib/openc3/conversions/object_write_conversion.rb +0 -0
  111. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +0 -0
  112. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +0 -0
  113. data/lib/openc3/conversions/polynomial_conversion.rb +0 -0
  114. data/lib/openc3/conversions/processor_conversion.rb +0 -0
  115. data/lib/openc3/conversions/received_count_conversion.rb +0 -0
  116. data/lib/openc3/conversions/received_time_formatted_conversion.rb +0 -0
  117. data/lib/openc3/conversions/received_time_seconds_conversion.rb +0 -0
  118. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +0 -0
  119. data/lib/openc3/conversions/unix_time_conversion.rb +0 -0
  120. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +0 -0
  121. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +0 -0
  122. data/lib/openc3/conversions.rb +0 -0
  123. data/lib/openc3/core_ext/array.rb +0 -0
  124. data/lib/openc3/core_ext/binding.rb +0 -0
  125. data/lib/openc3/core_ext/class.rb +0 -0
  126. data/lib/openc3/core_ext/exception.rb +0 -0
  127. data/lib/openc3/core_ext/faraday.rb +0 -0
  128. data/lib/openc3/core_ext/file.rb +0 -0
  129. data/lib/openc3/core_ext/io.rb +0 -0
  130. data/lib/openc3/core_ext/kernel.rb +0 -0
  131. data/lib/openc3/core_ext/math.rb +0 -0
  132. data/lib/openc3/core_ext/matrix.rb +0 -0
  133. data/lib/openc3/core_ext/objectspace.rb +0 -0
  134. data/lib/openc3/core_ext/openc3_io.rb +0 -0
  135. data/lib/openc3/core_ext/range.rb +0 -0
  136. data/lib/openc3/core_ext/socket.rb +0 -0
  137. data/lib/openc3/core_ext/string.rb +0 -0
  138. data/lib/openc3/core_ext/stringio.rb +0 -0
  139. data/lib/openc3/core_ext/tempfile.rb +0 -0
  140. data/lib/openc3/core_ext/time.rb +0 -0
  141. data/lib/openc3/core_ext.rb +0 -0
  142. data/lib/openc3/ext/.keep +0 -0
  143. data/lib/openc3/interfaces/file_interface.rb +0 -0
  144. data/lib/openc3/interfaces/http_client_interface.rb +0 -0
  145. data/lib/openc3/interfaces/http_server_interface.rb +0 -0
  146. data/lib/openc3/interfaces/interface.rb +5 -4
  147. data/lib/openc3/interfaces/mqtt_interface.rb +0 -0
  148. data/lib/openc3/interfaces/mqtt_stream_interface.rb +2 -0
  149. data/lib/openc3/interfaces/protocols/burst_protocol.rb +27 -0
  150. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +0 -0
  151. data/lib/openc3/interfaces/protocols/cobs_protocol.rb +0 -0
  152. data/lib/openc3/interfaces/protocols/crc_protocol.rb +0 -0
  153. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +0 -0
  154. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +0 -0
  155. data/lib/openc3/interfaces/protocols/length_protocol.rb +7 -0
  156. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +7 -0
  157. data/lib/openc3/interfaces/protocols/protocol.rb +4 -4
  158. data/lib/openc3/interfaces/protocols/slip_protocol.rb +0 -0
  159. data/lib/openc3/interfaces/protocols/template_protocol.rb +2 -2
  160. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +0 -0
  161. data/lib/openc3/interfaces/serial_interface.rb +2 -0
  162. data/lib/openc3/interfaces/simulated_target_interface.rb +0 -0
  163. data/lib/openc3/interfaces/stream_interface.rb +0 -0
  164. data/lib/openc3/interfaces/tcpip_client_interface.rb +2 -0
  165. data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -0
  166. data/lib/openc3/interfaces/udp_interface.rb +16 -5
  167. data/lib/openc3/interfaces.rb +0 -0
  168. data/lib/openc3/io/buffered_file.rb +0 -0
  169. data/lib/openc3/io/io_multiplexer.rb +0 -0
  170. data/lib/openc3/io/json_api.rb +3 -2
  171. data/lib/openc3/io/json_api_object.rb +0 -0
  172. data/lib/openc3/io/json_drb.rb +20 -4
  173. data/lib/openc3/io/json_drb_object.rb +0 -0
  174. data/lib/openc3/io/json_drb_rack.rb +0 -0
  175. data/lib/openc3/io/json_rpc.rb +0 -0
  176. data/lib/openc3/io/posix_serial_driver.rb +0 -0
  177. data/lib/openc3/io/serial_driver.rb +0 -0
  178. data/lib/openc3/io/stderr.rb +0 -0
  179. data/lib/openc3/io/stdout.rb +0 -0
  180. data/lib/openc3/io/udp_sockets.rb +38 -8
  181. data/lib/openc3/io/win32_serial_driver.rb +1 -0
  182. data/lib/openc3/logs/buffered_packet_log_reader.rb +0 -0
  183. data/lib/openc3/logs/buffered_packet_log_writer.rb +0 -0
  184. data/lib/openc3/logs/log_writer.rb +0 -0
  185. data/lib/openc3/logs/packet_log_constants.rb +0 -0
  186. data/lib/openc3/logs/packet_log_reader.rb +23 -2
  187. data/lib/openc3/logs/packet_log_writer.rb +5 -0
  188. data/lib/openc3/logs/stream_log.rb +1 -1
  189. data/lib/openc3/logs/stream_log_pair.rb +0 -0
  190. data/lib/openc3/logs/text_log_writer.rb +0 -0
  191. data/lib/openc3/logs.rb +0 -0
  192. data/lib/openc3/microservices/cleanup_microservice.rb +0 -0
  193. data/lib/openc3/microservices/decom_common.rb +0 -0
  194. data/lib/openc3/microservices/decom_microservice.rb +0 -0
  195. data/lib/openc3/microservices/interface_decom_common.rb +2 -1
  196. data/lib/openc3/microservices/interface_microservice.rb +72 -25
  197. data/lib/openc3/microservices/log_microservice.rb +0 -0
  198. data/lib/openc3/microservices/microservice.rb +3 -3
  199. data/lib/openc3/microservices/multi_microservice.rb +1 -1
  200. data/lib/openc3/microservices/periodic_microservice.rb +2 -2
  201. data/lib/openc3/microservices/plugin_microservice.rb +0 -0
  202. data/lib/openc3/microservices/queue_microservice.rb +0 -0
  203. data/lib/openc3/microservices/router_microservice.rb +0 -0
  204. data/lib/openc3/microservices/scope_cleanup_microservice.rb +0 -0
  205. data/lib/openc3/microservices/text_log_microservice.rb +0 -0
  206. data/lib/openc3/migrations/20220420190000_log_stuff.rb +0 -0
  207. data/lib/openc3/migrations/20221202214600_add_target_names.rb +0 -0
  208. data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +0 -0
  209. data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +0 -0
  210. data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +0 -0
  211. data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +0 -0
  212. data/lib/openc3/migrations/20241208080001_no_trigger_group.rb +0 -0
  213. data/lib/openc3/migrations/20250108060000_news_feed.rb +0 -0
  214. data/lib/openc3/migrations/20250402000000_periodic_only_default.rb +0 -0
  215. data/lib/openc3/migrations/20260203000000_remove_store_id.rb +0 -0
  216. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +0 -0
  217. data/lib/openc3/migrations/20260701000000_updated_at_to_int.rb +0 -0
  218. data/lib/openc3/migrations/20260704000000_default_bridge.rb +38 -0
  219. data/lib/openc3/migrations/20260731000000_system_health_json.rb +11 -0
  220. data/lib/openc3/models/activity_model.rb +0 -0
  221. data/lib/openc3/models/auth_model.rb +35 -7
  222. data/lib/openc3/models/bridge_model.rb +133 -0
  223. data/lib/openc3/models/config_keyword_model.rb +63 -0
  224. data/lib/openc3/models/cvt_model.rb +0 -0
  225. data/lib/openc3/models/db_sharded_model.rb +0 -0
  226. data/lib/openc3/models/environment_model.rb +0 -0
  227. data/lib/openc3/models/gem_model.rb +12 -9
  228. data/lib/openc3/models/host_interface_microservice_model.rb +114 -0
  229. data/lib/openc3/models/info_model.rb +0 -0
  230. data/lib/openc3/models/interface_model.rb +178 -19
  231. data/lib/openc3/models/interface_status_model.rb +0 -0
  232. data/lib/openc3/models/metadata_model.rb +0 -0
  233. data/lib/openc3/models/metric_model.rb +0 -0
  234. data/lib/openc3/models/microservice_model.rb +9 -22
  235. data/lib/openc3/models/microservice_status_model.rb +0 -0
  236. data/lib/openc3/models/migration_model.rb +0 -0
  237. data/lib/openc3/models/model.rb +0 -0
  238. data/lib/openc3/models/news_model.rb +0 -0
  239. data/lib/openc3/models/note_model.rb +0 -0
  240. data/lib/openc3/models/offline_access_model.rb +0 -0
  241. data/lib/openc3/models/ping_model.rb +0 -0
  242. data/lib/openc3/models/plugin_model.rb +9 -2
  243. data/lib/openc3/models/plugin_store_model.rb +0 -0
  244. data/lib/openc3/models/process_status_model.rb +0 -0
  245. data/lib/openc3/models/python_package_model.rb +4 -4
  246. data/lib/openc3/models/queue_model.rb +0 -0
  247. data/lib/openc3/models/reaction_model.rb +0 -0
  248. data/lib/openc3/models/reingest_job_model.rb +0 -0
  249. data/lib/openc3/models/router_model.rb +0 -0
  250. data/lib/openc3/models/router_status_model.rb +0 -0
  251. data/lib/openc3/models/scope_model.rb +51 -26
  252. data/lib/openc3/models/script_engine_model.rb +0 -0
  253. data/lib/openc3/models/script_status_model.rb +0 -0
  254. data/lib/openc3/models/secret_model.rb +0 -0
  255. data/lib/openc3/models/setting_model.rb +546 -3
  256. data/lib/openc3/models/sorted_model.rb +0 -0
  257. data/lib/openc3/models/stash_model.rb +0 -0
  258. data/lib/openc3/models/target_model.rb +30 -25
  259. data/lib/openc3/models/timeline_model.rb +0 -0
  260. data/lib/openc3/models/tool_config_model.rb +0 -0
  261. data/lib/openc3/models/tool_model.rb +1 -0
  262. data/lib/openc3/models/trigger_group_model.rb +0 -0
  263. data/lib/openc3/models/trigger_model.rb +20 -0
  264. data/lib/openc3/models/widget_model.rb +0 -0
  265. data/lib/openc3/operators/microservice_operator.rb +64 -22
  266. data/lib/openc3/operators/operator.rb +4 -4
  267. data/lib/openc3/packets/binary_accessor.rb +0 -0
  268. data/lib/openc3/packets/command_validator.rb +0 -0
  269. data/lib/openc3/packets/commands.rb +0 -0
  270. data/lib/openc3/packets/json_packet.rb +0 -0
  271. data/lib/openc3/packets/limits.rb +0 -0
  272. data/lib/openc3/packets/limits_response.rb +0 -0
  273. data/lib/openc3/packets/packet.rb +4 -0
  274. data/lib/openc3/packets/packet_config.rb +0 -0
  275. data/lib/openc3/packets/packet_item.rb +0 -0
  276. data/lib/openc3/packets/packet_item_limits.rb +0 -0
  277. data/lib/openc3/packets/parsers/format_string_parser.rb +0 -0
  278. data/lib/openc3/packets/parsers/limits_parser.rb +0 -0
  279. data/lib/openc3/packets/parsers/limits_response_parser.rb +0 -0
  280. data/lib/openc3/packets/parsers/packet_item_parser.rb +0 -0
  281. data/lib/openc3/packets/parsers/packet_parser.rb +0 -0
  282. data/lib/openc3/packets/parsers/processor_parser.rb +0 -0
  283. data/lib/openc3/packets/parsers/state_parser.rb +0 -0
  284. data/lib/openc3/packets/parsers/xtce_converter.rb +0 -0
  285. data/lib/openc3/packets/parsers/xtce_parser.rb +0 -0
  286. data/lib/openc3/packets/structure.rb +7 -1
  287. data/lib/openc3/packets/structure_item.rb +0 -0
  288. data/lib/openc3/packets/telemetry.rb +0 -0
  289. data/lib/openc3/processors/processor.rb +2 -0
  290. data/lib/openc3/processors/statistics_processor.rb +0 -0
  291. data/lib/openc3/processors/watermark_processor.rb +0 -0
  292. data/lib/openc3/processors.rb +0 -0
  293. data/lib/openc3/script/api_shared.rb +0 -0
  294. data/lib/openc3/script/autonomic.rb +12 -12
  295. data/lib/openc3/script/calendar.rb +0 -0
  296. data/lib/openc3/script/commands.rb +0 -0
  297. data/lib/openc3/script/critical_cmd.rb +0 -0
  298. data/lib/openc3/script/exceptions.rb +0 -0
  299. data/lib/openc3/script/extract.rb +0 -0
  300. data/lib/openc3/script/limits.rb +0 -0
  301. data/lib/openc3/script/metadata.rb +0 -0
  302. data/lib/openc3/script/packages.rb +0 -0
  303. data/lib/openc3/script/plugins.rb +0 -0
  304. data/lib/openc3/script/queue.rb +0 -0
  305. data/lib/openc3/script/screen.rb +0 -0
  306. data/lib/openc3/script/script.rb +2 -2
  307. data/lib/openc3/script/script_runner.rb +0 -0
  308. data/lib/openc3/script/storage.rb +4 -4
  309. data/lib/openc3/script/suite.rb +0 -0
  310. data/lib/openc3/script/suite_results.rb +0 -0
  311. data/lib/openc3/script/suite_runner.rb +26 -0
  312. data/lib/openc3/script/tables.rb +0 -0
  313. data/lib/openc3/script/telemetry.rb +9 -3
  314. data/lib/openc3/script/web_socket_api.rb +218 -145
  315. data/lib/openc3/script.rb +0 -0
  316. data/lib/openc3/script_engines/script_engine.rb +0 -0
  317. data/lib/openc3/streams/mqtt_stream.rb +0 -0
  318. data/lib/openc3/streams/serial_stream.rb +0 -0
  319. data/lib/openc3/streams/stream.rb +0 -0
  320. data/lib/openc3/streams/tcpip_client_stream.rb +0 -0
  321. data/lib/openc3/streams/tcpip_socket_stream.rb +0 -0
  322. data/lib/openc3/streams/web_socket_client_stream.rb +0 -0
  323. data/lib/openc3/subpacketizers/subpacketizer.rb +0 -0
  324. data/lib/openc3/system/system.rb +0 -0
  325. data/lib/openc3/system/target.rb +0 -0
  326. data/lib/openc3/system.rb +0 -0
  327. data/lib/openc3/tools/cmd_tlm_server/api.rb +0 -0
  328. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +7 -0
  329. data/lib/openc3/tools/table_manager/table.rb +0 -0
  330. data/lib/openc3/tools/table_manager/table_config.rb +0 -0
  331. data/lib/openc3/tools/table_manager/table_item.rb +0 -0
  332. data/lib/openc3/tools/table_manager/table_item_parser.rb +0 -0
  333. data/lib/openc3/tools/table_manager/table_manager_core.rb +0 -0
  334. data/lib/openc3/tools/table_manager/table_parser.rb +0 -0
  335. data/lib/openc3/tools/test_runner/test.rb +0 -0
  336. data/lib/openc3/top_level.rb +1 -1
  337. data/lib/openc3/topics/autonomic_topic.rb +0 -0
  338. data/lib/openc3/topics/calendar_topic.rb +0 -0
  339. data/lib/openc3/topics/command_decom_topic.rb +0 -0
  340. data/lib/openc3/topics/command_topic.rb +0 -0
  341. data/lib/openc3/topics/config_topic.rb +0 -0
  342. data/lib/openc3/topics/decom_interface_topic.rb +2 -1
  343. data/lib/openc3/topics/interface_topic.rb +8 -2
  344. data/lib/openc3/topics/limits_event_topic.rb +0 -0
  345. data/lib/openc3/topics/notebook_topic.rb +0 -0
  346. data/lib/openc3/topics/queue_topic.rb +0 -0
  347. data/lib/openc3/topics/router_topic.rb +6 -1
  348. data/lib/openc3/topics/system_events_topic.rb +0 -0
  349. data/lib/openc3/topics/telemetry_decom_topic.rb +0 -0
  350. data/lib/openc3/topics/telemetry_topic.rb +0 -0
  351. data/lib/openc3/topics/timeline_topic.rb +0 -0
  352. data/lib/openc3/topics/topic.rb +0 -0
  353. data/lib/openc3/utilities/authentication.rb +4 -4
  354. data/lib/openc3/utilities/authorization.rb +0 -0
  355. data/lib/openc3/utilities/aws_bucket.rb +2 -2
  356. data/lib/openc3/utilities/bucket.rb +1 -1
  357. data/lib/openc3/utilities/bucket_file_cache.rb +55 -22
  358. data/lib/openc3/utilities/bucket_require.rb +0 -0
  359. data/lib/openc3/utilities/bucket_utilities.rb +3 -4
  360. data/lib/openc3/utilities/cli_generator.rb +0 -0
  361. data/lib/openc3/utilities/cmd_log.rb +0 -0
  362. data/lib/openc3/utilities/config_overlay.rb +75 -0
  363. data/lib/openc3/utilities/cosmos_rails_formatter.rb +0 -0
  364. data/lib/openc3/utilities/crc.rb +59 -32
  365. data/lib/openc3/utilities/csv.rb +0 -0
  366. data/lib/openc3/utilities/ctrf.rb +0 -0
  367. data/lib/openc3/utilities/env_helper.rb +0 -0
  368. data/lib/openc3/utilities/local_bucket.rb +0 -0
  369. data/lib/openc3/utilities/local_mode.rb +32 -6
  370. data/lib/openc3/utilities/logger.rb +30 -8
  371. data/lib/openc3/utilities/message_log.rb +0 -0
  372. data/lib/openc3/utilities/metric.rb +0 -0
  373. data/lib/openc3/utilities/migration.rb +0 -0
  374. data/lib/openc3/utilities/open_telemetry.rb +0 -0
  375. data/lib/openc3/utilities/process_manager.rb +0 -0
  376. data/lib/openc3/utilities/pypi_url.rb +0 -0
  377. data/lib/openc3/utilities/python_proxy.rb +0 -0
  378. data/lib/openc3/utilities/python_venv.rb +135 -0
  379. data/lib/openc3/utilities/quaternion.rb +0 -0
  380. data/lib/openc3/utilities/questdb_client.rb +0 -0
  381. data/lib/openc3/utilities/redis_secrets.rb +0 -0
  382. data/lib/openc3/utilities/reingest_job.rb +0 -0
  383. data/lib/openc3/utilities/ruby_lex_utils.rb +0 -0
  384. data/lib/openc3/utilities/rubygems_url.rb +39 -0
  385. data/lib/openc3/utilities/running_script.rb +24 -55
  386. data/lib/openc3/utilities/s3_autoload.rb +5 -5
  387. data/lib/openc3/utilities/script.rb +12 -8
  388. data/lib/openc3/utilities/secrets.rb +48 -3
  389. data/lib/openc3/utilities/simulated_target.rb +0 -0
  390. data/lib/openc3/utilities/sleeper.rb +0 -0
  391. data/lib/openc3/utilities/store.rb +0 -0
  392. data/lib/openc3/utilities/store_autoload.rb +2 -2
  393. data/lib/openc3/utilities/store_queued.rb +0 -0
  394. data/lib/openc3/utilities/target_file.rb +18 -1
  395. data/lib/openc3/utilities/thread_manager.rb +0 -0
  396. data/lib/openc3/utilities/throttle.rb +0 -0
  397. data/lib/openc3/utilities/zip.rb +0 -0
  398. data/lib/openc3/utilities.rb +0 -0
  399. data/lib/openc3/version.rb +5 -5
  400. data/lib/openc3/win32/excel.rb +0 -0
  401. data/lib/openc3/win32/win32.rb +0 -0
  402. data/lib/openc3/win32/win32_main.rb +0 -0
  403. data/lib/openc3.rb +2 -2
  404. data/tasks/gemfile_stats.rake +0 -0
  405. data/tasks/spec.rake +0 -0
  406. data/templates/command_validator/command_validator.py +0 -0
  407. data/templates/command_validator/command_validator.rb +0 -0
  408. data/templates/conversion/conversion.py +0 -0
  409. data/templates/conversion/conversion.rb +0 -0
  410. data/templates/limits_response/response.py +0 -0
  411. data/templates/limits_response/response.rb +0 -0
  412. data/templates/microservice/microservices/TEMPLATE/microservice.py +0 -0
  413. data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -0
  414. data/templates/plugin/.gitignore +0 -0
  415. data/templates/plugin/LICENSE.md +0 -0
  416. data/templates/plugin/README.md +0 -0
  417. data/templates/plugin/Rakefile +0 -0
  418. data/templates/plugin/plugin.gemspec +0 -0
  419. data/templates/plugin/plugin.txt +0 -0
  420. data/templates/processor/processor.py +1 -1
  421. data/templates/processor/processor.rb +0 -0
  422. data/templates/target/targets/TARGET/cmd_tlm/cmd.txt +0 -0
  423. data/templates/target/targets/TARGET/cmd_tlm/tlm.txt +0 -0
  424. data/templates/target/targets/TARGET/lib/target.py +0 -0
  425. data/templates/target/targets/TARGET/lib/target.rb +0 -0
  426. data/templates/target/targets/TARGET/procedures/procedure.py +0 -0
  427. data/templates/target/targets/TARGET/procedures/procedure.rb +0 -0
  428. data/templates/target/targets/TARGET/public/README.txt +0 -0
  429. data/templates/target/targets/TARGET/screens/status.txt +0 -0
  430. data/templates/target/targets/TARGET/target.txt +0 -0
  431. data/templates/tool_angular/.editorconfig +0 -0
  432. data/templates/tool_angular/.gitignore +0 -0
  433. data/templates/tool_angular/.vscode/extensions.json +0 -0
  434. data/templates/tool_angular/.vscode/launch.json +0 -0
  435. data/templates/tool_angular/.vscode/tasks.json +0 -0
  436. data/templates/tool_angular/angular.json +0 -0
  437. data/templates/tool_angular/extra-webpack.config.js +0 -0
  438. data/templates/tool_angular/package.json +2 -2
  439. data/templates/tool_angular/src/app/app-routing.module.ts +0 -0
  440. data/templates/tool_angular/src/app/app.component.html +0 -0
  441. data/templates/tool_angular/src/app/app.component.scss +0 -0
  442. data/templates/tool_angular/src/app/app.component.spec.ts +0 -0
  443. data/templates/tool_angular/src/app/app.component.ts +0 -0
  444. data/templates/tool_angular/src/app/app.module.ts +0 -0
  445. data/templates/tool_angular/src/app/custom-overlay-container.ts +0 -0
  446. data/templates/tool_angular/src/app/empty-route/empty-route.component.ts +0 -0
  447. data/templates/tool_angular/src/app/openc3-api.d.ts +0 -0
  448. data/templates/tool_angular/src/assets/.gitkeep +0 -0
  449. data/templates/tool_angular/src/environments/environment.prod.ts +0 -0
  450. data/templates/tool_angular/src/environments/environment.ts +0 -0
  451. data/templates/tool_angular/src/favicon.ico +0 -0
  452. data/templates/tool_angular/src/index.html +0 -0
  453. data/templates/tool_angular/src/main.single-spa.ts +0 -0
  454. data/templates/tool_angular/src/single-spa/asset-url.ts +0 -0
  455. data/templates/tool_angular/src/single-spa/single-spa-props.ts +0 -0
  456. data/templates/tool_angular/src/styles.scss +0 -0
  457. data/templates/tool_angular/tsconfig.app.json +0 -0
  458. data/templates/tool_angular/tsconfig.json +0 -0
  459. data/templates/tool_angular/tsconfig.spec.json +0 -0
  460. data/templates/tool_react/.eslintrc +0 -0
  461. data/templates/tool_react/.gitignore +0 -0
  462. data/templates/tool_react/.prettierignore +0 -0
  463. data/templates/tool_react/babel.config.json +0 -0
  464. data/templates/tool_react/jest.config.js +0 -0
  465. data/templates/tool_react/package.json +1 -1
  466. data/templates/tool_react/src/openc3-tool_name.js +0 -0
  467. data/templates/tool_react/src/root.component.js +0 -0
  468. data/templates/tool_react/src/root.component.test.js +0 -0
  469. data/templates/tool_react/webpack.config.js +0 -0
  470. data/templates/tool_svelte/.gitignore +0 -0
  471. data/templates/tool_svelte/.prettierignore +0 -0
  472. data/templates/tool_svelte/.prettierrc.js +0 -0
  473. data/templates/tool_svelte/babel.config.js +0 -0
  474. data/templates/tool_svelte/build/smui.css +0 -0
  475. data/templates/tool_svelte/jest.config.js +0 -0
  476. data/templates/tool_svelte/package.json +1 -1
  477. data/templates/tool_svelte/rollup.config.js +0 -0
  478. data/templates/tool_svelte/src/App.svelte +0 -0
  479. data/templates/tool_svelte/src/App.test.js +0 -0
  480. data/templates/tool_svelte/src/theme/_smui-theme.scss +0 -0
  481. data/templates/tool_svelte/src/tool_name.js +0 -0
  482. data/templates/tool_vue/.env.standalone +0 -0
  483. data/templates/tool_vue/.gitignore +0 -0
  484. data/templates/tool_vue/.prettierrc.cjs +0 -0
  485. data/templates/tool_vue/eslint.config.mjs +0 -0
  486. data/templates/tool_vue/jsconfig.json +0 -0
  487. data/templates/tool_vue/package.json +3 -3
  488. data/templates/tool_vue/src/App.vue +0 -0
  489. data/templates/tool_vue/src/main.js +0 -0
  490. data/templates/tool_vue/src/router.js +0 -0
  491. data/templates/tool_vue/src/tools/tool_name/tool_name.vue +0 -0
  492. data/templates/tool_vue/vite.config.js +1 -1
  493. data/templates/widget/.prettierrc.cjs +0 -0
  494. data/templates/widget/LICENSE.md +0 -0
  495. data/templates/widget/Rakefile +0 -0
  496. data/templates/widget/package.json +2 -2
  497. data/templates/widget/src/Widget.vue +0 -0
  498. data/templates/widget/vite.config.js +0 -0
  499. metadata +36 -9
@@ -221,7 +221,12 @@ module OpenC3
221
221
  next 'SUCCESS'
222
222
  end
223
223
  if msg_hash.key?('interface_details')
224
- next @interface.details.as_json.to_json(allow_nan: true)
224
+ begin
225
+ next @interface.details.as_json.to_json(allow_nan: true)
226
+ rescue => e
227
+ @logger.error "#{@interface.name}: interface_details: #{e.formatted}"
228
+ next e.message
229
+ end
225
230
  end
226
231
  end
227
232
 
@@ -508,7 +513,12 @@ module OpenC3
508
513
  next 'SUCCESS'
509
514
  end
510
515
  if msg_hash.key?('router_details')
511
- next @router.details.as_json.to_json(allow_nan: true)
516
+ begin
517
+ next @router.details.as_json.to_json(allow_nan: true)
518
+ rescue => e
519
+ @logger.error "#{@router.name}: router_details: #{e.formatted}"
520
+ next e.message
521
+ end
512
522
  end
513
523
  next 'SUCCESS'
514
524
  end
@@ -610,7 +620,26 @@ module OpenC3
610
620
  # Called to connect the interface/router. It takes optional parameters to
611
621
  # rebuilt the interface/router. Once we set the state to 'ATTEMPTING' the
612
622
  # run method handles the actual connection.
623
+ # Connecting an interface/router which is already CONNECTED is a no-op.
624
+ # Without this the existing (working) connection would be torn down and
625
+ # rebuilt which can take up to the read_timeout to detect. Callers who want
626
+ # to force a reconnect should disconnect first or pass new parameters.
613
627
  def attempting(*params)
628
+ if params.empty? and @interface.state == 'CONNECTED' and @interface.connected?
629
+ @logger.info "#{@interface.name}: Connect ignored, already connected"
630
+ return @interface
631
+ end
632
+
633
+ attempt_connection(*params)
634
+ end
635
+
636
+ # Sets the state to 'ATTEMPTING', first rebuilding the interface/router if
637
+ # parameters are given, so the run method performs the actual connection.
638
+ # Unlike attempting() this always transitions. The reconnect path in
639
+ # disconnect() requires that, since @interface.disconnect may have raised or
640
+ # left connected? true, which would make attempting() ignore the request and
641
+ # leave the interface stuck in 'CONNECTED' with no way back to a connection.
642
+ def attempt_connection(*params)
614
643
  unless params.empty?
615
644
  @interface.disconnect()
616
645
  # Build New Interface, this can fail if passed bad parameters
@@ -828,6 +857,13 @@ module OpenC3
828
857
 
829
858
  def connect
830
859
  @logger.info "#{@interface.name}: Connect #{@interface.connection_string}"
860
+ # Interface connect implementations typically overwrite their stream / socket
861
+ # so cleanly close any existing connection rather than leaking it
862
+ begin
863
+ @interface.disconnect if @interface.connected?
864
+ rescue => e
865
+ @logger.error "Disconnect: #{@interface.name}: #{e.formatted}"
866
+ end
831
867
  begin
832
868
  @interface.connect
833
869
  @interface.post_connect
@@ -849,38 +885,49 @@ module OpenC3
849
885
  end
850
886
 
851
887
  def disconnect(allow_reconnect = true)
852
- return if @interface.state == 'DISCONNECTED' && !@interface.connected?
853
-
854
- # Synchronize the calls to @interface.disconnect since it takes an unknown
855
- # amount of time. If two calls to disconnect stack up, the if statement
856
- # should avoid multiple calls to disconnect.
888
+ reconnect = false
889
+
890
+ # Two threads reach here for a single connection loss: the cmd handler
891
+ # thread servicing a disconnect directive, and the run thread coming back
892
+ # out of read (or out of the connection maintenance sleep). The redundant
893
+ # check below and the state change that records the disconnect must be in
894
+ # the same critical section, otherwise the second thread reads the state
895
+ # before the first has updated it and disconnects the interface twice.
857
896
  @mutex.synchronize do
897
+ # A disconnect has already been performed so there is nothing left to do
898
+ return if @interface.state == 'DISCONNECTED' && !@interface.connected?
899
+
900
+ # Call disconnect without consulting connected? so any resources the
901
+ # interface is still holding are cleaned up. It takes an unknown amount
902
+ # of time which is the other reason for the mutex.
858
903
  begin
859
- @interface.disconnect if @interface.connected?
904
+ @interface.disconnect
860
905
  rescue => e
861
906
  @logger.error "Disconnect: #{@interface.name}: #{e.formatted}"
862
907
  end
863
- end
864
908
 
865
- # If the interface is set to auto_reconnect then delay so the thread
866
- # can come back around and allow the interface a chance to reconnect.
867
- # Skip reconnect if stop() has been called to avoid re-creating the status model
868
- if allow_reconnect and @interface.auto_reconnect and @interface.state != 'DISCONNECTED' and !@cancel_thread
869
- attempting()
870
- if !@cancel_thread
871
- # @logger.debug "reconnect delay: #{@interface.reconnect_delay}"
872
- @interface_thread_sleeper.sleep(@interface.reconnect_delay)
873
- end
874
- else
875
- @interface.state = 'DISCONNECTED'
876
- unless @cancel_thread
877
- if @interface_or_router == 'INTERFACE'
878
- InterfaceStatusModel.set(@interface.as_json(), queued: true, scope: @scope)
879
- else
880
- RouterStatusModel.set(@interface.as_json(), queued: true, scope: @scope)
909
+ # If the interface is set to auto_reconnect then delay so the thread
910
+ # can come back around and allow the interface a chance to reconnect.
911
+ # Skip reconnect if stop() has been called to avoid re-creating the status model
912
+ reconnect = allow_reconnect && @interface.auto_reconnect && @interface.state != 'DISCONNECTED' && !@cancel_thread
913
+ if reconnect
914
+ attempt_connection()
915
+ else
916
+ @interface.state = 'DISCONNECTED'
917
+ unless @cancel_thread
918
+ if @interface_or_router == 'INTERFACE'
919
+ InterfaceStatusModel.set(@interface.as_json(), queued: true, scope: @scope)
920
+ else
921
+ RouterStatusModel.set(@interface.as_json(), queued: true, scope: @scope)
922
+ end
881
923
  end
882
924
  end
883
925
  end
926
+
927
+ # Sleep outside the mutex so stop() and connect() are not blocked for the
928
+ # whole reconnect delay
929
+ # @logger.debug "reconnect delay: #{@interface.reconnect_delay}"
930
+ @interface_thread_sleeper.sleep(@interface.reconnect_delay) if reconnect && !@cancel_thread
884
931
  end
885
932
 
886
933
  # Disconnect from the interface and stop the thread
File without changes
@@ -43,7 +43,7 @@ module OpenC3
43
43
  attr_accessor :secrets
44
44
 
45
45
  def self.run(name = nil)
46
- name = ENV['OPENC3_MICROSERVICE_NAME'] unless name
46
+ name = ENV.fetch('OPENC3_MICROSERVICE_NAME', nil) unless name
47
47
  microservice = self.new(name)
48
48
  thread = Thread.new do
49
49
  begin
@@ -150,7 +150,7 @@ module OpenC3
150
150
  cmd_array = @config["cmd"]
151
151
 
152
152
  # Get Microservice files from bucket storage
153
- bucket = ENV['OPENC3_CONFIG_BUCKET']
153
+ bucket = ENV.fetch('OPENC3_CONFIG_BUCKET') { raise 'OPENC3_CONFIG_BUCKET environment variable is required' }
154
154
  client = Bucket.getClient()
155
155
 
156
156
  prefix = "#{@scope}/microservices/#{@name}/"
@@ -160,7 +160,7 @@ module OpenC3
160
160
  # unreachable while many microservices start at once. Rather than crash
161
161
  # and CrashLoopBackOff (which can outlast deploy timeouts), retry for a
162
162
  # bounded time before giving up.
163
- startup_timeout = (ENV['OPENC3_MICROSERVICE_STARTUP_BUCKET_TIMEOUT'] || 60).to_f
163
+ startup_timeout = (ENV.fetch('OPENC3_MICROSERVICE_STARTUP_BUCKET_TIMEOUT', 60)).to_f
164
164
  startup_deadline = Time.now + startup_timeout
165
165
  begin
166
166
  file_count = 0
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2024 OpenC3 Inc.
3
+ # Copyright 2026 OpenC3, Inc.
4
4
  # All Rights Reserved.
5
5
  #
6
6
  # This program is distributed in the hope that it will be useful,
@@ -46,8 +46,8 @@ module OpenC3
46
46
  NewsModel.news_error("Error contacting OpenC3 news feed (status: #{response.status})")
47
47
  end
48
48
  end
49
- rescue Exception => e
50
- NewsModel.news_error("Error contacting OpenC3 news feed. #{e.message})")
49
+ rescue StandardError => e
50
+ NewsModel.news_error("Error contacting OpenC3 news feed. #{e.message}")
51
51
  end
52
52
 
53
53
  def run
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,38 @@
1
+ # Copyright 2026 OpenC3, Inc.
2
+ # All Rights Reserved.
3
+ #
4
+ # This program is distributed in the hope that it will be useful,
5
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
6
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7
+ # See LICENSE.md for more details.
8
+
9
+ # This file may also be used under the terms of a commercial license
10
+ # if purchased from OpenC3, Inc.
11
+
12
+ require 'openc3/utilities/migration'
13
+ require 'openc3/models/scope_model'
14
+ require 'openc3/models/microservice_model'
15
+ require 'openc3/models/bridge_model'
16
+
17
+ module OpenC3
18
+ # Ensure every existing scope has a DEFAULT bridge_microservice (the Iroh hub
19
+ # that lets openc3-app run COSMOS interfaces on the host). New scopes get one
20
+ # via ScopeModel#deploy; this backfills any scope created before that.
21
+ class DefaultBridge < Migration
22
+ def self.run
23
+ ScopeModel.get_all_models(scope: nil).each do |scope, scope_model|
24
+ name = "#{scope}__BRIDGE__DEFAULT"
25
+ next if MicroserviceModel.get_model(name: name, scope: scope)
26
+
27
+ microservice = BridgeModel.build_microservice(bridge_name: "DEFAULT", scope: scope, shard: scope_model.shard)
28
+ microservice.create
29
+ microservice.deploy
30
+ Logger.info("Added DEFAULT bridge_microservice to scope #{scope}")
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ unless ENV['OPENC3_NO_MIGRATE']
37
+ OpenC3::DefaultBridge.run
38
+ end
@@ -1,3 +1,14 @@
1
+ # Copyright 2026 OpenC3, Inc.
2
+ # All Rights Reserved.
3
+ #
4
+ # This program is distributed in the hope that it will be useful,
5
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
6
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7
+ # See LICENSE.md for more details.
8
+ #
9
+ # This file may also be used under the terms of a commercial license
10
+ # if purchased from OpenC3, Inc.
11
+
1
12
  require 'json'
2
13
  require 'openc3/utilities/local_mode'
3
14
  require 'openc3/utilities/migration'
File without changes
@@ -27,7 +27,7 @@ module OpenC3
27
27
  PRIMARY_KEY = 'OPENC3__TOKEN' # for argon2 password hash
28
28
  SESSIONS_KEY = 'OPENC3__SESSIONS' # for hash containing session tokens
29
29
 
30
- # The length of time in minutes to keep redis values in memory
30
+ # The length of time in seconds to keep redis values in memory
31
31
  PW_HASH_CACHE_TIMEOUT = 5
32
32
  SESSION_CACHE_TIMEOUT = 5
33
33
 
@@ -44,6 +44,21 @@ module OpenC3
44
44
  SESSION_PREFIX = "ses_"
45
45
  OTP_PREFIX = "otp_"
46
46
 
47
+ # A session token is SESSION_PREFIX followed by the unpadded urlsafe base64
48
+ # encoding of 16 random bytes, i.e. exactly 22 characters of [A-Za-z0-9_-].
49
+ # Keep this in sync with generate_session.
50
+ SESSION_TOKEN_REGEX = /\A#{SESSION_PREFIX}[A-Za-z0-9_-]{22}\z/
51
+
52
+ # Whether the given value could have been produced by generate_session.
53
+ # Callers handling a token from an unauthenticated request use this to
54
+ # reject garbage before doing any Redis work. Checking only the prefix is
55
+ # not enough: something like "ses_AAA" would pass and still cost us a read.
56
+ # @param token [String] the value to check
57
+ # @return [Boolean] whether the value is shaped like a session token
58
+ def self.session_token?(token)
59
+ SESSION_TOKEN_REGEX.match?(token.to_s)
60
+ end
61
+
47
62
  def self.set?(key = PRIMARY_KEY)
48
63
  Store.exists(key) == 1
49
64
  end
@@ -56,7 +71,7 @@ module OpenC3
56
71
  def self.verify(token, no_password: true, service_only: false)
57
72
  # Handle a service password - Generally only used by ScriptRunner
58
73
  # TODO: Replace this with temporary service tokens
59
- service_password = ENV['OPENC3_SERVICE_PASSWORD']
74
+ service_password = ENV.fetch('OPENC3_SERVICE_PASSWORD', nil)
60
75
  return true if service_password and service_password == token
61
76
 
62
77
  return false if service_only
@@ -78,15 +93,28 @@ module OpenC3
78
93
  # Check cached session tokens and password hash
79
94
  time = Time.now
80
95
  unless mode == :password
81
- if @@session_cache and (time - @@session_cache_time) < SESSION_CACHE_TIMEOUT and @@session_cache[token]
96
+ # Drop the whole cache once it ages out rather than tracking per token
97
+ # times. This bounds how long a terminated token keeps working to
98
+ # SESSION_CACHE_TIMEOUT, same as before.
99
+ if @@session_cache.nil? or (time - @@session_cache_time) >= SESSION_CACHE_TIMEOUT
100
+ @@session_cache = {}
101
+ @@session_cache_time = time
102
+ end
103
+
104
+ if @@session_cache[token]
82
105
  terminate_otp(token)
83
106
  return true
84
107
  end
85
108
 
86
- # Check stored session tokens
87
- @@session_cache = Store.hgetall(SESSIONS_KEY)
88
- @@session_cache_time = time
89
- if @@session_cache[token]
109
+ # Check the stored session tokens for just this token. Deliberately not
110
+ # HGETALL: verify_token is unauthenticated, so reading the entire
111
+ # session hash here lets any caller make us pull every session ever
112
+ # created on every request, and that hash only grows. HGET is O(1) and
113
+ # can't be amplified. The cache then fills with the tokens actually in
114
+ # use instead of all of them.
115
+ stored = Store.hget(SESSIONS_KEY, token)
116
+ if stored
117
+ @@session_cache[token] = stored
90
118
  terminate_otp(token)
91
119
  return true
92
120
  end
@@ -0,0 +1,133 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2026 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ # See LICENSE.md for more details.
10
+ #
11
+ # This file may also be used under the terms of a commercial license
12
+ # if purchased from OpenC3, Inc.
13
+
14
+ require 'openc3/models/model'
15
+ require 'openc3/models/microservice_model'
16
+ require 'base64'
17
+ require 'json'
18
+ require 'securerandom'
19
+
20
+ module OpenC3
21
+ # Stores the identity of a named Iroh bridge (the bridge_microservice hub).
22
+ #
23
+ # A bridge is the COSMOS-side Iroh server that openc3-app and host interfaces
24
+ # connect to. This model stores the bridge's Iroh public key (its EndpointId)
25
+ # and most recent connection ticket, plus the authorized openc3-app control
26
+ # identity (app_public_key) recorded during enrollment. The bridge's own
27
+ # private key lives in the secrets store, never here.
28
+ #
29
+ # This Ruby model mirrors the Python BridgeModel (same scoped primary key and
30
+ # JSON shape) so the openc3cli `bridgeenroll` command can read and update the
31
+ # same records the Python BridgeMicroservice maintains.
32
+ class BridgeModel < Model
33
+ PRIMARY_KEY = 'openc3_bridges'
34
+ ENROLLMENT_CODE_TTL_SECONDS = 10 * 60
35
+
36
+ attr_accessor :public_key
37
+ attr_accessor :ticket
38
+ attr_accessor :app_public_key
39
+ attr_accessor :enroll_code
40
+ attr_accessor :enroll_code_generated_at
41
+ attr_accessor :port
42
+
43
+ # The bridge stack (bridge_microservice.py) is Python and runs from the core
44
+ # library regardless of the interfaces bridged through it.
45
+ def self.bridge_python_bin
46
+ ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
47
+ end
48
+
49
+ BRIDGE_WORK_DIR = '/openc3/python/openc3/microservices'
50
+
51
+ # Build (but do not create) the MicroserviceModel for a bridge_microservice
52
+ # hub named `bridge_name` in `scope`. Centralizes the cmd/work_dir/options so
53
+ # ScopeModel, the DEFAULT-bridge migration, and InterfaceModel stay in sync.
54
+ def self.build_microservice(bridge_name:, scope:, parent: nil, shard: 0, plugin: nil)
55
+ microservice_name = "#{scope}__BRIDGE__#{bridge_name.to_s.upcase}"
56
+ MicroserviceModel.new(
57
+ name: microservice_name,
58
+ cmd: [bridge_python_bin, 'bridge_microservice.py', microservice_name],
59
+ work_dir: BRIDGE_WORK_DIR,
60
+ options: [['BRIDGE_NAME', bridge_name.to_s.upcase]],
61
+ parent: parent,
62
+ shard: shard.to_i,
63
+ plugin: plugin,
64
+ scope: scope
65
+ )
66
+ end
67
+
68
+ # NOTE: The following three class methods are reimplemented (like other
69
+ # scoped models) so the ModelController and Model class methods work.
70
+ def self.get(name:, scope:)
71
+ super("#{scope}__#{PRIMARY_KEY}", name: name)
72
+ end
73
+
74
+ def self.names(scope:)
75
+ super("#{scope}__#{PRIMARY_KEY}")
76
+ end
77
+
78
+ def self.all(scope:)
79
+ super("#{scope}__#{PRIMARY_KEY}")
80
+ end
81
+ # END NOTE
82
+
83
+ def initialize(
84
+ name:,
85
+ scope:,
86
+ public_key: nil,
87
+ ticket: nil,
88
+ app_public_key: nil,
89
+ enroll_code: nil,
90
+ enroll_code_generated_at: nil,
91
+ port: nil,
92
+ updated_at: nil,
93
+ plugin: nil
94
+ )
95
+ super("#{scope}__#{PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
96
+ @public_key = public_key
97
+ @ticket = ticket
98
+ @app_public_key = app_public_key
99
+ @enroll_code = enroll_code
100
+ @enroll_code_generated_at = enroll_code_generated_at
101
+ @port = port
102
+ end
103
+
104
+ def as_json(*_a)
105
+ {
106
+ 'name' => @name,
107
+ 'scope' => @scope,
108
+ 'updated_at' => @updated_at,
109
+ 'plugin' => @plugin,
110
+ 'public_key' => @public_key,
111
+ 'ticket' => @ticket,
112
+ 'app_public_key' => @app_public_key,
113
+ 'enroll_code' => @enroll_code,
114
+ 'enroll_code_generated_at' => @enroll_code_generated_at,
115
+ 'port' => @port
116
+ }
117
+ end
118
+
119
+ # Generate a one-time manual-enrollment code, store it on this bridge, and
120
+ # return a token (base64 JSON of bridge name + hub ticket + code) that a
121
+ # remote openc3-app pastes to enroll. Requires the bridge to be up (ticket
122
+ # present). The code is redeemed once over the api/enroll ALPN.
123
+ def generate_enrollment_token
124
+ raise "Bridge '#{@name}' has no ticket yet; is its bridge_microservice running?" if @ticket.nil? || @ticket.empty?
125
+
126
+ @enroll_code = SecureRandom.hex(16)
127
+ @enroll_code_generated_at = Time.now.to_i
128
+ update()
129
+ payload = { 'v' => 1, 'bridge' => @name, 'ticket' => @ticket, 'code' => @enroll_code }
130
+ Base64.urlsafe_encode64(JSON.generate(payload), padding: false)
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2026 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ # See LICENSE.md for more details.
10
+ #
11
+ # This file may also be used under the terms of a commercial license
12
+ # if purchased from OpenC3, Inc.
13
+
14
+ require 'openc3/utilities/secrets'
15
+
16
+ # Mixin providing plugin.txt keyword handling shared by the models the operator
17
+ # turns into running containers (InterfaceModel / RouterModel and
18
+ # MicroserviceModel). Including classes must initialize @ports to an Array.
19
+ module OpenC3
20
+ module ConfigKeywordModel
21
+ # Parse a PORT keyword, appending [number, protocol] to @ports.
22
+ def parse_port(parser, keyword, parameters)
23
+ usage = "PORT <Number> <Protocol (Optional)"
24
+ parser.verify_num_parameters(1, 2, usage)
25
+ begin
26
+ @ports << [Integer(parameters[0])]
27
+ rescue # In case Integer fails
28
+ raise ConfigParser::Error.new(parser, "Port must be an integer: #{parameters[0]}", usage)
29
+ end
30
+ protocol = ConfigParser.handle_nil(parameters[1])
31
+ if protocol
32
+ # Per https://kubernetes.io/docs/concepts/services-networking/service/#protocol-support
33
+ if %w(TCP UDP SCTP).include?(protocol.upcase)
34
+ @ports[-1] << protocol.upcase
35
+ else
36
+ raise ConfigParser::Error.new(parser, "Unknown port protocol: #{parameters[1]}", usage)
37
+ end
38
+ else
39
+ @ports[-1] << 'TCP'
40
+ end
41
+ end
42
+
43
+ # Validate a SECRET / BRIDGE_SECRET definition. FILE type paths are restricted
44
+ # to Secrets.secret_file_dir to prevent reading or overwriting arbitrary files.
45
+ def validate_secret(parser, keyword, parameters)
46
+ type = parameters[0].to_s.upcase
47
+ unless ['ENV', 'FILE'].include?(type)
48
+ raise ConfigParser::Error.new(parser, "Unknown secret type '#{parameters[0]}' for #{keyword}. Must be ENV or FILE.")
49
+ end
50
+ # Normalize in place so the stored secret matches what the operator and
51
+ # Secrets.setup match on, both of which compare against 'ENV' / 'FILE' and
52
+ # use the path exactly as given.
53
+ parameters[0] = type
54
+ if type == 'FILE'
55
+ begin
56
+ parameters[2] = Secrets.validate_file_path(parameters[2])
57
+ rescue ArgumentError => error
58
+ raise ConfigParser::Error.new(parser, "#{keyword} #{error.message}")
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
File without changes
File without changes
File without changes
@@ -22,6 +22,7 @@ require 'rubygems'
22
22
  require 'rubygems/uninstaller'
23
23
  require 'tempfile'
24
24
  require 'openc3/utilities/process_manager'
25
+ require 'openc3/utilities/rubygems_url'
25
26
  require 'openc3/api/api'
26
27
  require 'pathname'
27
28
 
@@ -34,8 +35,8 @@ module OpenC3
34
35
  extend Api
35
36
 
36
37
  def self.names
37
- if Dir.exist?("#{ENV['GEM_HOME']}/gems")
38
- result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
38
+ if Dir.exist?("#{ENV.fetch('GEM_HOME', nil)}/gems")
39
+ result = Pathname.new("#{ENV.fetch('GEM_HOME', nil)}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
39
40
  else
40
41
  result = []
41
42
  end
@@ -43,9 +44,9 @@ module OpenC3
43
44
  end
44
45
 
45
46
  def self.get(name)
46
- path = "#{ENV['GEM_HOME']}/cosmoscache/#{name}"
47
+ path = "#{ENV.fetch('GEM_HOME', nil)}/cosmoscache/#{name}"
47
48
  return path if File.exist?(path)
48
- path = "#{ENV['GEM_HOME']}/cache/#{name}"
49
+ path = "#{ENV.fetch('GEM_HOME', nil)}/cache/#{name}"
49
50
  return path if File.exist?(path)
50
51
  raise "Gem '#{name}' not found"
51
52
  end
@@ -54,8 +55,8 @@ module OpenC3
54
55
  if File.file?(gem_file_path)
55
56
  gem_filename = File.basename(gem_file_path)
56
57
  # Put into cosmoscache folder that we control
57
- FileUtils.mkdir_p("#{ENV['GEM_HOME']}/cosmoscache") unless Dir.exist?("#{ENV['GEM_HOME']}/cosmoscache")
58
- FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cosmoscache/#{File.basename(gem_file_path)}")
58
+ FileUtils.mkdir_p("#{ENV.fetch('GEM_HOME', nil)}/cosmoscache") unless Dir.exist?("#{ENV.fetch('GEM_HOME', nil)}/cosmoscache")
59
+ FileUtils.cp(gem_file_path, "#{ENV.fetch('GEM_HOME', nil)}/cosmoscache/#{File.basename(gem_file_path)}")
59
60
  if gem_install
60
61
  Logger.info "Installing gem: #{gem_filename}"
61
62
  result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "package_install", gem_filename, Time.now + 3600.0, scope: scope)
@@ -79,9 +80,11 @@ module OpenC3
79
80
  rubygems_url = get_setting('rubygems_url', scope: scope)
80
81
  rescue
81
82
  # If Redis isn't running try the ENV, then simply rubygems.org
82
- rubygems_url = ENV['RUBYGEMS_URL']
83
- rubygems_url ||= 'https://rubygems.org'
83
+ rubygems_url = ENV.fetch('RUBYGEMS_URL', RubygemsUrl::DEFAULT)
84
84
  end
85
+ # The url comes from a user-writable setting or ENV so validate it before
86
+ # using it as a Gem source
87
+ rubygems_url = RubygemsUrl.validate(rubygems_url) if rubygems_url
85
88
  Gem.sources = [rubygems_url] if rubygems_url
86
89
  Gem.done_installing_hooks.clear
87
90
  begin
@@ -92,7 +95,7 @@ module OpenC3
92
95
  Gem.install(gem_file_path, "> 0.pre", build_args: ['--no-document, --without development,test'], prerelease: true, domain: :both)
93
96
  end
94
97
  rescue => e
95
- message = "Gem file #{gem_file_path} error installing to #{ENV['GEM_HOME']}\n#{e.formatted}"
98
+ message = "Gem file #{gem_file_path} error installing to #{ENV.fetch('GEM_HOME', nil)}\n#{e.formatted}"
96
99
  Logger.error message
97
100
  raise e
98
101
  end