messhy 0.8.2 → 0.8.3
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 +4 -4
- data/CHANGELOG.md +51 -0
- data/README.md +22 -25
- data/SECURITY.md +21 -0
- data/lib/messhy/dns_manager.rb +3 -3
- data/lib/messhy/health_checker.rb +3 -5
- data/lib/messhy/mesh_builder.rb +2 -2
- data/lib/messhy/ssh_executor.rb +1 -3
- data/lib/messhy/version.rb +1 -1
- data/lib/messhy/wireguard_status_parser.rb +25 -10
- metadata +12 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1c3b2d1ed2cc9058e323f73a2df9e9a9543709583cead2e9d9a23632c91a18f
|
|
4
|
+
data.tar.gz: c10668cc8cfab172b6dd6b267265fbc310baa4d31323661efb6b2fc32062d152
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f1f2154ec3f236ae9aeb741ffa338af96a9ac4a4ba29db327da04bc2e21c719bde98bd1b1e572cb1d415a1fadf64c5d3f54e2807fd27f2909fddf3dcb18af12
|
|
7
|
+
data.tar.gz: 4ee6e07ed4089a669ab5d11e7c1c074a845fff98782a42c282088c62289d96707df99ab287c4001065d0c097601e75cd1388019afaf60ee195f6be4fc4662ec9
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.8.3] - 2026-08-20
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Require Ruby 4.0 or newer.
|
|
15
|
+
- Align runtime and development dependencies with the maintained toolchain.
|
|
16
|
+
- Add blocking test, lint, dependency-audit, and package-build CI checks.
|
|
17
|
+
- Add automated Bundler and GitHub Actions dependency updates.
|
|
18
|
+
- Document key custody, SSH trust, and full-mesh security boundaries.
|
|
19
|
+
|
|
20
|
+
## [0.1.0] - 2025-01-21
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Initial release of Messhy
|
|
24
|
+
- Full WireGuard VPN mesh network setup across any VMs
|
|
25
|
+
- Every node connects directly to every other node (full mesh topology)
|
|
26
|
+
- Automated WireGuard installation and configuration
|
|
27
|
+
- Dynamic key generation and distribution
|
|
28
|
+
- Automatic peer configuration across all nodes
|
|
29
|
+
- Rails integration with generators and rake tasks
|
|
30
|
+
- Standalone Ruby support (no Rails dependency required)
|
|
31
|
+
- SSH-based deployment (no Python, Ansible, or Kubernetes)
|
|
32
|
+
- Health checking and status monitoring
|
|
33
|
+
- WireGuard status parsing and connection verification
|
|
34
|
+
- Host trust management for SSH connections
|
|
35
|
+
- Support for both public and private IP addresses
|
|
36
|
+
- YAML-based configuration with ERB support
|
|
37
|
+
- Secret management via environment variables or command execution
|
|
38
|
+
|
|
39
|
+
### Technical Details
|
|
40
|
+
- Uses SSHKit for SSH operations
|
|
41
|
+
- Thor-based CLI interface with `messhy` command
|
|
42
|
+
- Automatic IP allocation for VPN network (10.8.0.0/24 by default)
|
|
43
|
+
- UDP port configuration (default: 51820)
|
|
44
|
+
- Compatible with Ubuntu 20.04+ servers (Debian-based distributions)
|
|
45
|
+
- Ruby >= 3.4.0 required
|
|
46
|
+
|
|
47
|
+
### Commands
|
|
48
|
+
- `messhy setup` - Deploy WireGuard mesh network
|
|
49
|
+
- `messhy status` - Check mesh network health and connectivity
|
|
50
|
+
- `messhy add_node` - Add new node to existing mesh
|
|
51
|
+
- `messhy remove_node` - Remove node from mesh
|
data/README.md
CHANGED
|
@@ -6,21 +6,10 @@ A Ruby gem that sets up a full WireGuard VPN mesh across any VMs. Every node con
|
|
|
6
6
|
|
|
7
7
|
## Why messhy?
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- Complex VPN configurations
|
|
14
|
-
- NAT traversal issues
|
|
15
|
-
- Manual key management
|
|
16
|
-
- Cloud-specific networking (VPC, subnet, security groups)
|
|
17
|
-
|
|
18
|
-
✅ **With messhy:**
|
|
19
|
-
- Secure encrypted connections (WireGuard)
|
|
20
|
-
- Automatic key generation and distribution
|
|
21
|
-
- Works across any cloud/datacenter
|
|
22
|
-
- Zero application changes (just use 10.8.0.x IPs)
|
|
23
|
-
- Simple configuration
|
|
9
|
+
Messhy generates WireGuard keys and peer configuration, installs it over SSH,
|
|
10
|
+
and gives each node a stable private address. It is intended for small fleets
|
|
11
|
+
whose operators control every host and can explicitly allow WireGuard traffic
|
|
12
|
+
between peers.
|
|
24
13
|
|
|
25
14
|
## Installation
|
|
26
15
|
|
|
@@ -285,17 +274,25 @@ iptables -A INPUT -p udp --dport 51820 -j ACCEPT
|
|
|
285
274
|
- Optimal routing (direct connections)
|
|
286
275
|
- Scales to ~50 nodes
|
|
287
276
|
|
|
288
|
-
##
|
|
277
|
+
## Capacity and performance
|
|
289
278
|
|
|
290
|
-
|
|
279
|
+
A full mesh creates one peer relationship between every pair of nodes, so the
|
|
280
|
+
configuration grows quadratically. Messhy is designed for small fleets (about
|
|
281
|
+
50 nodes or fewer), not large or frequently changing membership. Throughput,
|
|
282
|
+
latency, and CPU overhead depend on the hosts, network path, MTU, and workload;
|
|
283
|
+
benchmark the actual topology before relying on a capacity estimate.
|
|
291
284
|
|
|
292
|
-
|
|
293
|
-
|--------|--------|-----------|----------|
|
|
294
|
-
| Throughput | 1000 Mbps | 950 Mbps | 5% |
|
|
295
|
-
| Latency | 10ms | 10.5ms | +0.5ms |
|
|
296
|
-
| CPU usage | 2% | 3% | +1% |
|
|
285
|
+
## Security boundaries
|
|
297
286
|
|
|
298
|
-
|
|
287
|
+
- `trust-hosts` collects host keys with `ssh-keyscan`; verify fingerprints over
|
|
288
|
+
a separate trusted channel before relying on them.
|
|
289
|
+
- Anyone with a generated node private key can impersonate that node. Store
|
|
290
|
+
`.secrets/wireguard` in an encrypted vault and rotate keys after suspected
|
|
291
|
+
disclosure or membership changes.
|
|
292
|
+
- Messhy configures WireGuard, but it does not replace host firewalls, patching,
|
|
293
|
+
SSH hardening, or application-level authentication.
|
|
294
|
+
- Full-mesh membership grants direct network reachability. Only enroll hosts
|
|
295
|
+
that should be able to reach one another.
|
|
299
296
|
|
|
300
297
|
## Troubleshooting
|
|
301
298
|
|
|
@@ -330,7 +327,7 @@ messhy setup
|
|
|
330
327
|
|
|
331
328
|
## Requirements
|
|
332
329
|
|
|
333
|
-
- Ruby
|
|
330
|
+
- Ruby 4.0+
|
|
334
331
|
- WireGuard tools (`wg` command)
|
|
335
332
|
- Target servers with Linux kernel 5.6+ (WireGuard built-in)
|
|
336
333
|
- SSH key-based authentication
|
|
@@ -345,4 +342,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
345
342
|
|
|
346
343
|
---
|
|
347
344
|
|
|
348
|
-
|
|
345
|
+
Maintained by [BoringCache](https://github.com/boringcache).
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
Please do not open a public issue for a potential vulnerability. Email
|
|
6
|
+
[security@boringcache.com](mailto:security@boringcache.com) or use
|
|
7
|
+
[GitHub private vulnerability reporting](https://github.com/boringcache/messhy/security/advisories/new).
|
|
8
|
+
|
|
9
|
+
Include the affected version, expected impact, and enough reproduction detail
|
|
10
|
+
for us to investigate. Do not include live credentials, private keys, customer
|
|
11
|
+
data, or production configuration.
|
|
12
|
+
|
|
13
|
+
## Supported Versions
|
|
14
|
+
|
|
15
|
+
Only the latest published release is supported. Security fixes are normally
|
|
16
|
+
shipped in a new release rather than backported.
|
|
17
|
+
|
|
18
|
+
## Scope
|
|
19
|
+
|
|
20
|
+
Reports about WireGuard key handling, SSH host verification, generated network
|
|
21
|
+
configuration, command execution, or release integrity are welcome.
|
data/lib/messhy/dns_manager.rb
CHANGED
|
@@ -79,7 +79,7 @@ module Messhy
|
|
|
79
79
|
execute :sudo, 'resolvectl', 'flush-caches', raise_on_error: false
|
|
80
80
|
elsif test('[ -d /etc/resolvconf/resolv.conf.d ]', raise_on_error: false)
|
|
81
81
|
head_path = '/etc/resolvconf/resolv.conf.d/head'
|
|
82
|
-
content = server_ips.map { |ip| "nameserver #{ip}" }.join("\n")
|
|
82
|
+
content = "#{server_ips.map { |ip| "nameserver #{ip}" }.join("\n")}\n"
|
|
83
83
|
upload! StringIO.new(content), '/tmp/messhy-resolv.conf'
|
|
84
84
|
execute :sudo, 'mv', '/tmp/messhy-resolv.conf', head_path
|
|
85
85
|
execute :sudo, 'chmod', '644', head_path
|
|
@@ -112,7 +112,7 @@ module Messhy
|
|
|
112
112
|
end
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
records.transform_values
|
|
115
|
+
records.transform_values(&:uniq)
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def resolve_target(target)
|
|
@@ -159,7 +159,7 @@ module Messhy
|
|
|
159
159
|
end
|
|
160
160
|
end
|
|
161
161
|
|
|
162
|
-
lines.join("\n")
|
|
162
|
+
"#{lines.join("\n")}\n"
|
|
163
163
|
end
|
|
164
164
|
end
|
|
165
165
|
end
|
|
@@ -94,7 +94,6 @@ module Messhy
|
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
98
97
|
def test_all
|
|
99
98
|
puts '==> Testing mesh connectivity...'
|
|
100
99
|
puts
|
|
@@ -148,7 +147,6 @@ module Messhy
|
|
|
148
147
|
puts 'If a pair still reports a failure, there has been no recent handshake—check UDP 51820 and ' \
|
|
149
148
|
'keepalive/route settings.'
|
|
150
149
|
end
|
|
151
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
152
150
|
|
|
153
151
|
def show_stats(node: nil)
|
|
154
152
|
if node
|
|
@@ -179,10 +177,10 @@ module Messhy
|
|
|
179
177
|
service = capture(:systemctl, 'is-active', 'dnsmasq', raise_on_non_zero_exit: false).strip
|
|
180
178
|
messhy_records = capture(:bash, '-c',
|
|
181
179
|
"sudo awk 'BEGIN{c=0} /^address=\\//{c++} END{print c}' " \
|
|
182
|
-
|
|
180
|
+
'/etc/dnsmasq.d/messhy.conf 2>/dev/null || true').strip
|
|
183
181
|
ap_records = capture(:bash, '-c',
|
|
184
182
|
"sudo awk 'BEGIN{c=0} /^address=\\//{c++} END{print c}' " \
|
|
185
|
-
|
|
183
|
+
'/etc/dnsmasq.d/active_postgres.conf 2>/dev/null || true').strip
|
|
186
184
|
|
|
187
185
|
status_icon = service == 'active' ? '✓' : '✗'
|
|
188
186
|
puts "#{status_icon} #{node_name} (#{node_config['private_ip']})#{label_display} - dnsmasq #{service}"
|
|
@@ -224,7 +222,7 @@ module Messhy
|
|
|
224
222
|
end
|
|
225
223
|
|
|
226
224
|
max_name_len = node_names.map(&:length).max
|
|
227
|
-
header = ' ' * (max_name_len + 2) + node_names.map { |n| n[0..7].rjust(8) }.join(' ')
|
|
225
|
+
header = (' ' * (max_name_len + 2)) + node_names.map { |n| n[0..7].rjust(8) }.join(' ')
|
|
228
226
|
puts header
|
|
229
227
|
puts '-' * header.length
|
|
230
228
|
|
data/lib/messhy/mesh_builder.rb
CHANGED
|
@@ -38,9 +38,9 @@ module Messhy
|
|
|
38
38
|
if config.dns_enabled?
|
|
39
39
|
dns_domain = config.dns_domain
|
|
40
40
|
dns_interface = config.dns_interface
|
|
41
|
-
dns_server_ips = config.dns_server_nodes.filter_map
|
|
41
|
+
dns_server_ips = config.dns_server_nodes.filter_map do |name|
|
|
42
42
|
config.node_config(name)&.dig('private_ip')
|
|
43
|
-
|
|
43
|
+
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
is_dns_server = config.dns_enabled? && config.dns_server_nodes.include?(node_name)
|
data/lib/messhy/ssh_executor.rb
CHANGED
|
@@ -176,9 +176,7 @@ module Messhy
|
|
|
176
176
|
latency = nil
|
|
177
177
|
execute_on_node(source_node) do
|
|
178
178
|
output = capture(:ping, '-c', '3', '-W', '2', '-I', 'wg0', target_ip, raise_on_non_zero_exit: false)
|
|
179
|
-
if output =~ %r{rtt min/avg/max/mdev = [\d.]+/([\d.]+)/}
|
|
180
|
-
latency = ::Regexp.last_match(1).to_f
|
|
181
|
-
end
|
|
179
|
+
latency = ::Regexp.last_match(1).to_f if output =~ %r{rtt min/avg/max/mdev = [\d.]+/([\d.]+)/}
|
|
182
180
|
end
|
|
183
181
|
latency
|
|
184
182
|
rescue StandardError
|
data/lib/messhy/version.rb
CHANGED
|
@@ -20,35 +20,50 @@ module Messhy
|
|
|
20
20
|
def parse_handshake_seconds(desc)
|
|
21
21
|
return nil if desc.strip.casecmp('(none)').zero?
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
total = 0
|
|
24
|
+
words = desc.downcase.delete(',').split
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
words.each_cons(2) do |value, unit|
|
|
27
|
+
unit = unit.delete_suffix('s')
|
|
28
|
+
next unless integer_token?(value) && TIME_UNITS_IN_SECONDS.key?(unit)
|
|
29
|
+
|
|
30
|
+
total += TIME_UNITS_IN_SECONDS.fetch(unit) * value.to_i
|
|
28
31
|
end
|
|
32
|
+
|
|
33
|
+
total.positive? ? total : nil
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
def extract_handshake_time(peer_block)
|
|
32
37
|
return nil unless peer_block
|
|
33
38
|
|
|
34
|
-
desc = peer_block
|
|
39
|
+
desc = line_value(peer_block, 'latest handshake: ')
|
|
35
40
|
return nil unless desc&.include?('ago')
|
|
36
41
|
|
|
37
42
|
parse_handshake_seconds(desc)
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
def extract_transfer_stats(peer_block)
|
|
41
|
-
|
|
42
|
-
tx =
|
|
43
|
-
|
|
46
|
+
transfer = line_value(peer_block, 'transfer: ')
|
|
47
|
+
rx, tx = transfer&.split(' received, ', 2)
|
|
48
|
+
tx = tx&.delete_suffix(' sent')
|
|
49
|
+
|
|
50
|
+
{ received: rx || '0 B', sent: tx || '0 B' }
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
def extract_endpoint(peer_block)
|
|
47
|
-
peer_block
|
|
54
|
+
line_value(peer_block, 'endpoint: ')
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
def extract_allowed_ips(peer_block)
|
|
51
|
-
peer_block
|
|
58
|
+
line_value(peer_block, 'allowed ips: ')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def line_value(block, prefix)
|
|
62
|
+
block.each_line(chomp: true).find { |line| line.start_with?(prefix) }&.delete_prefix(prefix)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def integer_token?(value)
|
|
66
|
+
!value.empty? && value.each_char.all? { |char| char.between?('0', '9') }
|
|
52
67
|
end
|
|
53
68
|
end
|
|
54
69
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: messhy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- BoringCache
|
|
@@ -29,84 +29,42 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '1.
|
|
32
|
+
version: '1.4'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '1.
|
|
39
|
+
version: '1.4'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: sshkit
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '1.
|
|
46
|
+
version: '1.25'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '1.
|
|
53
|
+
version: '1.25'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: thor
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '1.
|
|
60
|
+
version: '1.5'
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '1.
|
|
68
|
-
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: minitest
|
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - "~>"
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '5.0'
|
|
75
|
-
type: :development
|
|
76
|
-
prerelease: false
|
|
77
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - "~>"
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: '5.0'
|
|
82
|
-
- !ruby/object:Gem::Dependency
|
|
83
|
-
name: rubocop
|
|
84
|
-
requirement: !ruby/object:Gem::Requirement
|
|
85
|
-
requirements:
|
|
86
|
-
- - "~>"
|
|
87
|
-
- !ruby/object:Gem::Version
|
|
88
|
-
version: '1.50'
|
|
89
|
-
type: :development
|
|
90
|
-
prerelease: false
|
|
91
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
-
requirements:
|
|
93
|
-
- - "~>"
|
|
94
|
-
- !ruby/object:Gem::Version
|
|
95
|
-
version: '1.50'
|
|
96
|
-
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: simplecov
|
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - "~>"
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0.22'
|
|
103
|
-
type: :development
|
|
104
|
-
prerelease: false
|
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - "~>"
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: '0.22'
|
|
67
|
+
version: '1.5'
|
|
110
68
|
description: Sets up a full WireGuard VPN mesh across any VMs. Every node connects
|
|
111
69
|
directly to every other node for secure private networking.
|
|
112
70
|
email:
|
|
@@ -116,8 +74,10 @@ executables:
|
|
|
116
74
|
extensions: []
|
|
117
75
|
extra_rdoc_files: []
|
|
118
76
|
files:
|
|
77
|
+
- CHANGELOG.md
|
|
119
78
|
- LICENSE
|
|
120
79
|
- README.md
|
|
80
|
+
- SECURITY.md
|
|
121
81
|
- exe/messhy
|
|
122
82
|
- lib/messhy.rb
|
|
123
83
|
- lib/messhy/cli.rb
|
|
@@ -138,10 +98,10 @@ homepage: https://github.com/boringcache/messhy
|
|
|
138
98
|
licenses:
|
|
139
99
|
- MIT
|
|
140
100
|
metadata:
|
|
141
|
-
homepage_uri: https://github.com/boringcache/messhy
|
|
142
101
|
source_code_uri: https://github.com/boringcache/messhy
|
|
143
102
|
documentation_uri: https://github.com/boringcache/messhy/blob/main/README.md
|
|
144
103
|
changelog_uri: https://github.com/boringcache/messhy/blob/main/CHANGELOG.md
|
|
104
|
+
bug_tracker_uri: https://github.com/boringcache/messhy/issues
|
|
145
105
|
rubygems_mfa_required: 'true'
|
|
146
106
|
rdoc_options: []
|
|
147
107
|
require_paths:
|
|
@@ -150,14 +110,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
150
110
|
requirements:
|
|
151
111
|
- - ">="
|
|
152
112
|
- !ruby/object:Gem::Version
|
|
153
|
-
version:
|
|
113
|
+
version: 4.0.0
|
|
154
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
115
|
requirements:
|
|
156
116
|
- - ">="
|
|
157
117
|
- !ruby/object:Gem::Version
|
|
158
118
|
version: '0'
|
|
159
119
|
requirements: []
|
|
160
|
-
rubygems_version:
|
|
120
|
+
rubygems_version: 4.0.16
|
|
161
121
|
specification_version: 4
|
|
162
122
|
summary: WireGuard VPN mesh for Ruby & Rails apps
|
|
163
123
|
test_files: []
|