lex-node 0.2.3 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0260fcda3e0a5469d3bfc8d7065d4870addf415732a693da58d979746dcf97b
4
- data.tar.gz: 67a9e3e080786935fea85006e9623cae1aff3a0995d9e507ba37eb723ffff48b
3
+ metadata.gz: 28273777625572488b115147d2eb643bb19129c16b0ed5ec5dd80a22f627fa9e
4
+ data.tar.gz: 8a99ebfdda96540357239dc6b3b94fe54a92b4f4ff76cdf775d55d2c7d7e0357
5
5
  SHA512:
6
- metadata.gz: 27d58631dc278eceb7988cc6125382e55babcac167e2df4a68b386d0b2ac47c8ece02b76f8333732e9d342659d2fc0cc159241fae86a7194a5490ec90be6d504
7
- data.tar.gz: 886335964005f1f868d644c0b084a1a2e82991a9714e00a25154551864c6cf6b55d4d2aaf2d66edd4c9b9313cc1650b07589345c20dbdcb71e95ee815b247384
6
+ metadata.gz: 8eb13c6de81ed6faa745794a5dcd1b2ea469609dd6abc08a8a5c6a4180b2777df63d20afe5651bea69919520296df02a5bcf13b9fbe990c9a101969442089440
7
+ data.tar.gz: 23a8582edf5ad2890a493d0cbc7a399adc09715edb2ca47914f4c491c8b9a4842f648475a65f2358c6bbeabb4479b336da8de930d447a4e2611544f3b0c2c71d
data/.gitignore CHANGED
@@ -7,5 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ Gemfile.lock
11
+
10
12
  # rspec failure tracking
11
13
  .rspec_status
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2026-03-18
4
+
5
+ ### Fixed
6
+ - `update_public_key` changed from class method to instance method (was unreachable by AMQP dispatch)
7
+ - `request_cluster_secret` now uses correct message namespace
8
+ - Beat message uses `[:name]` instead of `[:hostname]` for node identity
9
+ - Boot time tracked as class constant (uptime_seconds was always ~0)
10
+ - Added `require 'base64'` for Ruby 3.4+ compatibility
11
+ - Public key encoding standardized to Base64 across all messages
12
+ - Transport settings access uses safe navigation (nil crash prevention)
13
+ - Beat actor uses symbol key for `beat_interval` setting
14
+ - VaultTokenRequest actor now has `use_runner? true` (was dead wiring)
15
+ - Request vault token message Base64-encodes public key
16
+
17
+ ### Changed
18
+ - Consolidated Runners::Crypt into Runners::Node (deleted runners/crypt.rb)
19
+ - Deleted data_test/ directory (broken MySQL-only migrations, zero consumers)
20
+ - Vault runners support multi-cluster token storage (backward-compatible)
21
+
22
+ ### Removed
23
+ - Duplicate push_public_key/receive_cluster_secret in Runners::Node (used Crypt versions)
24
+ - Unused `require 'socket'` in transport queue
25
+ - `|| nil` redundancies in push_cluster_secret message
26
+
3
27
  ## [0.2.3] - 2026-03-16
4
28
 
5
29
  ### Added
data/CLAUDE.md CHANGED
@@ -10,7 +10,7 @@ Core Legion Extension responsible for node identity within a LegionIO cluster. H
10
10
 
11
11
  **GitHub**: https://github.com/LegionIO/lex-node
12
12
  **License**: MIT
13
- **Version**: 0.2.3
13
+ **Version**: 0.3.0
14
14
 
15
15
  ## Architecture
