Repository navigation
chore: import the Python SDK history from NVIDIA/OpenShell - #2
Conversation
…ons (!17) Closes NVIDIA/OpenShell#24 ## Problem SSH sessions into the sandbox were **not entering the network namespace** or receiving proxy environment variables. This meant every command run via SSH (the only user-facing path) had unrestricted internet access, completely bypassing OPA network policy enforcement. The root cause: the SSH server was started **before** the network namespace and proxy were created in `lib.rs`, so it never received the netns fd or proxy URL. Additionally, **gRPC inference from within the sandbox did not work at all** — even after fixing the netns, multiple issues prevented the Python SDK from reaching the navigator server through the CONNECT proxy. ## Changes ### Sandbox binary **Core fix — reorder startup + thread netns through SSH:** - `lib.rs`: Move netns + proxy creation before SSH server start. Compute `ssh_netns_fd` and `ssh_proxy_url`, pass them to `run_ssh_server()`. - `ssh.rs`: Thread `netns_fd` and `proxy_url` through the full SSH call chain into `spawn_pty_shell()`. Set proxy env vars on the shell command. Call `setns(fd, CLONE_NEWNET)` in `install_pre_exec()`. **Proxy — control plane allowlist + IPv6 socket lookup:** - `proxy.rs`: Connections to the navigator endpoint (derived from `NAVIGATOR_ENDPOINT`) are always allowed without OPA evaluation, logged with `engine=control_plane`. This is infrastructure the sandbox needs to function, not a user-configurable policy. - `procfs.rs`: Extended `parse_proc_net_tcp` to also check `/proc/<pid>/net/tcp6`. gRPC C-core uses `AF_INET6` sockets even for IPv4 connections, so its TCP entries were invisible to the proxy's identity resolver. Also fixed port parsing to use `rsplit_once(':')` for correct IPv6 address handling. **Proxy env vars — lowercase variants for gRPC C-core:** - `process.rs` + `ssh.rs`: Added lowercase `http_proxy`, `https_proxy`, `grpc_proxy` alongside uppercase. gRPC C-core (libgrpc) checks lowercase first and was ignoring the uppercase-only vars. ### Server - `sandbox/mod.rs`: Added `CAP_SYS_PTRACE` to sandbox pod security context. Required for the proxy (root) to read `/proc/<pid>/fd/` of sandbox-user processes for binary identity resolution. ### Python SDK - `inference.py`: Strip `http://`/`https://` scheme from endpoint before passing to `grpc.insecure_channel()`, which expects `host:port`. Default `endpoint` and `sandbox_id` from `NAVIGATOR_ENDPOINT` / `NAVIGATOR_SANDBOX_ID` env vars so `Inference()` works inside sandboxes with no arguments. ### Build / infra - `ci.toml`: Added `--cap-add=SYS_PTRACE` to `mise run sandbox` to mirror the k8s pod capabilities. ### Documentation - `architecture/sandbox.md`: Documented `CAP_SYS_PTRACE` requirement and the full set of proxy env vars (uppercase + lowercase). ## Testing All 29 sandbox unit tests pass. `mise run pre-commit` passes (fmt, clippy, all workspace tests, python lint). E2E verified on a live cluster: | Test | Result | |------|--------| | Proxy env vars (6 vars, upper+lowercase) | PASS | | Blocked endpoints (google, anthropic via curl) | PASS — all denied | | **gRPC inference from SSH session** (`Inference()` with no args, env var defaults) | **PASS** | | Proxy log: navigator requests show `engine=control_plane` | PASS | | Proxy log: blocked requests show `engine=opa` with correct deny reasons | PASS |
…(!19) Closes NVIDIA/OpenShell#13 ## Summary - add Python sandbox execution APIs for command and callable workflows - consolidate sandbox policy fixtures and expand e2e test coverage for policy and Python exec paths - update CI/build config and images for sandbox e2e execution dependencies ## Test Plan - mise run pre-commit
…as claude, outlook, etc (!23) ## Summary - Add `Provider` entity for managing 3p deps from a sandbox - Add provider CRUD API/server persistence and new CLI workflows (`nav provider create/get/list/update/delete`), including `--from-existing` laptop discovery. - Integrate providers into sandbox create flow: infer from command (`-- claude`), support repeatable `--provider <type>`, prompt before auto-create, and allow manual in-sandbox setup. - Add a dedicated `navigator-providers` crate with per-provider modules and mockable discovery test helpers. ## Key UX Changes - `nav sandbox create --provider gitlab -- claude` - Missing provider prompt now asks before creating from local state. - `nav provider list --names` for scripting/cleanup. ## Test Plan - `mise run cluster:deploy` - `mise run test:e2e:sandbox` - `mise run pre-commit` Closes NVIDIA/OpenShell#19 Closes NVIDIA/OpenShell#22 Closes NVIDIA/OpenShell#11
Closes NVIDIA/OpenShell#67 ## Summary Implements transparent inference interception and routing for sandboxed AI agents. The sandbox proxy intercepts outbound AI SDK calls (OpenAI, Anthropic) and reroutes them through the gateway to policy-controlled backends — enabling organizations to redirect inference traffic to local or self-hosted models without modifying agent code. **Decision model** — a tri-state OPA evaluation for every CONNECT request: 1. Binary + endpoint explicitly allowed in `network_policies` → **allow** (pass through) 2. Not explicitly allowed + `inference.allowed_routes` configured → **inspect for inference** (TLS intercept, detect API patterns, route through gateway) 3. Otherwise → **deny** No endpoint declarations or binary lists needed for inference routing. Just configure `inference.allowed_routes`. ## Key Changes ### Sandbox (interception) - **OPA policy**: New `network_action` Rego rule with three outcomes (`allow`, `inspect_for_inference`, `deny`). New `NetworkAction` enum replaces `PolicyDecision.allowed` bool for the proxy's main decision path. - **Proxy**: New `InspectForInference` path — TLS-terminates client, parses HTTP, detects inference API patterns (`POST /v1/chat/completions`, `/v1/completions`, `/v1/messages`), strips auth headers, forwards via gRPC. - **New module**: `l7/inference.rs` — `InferenceApiPattern`, `detect_inference_pattern()`, HTTP request/response parsing. - **gRPC client**: New `proxy_inference()` for sandbox→gateway forwarding. - **Sandbox init**: Creates OPA engine when inference is configured, even without `network_policies`. ### Gateway (dispatch) - **InferenceService**: `ProxyInference` RPC loads sandbox policy, resolves allowed routes, dispatches to router. Full CRUD for inference routes. - **Proto**: `InferenceRoute`, `InferenceRouteSpec`, `ProxyInferenceRequest/Response`, Inference gRPC service. ### Router (backend proxying) - **New crate**: `navigator-router` with `Router`, `proxy_with_candidates()`, protocol-based route selection, backend HTTP proxying with auth header rewriting. - **Mock support** for testing (`mock://` scheme). ### CLI - `nav inference create/update/delete/list` commands for route management. ### Python SDK - Updated protobuf bindings. Removed old `inference.py` client (replaced by transparent interception). ### Documentation - New `architecture/inference-routing.md` — end-to-end system documentation. - Updated `architecture/sandbox.md` — proxy, OPA, and source index sections. - Updated `architecture/README.md` — new subsystem overview and diagram. ## Addendum: Chunked Transfer Compatibility This branch now also fixes intercepted SDK requests that send chunked request bodies: - `inspect_for_inference` now accepts `Transfer-Encoding: chunked` and decodes chunked request bodies before forwarding to the gateway - Removed the prior `411 Length Required` response for chunked intercepted requests - Added request/response header sanitization for framing and hop-by-hop headers (`content-length`, `transfer-encoding`, `connection`, etc.) to keep forwarded requests and returned responses valid - Added unit tests for chunked parsing and header sanitization Note: this improves compatibility for streaming-style SDK request patterns; true token-by-token passthrough response streaming is still a separate follow-up. ## Minimal Policy for Inference Routing ```yaml inference: allowed_routes: - local ``` Any outgoing connection from a binary not explicitly allowed in `network_policies` will be intercepted and checked for inference API patterns. ## Test Plan - [x] `cargo test --workspace` — all tests pass - [x] `mise run pre-commit` — all checks pass - [x] E2E: OpenAI chat completions routed through gateway - [x] E2E: Anthropic messages routed through gateway - [x] E2E: Python OpenAI SDK from sandbox (`examples/inference/inference.py`) - [x] E2E test: `e2e/python/test_inference_routing.py`
…Shell#71) Add Apache 2.0 licensing, SPDX copyright headers on all source files, DCO enforcement, third-party notices, and CI enforcement. - LICENSE: Apache License 2.0 full text - DCO: Developer Certificate of Origin 1.1 - SPDX headers on all 176 source files (.rs, .py, .proto, .rego, .sh, .toml, .yaml, Dockerfiles) - scripts/update_license_headers.py: header management with --check mode - scripts/generate_third_party_notices.py: dependency license aggregation - THIRD-PARTY-NOTICES: generated listing of all Rust and Python deps - build/license.toml: mise tasks for license:check and license:update - CI: license-headers job in checks.yml, DCO check workflow - CONTRIBUTING.md: DCO sign-off requirement and license header docs - Cargo.toml: license changed to Apache-2.0, repository URL updated - pyproject.toml: license field added Closes NVIDIA/OpenShell#58
…enShell#94) * set up docs * rm nv sphinx theme version * rm v* trigger * incorporate feedback with cursor * minor updates * doc docs * more small tweaks * clean contributing --------- Co-authored-by: Drew Newberry <anewberry@nvidia.com>
…IDIA/OpenShell#102) Closes NVIDIA/OpenShell#101 Add pytest-xdist for parallel e2e test execution with configurable concurrency. Default to 5 workers; override via E2E_PARALLEL env var (accepts a number or 'auto' for CPU-count matching). Make session-scoped mock inference route fixtures worker-safe by incorporating the xdist worker_id into route names and routing hints. Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
…move implicit catch-all (NVIDIA/OpenShell#146) Remove multi-route CRUD system and replace with single managed cluster route (inference.local). Key changes: - Remove inference route CRUD RPCs and CLI commands - Remove InspectForInference OPA action; policy is binary allow/deny - Introduce AuthHeader enum and InferenceProviderProfile in navigator-core - Router is now provider-agnostic: auth style carried on ResolvedRoute - Replace InferenceRouteSpec with ClusterInferenceConfig (2 fields vs 8) - Rename proto: routing_hint->name, SandboxResolvedRoute->ResolvedRoute, GetSandboxInferenceBundle->GetInferenceBundle, drop sandbox_id param - Rename RouteConfig.route -> RouteConfig.name; use inference.local - Add 'nemoclaw cluster inference update' for partial config changes - Delete stale navigator.inference.v1.rs checked-in proto file - Update architecture docs, agent skills, and CLI reference Closes NVIDIA/OpenShell#133
…nfig to gateway paths (NVIDIA/OpenShell#220)
* feat(sandbox): add gpu sandbox scheduling support Allow sandbox creation to request GPU resources explicitly or infer them from GPU image names. This wires GPU intent through bootstrap, validates gateway support, and adds dedicated GPU E2E coverage for follow-up cluster testing.
…ell#291) Closes NVIDIA/OpenShell#273 Verify inference endpoints synchronously on the server during set/update, expose a --no-verify escape hatch in the CLI and Python helper, and return actionable failures when validation does not pass.
Remove the legacy Sphinx pipeline and make docs/ the single source of truth so the published site matches the repository layout.
…/OpenShell#919) - adds filterable label selectors on resources Closes NVIDIA/OpenShell#864 Signed-off-by: Derek Carr <decarr@redhat.com>
…/OpenShell#4357) * feat(server): add operator-only provider credential retrieval Authenticate operators exclusively through verified direct gateway mTLS. Export selected runtime credentials with freshness validation and all-or-nothing delivery. Coordinate refresh across callers and replicas, preserve rotated grants across RPC cancellation, and fence credential delivery against reconfiguration. Add generated bindings, security and regression tests, and operator documentation. Signed-off-by: Seth Jennings <sjenning@redhat.com> * feat(sdk): add curated operator credential retrieval Expose provider credential clients in Python and TypeScript with explicit workspace and key selection, freshness options, optional expiration, and preserved gateway errors. Reuse existing mTLS transports without introducing retries or secret caching. Add generated-wire and error-mapping tests, SDK package validation, and operator usage documentation. Signed-off-by: Seth Jennings <sjenning@redhat.com> * fix(server): honor recovery state for queued automatic refreshes Signed-off-by: Seth Jennings <sjenning@redhat.com> --------- Signed-off-by: Seth Jennings <sjenning@redhat.com>
…nShell#4436) * feat(python): expose sandbox status conditions in the SDK Add SandboxConditionRef and surface status.conditions on SandboxStatusRef and SandboxRef. Callers need condition reasons and transition times to time terminal transitions and explain failures that happen before the main process starts, without reading the raw proto. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * test(python): use real gateway condition values in conditions fixtures Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> --------- Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
…4367) * fix(python): close client when Sandbox entry fails Signed-off-by: Eric Curtin <eric.curtin@docker.com> * test(python): share client patch helper Signed-off-by: Eric Curtin <eric.curtin@docker.com> --------- Signed-off-by: Eric Curtin <eric.curtin@docker.com>
…K module (NVIDIA/OpenShell#4439) * fix(python): let proto stub generation skip .pyi for older protoc Add a --no-pyi option to generate_python_proto.py for toolchains whose protoc predates --pyi_out, such as Fedora's grpcio-tools 1.48, and list python3-googleapis-common-protos and python3-httpx in the spec's Recommends and dist-info Requires-Dist, which the SDK needs to import. Default behavior is unchanged. Contributes to NVIDIA/OpenShell#3510. Signed-off-by: politerealism <burdcat17@gmail.com> * fix(rpm): install every SDK module and record the license accounting The python3-openshell %install copied only __init__.py, sandbox.py and _proto/, but the package imports .errors, .mutations and .providers and ships a py.typed marker, so an installed RPM could not import openshell. Install the missing files and add a %check that fails if any relative import in the installed package has no matching installed module. Importing the package needs grpcio, which the build environment lacks, so the check is static. Set License to the minimal SPDX expression covering every crate linked into the shipped binaries, keep the pure-Python subpackage at Apache-2.0, and note how the non-crate content is covered. Signed-off-by: politerealism <burdcat17@gmail.com> --------- Signed-off-by: politerealism <burdcat17@gmail.com>
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
…ll#4760) Closes NVIDIA/OpenShell#4759 Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
* feat(sandbox): add suspend and resume lifecycle Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(sandbox): preserve V8 state across failed resume launches Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(sandbox): handle suspend and resume recovery races Signed-off-by: Drew Newberry <anewberry@nvidia.com> * refactor(example): move V8 suspend support to a dependent change Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(sandbox): align suspend tests with current supervisor APIs Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(sandbox): isolate suspend requests and resume compensation Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(supervisor): allow restoration to wait until agent admission Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Drew Newberry <anewberry@nvidia.com>
…penShell#4969) * fix(deps): update setuptools to 84.1.0 Update the locked setuptools package and the build backend minimum to the patched release for GHSA-xmj3-9gf7-h52w. Signed-off-by: Adrien Langou <alangou@nvidia.com> * fix(deps): update vitest to 4.1.12 Update Vitest and V8 coverage together to resolve GHSA-qxg6-89mg-rqv8 and GHSA-xp26-3454-mjgp. Preserve unrelated locked dependencies. Signed-off-by: Adrien Langou <alangou@nvidia.com> --------- Signed-off-by: Adrien Langou <alangou@nvidia.com>
… breaks it (NVIDIA/OpenShell#5018) Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
…5040) Generate and package gRPC type stubs with concrete request and response types. Verify consumer typing, native async transport behavior, and wheel contents while preserving the existing synchronous APIs. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
…/OpenShell#4918) * feat(sdk): route Python SDK exec to the sandbox owner replica Ask GetSandbox for the owner replica, cache it for 30 seconds, and send it as x-openshell-replica on ExecSandbox. Retry once without the hint when the per-replica route has no healthy endpoint, forgetting the cached owner. Part of NVIDIA/OpenShell#4223 Signed-off-by: divesh <dgude@nvidia.com> * refactor(sdk): share the Python GetSandbox request for owner lookup Reuse get's request for the owner lookup, point the routing headers at their documented contract, and accept only string owner metadata. Part of NVIDIA/OpenShell#4223 Signed-off-by: divesh <dgude@nvidia.com> --------- Signed-off-by: divesh <dgude@nvidia.com>
This comment was marked as outdated.
This comment was marked as outdated.
|
The reason I enforced the DCO is because the CNCF requires it. Let's sync. |
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Filtered from NVIDIA/OpenShell at v0.1.7 (5d25211b25a8fc887e8d5c606d57d6ab22f1d76c) with git filter-repo --paths-from-file. The tip tree 5619e0c equals the SDK paths at that commit mapped to src/, tests/, and scripts/. 76 commits from 24 authors, with bare #N references rewritten to NVIDIA/OpenShell#N. Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
82b8e14 to
dd0bf72
Compare
|
Warning Merge this PR with "Create a merge commit". Do not use "Rebase and merge" or "Squash and merge". This PR carries the Python SDK history as a separate line: 76 commits from 24 authors, filtered from NVIDIA/OpenShell at v0.1.7. The import merge (
With a merge commit, After this merges, retarget #3 to |
|
@SDAChess After our discussion I added |
Summary
Imports the history of the Python SDK from NVIDIA/OpenShell into this repository. This is the first step of the move, following the Go SDK's import (ai-openshell/sdk-go#3) and the migration notes in NVIDIA/OpenShell discussion 5273. Tracking: NVIDIA/OpenShell#5287.
v0.1.7release tag,5d25211b2. Freezing at a tag means this repository starts from exactly the code theopenshell==0.1.7wheel was built from. sdk-go froze at a latermaincommit, and the Go notes list that as a lesson. No Python SDK file has changed in core sincev0.1.7.#Nreferences in commit messages now readNVIDIA/OpenShell#N, so they keep pointing at core.What was extracted
The Python SDK isn't a single directory in core, so the history was filtered with
git filter-repo --paths-from-fileinstead of--subdirectory-filter. The rules map core paths to this layout:python/openshell/src/openshell/python/openshell/*_test.py,python/*_test.py(SDK tests)tests/python/navigator/(the package before its rename)src/navigator/, history onlypyproject.toml(the SDK's package definition in core)pyproject.tomltasks/scripts/generate_python_proto.py,verify-python-wheel.pyscripts/Some tests exercise core packaging, release tooling or gateway config rather than the SDK. They stay in core:
release_formula,rpm_gateway_config_migration,gateway_config_fixture,gateway_schema_v2_*andrelease_tooling.Keeping
python/navigator/meansgit log --followand blame reach back to the first commit.The extraction scripts and rules (
hack/migration/) come in the follow-up, #3, as in sdk-go.Verification
verify-tree.shapplies the same rules to a one-commit copy of core's tree at the tag and compares tree hashes:The merge's second parent has tree
5619e0c4…; the merge commit adds only the existingREADME.md.This branch only imports history. It doesn't build on its own yet:
pyproject.tomlstill points atpython/, and proto generation still expects core'sproto/. #3 makes it standalone.DCO
34 of the 76 imported commits have no
Signed-off-bytrailer. They are core squash merges from January to August 2026 whose messages didn't carry the trailer. The required DCO check will flag them. Adding sign-offs now would mean signing on other people's behalf, so the history is imported unchanged. The check needs a maintainer override for this PR only; every commit written in this repository is signed off.How to merge
Merge with a merge commit, not squash or rebase. The merge commit is what carries the 76 imported commits; a squash would drop the history this PR exists for.
Relation to #1
This keeps the move itself non-breaking: same package, same
openshell._protomodules, same protobuf runtime. The Protobuf-Py switch in #1 can rebase onto the result as a separate, announced change.