Commit Graph
1237 Commits
Author SHA1 Message Date
Evan Lezar 5a6763c02d docs(triage): remove playbook whitespace
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-09-01 11:04:26 +02:00
Evan Lezar 07b736b7a4 docs(triage): clarify queue continuation order
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-09-01 11:03:40 +02:00
Evan Lezar c85f748b32 docs(triage): keep local state untracked
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-28 11:32:03 +02:00
Evan Lezar 8aa7615a6c docs(triage): surface dependabot prs early
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-28 11:31:11 +02:00
Evan Lezar 7e5ec78719 Add triage duty tooling
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-28 11:31:11 +02:00
Simon Scatton d1155aa700 fix(ci): normalize macOS binary dependencies (#3006)
Closes #2997

Signed-off-by: Simon Scatton <sscatton@nvidia.com>
v0.0.116
2026-08-28 08:27:17 +00:00
dependabot[bot] 7eed8da2a5 chore(deps): bump jdx/mise-action from 4.2.4 to 4.3.0 (#2970)
Bumps [jdx/mise-action](https://github.com/jdx/mise-action) from 4.2.4 to 4.3.0.
- [Release notes](https://github.com/jdx/mise-action/releases)
- [Changelog](https://github.com/jdx/mise-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jdx/mise-action/compare/7e36c90d9ab29c415a2384db3006f3ec8a8cc654...c2a87611a18de5b3828c5652fe268e992400cb5c)

---
updated-dependencies:
- dependency-name: jdx/mise-action
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 23:33:04 +00:00
krishicks f795a15992 test(network): avoid DNS socket bind race (#2996)
This changes the DNS resolver test fixture to retry until TCP and UDP can share
an ephemeral port.

It fixes an issue seen in CI where a port that was successfully assigned for
one protocol failed for the other.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-27 21:01:07 +00:00
krishicks 1e9ee47127 fix(server): suppress expected session errors on shutdown (#2994)
Ctrl-C closes gateway listeners while supervisor sessions are still current,
causing benign HTTP/2 broken pipes to be logged as warnings. Track gateway
shutdown so recognized transport closes are treated as expected.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-27 20:05:56 +00:00
krishicks 197b41371d fix(dev): harden local cluster and gateway startup (#2993)
* fix(helm): refresh kubeconfig for existing k3d clusters

Docker can recreate the k3d load balancer on a new API port. Start existing
clusters and prefer fresh k3d entries so create does not retain a stale
endpoint.

Signed-off-by: Kris Hicks <khicks@nvidia.com>

* fix(dev): conditionally enable local OTLP export

Probe port 4317 before adding OTLP configuration for the VM, Docker,
and Podman gateway tasks. Document the startup behavior and troubleshooting
for local collector availability.

Signed-off-by: Kris Hicks <khicks@nvidia.com>

---------

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-27 17:55:01 +00:00
krishicks f68867b869 feat(gateway): identify gateways in exported traces (#2647)
* feat(gateway): add installation name configuration

Add a first-class operator-assigned gateway name with TOML, CLI, environment,
and Helm configuration surfaces. Local gateways default to openshell, while
Helm defaults to the chart fullname; operators sharing a collector across
namespaces or clusters can set a globally distinct name.

Signed-off-by: Kris Hicks <khicks@nvidia.com>

* feat(gateway): identify gateways in exported traces

Attach the configured gateway installation name and compute driver to the
gateway OpenTelemetry resource so operators can filter traces from multiple
installations that share a collector. Forward the gateway name and OTLP
endpoint to managed external drivers so their distinct service resources carry
the same installation identity.

Keep service.name stable per process type, omit blank resource values, and
leave per-span operation names and request attributes unchanged.

Refs #2507

Signed-off-by: Kris Hicks <khicks@nvidia.com>

---------

Signed-off-by: Kris Hicks <khicks@nvidia.com>
v0.0.115
2026-08-27 17:27:34 +00:00
Simon Scatton 981606d2f8 ci: build release binaries with Nix (#2977)
* ci: build release binaries with Nix

Refs #1683

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: build VM artifacts with Nix

Refs #1683

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: build images from Nix artifacts

Refs #1683

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* fix(nix): prevent host header leakage

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: parallelize artifact builds

Refs #1683

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: build external driver test artifacts

Refs #1683

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* fix(nix): disable mold in musl shells

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: key Rust cache by Nix shell derivation

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: refactor end-to-end workflows

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: split platform binary workflows

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: remove obsolete native build workflows

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: replace disallowed mise action

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: fix refactored e2e lanes

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: check out local result action

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: cache mise installations

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: run docker builds on host runners

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci: disable unstable kubernetes e2e lanes

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* fix(ci): scope binary builds to cargo packages

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* fix(ci): address zizmor template injection findings

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* fix(ci): resolve remaining zizmor annotations

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

---------

Signed-off-by: Simon Scatton <sscatton@nvidia.com>
2026-08-27 17:25:06 +00:00
alangou 37072ee81c feat(build): publish OCI SBOM and provenance attestations (#2836)
* feat(build): embed auditable Rust dependency metadata

Signed-off-by: Adrien Langou <alangou@nvidia.com>

* feat(build): publish OCI SBOM and provenance attestations

Signed-off-by: Adrien Langou <alangou@nvidia.com>

---------

Signed-off-by: Adrien Langou <alangou@nvidia.com>
2026-08-27 14:53:33 +00:00
Evan Lezar ca61ee3744 feat(test-guest): add snap lifecycle reproduction harness (#2865)
* feat(test-guest): add snap lifecycle reproduction harness

Signed-off-by: Evan Lezar <elezar@nvidia.com>

* fix(test-guest): preserve cache seal executability

Signed-off-by: Evan Lezar <elezar@nvidia.com>

---------

Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-27 13:56:38 +00:00
dependabot[bot] 6e43a8bc99 chore(deps): bump google.golang.org/grpc in /sdk/go (#2985)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.81.1 to 1.82.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.81.1...v1.82.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.82.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 13:39:22 +00:00
dependabot[bot] 23351771ca chore(deps): bump quinn-proto from 0.11.14 to 0.11.17 (#2986)
Bumps [quinn-proto](https://github.com/quinn-rs/quinn) from 0.11.14 to 0.11.17.
- [Release notes](https://github.com/quinn-rs/quinn/releases)
- [Commits](https://github.com/quinn-rs/quinn/compare/quinn-proto-0.11.14...quinn-proto-0.11.17)

---
updated-dependencies:
- dependency-name: quinn-proto
  dependency-version: 0.11.17
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-27 13:33:36 +00:00
alangou 5f90c8579c ci(security): add informational security checks (#2930)
Signed-off-by: Adrien Langou <alangou@nvidia.com>
2026-08-27 13:32:16 +00:00
Evan Lezar 5544715a25 ci(stale): increase stale workflow processing budget (#2979)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-27 12:07:15 +00:00
Evan Lezar 9f88f8ff9b ci: remove rootless podman e2e lane (#2981)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-27 08:48:44 +00:00
bcd517bbe0 feat(driver-mxc): native Windows MXC compute driver + server wiring (#2721)
* feat(driver): add MXC compute driver for Windows isolation sessions

Introduces the openshell-driver-mxc crate implementing ComputeDriver
backed by Microsoft MXC isolation sessions (Windows only). Wires the
new driver into the server's build_compute_runtime dispatch and adds
the Mxc variant to ComputeDriverKind.

Also adds a local protobuf-src stub (tools/protobuf-src-local) to
unblock Windows builds that lack MSYS2/MinGW, and pins the zig
Windows x64 toolchain in mise.lock.

(cherry picked from commit 4f7012224efb18fbfeb47aa87e0cfd3f036f32f0)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* wip(mxc): checkpoint hung-agent work (recon, policy_map embed, A1 wiring, demo artifacts)

Safety checkpoint of uncommitted work from the background agent run that stalled mid-Step-7. Includes: mxc-driver-recon.md (Step 0.5), policy_map.rs (~876L embedded mapper), A1 policy-threading edits across driver.rs/policy.rs/mxc.rs/compute/mod.rs, and examples/ (demo.yaml + mxc-gateway.toml). Not yet verified to compile end-to-end; to be reorganized into the skill's Step 11 commit sequence.

(cherry picked from commit 38e42c03870be3d10e984a54f17a3b61122ff510)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* test(mxc): fix lifecycle and policy unit-test compile drift

- Bring futures::StreamExt into scope for the watch-stream `.next()` call in
  driver::lifecycle_tests so the negative policy proof test compiles.
- Bind a local `mapper` and drop the unused/deprecated NetworkBinary in the
  embedded-mapper network-policy rejection test.

Signed-off-by: Jamie King <jamiek@nvidia.com>
(cherry picked from commit 039b0baf98735ca672dae52be8d3af2417dc0c1a)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(mxc): downgrade missing sandbox_token to debug log

The gateway mints `sandbox_token` only when a sandbox-JWT issuer is
configured. There is no in-sandbox supervisor on MXC (supervisor-removal
design — D1/D4), so no component ever consumes the token; requiring it
on the driver side blocks the demo's `--disable-tls` smoke gateway with a
spurious `invalid_argument`. Log the absence and proceed instead.

Signed-off-by: Jamie King <jamiek@nvidia.com>
(cherry picked from commit cea209797d0edcb1d152251e748900b0a63cca62)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(mxc): keep sandbox Ready after a successful one-shot agent exec

monitor_exec demoted Ready->Error on exit 0 (reason ExecCompleted), so the positive demo (write hello.txt + exit) landed in Error phase. Keep Ready=True (reason AgentCompleted) on success; only non-zero exits go to ExecFailed. Tighten the positive lifecycle test to assert the terminal condition stays Ready=True/AgentCompleted. Verified live via gateway mock round-trip: phase now Provisioning->Ready with no demotion.

(cherry picked from commit 54ab030f03ca0f83d0050d8dd843b633651684ad)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* feat(mxc): add processContainer backend for default-deny enforcement

Add a backend selector to the MXC driver (isolation_session default | process_container). process_container drives a one-shot AppContainer that is genuinely default-deny: a write to any ungranted path is denied by the OS, unlike isolation_session which is grant-only and cannot deny. The lifecycle forks on the flag - isolation_session keeps provision/start/exec, process_container runs a single ephemeral container via run_oneshot.

Also: run-demo.ps1 gains -Backend and hardens the CLI register/create calls; docs corrected to state isolation_session does NOT deny out-of-policy writes and that the negative proof requires process_container.

Verified end-to-end on a real demo box (gateway -> CLI -> driver -> MXC): in-policy write succeeds, out-of-policy write denied (PermissionDenied), OVERALL: PASS.

(cherry picked from commit c6cde3860bbe1b8edb3147d3e840f6bf0ece32d8)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* refactor(driver-mxc): embed policy mapper as a module; remove standalone crate

Adopt the proto-based mapper (map_to_mxc) as the single source of truth,
embedded in openshell-driver-mxc as a Windows-gated `policy_map` module.
Rewire EmbeddedPolicyMapper to call it directly on the typed SandboxPolicy,
deleting the serde_yaml proto->YAML bridge. Move the CLI to a windows-gated
example and the parity tests into the crate; delete openshell-policy-mapper.

- gate policy_map + seam Windows-only (MXC is Windows-only)
- drop serde_yaml; add dev-deps openshell-policy, clap, anyhow
- normalize mapped paths to Windows form in the seam, in one place
- docs: add driver-mxc to AGENTS.md table; correct design doc section 17 test lane

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit f22f9c7a25b9a651c5c5cc73f62fb01c4d6c1a8d)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* feat(driver-mxc): implement lossless split_policy for proxy-delegated egress

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit 96d6afa0e2dc6a1d54edd12c34a0ceb0a30dadd0)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* feat(driver-mxc): implement Pattern-C governed-egress split through the policy seam

- split_policy: SocketAddr proxy_redirect (replaces bare port), processcontainer
  containment guard naming MXC M1, version preserved in the trimmed proxy_policy,
  delegation reported as an info loss item
- seam: MappedConfig carries trimmed_policy + proxy_addr; MapCtx.egress selects
  the split path; coarse path unchanged when egress is disabled
- driver: [openshell.drivers.mxc] egress_proxy / egress_proxy_addr config,
  validated at create (isolation_session rejected until M1); lifecycle threads
  the redirect into provision and stores the trimmed policy per sandbox,
  emitting an EgressRedirect platform event
- mxc: optional MxcNetwork block (defaultPolicy=block + proxy) in provision and
  one-shot configs; mock records configs for test assertions
- tests: lossless-invariant suite over all example policies (validate +
  serialize round-trip), split lifecycle proof, M1 rejection; example gains
  --split --proxy-addr writing mxc-config.json / trimmed-policy.yaml /
  loss-report.json

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit 34d54ad9f25dc6034c3ba15668555ff0d22cddd8)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(driver-mxc): emit MXC network.proxy as {localhost: port}

Verified against the real wxc-exec 0.6.0-alpha via --dry-run: MXC accepts
only the {localhost: N} proxy shape (the form the design doc specifies)
and rejects {host, port} with a parse error. Schema 0.6.0-alpha can
express only a loopback port, so non-127.0.0.1 redirect addresses are now
rejected: split_policy emits an error loss (no proxy block) and the driver
refuses egress_proxy_addr values off 127.0.0.1. Per-sandbox attribution
must use per-sandbox ports until the schema widens.

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit edde8d5434571fd5398409204fcf6862672c0793)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(driver-mxc): serialize isolation_session stop/deprovision as unit variants

Empirical contract finding from the real test lane (build 26300.8553,
wxc-exec 2026-06-10): the stop and deprovision experimental blocks are
unit variants in the wxc-exec schema and must serialize as null; sending
{} is rejected with malformed_request (invalid type: map, expected unit),
while provision/start accept maps. The production invoker, the real-lane
test, the probe script, and the e2e runner all sent {} - the driver could
provision and run an agent but never stop or delete an isolation-session
sandbox against this build. Pinned by a unit test.

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit 0df39ca0b22ebc21eb965b2b567a5b2cac26af32)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* test(driver-mxc): add Tier-0 mapper coverage matrix with schema drift guard

Three-quadrant, table-driven matrix (38 tests): mappable fields assert
exact MXC output; every OpenShell field MXC cannot express asserts a loss
item with the expected severity (and seam rejection on error); an empty
policy asserts the restrictive default-deny posture for every MXC knob
OpenShell does not control. The handled_fields_inventory drift guard
serializes a fully-populated policy and compares its YAML keys against
the mapper-handled field lists, so a new openshell-policy field fails the
suite until consciously mapped, delegated, or reported as loss.

Re-exports the policy seam types for integration tests; adds serde_yml,
base64, serde_json as dev-dependencies.

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit 91807f984a3b16846e35d6ca0d5ec41057aafa3a)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* feat(driver-mxc): inject agent_env into sandbox process.env

Add MxcComputeConfig.agent_env: each entry is either KEY=VALUE (verbatim) or a bare KEY resolved from the gateway host environment at launch, keeping secrets (e.g. inference API keys) out of the config file. Wire it into the agent process so gateway-launched agents can authenticate to cloud endpoints (process.env was previously hardcoded empty). Unit-tested via resolve_agent_env_passthrough_and_host_lookup.

Also add a gateway-driven cloud-inference (T1) test harness: mxc-inference.toml (agent_env + curl agent), inference.yaml policy, and run-inference-test.ps1 which starts the gateway, creates an isolation_session sandbox, runs an authenticated Nemotron call, and bundles redacted results. Documented agent_env in mxc-gateway.toml. Validated end-to-end on the test box (chat HTTP 200 + completion via the gateway).

(cherry picked from commit 94d9e827b8b77e0af9dc654943ea8e7d8400cc9f)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* test(driver-mxc): avoid unsafe env mutation in resolve_agent_env test

Replace std::env::{set,remove}_var (unsafe + racy under parallel test
execution in edition 2024) with a read-only PATH lookup. Preserves all
three behaviors under test and drops the #[allow(unsafe_code)].

(cherry picked from commit ac5766eeab2db4e8cc6fcd8d8a97809edaf3df30)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(mxc): adapt MXC driver to current GitHub OpenShell API

The MXC driver crate was authored on GitLab against an earlier proto/core
API. Adapt it to the API on GitHub main:

- build_capabilities_response no longer takes supports_interactive_session
- DriverSandboxSpec.gpu (bool) is now resource_requirements; detect GPU via
  effective_driver_gpu_count(driver_gpu_requirements(..))
- DriverSandbox gained a `workspace` field
- SandboxPolicy gained `network_middlewares`: pass it through the proxy split,
  emit a loss item on the coarse MXC path, and account for it in the mapper
  drift-guard test

Verified: cargo check + 75 mock-based tests pass (lib 27, examples 10,
policy_mapper_matrix 38).

Signed-off-by: Jamie King <jamiek@nvidia.com>

* feat(server): wire the MXC compute driver into the gateway on Windows

Register openshell-driver-mxc as the Windows-only in-process compute
backend so compute_driver = "mxc" resolves to a working runtime:

- ComputeRuntime::new_mxc, adapted to the current 11-arg from_driver
- mxc_policy_sink A1 side channel, staged in create_sandbox before dispatch
- mxc_config_from_context loader and the Mxc dispatch arm (Windows
  constructs; other targets return an explicit "Windows-only" error)
- Windows-gated openshell-driver-mxc dependency
- Mxc arms for the telemetry, config-file required-fields, and CLI
  reserved-builtin matches to keep them exhaustive/correct

Verified with cargo check --workspace --features openshell-prover/bundled-z3
on x86_64-pc-windows-msvc, stacked on PR #2496.

Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(driver-mxc): implement GetGatewayListenerRequirements for #2496 base

Signed-off-by: Jamie King <jamiek@nvidia.com>

* test(driver-mxc): add probe-gated real wxc-exec test lane (no mocks)

- tests/wxc_exec_real.rs: ignored-by-default integration tests against a
  real wxc-exec. Six --dry-run contract tests run wherever the binary
  exists (they caught the network.proxy shape mismatch); enforcement
  tests (processcontainer default-deny positive/negative, isolation
  session lifecycle round trip with a deprovision drop-guard) probe the
  backend and SKIP with a recorded reason where it is not live.
- examples/probe-mxc-host.ps1: classifies a host (OS build, --probe,
  per-backend trial) and emits a JSON capability verdict.
- examples/run-mxc-e2e.ps1 + e2e-policies/: scenario runner generalizing
  run-demo.ps1 (fs-rw, fs-readonly, fs-default-deny-empty,
  network-policy-rejected) with PASS/FAIL/SKIP gating and a stale
  OPENSHELL_MXC_MOCK_WXC guard in real mode.
- tasks/windows.toml: windows:test:mxc-real:x64, windows:e2e:mxc,
  windows:e2e:mxc:mock.

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
(cherry picked from commit 49afafe892caded59c4df50a9b652011ad97f41c)
Signed-off-by: Jamie King <jamiek@nvidia.com>

* fix(driver-mxc): address PR review feedback

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* docs: defer public MXC documentation

Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>

* fix(driver-mxc): build Windows capabilities response

Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>

* fix(server): gate in-tree tracing on Windows

Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>

* test(windows): fix cross-platform test assumptions

Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>

* test(windows): verify process and PEM portably

Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>

* test(windows): keep lifecycle command in policy

Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>

---------

Signed-off-by: Jamie King <jamiek@nvidia.com>
Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>
Co-authored-by: Prashant K <pkhodade@nvidia.com>
Co-authored-by: Giedrius Burachas <gburachas@nvidia.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Drew Newberry <385+drew@users.noreply.github.com>
2026-08-27 07:24:04 +00:00
Artem Lytvyn 56088d0811 fix(supervisor-network): distinguish absent policy binary from filesystem-access failure (#2948)
* fix(supervisor-network): distinguish absent policy binary from filesystem-access failure

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>

* fix(supervisor-network): resolve symlink chains at the SYMLOOP_MAX limit

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>

---------

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>
2026-08-26 23:26:25 +00:00
William Trépanier 0618ab0465 fix(supervisor): log unmatched L7 route denials (#2916)
Requests denied by relay_with_route_selection because no L7 endpoint path
matched returned a 403 without emitting an OCSF event or recording policy
activity, leaving the deny invisible to operators and telemetry. Every
other early-return deny in that loop already logs.

Emit an HTTP Activity deny event and an l7_policy activity record before
writing the denial. Split event construction out of emit_l7_request_log
so the event shape is directly testable, and narrow both helpers to take
the request action rather than the full L7RequestInfo, which they only
used for that field.

Signed-off-by: William Trépanier <wtrep@icloud.com>
2026-08-26 21:29:56 +00:00
krishicks d0dfb22baf feat(kubernetes): export driver traces over OTLP (#2958)
Mirror the VM, Podman, and Docker driver tracing setup for Kubernetes.
Export standalone driver spans through OTLP/gRPC as the distinct
openshell-driver-kubernetes service, preserve gateway trace context, record
lifecycle operations and gRPC failures, and flush spans on shutdown.

Kubernetes currently runs in-process when selected as a built-in gateway
driver. Use the temporary server-boundary shim shared with Podman and Docker
so traces retain the shape they will have when Kubernetes moves to a
separate process. Move the common ComputeDriver RPC tracing layer into
openshell-otel to keep all drivers aligned.

Propagate the active W3C context through the controller-reserved Sandbox
annotation and enable Agent Sandbox OTLP export in the local k3s workflow.
This connects asynchronous controller reconciliation spans to the originating
OpenShell create trace.

Expose gateway OTLP configuration through Helm and add an Aspire collector
to the local k3s workflow. Extend helm:k3s:forward with OTLP ingest and trace
UI forwarding for Kubernetes and local container gateway development.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-26 21:22:51 +00:00
krishicks 572843baf8 docs(agents): clarify user-visible PR review feedback (#2910)
Agents will often give a ton of feedback, but it can be hard to quantify the
impact of the issue the feedback is about.

This change updates the PR review feedback template to be more human-readable,
rooting any concerns in user-visible behavior when appropriate, and comparing
the new behavior to old behavior so that PR authors can make a determination of
whether they want to accept or reject the feedback.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-26 15:39:18 +00:00
krishicks c399342649 feat(dev): unify local Kubernetes gateway workflow (#2914)
Make the local k3s gateway workflow match the Docker and Podman flows by
registering and selecting successful plaintext Skaffold deployments with the
OpenShell CLI. Derive the registration name from the worktree-specific k3d
cluster name so parallel worktrees retain independent gateway metadata.

Add helm:k3s:forward as the standard way to expose the Kubernetes gateway on
localhost:8090, and update the development and debugging guidance to use the
active registered gateway instead of one-off endpoint flags.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-26 14:44:47 +00:00
Simon Scatton 8d16a59ea3 ci(branch-checks): run Rust checks in Nix shells (#2876)
* ci(branch-checks): run Rust checks in Nix shells

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci(branch-checks): run Rust tests with nextest

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci(branch-checks): cache Rust workspace artifacts

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

* ci(branch-checks): run cargo-deny in Nix shell

Signed-off-by: Simon Scatton <sscatton@nvidia.com>

---------

Signed-off-by: Simon Scatton <sscatton@nvidia.com>
v0.0.114
2026-08-26 13:09:00 +00:00
Evan Lezar 8be8b62ab5 ci(vouch): close approved request discussions (#2929)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-26 06:31:42 +00:00
Evan Lezar 4e992093f8 fix(docker): trace standalone driver over OTLP (#2923)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-26 06:27:21 +00:00
Evan Lezar a715a90db5 fix(vm): bump gvproxy to v0.8.9 (#2901)
Closes #2895

Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-26 04:54:52 +00:00
Polite_realism 60a9b4dc62 fix(ssh): add EMFILE backoff and exit notification to SSH accept loop (#2705)
Apply the same two-layer defense from the proxy accept loop (#2369/#2370)
to the SSH accept loop: classify transient vs terminal accept errors with
exponential backoff on EMFILE/resource-exhaustion, and notify the sandbox
when the accept loop exits so the container terminates instead of running
without SSH access.

- Add SshAcceptAction enum and classify_ssh_accept_error in ssh.rs,
  mirroring the proxy pattern (EMFILE/ENFILE/ENOBUFS → Retry with
  backoff, unknown errors → Terminal after 10 consecutive failures)
- Replace the bare accept().await in run_ssh_server with a classify-and-
  retry loop; resets consecutive-error counter on each successful accept
- Thread ssh_exit_tx: Option<oneshot::Sender<()>> through run_process;
  hold it as a drop-guard inside the SSH spawn so the receiver fires when
  the task ends for any reason
- Wire ssh_exited future in lib.rs (created only when ssh_socket_path is
  Some) and select! on it in both process_enabled paths, returning an
  error so the sandbox container restarts

Closes #2372

Signed-off-by: politerealism <burdcat17@gmail.com>
2026-08-25 21:07:10 +00:00
John T. MyersandJohn Myers 4d16a2a6f4 feat(gator): improve review output and launch compatibility (#2896)
* feat(gator): render human-readable review findings

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* feat(gator): collapse operational review metadata

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* fix(gator): support canonical sandbox startup

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* fix(gator): preserve credential placeholder identity

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* feat(gator): resolve addressed review threads

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

* fix(agents): preserve provider profile revisions

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>

---------

Signed-off-by: John Myers <johntmyers@users.noreply.github.com>
Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
2026-08-25 19:59:04 +00:00
Vyncint Ng 0e79653a7f feat(tui): show persisted guidance on rejected policy chunks (#2908)
* feat(tui): show persisted guidance on rejected policy chunks

The reviewer's rejection note is stored in PolicyChunk.rejection_reason and
already reaches the TUI in GetDraftPolicyResponse.chunks, but openshell-tui
never read the field. The note was dropped at the last step, so a reviewer
had no way to recall why a chunk had been rejected.

Render it in two places, following the truncate-in-list / full-in-popup
convention in the TUI development guide: a truncated, dimmed suffix on the
list row, and a "Guidance:" line in the detail popup.

Gate the accessor on status == "rejected" rather than on the field alone.
Approving a chunk passes None for the reason and the gateway writes the
field only when Some, so a chunk that was rejected and later approved still
carries the old note. Reading the field unconditionally would surface a
stale rejection on an approved rule.

Part of #1098, Definition of Done item "Rejected chunks show persisted
guidance". The other items on that issue are untouched.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>

* fix(tui): scroll the draft detail popup so long guidance stays reachable

A rejection reason has no server-side length cap, so a paragraph-length one
overflowed the fixed 22-row detail popup: the tail was clipped and the later
fields and both action hints were pushed off screen. The same overflow already
affected the unbounded rationale and security_notes fields, so fix the popup
rather than special-case the guidance line.

Split the popup's inner area into a scrolling body and a pinned hint row,
following the pattern in ui/create_provider.rs, and drive it with j/k, the
arrow keys, PageUp, PageDown, g and G. The approve and close controls now stay
on screen at every scroll position, and the bottom border carries the scroll
position.

Wrap free-form values explicitly instead of relying on Paragraph's Wrap, so the
rendered row count is exactly lines.len() and the scroll clamp cannot under-run
the content. Paragraph::line_count would answer the same question but sits
behind ratatui's unstable-rendered-line-info feature.

Add deterministic TestBackend coverage at 80x24 with a 2,000-character reason,
covering the head and tail, the pinned hints, over-scroll clamping, and the
pre-existing long-rationale case. Document the reviewer-facing guidance in the
policy advisor page.

Part of #1098.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>

* fix(tui): wrap the denial row and hint footer on a narrow popup

Making the scroll clamp exact meant dropping Paragraph's Wrap, which also
removed wrapping from the lines that do not go through push_wrapped. At 70
columns the denial row clipped mid-value and lost the last-seen timestamp, and
at 60 a pending chunk with scrollable content pushed [Esc] Close past the right
edge of the single hint row. The key still worked, but that is the same
controls-not-visible failure one axis over.

Keep the denial row on one line while it fits and wrap it onto the label indent
when it does not, so the common 80-column layout is unchanged. Pack the footer
hints into as many rows as they need without splitting a hint, and derive the
footer height from that: adding a hint row shrinks the body and can itself
change whether the content scrolls, so the two settle together.

The earlier tests were all 80x24, which is why they missed this. Cover the
denial timestamps and the approve, reject and close hints at 60, 70 and 80
columns, plus the packing helper directly.

Part of #1098.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>

* fix(tui): measure popup wrapping in display columns, not chars

wrap_value packed rows by chars().count(), and a CJK glyph is one char but two
terminal columns. A double-width rejection reason therefore produced rows about
twice the popup's width: the right half of every row was clipped, and unlike
the vertical case there is no horizontal scroll to recover it.

Measure width with Span::width(), the same measurement ratatui applies when it
lays cells out, so the wrap agrees with the renderer by construction and no new
dependency is needed. This covers word packing, the hard-break path for a word
wider than the line, the label indent, the footer hint packing, and the denial
row's fits-on-one-line check.

The list row's shortened copy had the same defect through truncate_str, which
counts chars. Leave truncate_str alone for its two existing callers and add
truncate_display for the guidance row.

Cover it with an 80x24 render regression that interleaves markers through the
CJK text: a trailing marker lands on its own short row in both the broken and
fixed layouts, so it would not detect this. The two unit tests measure with an
independent column oracle rather than the helper under test, which would make
them tautological.

Part of #1098.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>

---------

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
2026-08-25 18:37:36 +00:00
Polite_realism 74015b951a fix(sandbox): terminate sandbox when proxy accept loop exits unexpectedly (#2370)
* fix(sandbox): terminate sandbox when proxy accept loop exits unexpectedly

Add a oneshot notification channel to ProxyHandle that fires when the
proxy task exits for any reason (panic, abort, or loop break). The
sandbox main loop now races this signal alongside the entrypoint process
and shutdown signals, terminating with a clear OCSF error if the proxy
dies. This prevents the sandbox from continuing to report Ready with a
dead proxy.

Follows the existing sidecar control channel pattern. All five wait
paths (process+sidecar, process-only, network+sidecar, network-only
on Linux, and non-Linux) now monitor proxy liveness.

Refs #2337

Signed-off-by: politerealism <burdcat17@gmail.com>

* test(proxy): add unit tests for exit detection drop-guard and take_exit_receiver

Verify the oneshot drop-guard pattern fires on normal task exit and
abort, and document the take_exit_receiver contract including the
double-call hazard.

Signed-off-by: politerealism <burdcat17@gmail.com>

* fix(proxy): classify terminal vs transient accept errors and exit on unrecoverable failures

Extract accept-loop error handling into classify_accept_error() with
AcceptAction enum so the decision logic is independently testable.
Terminal errors (EBADF/EINVAL/ENOTSOCK) now exit the loop immediately,
allowing the supervisor to detect proxy death. Unknown errors exit
after 10 consecutive failures. FD exhaustion retries with exponential
backoff capped at 5s (fix unreachable cap from min(6) to min(7)).

Adds 9 unit tests covering classification, counter interactions,
backoff progression, and bounded exit behavior.

Signed-off-by: politerealism <burdcat17@gmail.com>

* refactor(proxy): use libc constants and Option-based exit receiver

Replace raw errno numbers with libc:: constants behind #[cfg(unix)]
gates in classify_accept_error and its tests. Refactor
ProxyHandle::exited_rx to Option<Receiver<()>> so take_exit_receiver
uses .take() instead of allocating a dummy sender. Add a
ProxyHandle-level test that verifies the full drop → abort → receiver
fires path.

Signed-off-by: politerealism <burdcat17@gmail.com>

* fix(proxy): restore full transient errno classification from main

The refactored classifier only recognized EMFILE/ENFILE as retryable,
routing all other documented transient accept errors (ENOBUFS, ENOMEM,
ECONNABORTED, ECONNRESET, EINTR, ENETDOWN, etc.) through the unknown-
error budget which terminates the proxy after 10 consecutive failures.
Restore the complete transient errno table from main so these errors
retry indefinitely with appropriate backoff.

Signed-off-by: politerealism <burdcat17@gmail.com>

* fix(sandbox): resolve Clippy warnings in proxy-exit detection code

Replace redundant closure |p| p.take_exit_receiver() with the method
reference ProxyHandle::take_exit_receiver, and change all five
select! arms from _ = &mut proxy_exited to () = &mut proxy_exited
to satisfy clippy::ignored_unit_patterns. Also run cargo fmt on the
new proxy test in proxy.rs.

Signed-off-by: politerealism <burdcat17@gmail.com>

* ci: trigger required check re-run

Signed-off-by: politerealism <burdcat17@gmail.com>

* fix(sandbox): remove unnecessary std::pin qualification

Signed-off-by: politerealism <burdcat17@gmail.com>

---------

Signed-off-by: politerealism <burdcat17@gmail.com>
2026-08-25 18:30:59 +00:00
Mrunal Patel 18ce13b9b1 feat(providers): expose actionable OAuth refresh failures (#2887)
* fix(providers): classify OAuth refresh failures

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

* test(providers): add Keycloak refresh e2e lane

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

* fix(providers): harden OAuth refresh recovery

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

* fix(providers): classify post-mint refresh failures

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

* fix(providers): tolerate malformed OAuth subtypes

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

---------

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
2026-08-25 18:00:00 +00:00
Seth Jennings 5206bc51b2 feat(sdk): add OAuth Client Credentials support to SDKs (#2907)
* feat(sdk): add renewable client credentials auth

Implement lazy OAuth client-credentials acquisition and renewal for the Python, TypeScript, and Go SDK clients, with shared security conformance coverage and service-account documentation.\n\nCloses #2803

Signed-off-by: Seth Jennings <sjenning@redhat.com>

* fix(sdk): address client credentials review

Signed-off-by: Seth Jennings <sjenning@redhat.com>

* fix(sdk): address client credentials review

Signed-off-by: Seth Jennings <sjenning@redhat.com>

---------

Signed-off-by: Seth Jennings <sjenning@redhat.com>
2026-08-25 17:53:47 +00:00
Russell Bryant 38a94931ff fix(gateway): let Ready win over stale Suspended in derive_phase (#2933)
On Agent Sandbox v1beta1, a `sandbox stop` then `sandbox start` left the
sandbox stuck at `Starting` and `start` timed out after 300s, even though
the workload pod was `Running 1/1` and the CR reported `Ready=True`.

The v1beta1 controller (v0.5.0) sets `Suspended=True (PodTerminated)` on
stop and does not clear it on resume, so a resumed CR carries both
`Ready=True (DependenciesReady)` and a stale `Suspended=True`. The gateway's
`derive_phase` treated any `Suspended=True` condition as `Stopped` before it
consulted `Ready`, so a resumed-but-Ready sandbox was read as not-running and
the `Starting` phase never advanced to `Ready`.

`Ready=True` means the sandbox is usable through this gateway and must win
over a `Suspended=True` condition. Only resolve `Stopped` from `Suspended`
when `Ready` is not `True`; a genuine stop leaves `Ready` unset or `False`,
so it still resolves to `Stopped`. v1alpha1 (which encodes desired state as
`spec.replicas` and emits no lingering `Suspended`) is unaffected.

The underlying stale condition is an upstream bug in the agent-sandbox
controller, fixed by kubernetes-sigs/agent-sandbox#1150 (in v0.5.6). This
gateway change keeps OpenShell resilient regardless of controller version.

Fixes #2932

Signed-off-by: Russell Bryant <rbryant@redhat.com>
2026-08-25 17:04:43 +00:00
alangou fb6610df39 feat(build): embed auditable Rust dependency metadata (#2734)
Signed-off-by: Adrien Langou <alangou@nvidia.com>
2026-08-25 14:36:15 +00:00
Evan Lezar e2ca9cb890 fix(test-guest): pin HVF runtime dependencies (#2924)
Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-25 14:01:29 +00:00
Simon Scatton 455883905a fix(python): remove CLI from wheel (#2321)
The Maturin-based wheel packaging was a historical remnant from when the local gateway launch path and OpenShell CLI were coupled in one binary. The gateway and CLI now ship as standalone artifacts, so the Python distribution should contain only the SDK.

Build a single platform-independent setuptools wheel, verify that it cannot contain native code or an openshell entry point, and simplify the release jobs and documentation for SDK-only PyPI installs.

Signed-off-by: Simon Scatton <sscatton@nvidia.com>
v0.0.113
2026-08-25 13:20:42 +00:00
Evan Lezar 4fe5b0f605 fix(ci): allow pasta to receive Podman stop signals (#2900)
* fix(ci): allow pasta to receive Podman stop signals

Signed-off-by: Evan Lezar <elezar@nvidia.com>

* test(ci): fail on pasta SIGTERM denial

Signed-off-by: Evan Lezar <elezar@nvidia.com>

* test(ci): retain AppArmor denial diagnostics

Signed-off-by: Evan Lezar <elezar@nvidia.com>

---------

Signed-off-by: Evan Lezar <elezar@nvidia.com>
2026-08-25 12:43:45 +00:00
Russell Bryant aa848f164d fix(core): fall back to podman CLI when no API socket is found (#1858)
* fix(core): fall back to podman CLI when no API socket responds

Auto-detection only checked well-known Podman socket paths, so a Podman
machine exposing its API socket at a non-standard location went
undetected. The symlink at a well-known path is not always present; it
varies by Podman version, machine provider, and platform.

Extend detect_podman_socket() to fall back to podman CLI discovery when
no well-known candidate responds: podman info --format json determines
whether the service is local or remote, and podman machine inspect
resolves the host-side forwarded socket for VM-backed machines. All
existing callers (driver auto-detection, the Podman driver, and the VM
driver's container-engine fallback) pick this up without change.

Select the machine backing the active Podman connection instead of the
first entry from podman machine inspect, honoring podman's connection
precedence: CONTAINER_CONNECTION, then CONTAINER_HOST (mapped to a
connection by URI), then the containers.conf default. An explicit
endpoint that maps to no known machine is left unresolved rather than
guessing an unrelated machine. When CONTAINER_HOST is an explicit unix://
socket, use that path directly since podman info connects through it.

Update the gateway config reference and the Podman driver README, which
described probe-only detection.

Signed-off-by: Russell Bryant <rbryant@redhat.com>

* fix(core): inspect podman machine by name and bound discovery probes

Address two startup defects in Podman socket auto-detection:

- Remote discovery ran `podman machine inspect` with no arguments, which
  inspects only `podman-machine-default`. On a host whose default
  connection is a different machine, selection fell back to the first
  (wrong) entry, so the gateway could operate against the wrong backend.
  Resolve the active machine first and inspect it by name, trying the
  `-root`-stripped machine for rootful connections and returning None
  when no machine can be mapped instead of substituting another.

- `podman info`, `podman machine inspect`, and `podman system connection
  list` used unbounded `Command::output()`, so a stalled machine, SSH
  connection, or helper could hang gateway startup indefinitely. Route
  all three through a bounded runner that kills and reaps the child on a
  documented deadline and returns None so detection can continue.

Signed-off-by: Russell Bryant <rbryant@redhat.com>

* fix(core): bound podman probe stdout drainage and kill descendants

The previous timeout only bounded waiting for the direct child to exit.
Draining stdout still called read_to_end, which waits for pipe EOF — a
Podman probe can leave a daemonized descendant (SSH multiplexer, gvproxy)
that inherited the stdout pipe, so drainage could wait indefinitely even
after the direct child exited, defeating the deadline.

Run each probe as its own process-group leader, cap stdout drainage by
the same deadline via a channel, and on expiry kill the whole process
group so descendants holding the pipe are terminated and EOF is reached.
Add a regression test where the direct child exits but a backgrounded
descendant keeps stdout open.

Signed-off-by: Russell Bryant <rbryant@redhat.com>

* fix(core): make podman probe deadline absolute against escaped descendants

The prior drainage timeout still ended in an untimed rx.recv() after
killing the process group. A descendant that escaped the probe's process
group (e.g. via setsid) while holding the stdout pipe would not be killed,
so read_to_end never saw EOF, the reader never sent, and that recv() could
block gateway startup indefinitely.

On drainage timeout, best-effort kill the process group and return None
immediately, abandoning the reader thread instead of waiting on it again.
The deadline now bounds the whole call regardless of what descendants do.
Add a regression test whose stdout-holding descendant escapes the process
group (`set -m`) and assert the call still returns promptly.

Signed-off-by: Russell Bryant <rbryant@redhat.com>

---------

Signed-off-by: Russell Bryant <rbryant@redhat.com>
2026-08-25 11:49:22 +00:00
Russell Bryant 72b9c4acef test(e2e): pin direct podman calls to harness socket on macOS (#2909)
The podman_gateway_start e2e test shells out to `podman` directly (unlike
every other Podman e2e test, which routes through the gateway). The harness
`e2e/with-podman-gateway.sh` clobbers `XDG_CONFIG_HOME` with an empty dir to
isolate CLI/SDK gateway metadata. On macOS the podman client resolves its VM
connection config from `XDG_CONFIG_HOME`, so a bare `podman ps` falls back to a
nonexistent native rootless socket and fails with "unable to connect to Podman
socket". On Linux the socket resolves via `XDG_RUNTIME_DIR`, so the test passes
there and this is a macOS-only false failure.

Target the same API socket the gateway uses by passing `--url unix://$SOCKET`
when the harness-exported `OPENSHELL_PODMAN_SOCKET` is set, mirroring the
shell's `podman_cmd` helper. When the var is unset (running the test outside the
harness), fall back to plain `podman`, so Linux behavior is unchanged.

Signed-off-by: Russell Bryant <rbryant@redhat.com>
2026-08-24 22:04:25 +00:00
Philippe Martin 0a1f246587 feat(sandbox,podman): trust corporate CA for https:// proxies and intercepted TLS (#2512)
* feat(sandbox,podman): trust corporate CA for https:// proxies and intercepted TLS

The corporate proxy chaining only accepted plain http:// proxy URLs, so
operators whose forward proxy terminates TLS with a private corporate CA
had no way to reach it, and TLS-intercepting proxies (mitmproxy, squid
ssl-bump) that re-sign tunneled server certificates broke every upstream
handshake after CONNECT.

The supervisor now accepts https:// proxy URLs: it wraps the connection to
the proxy in TLS before the CONNECT handshake, verifying the proxy
certificate against the built-in Mozilla roots, the system CA bundle, and an
optional operator corporate CA bundle. The upstream dial returns a
Plain/Tls stream enum consumed generically by the relay paths.

The corporate CA is delivered as a driver-supplied command-line argument
(--upstream-proxy-ca-bundle), never an environment variable, matching the
hardened proxy-config model where a sandbox image cannot influence the
operator's egress boundary. It is folded into the sandbox combined trust
bundle (write_ca_files) and the L7 upstream verification store
(build_upstream_client_config) at startup, so intercepted upstream
handshakes succeed and sandbox workloads trust the re-signed certificates.
Configuration is fail-closed: a CA bundle set without a proxy, or an
unreadable or certificate-free file, is fatal rather than silently
weakening the trust boundary.

The shared parse_upstream_proxy_url validator accepts https:// (recording
the scheme so the driver and supervisor agree), keeping the explicit-port
requirement. The Podman driver gains a proxy_ca_bundle operator setting
(TOML, --sandbox-proxy-ca-bundle, OPENSHELL_SANDBOX_PROXY_CA_BUNDLE) that
bind-mounts the host PEM read-only into the sandbox (a CA certificate is not
secret) and points --upstream-proxy-ca-bundle at it, with a create-time
readability check.

The standalone dev gateway task passes OPENSHELL_SANDBOX_PROXY_CA_BUNDLE
through to the generated podman config, so a local gateway can be pointed at
a TLS-intercepting proxy without hand-editing the regenerated TOML.

Refs #1792

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(sandbox): reject CA bundles with valid PEM framing but invalid X.509 DER

The proxy CA bundle validation counted PEM blocks that base64-decoded
successfully, but did not verify the decoded bytes were accepted as
trust anchors by RootCertStore. A bundle with syntactically valid PEM
framing but invalid DER would pass the startup check while contributing
zero usable anchors, causing opaque TLS failures at runtime instead of
a fail-closed startup error.

Validate decoded certificates through RootCertStore::add_parsable_certificates
and reject the bundle unless at least one is accepted.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(sandbox,podman): allow proxy auth without insecure acknowledgement for https:// proxies

For an https:// proxy the Proxy-Authorization credential travels inside
the verified TLS session, so the proxy_auth_allow_insecure
acknowledgement is unnecessary. Previously both http:// and https://
proxies required it, producing a misleading cleartext-risk diagnostic
for a path that is already encrypted.

Skip the requirement when the proxy URL uses https://; the
acknowledgement is still tolerated if set. Updated in both the
supervisor and Podman driver validation paths, with docs and tests.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix: format

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(kubernetes): use truly unsupported scheme in proxy validation test

https:// is now a supported proxy scheme after a13c4dce, so the
unsupported-scheme test must use a genuinely unsupported scheme.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(e2e): sign the https proxy fixture listener cert with a CA

The corporate-proxy E2E fixture served a single `openssl req -x509`
certificate as its TLS listener identity. OpenSSL marks that certificate
`basicConstraints: critical, CA:TRUE`, and rustls refuses a CA
certificate presented as an end-entity certificate (CaUsedAsEndEntity).
The supervisor's TLS handshake with the proxy therefore failed, the
upstream dial errored, and the workload's CONNECT was dropped without a
response, so podman_corporate_proxy_trusts_ca_bundle_for_https_proxy
failed on the approved destination while policy denial still worked.

Generate a corporate CA and a separate listener leaf signed by it, serve
the leaf chain, and publish only the CA as the bundle the supervisor
trusts. This is what an intercepting proxy actually presents, and it
exercises the corporate-CA trust path rather than pinning the listener
certificate itself.

Refs #1792

Signed-off-by: Philippe Martin <phmartin@redhat.com>

---------

Signed-off-by: Philippe Martin <phmartin@redhat.com>
2026-08-24 21:10:25 +00:00
Artem Lytvyn d7e137f12e fix(proxy): normalize trailing-dot CONNECT hosts before policy evaluation (#2248)
* fix(proxy): normalize trailing-dot CONNECT hosts before policy evaluation

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>

* fix(supervisor-network): keep trailing dot on upstream proxy CONNECT host and add regression test

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>

* fix(supervisor-network): drop redundant UpstreamProxyArgs import in tests

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>

---------

Signed-off-by: Artem Lytvyn <alytvyn@redhat.com>
2026-08-24 20:25:48 +00:00
krishicks e457974a52 feat(docker): export driver traces over OTLP (#2851)
Mirror the VM and Podman driver tracing setup for Docker. Export Docker
driver spans through OTLP/gRPC as the distinct openshell-driver-docker
service, preserve gateway trace context, record lifecycle and asynchronous
provisioning operations, and report gRPC failures.

Docker currently runs in-process when selected as a built-in gateway driver.
Add the same temporary server-boundary shim used by Podman so traces retain
the shape they will have when Docker moves to a separate process. Generalize
the gateway provider selection for both in-process drivers and share the OTLP
collector fixture across Docker, Podman, and VM tracing tests.

Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-24 18:39:21 +00:00
Simon Scatton 905e99aa2a feat(build): add Nix-native Linux toolchains (#2875)
* feat(nix): add glibc 2.28 development shell

* feat(nix): add musl development shell

* feat(build): use mold in musl development shell

* feat(flake): add nix remote cache

* feat(build): use mold in default development shell
v0.0.112
2026-08-24 12:16:00 +00:00
krishicks 7fc6138981 docs(agent): warn on missing workflow labels (#2815)
Signed-off-by: Kris Hicks <khicks@nvidia.com>
2026-08-24 08:10:10 +00:00
grs 40d1b48666 feat(provider): support for SPIFFE backed token exchange (#1970)
* feat(provider): add ability to request token exchange instead of client credentials as OAuth grant_type

Signed-off-by: Gordon Sim <gsim@redhat.com>

* test(proxy): add further tests for token exchange

Signed-off-by: Gordon Sim <gsim@redhat.com>

* test(provider): add runnable example for token exchange

Signed-off-by: Gordon Sim <gsim@redhat.com>

* test(e2e): cover Podman token exchange grants

Signed-off-by: Gordon Sim <gsim@redhat.com>

* refactor(oauth): extract duplicated functionality from server and supervisor

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(provider): evict nearest-to-expiry entry from intermediate token cache

Signed-off-by: Gordon Sim <gsim@redhat.com>

* doc(supervisor): add podman example for token exchange

Signed-off-by: Gordon Sim <gsim@redhat.com>

* fix(provider): withhold token-exchange subject credentials

Signed-off-by: Gordon Sim <gsim@redhat.com>

---------

Signed-off-by: Gordon Sim <gsim@redhat.com>
2026-08-24 05:42:30 +00:00
Prekshi Vyas e3dc011208 fix(provider): isolate unbound static credentials (#2862)
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
2026-08-23 20:36:19 +00:00
Drew Newberry 2f7fb65591 chore(docs): relicense Fern stylesheet under Apache 2.0 (#2882)
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
2026-08-21 19:50:00 +00:00