Module: Legion::LLM::Fleet::TokenIssuer

Defined in:
lib/legion/llm/fleet/token_issuer.rb

Constant Summary collapse

ISSUER =
'legion-llm'
AUDIENCE =
'lex-llm-fleet-worker'
ALGORITHM =
'HS256'

Class Method Summary collapse

Class Method Details

.algorithmObject



80
81
82
# File 'lib/legion/llm/fleet/token_issuer.rb', line 80

def algorithm
  auth_setting(:algorithm, default: ALGORITHM)
end

.audienceObject



76
77
78
# File 'lib/legion/llm/fleet/token_issuer.rb', line 76

def audience
  auth_setting(:audience, default: AUDIENCE)
end

.auth_setting(key, default:) ⇒ Object



84
85
86
87
88
# File 'lib/legion/llm/fleet/token_issuer.rb', line 84

def auth_setting(key, default:)
  Legion::LLM::Settings.value(:fleet, :auth, key, default: default)
rescue StandardError
  default
end

.issue(payload) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/legion/llm/fleet/token_issuer.rb', line 20

def issue(payload)
  ttl = token_ttl_seconds
  now = Time.now.to_i
  claims = required_claims(payload).merge(
    iss: issuer,
    aud: audience,
    iat: now,
    exp: now + ttl,
    nbf: now,
    jti: "fleet-jti-#{SecureRandom.uuid}"
  )

  jwt_module.issue(
    claims,
    signing_key: signing_key,
    ttl:         ttl,
    issuer:      issuer,
    algorithm:   algorithm
  )
rescue TokenError
  raise
rescue StandardError => e
  raise TokenError, "failed to issue fleet token: #{e.message}"
end

.issuerObject



72
73
74
# File 'lib/legion/llm/fleet/token_issuer.rb', line 72

def issuer
  auth_setting(:issuer, default: ISSUER)
end

.jwt_moduleObject

Raises:



101
102
103
104
105
106
# File 'lib/legion/llm/fleet/token_issuer.rb', line 101

def jwt_module
  require_crypt!
  return Legion::Crypt::JWT if defined?(Legion::Crypt::JWT) && Legion::Crypt::JWT.respond_to?(:issue)

  raise TokenError, 'Legion::Crypt::JWT.issue unavailable'
end

.require_crypt!Object

Raises:



108
109
110
111
112
# File 'lib/legion/llm/fleet/token_issuer.rb', line 108

def require_crypt!
  return if defined?(Legion::Crypt)

  raise TokenError, 'Legion::Crypt is required for fleet token issuance'
end

.required_claims(payload) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/legion/llm/fleet/token_issuer.rb', line 45

def required_claims(payload)
  payload = symbolize_keys(payload || {})
  claims = {}
  %i[
    request_id correlation_id idempotency_key operation provider provider_instance
    model reply_to message_context
  ].each do |key|
    value = payload[key]
    raise TokenError, "missing token claim source: #{key}" if value.nil?

    claims[key] = value
  end
  %i[params caller trace_context timeout_seconds expires_at].each do |key|
    value = payload[key]
    raise TokenError, "missing token claim source: #{key}" if value.nil?

    claims[key] = value
  end
  claims
end

.signing_keyObject



90
91
92
93
94
95
96
97
98
99
# File 'lib/legion/llm/fleet/token_issuer.rb', line 90

def signing_key
  require_crypt!
  return Legion::Crypt.cluster_secret if Legion::Crypt.respond_to?(:cluster_secret)

  raise TokenError, 'no signing key available - Legion::Crypt not initialized'
rescue TokenError
  raise
rescue StandardError => e
  raise TokenError, "no signing key available: #{e.message}"
end

.symbolize_keys(hash) ⇒ Object



114
115
116
117
118
# File 'lib/legion/llm/fleet/token_issuer.rb', line 114

def symbolize_keys(hash)
  hash.each_with_object({}) do |(key, value), result|
    result[key.respond_to?(:to_sym) ? key.to_sym : key] = value
  end
end

.token_ttl_secondsObject



66
67
68
69
70
# File 'lib/legion/llm/fleet/token_issuer.rb', line 66

def token_ttl_seconds
  Integer(Legion::LLM::Settings.value(:fleet, :dispatch, :token_ttl_seconds, default: 180))
rescue ArgumentError, TypeError
  180
end