diff --git a/.agents/skills/build-from-issue/SKILL.md b/.agents/skills/build-from-issue/SKILL.md index 8c843bfc6d..08054263fb 100644 --- a/.agents/skills/build-from-issue/SKILL.md +++ b/.agents/skills/build-from-issue/SKILL.md @@ -444,6 +444,8 @@ same branch. If the change affects user-facing compute-driver setup, also update `docs/reference/sandbox-compute-drivers.mdx` or the relevant deployment page. +Use the `sync-agent-infra` skill's maintenance map to identify related skill updates when the implementation changes behavior, commands, or development workflows. Run its full consistency check when the implementation adds, removes, or renames skills or crates; changes workflow relationships or skill coverage; modifies issue or PR templates; or changes agent cross-references. Fix any drift before committing. + ### Step 12: Commit and Push Commit all changes using conventional commit format. The `` comes from the issue type in the plan: diff --git a/.agents/skills/create-github-pr/SKILL.md b/.agents/skills/create-github-pr/SKILL.md index 8690783ea4..6c36af3833 100644 --- a/.agents/skills/create-github-pr/SKILL.md +++ b/.agents/skills/create-github-pr/SKILL.md @@ -24,6 +24,10 @@ in the same branch. If the change affects user-facing compute-driver setup, also update `docs/reference/sandbox-compute-drivers.mdx` or the relevant deployment docs. +### Check Agent Infrastructure + +Use the `sync-agent-infra` skill's maintenance map to identify related skill updates when the branch changes behavior, commands, or development workflows. Run its full consistency check when the branch adds, removes, or renames skills or crates; changes workflow relationships or skill coverage; modifies issue or PR templates; or changes agent cross-references. Resolve any drift before creating the PR. + ### Run Pre-commit Checks Run the local pre-commit task before opening a PR: diff --git a/.agents/skills/debug-inference/SKILL.md b/.agents/skills/debug-inference/SKILL.md index 6770da5987..3cb08b5861 100644 --- a/.agents/skills/debug-inference/SKILL.md +++ b/.agents/skills/debug-inference/SKILL.md @@ -1,6 +1,6 @@ --- name: debug-inference -description: Debug why inference.local or external inference setup is failing. Use when the user cannot reach a local model server, has provider base URL issues, sees inference verification failures, hits protocol mismatches, or needs to diagnose inference on local vs remote gateways. Trigger keywords - debug inference, inference.local, local inference, ollama, vllm, sglang, trtllm, NIM, inference failing, model server unreachable, failed to verify inference endpoint, host.openshell.internal. +description: Debug why inference.local, direct external inference, or supervisor-only system inference is failing. Use when the user cannot reach a local model server, has provider base URL issues, sees inference verification failures, hits protocol mismatches, or needs to diagnose inference on local vs remote gateways. Trigger keywords - debug inference, inference.local, system inference, sandbox-system, local inference, ollama, vllm, sglang, trtllm, NIM, inference failing, model server unreachable, failed to verify inference endpoint, host.openshell.internal. --- # Debug Inference @@ -11,7 +11,7 @@ Use `openshell` CLI commands to inspect the active gateway, provider records, ma ## Overview -OpenShell supports two different inference paths. Diagnose the correct one first. +OpenShell supports three inference paths. Diagnose the correct one first. 1. **Managed inference** through `https://inference.local` - Configured by `openshell inference set` @@ -21,6 +21,10 @@ OpenShell supports two different inference paths. Diagnose the correct one first - Controlled by `network_policies` - Requires the application to call the external host directly - Requires provider attachment and network access to be configured separately +3. **System inference** used by platform functions + - Configured by `openshell inference set --system` + - Uses the `sandbox-system` route + - Consumed in-process by the sandbox supervisor and not exposed to sandbox user code through `inference.local` For local or self-hosted engines such as Ollama, vLLM, SGLang, TRT-LLM, and many NIM deployments, the most common managed inference pattern is an `openai` provider with `OPENAI_BASE_URL` pointing at a host the gateway can reach. @@ -38,10 +42,13 @@ Use these commands first: # Which gateway is active, and can the CLI reach it? openshell status -# Show managed inference config for inference.local +# Show both the user-facing and system inference routes openshell inference get -# Inspect the provider record referenced by inference.local +# Show only the supervisor-only system route +openshell inference get --system + +# Inspect the provider record referenced by the relevant route openshell provider get # Inspect gateway topology details when remote/local confusion is suspected @@ -59,9 +66,9 @@ When the user asks to debug inference, run diagnostics automatically in this ord Establish these facts first: -1. Is the application calling `https://inference.local` or a direct external host? +1. Is sandbox code calling `https://inference.local`, is the application calling a direct external host, or is a platform function using system inference? 2. Which gateway is active, and is it local, remote, or cloud? -3. Which provider and model are configured for managed inference? +3. Which provider, model, and timeout are configured for the relevant route? 4. Is the upstream local to the gateway host, or somewhere else? ### Step 0: Check the Active Gateway @@ -83,23 +90,31 @@ Common mistake: - **Laptop-local model + remote gateway**: `host.openshell.internal` points to the remote gateway host, not your laptop. A laptop-local Ollama or vLLM server will not be reachable without a tunnel or shared reachable network path. -### Step 1: Check Whether Managed Inference Is Configured +### Step 1: Check Whether the Relevant Route Is Configured Run: ```bash openshell inference get +openshell inference get --system ``` Interpretation: -- **`Not configured`**: `inference.local` has no backend yet. Fix by configuring it: +- `openshell inference get` shows both the user-facing `inference.local` route and the system route. `--system` isolates the system route. +- **The `inference.local` route is `Not configured`**: managed inference has no backend. Configure it without `--system`: ```bash openshell inference set --provider --model ``` -- **Provider and model shown**: Continue to provider inspection. +- **System inference is `Not configured`**: platform functions have no system backend. Configure it separately: + + ```bash + openshell inference set --system --provider --model + ``` + +- **Provider, model, and timeout shown**: Continue to provider inspection for the relevant route. ### Step 2: Inspect the Provider Record @@ -111,10 +126,13 @@ openshell provider get Check: -- Provider type matches the client API shape +- Provider type matches the client API shape and is supported for managed inference - `openai` for OpenAI-compatible engines such as Ollama, vLLM, SGLang, TRT-LLM, and many NIM deployments - `anthropic` for Anthropic Messages API - `nvidia` for NVIDIA-hosted OpenAI-compatible endpoints + - `deepinfra` for DeepInfra's OpenAI-compatible endpoint + - `google-vertex-ai` for Vertex AI; Claude models use Anthropic Messages and other models use OpenAI Chat Completions + - `aws-bedrock` only through a configured Bedrock-compatible bridge today - Required credential key exists - `*_BASE_URL` override is correct when using a self-hosted endpoint @@ -123,9 +141,11 @@ Fix examples: ```bash openshell provider create --name ollama --type openai --credential OPENAI_API_KEY=empty --config OPENAI_BASE_URL=http://host.openshell.internal:11434/v1 -openshell provider update ollama --type openai --credential OPENAI_API_KEY=empty --config OPENAI_BASE_URL=http://host.openshell.internal:11434/v1 +openshell provider update ollama --credential OPENAI_API_KEY=empty --config OPENAI_BASE_URL=http://host.openshell.internal:11434/v1 ``` +`provider update` preserves the provider type and does not accept `--type`. Prefer bare credential keys, such as `--credential OPENAI_API_KEY`, when reading a real secret from the CLI environment. + ### Step 3: Check Local Host Reachability For host-backed local inference, confirm the upstream server: @@ -142,21 +162,25 @@ Common mistakes: ### Step 4: Check Request Shape -Managed inference only works for `https://inference.local` and supported inference API paths. +User-facing managed inference only works for `https://inference.local` and supported inference API paths. Supported patterns include: - `POST /v1/chat/completions` - `POST /v1/completions` - `POST /v1/responses` +- `POST /v1/embeddings` - `POST /v1/messages` - `GET /v1/models` +- `GET /v1/models/*` +- `POST /model/{modelId}/invoke` for bridge-fronted `aws-bedrock` Common mistakes: - **Wrong scheme**: `http://inference.local` instead of `https://inference.local` - **Unsupported path**: request does not match a known inference API - **Protocol mismatch**: Anthropic client against an `openai` provider, or vice versa +- **Provider-specific mismatch**: Vertex Claude requests must use `/v1/messages`; other Vertex models currently use `/v1/chat/completions`; Bedrock uses its model-in-path invoke shape Fix guidance: @@ -166,6 +190,8 @@ Fix guidance: ### Step 5: Probe from a Sandbox +This probe validates the user-facing `inference.local` route. It does not exercise supervisor-only system inference. + Run a minimal request from inside a sandbox: ```bash @@ -179,21 +205,28 @@ Interpretation: - **`no compatible route`**: provider type and client API shape do not match - **Connection refused / upstream unavailable / verification failures**: base URL, bind address, topology, or credentials are wrong +For system inference failures, inspect the platform function and sandbox supervisor/network logs after confirming `openshell inference get --system`. User code cannot call the `sandbox-system` route directly. + ### Step 6: Reapply or Repair the Managed Route -After fixing the provider, repoint `inference.local`: +After fixing the provider, use `update` for a partial change or `set` to replace the route: ```bash openshell inference set --provider --model +openshell inference update --provider +openshell inference update --model +openshell inference update --timeout 120 ``` +Add `--system` to target the system route. Without it, these commands target `inference.local`. A timeout of `0` uses the 60-second default; increase it for models with long reasoning or idle streaming phases. + If the endpoint is intentionally offline and you only want to save the config: ```bash openshell inference set --provider --model --no-verify ``` -Inference updates are hot-reloaded to all sandboxes on the active gateway within about 5 seconds by default. +Use `--no-verify` only when the endpoint is intentionally offline or the provider protocol cannot be verified, such as the current bridge-fronted Bedrock flow. Inference updates are hot-reloaded to running sandboxes within about 5 seconds by default. ### Step 7: Diagnose Direct External Inference @@ -203,9 +236,11 @@ Check instead: 1. The application is configured to call the external hostname directly 2. A provider with the needed credentials exists -3. The sandbox is launched with that provider attached +3. The sandbox has that provider attached (`openshell sandbox provider list [name]`) 4. `network_policies` allow that host, port, and HTTP rules +Attach or detach a provider on an existing sandbox with `openshell sandbox provider attach ` and `openshell sandbox provider detach `. + Use the `generate-sandbox-policy` skill when the user needs help authoring policy YAML. ## Fix: Local Host Inference Timeouts (Firewall) @@ -305,11 +340,13 @@ Both commands should return the upstream model list. | Symptom | Likely cause | Fix | |---------|--------------|-----| | `openshell inference get` shows `Not configured` | No managed inference route configured | `openshell inference set --provider --model ` | +| System inference is `Not configured` | Platform-only route has no backend | `openshell inference set --system --provider --model ` | | `failed to verify inference endpoint` | Bad base URL, wrong credentials, wrong provider type, or upstream not reachable | Fix provider config, then rerun `openshell inference set`; use `--no-verify` only when the endpoint is intentionally offline | | Base URL uses `127.0.0.1` | Loopback points at the wrong runtime | Use `host.openshell.internal` or another gateway-reachable host | | Local engine works only when gateway is local | Gateway moved to remote host | Run the engine on the gateway host, add a tunnel, or use direct external access | | `connection not allowed by policy` on `inference.local` | Unsupported path or method | Use a supported inference API path | -| `no compatible route` | Provider type does not match request shape | Switch provider type or change the client API | +| `no compatible route` | Provider type does not match request shape | Create or select a provider of the matching type, or change the client API | +| `inference.local` works but a platform function fails | User route is configured but `sandbox-system` is missing or wrong | `openshell inference get --system`; configure or update with `--system`; inspect supervisor logs | | Direct call to external host is denied | Missing policy or provider attachment | Update `network_policies` and launch sandbox with the right provider | | SDK fails on empty auth token | Client requires a non-empty API key even though OpenShell injects the real one | Use any placeholder token such as `test` | | Upstream timeout from container to host-local backend | Host firewall or network config blocks container-to-host traffic | Allow the Docker bridge subnet to reach the inference port on the host gateway IP (see firewall fix section above) | @@ -328,6 +365,9 @@ openshell gateway info echo "=== Managed Inference ===" openshell inference get +echo "=== System Inference Only ===" +openshell inference get --system + echo "=== Providers ===" openshell provider list @@ -340,7 +380,7 @@ openshell sandbox create -- curl https://inference.local/v1/chat/completions --j When you report back, state: -1. Which inference path is failing (`inference.local` vs direct external) +1. Which inference path is failing (`inference.local`, direct external, or system inference) 2. Whether gateway topology is part of the problem 3. The most likely root cause 4. The exact fix commands the user should run diff --git a/.agents/skills/debug-openshell-cluster/SKILL.md b/.agents/skills/debug-openshell-cluster/SKILL.md index 82ce9650cd..07de687103 100644 --- a/.agents/skills/debug-openshell-cluster/SKILL.md +++ b/.agents/skills/debug-openshell-cluster/SKILL.md @@ -1,11 +1,11 @@ --- name: debug-openshell-cluster -description: Debug why an OpenShell gateway deployment is unhealthy, unreachable, or unable to create sandboxes. Use when the user has a gateway health failure, Docker/Podman runtime issue, Helm install failure, Kubernetes scheduling issue, TLS secret issue, VM driver issue, or sandbox startup problem. Trigger keywords - debug gateway, gateway failing, deployment failing, helm install failing, cluster health, gateway health, gateway not starting, health check failed, sandbox pending, docker driver, podman driver, vm driver. +description: Debug why an OpenShell gateway deployment is unhealthy, unreachable, or unable to create sandboxes. Use for gateway health failures, Docker/Podman runtime issues, Helm failures, Kubernetes scheduling, TLS or auth, gateway interceptors, supervisor middleware startup or runtime failures, external compute-driver sockets, VM drivers, or sandbox startup. Trigger keywords - debug gateway, gateway failing, deployment failing, helm install failing, cluster health, gateway health, gateway not starting, health check failed, sandbox pending, docker driver, podman driver, kubernetes driver, external driver, compute driver socket, gateway interceptor, supervisor middleware, middleware failed, vm driver. --- # Debug OpenShell Gateway Deployment -Diagnose a gateway and its selected compute platform. Do not assume OpenShell provisions Kubernetes or runs a k3s container. OpenShell targets a reachable gateway endpoint backed by Docker, Podman, Kubernetes, or the experimental VM driver. +Diagnose a gateway and its selected compute platform. Do not assume OpenShell provisions Kubernetes or runs a k3s container. OpenShell targets a reachable gateway endpoint backed by Docker, Podman, Kubernetes, the experimental VM driver, or an operator-managed out-of-tree compute driver. Use `openshell` first to identify the active endpoint. Then use the platform tools that match the gateway's compute driver: `docker`, `podman`, `kubectl`/`helm`, or VM driver logs. @@ -15,7 +15,7 @@ The target deployment flow is: 1. Operator starts or deploys the gateway with system packages, systemd, Helm, or a development task. The CLI does not start, stop, or destroy gateway services. 2. Operator configures the compute driver. -3. Operator provides TLS and SSH relay material for the deployment mode. +3. Operator provides the CLI and supervisor authentication material required by the deployment mode: edge or OIDC user auth, optional CLI mTLS, and gateway-minted sandbox JWTs. 4. The CLI registers a reachable gateway endpoint with `openshell gateway add`. 5. The gateway creates sandboxes through the selected compute driver. @@ -25,7 +25,7 @@ For local evaluation only, TLS may be disabled and the gateway can be reached th - The `openshell` CLI must be available for endpoint checks. - Know the active gateway name and endpoint, or be able to inspect local gateway metadata. -- Know the compute platform: Docker, Podman, Kubernetes, or VM. +- Know the compute platform: Docker, Podman, Kubernetes, VM, or an out-of-tree driver. - For Kubernetes: `kubectl` must target the cluster that hosts OpenShell and Helm version 3 or later must be available. - For Docker or Podman: the runtime socket must be reachable from the gateway host. @@ -36,15 +36,24 @@ Run diagnostics in order and stop once the root cause is clear. ### Step 1: Check CLI Reachability ```bash +openshell gateway list --output json openshell gateway info openshell status ``` +For a one-off endpoint check that bypasses stored gateway selection and metadata: + +```bash +openshell --gateway-endpoint status +``` + Common findings: - `No active gateway`: register one with `openshell gateway add `. - Connection refused: gateway process is not running, service exposure is wrong, or a port-forward/proxy is not active. -- TLS/certificate errors: CLI mTLS bundle does not match the gateway CA, or the gateway is running with unexpected TLS settings. +- TLS/certificate errors: the endpoint scheme or trust chain is wrong, a local mTLS bundle does not match the gateway CA, or TLS termination does not match the gateway listener. +- `Unauthenticated` from an edge or OIDC gateway: refresh stored credentials with `openshell gateway login [name]`, then retry. Use `gateway logout` only when intentionally clearing local credentials. +- A direct development endpoint with a private or self-signed certificate can be isolated with `--gateway-endpoint --gateway-insecure`; do not persist or recommend insecure verification for shared gateways. ### Step 2: Identify the Compute Platform @@ -56,8 +65,48 @@ Use gateway metadata, deployment values, or the user's setup notes to identify t | Podman | Podman socket, rootless networking, sandbox containers, image pulls. | | Kubernetes | Helm release, gateway workload, service, secrets, sandbox pods, events. | | VM | VM driver logs, rootfs availability, host virtualization support. | +| Extension | External driver process, Unix socket ownership/mode, configured driver name, capability handshake, gateway logs. | + +### Step 3: Check Gateway Startup Dependencies + +Before debugging the compute platform, inspect gateway logs for failures in dependencies initialized before the listener becomes ready. + +For out-of-tree compute drivers, confirm the custom driver name and socket agree across CLI flags or `gateway.toml`, and that the operator-owned driver is running before the gateway starts: + +```bash +rg -n 'compute_drivers|socket_path' /etc/openshell/gateway.toml +stat /run/openshell/.sock +journalctl -u --no-pager --lines=200 +journalctl -u openshell-gateway --no-pager --lines=200 +``` + +The custom driver name must not be a reserved built-in name (`docker`, `podman`, `kubernetes`, or `vm`). The socket must be accessible only to the intended gateway identity. Check gateway logs for connection errors, `GetCapabilities` failures, or an unexpected advertised driver name. The gateway does not create or supervise out-of-tree driver processes or sockets. + +For configured gateway interceptors, inspect `[[openshell.gateway.interceptors]]`, their Unix or network endpoints, and gateway startup logs: + +```bash +rg -n 'interceptors|provider_profile_sources|grpc_endpoint|binding_policy|failure_policy' /etc/openshell/gateway.toml +stat /run/openshell/interceptors/.sock +journalctl -u --no-pager --lines=200 +journalctl -u openshell-gateway --no-pager --lines=200 +``` + +The gateway calls each interceptor's `Describe` RPC and validates its manifest at startup. Check for unreachable endpoints, invalid RPC/phase bindings, strict `allowlist` or `exact` mismatches, and `post_commit` bindings that resolve to `fail_closed`. If `provider_profile_sources` names an interceptor, that interceptor must advertise provider-profile capability and return a valid, duplicate-free catalog. A selected interceptor-only source is authoritative; include `builtin` or `user` sources explicitly when composition is intended. + +For operator-run supervisor middleware, inspect `[[openshell.supervisor.middleware]]`, service reachability, and both gateway and supervisor logs: + +```bash +rg -n 'supervisor|middleware|grpc_endpoint|max_body_bytes|timeout' /etc/openshell/gateway.toml +journalctl -u --no-pager --lines=200 +journalctl -u openshell-gateway --no-pager --lines=200 +openshell logs --tail --source sandbox +``` + +The middleware service must start before the gateway and be reachable from both the gateway and sandbox supervisors. Gateway startup fails if `Describe` is unavailable, a manifest exposes duplicate `HttpRequest/pre_credentials` bindings, the registration claims the reserved `openshell/` namespace, or body and timeout limits are invalid. Changing a registration requires a gateway restart. A policy update can also fail before persistence if the selected implementation rejects its `network_middlewares` config. + +At request time, distinguish an explicit `middleware_denied` result from `middleware_failed`. A denial is always enforced. A failure follows the policy-local `on_error`: `fail_closed` blocks the request, while `fail_open` bypasses only that stage and emits a detection finding. If a running supervisor cannot install a new registry, it preserves its last-known-good generation and emits a configuration failure event. -### Step 3: Check Docker-Backed Gateways +### Step 4: Check Docker-Backed Gateways ```bash docker info @@ -110,7 +159,7 @@ For source checkout development, restart the local gateway with: mise run gateway:docker ``` -### Step 4: Check Podman-Backed Gateways +### Step 5: Check Podman-Backed Gateways ```bash podman info @@ -126,7 +175,7 @@ Common findings: - Sandbox image missing or pull denied: verify image reference and registry credentials. - Supervisor cannot call back: check callback endpoint and gateway logs. -### Step 5: Check Kubernetes Helm Gateways +### Step 6: Check Kubernetes Helm Gateways ```bash helm -n openshell status openshell @@ -319,7 +368,7 @@ kubectl -n logs -c openshell-supervisor-networ kubectl -n logs -c agent --tail=200 ``` -### Step 6: Check VM-Backed Gateways +### Step 7: Check VM-Backed Gateways Use the VM driver logs and host diagnostics available in the user's environment. Verify: @@ -346,6 +395,13 @@ openshell logs | Kubernetes gateway pod pending | PVC unbound, taint, selector, or insufficient resources | `kubectl -n openshell describe pod ` | | Kubernetes gateway pod crash loops | Missing secret, bad DB URL, bad TLS config | `kubectl -n openshell logs deployment/openshell -c openshell-gateway` or `kubectl -n openshell logs statefulset/openshell -c openshell-gateway` | | CLI TLS error | Local mTLS bundle does not match server cert/CA | Check `~/.config/openshell/gateways//mtls/` | +| Edge or OIDC gateway returns `Unauthenticated` | Stored login expired, audience/scopes mismatch, or gateway auth configuration changed | `openshell gateway info`, `openshell gateway login `, gateway auth logs | +| Gateway fails before serving health after enabling an interceptor | Interceptor endpoint unavailable or manifest/binding validation failed | Gateway and interceptor logs; interceptor socket; `binding_policy`, phases, and failure policy | +| Provider profiles disappear after enabling an interceptor catalog | `provider_profile_sources` selected only an authoritative interceptor or returned invalid/duplicate IDs | Inspect source list and interceptor `Describe`/catalog logs; include `builtin` and `user` when intended | +| Gateway fails after registering supervisor middleware | Service unavailable, invalid manifest, duplicate binding, reserved name, or invalid body/timeout limit | Middleware service and gateway logs; `[[openshell.supervisor.middleware]]`; `Describe` response | +| Policy update rejects `network_middlewares` | Unknown middleware name, implementation-owned config invalid, duplicate order, broad/invalid host selector, or fail-closed coverage of `tls: skip` | Policy error, gateway logs, middleware `ValidateConfig`, selector and order fields | +| HTTP request returns `middleware_failed` or `middleware_denied` | Selected stage failed or explicitly denied the admitted request | Sandbox OCSF logs; policy-local middleware config; service availability; `on_error` | +| Custom compute driver is unavailable | Driver process/socket missing, inaccessible, or configured with a reserved/mismatched name | Socket ownership/mode, driver service logs, gateway `GetCapabilities` logs | | Image pull failure | Gateway or sandbox image cannot be pulled | Runtime events and image pull credentials | | `K8s namespace not ready` with `envoy-gateway-openshell.yaml: the server could not find the requested resource` | Optional Gateway API manifest was applied without Envoy Gateway CRDs, or k3s Helm controller startup exceeded the namespace wait | Apply `deploy/kube/manifests/envoy-gateway-openshell.yaml` manually only after Envoy Gateway is installed and `grpcRoute` is enabled | | HTTPS ingress (`grpcRoute.gateway.listener.protocol=HTTPS`) connection resets or TLS handshake hangs | Envoy terminates TLS but the gateway pod still expects TLS, so the plaintext backend hop fails | Set `server.disableTls=true` so Envoy forwards plaintext to the pod; verify the listener `certificateRefs` Secret exists in the release namespace and `openshell status` over `https://` | @@ -359,7 +415,7 @@ When handing results back to the user, include: - Compute platform and driver. - Gateway process or workload status. - Recent gateway log summary. -- Missing or malformed TLS or SSH relay material. +- Missing or malformed TLS, OIDC/mTLS, or sandbox JWT material. - Service exposure status. - Sandbox workload status. - The exact command that failed and the shortest fix. diff --git a/.agents/skills/generate-sandbox-policy/SKILL.md b/.agents/skills/generate-sandbox-policy/SKILL.md index 97f6dbe31b..8da14420c9 100644 --- a/.agents/skills/generate-sandbox-policy/SKILL.md +++ b/.agents/skills/generate-sandbox-policy/SKILL.md @@ -1,17 +1,17 @@ --- name: generate-sandbox-policy -description: Generate sandbox security policies from plain-language requirements and optional REST API documentation. At minimum, takes API host:port endpoints and intent to produce preset-based or L4 policies. With full API docs (OpenAPI, Swagger, markdown), generates fine-grained per-endpoint L7 rules. Trigger keywords - generate policy, create policy, update policy, change policy, sandbox policy, network policy, API policy, security policy, allow API, restrict API. +description: Generate sandbox security policies from plain-language requirements and optional REST API documentation. Produces L4 or fine-grained L7 network policies and ordered network middleware configuration. Use for API access rules, middleware host selection, failure behavior, or built-in and operator-run middleware attachment. Trigger keywords - generate policy, create policy, update policy, change policy, sandbox policy, network policy, API policy, security policy, allow API, restrict API, network middleware, supervisor middleware. --- # Generate Sandbox Policy -Generate YAML sandbox network policies from REST API documentation and natural-language user requirements. +Generate YAML sandbox network policies and network middleware configuration from API documentation and natural-language user requirements. ## Overview This skill translates a user's plain-language policy intent into a valid sandbox policy. The amount of detail the user provides determines the granularity of the generated policy — from broad L4 or preset-based policies (just a host:port) up to fine-grained per-endpoint L7 rules (full API docs). -The output is a `network_policies` YAML block (and optionally a full policy file) that conforms to the sandbox policy schema. +The output is a `network_policies` YAML block, an optional `network_middlewares` block, and optionally a full policy file that conforms to the sandbox policy schema. ## Step 1: Gather Inputs @@ -79,6 +79,7 @@ Regardless of tier, extract (or infer) these from the user's description: | **Paths** | Specific URL paths or patterns | Only for custom/fine-grained | | **Enforcement** | `enforce` or `audit`? Default to `enforce`. | No — has a default | | **Binary** | Which binary/process should have access | Yes — ask if not stated | +| **Middleware** | Whether admitted HTTP requests need an ordered built-in or operator-run processing stage | No | If the host and access level are clear but binaries are not specified, ask the user which binary or process will be making the requests. Suggest common defaults like `/usr/bin/curl`, `/usr/local/bin/claude`, etc. @@ -165,8 +166,15 @@ Key sections to reference: - **`L7Rule` / `L7Allow`** — method + path matching - **Access Presets** — `read-only`, `read-write`, `full` - **Private IP Access via `allowed_ips`** — CIDR allowlist for private IP space +- **Network Middleware** - top-level middleware configs, ordering, host selection, and failure behavior - **Validation Rules** — what combinations are valid/invalid +When middleware is requested, also read the full operational reference: + +``` +Read docs/extensibility/supervisor-middleware.mdx +``` + Also read the architecture overview for enforcement context. The default policy is baked into the community base image (`ghcr.io/nvidia/openshell-community/sandboxes/base:latest`). For reference, consult: ``` @@ -206,6 +214,17 @@ Is L7 inspection needed? **Critical**: `protocol: rest` on port 443 without `tls: terminate` will not work — the proxy cannot inspect encrypted traffic. Always set `tls: terminate` when combining port 443 with L7 rules. +### Middleware Decision + +Add `network_middlewares` only when the user asks to inspect, transform, redact, or independently authorize admitted HTTP requests. Middleware runs after network and L7 policy admission and before provider credential injection. + +- Use a built-in name such as `openshell/regex` without gateway registration. +- Use an operator-owned middleware name only when it is already registered under `[[openshell.supervisor.middleware]]` and reachable from both the gateway and sandbox supervisors. +- Default `on_error` to `fail_closed`. Use `fail_open` only when bypassing the stage preserves the user's stated security requirement. +- Assign unique `order` values across the complete policy. Lower values run first, and at most 10 configs may be selected. +- Match the narrowest destination hosts possible with `endpoints.include`; use `exclude` when a broad selector has trusted exceptions. +- Do not select fail-closed middleware for `tls: skip` endpoints because the supervisor cannot inspect that traffic. + ### Mapping Paths to Glob Patterns (when building explicit rules) Only needed for the **Moderate** and **Full** tiers. Translate API path parameters to glob patterns: @@ -266,6 +285,23 @@ network_policies: - { path: } ``` +When middleware is requested, add it as a separate top-level map rather than nesting it under a network policy: + +```yaml +network_middlewares: + : + name: + middleware: + order: 10 + config: {} + on_error: fail_closed + endpoints: + include: [""] + # exclude: [""] +``` + +The map key is the stable policy-local identity. Middleware selection is independent of the network policy entry that admitted the request. + ### Deny Rules Use `deny_rules` to block specific dangerous operations while allowing broad access. Deny rules are evaluated after allow rules and take precedence. This is the inverse of the `rules` approach — instead of enumerating every allowed operation, you grant broad access and block a small set of dangerous ones. @@ -337,6 +373,9 @@ Before presenting the policy to the user, verify correctness **and** flag breadt - [ ] If `tls: terminate` is set, `protocol` is also set - [ ] `rules` list is not empty when present - [ ] If `protocol: sql`, `enforcement` is not `enforce` +- [ ] Every middleware config has a non-empty `middleware` name and non-empty `endpoints.include` +- [ ] Middleware `order` values are unique and no selected chain exceeds 10 stages +- [ ] No fail-closed middleware selector can cover a `tls: skip` endpoint ### Schema Warnings (log-only, but should be fixed) @@ -349,6 +388,7 @@ Before presenting the policy to the user, verify correctness **and** flag breadt - [ ] Every endpoint has `host` and `port` - [ ] Every binary has `path` - [ ] Policy key matches `name` field +- [ ] Every middleware selector has at most 32 combined `include` and `exclude` patterns ### Breadth Warnings @@ -365,6 +405,8 @@ Evaluate the generated policy for overly broad access and **include warnings in | **Multiple broad endpoints** in one policy | "This policy grants the same broad access to N different hosts. If any of these hosts needs tighter restrictions later, you'll need to split the policy." | | **Hostless `allowed_ips`** (no `host` field) | "This endpoint has no `host` — any domain resolving to the allowed IP range on this port will be permitted. Consider adding a `host` field to restrict which domains can use this allowlist." | | **Broad CIDR** in `allowed_ips` (e.g., `10.0.0.0/8`) | "This `allowed_ips` entry covers a very broad range. Consider narrowing to a specific subnet (e.g., `10.0.5.0/24`) to minimize exposure." | +| **`on_error: fail_open`** | "This middleware can be bypassed when it is unavailable, rejects configuration, returns an invalid result, or exceeds its body limit. Use `fail_closed` unless availability is more important than this control." | +| **Broad middleware host selector** | "This middleware applies independently of the admitting network rule to every matching HTTP destination. Narrow `endpoints.include` or add exclusions if the stage is not required for every matching host." | Format breadth warnings clearly in the output, e.g.: diff --git a/.agents/skills/launch-openshell-gator/SKILL.md b/.agents/skills/launch-openshell-gator/SKILL.md index bad9441567..20f902f085 100644 --- a/.agents/skills/launch-openshell-gator/SKILL.md +++ b/.agents/skills/launch-openshell-gator/SKILL.md @@ -123,11 +123,11 @@ pr_number="" [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } ``` -Use a restricted sandbox-name character set: +Use the portable Kubernetes DNS-1123 sandbox-name format even when the selected gateway currently uses another driver: ```bash sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ``` For local image contexts passed to `--from`, use an agent-created path such as `mktemp -d`; do not pass raw user-supplied paths without validating that they are expected local Dockerfile contexts. @@ -144,7 +144,7 @@ pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -165,7 +165,7 @@ issue_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$issue_number" =~ ^[0-9]+$ ]] || { echo "invalid issue number" >&2; exit 1; } sandbox_name="gator-issue-${issue_number}-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -186,7 +186,7 @@ issue_number="" [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } [[ "$issue_number" =~ ^[0-9]+$ ]] || { echo "invalid issue number" >&2; exit 1; } sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -207,7 +207,7 @@ pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } sandbox_name="gator-pr-${pr_number}-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } ./scripts/agents/run.sh \ --agent gator \ @@ -228,7 +228,7 @@ pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } sandbox_name="gator-pr-${pr_number}-gpt56sol-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } CODEX_MODEL=gpt-5.6-sol \ ./scripts/agents/run.sh \ @@ -250,7 +250,7 @@ pr_number="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } [[ "$pr_number" =~ ^[0-9]+$ ]] || { echo "invalid PR number" >&2; exit 1; } sandbox_name="gator-pr-${pr_number}-gpt56sol-supervised" -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } tmp_context="$(mktemp -d "${TMPDIR:-/tmp}/gator-codex-XXXXXX")" cp -R scripts/agents/gator/. "$tmp_context"/ @@ -290,7 +290,7 @@ Read that file directly. Important markers: gateway_name="" sandbox_name="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } openshell --gateway "$gateway_name" sandbox list openshell --gateway "$gateway_name" sandbox get "$sandbox_name" @@ -318,7 +318,7 @@ Before deleting, check that the sandbox is truly stale or that the operator aske gateway_name="" sandbox_name="" [[ "$gateway_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid gateway name" >&2; exit 1; } -[[ "$sandbox_name" =~ ^[A-Za-z0-9_.-]+$ ]] || { echo "invalid sandbox name" >&2; exit 1; } +[[ "$sandbox_name" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]] || { echo "invalid sandbox name" >&2; exit 1; } openshell --gateway "$gateway_name" sandbox delete "$sandbox_name" ./scripts/agents/run.sh \ diff --git a/.agents/skills/openshell-cli/SKILL.md b/.agents/skills/openshell-cli/SKILL.md index a559058917..a97b6a7af2 100644 --- a/.agents/skills/openshell-cli/SKILL.md +++ b/.agents/skills/openshell-cli/SKILL.md @@ -1,6 +1,6 @@ --- name: openshell-cli -description: Guide agents through using the OpenShell CLI (openshell) for sandbox management, gateway registration, provider configuration, policy iteration, BYOC workflows, and inference routing. Covers basic through advanced multi-step workflows. Trigger keywords - openshell, sandbox create, sandbox connect, logs, provider create, policy set, policy get, image push, forward, port forward, BYOC, bring your own container, use openshell, run openshell, CLI usage, manage sandbox, manage provider, gateway add, gateway select. +description: Guide agents through using the OpenShell CLI (openshell) for sandbox management, gateway registration, provider configuration and refresh, policy iteration, settings, service exposure, BYOC workflows, and inference routing. Covers basic through advanced multi-step workflows. Trigger keywords - openshell, sandbox create, sandbox exec, sandbox connect, logs, provider create, provider profile, provider refresh, policy set, policy get, settings, service expose, forward, port forward, BYOC, bring your own container, inference, use openshell, run openshell, CLI usage, manage sandbox, manage provider, gateway add, gateway select. --- # OpenShell CLI @@ -9,7 +9,7 @@ Guide agents through using the `openshell` CLI for sandbox and platform manageme ## Overview -The OpenShell CLI (`openshell`) is the primary interface for managing sandboxes, providers, policies, inference routes, and gateway registrations. Gateway service lifecycle is handled outside the CLI by packages, systemd, Helm, or development tasks. This skill teaches agents how to orchestrate CLI commands for common and complex workflows. +The OpenShell CLI (`openshell`) is the primary interface for managing sandboxes, providers, policies, settings, exposed services, inference routes, and gateway registrations. Gateway service lifecycle is handled outside the CLI by packages, systemd, Helm, or development tasks. This skill teaches agents how to orchestrate CLI commands for common and complex workflows. **Companion skill**: For creating or modifying sandbox policy YAML content (network rules, L7 inspection, access presets), use the `generate-sandbox-policy` skill. This skill covers the CLI *commands* for the policy lifecycle; `generate-sandbox-policy` covers policy *content authoring*. @@ -32,7 +32,7 @@ This is your primary fallback. Use it freely -- the CLI's help output is authori ## Command Reference -See [cli-reference.md](cli-reference.md) for the full command tree with all flags and options. Use it as a quick-reference to avoid round-tripping through `--help` for common commands. +See [cli-reference.md](cli-reference.md) for the current command tree and commonly used flags. Use it as a quick-reference, then confirm uncommon or security-sensitive options with `--help`. --- @@ -66,6 +66,8 @@ openshell sandbox create This creates a sandbox with defaults and drops you into an interactive shell. +When supplying `--name`, use a portable DNS-1123 label: at most 63 lowercase alphanumeric or `-` characters, beginning and ending with an alphanumeric character. The Kubernetes driver rejects uppercase letters, underscores, dots, and other names that cannot become Kubernetes resource labels. + **Shortcut for known tools**: When the trailing command is a recognized tool, the CLI auto-creates the required provider from local credentials: ```bash @@ -87,9 +89,12 @@ openshell sandbox delete ## Workflow 2: Provider Management -Providers supply credentials to sandboxes (API keys, tokens, etc.). Manage them before creating sandboxes that need them. +Providers supply credentials and provider-specific configuration to sandboxes. Provider types come from built-in and custom profiles; do not rely on a hard-coded type list. Discover the profiles available on the selected gateway: -Supported types: `claude`, `opencode`, `codex`, `generic`, `nvidia`, `gitlab`, `github`, `outlook`. +```bash +openshell provider list-profiles +openshell provider list-profiles --output json +``` ### Create a provider from local credentials @@ -103,25 +108,57 @@ The `--from-existing` flag discovers credentials from local state (e.g., `gh aut ```bash openshell provider create --name my-api --type generic \ - --credential API_KEY=sk-abc123 \ + --credential API_KEY \ --config base_url=https://api.example.com ``` -Bare `KEY` (without `=VALUE`) reads the value from the environment variable of that name: +Bare `KEY` reads the value from the environment variable of that name and avoids placing the secret in shell history. Use `KEY=VALUE` only when the user explicitly accepts that exposure. + +Other credential sources are `--from-gcloud-adc` for compatible profiles and `--runtime-credentials` when the gateway or sandbox resolves the required credentials at runtime. + +Profile-backed provider policy and composition are controlled by the gateway-global `providers_v2_enabled` setting: ```bash -openshell provider create --name my-api --type generic --credential API_KEY +openshell settings get --global +openshell settings set --global --key providers_v2_enabled --value true +``` + +### Inspect and manage provider profiles + +```bash +openshell provider profile export github --output yaml +openshell provider profile lint --file ./my-profile.yaml +openshell provider profile import --file ./my-profile.yaml ``` ### List, inspect, update, delete ```bash openshell provider list +openshell provider list --output json openshell provider get my-github -openshell provider update my-github --type github --from-existing +openshell provider update my-github --from-existing openshell provider delete my-github ``` +`provider update` does not take `--type`. It updates credentials, config, or credential expiry on the existing provider. + +### Configure credential refresh + +Use refresh commands only when the provider profile and gateway support refreshable credentials: + +```bash +openshell provider refresh status my-outlook +openshell provider refresh configure my-outlook \ + --credential-key MS_GRAPH_ACCESS_TOKEN \ + --strategy oauth2-refresh-token \ + --secret-material-env REFRESH_TOKEN=MS_GRAPH_REFRESH_TOKEN \ + --credential-expires-at 2026-07-16T00:00:00Z +openshell provider refresh rotate my-outlook --credential-key MS_GRAPH_ACCESS_TOKEN +``` + +Prefer `--secret-material-env KEY[=ENVVAR]` for secret refresh material. `--material KEY=VALUE` is for non-secret material; `--secret-material-key` marks supplied material keys as secret. + --- ## Workflow 3: Sandbox Lifecycle @@ -134,29 +171,41 @@ openshell sandbox create \ --provider my-github \ --provider my-claude \ --policy ./my-policy.yaml \ - --upload .:/sandbox \ + --upload .:/workspace \ + --label team=agents \ -- claude ``` Key flags: - `--provider`: Attach one or more providers (repeatable) - `--policy`: Custom policy YAML (otherwise uses built-in default or `OPENSHELL_SANDBOX_POLICY` env var) +- `--gpu [COUNT]`: Request the driver's default GPU selection or a specific GPU count - `--cpu`, `--memory`: Set per-sandbox compute sizing. Docker/Podman apply limits; Kubernetes applies matching requests and limits. -- `--upload [:]`: Upload local files into the sandbox (default dest: `/sandbox`) +- `--driver-config-json`: Pass experimental driver-specific sandbox configuration +- `--label KEY=VALUE`: Add labels for later selection (repeatable) +- `--env KEY=VALUE`: Inject sandbox environment variables (repeatable) +- `--approval-mode manual|auto`: Control handling of agent-authored policy proposals; `manual` is the default +- `--upload [:]`: Upload local files into the container working directory or an explicit destination +- `--no-git-ignore`: Disable `.gitignore` filtering for uploads - `--no-keep`: Delete the sandbox after the initial command or shell exits -- `--forward `: Forward a local port and keep the sandbox alive +- `--forward [BIND_ADDRESS:]PORT`: Forward a local port and keep the sandbox alive +- `--editor vscode|cursor`: Open a remote editor after creation and keep the sandbox alive ### List and inspect sandboxes ```bash openshell sandbox list +openshell sandbox list --selector team=agents --output json openshell sandbox get my-sandbox ``` +Most commands with an optional sandbox name use the last-used sandbox. Pass an explicit name in automation. + ### Connect to a running sandbox ```bash openshell sandbox connect my-sandbox +openshell sandbox connect my-sandbox --editor vscode ``` Opens an interactive SSH shell. To configure VS Code Remote-SSH: @@ -175,6 +224,27 @@ openshell sandbox upload my-sandbox ./src /sandbox/src openshell sandbox download my-sandbox /sandbox/output ./local-output ``` +Uploads honor `.gitignore` by default. Add `--no-git-ignore` only when ignored files are intentionally in scope. + +Uploads preserve symlinks, including dangling symlinks, instead of dereferencing their targets. A symlink source bypasses Git-aware filtering so the link itself is archived. + +### Execute a non-interactive command + +```bash +openshell sandbox exec --name my-sandbox --workdir /workspace -- ls -la +openshell sandbox exec --name my-sandbox --env MODE=test -- cargo test +``` + +`sandbox exec` streams output and exits with the remote command's exit code. Use `sandbox connect` for an interactive shell. + +### Change attached providers + +```bash +openshell sandbox provider list my-sandbox +openshell sandbox provider attach my-sandbox my-github +openshell sandbox provider detach my-sandbox my-github +``` + ### View logs ```bash @@ -196,6 +266,7 @@ openshell logs my-sandbox --since 5m ```bash openshell sandbox delete my-sandbox openshell sandbox delete sandbox-1 sandbox-2 sandbox-3 # Multiple at once +openshell sandbox delete --all ``` --- @@ -204,7 +275,7 @@ openshell sandbox delete sandbox-1 sandbox-2 sandbox-3 # Multiple at once This is the most important multi-step workflow. It enables a tight feedback cycle where sandbox policy is refined based on observed activity. -**Key concept**: Policies have static fields (immutable after creation: `filesystem_policy`, `landlock`, `process`) and one dynamic field (`network_policies`). Only `network_policies` can be updated without recreating the sandbox. +**Key concept**: Policies have static fields (immutable after creation: `filesystem_policy`, `landlock`, `process`) and two dynamic fields: `network_policies` and `network_middlewares`. Both dynamic fields can be updated without recreating the sandbox. ``` Create sandbox with initial policy @@ -256,19 +327,20 @@ Look for log lines with `action: deny` -- these indicate blocked network request openshell policy get dev --full > current-policy.yaml ``` -The `--full` flag outputs valid YAML that can be directly re-submitted. This is the round-trip format. +The `--full` flag includes the effective policy, including provider-composed entries. Use `--base` instead when the editable base policy is needed without provider-composed entries. Before resubmitting a `--full` result, review composed entries and prefer incremental updates or the base policy when appropriate. ### Step 4: Modify the policy Edit `current-policy.yaml` to allow the blocked actions. **For policy content authoring, delegate to the `generate-sandbox-policy` skill.** That skill handles: - Network endpoint rule structure -- L4 vs L7 policy decisions +- L4 vs REST, WebSocket, JSON-RPC, MCP, and SQL L7 policy decisions - Access presets (`read-only`, `read-write`, `full`) - TLS termination configuration - Enforcement modes (`audit` vs `enforce`) - Binary matching patterns +- Ordered `network_middlewares`, host selection, and `fail_open` or `fail_closed` behavior -Only `network_policies` can be modified at runtime. If `filesystem_policy`, `landlock`, or `process` need changes, the sandbox must be recreated. +`network_policies` and `network_middlewares` can be modified at runtime. If `filesystem_policy`, `landlock`, or `process` need changes, the sandbox must be recreated. Built-in middleware such as `openshell/regex` needs no gateway registration. An operator-run middleware must already be registered under `[[openshell.supervisor.middleware]]`; changing that static registration requires a gateway restart. ### Step 5: Push the updated policy @@ -307,27 +379,47 @@ Fetch a specific historical revision: openshell policy get dev --rev 3 --full ``` +Gateway-global policy commands use `--global` and require confirmation unless `--yes` is supplied: + +```bash +openshell policy get --global --full +openshell policy set --global --policy ./global-policy.yaml +openshell policy list --global +openshell policy delete --global +``` + +Avoid `--yes` during interactive work. A global policy locks policy control for all sandboxes on the gateway. + +### Review agent-authored rule proposals + +Sandboxes created with `--approval-mode manual` place every proposal in the review inbox. `auto` approves only proposals with an empty prover delta; findings still require review. + +```bash +openshell rule get dev --status pending +openshell rule approve dev --chunk-id +openshell rule reject dev --chunk-id --reason "too broad" +openshell rule history dev +``` + +Review the proposed scope and prover findings before approval. Treat `rule approve-all --include-security-flagged` as a high-risk bulk action. + --- ## Workflow 5: BYOC (Bring Your Own Container) Build a custom container image and run it as a sandbox. -### Step 1: Create a sandbox from a Dockerfile +### Create a sandbox from a Dockerfile ```bash openshell sandbox create --from ./Dockerfile --name my-app ``` -The `--from` flag accepts a Dockerfile path, a directory containing a Dockerfile, a full image reference (e.g. `myregistry.com/img:tag`), or a community sandbox name (e.g. `openclaw`). +The `--from` flag accepts a Dockerfile path, a directory containing a Dockerfile, a full image reference such as `myregistry.com/img:tag`, or a community sandbox name such as `ollama`. -When given a Dockerfile or directory, the image is built locally via Docker and delivered through the selected compute driver. Docker and Podman-backed gateways can use local images directly. Kubernetes gateways usually need the image available to the cluster through a registry or driver-supported image push path. +Local Dockerfile and directory builds require a local gateway because the CLI builds through the local Docker daemon. Use a registry image reference for remote gateways. Bare community names resolve under `ghcr.io/nvidia/openshell-community/sandboxes` unless `OPENSHELL_COMMUNITY_REGISTRY` overrides the prefix. -When `--from` is specified, the CLI: -- Clears default `run_as_user`/`run_as_group` (custom images may not have the `sandbox` user) -- Uses a supervisor bootstrap pattern (init container copies the sandbox supervisor into a shared volume) - -### Step 2: Forward ports (if the container runs a service) +### Forward ports ```bash # Foreground (blocks) @@ -339,161 +431,118 @@ openshell forward start 8080 my-app -d The service is now reachable at `localhost:8080`. -### Step 3: Manage port forwards +Manage or iterate on the sandbox: ```bash -# List active forwards openshell forward list - -# Stop a forward openshell forward stop 8080 my-app -``` - -### Step 4: Iterate - -To update the container: - -```bash openshell sandbox delete my-app openshell sandbox create --from ./Dockerfile --name my-app --forward 8080 ``` -### Shortcut: Create with port forward in one command +Create and forward in one command: ```bash openshell sandbox create --from ./Dockerfile --forward 8080 -- ./start-server.sh ``` -The `--forward` flag starts a background port forward before the command runs, so the service is reachable immediately. - -### Limitations - -- Distroless / `FROM scratch` images are not supported (the supervisor needs glibc, `/proc`, and a shell) -- Missing `iproute2` or required capabilities blocks startup in proxy mode - ---- +The `--forward` flag starts a background port forward before the command runs. ## Workflow 6: Agent-Assisted Sandbox Session -This workflow supports a human working in a sandbox while an agent monitors activity and refines the policy in parallel. +Support a human working in a sandbox while an agent monitors activity and refines the policy in parallel. -### Step 1: Create sandbox with providers and keep alive +Create the sandbox and keep it alive: ```bash openshell sandbox create \ --name work-session \ --provider github \ --provider claude \ - --policy ./dev-policy.yaml \ - # sandbox create keeps the sandbox alive by default + --policy ./dev-policy.yaml ``` -### Step 2: User connects in a separate shell - -Tell the user to run: +Tell the user to connect in another shell: ```bash openshell sandbox connect work-session +openshell sandbox connect work-session --editor vscode ``` -Or for VS Code: - -```bash -openshell sandbox ssh-config work-session >> ~/.ssh/config -# Then connect via VS Code Remote-SSH to the host "work-session" -``` - -### Step 3: Agent monitors logs - -While the user works, monitor the sandbox logs: +Monitor denied activity: ```bash openshell logs work-session --tail --source sandbox --level warn ``` -Watch for `deny` actions that indicate the user's work is being blocked by policy. - -### Step 4: Agent refines policy - -When denied actions are observed: +When denied actions appear: 1. Prefer incremental updates for additive network changes: `openshell policy update work-session --add-endpoint api.github.com:443:read-only:rest:enforce --binary /usr/bin/gh --wait` `openshell policy update work-session --add-allow 'api.github.com:443:POST:/repos/*/issues' --wait` -2. Use full YAML replacement when the change is broad or touches non-network fields: +2. Use full YAML replacement for broad changes or non-network fields: `openshell policy get work-session --full > policy.yaml` - Modify the policy to allow the blocked actions (use `generate-sandbox-policy` skill for content) + Modify the policy with the `generate-sandbox-policy` skill. `openshell policy set work-session --policy policy.yaml --wait` -3. Verify: `openshell policy list work-session` +3. Verify with `openshell policy list work-session`. -The user does not need to disconnect -- policy updates are hot-reloaded within ~30 seconds (or immediately when using `--wait`, which polls for confirmation). - -### Step 5: Clean up when done +The user does not need to disconnect. Policy updates are hot-reloaded; `--wait` blocks until the sandbox confirms the revision or the timeout expires. Delete the sandbox when the session ends: ```bash openshell sandbox delete work-session ``` ---- - -## Workflow 7: Gateway Inference +## Workflow 7: Managed Inference -Configure the gateway's managed inference route for `inference.local`. +Configure the user-facing `inference.local` route or the system inference route used by platform functions. -### Set gateway inference - -First ensure the provider record exists: +Ensure the provider exists, then set the route: ```bash openshell provider list -``` - -Then point gateway inference at that provider and model: - -```bash openshell inference set \ --provider nvidia \ --model nvidia/nemotron-3-nano-30b-a3b ``` -This updates the gateway-managed `inference.local` route. There is no per-route create/list/update/delete workflow for sandbox inference. +This updates the managed `inference.local` route. Endpoint verification runs before the route is saved. Use `--no-verify` only when verification is intentionally impossible, and use `--timeout SECONDS` to configure the request timeout. Add `--system` to `set` or `update` for the platform-only system route. -### Inspect current inference config +Inspect both configurations: ```bash openshell inference get +openshell inference get --system ``` -### How sandboxes use it - -- Agents send HTTPS requests to `inference.local`. -- The sandbox intercepts those requests locally and routes them through the gateway inference config. -- Sandbox policy is separate from gateway inference configuration. - ---- +Agents send HTTPS requests to `inference.local`; the sandbox intercepts them and routes them through the configured inference route. Sandbox policy remains separate from inference route configuration. ## Workflow 8: Gateway Management -### List and switch gateways +List, switch, and verify gateways: ```bash -openshell gateway select # See all gateways (no args shows list) -openshell gateway select production # Switch active gateway -openshell status # Verify connectivity +openshell gateway select +openshell gateway list --output json +openshell gateway select production +openshell gateway info --name production +openshell status ``` -### Registration +Register or remove gateways: ```bash openshell gateway add http://127.0.0.1:8080 --local --name local openshell gateway add https://gateway.example.com --name production -openshell gateway remove local # Remove local registration +openshell gateway remove local ``` -### Platform-specific deployment inspection +`https://` registrations default to edge authentication. Use `gateway login` and `gateway logout` to refresh or clear stored authentication. For an OIDC gateway, supply `--oidc-issuer` and, when needed, `--oidc-client-id`, `--oidc-audience`, and `--oidc-scopes`. For remote mTLS gateways, use `--remote USER@HOST` or an `ssh://` endpoint. + +For one-off automation, `--gateway-endpoint URL` connects directly without stored metadata. Limit `--gateway-insecure` to explicitly trusted development endpoints. + +Inspect a Kubernetes deployment: ```bash -# Inspect a Kubernetes Helm release and gateway pod helm -n openshell status openshell kubectl -n openshell get deployment,statefulset,pods,svc kubectl -n openshell logs deployment/openshell -c openshell-gateway --tail=100 @@ -502,6 +551,41 @@ kubectl -n openshell logs statefulset/openshell -c openshell-gateway --tail=100 For Docker, Podman, and VM-backed gateways, inspect the gateway process or container logs and the selected runtime directly. +## Workflow 9: Settings Management + +Manage sandbox-scoped or gateway-global settings: + +```bash +openshell settings get work-session +openshell settings set work-session --key ocsf_json_enabled --value true +openshell settings delete work-session --key ocsf_json_enabled + +openshell settings get --global --json +openshell settings set --global --key providers_v2_enabled --value true +``` + +Global mutations prompt for confirmation. Use `--yes` only in reviewed automation. + +## Workflow 10: Service Access + +Use `forward` for local access and `service` for a gateway-managed HTTP endpoint: + +```bash +# SSH-based same-port forwarding; optional bind address is accepted. +openshell forward start 127.0.0.1:8080 my-app -d + +# gRPC relay to a loopback TCP service, with an optional dynamic local port. +openshell forward service my-app --target-port 8000 --local 127.0.0.1:0 + +# Expose and manage an HTTP service through the gateway. +openshell service expose my-app 8080 web +openshell service list my-app +openshell service get my-app web +openshell service delete my-app web +``` + +Prefer loopback binds unless the user explicitly needs LAN-visible local access. + --- ## Self-Teaching via `--help` @@ -518,7 +602,7 @@ The CLI help is always authoritative. If the help output contradicts this skill, ```bash $ openshell sandbox --help -# Shows: create, get, list, delete, connect, upload, download, ssh-config, image +# Shows: create, get, list, delete, exec, connect, upload, download, ssh-config, provider $ openshell sandbox upload --help # Shows: positional arguments (name, path, dest), usage examples @@ -533,23 +617,32 @@ $ openshell sandbox upload --help | Register local port-forwarded gateway | `openshell gateway add http://127.0.0.1:8080 --local --name local` | | Check gateway health | `openshell status` | | List/switch gateways | `openshell gateway select [name]` | +| Connect directly to a gateway | `openshell --gateway-endpoint status` | | Create sandbox (interactive) | `openshell sandbox create` | | Create sandbox with tool | `openshell sandbox create -- claude` | +| Create sandbox with GPUs | `openshell sandbox create --gpu 1` | | Create with custom policy | `openshell sandbox create --policy ./p.yaml` | | Connect to sandbox | `openshell sandbox connect ` | +| Execute in sandbox | `openshell sandbox exec --name -- ` | | Stream live logs | `openshell logs --tail` | | Incremental policy update | `openshell policy update --add-endpoint host:443:read-only:rest:enforce --binary /usr/bin/curl --wait` | | Pull current policy | `openshell policy get --full > p.yaml` | | Push updated policy | `openshell policy set --policy p.yaml --wait` | | Policy revision history | `openshell policy list ` | +| View global policy | `openshell policy get --global --full` | +| Review proposed rules | `openshell rule get --status pending` | | Create sandbox from Dockerfile | `openshell sandbox create --from ./Dockerfile` | | Forward a port | `openshell forward start -d` | +| Expose an HTTP service | `openshell service expose [service]` | | Upload files to sandbox | `openshell sandbox upload ` | | Download files from sandbox | `openshell sandbox download ` | | Create provider | `openshell provider create --name N --type T --from-existing` | | List providers | `openshell provider list` | -| Configure gateway inference | `openshell inference set --provider P --model M` | -| View gateway inference | `openshell inference get` | +| Discover provider profiles | `openshell provider list-profiles` | +| List attached providers | `openshell sandbox provider list ` | +| View settings | `openshell settings get [name]` | +| Configure managed inference | `openshell inference set --provider P --model M` | +| View managed inference | `openshell inference get` | | Delete sandbox | `openshell sandbox delete ` | | Remove gateway registration | `openshell gateway remove ` | | Self-teach any command | `openshell --help` | @@ -558,7 +651,7 @@ $ openshell sandbox upload --help | Skill | When to use | |-------|------------| -| `generate-sandbox-policy` | Creating or modifying policy YAML content (network rules, L7 inspection, access presets, endpoint configuration) | +| `generate-sandbox-policy` | Creating or modifying policy YAML content (network rules, L7 inspection, access presets, endpoint configuration, and network middleware) | | `debug-openshell-cluster` | Diagnosing gateway deployment, runtime, or health failures | | `debug-inference` | Diagnosing `inference.local`, host-backed local inference, and provider base URL issues | | `tui-development` | Developing features for the OpenShell TUI (`openshell term`) | diff --git a/.agents/skills/openshell-cli/cli-reference.md b/.agents/skills/openshell-cli/cli-reference.md index 7998502326..1135fe630d 100644 --- a/.agents/skills/openshell-cli/cli-reference.md +++ b/.agents/skills/openshell-cli/cli-reference.md @@ -10,13 +10,19 @@ Quick-reference for the `openshell` command-line interface. For workflow guidanc |------|-------------| | `-v`, `--verbose` | Increase verbosity (`-v` = info, `-vv` = debug, `-vvv` = trace) | | `-g`, `--gateway ` | Gateway to operate on. Also settable via `OPENSHELL_GATEWAY` env var. Falls back to active gateway in `~/.config/openshell/active_gateway`. | +| `--gateway-endpoint ` | Connect directly to a gateway endpoint without looking up stored metadata. Also settable via `OPENSHELL_GATEWAY_ENDPOINT`. | +| `--gateway-insecure` | Skip TLS certificate verification. Also settable via `OPENSHELL_GATEWAY_INSECURE`; use only for trusted development endpoints. | ## Environment Variables | Variable | Description | |----------|-------------| | `OPENSHELL_GATEWAY` | Override active gateway name (same as `--gateway`) | +| `OPENSHELL_GATEWAY_ENDPOINT` | Connect directly to a gateway endpoint (same as `--gateway-endpoint`) | +| `OPENSHELL_GATEWAY_INSECURE` | Skip TLS verification when set (same as `--gateway-insecure`) | | `OPENSHELL_SANDBOX_POLICY` | Path to default sandbox policy YAML (fallback when `--policy` is not provided) | +| `OPENSHELL_COMMUNITY_REGISTRY` | Override the community sandbox image registry prefix used by `sandbox create --from ` | +| `OPENSHELL_THEME` | TUI theme: `auto`, `dark`, or `light` | --- @@ -39,29 +45,64 @@ openshell │ └── get ├── sandbox │ ├── create [opts] [-- CMD...] -│ ├── get +│ ├── get [name] │ ├── list [opts] -│ ├── delete ... -│ ├── connect +│ ├── delete [name]... [--all] +│ ├── exec [--name ] [opts] -- CMD... +│ ├── connect [name] [--editor ] │ ├── upload [dest] │ ├── download [dest] -│ ├── ssh-config -│ └── image -│ └── push [opts] +│ ├── ssh-config [name] +│ └── provider +│ ├── list [name] +│ ├── attach +│ └── detach ├── forward -│ ├── start [-d] -│ ├── stop -│ └── list -├── logs [opts] +│ ├── start [name] [-d] +│ ├── stop [name] +│ ├── list +│ └── service [name] --target-port [opts] +├── service +│ ├── expose [service] +│ ├── list [sandbox] +│ ├── get [service] +│ └── delete [service] +├── logs [name] [opts] ├── policy -│ ├── set --policy [--wait] -│ ├── get [--full] -│ └── list +│ ├── set [name] --policy [--global] [--wait] +│ ├── update [name] [opts] +│ ├── get [name] [--full|--base] [--global] +│ ├── list [name] [--global] +│ ├── delete --global +│ └── prove --policy --credentials [opts] +├── settings +│ ├── get [name] [--global] +│ ├── set [name] --key --value [--global] +│ └── delete [name] --key [--global] +├── rule (advanced; hidden from top-level help) +│ ├── get [name] [--status ] +│ ├── approve [name] --chunk-id +│ ├── reject [name] --chunk-id [--reason ] +│ ├── approve-all [name] [--include-security-flagged] +│ ├── clear [name] +│ └── history [name] ├── provider │ ├── create --name --type [opts] +│ ├── refresh +│ │ ├── status [opts] +│ │ ├── configure [opts] +│ │ ├── rotate --credential-key +│ │ └── delete --credential-key │ ├── get │ ├── list [opts] -│ ├── update --type [opts] +│ ├── list-profiles [opts] +│ ├── profile +│ │ ├── export [opts] +│ │ ├── import (--file |--from ) +│ │ ├── update --file +│ │ ├── lint (--file |--from ) +│ │ └── delete +│ ├── update [opts] │ └── delete ... ├── doctor │ └── check @@ -81,13 +122,20 @@ Register an existing gateway endpoint. | Flag | Description | |------|-------------| | `--name ` | Gateway name | -| `--local` | Register a local endpoint, commonly a trusted port-forward | -| `--remote ` | Register a remote gateway associated with an SSH destination | +| `--local` | Register a local mTLS gateway; with HTTP, store a local plaintext registration | +| `--remote ` | Register a remote mTLS gateway over SSH; with HTTP, store a remote plaintext registration | +| `--oidc-issuer ` | Register an OIDC-authenticated gateway | +| `--oidc-client-id ` | OIDC client ID (default: `openshell-cli`; requires `--oidc-issuer`) | +| `--oidc-audience ` | OIDC API audience (requires `--oidc-issuer`) | +| `--oidc-scopes ` | Space-separated OAuth2 scopes (requires `--oidc-issuer`) | Examples: - `openshell gateway add http://127.0.0.1:8080 --local --name local` - `openshell gateway add https://gateway.example.com --name production` +- `openshell gateway add ssh://user@gateway.example.com:8080 --name remote` + +An `http://` endpoint is direct plaintext. A plain `https://` endpoint uses edge authentication. `--local` and `--remote` select mTLS registration modes when used with HTTPS; required certificates must already exist. An `ssh://` endpoint is shorthand for a remote gateway. ### `openshell gateway remove [name]` @@ -95,7 +143,11 @@ Remove a local gateway registration. This removes CLI metadata and stored auth t ### `openshell gateway login [name]` -Refresh browser-based authentication for a gateway behind an edge proxy. +Refresh browser-based authentication for an edge-authenticated or OIDC gateway. + +### `openshell gateway logout [name]` + +Clear locally stored OIDC or edge credentials for a gateway. ### `openshell gateway info` @@ -107,7 +159,11 @@ Show gateway details: endpoint, auth mode, and remote host metadata when present ### `openshell gateway select [name]` -Set the active gateway. Writes to `~/.config/openshell/active_gateway`. When called without arguments, lists all registered gateways with the active one marked with `*`. +Set the active gateway. Writes to `~/.config/openshell/active_gateway`. Without a name, opens an interactive chooser on a TTY or lists gateways in non-interactive mode. + +### `openshell gateway list` + +List registered gateways and mark the active one. `--output table|yaml|json` selects the format. --- @@ -133,103 +189,138 @@ Show server connectivity and version for the active gateway. ### `openshell sandbox create [OPTIONS] [-- COMMAND...]` -Create a sandbox through the active gateway, wait for readiness, then connect or execute the trailing command. +Create a sandbox through the selected gateway, wait for readiness, then connect, open an editor, or execute the trailing command. | Flag | Description | |------|-------------| | `--name ` | Sandbox name (auto-generated if omitted) | -| `--from ` | Sandbox source: community name, Dockerfile path, directory, or image reference (BYOC) | -| `--upload [:]` | Upload local files into sandbox (default dest: `/sandbox`) | -| `--no-keep` | Delete sandbox after the initial command or shell exits | +| `--from ` | Community name, Dockerfile path, directory, or image reference (BYOC) | +| `--no-keep` | Delete the sandbox after the initial command or shell exits | +| `--editor vscode|cursor` | Launch a remote editor and keep the sandbox alive | +| `--gpu [COUNT]` | Request the driver's default GPU selection or a specific count | +| `--cpu ` | CPU limit (for example: `500m`, `1`, `2.5`) | +| `--memory ` | Memory limit (for example: `512Mi`, `4Gi`, `8G`) | +| `--driver-config-json ` | Experimental driver-keyed configuration object | | `--provider ` | Provider to attach (repeatable) | -| `--policy ` | Path to custom policy YAML | -| `--cpu ` | CPU amount for the sandbox (for example: `500m`, `1`, `2.5`) | -| `--memory ` | Memory amount for the sandbox (for example: `512Mi`, `4Gi`, `8G`) | -| `--forward ` | Forward local port to sandbox (keeps the sandbox alive) | -| `--tty` | Force pseudo-terminal allocation | -| `--no-tty` | Disable pseudo-terminal allocation | -| `--auto-providers` | Auto-create missing providers from local credentials (skips interactive prompt) | -| `--no-auto-providers` | Never auto-create providers; skip missing providers silently | -| `[-- COMMAND...]` | Command to execute (defaults to interactive shell) | - -### `openshell sandbox get ` - -Show sandbox details (id, name, namespace, phase) and the **active** policy from the gateway (same source whether policy is sandbox-scoped or global). Metadata includes **Policy source** (`sandbox` or `global`) and **Revision** (global policy row when source is global, otherwise sandbox policy row). +| `--policy ` | Custom policy YAML; overrides the built-in default and `OPENSHELL_SANDBOX_POLICY` | +| `--forward <[BIND:]PORT>` | Start a local port forward and keep the sandbox alive | +| `--tty`, `--no-tty` | Force or disable pseudo-terminal allocation | +| `--auto-providers` | Auto-create missing providers from local credentials | +| `--no-auto-providers` | Never auto-create providers; error if a required provider is missing | +| `--label ` | Attach a label (repeatable) | +| `--env ` | Inject an environment variable (repeatable) | +| `--approval-mode manual|auto` | Handle agent-authored policy proposals; default: `manual` | +| `--upload [:]` | Upload local files to the working directory or an explicit destination (repeatable) | +| `--no-git-ignore` | Disable `.gitignore` filtering for `--upload` | +| `[-- COMMAND...]` | Initial command (defaults to an interactive shell) | + +### `openshell sandbox get [name]` + +Show sandbox details and the active policy. Metadata identifies sandbox or global policy source and the corresponding revision. The name defaults to the last-used sandbox. | Flag | Description | |------|-------------| -| `--policy-only` | Print only the active policy YAML to stdout (same policy as above; use for scripts and piping) | +| `--policy-only` | Print only the active policy YAML to stdout | ### `openshell sandbox list` -List sandboxes in a table. - | Flag | Default | Description | |------|---------|-------------| -| `--limit ` | 100 | Max sandboxes to return | +| `--limit ` | 100 | Maximum sandboxes | | `--offset ` | 0 | Pagination offset | | `--ids` | false | Print only sandbox IDs | | `--names` | false | Print only sandbox names | +| `--selector ` | none | Filter by `key1=value1,key2=value2` | +| `--output table|yaml|json` | `table` | Output format | -### `openshell sandbox delete ...` +### `openshell sandbox delete [NAME]...` -Delete one or more sandboxes by name. Stops any background port forwards. +Delete one or more named sandboxes, or use `--all`. Deletion stops background port forwards. -### `openshell sandbox connect ` +### `openshell sandbox exec [OPTIONS] -- COMMAND...` -Open an interactive SSH shell to a sandbox. +Execute a command through the gRPC exec endpoint, stream its output, and exit with the remote command's exit code. -### `openshell sandbox upload [dest]` +| Flag | Default | Description | +|------|---------|-------------| +| `-n`, `--name ` | last-used | Sandbox name | +| `--workdir ` | none | Working directory in the sandbox | +| `--timeout ` | 0 | Command timeout; `0` disables it | +| `--tty`, `--no-tty` | auto | Force or disable a pseudo-terminal | +| `--env ` | none | Command environment variable (repeatable) | + +### `openshell sandbox connect [name]` -Upload local files to a sandbox using tar-over-SSH. +Open an interactive SSH shell. The name defaults to the last-used sandbox. `--editor vscode|cursor` launches a supported remote editor instead. -| Argument | Default | Description | -|----------|---------|-------------| -| `` | -- | Sandbox name (required) | -| `` | -- | Local path to upload (required) | -| `[dest]` | `/sandbox` | Destination path in sandbox | +### `openshell sandbox upload [dest]` + +Upload files using tar-over-SSH. The destination defaults to the container working directory. `.gitignore` filtering is enabled unless `--no-git-ignore` is passed. ### `openshell sandbox download [dest]` -Download files from a sandbox using tar-over-SSH. +Download files using tar-over-SSH. The local destination defaults to `.`. + +### `openshell sandbox ssh-config [name]` -| Argument | Default | Description | -|----------|---------|-------------| -| `` | -- | Sandbox name (required) | -| `` | -- | Sandbox path to download (required) | -| `[dest]` | `.` | Local destination path | +Print an SSH config `Host` block. The name defaults to the last-used sandbox. -### `openshell sandbox ssh-config ` +### `openshell sandbox provider` -Print an SSH config `Host` block for a sandbox. Useful for VS Code Remote-SSH. +Manage providers on an existing sandbox: + +- `openshell sandbox provider list [name]` +- `openshell sandbox provider attach ` +- `openshell sandbox provider detach ` --- ## Port Forwarding Commands -### `openshell forward start ` +### `openshell forward start [name]` Start forwarding a local port to a sandbox. | Flag | Description | |------|-------------| -| `` | Port number (used as both local and remote) | -| `` | Sandbox name | +| `` | `[bind_address:]port`; the port is used locally and remotely | +| `[name]` | Sandbox name (defaults to last-used) | | `-d`, `--background` | Run in background | -### `openshell forward stop ` +### `openshell forward stop [name]` -Stop a background port forward. +Stop a background port forward. When the sandbox name is omitted, it is inferred from active forwards. ### `openshell forward list` List all active port forwards (sandbox, port, PID, status). +### `openshell forward service [name] --target-port ` + +Forward a local TCP port to a loopback service inside a sandbox over the gRPC relay. + +| Flag | Default | Description | +|------|---------|-------------| +| `--target-port ` | required | Service port inside the sandbox | +| `--target-host ` | `127.0.0.1` | Loopback service host | +| `--local <[BIND:]PORT>` | target port | Local bind; port `0` requests dynamic assignment | + +--- + +## Service Commands + +Gateway-managed HTTP service endpoints: + +- `openshell service expose [service]` +- `openshell service list [sandbox] [--limit N] [--offset N]` +- `openshell service get [service]` +- `openshell service delete [service]` + --- ## Logs Command -### `openshell logs ` +### `openshell logs [name]` View sandbox logs. Supports one-shot and streaming. @@ -241,20 +332,22 @@ View sandbox logs. Supports one-shot and streaming. | `--source ` | `all` | Filter: `gateway`, `sandbox`, or `all` (repeatable) | | `--level ` | none | Minimum level: `error`, `warn`, `info`, `debug`, `trace` | +The sandbox name defaults to the last-used sandbox. + --- ## Policy Commands -### `openshell policy update ` +### `openshell policy update [name]` Incrementally merge live network policy changes into the current sandbox policy. Multiple flags in one invocation are applied as one atomic batch and create at most one new revision. | Flag | Default | Description | |------|---------|-------------| -| `--add-endpoint ` | repeatable | `host:port[:access[:protocol[:enforcement]]]`. Adds or merges an endpoint. `access`: `read-only`, `read-write`, `full`. `protocol`: `rest`, `sql`. `enforcement`: `enforce`, `audit`. | +| `--add-endpoint ` | repeatable | `host:port[:access[:protocol[:enforcement[:options]]]]`. Adds or merges an endpoint. | | `--remove-endpoint ` | repeatable | `host:port`. Removes the endpoint or just the requested port from a multi-port endpoint. | -| `--add-allow ` | repeatable | `host:port:METHOD:path_glob`. Adds REST allow rules to an existing `protocol: rest` endpoint. | -| `--add-deny ` | repeatable | `host:port:METHOD:path_glob`. Adds REST deny rules to an existing `protocol: rest` endpoint that already has an allow base. | +| `--add-allow ` | repeatable | `host:port:METHOD:path_glob`. Adds REST or WebSocket allow rules. | +| `--add-deny ` | repeatable | `host:port:METHOD:path_glob`. Adds REST or WebSocket deny rules. | | `--remove-rule ` | repeatable | Deletes a named network rule. | | `--binary ` | repeatable | Adds binaries to each `--add-endpoint` rule. Valid only with `--add-endpoint`. | | `--rule-name ` | none | Overrides the generated rule name. Valid only when exactly one `--add-endpoint` is provided. | @@ -264,44 +357,92 @@ Incrementally merge live network policy changes into the current sandbox policy. Notes: -- `--add-allow` and `--add-deny` currently operate only on `protocol: rest` endpoints. +- The sandbox name defaults to the last-used sandbox. +- `--add-allow` and `--add-deny` operate on REST and WebSocket endpoints. Use full YAML for JSON-RPC, MCP, SQL, or other policy structure. - `--wait` cannot be combined with `--dry-run`. - Use `policy set` when replacing the full policy or changing static sections. -### `openshell policy set --policy ` +### `openshell policy set [name] --policy ` Replace the full policy on a live sandbox. Only the dynamic `network_policies` field can be changed at runtime. | Flag | Default | Description | |------|---------|-------------| | `--policy ` | -- | Path to policy YAML (required) | +| `--global` | false | Apply as the gateway-global policy | +| `--yes` | false | Skip confirmation for a global update | | `--wait` | false | Wait for sandbox to confirm policy is loaded | | `--timeout ` | 60 | Timeout for `--wait` | Exit codes with `--wait`: 0 = loaded, 1 = failed, 124 = timeout. -### `openshell policy get ` +### `openshell policy get [name]` -Show current active policy for a sandbox. +Show the current effective sandbox policy or stored global policy. | Flag | Default | Description | |------|---------|-------------| -| `--rev ` | 0 (latest) | Show a specific revision | -| `--full` | false | Print the full policy as YAML (round-trips with `--policy` input) | +| `--rev ` | 0 | Show a stored revision; `0` shows the current effective policy | +| `--full` | false | Include the effective policy payload and provider-composed entries | +| `--base` | false | Include the base policy payload without provider-composed entries | +| `--output table|json` | `table` | Output format | +| `--global` | false | Show the global policy revision | -### `openshell policy list ` +### `openshell policy list [name]` List policy revision history (version, hash, status, created, error). | Flag | Default | Description | |------|---------|-------------| | `--limit ` | 20 | Max revisions to return | +| `--global` | false | List global policy revisions | + +### `openshell policy delete --global` + +Delete the global policy lock and restore sandbox-level policy control. `--yes` skips confirmation. + +### `openshell policy prove` + +Prove policy properties or find counterexamples. + +| Flag | Description | +|------|-------------| +| `--policy ` | Policy YAML (required) | +| `--credentials ` | Credential descriptor YAML (required) | +| `--registry ` | Capability registry directory (defaults to bundled) | +| `--accepted-risks ` | Accepted-risks YAML | +| `--compact` | One-line-per-finding output | + +### `openshell rule` (advanced) + +Review agent-authored network rule proposals. This command group is intentionally hidden from top-level help but is part of the policy-advisor workflow. + +- `openshell rule get [name] [--status pending|approved|rejected]` +- `openshell rule approve [name] --chunk-id ` +- `openshell rule reject [name] --chunk-id [--reason ]` +- `openshell rule approve-all [name] [--include-security-flagged]` +- `openshell rule clear [name]` +- `openshell rule history [name]` + +Sandbox names default to the last-used sandbox. Bulk approval of security-flagged proposals requires explicit `--include-security-flagged`. + +--- + +## Settings Commands + +Settings support sandbox and gateway-global scopes: + +- `openshell settings get [name] [--global] [--json]` +- `openshell settings set [name] --key --value [--global] [--yes]` +- `openshell settings delete [name] --key [--global] [--yes]` + +Sandbox names default to the last-used sandbox. Global mutations prompt unless `--yes` is passed. --- ## Provider Commands -Supported provider types: `claude`, `opencode`, `codex`, `generic`, `nvidia`, `gitlab`, `github`, `outlook`. +Provider types are defined by built-in and custom provider profiles. Use `openshell provider list-profiles` to discover the selected gateway's current inventory. ### `openshell provider create --name --type ` @@ -311,10 +452,14 @@ Create a provider configuration. |------|-------------| | `--name ` | Provider name (required) | | `--type ` | Provider type (required) | -| `--from-existing` | Load credentials from local state (mutually exclusive with `--credential`) | +| `--from-existing` | Load credentials and config from local state | | `--credential KEY[=VALUE]` | Credential pair. Bare `KEY` reads from env var. Repeatable. | +| `--from-gcloud-adc` | Load a compatible credential from gcloud Application Default Credentials | +| `--runtime-credentials` | Resolve required credentials at runtime in the gateway or sandbox | | `--config KEY=VALUE` | Config key/value pair. Repeatable. | +Exactly one credential source is required. Credential-source flags conflict with one another. + ### `openshell provider get ` Show provider details (id, name, type, credential keys, config keys). @@ -328,40 +473,80 @@ List providers in a table. | `--limit ` | 100 | Max providers | | `--offset ` | 0 | Pagination offset | | `--names` | false | Print only names | +| `--output table|yaml|json` | `table` | Output format | -### `openshell provider update --type ` +### `openshell provider update ` -Update an existing provider. Same flags as `create`. +Update an existing provider without changing its type. + +| Flag | Description | +|------|-------------| +| `--from-existing` | Rediscover local credentials and config | +| `--credential KEY[=VALUE]` | Update a credential (repeatable) | +| `--config KEY=VALUE` | Update config (repeatable) | +| `--credential-expires-at KEY=TIMESTAMP` | Set or clear credential expiry; accepts epoch milliseconds or RFC3339, and `0` clears | ### `openshell provider delete ...` Delete one or more providers by name. +### Provider profiles + +- `openshell provider list-profiles [--output table|yaml|json]` +- `openshell provider profile export [--output table|yaml|json]` +- `openshell provider profile import (--file |--from )` +- `openshell provider profile update --file ` +- `openshell provider profile lint (--file |--from )` +- `openshell provider profile delete ` + +### Provider credential refresh + +- `openshell provider refresh status [--credential-key ]` +- `openshell provider refresh rotate --credential-key ` +- `openshell provider refresh delete --credential-key ` + +`provider refresh configure ` accepts: + +| Flag | Description | +|------|-------------| +| `--credential-key ` | Injectable credential key (required) | +| `--strategy ` | `oauth2-refresh-token`, `oauth2-client-credentials`, or `google-service-account-jwt` | +| `--material KEY=VALUE` | Non-secret refresh material (repeatable) | +| `--secret-material-env KEY[=ENVVAR]` | Secret refresh material read from the CLI environment (repeatable) | +| `--secret-material-key KEY` | Mark a supplied material key secret (repeatable) | +| `--credential-expires-at TIMESTAMP` | Current credential expiry in epoch milliseconds or RFC3339 | + --- ## Inference Commands ### `openshell inference set` -Configure the managed gateway inference route used by `inference.local`. Both flags are required. +Configure the gateway's user-facing `inference.local` route or the platform-only system route. Provider and model are required. | Flag | Default | Description | |------|---------|-------------| | `--provider ` | -- | Provider record name (required) | | `--model ` | -- | Model identifier to use for generation requests (required) | +| `--system` | false | Configure the system inference route | +| `--no-verify` | false | Skip endpoint verification before saving | +| `--timeout ` | 0 | Request timeout; `0` uses the 60-second default | ### `openshell inference update` -Partially update the gateway inference configuration. Fetches the current config and applies only the provided overrides. At least one flag is required. +Partially update the selected inference route. | Flag | Default | Description | |------|---------|-------------| | `--provider ` | unchanged | Provider record name | | `--model ` | unchanged | Model identifier | +| `--system` | false | Target the system inference route | +| `--no-verify` | false | Skip endpoint verification before saving | +| `--timeout ` | unchanged | Request timeout; `0` uses the 60-second default | ### `openshell inference get` -Show the current gateway inference configuration. +Show both inference routes. `--system` shows only the system route. --- @@ -369,7 +554,7 @@ Show the current gateway inference configuration. ### `openshell term` -Launch the OpenShell interactive TUI. +Launch the OpenShell interactive TUI. `--theme auto|dark|light` overrides `OPENSHELL_THEME`. ### `openshell completions ` diff --git a/.agents/skills/review-github-pr/SKILL.md b/.agents/skills/review-github-pr/SKILL.md index 4901d19754..1058bfb3e9 100644 --- a/.agents/skills/review-github-pr/SKILL.md +++ b/.agents/skills/review-github-pr/SKILL.md @@ -111,6 +111,7 @@ Read through the full diff (and the PR description if available). Produce a summ - **Key Design Decisions**: Focus on _why_ something was done a particular way, not _what_ changed. Include `file_path:line_number` references. Examples: choice of algorithm, new abstraction introduced, API contract change, migration strategy. - **Notable Code**: Include only the most instructive or surprising snippets. Keep each snippet under 15 lines. Always include the file path above the code block. - **Potential Concerns**: Only include if there are genuine risks — missing error handling, breaking changes, performance implications, security issues. Do not fabricate concerns. +- **Agent infrastructure**: When the PR changes behavior, commands, or development workflows, use the `sync-agent-infra` maintenance map to check that related skills were updated. When it adds, removes, or renames skills or crates; changes workflow relationships or skill coverage; modifies issue or PR templates; or changes agent cross-references, apply the full consistency checklist. Report missing companion updates or drift under **Potential Concerns**. ## Step 5: Output diff --git a/.agents/skills/sync-agent-infra/SKILL.md b/.agents/skills/sync-agent-infra/SKILL.md index 2c87dd4182..06082190a1 100644 --- a/.agents/skills/sync-agent-infra/SKILL.md +++ b/.agents/skills/sync-agent-infra/SKILL.md @@ -9,7 +9,7 @@ Detect and fix drift across the agent-first infrastructure files. These files re | File | What it tracks | |------|---------------| -| `AGENTS.md` | Project identity, workflow chains, architecture overview, issue/PR conventions | +| `AGENTS.md` | Project identity, workflow chains, architecture overview, issue/PR conventions, skill maintenance pointer | | `CONTRIBUTING.md` | Skills table, workflow chains, "When to Open an Issue" guidance, skill references | | `README.md` | "Built With Agents" section, "Explore with your agent" skill references | | `.github/ISSUE_TEMPLATE/bug_report.yml` | Skill name references in diagnostic guidance | @@ -18,16 +18,43 @@ Detect and fix drift across the agent-first infrastructure files. These files re | `.github/workflows/issue-triage.yml` | Comment text referencing skills | | `.agents/skills/triage-issue/SKILL.md` | Skill name references in gate check and diagnosis steps | | `.agents/skills/openshell-cli/SKILL.md` | Companion skills table | -| `.agents/skills/build-from-issue/SKILL.md` | `state:triage-needed` label awareness | +| `.agents/skills/create-github-pr/SKILL.md` | Pre-PR agent infrastructure check | +| `.agents/skills/review-github-pr/SKILL.md` | Review-time agent infrastructure check | +| `.agents/skills/build-from-issue/SKILL.md` | Label awareness and pre-commit agent infrastructure check | +| `.claude/agents/principal-engineer-reviewer.md` | Shared review-time agent infrastructure check | ## When to Run - After adding, removing, or renaming a skill in `.agents/skills/` - After adding, removing, or renaming a crate in `crates/` - After changing workflow chain relationships between skills +- After changing which product or development areas a skill covers - After modifying issue or PR templates - Before opening a PR that touches any of the above +## Skill Maintenance Map + +Use this map when product behavior, commands, or development workflows change. It is a routing aid, not an exhaustive dependency list. Search `.agents/skills/` for the changed command, field, component, or workflow before concluding that no other skill needs an update. + +| Change area | Skills to review | +|---|---| +| CLI commands, flags, defaults, or workflows | `openshell-cli` | +| Sandbox policy schema, presets, or enforcement behavior | `generate-sandbox-policy`, `openshell-cli` | +| Supervisor middleware policy, registrations, runtime, or failure behavior | `generate-sandbox-policy`, `openshell-cli`, `debug-openshell-cluster` | +| Gateway deployment, Helm, runtime drivers, or health checks | `debug-openshell-cluster`, `helm-dev-environment` | +| Inference routing, providers, or `inference.local` behavior | `debug-inference`, `openshell-cli` | +| TUI architecture, navigation, data fetching, or UX | `tui-development` | +| Release artifacts or post-publish smoke coverage | `test-release-canary` | +| GitHub Actions workflows, required checks, or CI diagnostics | `watch-github-actions`; also `test-release-canary` for release smoke coverage | +| Gator harness, sandbox image, supervision, or model overrides | `launch-openshell-gator` | +| SBOM generation, dependency metadata, or license workflows | `sbom` | +| Issue templates, labels, contribution gates, or spike/build workflow | `triage-issue`, `create-spike`, `build-from-issue`, `create-github-issue` | +| PR template, review conventions, or vouch behavior | `create-github-pr`, `review-github-pr`, `build-from-issue` | +| Security review or remediation workflow | `review-security-issue`, `fix-security-issue` | +| RFC template, numbering, or lifecycle | `create-rfc` | +| Documentation structure, navigation, or doc-update workflow | `update-docs` | +| Skills, crates, workflow chains, issue/PR templates, or agent cross-references | `sync-agent-infra` | + ## Prerequisites You must be in the OpenShell repository root. @@ -77,6 +104,7 @@ For each file in the table above, check for the following inconsistencies: 1. **Architecture overview** — Every crate in `crates/` must appear in the architecture table. The `python/`, `proto/`, `deploy/`, `.agents/` rows must also be present. 2. **Workflow chains** — Verify each skill named in a chain exists in `.agents/skills/`. 3. **Issue/PR conventions** — Verify referenced skills (`create-github-issue`, `create-github-pr`, `build-from-issue`) exist. +4. **Skill maintenance pointer** — Verify it still points to `sync-agent-infra` and does not duplicate the maintenance map from this skill. ### `README.md` @@ -100,6 +128,7 @@ For each file in the table above, check for the following inconsistencies: 3. **`build-from-issue`** — Label names must match the project's label taxonomy. 4. **`create-spike`** — Reference to `build-from-issue` as next step must be accurate. 5. **`review-security-issue`** / **`fix-security-issue`** — Cross-references between the two must be accurate. +6. **PR creation and review checks** — The `create-github-pr`, `review-github-pr`, `build-from-issue`, and `principal-engineer-reviewer` references to `sync-agent-infra` must exist and use trigger conditions aligned with this skill. ## Step 3: Report Drift @@ -125,6 +154,7 @@ If any inconsistencies are found, report them in a structured format: ### Cross-References - : references non-existent skill - : references non-existent label