Migrate NEAR AI MCP credentials to product auth - #4246
Merged
Merged
Conversation
…uired, error variants, thiserror, tests (#4233) - authorization: wrap ProductAuthAccount obligation push in `if credential.required` so optional product-auth credentials do not hard-fail dispatch when unconfigured, mirroring the existing SecretHandle semantics - product_auth_runtime_credentials: return `Failed` (not `AuthRequired`) when a Configured account has `access_secret = None` — data corruption is not a re-auth prompt; log discarded AuthProviderId parse error via tracing::debug before mapping - obligations: add `#[derive(thiserror::Error)]` + `#[error(...)]` to RuntimeCredentialAccountError (CLAUDE.md: use thiserror); log discarded lease_once / consume / insert errors with tracing::debug instead of silent |_| - tests (product_auth_runtime_credentials): add resolver_maps_unconfigured_account_status_to_auth_required, resolver_maps_configured_account_without_access_secret_to_failed, resolver_maps_multiple_accounts_to_auth_required - tests (builtin_obligation_handler_contract): add inject_credential_account_once_fails_when_no_resolver_wired, inject_credential_account_once_fails_when_resolver_returns_auth_required - tests (manifest_v2_contract): add rejects_unknown_runtime_credential_source_type - docs: clarify required/optional semantics apply uniformly across credential sources
…ant use tracing - extract stage_credential_material(store, injections, scope, cap_id, source, target) as a module-level helper that encapsulates the lease->consume->insert sequence with tracing on each error; inject_credential_accounts uses it after resolving the access_secret, removing ~15 lines of duplicated store manipulation - remove redundant 'use tracing;' import from product_auth_runtime_credentials.rs; tracing::debug! resolves via full path without the explicit module import
… builds build_backend_production (used by libsql + postgres production profiles) had product_auth_ports available but never called with_runtime_credential_account_resolver, meaning ProductAuthAccount runtime credentials would silently fail to inject in production deployments (resolver=None path in inject_credential_accounts). Wire the resolver the same way as build_local_dev: when product_auth_ports is Some, create a ProductAuthRuntimeCredentialResolver from ports.credential_account_service() and set it on the services builder before host_runtime_for_production is called.
…ormalize helper CI failures: factory.rs called ports.credential_account_service() but RebornProductAuthServicePorts had no such method (only RebornProductAuthServices did). Add the missing public accessor. Also address henrypark133 review findings: - tests (High): add inject_credential_account_once_resolves_and_stages_secret (happy path: resolver Ok, material in store, staged injection present) and inject_credential_account_once_fails_when_resolved_secret_not_in_store (resolver Ok but resolved handle absent from store -> Failed) - naming (Low): rename secret_injection_obligations -> secret_injection_handles to match staged_secret_injection_handles sibling (suffix _obligations implied Obligation return type, suffix _handles is accurate) - refactor (Low): extract normalize_multi_inject + extract_inject_handle helpers in decision.rs; collapse duplicate InjectSecretOnce / InjectCredentialAccountOnce normalization loops (identical structure, only variant differed) into one call
…lide needless lifetime - capability.rs: replace explicit impl Default with #[derive(Default)] + #[default] on SecretHandle; fixes clippy::derivable-impls (CI Clippy all-features failure) - decision.rs: elide needless 'a lifetime on extract_inject_handle return-owned fn; fixes clippy::needless-lifetimes
Resolve conflicts in: - crates/ironclaw_reborn_composition/src/lib.rs (mod list — keep nearai_mcp, product_auth_durable, product_auth_runtime_credentials) - crates/ironclaw_reborn_composition/src/factory.rs (build_backend_production — adopt reborn's require_product_auth_runtime_ports + attach_nearai_mcp_runtime pre-step; drop obsolete Option<product_auth_ports> conditional; re-add resolver wiring unconditionally after compose_product_auth_services, before with_first_party_capabilities, sourcing credential_account_service from product_auth_services so the durable filesystem fallback from #4234 is honored) - docs/reborn/contracts/host-runtime.md (keep MCP HTTP/SSE planner paragraph alongside PR4233's 'InjectSecretOnce or InjectCredentialAccountOnce' wording)
Reborn-integration introduced a canonical staged-credential vocabulary that
PR4233's auth flow now adopts:
A. Unify on ironclaw_host_api::CredentialStageError {AuthRequired, Backend}.
- Delete RuntimeCredentialAccountError {AuthRequired, Failed} from
ironclaw_host_runtime::obligations.
- RuntimeCredentialAccountResolver::resolve_access_secret now returns
Result<SecretHandle, CredentialStageError>, sharing its error type with
ProductAuthCredentialStageError (host-runtime staging primitive) and
GsuiteCredentialStageError (per-extension stager), so no per-layer
conversion glue is needed.
- ProductAuthRuntimeCredentialResolver in ironclaw_reborn_composition maps
AuthProductError to CredentialStageError directly; documents the durable
product-auth Configured <-> access_secret=Some invariant (#4234) under
which the corrupt-state branch returns Backend, not AuthRequired.
B. Share the lease/consume/insert classifier between the WASM
InjectCredentialAccountOnce path and the first-party stager path.
- stage_credential_material in obligations.rs now returns
CredentialStageError, classifying SecretStoreError via
services::stage_secret_error — the same classifier
ProductAuthProviderRuntimePorts::stage_secret_once uses. Result: unknown,
expired, revoked, consumed, or unknown-lease access secrets now surface
as AuthRequired on the WASM lane just as they do on the GSuite lane, so
the runtime auth gate fires consistently regardless of which staging
entry point discovered the gap.
- credential_stage_error_to_obligation_error replaces
runtime_credential_account_error; used by both inject_credential_accounts
and stage_credential_material callers, eliminating duplicate mapping.
- inject_credential_account_once_fails_when_resolved_secret_not_in_store
renamed to inject_credential_account_once_maps_unknown_resolved_secret_to_auth_required
to assert the new aligned semantics (was Failed; now AuthRequired).
C. Fix RuntimeCredentialRequirement struct literal in web-access network
policy test (extension_surface.rs) — reborn-integration's #4219 test was
written before PR4233 added the 'source' field; add explicit
RuntimeCredentialRequirementSource::SecretHandle.
Tests: product_auth_runtime_credentials (5/5),
builtin_obligation_handler_contract inject_credential_account_once (4/4),
github_wasm_runtime_contract (5/5),
authorization/runtime_credentials_contract (6/6),
extension_v2_lifecycle_e2e github_v2_package_discovers_... (1/1).
Port the NEAR AI MCP slice of #4176 by switching the bundled nearai-mcp manifest from a static SecretHandle to a ProductAuthAccount runtime credential source. Before: runtime_credentials = [ { handle = "llm_nearai_api_key", audience = ..., target = ... }, ] After: runtime_credentials = [ { handle = "llm_nearai_api_key", source = { type = "product_auth_account", provider = "nearai" }, audience = ..., target = ... }, ] Effect: - The authorization layer (ironclaw_authorization, PR #4233) already maps ProductAuthAccount sources to Obligation::InjectCredentialAccountOnce when credential.required is true, so flipping the source field is sufficient to route NEAR AI MCP credentials through the product-auth account path. - At dispatch time, the obligation handler resolves the configured nearai account via RuntimeCredentialAccountResolver (ProductAuthRuntimeCredentialResolver), leases its access_secret from the SecretStore, and stages it in RuntimeSecretInjectionStore under the 'llm_nearai_api_key' slot. - NearAiMcpEgressPlanner continues referencing the slot via RuntimeCredentialSource::StagedObligation — no planner change required. - Reuses the same staged-credential vocabulary (CredentialStageError) and classifier (services::stage_secret_error) the GSuite stager and the WASM InjectCredentialAccountOnce path use, so AuthRequired vs Backend semantics are uniform across first-party / WASM / MCP lanes. Manifest digest auto-updates via include_str! at compile time. The narrow nearai_mcp planner comment that pointed at #4176 as future work is updated to reflect that this PR closes the MCP slice. Stacked on PR #4233 (codex/github-wasm-product-auth) for the InjectCredentialAccountOnce / RuntimeCredentialAccountResolver / RuntimeCredentialRequirementSource::ProductAuthAccount machinery. Merge order: #4233 must land first. Tests: - cargo test -p ironclaw_reborn_composition local_dev_nearai_mcp_installs_and_activates_model_visible_capability (asserts source = ProductAuthAccount(provider=nearai)) - cargo test -p ironclaw_reborn_composition --lib nearai_mcp (9/9) - cargo test -p ironclaw_reborn_composition --lib available_extensions (7/7) - cargo test -p ironclaw_extensions --test manifest_v2_contract (44/44) - cargo test -p ironclaw_authorization --test runtime_credentials_contract (6/6)
Contributor
There was a problem hiding this comment.
Code Review
This pull request updates the NEAR AI MCP server configuration to source its runtime credentials from a product-auth account provider (nearai) instead of relying on an out-of-band secret store handle. It updates the manifest file, adjusts the corresponding unit tests in factory.rs to assert this new source, and updates documentation comments in nearai_mcp.rs to clarify the upstream credential resolution flow. There are no review comments, so I have no feedback to provide.
Base automatically changed from
codex/github-wasm-product-auth
to
reborn-integration
May 29, 2026 23:34
This was referenced May 30, 2026
theredspoon
pushed a commit
to theredspoon/ironclaw
that referenced
this pull request
Jun 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port the NEAR AI MCP slice of #4176 by switching the bundled
nearai-mcpmanifest from a staticSecretHandleto aProductAuthAccountruntime credential source. Mirrors PR #4233's GitHub WASM migration for the MCP lane.Effect on the auth flow
ProductAuthAccount-sourced credentials throughObligation::InjectCredentialAccountOncewhencredential.requiredis true. Flipping the manifest source field is therefore sufficient end-to-end.nearaiaccount viaRuntimeCredentialAccountResolver(ProductAuthRuntimeCredentialResolverimpl), leases the access secret from theSecretStore, and stages it intoRuntimeSecretInjectionStoreunder thellm_nearai_api_keyslot.NearAiMcpEgressPlannercontinues referencing the slot viaRuntimeCredentialSource::StagedObligation { capability_id }— no planner change required.CredentialStageErrorvocabulary from Wire Reborn auth consumers through staged credentials #4231 / Migrate GitHub WASM credentials to product auth #4233's alignment commit, soAuthRequiredvsBackendsemantics are uniform across first-party / WASM / MCP lanes.The narrow
nearai_mcpplanner comment that pointed at #4176 as future work is updated to reflect that this PR closes the MCP slice.Dependency
Stacked on #4233 (
codex/github-wasm-product-auth). Merge order: #4233 must land first, since this PR depends on:Obligation::InjectCredentialAccountOnce(host-api)RuntimeCredentialAccountResolvertrait +ProductAuthRuntimeCredentialResolverimpl (host-runtime, composition)RuntimeCredentialRequirementSource::ProductAuthAccount(host-api capability schema)source = { type = "product_auth_account", ... }Base branch is intentionally
codex/github-wasm-product-authso the diff stays surgical. Once #4233 lands, retarget toreborn-integrationand merge.Tests
cargo test -p ironclaw_reborn_composition local_dev_nearai_mcp_installs_and_activates_model_visible_capability— assertssource = ProductAuthAccount(provider=nearai)cargo test -p ironclaw_reborn_composition --lib nearai_mcp— 9/9cargo test -p ironclaw_reborn_composition --lib available_extensions— 7/7cargo test -p ironclaw_extensions --test manifest_v2_contract— 44/44 (covers parsing of the new source variant)cargo test -p ironclaw_authorization --test runtime_credentials_contract— 6/6 (covers ProductAuthAccount → InjectCredentialAccountOnce emission)cargo fmt --checkcleancargo check -p ironclaw_reborn_compositioncleanOut of scope
UnavailableAuthProviderClient) + manual-token submit flow already lets users create anearaiaccount and supply the access secret without a dedicated provider client. A first-classAuthProviderClientimplementation can land as a follow-up.NEARAI_API_KEYenv var inironclaw_llm(LLM chat session auth). That's a separate auth surface from the MCP extension credential and is not migrated here.Related