Skip to content

RSPEED-0000: Close OpenShell spawner production gaps — skills, credentials, filesystem policy, MCP secrets - #105

Merged
jameswnl merged 2 commits into
mainfrom
issue-104-openshell-production-gaps
Jul 11, 2026
Merged

jameswnl merged 2 commits into
mainfrom
issue-104-openshell-production-gaps

Conversation

@jameswnl

@jameswnl jameswnl commented Jul 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Features implemented

  • Skills image: Extract via Podman CLI + stream into sandbox via exec_stream tar
  • Credentials: OpenShell Provider API (CreateProvider + AttachSandboxProvider) with file-based fallback for Vertex
  • Filesystem policy: SandboxPolicy.filesystem.read_only/read_write with injection-target allowlist
  • MCP secrets: File injection via _do_write_file with correct mount_path/key handling
  • TLS certs: Skip with info log (gateway provides transport security)
  • Service account: Skip with info log (OpenShell manages identity)
  • Provider cleanup: _detach_provider in _do_destroy

JWT workaround removed

OpenShell v0.0.79 (PR NVIDIA/OpenShell#2156) delivers sandbox JWTs via Podman secrets natively when gateway_jwt is configured with signing keys. The old _inject_podman_token workaround (issue #82, PR #84) is no longer needed.

Closes #104

Test plan

  • 52 unit tests pass (1194 total)
  • E2E on EC2 (RHEL 9, m5.xlarge) with OpenShell gateway v0.0.80
  • All 6 standard example workflows pass via OpenShell spawner
  • K8s real-cluster workflow via Kind + MCP kubectl via OpenShell spawner
  • Full sandbox lifecycle: create → wait_ready → exec → destroy confirmed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved sandbox setup with provider-based authentication and credential injection.
    • Added support for filesystem read-only policies and MCP secret configuration.
    • Enhanced sandbox startup readiness checks and skills loading.
  • Bug Fixes

    • Sandboxes are now cleaned up reliably when startup steps fail.
    • Provider connections are detached during cleanup and destruction.
    • Improved handling of missing secrets and optional security configuration.

Implement all missing features in OpenShellSpawner:
- Skills image loading via Podman CLI extraction + exec_stream tar
- Credential injection via OpenShell Provider API with file-based fallback
- Read-only filesystem via SandboxPolicy with injection-target allowlist
- MCP secret file injection with correct mount_path/key handling
- TLS certs and service_account skip with info log
- Provider cleanup in _do_destroy()

Remove Podman 5.8.x JWT workaround (issue #82):
- OpenShell v0.0.79+ delivers sandbox JWTs natively via Podman secrets
  when gateway_jwt is configured (PR NVIDIA/OpenShell#2156)
- Removed _inject_podman_token and all helpers/constants
- Removed podman_cli/podman_socket constructor params
- History documented in spawner docstring

E2E verified on EC2 (RHEL 9, m5.xlarge):
- OpenShell gateway v0.0.80 with Podman driver
- All 6 example workflows pass through OpenShell spawner
- Sandbox lifecycle: create → wait_ready → exec → destroy confirmed

Closes #104

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jameswnl, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e3ec137-536d-4a49-b0df-249417b323c2

📥 Commits

Reviewing files that changed from the base of the PR and between b1d4137 and 1903090.

📒 Files selected for processing (2)
  • src/cloud_agents/spawner/openshell_spawner.py
  • tests/unit/spawner/test_openshell_spawner.py
📝 Walkthrough

Walkthrough

OpenShellSpawner now uses Provider-based credential injection, filesystem policies, skills and MCP secret setup, gateway-routed readiness checks, and centralized Provider-aware cleanup. Podman workaround configuration and entrypoint wiring are removed, with expanded lifecycle and injection tests.

Changes

OpenShell sandbox lifecycle

Layer / File(s) Summary
Spawner contract and provider tracking
src/cloud_agents/spawner/openshell_spawner.py, src/cloud_agents/workflow/temporal_entrypoint.py
Removes Podman workaround configuration and implementation, tracks Provider IDs, and constructs the spawner from SandboxClient only.
Sandbox setup and injection
src/cloud_agents/spawner/openshell_spawner.py, tests/unit/spawner/test_openshell_spawner.py
Adds filesystem policy construction, Provider credential injection with file fallback, skills and MCP secret loading, TLS/service-account skip behavior, gateway exposure, and readiness validation.
Cleanup and lifecycle validation
src/cloud_agents/spawner/openshell_spawner.py, tests/unit/spawner/test_openshell_spawner.py
Centralizes failure cleanup, detaches Providers during cleanup and destruction, preserves tracking when deletion fails, and tests post-create failures and cleanup semantics.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OpenShellSpawner
  participant SandboxClient
  participant ProviderAPI
  participant SandboxHTTPServer
  participant OpenShellGateway
  OpenShellSpawner->>SandboxClient: create sandbox with filesystem policy
  OpenShellSpawner->>ProviderAPI: create and attach credential provider
  OpenShellSpawner->>SandboxClient: inject skills and MCP secret files
  OpenShellSpawner->>SandboxHTTPServer: start HTTP server
  OpenShellSpawner->>OpenShellGateway: expose sandbox service
  OpenShellSpawner->>OpenShellGateway: perform host-routed readiness check
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main OpenShellSpawner production-gap work and names the key feature areas changed.
Linked Issues check ✅ Passed The changes align with #104: skills loading, provider-based credentials, read-only filesystem policy, MCP secrets, cleanup, and JWT-workaround removal.
Out of Scope Changes check ✅ Passed The modified files stay focused on OpenShellSpawner behavior, workflow wiring, and matching tests; no unrelated feature work is evident.
Docstring Coverage ✅ Passed Docstring coverage is 89.47% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-104-openshell-production-gaps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review (initial — from PR body + metadata)

Files: 3 files (openshell_spawner.py, temporal_entrypoint.py, test_openshell_spawner.py)
Refs: Closes #104

Assessment from PR body:

[PASS] All 7 gaps from issue #104 addressed:

  1. Skills image — Podman CLI + exec_stream tar ✅
  2. Credentials — Provider API + file-based Vertex fallback ✅
  3. Filesystem policy — read_only/read_write with injection targets ✅
  4. MCP secrets — file injection with mount_path/key fix (per author's refinement) ✅
  5. TLS certs — skip with info log ✅
  6. Service account — skip with info log ✅
  7. Provider cleanup — _detach_provider in _do_destroy ✅

[PASS] JWT workaround removed:

  • OpenShell v0.0.79+ handles JWT natively — clean removal of _inject_podman_token workaround ✅
  • Breaking change: requires gateway v0.0.79+ with gateway_jwt configured ✅

[PASS] Test coverage:

  • 52 unit tests (net new), 1194 total ✅
  • E2E verified on RHEL 9 with real OpenShell gateway ✅
  • K8s Kind + MCP kubectl workflow verified ✅

CI: unit-tests ✅, e2e ✅, integration ✅, build ✅, CodeRabbit ⏳ — holding approval until CodeRabbit completes.

🤖 Reviewed by beesarmy via Claude Code

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one remaining blocking gap in the new OpenShellSpawner implementation.

  • skills_image still appears to be effectively Podman-only, so this PR does not actually close that production gap across OpenShell runtimes. In _load_skills(), the only real implementation path is guarded by if self._podman_cli: and shells out to podman run ... cp .... When podman_cli is absent, the code just logs a warning and returns early:

    • "No podman_cli — attempting direct image pull for skills. K8s environments should pre-populate skills."
    • followed by return

    That means OpenShell deployments that are not relying on a local Podman CLI in the workflow-runner process still silently skip skills_image, which is exactly the class of “silently ignored” production gap this issue says it closes. The current tests also don’t seem to cover a non-Podman OpenShell path for skills_image; they only prove the mocked happy path and the Podman extraction path.

I think the overall direction of the PR is good, but until skills_image works in the non-Podman OpenShell case (or the PR is explicitly narrowed so it no longer claims this gap is closed), I can’t approve it as-is.

🤖 Reviewed by beesarmy via Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/unit/spawner/test_openshell_spawner.py (1)

937-1046: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding _load_skills coverage.

The new suite covers credential, MCP, filesystem, and destroy paths well, but there is no test for _load_skills. That gap is why the uninitialized self._podman_cli regression (flagged in openshell_spawner.py) is not caught here. A test that invokes _load_skills with and without _podman_cli set would guard both the crash and the K8s early-return branch.

🤖 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 `@tests/unit/spawner/test_openshell_spawner.py` around lines 937 - 1046, Add
focused async tests for OpenShellSpawner._load_skills covering both an unset
_podman_cli and an initialized _podman_cli. Verify the unset case does not
raise, and verify the configured case preserves the Kubernetes early-return
behavior without invoking unnecessary skill-loading operations.
🤖 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 `@src/cloud_agents/spawner/openshell_spawner.py`:
- Around line 592-614: Restore initialization of the _podman_cli instance
attribute in OpenShellSpawner.__init__, setting it to None by default so
_load_skills can safely select the no-podman_cli branch when skills_image is
used.
- Around line 469-475: Update the credential validation in the sandbox spawning
flow around cred_value and credential_secret_name so a missing environment
credential aborts sandbox startup instead of merely logging and returning.
Preserve the existing warning context, and propagate an appropriate failure
through the surrounding spawner error-handling path.

---

Nitpick comments:
In `@tests/unit/spawner/test_openshell_spawner.py`:
- Around line 937-1046: Add focused async tests for
OpenShellSpawner._load_skills covering both an unset _podman_cli and an
initialized _podman_cli. Verify the unset case does not raise, and verify the
configured case preserves the Kubernetes early-return behavior without invoking
unnecessary skill-loading operations.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43d49b21-b6c9-4110-8065-c592b6b48e48

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd30ee and b1d4137.

📒 Files selected for processing (3)
  • src/cloud_agents/spawner/openshell_spawner.py
  • src/cloud_agents/workflow/temporal_entrypoint.py
  • tests/unit/spawner/test_openshell_spawner.py

…odman_cli init

beesarmy:
- Skills image extraction now cross-platform: uses Podman Python SDK
  (works in any container) with CLI fallback. Raises RuntimeError if
  neither is available instead of silently skipping.

CodeRabbit:
- Initialize _podman_cli in __init__ to prevent AttributeError
- Missing credentials now raise RuntimeError instead of warn+return

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review (new commits + CodeRabbit findings)

CodeRabbit passed with 2 comments:

  • [NOTED] Security (Minor) — credential handling observation
  • [NOTED] Stability (Major) — _podman_cli never assigned concern. Worth checking — if JWT workaround was removed but skills loading still uses podman_cli, this could affect the skills extraction path.

CI: build ✅, e2e ✅, integration ✅, CodeRabbit ✅, unit-tests ⏳ — holding approval until unit-tests pass.

🤖 Reviewed by beesarmy via Claude Code

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The follow-up commit addresses the remaining blocking gap I called out.

  • skills_image is no longer effectively Podman-CLI-only: _load_skills() now routes through _extract_skills_image(), which first tries the Podman Python SDK and only falls back to the CLI when needed.
  • _podman_cli is initialized again, so that fallback path is safe.
  • Missing credential_secret_name values now fail explicitly instead of being silently skipped, which is a better production behavior for this spawner.

With those fixes in place, I don’t see any remaining blocking issues.

🤖 Reviewed by beesarmy via Claude Code

@jameswnl
jameswnl merged commit a0681e3 into main Jul 11, 2026
6 checks passed
@jameswnl
jameswnl deleted the issue-104-openshell-production-gaps branch July 11, 2026 19:24

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval

CI fully passing (unit-tests ✅, e2e ✅, integration ✅, build ✅, CodeRabbit ✅).

All 7 production gaps from issue #104 implemented + JWT workaround cleanly removed. 52 new tests, E2E verified on RHEL 9 + Kind. CodeRabbit findings noted (security minor + podman_cli stability) — non-blocking for this scope.

Approving.

🤖 Reviewed by beesarmy via Claude Code

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The follow-up commit addresses the remaining blocker I raised.

  • skills_image is no longer effectively Podman-CLI-only. _load_skills() now routes through _extract_skills_image(), which tries crane first, then the Podman Python SDK, then the Podman CLI.
  • That makes the feature meaningfully more cross-runtime and aligns much better with the PR’s claim of closing the OpenShell production-gap around skills loading.
  • The explicit failure on missing credentials is also a better production behavior than silently skipping them.

With that fix in place, I don’t see any remaining blocking issues.

🤖 Reviewed by beesarmy via Claude Code

@beesarmy beesarmy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one remaining correctness issue in the new OpenShell production-gap implementation.

  • The read-only filesystem policy appears to allow the wrong home directory. _build_filesystem_policy() currently whitelists /home/agent, but the repo’s own OpenShell spike docs consistently describe the sandbox runtime user as sandbox with home /home/sandbox (for example in docs/spikes/openshell-spawner-spike.md and docs/spikes/openshell-standalone-setup.md). If OpenShell is still running commands as the sandbox user in the environments we validated, then enabling read_only=True will not actually preserve write access to that user’s home directory, even though the policy is supposed to keep the normal writable paths working. The current unit tests also codify /home/agent, so they would miss that mismatch.

I’d update the policy and tests to match the actual sandbox runtime home directory (or document/prove that OpenShell now uses /home/agent instead). Until that is clarified, I can’t safely approve the filesystem-policy part of this PR.

🤖 Reviewed by beesarmy via Claude Code

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.

OpenShellSpawner: Close production gaps — skills, credentials, filesystem policy, MCP secrets

2 participants