Skip to content

fix(credential-gateway): check only selected native model endpoints and name the setting - #4071

Merged
freeqaz merged 9 commits into
mainfrom
fix-fail-early-1029-endpoint
Oct 29, 2026
Merged

freeqaz merged 9 commits into
mainfrom
fix-fail-early-1029-endpoint

Conversation

@freeqaz

@freeqaz freeqaz commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Dedicated native OpenClaw with a credential_source Harness binding sends each model's requests to that model's catalog baseUrl. The source's OpenShell profile admits only the source endpoint, so the Credential Gateway refuses a mismatch in attachForRevision. The check had two defects:

  • It checked every catalog entry. Compute renders only the selected models into the native worker's catalog (nativeRuntimeConfiguration). An unselected entry for another endpoint gets no traffic, but it still failed the deployment.
  • Its message named nothing. The refusal was "Dedicated native OpenClaw model endpoints must match the bound credential source endpoint." The operator could not tell which setting to fix.

Change

  • One selection rule. A new apps/controller/src/drivers/native-openai-catalog.ts holds nativeOpenAiModelSelections, beside the shared openai-endpoint.ts, which stays free of package imports for the profile renderer. It returns the references in agents.defaults.model and each agents.entries.*.model (primary and fallbacks, once each) and the models.providers.openai.models entries each one names. Compute's nativeRuntimeConfiguration now uses it in place of its private harnessModels and inline matcher, with the same refusals in the same order.
  • Gateway backstop. attachForRevision checks only the selected entries. A mismatch names the baseUrl setting that applies (models.providers.openai.models.N.baseUrl, or models.providers.openai.baseUrl when the entry inherits it) and the source and written hosts, with any explicit port, so a port mismatch shows. Hosts are shortened to fit the 256-character message cap. It never shows the source ID, the key or a model reference.
  • Still fail-closed. If there is no selection, a selection that is not openai/<model>, or a selection without exactly one catalog entry, the check keeps the generic refusal. Compute refuses all of these at admission, so a real revision does not reach the gateway in that state.
  • Ids match after trimming. OpenClaw trims catalog ids and merges entries whose trimmed ids are equal, so an unselected entry whose id differs only by surrounding spaces still configures the selected model. nativeOpenAiModelSelections now compares trimmed ids, so such an entry is a second match: Compute admission and preparation refuse it ("must hold exactly one entry for each selected openai model"), and the gateway keeps its generic refusal.
  • The startup model probe gets only the primary model's entry. On Dedicated OpenClaw, harnessProbeConfiguration used to pass the whole authored models.providers.openai block, unselected entries included, to the startup model check. It now keeps only the primary model's catalog entry (as written), as the native worker gets only the selected entries. Admission checks are unchanged; embedded OpenClaw still gets the whole block, as its Gateway uses it.
  • Compute admission uses the shared selection. validateHarnessAuth no longer has its own copy of the selection walk; the refusals are the same.
  • Docs. The OpenShell Credential Gateway reference now says "every selected native model endpoint" (the page is at 2,492 words, under the cap). The Harness execution topology flow names the shared selection, the named refusal, trimmed id matching and the probe's single entry.

Not in this PR (finding follow-up): an earlier refusal at Agent save or the Create Agent preflight. When an Agent is saved or preflighted, no Driver sees both the Configuration and the bound source's endpoint. ComputeDriver.validateHarnessAuth gets only the source's catalog type, and the Credential Gateway contract has no admission hook. OCC would need to learn the gateway's base_url config key. An earlier refusal needs a Driver contract change, so it needs an RFC first.

