Skip to content

Feature/credential scoping service#9

Closed
Esity wants to merge 854 commits intoOptum:mainfrom
LegionIO:feature/credential-scoping-service
Closed

Feature/credential scoping service#9
Esity wants to merge 854 commits intoOptum:mainfrom
LegionIO:feature/credential-scoping-service

Conversation

@Esity
Copy link
Copy Markdown
Member

@Esity Esity commented Apr 7, 2026

No description provided.

Esity added 30 commits March 26, 2026 15:43
add specs for codegen CLI, API, and boot loading (tasks 15-17)
exercises: gap detection -> code generation -> validation -> registry
persistence -> boot loading. uses stubbed LLM and InProcess transport.
9 examples covering full approval, rejection, retry exhaustion, tier
classification, and ReviewSubscriber routing.
these are only needed for integration tests, not runtime.
removes path + File.exist? guards so the Gemfile works outside
the monorepo folder structure.
- guard Dynamic/LLM stub definitions with unless defined? in integration spec
- wrap lex-codegen and lex-eval requires with LoadError rescue guards
- fix service_setup_apollo_spec double-call failure by stubbing Apollo.start
add end-to-end integration test for self-generating functions
* split bootstrap config into per-subsystem files

write_config now extracts recognized subsystem keys (microsoft_teams,
rbac, api, logging, gaia, extensions, llm, data, cache_local, cache,
transport, crypt, role) into individual {key}.json files. remaining
keys go to bootstrapped_settings.json. subsystem files are always
overwritten; remainder respects the force flag for merge behavior.

