Drop `no_driver_configured_message` and `compiled_in_builtin_names` in
favor of an inline static error. The dynamic list of supported drivers
belonged in a discoverable channel (a future `openshell-gateway info`
subcommand, tracked separately) rather than in an error message the
operator can only reach by triggering a failure.
Adjust `configured_compute_driver_empty_points_at_extensions_when_no_builtins`
to assert only the socket_path escape hatch, which the single static
message still surfaces for the extension-only build shape.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Operators running packaged gateway binaries (Docker images, Helm
charts, Homebrew) can't act on "rebuild openshell-server with
--features driver-<name>" — that advice is aimed at contributors
editing the source tree. Replace with a shorter operator-oriented
phrase that says what's wrong without prescribing a fix outside the
operator's reach.
Update `configured_compute_driver_matches_compiled_features` to
assert the new phrase and the driver name rather than the Cargo flag.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Gate driver-specific fields on `ServerStartupConfig` and
`DriverStartupContext` (and the `GuestTlsPaths` type they reference)
with `#[cfg]` directly instead of double-negated
`#[cfg_attr(not(any(...)), allow(dead_code))]`. The direct form makes
the struct definition accurately reflect what exists per build and
removes the drift risk where a new driver reading one of these fields
would silently re-suppress the warning if the exclusion predicate went
stale.
Every construction site (`run_server`, `prepare_server_config`,
`test_context_with_endpoint_overrides`, `test_driver_startup`) is
updated to gate the matching field assignments with the same predicate,
so the gates cannot drift apart.
Split `apply_runtime_defaults` into a pure-side-effect `Result<()>`
plus a separate `resolved_local_tls` accessor so the call site no
longer needs a two-branch `#[cfg]` split around `let _ = ...` just
to keep the return value from being unused under
`--no-default-features`. The two are now clean concerns: one mutates
args, one reads TLS paths off disk. Callers invoke whichever they
need.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Replace the `#[cfg(not(all(feature = "driver-*")))]`-gated
`ConfiguredComputeDriver::Builtin(kind)` catch-all in the dispatch
match with a plain `_ => unreachable!(...)` guarded by
`#[allow(unreachable_patterns)]`. Adding or removing a driver feature
no longer requires updating this arm, and there is no feature list to
drift out of sync with the enum.
The arm remains genuinely unreachable at runtime: config validation in
`configured_compute_driver` rejects builtins whose feature was compiled
out before dispatch runs. The trade-off is that the panic message no
longer names the specific driver — acceptable because the actionable
error operators see is the validation-layer message, which does name
the driver and the Cargo feature that re-enables it.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Break the flat 12-step run list into seven hidden sibling tasks — one
positive control, four single-driver builds, one mixed docker+podman
build, and one zero-driver build (which also runs the extension-only
unit tests). The top-level task becomes a `depends` aggregator over
them.
Each sub-task runs the exact same cargo build + verify-drivers-compiled-
out.sh pair as before, so total work and CI wall clock are unchanged.
The split lets a developer re-run a single leg in isolation
(`mise run rust:verify:drivers-off:podman`) after a failure, and mise's
per-task log prefix makes a failing leg trivially identifiable in CI
output. The parent task keeps the same name so
`.github/workflows/branch-checks.yml` needs no change.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Cover both directions of the compile-time driver gate in one test: a
compiled-in driver must resolve to its Builtin variant; a compiled-out
driver must fail with a message naming the Cargo feature that re-enables
it. An exhaustive match on `ComputeDriverKind` inside the test makes a
new variant a compile error until it's given a feature gate. Derive
`Hash` on `ComputeDriverKind` so it can key the driver-features map.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
- README.md gains a "Choosing Which Compute Drivers Are Compiled In"
section covering the four driver features, the extension-only
build shape, and the startup-time compiled-out rejection.
- architecture/compute-runtimes.md gains a "Compile-Time Driver
Selection" section covering the four driver features, the
extension-only zero-driver build, the startup-time missing-driver
error, filtered auto-detection, and the always-available extension
driver path.
- docs/reference/gateway-config.mdx gains a matching reference
section under "Choosing Which Drivers Are Compiled In" with a
per-feature table, single-driver / mixed / extension-only build
examples, and the startup-time rejection contract.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Adds verify-drivers-compiled-out.sh, a mise task that exercises the
per-driver Cargo features, and a matching step in the branch-checks
workflow.
The script mirrors verify-telemetry-compiled-out.sh: a table of stable
driver-exclusive marker strings pulled from each driver crate, with
present/absent/only modes. The 'only' mode asserts presence for a
selected driver list AND absence for every other driver in one sweep,
so a single script invocation covers both directions per build.
The mise task exercises: default (all four drivers present), each
single-driver build, one mixed feature set (docker+podman), and a
zero-driver extension-only build that must link cleanly and must
contain none of the four driver markers. After the zero-driver build
the task also runs `cargo test -p openshell-server --lib
--no-default-features` so the #[cfg]-gated extension-only unit tests
actually execute in CI rather than being silently skipped by the
default-features test job.
The telemetry-off task uses a mixed --features driver-...,driver-...
gateway build so the telemetry-marker absence check still exercises a
representative in-tree-driver build path.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Move the availability check out of the build_compute_runtime match arm
into resolve_configured_compute_driver so it fires alongside the other
config-time driver validation. Configuring a driver that this gateway
was not built with now fails at startup with a message naming both the
driver and the Cargo flag that re-enables it.
Auto-detection (empty --drivers) filters through filtered_detect_driver:
if openshell-core's environment probe picks a driver that was compiled
out, we return None and let the caller produce the "no suitable driver"
error rather than silently starting a different backend. In an
extension-only build (zero built-ins compiled in) the message adapts to
point operators at the [openshell.drivers.<name>].socket_path path
instead of listing built-in names that don't exist in this binary;
otherwise it lists only the built-ins actually linked and mentions
that extension drivers are also valid.
Adds unit tests that exercise the compiled-out rejection path and the
extension-only empty-drivers message. The latter is #[cfg]-gated so it
only compiles when no driver features are on.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
Introduce driver-kubernetes, driver-docker, driver-podman, and driver-vm
cargo features on openshell-server, all enabled by default. Building with
--no-default-features and selecting a subset produces a gateway that only
carries the requested drivers.
The three container-driver crates flip to optional dependencies. The VM
driver runs out-of-process already, so driver-vm gates the in-server
launcher and match arm rather than a Cargo dep.
Feature gates cover:
- driver crate imports and re-exports in compute/mod.rs
- ComputeRuntime::new_docker / new_kubernetes / new_podman
- Docker's ShutdownCleanup / StartupResume trait impls
- the compute::vm module and VmComputeConfig re-export
- driver-specific *_config_from_context and apply_*_runtime_defaults
- the K8s ServiceAccount authenticator bootstrap in ServerState
- the auth::k8s_sa module and state field
- authenticator-chain construction in multiplex.rs
- SupervisorReadiness impl for SupervisorSessionRegistry
- driver-specific unit tests in cli.rs, lib.rs, driver_config.rs
A zero-driver build is a supported extension-only gateway shape: it
carries no in-tree driver code and speaks compute_driver.proto over
a Unix socket to an out-of-tree driver named in compute_drivers with
a matching [openshell.drivers.<name>].socket_path. The runtime gate
in resolve_configured_compute_driver (see the follow-up commit)
still rejects a built-in that was compiled out with the "rebuild
with --features driver-X" message, so silent misconfiguration is
impossible.
Default builds are byte-equivalent to before. Each of the four
single-driver builds compiles cleanly under -D warnings, as does the
docker + podman combo and the zero-driver extension-only build.
Signed-off-by: Dimitar Mirchev <28221091+dimityrmirchev@users.noreply.github.com>
The ListSandboxesRequest proto gained workspace and all_workspaces
fields in the workspace feature but the governance interceptor
example was not updated, causing a build failure.
Set all_workspaces to true so the interceptor's policy reload
covers sandboxes across all workspaces.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Pavel Anni <panni@redhat.com>
* fix(sandbox): skip read-only mounts during recursive chown of /sandbox
The sandbox supervisor crashed with EROFS when the recursive chown of
/sandbox encountered read-only submounts. This is common in gVisor-based
Kubernetes deployments where read-only volume mounts are the only way to
enforce per-directory immutability (Landlock is unavailable under gVisor).
The fix adds two guards to the ownership walk:
1. Mount-boundary detection via st_dev comparison — paths on a different
filesystem than /sandbox are skipped entirely, avoiding the chown
call on nested read-only mounts.
2. EROFS tolerance — if chown still returns EROFS (e.g. the root mount
itself is read-only), the error is logged at debug level and startup
continues.
Symlink skipping (already present) is preserved and extracted into the
recursive walker for consistency.
Manually verified on a kind cluster with a read-only PVC mounted at
/sandbox/readonly-data: the pod starts successfully, writable paths are
owned by the sandbox user, and the read-only mount retains root
ownership. Kubernetes e2e test coverage is a separate follow-up.
Closes#2294
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* refactor(sandbox): consolidate symlink check into chown_recursive
Move the per-child symlink guard from the directory iteration loop into
the top of chown_recursive, reusing the symlink_metadata call that is
already performed there. This centralizes all three skip guards
(symlink, mount boundary, EROFS) in one place and eliminates a redundant
symlink_metadata call per child entry.
Suggested-by: elezar
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(sandbox): recurse into children after EROFS and fix doc comment
When chown returns EROFS on a directory, the code previously returned
early without recursing into children. This skipped writable submounts
nested under a read-only directory on the same device (e.g., a writable
emptyDir inside a read-only ConfigMap mount sharing the same st_dev).
Now the EROFS case falls through to the directory recursion so that
writable children are still chowned.
Also fixes the doc comment which said EROFS errors are "logged as
warnings" when they are actually logged at debug level.
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(sandbox): allow similar_names clippy lint in chown test
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(sandbox): remove cross-device skip and inject chown for EROFS tests
Remove the unconditional st_dev boundary check that prevented chown on
writable PVC mounts with a different device ID. Rely solely on EROFS
errors to skip read-only paths while continuing traversal into children
and siblings.
Inject the chown operation into chown_recursive to enable unit testing
the EROFS recovery path without requiring root or mount privileges.
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* style(sandbox): apply rustfmt to chown test closures
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(sandbox): stop descending into EROFS subtrees to prevent TOCTOU race
After chown returns EROFS, return immediately instead of recursing into
the read-only directory's children. Descending into a mount we cannot
own creates a TOCTOU window where an attacker could swap a child for a
symlink between symlink_metadata and chown (CWE-367/CWE-59). Siblings
of the EROFS path are still visited by the caller's loop.
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(sandbox): hard-error on root EROFS, log read_dir failures, eliminate double stat
Chown root directly in chown_sandbox_home so EROFS on /sandbox itself
is a hard startup error rather than being silently swallowed. Extract
chown_children to log read_dir failures instead of silently skipping
them. This also eliminates the redundant symlink_metadata syscall on
the root path.
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
---------
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
* refactor(cli): extract shared helpers into commands/common module
Move shared formatting, parsing, display, and settings helpers from
run.rs into a new commands/common.rs module. run.rs re-exports all
public items so no callers change. This establishes the commands/
directory structure for the incremental run.rs split (#2304).
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* refactor(cli): narrow visibility and fix review issues in shared helpers extraction
- Remove orphan doc comment left on sandbox_list after print_yaml_line extraction
- Change `pub mod commands` to `pub(crate) mod commands` in lib.rs
- Move 6 unnecessarily pub-exported items to private use imports in run.rs
- Restore stripped rationale comment in parse_cli_setting_value
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* style(cli): fix rustfmt import line wrapping in run.rs
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(cli): restore parse_secret_material_env_pairs public re-export and narrow includes_policy visibility
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
---------
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
* fix(driver-k8s): harden list path and tighten label selectors
- Add warning log to list_sandboxes() filter_map so skipped objects
are visible in logs instead of silently dropped.
- Add LABEL_SANDBOX_ID existence requirement to both list and watch
label selectors so managed-but-non-sandbox resources (e.g. warm pool
pods without sandbox identity) never enter either code path.
Signed-off-by: Roland Huß <rhuss@redhat.com>
* fix(driver-k8s): add watch cancellation and selector tests
Address review feedback on PR #2223:
- Extract shared label selector into openshell_sandbox_label_selector()
helper in openshell-core so both list and watch paths use a single
tested definition.
- Add tx.closed() branch to the watch_sandboxes tokio::select! loop so
the producer task exits when the receiver is dropped, preventing
leaked Kubernetes watch connections.
- Add unit tests for the label selector string and for the cancellation
behavior.
Signed-off-by: Roland Huß <rhuss@redhat.com>
* fix(driver-k8s): remove tautological and non-functional tests
Remove three tests that don't provide meaningful regression coverage:
- openshell_sandbox_label_selector_contains_managed_by_and_sandbox_id:
mirrors the implementation with hardcoded strings; any constant change
requires updating the test string in lockstep.
- label_selector_used_by_list_and_watch_matches_shared_helper:
weaker duplicate of the above, checking substrings instead of equality.
- watch_producer_exits_when_receiver_is_dropped:
tests tokio channel semantics (tx.closed() fires when rx is dropped),
not watch_sandboxes(). No K8s API mock infrastructure exists to test
the actual watch path.
Signed-off-by: Roland Huß <rhuss@redhat.com>
* style(driver-k8s): fix rustfmt formatting
Signed-off-by: Roland Huß <rhuss@redhat.com>
* fix(driver-k8s): use explicit unit pattern for clippy 1.95
Replace `_ = tx.closed()` with `() = tx.closed()` to satisfy
the `ignored_unit_patterns` clippy lint introduced in Rust 1.95.
Assisted-By: 🤖 Claude Code
Signed-off-by: Roland Huß <rhuss@redhat.com>
---------
Signed-off-by: Roland Huß <rhuss@redhat.com>
Remove temporary registry layer data before formatting the merged rootfs, preserve formatter execution failures over missing fallbacks, and validate required ext4 tools in rust-vm CI.
Closes#2423
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
The OpenClaw community sandbox was removed from NVIDIA/OpenShell-Community
in PR #73 (May 16, 2026). The Docker Compose tutorial and docker-compose.yml
comment still referenced the stale --from openclaw command and GHCR image.
Replace the broken OpenClaw tab with a redirect to the NemoClaw Quickstart,
which is the supported path per docs/about/supported-agents.mdx. Remove the
stale pre-pull command for the removed image.
Fixes#2404
Signed-off-by: Matias Schimuneck <schimuneck.matias@gmail.com>
The scripts/bin/openshell wrapper hardcoded the binary path to
$PROJECT_ROOT/target/debug/openshell. When CARGO_TARGET_DIR is set
(e.g. via .bashrc or mise), cargo places the binary elsewhere and the
wrapper fails with 'No such file or directory'.
Use ${CARGO_TARGET_DIR:-$PROJECT_ROOT/target} so the wrapper finds
the binary regardless of where the build artifacts live.
Signed-off-by: Jesse Jaggars <jjaggars@jjaggars-kubevirt.rht.csb>
Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
* fix(proxy): include OPA deny reason in CONNECT 403 response
When a CONNECT request was denied by OPA policy, the 403 response
used a generic "not permitted by policy" message for both "endpoint
not in policy" and "endpoint matched but binary didn't match." Users
had no way to distinguish the two without reading supervisor logs.
The OPA policy already computes a detailed deny_reason (e.g.,
"binary '/usr/bin/node' not allowed in policy 'X'") but the proxy
was not including it in the HTTP response.
Now the CONNECT deny response includes a "reason" field with the
OPA deny reason when available. When the reason is empty, the field
is omitted for backward compatibility.
Fixes#2355
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
* docs(observability): document optional reason field in CONNECT 403 response
The proxy now includes a reason field in the JSON body of denied
CONNECT responses when the policy engine provides a specific denial
cause. Update the Proxy Error Responses section to show the field
and describe when it is present vs omitted.
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
---------
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
The branches API endpoint cannot handle ref names with slashes (e.g.
pull-request/2223). When the API returns a 404, gh api writes the error
JSON to stdout before exiting non-zero, so the || fallback never fires.
This causes mirror_sha to contain raw JSON like {"message":"Branch not
found",...} and the comment displays `{"messa` as the SHA.
Switch to the git/ref/heads/ endpoint which handles slashes natively,
and add a regex guard to reset mirror_sha to empty when it does not
look like a valid 40-char hex SHA.
Signed-off-by: Roland Huß <rhuss@redhat.com>
* fix(driver-podman): resolve Podman socket via auto-detection
Align Podman socket selection with the existing Docker model: explicit
config wins, otherwise probe openshell-core for a responsive socket.
This also fixes the original HOME-unset panic, since resolution no
longer hardcodes a per-OS default path.
- add detect_podman_socket() in openshell-core, mirroring
detect_docker_socket
- PodmanComputeConfig.socket_path is now Option<PathBuf>, no default
- remove default_socket_path() (podman driver) and podman_socket_path()
(vm driver), both replaced by the shared detector
- update server env override and CLI for the new Option type
- add tests: responsive-candidate detection in openshell-core, and
config-error (not panic) when no socket is configured or reachable
* fix(driver-podman): address review feedback on socket resolution
Extract socket resolution into resolve_socket_path, taking the
detector as a parameter so tests do not depend on real env vars or
the host's actual Podman state. Replace the flaky env-mutating test
with three deterministic cases: explicit wins, detected is used when
absent, and neither source errors.
Fix the socket_path doc comment to describe it from a config user's
point of view, matching DockerComputeConfig's docstring. Drop a
comment that only made sense next to the Docker driver code.
Update the Podman README and gateway docs: they described a fixed
per-OS default path that no longer exists, replace with the actual
probe-then-fail behavior.
* docs(driver-podman): simplify socket default description
Previous wording was self-contradictory (says auto-detect on unset,
then lists the same var as a probed candidate) and omitted the Linux
/run/user/uid/podman/podman.sock candidate.
* perf(build): share sccache across worktrees
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
* fix(build): make sccache directory overridable
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
* fix(build): support pinned mise version
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
---------
Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
* feat(workspace): implement workspace model (Phase 1 of RFC 0011)
Implements workspace and membership model providing hard isolation
boundaries for multi-player OpenShell deployments.
Workspace CRUD with Kubernetes-style Terminating phase for graceful
deletion. All resources scoped by workspace via ObjectMeta. Membership
RPCs for workspace access control. Persistence migration shifts name
uniqueness to (object_type, workspace, name). Provider profiles support
platform and workspace scoping. Service routing uses workspace-prefixed
DNS labels. Inference routes renamed and workspace-scoped with
DeleteInferenceRoute RPC. Python SDK with WorkspaceClient, two-method
list pattern (workspace-scoped and for_all_workspaces), and workspace
parameter on all methods. CLI workspace flags, TUI workspace cycling.
K8s driver filters unmanaged CRs and uses delete preconditions. Podman
driver uses immutable container IDs. Label serialization fixed across
all put_if call sites.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(cli): delegate sandbox upload command to existing upload function
The standalone `sandbox upload` command reimplemented upload logic
inline with two bugs: it used `Path::exists()` which follows symlinks
(rejecting dangling symlinks), and it ran git-aware filtering on
symlink sources. The `run::sandbox_upload()` function already handles
both cases correctly via `sandbox_upload_plan()`. Replace the inline
logic with a call to the existing function.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(e2e): shorten sandbox names and fix test compatibility
Shorten the sandbox name in initial_sparse_policy_is_acknowledged_as_loaded
from 'e2e-2159-sparse-enrich' (22 chars) to 'e2e-sparse-enrich' (17 chars)
to comply with MAX_ROUTABLE_NAME_LEN (19 chars).
Also capture stderr in create_keep_with_args so future sandbox creation
failures include the actual CLI error instead of reporting empty output.
Signed-off-by: Derek Carr <decarr@redhat.com>
* test(workspace): add test coverage for workspace CRUD and persistence isolation
Add unit tests for workspace create happy path, get round-trip, get
not-found, get empty-name rejection, already-exists error, and
resolve_workspace not-found. Add persistence test proving cross-workspace
name uniqueness (same name in different workspaces produces separate
records). Add workspace name max-length boundary tests. Fix e2e harness
to include stderr in name-parse-failure error path. Align Python e2e
test_workspace_crud with try/finally pattern. Document provider profile
catalog workspace scoping gap in RFC 0011.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(examples): update examples for workspace model compatibility
Shorten sandbox names in demo scripts to fit the 19-character
MAX_ROUTABLE_NAME_LEN limit: policy-demo prefix to pd-, multi-agent
notepad derives a short SANDBOX_TAG from the run ID, governance
interceptor uses gs-PID-RANDOM. Update vscode-remote-sandbox.md SSH
host aliases from openshell-{name} to openshell-{name}.{workspace}
format.
Signed-off-by: Derek Carr <decarr@redhat.com>
* feat(sdk): add workspace-scoped client and workspace CRUD
Add WorkspaceScopedClient modeled after kube::Api::namespaced — captures
workspace once and injects it into every sandbox request. Add workspace
CRUD methods (create, get, list, delete) and list_sandboxes_all_workspaces
on OpenShellClient. Extend SandboxRef with workspace field and add
WorkspaceRef type. Include mock tests for all new operations.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(lint): resolve clippy warnings in workspace test assertions
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(docs): convert indented code blocks to fenced in RFC 0011
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(lint): resolve clippy warnings and apply cargo fmt across workspace
Auto-format with cargo fmt and fix clippy warnings exposed by the
reformat: unnecessary qualifications, map_unwrap_or, identical match
arms, unused variable prefix, dead code annotations, and let-unit-value
in e2e harness.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(workspace): address workspace scoping issues from review
- Add workspace field to settings JSON output (CLI)
- Skip Podman containers missing workspace label instead of defaulting
to empty string, matching K8s driver behavior
- Add resource_version to list_by_scope SELECT in both SQLite and
Postgres backends, with regression test
- Gate PolicyLocalContext proposal/lookup routes on workspace readiness,
returning 503 when workspace is not yet discovered
- Block sandbox and provider creation in TUI all-workspaces mode
- Clear workspace vectors in TUI reset_sandbox_state
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(workspace): make provider profile catalog workspace-aware
Thread workspace through snapshot_catalog so the
EffectiveProviderProfileCatalog enforces workspace boundaries on both
read and write paths. UserProviderProfileSource now loads platform-scoped
profiles (workspace "") plus the target workspace's profiles, preventing
cross-workspace duplicate profile ID collisions that previously caused
global catalog failures.
Update RFC 0011 to reflect catalog scoping is implemented in Phase 1
rather than deferred to future work.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(persistence): include workspace column in atomic policy revision INSERT
put_policy_revision_atomic omitted the workspace column from the INSERT
into the objects table in both SQLite and Postgres backends, causing
atomically-written policy revisions to lose their workspace association.
Add workspace field to AtomicPolicyRevisionWrite and thread it through
both backend INSERT statements, matching the non-atomic put_policy_revision
path which already included it.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(proxy): skip ancestor walk when socket owner is the entrypoint
collect_ancestor_identities walked the entire process tree above the
entrypoint when the connecting process was the entrypoint itself,
SHA256-hashing every ancestor binary (IDE, shell, container runtime).
On dev machines with large binaries in the ancestor chain this exceeded
the 30-second test timeout. When start_pid == stop_pid there are no
intermediate ancestors to verify, so return an empty list immediately.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(workspace): make provider profile catalog scope-aware
Allow the same profile ID at platform and workspace scopes by
introducing layered catalog entries where workspace profiles shadow
platform profiles. Add source and scope fields to the ProviderProfile
proto and CLI output. Migrate List/Get handlers to the catalog,
fixing divergence with runtime profile resolution.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(e2e): align podman e2e labels with centralized driver constants
The podman driver moved its container labels to the centralized
openshell.ai/ prefix, but the e2e test harness and cleanup script
still referenced the old openshell.sandbox-* keys, causing the
local_driver_token_restart test to fail on container lookup.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(e2e): align python profile isolation test with scope-aware catalog
Platform profiles are now visible in workspace listings as fallbacks
per the layered catalog design. Update the assertion to match.
Signed-off-by: Derek Carr <decarr@redhat.com>
* fix(workspace): honor profile_workspace in runtime profile resolution
Runtime profile lookups now consult provider.profile_workspace via
get_type_profile_for_scope. Providers created with --global-profile
(profile_workspace="") resolve to the platform profile even when a
workspace profile shadows the same ID. All 6 runtime call sites
updated; type-only call sites remain scope-agnostic.
Signed-off-by: Derek Carr <decarr@redhat.com>
---------
Signed-off-by: Derek Carr <decarr@redhat.com>
Closes#2112
The host `cargo zigbuild` for `*-unknown-linux-musl` opens ~333 `.rlib`
files at once during the static link, exceeding macOS's default soft
limit of 256 and failing with `ProcessFdQuotaExceeded`. This blocked the
docker/podman `mise run gateway` paths for macOS contributors; only the
VM driver path guarded against it.
Extract the VM path's `ensure_build_nofile_limit` guard into a shared
`tasks/scripts/build-env.sh` and call it from the host-staging chokepoint
(`stage-prebuilt-binaries.sh`), fixing docker, podman, and all
docker:*/multiarch host cross-compiles at once. De-duplicate the VM
script to source the shared helper. The guard is a no-op on Linux and
when cargo-zigbuild is absent, so CI and Linux dev are unaffected. The
limit is read from `OPENSHELL_BUILD_NOFILE_LIMIT` (default 8192),
honoring the legacy `OPENSHELL_VM_BUILD_NOFILE_LIMIT` for back-compat.
Also correct the stale comment in gateway-docker.sh (the cross-compile
runs on the host, not inside Linux containers) and document the guard in
architecture/build.md. Adds tasks/scripts/test-build-env.sh, wired into
`mise run test` via `test:build-env`.
Signed-off-by: Jim Meyer <jim@meyer4hire.com>
* fix(providers): allow git clone/fetch via default GitHub provider
The github.com:443 git-transport endpoint used the read-only access
preset, which expands to GET/HEAD/OPTIONS only. Git smart HTTP requires
a POST to */git-upload-pack for clone and fetch, so the L7 proxy denied
those operations and `gh repo clone` / `git clone https://...` failed.
Replace the preset with explicit rules that permit the read-only methods
plus POST */git-upload-pack, so clone/fetch work while push
(git-receive-pack) stays blocked. Enabling push still requires an
explicit policy proposal.
Why allowing this POST is still read-only: in git's smart HTTP protocol
POST is an RPC transport, not a write. A clone/fetch does GET
*/info/refs (ref discovery) followed by POST */git-upload-pack, whose
body is only the client's want/have negotiation; the server responds
with a packfile and nothing on the server is modified (data flows
server -> client). The service names are from the server's perspective:
git-upload-pack = the server uploads a pack to the client (a read/
download), while git-receive-pack = the server receives a pack from the
client (the actual write/push). The new rule is scoped to
*/git-upload-pack only, so push (git-receive-pack) and arbitrary POSTs
to github.com remain denied.
Add a provider-profile regression test and a rego enforcement test
covering ref discovery, upload-pack (allowed), and receive-pack (denied).
Closes#1769
Signed-off-by: Russell Bryant <rbryant@redhat.com>
* test(providers): strengthen git-transport regression and add clone e2e
Pin the exact allowed rule set for the built-in github git-transport
endpoint in both the provider-profile and composed-policy tests, so a
broader or additional POST rule (e.g. POST **) that could enable push
via git-receive-pack fails the test instead of passing a substring
check. Add an e2e test that attaches the built-in github provider and
clones a public repo over HTTPS, exercising provider attachment,
effective-policy composition, TLS interception, and real git behavior.
Update the Providers V2 docs so the github.com git-transport endpoint
shows explicit clone/fetch rules instead of the stale read-only preset.
Refs #1769
Signed-off-by: Russell Bryant <rbryant@redhat.com>
* test(providers): isolate providers_v2 mutation in clone e2e
The clone e2e enables the gateway-global providers_v2_enabled setting.
Restore its exact prior value (or absence) captured via GetGatewayConfig
instead of unconditionally deleting it, and serialize the mutation
across xdist workers with an exclusive file lock on the run's shared
base temp dir, so a shared or pre-configured gateway is left untouched
and parallel workers cannot race the read-modify-restore.
Refs #1769
Signed-off-by: Russell Bryant <rbryant@redhat.com>
* test(providers): serialize providers_v2 mutation with a suite-wide guard
The clone e2e's per-fixture lock only coordinated fixtures that acquired
it; other xdist workers hit the same gateway without it and could
observe the transiently-enabled providers_v2_enabled global during their
own sandbox creation (CWE-362).
Add an autouse readers-writer guard in conftest: every test holds a
shared lock on the gateway config, and a test marked
exclusive_gateway_config holds an exclusive lock. Mark the clone test
exclusive so no other worker is mid-test while it enables and restores
the gateway-global setting. Exact prior-value restoration is retained.
Refs #1769
Signed-off-by: Russell Bryant <rbryant@redhat.com>
---------
Signed-off-by: Russell Bryant <rbryant@redhat.com>
* fix(gateway): honor tty flag for interactive exec
Pass the requested TTY mode through the interactive SSH relay.
Skip PTY allocation and resize forwarding when TTY is disabled,
and add regression coverage for both modes.
Signed-off-by: emonq <emonq@outlook.com>
* test(gateway): improve `test_sandbox_interactive_exec_honors_tty` to test streamed stdin and stdout/stderr
Signed-off-by: emonq <emonq@outlook.com>
---------
Signed-off-by: emonq <emonq@outlook.com>
- README: fix github-sandbox tutorial link missing get-started segment
- README: replace dead community-sandboxes doc link with the actual repo
- README: match supported host list to support-matrix.mdx
- architecture/README: list the missing google-vertex-ai-provider doc
- SECURITY.md: fix a mis-indented list item
- standardize on NVIDIA/OpenShell-Community casing for repo links
login-action and setup-buildx-action used a mutable version tag while
every other action in the repo is pinned to a commit SHA. Pin both,
and align login-action to the same v4 SHA already used in ci-image.yml.
Use the full resolved version in the trailing comment (v3.12.0) to
match the more common convention used elsewhere in .github/.
* docs: bump stated Rust MSRV from 1.88 to 1.90
Cargo.toml sets rust-version = "1.90" (rust-toolchain.toml pins
1.95.0), so building with the previously documented 1.88 fails
Cargo's MSRV check.
* docs: bump e2e/rust MSRV to 1.90
* fix: align remaining Rust version fields to 1.90
examples/governance-interceptor/Cargo.toml still had rust-version
1.88. Also bump e2e/rust's prost dependency to 0.14 to match the
workspace, since it was on 0.13 in an otherwise standalone crate.