16
16
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Node identity and cluster management for [LegionIO](https://github.com/LegionIO/LegionIO). Handles heartbeat broadcasting, dynamic configuration distribution, cluster secret exchange, Vault token management, and RSA public key distribution between nodes.
4
4
 
5
- **Version**: 0.2.2
5
+ **Version**: 0.3.0
6
6
 
7
7
  This is a core LEX installed by default with LegionIO.
8
8
 
data/lex-node.gemspec CHANGED
@@ -25,4 +25,6 @@ Gem::Specification.new do |spec|
25
25
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
26
  end
27
27
  spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'base64'
28
30
  end
@@ -26,7 +26,7 @@ module Legion
26
26
  end
27
27
 
28
28
  def time
29
- settings['beat_interval']
29
+ settings[:beat_interval]
30
30
  end
31
31
  end
32
32
  end
@@ -14,7 +14,7 @@ module Legion
14
14
  end
15
15
 
16
16
  def use_runner?
17
- false
17
+ true
18
18
  end
19
19
 
20
20
  def check_subtask?
@@ -8,9 +8,9 @@ module Legion
8
8
  include Legion::Extensions::Helpers::Transport
9
9
 
10
10
  def beat(status: 'active', **opts)
11
- log.debug 'sending hearbeat'
11
+ log.debug 'sending heartbeat'
12
12
  messages::Beat.new(status: status).publish
13
- { success: true, status: status, version: Legion::VERSION || nil, **opts }
13
+ { success: true, status: status, version: defined?(Legion::VERSION) ? Legion::VERSION : nil, **opts }
14
14
  end
15
15
 
16
16
  include Legion::Extensions::Helpers::Lex
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
4
+
3
5
  module Legion
4
6
  module Extensions
5
7
  module Node
@@ -60,9 +62,9 @@ module Legion
60
62
  def push_public_key(**_opts)
61
63
  log.debug 'push_public_key'
62
64
  message_hash = { function: 'update_public_key',
63
- public_key: Legion::Crypt.public_key.to_s,
65
+ public_key: Base64.encode64(Legion::Crypt.public_key),
64
66
  **Legion::Settings[:client] }
65
- Legion::Extensions::Node::Transport::Messages::PublicKey.new(**message_hash).publish
67
+ Legion::Extensions::Node::Transport::Messages::PublicKey.new(message_hash).publish
66
68
  {}
67
69
  end
68
70
 
@@ -72,6 +74,25 @@ module Legion
72
74
  {}
73
75
  end
74
76
 
77
+ def delete_public_key(name:, **_opts)
78
+ log.debug 'delete_public_key'
79
+ Legion::Settings[:cluster][:public_keys].delete(name)
80
+ {}
81
+ end
82
+
83
+ def request_public_keys(**_opts)
84
+ log.debug 'request_public_keys'
85
+ message_hash = { function: 'push_public_key' }
86
+ Legion::Extensions::Node::Transport::Messages::RequestPublicKeys.new(**message_hash).publish
87
+ {}
88
+ end
89
+
90
+ def request_cluster_secret(**_opts)
91
+ log.debug 'request_cluster_secret'
92
+ Legion::Extensions::Node::Transport::Messages::RequestClusterSecret.new.publish
93
+ {}
94
+ end
95
+
75
96
  def push_cluster_secret(public_key:, queue_name:, **_opts)
76
97
  log.debug 'push_cluster_secret'
77
98
  return {} unless Legion::Settings[:crypt][:cs_encrypt_ready]
@@ -86,9 +107,11 @@ module Legion
86
107
  {}
87
108
  end
88
109
 
89
- def receive_cluster_secret(message:, **_opts)
110
+ def receive_cluster_secret(message:, **opts)
90
111
  log.debug 'receive_cluster_secret'
91
112
  Legion::Settings[:crypt][:cluster_secret] = Legion::Crypt.decrypt_from_keypair(message: message)
113
+ Legion::Settings[:crypt][:encrypted_string] = opts[:encrypted_string]
114
+ Legion::Settings[:crypt][:validation_string] = opts[:validation_string]
92
115
  {}
93
116
  end
94
117
 
@@ -17,10 +17,18 @@ module Legion
17
17
  {}
18
18
  end
19
19
 
20
- def receive_vault_token(message:, **opts)
21
- return if Legion::Settings[:crypt][:vault][:connected]
20
+ def receive_vault_token(message: nil, token: nil, cluster_name: nil, **opts)
21
+ return { success: false, already_connected: true } if Legion::Settings[:crypt][:vault][:connected]
22
+
23
+ token ||= Legion::Crypt.decrypt_from_keypair(message: message)
24
+ clusters = Legion::Settings[:crypt][:vault][:clusters]
25
+ if cluster_name && clusters.is_a?(Hash) && clusters[cluster_name.to_sym]
26
+ clusters[cluster_name.to_sym][:token] = token
27
+ clusters[cluster_name.to_sym][:connected] = true
28
+ return { success: true }
29
+ end
22
30
 
23
- Legion::Settings[:crypt][:vault][:token] = Legion::Crypt.decrypt_from_keypair(message: message)
31
+ Legion::Settings[:crypt][:vault][:token] = token
24
32
  %i[protocol address port].each do |setting|
25
33
  next unless opts.key? setting
26
34
  next unless Legion::Settings[:crypt][:vault][setting].nil?
@@ -6,6 +6,7 @@ module Legion
6
6
  module Transport
7
7
  module Messages
8
8
  class Beat < Legion::Transport::Message
9
+ BOOT_TIME = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
9
10
  def routing_key
10
11
  'status'
11
12
  end
@@ -24,7 +25,7 @@ module Legion
24
25
 
25
26
  def message
26
27
  hash = {
27
- name: Legion::Settings[:client][:hostname],
28
+ name: Legion::Settings[:client][:name],
28
29
  pid: ::Process.pid,
29
30
  timestamp: Time.now,
30
31
  status: @options[:status].nil? ? 'healthy' : @options[:status]
@@ -72,11 +73,7 @@ module Legion
72
73
  end
73
74
 
74
75
  def uptime_seconds
75
- (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - boot_time).round(0)
76
- end
77
-
78
- def boot_time
79
- @boot_time ||= ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
76
+ (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - BOOT_TIME).round(0)
80
77
  end
81
78
 
82
79
  def collect_worker_ids
@@ -18,8 +18,8 @@ module Legion
18
18
  { function: 'receive_cluster_secret',
19
19
  runner_class: 'Legion::Extensions::Node::Runners::Crypt',
20
20
  message: @options[:message],
21
- validation_string: @options[:validation_string] || nil,
22
- encrypted_string: @options[:encrypted_string] || nil,
21
+ validation_string: @options[:validation_string],
22
+ encrypted_string: @options[:encrypted_string],
23
23
  public_key: Base64.encode64(Legion::Crypt.public_key) }
24
24
  end
25
25
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
4
+
3
5
  module Legion
4
6
  module Extensions
5
7
  module Node
@@ -15,7 +17,7 @@ module Legion
15
17
  function: 'push_vault_token',
16
18
  node_name: Legion::Settings[:client][:name],
17
19
  runner_class: 'Legion::Extensions::Node::Runners::Vault',
18
- public_key: Legion::Crypt.public_key
20
+ public_key: Base64.encode64(Legion::Crypt.public_key.to_s)
19
21
  }
20
22
  end
21
23
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'socket'
4
-
5
3
  module Legion
6
4
  module Extensions
7
5
  module Node
@@ -10,8 +10,8 @@ module Legion
10
10
 
11
11
  def self.additional_e_to_q
12
12
  array = [{ from: 'node', to: 'node', routing_key: "node.#{Legion::Settings[:client][:name]}" }]
13
- array.push(from: 'node', to: 'node', routing_key: 'node.data.#') if Legion::Settings[:data][:connected]
14
- array.push(from: 'node', to: 'node', routing_key: 'node.cache.#') if Legion::Settings[:cache][:connected]
13
+ array.push(from: 'node', to: 'node', routing_key: 'node.data.#') if Legion::Settings[:data]&.[](:connected) || false
14
+ array.push(from: 'node', to: 'node', routing_key: 'node.cache.#') if Legion::Settings[:cache]&.[](:connected) || false
15
15
  array.push(from: 'node', to: 'node', routing_key: 'node.crypt.#')
16
16
  array
17
17
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Node
6
- VERSION = '0.2.3'
6
+ VERSION = '0.3.0'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-node
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  description: This lex is responsible for sending heartbeats, allowing for dynamic
13
27
  config, cluster secret, etc
14
28
  email:
@@ -25,7 +39,6 @@ files:
25
39
  - CLAUDE.md
26
40
  - Dockerfile
27
41
  - Gemfile
28
- - Gemfile.lock
29
42
  - LICENSE
30
43
  - README.md
31
44
  - docker_deploy.rb
@@ -41,7 +54,6 @@ files:
41
54
  - lib/legion/extensions/node/data_test/migrations/003_legion_version_colume.rb
42
55
  - lib/legion/extensions/node/data_test/migrations/004_node_extensions.rb
43
56
  - lib/legion/extensions/node/runners/beat.rb
44
- - lib/legion/extensions/node/runners/crypt.rb
45
57
  - lib/legion/extensions/node/runners/node.rb
46
58
  - lib/legion/extensions/node/runners/vault.rb
47
59
  - lib/legion/extensions/node/transport.rb
data/Gemfile.lock DELETED
@@ -1,87 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- lex-node (0.2.3)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- addressable (2.8.9)
10
- public_suffix (>= 2.0.2, < 8.0)
11
- ast (2.4.3)
12
- bigdecimal (4.0.1)
13
- diff-lcs (1.6.2)
14
- docile (1.4.1)
15
- json (2.19.1)
16
- json-schema (6.2.0)
17
- addressable (~> 2.8)
18
- bigdecimal (>= 3.1, < 5)
19
- language_server-protocol (3.17.0.5)
20
- lint_roller (1.1.0)
21
- mcp (0.8.0)
22
- json-schema (>= 4.1)
23
- parallel (1.27.0)
24
- parser (3.3.10.2)
25
- ast (~> 2.4.1)
26
- racc
27
- prism (1.9.0)
28
- public_suffix (7.0.5)
29
- racc (1.8.1)
30
- rainbow (3.1.1)
31
- rake (13.3.1)
32
- regexp_parser (2.11.3)
33
- rspec (3.13.2)
34
- rspec-core (~> 3.13.0)
35
- rspec-expectations (~> 3.13.0)
36
- rspec-mocks (~> 3.13.0)
37
- rspec-core (3.13.6)
38
- rspec-support (~> 3.13.0)
39
- rspec-expectations (3.13.5)
40
- diff-lcs (>= 1.2.0, < 2.0)
41
- rspec-support (~> 3.13.0)
42
- rspec-mocks (3.13.8)
43
- diff-lcs (>= 1.2.0, < 2.0)
44
- rspec-support (~> 3.13.0)
45
- rspec-support (3.13.7)
46
- rspec_junit_formatter (0.6.0)
47
- rspec-core (>= 2, < 4, != 2.12.0)
48
- rubocop (1.85.1)
49
- json (~> 2.3)
50
- language_server-protocol (~> 3.17.0.2)
51
- lint_roller (~> 1.1.0)
52
- mcp (~> 0.6)
53
- parallel (~> 1.10)
54
- parser (>= 3.3.0.2)
55
- rainbow (>= 2.2.2, < 4.0)
56
- regexp_parser (>= 2.9.3, < 3.0)
57
- rubocop-ast (>= 1.49.0, < 2.0)
58
- ruby-progressbar (~> 1.7)
59
- unicode-display_width (>= 2.4.0, < 4.0)
60
- rubocop-ast (1.49.1)
61
- parser (>= 3.3.7.2)
62
- prism (~> 1.7)
63
- ruby-progressbar (1.13.0)
64
- simplecov (0.22.0)
65
- docile (~> 1.1)
66
- simplecov-html (~> 0.11)
67
- simplecov_json_formatter (~> 0.1)
68
- simplecov-html (0.13.2)
69
- simplecov_json_formatter (0.1.4)
70
- unicode-display_width (3.2.0)
71
- unicode-emoji (~> 4.1)
72
- unicode-emoji (4.2.0)
73
-
74
- PLATFORMS
75
- arm64-darwin-25
76
- ruby
77
-
78
- DEPENDENCIES
79
- lex-node!
80
- rake
81
- rspec
82
- rspec_junit_formatter
83
- rubocop
84
- simplecov
85
-
86
- BUNDLED WITH
87
- 2.6.9
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Node
6
- module Runners
7
- module Crypt
8
- def push_public_key(**_opts)
9
- log.debug 'push_public_key'
10
- message_hash = { function: 'update_public_key',
11
- public_key: Base64.encode64(Legion::Crypt.public_key),
12
- **Legion::Settings[:client] }
13
- Legion::Extensions::Node::Transport::Messages::PublicKey.new(message_hash).publish
14
- {}
15
- end
16
-
17
- def self.update_public_key(name:, public_key:, **_opts)
18
- log.debug 'update_public_key'
19
- Legion::Settings[:cluster][:public_keys][name] = public_key
20
- {}
21
- end
22
-
23
- def delete_public_key(name:, **_opts)
24
- log.debug 'delete_public_key'
25
- Legion::Settings[:cluster][:public_keys].delete(name)
26
- {}
27
- end
28
-
29
- def request_public_keys(**_opts)
30
- log.debug 'request_public_keys'
31
- message_hash = { function: 'push_public_key' }
32
- Legion::Extensions::Node::Transport::Messages::RequestPublicKeys.new(**message_hash).publish
33
- {}
34
- end
35
-
36
- def request_cluster_secret(**_opts)
37
- log.debug 'request_cluster_secret'
38
- Legion::Transport::Messages::RequestClusterSecret.new.publish
39
- {}
40
- end
41
-
42
- def push_cluster_secret(public_key:, queue_name:, **_opts)
43
- log.debug 'push_cluster_secret'
44
- return {} unless Legion::Settings[:crypt][:cs_encrypt_ready]
45
-
46
- encrypted = Legion::Crypt.encrypt_from_keypair(pub_key: public_key,
47
- message: Legion::Settings[:crypt][:cluster_secret].to_s)
48
- legion = Legion::Crypt.encrypt('legion')
49
- Legion::Extensions::Node::Transport::Messages::PushClusterSecret.new(message: encrypted,
50
- queue_name: queue_name,
51
- validation_string: 'legion',
52
- encrypted_string: legion).publish
53
- {}
54
- end
55
-
56
- def receive_cluster_secret(message:, **opts)
57
- log.debug 'receive_cluster_secret'
58
- Legion::Settings[:crypt][:cluster_secret] = Legion::Crypt.decrypt_from_keypair(message)
59
- Legion::Settings[:crypt][:encrypted_string] = opts[:encrypted_string]
60
- Legion::Settings[:crypt][:validation_string] = opts[:validation_string]
61
- {}
62
- end
63
-
64
- include Legion::Extensions::Helpers::Lex
65
- end
66
- end
67
- end
68
- end
69
- end