* bump to 1.6.10, update changelog (#42)

* apply copilot review suggestions (#42)
add local dispatch for non-AMQP cognitive extensions
…ent sync, SSE events (#44)

- add DigitalWorker.heartbeat, .detect_orphans, .pause_orphans! methods
- wire consent tier sync into Lifecycle.transition! using CONSENT_MAPPING
- add sync_consent_tier private method for optional lex-consent integration
- replace per-worker events stub with dual-mode SSE streaming + polling
- bump to 1.6.13
…45)

- rewrite Compliance module with DEFAULTS hash and merge_settings
- add setup_compliance to Service boot (after settings load)
- all protections enabled by default: PHI, PCI, PII, FedRAMP
- classification_level defaults to 'confidential' (highest)
- update existing specs to use merge_settings pattern
- bump to 1.6.14
#46)

* add absorber matcher system with url pattern matching

* add absorber base class with pattern DSL and knowledge helpers

* add pattern matcher for absorber dispatch resolution

* add absorber builder for auto-discovery during extension boot

* register absorbers as capabilities during extension boot

* add absorber dispatch module for pattern resolution and execution

* add legion generate absorber command for scaffolding new absorbers

* add legion absorb CLI command with url, list, and resolve subcommands

* fix absorb_command_spec test pollution from permanent module monkey-patch

The spec was reopening PatternMatcher and AbsorberDispatch at file load
time, permanently replacing .resolve, .list, and .dispatch with stubs
that return nil. This broke pattern_matcher_spec and absorber_dispatch_spec
when the full suite ran in random order.

Replace the module-level overwrite with proper RSpec allow/receive stubs
in a before block so the originals are restored after each example.

* bump version to 1.6.13

* apply copilot review suggestions (#46)

* fix config validate transport host check and doctor settings loading

* fix secret resolution in cli commands and check credential validation

* apply copilot re-review suggestions (#46)

- absorber_dispatch: switch publish_event to Messages::Dynamic with session open? guard
- extensions: add per-absorber error rescue in register_absorber_capabilities
- builders/absorbers: key absorbers hash by snake_case filename, not CamelCase
- absorbers/base: guard Apollo availability in absorb_to_knowledge; extract helpers to reduce complexity
- CHANGELOG: correct generator invocation to legionio dev generate absorber

* fix image analyze llm call and trace search llm boot

* fix Open3 thread leak in RunCommand and SimpleCov exit code (#46)

- replace Timeout.timeout + Open3.capture3 with Open3.popen3 and
  manual process kill on timeout to prevent dangling reader threads
- set SimpleCov.external_at_exit to prevent exit code 1 from
  SystemExit in Ruby 3.4 + RSpec

* apply copilot re-review suggestions (#46)

- absorb_raw: return structured { success: false, error: :apollo_not_available } instead of nil
- apollo_available?: also gate on Legion::Apollo.started? when available
- check_transport: redact vault/lease URI in error message (emit scheme only)
- check_data: extract raise_if_unresolved_data_creds helper, report unresolved fields + scheme hints without leaking paths
- trace_command: setup_connection returns false on CLI::Error (prints error); search/summarize gate on return value and run Connection.shutdown in ensure

* remove permanently-pending cross-gem integration specs (#46)

MCP, codegen, eval specs belong in their own gem suites.
Keeps the function metadata DSL spec that runs locally.

* fix SimpleCov exit code 1 on CI by overriding previous_error? (#46)

* temporarily disable detect_lex spec that leaks SystemExit (#46)

* fix CI exit code 1: update image_command_spec for chat.ask() interface, fix trace_command Connection resolution (#46)

image_command_spec stubs returned plain hash for Legion::LLM.chat but
code changed to call chat.ask() on the result. NoMethodError was caught
by rescue StandardError which raised SystemExit(1), killing the RSpec
process mid-suite (1944 of 3767 specs ran, 0 recorded failures, exit 1).

trace_command.rb used bare Connection constant which couldn't resolve
inside Thor subclass — now uses fully qualified Legion::CLI::Connection
and requires the connection module. Spec stubs Connection methods.

* add explicit requires for absorber constants in absorb_command (#46)

* remove duplicate test job from ci-cd.yml (#46)

the test job (rspec + rubocop with rabbitmq/postgres) duplicates what
the shared ci.yml workflow already runs. keep only helm-lint which is
unique to this workflow.

* trigger CI with updated shared workflow

* trigger ci with rabbitmq cookie fix

* trigger ci with init container cookie fix
#46)

* add absorber matcher system with url pattern matching

* add absorber base class with pattern DSL and knowledge helpers

* add pattern matcher for absorber dispatch resolution

* add absorber builder for auto-discovery during extension boot

* register absorbers as capabilities during extension boot

* add absorber dispatch module for pattern resolution and execution

* add legion generate absorber command for scaffolding new absorbers

* add legion absorb CLI command with url, list, and resolve subcommands

* fix absorb_command_spec test pollution from permanent module monkey-patch

The spec was reopening PatternMatcher and AbsorberDispatch at file load
time, permanently replacing .resolve, .list, and .dispatch with stubs
that return nil. This broke pattern_matcher_spec and absorber_dispatch_spec
when the full suite ran in random order.

Replace the module-level overwrite with proper RSpec allow/receive stubs
in a before block so the originals are restored after each example.

* bump version to 1.6.13

* apply copilot review suggestions (#46)

* fix config validate transport host check and doctor settings loading

* fix secret resolution in cli commands and check credential validation

* apply copilot re-review suggestions (#46)

- absorber_dispatch: switch publish_event to Messages::Dynamic with session open? guard
- extensions: add per-absorber error rescue in register_absorber_capabilities
- builders/absorbers: key absorbers hash by snake_case filename, not CamelCase
- absorbers/base: guard Apollo availability in absorb_to_knowledge; extract helpers to reduce complexity
- CHANGELOG: correct generator invocation to legionio dev generate absorber

* fix image analyze llm call and trace search llm boot

* fix Open3 thread leak in RunCommand and SimpleCov exit code (#46)

- replace Timeout.timeout + Open3.capture3 with Open3.popen3 and
  manual process kill on timeout to prevent dangling reader threads
- set SimpleCov.external_at_exit to prevent exit code 1 from
  SystemExit in Ruby 3.4 + RSpec

* apply copilot re-review suggestions (#46)

- absorb_raw: return structured { success: false, error: :apollo_not_available } instead of nil
- apollo_available?: also gate on Legion::Apollo.started? when available
- check_transport: redact vault/lease URI in error message (emit scheme only)
- check_data: extract raise_if_unresolved_data_creds helper, report unresolved fields + scheme hints without leaking paths
- trace_command: setup_connection returns false on CLI::Error (prints error); search/summarize gate on return value and run Connection.shutdown in ensure

* remove permanently-pending cross-gem integration specs (#46)

MCP, codegen, eval specs belong in their own gem suites.
Keeps the function metadata DSL spec that runs locally.

* fix SimpleCov exit code 1 on CI by overriding previous_error? (#46)

* temporarily disable detect_lex spec that leaks SystemExit (#46)

* fix CI exit code 1: update image_command_spec for chat.ask() interface, fix trace_command Connection resolution (#46)

image_command_spec stubs returned plain hash for Legion::LLM.chat but
code changed to call chat.ask() on the result. NoMethodError was caught
by rescue StandardError which raised SystemExit(1), killing the RSpec
process mid-suite (1944 of 3767 specs ran, 0 recorded failures, exit 1).

trace_command.rb used bare Connection constant which couldn't resolve
inside Thor subclass — now uses fully qualified Legion::CLI::Connection
and requires the connection module. Spec stubs Connection methods.

* add explicit requires for absorber constants in absorb_command (#46)

* remove duplicate test job from ci-cd.yml (#46)

the test job (rspec + rubocop with rabbitmq/postgres) duplicates what
the shared ci.yml workflow already runs. keep only helm-lint which is
unique to this workflow.

* trigger CI with updated shared workflow

* trigger ci with rabbitmq cookie fix

* trigger ci with init container cookie fix
Esity and others added 28 commits April 6, 2026 16:48
…eanup

- Add ALWAYS_LOADED patterns for apollo/knowledge and eval/evaluation
- Change tool name format from dots to dashes (legion-ext-runner-func)
- Add always_loaded_names to Registry
- Strip debug logging, add production info logging
- Clean rubocop offenses
Core identity module + schema for the unified identity system:
- Legion::Mode with LEGACY_MAP, ENV/Settings fallback, 4 predicates
- Legion.instance_id (UUID, available at boot)
- Identity::Process singleton (bind!, queue_prefix per-mode, AtomicReference)
- Identity::Request per-request (from_env, from_auth_context, to_caller_hash)
- Identity::Lease value object (expired?, stale? at 50% TTL, ttl_seconds)
- Identity::LeaseRenewer background thread (cooperative shutdown, no Thread#kill)
- Identity::Broker provider management (groups cache with 60s TTL, single-flight CAS)
- Identity::Middleware Rack bridge (auth -> legion.principal)
- Boot: setup_identity step 9 with parallel provider resolution, fallback
- Extension publish suppression (deferred LexRegister until identity resolves)
- Identity provider registration during phased extension load
- Readiness: Concurrent::Hash, :identity component
- Settings: READONLY_SECTIONS += :identity, :rbac, :api
- Default API bind changed to 127.0.0.1
- Doctor checks: ApiBindCheck, ModeCheck
- GET /api/identity/audit route
- ProcessRole delegates to Mode, added :agent/:infra roles
- Reload path: Identity::Process.refresh_credentials
- Shutdown: cooperative Broker stop + JWKS refresh stop
- 200+ spec examples across 8 spec files

Design: docs/plans/2026-04-04-unified-identity-design.md
Plan: docs/plans/2026-04-04-unified-identity-implementation.md (phase 2)
DoCommand.resolve_function now splits on both '-' and '.' to
extract the function name from tool names, supporting the new
dash-separated format required by LLM providers (Bedrock).
- move setup_identity after load_extensions so lex-identity-*
  providers are discovered (fixes P1 boot ordering)
- pending_registrations flush now runs after extensions load
- move parse_since_duration to Sinatra helper module (fixes P2)
- fix do_command to split on dash and dot for tool names (fixes P2)
fix: tool discovery improvements — always-loaded patterns, dash naming
- fix broker_spec: use plain doubles instead of instance_doubles to
  prevent cross-example leak detection; add after(:each) reset
- fix broker single-flight spec: prime stale cache before concurrent
  test so CAS guard has a value to return
- fix process queue_prefix: collapse duplicate :agent branch
- fix identity_audit: move parse_since_duration to Sinatra helper
- fix do_command: split on dash and dot for tool name extraction
- fix service: move setup_identity after load_extensions so identity
  providers are discoverable
- fix service shutdown: add PerceivedComplexity to rubocop disable
- fix lease/request: add ParameterLists rubocop disable
- disable Style/RedundantConstantBase globally (::Process required)
- add Settings stub fallback in tls_spec
- bump version to 1.7.20
Co-authored-by: Esity <1851830+Esity@users.noreply.github.com>
- fix api default bind from 0.0.0.0 to 127.0.0.1 (matching changelog)
- add dual symbol/string key lookup in Mode.settings_dig
- rescue per-future in resolve_identity_providers, move pool teardown to ensure
- set @pending_registrations = nil after flush so subsequent publishes are immediate
- normalize credentials_for provider_name to symbol for consistency
- flush pending registrations during reload after hook_extensions reinitializes array
split Readiness::COMPONENTS into REQUIRED and OPTIONAL sets. optional
components (rbac, llm, apollo, gaia, identity) now mark_skipped instead
of blocking /api/ready. fixes 503 on startup when legion-rbac or other
optional gems are not installed.
Add source: key to EMPTY_STATE, bind!, bind_fallback!, and identity_hash.
Derives source from provider.provider_name in bind!, defaults to :system
in bind_fallback!. Prerequisite for AMQP identity headers and JWT claims.
- qualify stream_queue call with Routes::Events. prefix in events.rb
- inside registered(app) block, self is the API instance not the module
- workers.rb already had the correct qualified call
- add Identity::Request::SOURCE_NORMALIZATION to normalize middleware
  source values (:local->:system, :api_key->:api) at construction time
- update response_meta in API::Helpers to include caller block
  (canonical_name, kind, source) when authenticated and principal present
- wire to_caller_hash into POST /api/llm/inference, replacing hardcoded
  requested_by: { type: :user, credential: :api } fallback
- add specs for SOURCE_NORMALIZATION, normalization behavior, and
  response_meta caller injection
implement wire format phase 3 group 2 (api meta + source normalization)
Wire §8 of the credential-scoping design into service.rb:
- boot: call Crypt.fetch_bootstrap_rmq_creds after Crypt.start
- setup_identity: call Crypt.swap_to_identity_creds(mode:) after identity resolves, gated on vault_connected? && dynamic_rmq_creds? && !lite?
- shutdown: call Crypt.revoke_bootstrap_lease before Crypt.shutdown
- reload: add fetch_bootstrap_rmq_creds + resolve_secrets! after Crypt.start, replace static mark_ready(:identity) with setup_identity

All changes are no-ops when dynamic_rmq_creds: false (default).
Specs cover all guards, modes, failure paths, and reload flow.
Connection.ensure_settings now accepts resolve_secrets: keyword to
skip Vault/lease resolution for CLI commands that only need local
settings. legionio update passes resolve_secrets: false to eliminate
noisy unresolved credential warnings on gem updates. (1.7.27)
@Esity Esity closed this Apr 7, 2026
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ Esity
❌ Codex
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants