legionio 1.6.1 → 1.6.2
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/.gitignore +2 -1
- data/CHANGELOG.md +6 -0
- data/lib/legion/cli/update_command.rb +20 -25
- data/lib/legion/extensions/helpers/lex.rb +53 -0
- data/lib/legion/version.rb +1 -1
- metadata +1 -9
- data/docs/plans/2026-03-18-config-import-vault-multicluster-design.md +0 -272
- data/docs/plans/2026-03-18-config-import-vault-multicluster-implementation.md +0 -833
- data/docs/plans/2026-03-18-core-lex-uplift-design.md +0 -252
- data/docs/plans/2026-03-18-core-lex-uplift-implementation.md +0 -1816
- data/docs/plans/2026-03-18-legion-tty-default-cli-design.md +0 -162
- data/docs/plans/2026-03-18-legion-tty-default-cli-implementation.md +0 -1049
- data/docs/plans/2026-03-19-hooks-expansion-design.md +0 -211
- data/legionio_local.db +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49e219b9a9c790b6ec63903daee3a26194a5a1611cccd0013a05bb7a34a3d7c9
|
|
4
|
+
data.tar.gz: eedbb6e81c2ba386cee0718eb3aed307a021d25959579bb2a02c8bd011ca4fa5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 141f1ad15ae3595a411e4e1ddaf7fa4544bd1fd5aa23be7425574e783a55581773d0a860ace1d7f6dce97bf3004b6fc42998374cdfe8b3555b01547e98be5f1e
|
|
7
|
+
data.tar.gz: a05b2f2cb76d9237dc785b396af6f72b03657866ba4f57fd9b7673594d3212634e9c920d90529c5134544cbdb8d32e9994b188e155366f6f69f7df803355dc90
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.2] - 2026-03-26
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- `legionio update` remote check failed for all gems due to TCP connection exhaustion (24 parallel SSL connections to rubygems.org)
|
|
7
|
+
- Replace thread pool with 4 batched threads using persistent HTTP keep-alive connections (55 gems in ~4s)
|
|
8
|
+
|
|
3
9
|
## [1.6.1] - 2026-03-26
|
|
4
10
|
|
|
5
11
|
### Fixed
|
|
@@ -127,36 +127,31 @@ module Legion
|
|
|
127
127
|
|
|
128
128
|
def fetch_remote_versions_parallel(gem_names)
|
|
129
129
|
results = Concurrent::Hash.new
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
version = fetch_remote_version(name)
|
|
136
|
-
results[name] = version if version
|
|
137
|
-
rescue StandardError => e
|
|
138
|
-
Legion::Logging.debug("UpdateCommand#fetch_remote_version #{name}: #{e.message}") if defined?(Legion::Logging)
|
|
139
|
-
ensure
|
|
140
|
-
latch.count_down
|
|
130
|
+
thread_count = [gem_names.size, 4].min
|
|
131
|
+
slices = gem_names.each_slice((gem_names.size / thread_count.to_f).ceil).to_a
|
|
132
|
+
threads = slices.map do |batch|
|
|
133
|
+
Thread.new(batch) do |names|
|
|
134
|
+
fetch_batch(names, results)
|
|
141
135
|
end
|
|
142
136
|
end
|
|
143
|
-
|
|
144
|
-
latch.wait(30)
|
|
145
|
-
pool.shutdown
|
|
137
|
+
threads.each { |t| t.join(60) }
|
|
146
138
|
results
|
|
147
139
|
end
|
|
148
140
|
|
|
149
|
-
def
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
141
|
+
def fetch_batch(names, results)
|
|
142
|
+
Net::HTTP.start('rubygems.org', 443, use_ssl: true, open_timeout: 10, read_timeout: 10) do |http|
|
|
143
|
+
names.each do |name|
|
|
144
|
+
response = http.request(Net::HTTP::Get.new("/api/v1/versions/#{name}/latest.json"))
|
|
145
|
+
next unless response.is_a?(Net::HTTPSuccess)
|
|
146
|
+
|
|
147
|
+
data = ::JSON.parse(response.body)
|
|
148
|
+
results[name] = data['version'] if data['version']
|
|
149
|
+
rescue StandardError => e
|
|
150
|
+
Legion::Logging.debug("UpdateCommand#fetch_batch #{name}: #{e.message}") if defined?(Legion::Logging)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
rescue StandardError => e
|
|
154
|
+
Legion::Logging.debug("UpdateCommand#fetch_batch connection: #{e.message}") if defined?(Legion::Logging)
|
|
160
155
|
end
|
|
161
156
|
|
|
162
157
|
def display_results(out, results, before, after)
|
|
@@ -10,6 +10,30 @@ module Legion
|
|
|
10
10
|
include Legion::Extensions::Helpers::Logger
|
|
11
11
|
include Legion::JSON::Helper
|
|
12
12
|
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def expose_as_mcp_tool(value = :_unset)
|
|
15
|
+
if value == :_unset
|
|
16
|
+
return @expose_as_mcp_tool unless @expose_as_mcp_tool.nil?
|
|
17
|
+
|
|
18
|
+
if defined?(Legion::Settings) && Legion::Settings.respond_to?(:dig)
|
|
19
|
+
Legion::Settings.dig(:mcp, :auto_expose_runners) || false
|
|
20
|
+
else
|
|
21
|
+
false
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
@expose_as_mcp_tool = value
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def mcp_tool_prefix(value = :_unset)
|
|
29
|
+
if value == :_unset
|
|
30
|
+
@mcp_tool_prefix
|
|
31
|
+
else
|
|
32
|
+
@mcp_tool_prefix = value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
13
37
|
def function_example(function, example)
|
|
14
38
|
function_set(function, :example, example)
|
|
15
39
|
end
|
|
@@ -22,6 +46,34 @@ module Legion
|
|
|
22
46
|
function_set(function, :desc, desc)
|
|
23
47
|
end
|
|
24
48
|
|
|
49
|
+
def function_outputs(function, outputs)
|
|
50
|
+
function_set(function, :outputs, outputs)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def function_category(function, category)
|
|
54
|
+
function_set(function, :category, category)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def function_tags(function, tags)
|
|
58
|
+
function_set(function, :tags, tags)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def function_risk_tier(function, tier)
|
|
62
|
+
function_set(function, :risk_tier, tier)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def function_idempotent(function, value)
|
|
66
|
+
function_set(function, :idempotent, value)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def function_requires(function, deps)
|
|
70
|
+
function_set(function, :requires, deps)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def function_expose(function, value)
|
|
74
|
+
function_set(function, :expose, value)
|
|
75
|
+
end
|
|
76
|
+
|
|
25
77
|
def function_set(function, key, value)
|
|
26
78
|
unless respond_to? function
|
|
27
79
|
log.debug "function_#{key} called but function doesn't exist, f: #{function}"
|
|
@@ -41,6 +93,7 @@ module Legion
|
|
|
41
93
|
def self.included(base)
|
|
42
94
|
base.send :extend, Legion::Extensions::Helpers::Core if base.instance_of?(Class)
|
|
43
95
|
base.send :extend, Legion::Extensions::Helpers::Logger if base.instance_of?(Class)
|
|
96
|
+
base.extend ClassMethods if base.instance_of?(Class)
|
|
44
97
|
base.extend base if base.instance_of?(Module)
|
|
45
98
|
end
|
|
46
99
|
|
data/lib/legion/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legionio
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -431,17 +431,9 @@ files:
|
|
|
431
431
|
- deploy/helm/legion/values.yaml
|
|
432
432
|
- docker_deploy.rb
|
|
433
433
|
- docs/README.md
|
|
434
|
-
- docs/plans/2026-03-18-config-import-vault-multicluster-design.md
|
|
435
|
-
- docs/plans/2026-03-18-config-import-vault-multicluster-implementation.md
|
|
436
|
-
- docs/plans/2026-03-18-core-lex-uplift-design.md
|
|
437
|
-
- docs/plans/2026-03-18-core-lex-uplift-implementation.md
|
|
438
|
-
- docs/plans/2026-03-18-legion-tty-default-cli-design.md
|
|
439
|
-
- docs/plans/2026-03-18-legion-tty-default-cli-implementation.md
|
|
440
|
-
- docs/plans/2026-03-19-hooks-expansion-design.md
|
|
441
434
|
- exe/legion
|
|
442
435
|
- exe/legionio
|
|
443
436
|
- legionio.gemspec
|
|
444
|
-
- legionio_local.db
|
|
445
437
|
- lib/legion.rb
|
|
446
438
|
- lib/legion/alerts.rb
|
|
447
439
|
- lib/legion/api.rb
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
# Config Import + Multi-Cluster Vault Design
|
|
2
|
-
|
|
3
|
-
## Problem
|
|
4
|
-
|
|
5
|
-
LegionIO currently supports a single Vault cluster (`crypt.vault.address/port/token`). In enterprise environments, engineers work with multiple Vault clusters (dev, test, stage, production) and need different tokens for each. There's also no way to bootstrap a new developer's environment from a shared config — they must manually create JSON files in `~/.legionio/settings/`.
|
|
6
|
-
|
|
7
|
-
## Solution
|
|
8
|
-
|
|
9
|
-
Three changes across three repos:
|
|
10
|
-
|
|
11
|
-
### 1. legion-crypt: Multi-Cluster Vault Support
|
|
12
|
-
|
|
13
|
-
Upgrade `crypt.vault` from a single cluster to a named clusters hash with a `default` pointer.
|
|
14
|
-
|
|
15
|
-
#### Settings Schema
|
|
16
|
-
|
|
17
|
-
```json
|
|
18
|
-
{
|
|
19
|
-
"crypt": {
|
|
20
|
-
"vault": {
|
|
21
|
-
"default": "prod",
|
|
22
|
-
"clusters": {
|
|
23
|
-
"dev": {
|
|
24
|
-
"address": "vault-dev.example.com",
|
|
25
|
-
"port": 8200,
|
|
26
|
-
"protocol": "https",
|
|
27
|
-
"namespace": "myapp",
|
|
28
|
-
"token": null,
|
|
29
|
-
"auth_method": "ldap"
|
|
30
|
-
},
|
|
31
|
-
"stage": {
|
|
32
|
-
"address": "vault-stage.example.com",
|
|
33
|
-
"port": 8200,
|
|
34
|
-
"protocol": "https",
|
|
35
|
-
"namespace": "myapp",
|
|
36
|
-
"token": null,
|
|
37
|
-
"auth_method": "ldap"
|
|
38
|
-
},
|
|
39
|
-
"prod": {
|
|
40
|
-
"address": "vault.example.com",
|
|
41
|
-
"port": 8200,
|
|
42
|
-
"protocol": "https",
|
|
43
|
-
"namespace": "myapp",
|
|
44
|
-
"token": null,
|
|
45
|
-
"auth_method": "ldap"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
#### Backward Compatibility
|
|
54
|
-
|
|
55
|
-
If `crypt.vault.clusters` is absent but `crypt.vault.address` is present, treat it as a single unnamed cluster (current behavior). The migration path is:
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
# Old style (still works)
|
|
59
|
-
Legion::Settings[:crypt][:vault][:address] # => "vault.example.com"
|
|
60
|
-
|
|
61
|
-
# New style
|
|
62
|
-
Legion::Crypt.cluster(:prod) # => cluster config hash
|
|
63
|
-
Legion::Crypt.cluster # => default cluster config hash
|
|
64
|
-
Legion::Crypt.default_cluster # => "prod"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
#### New Module: `Legion::Crypt::VaultCluster`
|
|
68
|
-
|
|
69
|
-
Manages per-cluster Vault connections:
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
module Legion::Crypt
|
|
73
|
-
module VaultCluster
|
|
74
|
-
# Get a configured ::Vault client for a named cluster
|
|
75
|
-
def vault_client(name = nil)
|
|
76
|
-
name ||= default_cluster_name
|
|
77
|
-
@vault_clients ||= {}
|
|
78
|
-
@vault_clients[name] ||= build_client(clusters[name])
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Cluster config hash
|
|
82
|
-
def cluster(name = nil)
|
|
83
|
-
name ||= default_cluster_name
|
|
84
|
-
clusters[name]
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def default_cluster_name
|
|
88
|
-
vault_settings[:default] || clusters.keys.first
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def clusters
|
|
92
|
-
vault_settings[:clusters] || {}
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Connect to all clusters that have tokens
|
|
96
|
-
def connect_all
|
|
97
|
-
clusters.each do |name, config|
|
|
98
|
-
next unless config[:token]
|
|
99
|
-
connect_cluster(name)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
private
|
|
104
|
-
|
|
105
|
-
def build_client(config)
|
|
106
|
-
client = ::Vault::Client.new(
|
|
107
|
-
address: "#{config[:protocol]}://#{config[:address]}:#{config[:port]}",
|
|
108
|
-
token: config[:token]
|
|
109
|
-
)
|
|
110
|
-
client.namespace = config[:namespace] if config[:namespace]
|
|
111
|
-
client
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
#### New Module: `Legion::Crypt::LdapAuth`
|
|
118
|
-
|
|
119
|
-
LDAP authentication against Vault's LDAP auth method (HTTP API, no vault CLI):
|
|
120
|
-
|
|
121
|
-
```ruby
|
|
122
|
-
module Legion::Crypt
|
|
123
|
-
module LdapAuth
|
|
124
|
-
# Authenticate to a single cluster via LDAP
|
|
125
|
-
# POST /v1/auth/ldap/login/:username
|
|
126
|
-
# Returns: { token:, lease_duration:, renewable:, policies: }
|
|
127
|
-
def ldap_login(cluster_name:, username:, password:)
|
|
128
|
-
client = vault_client(cluster_name)
|
|
129
|
-
# Or raw HTTP if ::Vault gem doesn't expose ldap auth:
|
|
130
|
-
response = client.post("/v1/auth/ldap/login/#{username}", password: password)
|
|
131
|
-
token = response.auth.client_token
|
|
132
|
-
# Store token in cluster config (in-memory only, not written to disk with password)
|
|
133
|
-
clusters[cluster_name][:token] = token
|
|
134
|
-
clusters[cluster_name][:connected] = true
|
|
135
|
-
{ token: token, lease_duration: response.auth.lease_duration,
|
|
136
|
-
renewable: response.auth.renewable, policies: response.auth.policies }
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Authenticate to ALL configured clusters with same credentials
|
|
140
|
-
def ldap_login_all(username:, password:)
|
|
141
|
-
results = {}
|
|
142
|
-
clusters.each do |name, config|
|
|
143
|
-
next unless config[:auth_method] == 'ldap'
|
|
144
|
-
results[name] = ldap_login(cluster_name: name, username: username, password: password)
|
|
145
|
-
rescue StandardError => e
|
|
146
|
-
results[name] = { error: e.message }
|
|
147
|
-
end
|
|
148
|
-
results
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
#### Existing Code Changes
|
|
155
|
-
|
|
156
|
-
- `Legion::Crypt.start` — if `clusters` present, call `connect_all` instead of `connect_vault`
|
|
157
|
-
- `Legion::Crypt::Vault.read/write/get` — route through `vault_client(name)` for cluster-aware reads
|
|
158
|
-
- `Legion::Crypt::Vault.connect_vault` — still works for legacy single-cluster config
|
|
159
|
-
- `Legion::Crypt::VaultRenewer` — renew tokens for ALL connected clusters
|
|
160
|
-
- `Legion::Settings::Resolver` — `vault://` refs gain optional cluster prefix: `vault://prod/secret/data/myapp#password` (falls back to default cluster if no prefix)
|
|
161
|
-
|
|
162
|
-
### 2. LegionIO: `legion config import` / `legionio config import` CLI Command
|
|
163
|
-
|
|
164
|
-
New subcommand under `Config`:
|
|
165
|
-
|
|
166
|
-
```
|
|
167
|
-
legionio config import <source> # URL or local file path
|
|
168
|
-
legion config import <source> # same command available in interactive binary
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
#### Behavior
|
|
172
|
-
|
|
173
|
-
1. **Fetch source:**
|
|
174
|
-
- If `source` starts with `http://` or `https://` — HTTP GET, follow redirects
|
|
175
|
-
- Otherwise — read local file
|
|
176
|
-
2. **Decode payload:**
|
|
177
|
-
- Try `JSON.parse(body)` first
|
|
178
|
-
- If that fails, try `JSON.parse(Base64.decode64(body))`
|
|
179
|
-
- If both fail, error with "not valid JSON or base64-encoded JSON"
|
|
180
|
-
3. **Validate structure:**
|
|
181
|
-
- Must be a Hash
|
|
182
|
-
- Warn on unrecognized top-level keys (not in known settings keys)
|
|
183
|
-
4. **Write to `~/.legionio/settings/imported.json`:**
|
|
184
|
-
- Deep merge with existing imported.json if present
|
|
185
|
-
- Or overwrite with `--force`
|
|
186
|
-
5. **Display summary:**
|
|
187
|
-
- Which settings sections were imported (crypt, transport, cache, etc.)
|
|
188
|
-
- How many vault clusters configured
|
|
189
|
-
- Remind user to run `legion` for onboarding vault auth
|
|
190
|
-
|
|
191
|
-
#### Example Config File
|
|
192
|
-
|
|
193
|
-
```json
|
|
194
|
-
{
|
|
195
|
-
"crypt": {
|
|
196
|
-
"vault": {
|
|
197
|
-
"default": "prod",
|
|
198
|
-
"clusters": {
|
|
199
|
-
"dev": { "address": "vault-dev.uhg.com", "port": 8200, "protocol": "https", "auth_method": "ldap" },
|
|
200
|
-
"test": { "address": "vault-test.uhg.com", "port": 8200, "protocol": "https", "auth_method": "ldap" },
|
|
201
|
-
"stage": { "address": "vault-stage.uhg.com", "port": 8200, "protocol": "https", "auth_method": "ldap" },
|
|
202
|
-
"prod": { "address": "vault.uhg.com", "port": 8200, "protocol": "https", "auth_method": "ldap" }
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
"transport": {
|
|
207
|
-
"host": "rabbitmq.uhg.com",
|
|
208
|
-
"port": 5672,
|
|
209
|
-
"vhost": "legion"
|
|
210
|
-
},
|
|
211
|
-
"cache": {
|
|
212
|
-
"driver": "dalli",
|
|
213
|
-
"servers": ["memcached.uhg.com:11211"]
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
### 3. legion-tty: Onboarding Vault Auth Step
|
|
219
|
-
|
|
220
|
-
After the wizard (name + LLM providers), before the reveal box:
|
|
221
|
-
|
|
222
|
-
```
|
|
223
|
-
[digital rain]
|
|
224
|
-
[intro - kerberos identity, github quick]
|
|
225
|
-
[wizard - name, LLM providers]
|
|
226
|
-
[NEW: vault auth prompt]
|
|
227
|
-
[reveal box - now includes vault cluster status]
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
#### Flow
|
|
231
|
-
|
|
232
|
-
1. Check if any vault clusters are configured in settings
|
|
233
|
-
2. If none, skip entirely
|
|
234
|
-
3. If clusters exist, ask: "I found N Vault clusters. Connect now?" (TTY::Prompt confirm)
|
|
235
|
-
4. If yes:
|
|
236
|
-
- Default username = kerberos `samaccountname` (from `@kerberos_identity[:samaccountname]`), fallback to `ENV['USER']`
|
|
237
|
-
- Ask: "Username:" with default pre-filled (TTY::Prompt ask)
|
|
238
|
-
- Ask: "Password:" with `echo: false` (hidden input)
|
|
239
|
-
- For each LDAP-configured cluster, attempt `Legion::Crypt.ldap_login`
|
|
240
|
-
- Show green checkmark / red X per cluster with name
|
|
241
|
-
5. Store tokens in memory (settings hash), NOT on disk with the password
|
|
242
|
-
6. Reveal box now shows vault cluster connection status
|
|
243
|
-
|
|
244
|
-
#### New Background Probe: Not Needed
|
|
245
|
-
|
|
246
|
-
Vault auth requires user interaction (password prompt), so it runs inline after the wizard, not in a background thread.
|
|
247
|
-
|
|
248
|
-
## Alternatives Considered
|
|
249
|
-
|
|
250
|
-
**Use lex-vault instead of vault gem for multi-cluster:** lex-vault's Faraday-based client is simpler and already supports per-instance address/token/namespace. Could replace the `vault` gem dependency in legion-crypt entirely. Deferred — not a requirement for this iteration but a good future optimization.
|
|
251
|
-
|
|
252
|
-
**Kerberos auth for Vault:** Not a default Vault auth method. Would require a custom Vault plugin. Deferred.
|
|
253
|
-
|
|
254
|
-
**Store tokens on disk:** Vault tokens are renewable and short-lived. Storing them risks stale tokens. Better to re-auth on each `legion` startup if needed. Could add optional token caching later.
|
|
255
|
-
|
|
256
|
-
## Constraints
|
|
257
|
-
|
|
258
|
-
- LDAP password is NEVER written to disk or settings files
|
|
259
|
-
- Vault tokens are stored in-memory only during the session
|
|
260
|
-
- `vault://` resolver must remain backward compatible (no cluster prefix = default cluster)
|
|
261
|
-
- Single-cluster config (`crypt.vault.address`) must continue to work unchanged
|
|
262
|
-
- Config import file is plain JSON, no wrapper format
|
|
263
|
-
- HTTP sources must handle both raw JSON and base64-encoded JSON
|
|
264
|
-
|
|
265
|
-
## Repos Affected
|
|
266
|
-
|
|
267
|
-
| Repo | Changes |
|
|
268
|
-
|------|---------|
|
|
269
|
-
| `legion-crypt` | `VaultCluster` module, `LdapAuth` module, multi-cluster settings, `VaultRenewer` update, backward compat |
|
|
270
|
-
| `LegionIO` | `config import` CLI command (both binaries), HTTP fetch + base64 detection |
|
|
271
|
-
| `legion-tty` | Onboarding vault auth step after wizard |
|
|
272
|
-
| `legion-settings` | `Resolver` update for cluster-prefixed `vault://` refs (optional, can defer) |
|