feat(onboard): activate portable runtime inference - #8753
Conversation
Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>
📝 WalkthroughWalkthroughPortable inference descriptors can now activate custom endpoints during onboarding. The loader securely validates and consumes descriptor files. Scoped credential overrides keep API keys in asynchronous runtime state without exporting or persisting them. Tests and documentation cover activation, validation, and cleanup. ChangesPortable inference onboarding
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant OnboardCommand
participant DescriptorLoader
participant CredentialStore
participant runOnboard
OnboardCommand->>DescriptorLoader: Load portable inference descriptor
DescriptorLoader-->>OnboardCommand: Return validated activation
OnboardCommand->>CredentialStore: Apply scoped API-key override
OnboardCommand->>runOnboard: Run onboarding with portable settings
runOnboard->>CredentialStore: Resolve runtime credential
CredentialStore-->>runOnboard: Return scoped API key
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Comment |
|
🌿 Preview your docs: https://nvidia-preview-pr-8753.docs.buildwithfern.com/nemoclaw |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/lib/onboard/experimental/portable-inference-descriptor.test.ts (2)
198-212: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRely on project-level spy restoration.
The
cliVitest project enablesrestoreMocks, so thetry/finallywithopenSpy.mockRestore()is redundant. You can keep the assertions and drop the wrapper.♻️ Proposed simplification
const openSpy = vi.spyOn(fs, "openSync"); - try { - await expect(loadPortableInferenceDescriptor({ filePath, now: () => NOW })).rejects.toThrow( - /must be a regular file/, - ); - expect(openSpy).not.toHaveBeenCalled(); - expect(fs.lstatSync(filePath).isFIFO()).toBe(true); - } finally { - openSpy.mockRestore(); - } + await expect(loadPortableInferenceDescriptor({ filePath, now: () => NOW })).rejects.toThrow( + /must be a regular file/, + ); + expect(openSpy).not.toHaveBeenCalled(); + expect(fs.lstatSync(filePath).isFIFO()).toBe(true);Based on learnings: Vitest test files under
srcrun in thecliproject, which enablesclearMocks,restoreMocks,unstubEnvs, andunstubGlobals, so suite teardown should only clean resources Vitest does not manage.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/experimental/portable-inference-descriptor.test.ts` around lines 198 - 212, In the test “leaves a FIFO in place without opening it,” remove the try/finally wrapper and the explicit openSpy.mockRestore() call, relying on the cli Vitest project’s restoreMocks setting while preserving the existing assertions and FIFO cleanup behavior.Source: Learnings
82-112: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAdd negative cases for the
apiKeyformat guard and the size limit.The table covers JSON, expiry, extra fields, and query strings. It does not cover three guards in
parseDescriptorandreadDescriptorBytes:
apiKeythat contains\n,\r, or\u0000, or that has surrounding whitespace (lines 220-226 ofportable-inference-descriptor.ts).modelthat contains a control character (lines 227-235).- A descriptor larger than
DESCRIPTOR_MAX_BYTES(lines 127-129).The
apiKeyguard blocks header and shell injection through the credential value. A regression there is silent without a test.💚 Proposed additional cases
["an extra field", descriptor({ region: "us-test-1" }), /exactly these fields/], + [ + "a newline-bearing API key", + descriptor({ apiKey: "secret\nX-Injected: 1" }), + /apiKey has an invalid length or format/, + ], + [ + "a control character in the model", + descriptor({ model: "vendor/model\u0001" }), + /model has an invalid length or format/, + ], + [ + "an oversized descriptor", + descriptor({ apiKey: "a".repeat(17 * 1024) }), + /apiKey has an invalid length or format/, + ],As per path instructions for
src/lib/{security,credentials,shields}/**-style security boundaries: "Require negative-path tests that prove the boundary rejects bypasses and does not leak secrets in errors, logs, state, or process arguments."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/experimental/portable-inference-descriptor.test.ts` around lines 82 - 112, Add negative cases to the parameterized tests around the existing “deletes an admitted descriptor” test for apiKey values containing newline, carriage return, NUL, or surrounding whitespace, a model containing a control character, and a descriptor exceeding DESCRIPTOR_MAX_BYTES. Assert each is rejected and the file is deleted, while ensuring invalid apiKey values are not exposed in errors or other observable output.Source: Path instructions
src/lib/onboard/experimental/portable-inference-descriptor.ts (1)
101-106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueInclude the errno code in the open failure message.
openDescriptormaps every non-ENOENTfailure to one message.ELOOP,EACCES, andEMFILEthen look identical to an operator. The errno code contains no secret, so you can report it.♻️ Proposed change
} catch (error) { if (isErrnoException(error) && error.code === "ENOENT") return null; - throw descriptorError(`cannot open ${filePath} without following links or blocking.`); + const code = isErrnoException(error) ? ` (${error.code ?? "unknown"})` : ""; + throw descriptorError( + `cannot open ${filePath} without following links or blocking${code}.`, + ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/experimental/portable-inference-descriptor.ts` around lines 101 - 106, Update the non-ENOENT error path in openDescriptor to include the caught errno code in the descriptorError message, while preserving the existing ENOENT-to-null behavior and rethrowing all other failures as descriptor errors.test/credentials.test.ts (1)
137-139: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the temporary HOME directory after the test.
Line 138 creates a directory with
mkdtempSyncand never removes it. Each run leaves a directory in the system temp location. Vitest does not manage filesystem resources, so this test must clean it up.♻️ Proposed fix
it("scopes a runtime credential without exporting or enumerating it", async () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-creds-")); - const credentials = await importCredentialsModule(home); + try { + const credentials = await importCredentialsModule(home); + // ... existing body ... + } finally { + fs.rmSync(home, { recursive: true, force: true }); + }If the surrounding suite already removes these directories in a shared hook, disregard this comment.
Based on learnings: in suite-level teardown, clean up only resources Vitest does not manage, for example temporary directories and files.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/credentials.test.ts` around lines 137 - 139, Update the test containing the runtime credential case and its suite teardown to remove the directory created by mkdtempSync after the test completes, using the existing home variable and a guaranteed cleanup hook or try/finally; preserve the test behavior and avoid duplicating cleanup if a shared hook already handles it.Source: Learnings
src/lib/onboard/command.test.ts (1)
544-548: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a case for a portable run with no descriptor, and inject the loader.
Two related gaps:
No test covers
loadPortableInferenceDescriptorresolvingnullfor a portable profile. That is the documented default path, in whichNEMOCLAW_PROVIDERstaysollamaandNEMOCLAW_MODELstaysqwen3-vl:4b(src/lib/onboard/command.ts, lines 438-441).
activatePortableInferencenow runs for every portable test in this file. The existing portable-defaults test at lines 506-522 does not injectloadPortableInferenceDescriptor, so it calls the real loader against/run/nemoclaw/portable-inference.json. On a machine that has a descriptor at that path, the test consumes and deletes it and its assertions change. Inject a loader that returnsnullthere to keep the test hermetic.💚 Proposed additional case
+ it("keeps local portable defaults when no descriptor is present", async () => { + const env: NodeJS.ProcessEnv = {}; + const runOnboard = vi.fn(async (options) => { + expect(options.portableInferenceActivation).toBeNull(); + expect(env).toMatchObject({ + NEMOCLAW_PROVIDER: "ollama", + NEMOCLAW_MODEL: "qwen3-vl:4b", + }); + expect(env.NEMOCLAW_ENDPOINT_URL).toBeUndefined(); + expect(env.NEMOCLAW_PREFERRED_API).toBeUndefined(); + }); + + await runOnboardCommand({ + flags: { "experimental-profile": "portable" }, + env, + loadPortableInferenceDescriptor: async () => null, + runOnboard, + }); + + expect(runOnboard).toHaveBeenCalledOnce(); + });As per path instructions for test review: "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/command.test.ts` around lines 544 - 548, Add coverage for a portable onboarding run where loadPortableInferenceDescriptor resolves null, asserting NEMOCLAW_PROVIDER remains ollama and NEMOCLAW_MODEL remains qwen3-vl:4b. Update the existing portable-defaults test to inject a loadPortableInferenceDescriptor stub returning null through activatePortableInference, keeping the test isolated from the real descriptor file.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/inference/set-up-openai-compatible-endpoint.mdx`:
- Line 107: Update the baseUrl description in the endpoint configuration table
to clearly state that the compatible endpoint base URL must use HTTPS, while
preserving “compatible endpoint” as the fixed term and retaining the existing
credential, query, fragment, and SSRF-policy guidance.
In `@src/lib/credentials/scoped-overrides.ts`:
- Around line 15-19: Update the validation loop in withCredentialOverrides to
reject any value containing \u0000, \r, or \n, matching the portable descriptor
apiKey guard, instead of stripping carriage returns. Preserve trimming and
empty-value rejection, and only store values that pass this fail-closed
control-character validation.
---
Nitpick comments:
In `@src/lib/onboard/command.test.ts`:
- Around line 544-548: Add coverage for a portable onboarding run where
loadPortableInferenceDescriptor resolves null, asserting NEMOCLAW_PROVIDER
remains ollama and NEMOCLAW_MODEL remains qwen3-vl:4b. Update the existing
portable-defaults test to inject a loadPortableInferenceDescriptor stub
returning null through activatePortableInference, keeping the test isolated from
the real descriptor file.
In `@src/lib/onboard/experimental/portable-inference-descriptor.test.ts`:
- Around line 198-212: In the test “leaves a FIFO in place without opening it,”
remove the try/finally wrapper and the explicit openSpy.mockRestore() call,
relying on the cli Vitest project’s restoreMocks setting while preserving the
existing assertions and FIFO cleanup behavior.
- Around line 82-112: Add negative cases to the parameterized tests around the
existing “deletes an admitted descriptor” test for apiKey values containing
newline, carriage return, NUL, or surrounding whitespace, a model containing a
control character, and a descriptor exceeding DESCRIPTOR_MAX_BYTES. Assert each
is rejected and the file is deleted, while ensuring invalid apiKey values are
not exposed in errors or other observable output.
In `@src/lib/onboard/experimental/portable-inference-descriptor.ts`:
- Around line 101-106: Update the non-ENOENT error path in openDescriptor to
include the caught errno code in the descriptorError message, while preserving
the existing ENOENT-to-null behavior and rethrowing all other failures as
descriptor errors.
In `@test/credentials.test.ts`:
- Around line 137-139: Update the test containing the runtime credential case
and its suite teardown to remove the directory created by mkdtempSync after the
test completes, using the existing home variable and a guaranteed cleanup hook
or try/finally; preserve the test behavior and avoid duplicating cleanup if a
shared hook already handles it.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: f1642f81-63fe-4406-8bb5-cfb52f248abf
📒 Files selected for processing (8)
docs/inference/set-up-openai-compatible-endpoint.mdxsrc/lib/credentials/scoped-overrides.tssrc/lib/credentials/store.tssrc/lib/onboard/command.test.tssrc/lib/onboard/command.tssrc/lib/onboard/experimental/portable-inference-descriptor.test.tssrc/lib/onboard/experimental/portable-inference-descriptor.tstest/credentials.test.ts
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
Second-opinion terminology and E2E selections are advisory. Live E2E does not run automatically for pull requests. 3 semantic terminology decisionsTerminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.
E2E guidanceAdvisory only. A maintainer can dispatch the default E2E suite against this exact revision. Recommended E2E: This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>
|
PRA-1 is not valid for the current implementation. The scoped credential already reaches only the authorized provider-registration operation: withCredentialOverrides stores it in the asynchronous credential scope, hydrateCredentialEnv resolves that scoped value, and setupRemoteProviderInference passes a dedicated COMPATIBLE_API_KEY environment to upsertProvider. The ambient process.env remains unset. Commit 91ccff7 adds an integrated regression for that exact chain. It asserts that provider registration receives the scoped key and that process.env.COMPATIBLE_API_KEY is unset both inside and after the scope. The same commit also addresses the valid inline review findings by rejecting NUL, CR, and LF without mutating valid credential bytes, expanding negative descriptor tests, and correcting the documentation. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/onboard/inference-providers/remote-openai-surface.test.ts (1)
164-168: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winVerify scoped credential cleanup after the callback.
Line 164 and Line 167 check only
process.env.COMPATIBLE_API_KEY. They do not prove thatwithCredentialOverridesremovedruntime-only-secretfrom the asynchronous credential scope. A leaked override could still reach a later provider registration while these assertions pass. Add a post-scope resolution or registration assertion that the secret is no longer returned.As per path instructions, tests must verify observable behavior through the public boundary. Based on the PR objective, the key must remain scoped to the asynchronous call tree and must not persist.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/inference-providers/remote-openai-surface.test.ts` around lines 164 - 168, Add a post-callback assertion in the test around withCredentialOverrides that resolves or registers credentials through the public provider boundary and verifies runtime-only-secret is no longer returned. Keep the existing process.env.COMPATIBLE_API_KEY checks, and ensure the assertion runs after the asynchronous scope completes to detect leaked overrides.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/lib/onboard/inference-providers/remote-openai-surface.test.ts`:
- Around line 164-168: Add a post-callback assertion in the test around
withCredentialOverrides that resolves or registers credentials through the
public provider boundary and verifies runtime-only-secret is no longer returned.
Keep the existing process.env.COMPATIBLE_API_KEY checks, and ensure the
assertion runs after the asynchronous scope completes to detect leaked
overrides.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 3ce0cd7e-be5d-4ead-a51d-72a95cc9943d
📒 Files selected for processing (8)
docs/inference/set-up-openai-compatible-endpoint.mdxsrc/lib/credentials/scoped-overrides.tssrc/lib/onboard/command.test.tssrc/lib/onboard/credential-env.tssrc/lib/onboard/experimental/portable-inference-descriptor.test.tssrc/lib/onboard/experimental/portable-inference-descriptor.tssrc/lib/onboard/inference-providers/remote-openai-surface.test.tstest/credentials.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- docs/inference/set-up-openai-compatible-endpoint.mdx
- src/lib/credentials/scoped-overrides.ts
- src/lib/onboard/command.test.ts
- src/lib/onboard/experimental/portable-inference-descriptor.ts
- test/credentials.test.ts
<!-- markdownlint-disable MD041 --> ## Summary Adds the August 10, 2026 release entry for v0.0.106 before the release plan captures the candidate commit. The entry groups supported user-visible changes since v0.0.105 and omits work that is private, dormant, qualification-only, internal, or outside accepted product scope. ## Changes - Add the canonical `docs/changelog/2026-08-10.mdx` entry with the exact `v0.0.106` heading. - Summarize readiness, OpenShell v0.0.101, bounded DGX Spark and vLLM choices, inference, sandbox lifecycle, security, Hermes, and host maintenance changes. - Use root-absolute documentation routes and the shared dated changelog source for all published variants. - Exclude PR #8753 from canonical release claims because no accepted issue or design decision establishes its product scope. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [x] Existing tests cover changed behavior — justification: `test/changelog-docs.test.ts` validates dated changelog SPDX placement, version headings, forbidden terms, and link form. - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: `docs/changelog/2026-08-10.mdx`; an independent Codex Desktop subagent reviewed the writing rules and documentation style, terminology, structure, voice, code-sample presentation, links, source and test accuracy, and product scope at commit `c15039c94`. - Agent: Codex Desktop <!-- docs-review-head-sha: c15039c --> <!-- docs-review-agents-blob-sha: c4923a3 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: Not applicable; `scripts/prepare-dgx-station-host.sh` is unchanged. - Station profile/scenario: Not applicable. - Result: Not applicable. - Supporting evidence: Not applicable. ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: `npx vitest run test/changelog-docs.test.ts` passed 6 tests. - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: Not applicable to one documentation-only release entry. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — result: passed with 0 errors and 2 existing warnings. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) — dated changelog entries use the required parser-safe MDX SPDX block and no frontmatter. --- Signed-off-by: Julie Yaunches <jyaunches@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added release notes for v0.0.106. * Documented lifecycle readiness checks, sandbox lifecycle improvements, safer updates, and scoped uninstall capabilities. * Added details on OpenShell v0.0.101 adoption and DGX Spark vLLM configuration options. * Documented recovery and inference diagnostics, endpoint security enforcement, and installation and image updates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- markdownlint-disable MD041 --> ## Summary Completes the v0.0.106 changelog for four user-visible changes that merged before the tag but were omitted from the pre-tag entry. Keeps public security pages focused on operator guidance by relocating maintenance contracts to contributor guidance and the owning OpenClaw dependency review. Records PR #8753's portable inference descriptor as Experimental while leaving its existing workflow documentation unchanged. ## Changes - Add managed-container restart-transition recovery from PR #8765, Shields parent-owner preservation from PR #8767, and managed storage remediation plus NVIDIA driver parsing from PR #8768 to the canonical v0.0.106 entry. - Add the Experimental portable inference descriptor from PR #8753 to the v0.0.106 entry, including its short-lived credential boundary, manual standby behavior, and owning setup page. - Keep Process Controls focused on the operator-facing immutable-image boundary and move the blueprint image-pin maintenance contract to `CONTRIBUTING.md`. - Keep Gateway and Secret Controls focused on operator actions and move the OpenClaw audit-suppression tests and distinct removal conditions to the owning OpenClaw 2026.7.1 dependency review. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [x] Existing tests cover changed behavior — justification: the dated-changelog, published-route, and documentation-link tests cover the changed release entry and links. - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [x] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [x] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: an independent Codex Desktop documentation writer reviewed exact head `bbfed36ca`; the review verified the operator-facing security claims, the distinct `allowInsecureAuth` and device-auth suppression removal conditions against their generator branches, and the confirmed Experimental #8753 release claim. No runtime or policy behavior changes. - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: `CONTRIBUTING.md`, `docs/changelog/2026-08-10.mdx`, `docs/security/gateway-authentication-controls.mdx`, `docs/security/openclaw-2026.7.1-dependency-review.md`, and `docs/security/process-controls.mdx`; the subagent reviewed `docs/CONTRIBUTING.md`, `WRITING.md`, terminology, structure, voice, code-sample presentation, canonical ownership, factual accuracy, and product scope. - Agent: Codex Desktop <!-- docs-review-head-sha: bbfed36 --> <!-- docs-review-agents-blob-sha: c4923a3 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: Not applicable; `scripts/prepare-dgx-station-host.sh` is unchanged. - Station profile/scenario: Not applicable. - Result: Not applicable. - Supporting evidence: Not applicable. ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: `npx vitest run test/changelog-docs.test.ts test/check-docs-published-routes.test.ts test/check-docs-links.test.ts` passed; `npm run docs` and `git diff --check` passed again after the review correction. - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: not applicable to this bounded documentation-only change. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — result: passed with zero errors and the existing light-mode accent contrast warning. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) — no new pages. --- Signed-off-by: Miyoung Choi <miyoungc@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Documented requirements for keeping managed sandbox image digest pins synchronized and immutable. - Added guidance for validating custom images and using reviewed image sources during onboarding. - Expanded release notes with portable inference profiles, endpoint references, cleanup behavior, startup handling, and installer details. - Updated security documentation with current dependency-review information and authentication-control boundaries. - Clarified sandbox ownership, permissions, workload identity, and managed-container restart behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>
Summary
Add an optional, single-use runtime inference descriptor for the portable experimental profile. A valid descriptor selects an OpenAI-compatible endpoint without exporting its API key; an absent descriptor preserves local
qwen3-vl:4bbehavior, and an existing local runner remains installed as manual standby.Changes
/run/nemoclaw/portable-inference.jsononly from a protected directory and a current-user-owned regular file with mode0600and one hard link.process.env, child processes, persisted state, or credential listings. The current consumer is compatible-endpoint validation and OpenShell provider registration;scopes a runtime credential without exporting or enumerating itprotects the boundary.Type of Change
Quality Gates
Documentation Writer Review
docs-updateddocs/inference/set-up-openai-compatible-endpoint.mdx; reviewed the completed source, tests, terminology, structure, voice, and code-sample presentation against the implementation and repository writing rules.Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailablenpm run typecheck:clipassed.npm run docsbuilds without warnings (doc changes only) — passed with 0 errors; 2 existing warnings remain.Signed-off-by: Senthil Ravichandran senthilr@nvidia.com
Summary by CodeRabbit
New Features
Documentation
Bug Fixes