Class: OpenC3::BridgeModel

Inherits:
Model show all
Defined in:
lib/openc3/models/bridge_model.rb

Overview

Stores the identity of a named Iroh bridge (the bridge_microservice hub).

A bridge is the COSMOS-side Iroh server that openc3-app and host interfaces connect to. This model stores the bridge's Iroh public key (its EndpointId) and most recent connection ticket, plus the authorized openc3-app control identity (app_public_key) recorded during enrollment. The bridge's own private key lives in the secrets store, never here.

This Ruby model mirrors the Python BridgeModel (same scoped primary key and JSON shape) so the openc3cli bridgeenroll command can read and update the same records the Python BridgeMicroservice maintains.

Constant Summary collapse

PRIMARY_KEY =
'openc3_bridges'
ENROLLMENT_CODE_TTL_SECONDS =
10 * 60
BRIDGE_WORK_DIR =
'/openc3/python/openc3/microservices'

Instance Attribute Summary collapse

Attributes inherited from Model

#name, #plugin, #scope, #updated_at

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Model

#check_disable_erb, #create, #deploy, #destroy, #destroyed?, #diff, filter, find_all_by_plugin, from_json, get_all_models, get_model, handle_config, set, store, store_queued, #undeploy, #update

Constructor Details

#initialize(name:, scope:, public_key: nil, ticket: nil, app_public_key: nil, enroll_code: nil, enroll_code_generated_at: nil, port: nil, updated_at: nil, plugin: nil) ⇒ BridgeModel

END NOTE



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/openc3/models/bridge_model.rb', line 83

def initialize(
  name:,
  scope:,
  public_key: nil,
  ticket: nil,
  app_public_key: nil,
  enroll_code: nil,
  enroll_code_generated_at: nil,
  port: nil,
  updated_at: nil,
  plugin: nil
)
  super("#{scope}__#{PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
  @public_key = public_key
  @ticket = ticket
  @app_public_key = app_public_key
  @enroll_code = enroll_code
  @enroll_code_generated_at = enroll_code_generated_at
  @port = port
end

Instance Attribute Details

#app_public_keyObject

Returns the value of attribute app_public_key.



38
39
40
# File 'lib/openc3/models/bridge_model.rb', line 38

def app_public_key
  @app_public_key
end

#enroll_codeObject

Returns the value of attribute enroll_code.



39
40
41
# File 'lib/openc3/models/bridge_model.rb', line 39

def enroll_code
  @enroll_code
end

#enroll_code_generated_atObject

Returns the value of attribute enroll_code_generated_at.



40
41
42
# File 'lib/openc3/models/bridge_model.rb', line 40

def enroll_code_generated_at
  @enroll_code_generated_at
end

#portObject

Returns the value of attribute port.



41
42
43
# File 'lib/openc3/models/bridge_model.rb', line 41

def port
  @port
end

#public_keyObject

Returns the value of attribute public_key.



36
37
38
# File 'lib/openc3/models/bridge_model.rb', line 36

def public_key
  @public_key
end

#ticketObject

Returns the value of attribute ticket.



37
38
39
# File 'lib/openc3/models/bridge_model.rb', line 37

def ticket
  @ticket
end

Class Method Details

.all(scope:) ⇒ Object



78
79
80
# File 'lib/openc3/models/bridge_model.rb', line 78

def self.all(scope:)
  super("#{scope}__#{PRIMARY_KEY}")
end

.bridge_python_binObject

The bridge stack (bridge_microservice.py) is Python and runs from the core library regardless of the interfaces bridged through it.



45
46
47
# File 'lib/openc3/models/bridge_model.rb', line 45

def self.bridge_python_bin
  ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
end

.build_microservice(bridge_name:, scope:, parent: nil, shard: 0, plugin: nil) ⇒ Object

Build (but do not create) the MicroserviceModel for a bridge_microservice hub named bridge_name in scope. Centralizes the cmd/work_dir/options so ScopeModel, the DEFAULT-bridge migration, and InterfaceModel stay in sync.



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/openc3/models/bridge_model.rb', line 54

def self.build_microservice(bridge_name:, scope:, parent: nil, shard: 0, plugin: nil)
  microservice_name = "#{scope}__BRIDGE__#{bridge_name.to_s.upcase}"
  MicroserviceModel.new(
    name: microservice_name,
    cmd: [bridge_python_bin, 'bridge_microservice.py', microservice_name],
    work_dir: BRIDGE_WORK_DIR,
    options: [['BRIDGE_NAME', bridge_name.to_s.upcase]],
    parent: parent,
    shard: shard.to_i,
    plugin: plugin,
    scope: scope
  )
end

.get(name:, scope:) ⇒ Object

NOTE: The following three class methods are reimplemented (like other scoped models) so the ModelController and Model class methods work.



70
71
72
# File 'lib/openc3/models/bridge_model.rb', line 70

def self.get(name:, scope:)
  super("#{scope}__#{PRIMARY_KEY}", name: name)
end

.names(scope:) ⇒ Object



74
75
76
# File 'lib/openc3/models/bridge_model.rb', line 74

def self.names(scope:)
  super("#{scope}__#{PRIMARY_KEY}")
end

Instance Method Details

#as_json(*_a) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/openc3/models/bridge_model.rb', line 104

def as_json(*_a)
  {
    'name' => @name,
    'scope' => @scope,
    'updated_at' => @updated_at,
    'plugin' => @plugin,
    'public_key' => @public_key,
    'ticket' => @ticket,
    'app_public_key' => @app_public_key,
    'enroll_code' => @enroll_code,
    'enroll_code_generated_at' => @enroll_code_generated_at,
    'port' => @port
  }
end

#generate_enrollment_tokenObject

Generate a one-time manual-enrollment code, store it on this bridge, and return a token (base64 JSON of bridge name + hub ticket + code) that a remote openc3-app pastes to enroll. Requires the bridge to be up (ticket present). The code is redeemed once over the api/enroll ALPN.



123
124
125
126
127
128
129
130
131
# File 'lib/openc3/models/bridge_model.rb', line 123

def generate_enrollment_token
  raise "Bridge '#{@name}' has no ticket yet; is its bridge_microservice running?" if @ticket.nil? || @ticket.empty?

  @enroll_code = SecureRandom.hex(16)
  @enroll_code_generated_at = Time.now.to_i
  update()
  payload = { 'v' => 1, 'bridge' => @name, 'ticket' => @ticket, 'code' => @enroll_code }
  Base64.urlsafe_encode64(JSON.generate(payload), padding: false)
end