Verification

  • tests/integration/credential-source-api.test.mjs, "OpenShell custom and default credential profiles keep independent ownership and lifecycle". The test drives the real OpenShell Credential Gateway Driver against the test's provider store, and its native revision now selects its model like a real one. New cases:
    • An unselected entry for another endpoint attaches. With main's gateway code this fails with the generic refusal.
    • Five selected mismatches each fail with their exact message. The cases are the entry's own baseUrl, the provider baseUrl with a path-only mismatch, no baseUrl at all, a fallback, and a roster entry.
    • No selection, a selection with no catalog entry, and an unselected entry whose id matches the selected one after trimming keep the generic refusal.
    • A differently spelled but equal endpoint (https://MODELS.example.test:8443/api/v1/) attaches, and a port-only mismatch names both hosts with their ports.
  • tests/conformance/kubernetes-compute.test.mjs:
    • "revision lifecycle rejects invalid ownership and unsupported workloads before cluster access": a Secret-backed Dedicated OpenClaw catalog with an unselected entry whose id matches after trimming is refused by name at admission.
    • "native OpenClaw preparation and activation use only outbound Sandbox enrollment": with a selected fallback and an unselected entry for another endpoint, the native worker gets both selected entries and the startup probe configuration holds only the primary model's entry.
    • The new cases fail with the previous head's code (e43a2a7).
  • Mutants, all killed (10 on the latest changes): ids compared without trimming; endpoints compared without normalization; the host shown without its port or as the whole URL; the probe given the whole catalog; preparation not filtering the probe; the default model, fallbacks or roster entries not selected; always naming the provider setting; the probe given another selected model's entry instead of the primary's. Earlier, also killed: generic message restored; empty selection admitted; unmatched selection admitted.
  • On a remote test VM, against a merge with current main (59e2068): tsc --build passes; credential-source-api and kubernetes-compute pass 412/412; both files pinned 20/20 at 2 CPUs. Earlier head: tsc --build, the checks-baseline-1 lane (it runs this file and tests/conformance/kubernetes-compute.test.mjs, which covers every nativeRuntimeConfiguration refusal) pass (3,447 tests), and the file pinned 20/20 at 2 CPUs.
  • The docs word count and the flow-doc validator pass. Prettier and ESLint pass on the changed files.

Risks

  • Less checking at the gateway. Unselected entries are no longer compared. Only selected models reach the native worker's catalog, and the source's profile still admits only the source endpoint, so an unselected entry cannot get the credential. The startup model probe now gets only the primary entry too.
  • Compute refactor. The selection logic moved to the shared module, and the existing Compute conformance tests cover its refusals. The only behaviour change is that ids are matched after trimming, which refuses a catalog that OpenClaw would have merged.
  • Probe input change. A Dedicated OpenClaw startup probe now sees one catalog entry. A probe that relied on another entry for the primary model's settings could have resolved a model the worker would not run; now both use the same entry.
  • Message change. The refusal for a selected mismatch now has a new text. Its error class is unchanged, so worker handling is the same.

…nd name the setting

The deploy-time check that a Dedicated OpenClaw catalog uses the bound
credential source endpoint checked every catalog entry, so an unselected
entry for another endpoint failed the deployment, and its generic message
named no setting. It now checks the models the Agents select (the shared
selection that Compute uses to render the native worker catalog) and names
the baseUrl setting and both hosts. Shapes Compute refuses earlier stay
refused with the generic text.
…endency-free endpoint module

The installation profile renderer imports openai-endpoint.ts from a checkout
without node_modules, so the helper's @openclaw-enterprise/utils import broke
profile-renderer's no-dependencies test. Move it to native-openai-catalog.ts.
@freeqaz
freeqaz requested a review from a team as a code owner October 29, 2026 17:13
@clawsweeper

clawsweeper Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 29, 2026
@clawsweeper

clawsweeper Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes

The PR checks only selected native model endpoints against the credential source and names the configuration setting when they differ.

Example: Select openai/model at https://models.example.test/v1 while retaining an unused catalog entry at https://other.example.test/v1.

  • Before: Credential attachment rejects the deployment because the unused entry differs from the source endpoint.
  • After: Attachment succeeds; selecting the other entry instead produces a refusal naming its baseUrl setting.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) Focused source-backed repair with useful regression coverage; confidence in executed behavior is limited to reported harness validation.
Proof confidence 🦐 gold shrimp (3/6) This collaborator-authored PR is outside the ordinary contributor-proof gate; supplied validation exercises the gateway driver with test-double storage, while the worker projection and credential scope remain unchanged.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: An operator cannot deploy a native Agent when an unused model catalog entry has a different endpoint, and selected mismatches produce an unhelpful generic refusal.
Reason: The collaborator-authored direction repairs a mismatch between deployment validation and the actual worker catalog without adding configuration or public contracts.

Merge readiness

✅ Ready for maintainer review

Keep open: this collaborator-authored PR fixes a source-confirmed deployment refusal still present on main; no discrete introduced defect was found.

Priority: P2
Reviewed head: e43a2a752c1e96e072df2fed56de1b81d58e8c0d

Before merge

None.

Findings

None.

Tests

  • Missing end-to-end proof: No supplied shipped-flow run demonstrates deployment with an unused mismatched entry and the named refusal after selecting it; base/head regression results are reported in the body.
Agent review details

How this fits together

Kubernetes Compute projects selected models into the native worker configuration, while the OpenShell Credential Gateway checks their endpoints before attaching a source-owned credential profile.

flowchart TB
A[Agent configuration] --> B[Shared model selection]
B --> C[Compute worker catalog]
B --> D[Gateway endpoint check]
E[Credential source endpoint] --> D
E --> F[OpenShell endpoint profile]
D --> F
C --> G[Native worker]
F --> G
Loading

Technical review

Best possible solution:

Use the shared selection rule for both worker rendering and gateway validation while retaining exact source-endpoint equality.

Do we have a high-confidence way to reproduce the issue?

Main's all-entry comparison rejects an unused mismatched endpoint even though Compute excludes it from the worker catalog; this was verified from source without executing target code.

Is this the best way to solve the issue?

The shared selector removes selection drift between existing owners, preserves Compute's validation behavior, and avoids expanding the Driver contract for earlier admission.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 6caa633640ac.

Provenance checked

Testing

Proof path: in-process harness.

Security

None.

Evidence

What I checked:

Labels

Label changes:

  • add P2: A supported dedicated-native configuration can fail deployment because of an unused catalog entry; the affected topology and fix are narrow.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Label justifications:

  • P2: A supported dedicated-native configuration can fail deployment because of an unused catalog entry; the affected topology and fix are narrow.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Provide redacted deployment output showing unused-endpoint acceptance and the named refusal for a selected mismatch.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

Reviewed October 29, 2026, 1:27 PM ET / 17:27 UTC.

…ly the primary entry

OpenClaw trims catalog ids and merges entries whose trimmed ids collide, so an
unselected entry such as " model" could give the selected model its endpoint
while the selection check skipped it. The shared selection now matches trimmed
ids, so such an entry is a second match that Compute and the Credential Gateway
refuse. On Dedicated OpenClaw the startup model probe now gets only the primary
model's catalog entry instead of the whole authored catalog.

Also: Compute admission uses the shared selection walk, the endpoint refusal
shows the host with its port, and tests cover a differently spelled but equal
endpoint and a port mismatch.
…ed Dedicated OpenClaw key

OpenClaw merges catalog entries whose ids match after trimming, so an
unselected padded entry with its own endpoint would give the selected model
that endpoint. Kubernetes admission now counts it as a second match.
@freeqaz
freeqaz merged commit 27ca6d8 into main Oct 29, 2026
40 of 41 checks passed
@freeqaz
freeqaz deleted the fix-fail-early-1029-endpoint branch October 29, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant