OpenShell Deployment#
This is the canonical operator guide for running AI-Q deep-research code in NVIDIA OpenShell. It owns the setup, deployment, acceptance, and troubleshooting contract. The architecture and implementation pages describe invariants and extension points; they intentionally link here for operational steps.
OpenShell is the primary path for current sandbox-enabled deep-research validation. It does not replace AI-Q’s non-sandbox workflow defaults, and it remains experimental until the Linux hard-Landlock acceptance gate passes.
AI-Q Environment Prerequisite#
Run all commands in this guide from the AI-Q repository root using the standard AI-Q virtual environment:
./scripts/setup.sh
source .venv/bin/activate
setup.sh installs the locked AI-Q dependencies, API frontends, data sources,
pre-commit hooks, and UI packages. A new shell must run
source .venv/bin/activate again before validation, tests, or E2E startup.
Setup Command Overview#
Command |
Purpose |
|---|---|
|
Create |
|
Select the repository Python environment in the current shell |
|
Install the certified OpenShell SDK/adapter, generate a production hard-Landlock policy, and build the sandbox image |
|
Generate the explicit |
|
Explicitly install or repair the certified packaged gateway on Apple Silicon macOS |
|
Report safe CLI, SDK, package, and live-gateway version diagnostics |
|
Start or reuse the packaged authenticated gateway and run the disposable strict capability probe |
|
Validate the AI-Q workflow and its OpenShell policy/config pairing before startup |
|
Re-run gateway readiness, then start the AI-Q backend and UI |
Purpose and Security Boundary#
OpenShell executes code generated during deep research. AI-Q orchestration, inference, retrieval tools, credentials, checkpoints, and report state remain on the host side. AI-Q does not copy the host environment or model credentials into the sandbox creation specification.
In production, every job receives a distinct physical sandbox bound to the submitted policy. AI-Q verifies the authoritative policy source, content, hash, and revision before exposing the execution adapter. Successful, failed, timed-out, and cancelled jobs must delete the sandboxes they own.
AI-Q does not reproduce OpenShell’s private policy-hash algorithm. It requires the effective-config policy and revision policy to both equal the submitted protobuf, requires both authoritative hashes to be non-empty and equal, and emits that OpenShell-provided hash in the sanitized attestation event.
Attaching to an existing shared sandbox is an explicit debug escape hatch. It is not job-isolated and is not a production mode. OpenShell is an external runtime and authentication boundary, not merely a Python dependency: an operator must own the gateway service, registration, credentials, version, and availability.
Supported Platforms#
Path |
Intended use |
Landlock |
Gateway owner |
AI-Q status |
|---|---|---|---|---|
Linux + Docker |
Production acceptance |
|
systemd or external operator |
Tested path after the live suite passes |
macOS + Docker Desktop |
Local demo |
|
Homebrew |
Demo only |
Linux + Podman |
Operator-managed evaluation |
|
external operator/system service |
Supported upstream, not automated or certified by AI-Q acceptance |
Remote authenticated gateway |
Managed deployment |
Gateway-host dependent |
external operator |
Accepted only after the AI-Q live suite passes |
Windows/WSL |
– |
– |
– |
Outside current AI-Q setup-script support |
OpenShell supports Docker and rootless Podman upstream. AI-Q’s provisioning and acceptance automation certify only the exercised Docker path. Follow the official OpenShell installation documentation for gateway-host prerequisites and upstream runtime support.
Known Limitations#
OpenShell integration remains experimental until the Linux/Docker live suite passes with Landlock
hard_requirement; macOSbest_effortresults are local functional evidence only.Docker is the only AI-Q-automated runtime path. Podman is supported upstream but not automated or production-accepted by AI-Q; Windows/WSL is unsupported.
best_effortpermits execution when Landlock is unavailable, so macOS/Docker Desktop does not provide the production filesystem-confinement guarantee.OpenShell does not currently satisfy AI-Q’s optional CPU/memory resource-limit capability. Configuring those limits fails closed rather than silently ignoring them.
Shared named-sandbox attachment is debug-only and is not a tenant or job isolation boundary.
The authenticated gateway is an operator-owned external service. AI-Q can validate or start a packaged local service, but E2E shutdown intentionally does not stop the gateway.
Version Compatibility#
scripts/openshell/setup_openshell.sh accepts only the certified OpenShell
version. latest and other exact releases are rejected so an evaluation upgrade
cannot silently become a production stack. The strict gateway launcher then
requires the virtual-environment CLI, SDK, packaged local CLI, and live gateway
to match that version.
The published langchain-nvidia-openshell==0.1.0 metadata still declares
deepagents<0.6, while AI-Q uses DeepAgents 0.6.x. The OpenShell setup therefore
installs the optional adapter and then restores AI-Q’s required DeepAgents and
OpenShell versions. This is intentionally isolated from scripts/setup.sh; it
may repeat package installation, and it remains an upstream metadata limitation
until a compatible adapter release is published. Do not move an unreleased
adapter into the AI-Q lockfile or hide the conflict with a dependency override.
Until that release exists, pip check reports the adapter’s declared
DeepAgents-range conflict; this PR must not describe that metadata state as
clean even though the tested adapter surface works with AI-Q’s locked runtime.
AI-Q currently requires and defaults to OpenShell 0.0.80. It is the first
released version that acknowledges the initial sandbox-scoped policy revision as
LOADED after successful policy-engine construction and exposes immutable
request-level labels/selectors through the Python SDK. Earlier releases can leave
the revision PENDING with current_policy_version=0 or omit ownership labels,
so they fail AI-Q’s strict readiness checks.
The certified release is necessary but not sufficient: runtime security decisions
remain capability-based. On the supported 0.0.80 stack, the current, active,
revision, and effective-config versions must all be positive and agree. Any
missing capability or version/content/hash/source mismatch fails closed.
Responsibility and Lifecycle Ownership#
Component |
Owns |
Must not do |
|---|---|---|
|
SDK/adapter install, policy generation, image build |
Start, stop, register, select, probe, or kill gateways |
|
Explicit installation or repair of the official packaged local macOS gateway |
Install Linux/remote gateways, create custom taps, weaken TLS, or launch raw binaries |
Homebrew/systemd/external operator |
Long-running gateway service and credentials |
Delegate process ownership to AI-Q setup |
|
Validate registration/auth, optionally start a packaged service, select the gateway, and run the strict disposable capability probe |
Install or upgrade gateways, launch raw binaries, stop externally managed services, or persist credentials |
AI-Q runtime |
Per-job create, readiness, attestation, execution, and terminal deletion |
Reuse a shared sandbox without explicit debug opt-in |
Live pytest fixtures |
Acceptance-test resources and verified teardown |
Leave resources for manual cleanup |
Provisioning and long-running service lifecycle are deliberately separate. E2E shutdown never stops a Homebrew-, systemd-, or operator-managed gateway.
Policy and AI-Q Config Pairing#
Both policy layers are enforced:
The OpenShell policy is authoritative at the gateway.
networkandnetwork_allowin the AI-Q config are an upper bound on that policy. They never grant additional access.network: blockedpermits no policy endpoint.network: allowlistrequires every endpoint to have a non-empty normalized host, and every host must appear innetwork_allow.Hostless endpoints and
allowed_ipsor CIDR exceptions are rejected because the public AI-Q policy does not model those exceptions.Production requires both
landlock.compatibility: hard_requirementin the policy andrequire_hard_landlock: truein the AI-Q config.A local demo using
best_effortrequires both the policy valuebest_effortandAIQ_OPENSHELL_REQUIRE_HARD_LANDLOCK=falsewhen validating or running the standard OpenShell config.Custom policies must explicitly include OpenShell’s proxy filesystem baseline, including read-only
/proc. Otherwise the supervisor creates an enriched revision whose content and hash correctly fail AI-Q’s exact attestation. The generated policy fromscripts/openshell/setup_openshell.shalready includes this baseline.
Any mismatch fails closed before the execution adapter is available. Keep environment-specific generated policies out of commits, and never put credentials in policy or workflow configuration files.
Environment Contract#
The gateway launcher, AI-Q runtime, and live suite use these non-secret settings:
Variable |
Default |
Purpose |
|---|---|---|
|
unset |
Must equal |
|
active gateway |
Registered gateway name |
|
|
Policy submitted and attested |
|
|
Prebuilt sandbox image |
|
installed SDK version |
Optional exact live-test override |
|
|
Set |
|
unset |
Explicit non-production macOS/demo opt-in |
Linux Production Acceptance#
First install and register an authenticated packaged gateway, or arrange an
externally operated gateway, using the official OpenShell documentation. The
registration must use HTTPS and mTLS, OIDC, or trusted edge authentication.
Do not launch a raw openshell-gateway process.
From the AI-Q repository root, provision the pinned SDK, hard policy, and image:
./scripts/openshell/setup_openshell.sh \
--openshell-version 0.0.80 \
--policy offline \
--landlock-compatibility hard_requirement
Select the authenticated registration and prove version, policy, selector,
execution, and deletion capabilities. Omit --reuse-existing only when the gateway is a local packaged
service that the launcher may start through systemd.
./scripts/openshell/start_openshell_gateway.sh \
--gateway-name openshell \
--image-name aiq-openshell-demo:latest \
--policy-file configs/openshell/generated/aiq-openshell-policy.yaml
The readiness probe proves that the submitted policy is effective, but it does
not independently require hard Landlock. Production acceptance therefore uses
the hard_requirement policy generated above and the matching
require_hard_landlock: true AI-Q config.
Export the same image and policy for the AI-Q process:
export AIQ_OPENSHELL_GATEWAY_NAME=openshell
export AIQ_OPENSHELL_IMAGE=aiq-openshell-demo:latest
export AIQ_OPENSHELL_POLICY_FILE="$PWD/configs/openshell/generated/aiq-openshell-policy.yaml"
export AIQ_OPENSHELL_EXPECTED_GATEWAY_VERSION=0.0.80
Validate and start AI-Q with the production pairing in
configs/config_openshell.yml:
source .venv/bin/activate
nat validate --config_file configs/config_openshell.yml
./scripts/start_e2e.sh --config_file configs/config_openshell.yml
Because the gateway was already verified above, --start-openshell-gateway is
not needed here. It is an optional convenience that reruns the same strict probe
before E2E startup.
In a separate shell with the same exported settings, run the required acceptance suite:
AIQ_OPENSHELL_LIVE_TESTS=1 \
AIQ_OPENSHELL_GATEWAY_NAME=openshell \
AIQ_OPENSHELL_POLICY_FILE=configs/openshell/generated/aiq-openshell-policy.yaml \
AIQ_OPENSHELL_IMAGE=aiq-openshell-demo:latest \
AIQ_OPENSHELL_EXPECTED_GATEWAY_VERSION=0.0.80 \
.venv/bin/python -m pytest -m integration -vv \
tests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.py
Only this Linux, hard-Landlock run can be recorded as production acceptance.
macOS Local Demo#
Use Docker Desktop and the official nvidia/openshell Homebrew service. First
provision AI-Q’s optional Python components, policy, and image. This step also
prints safe component diagnostics when the packaged gateway is missing or stale.
macOS ships Bash 3.2. Install Bash 5 when the setup script reports unsupported Bash behavior:
brew install bash
/opt/homebrew/bin/bash ./scripts/openshell/setup_openshell.sh \
--openshell-version 0.0.80 \
--local-demo \
--policy offline
If setup reports packaged_gateway_missing or
component_version_mismatch, inspect the explicit operation and then run it as
the logged-in user:
./scripts/openshell/install_gateway.sh --dry-run
./scripts/openshell/install_gateway.sh
For Colima, persist the driver configuration in OpenShell’s service environment instead of the caller’s transient launchd environment:
./scripts/openshell/install_gateway.sh --colima
# Or select a specific local socket:
./scripts/openshell/install_gateway.sh \
--docker-host "unix://$HOME/.colima/default/docker.sock"
The wrapper downloads the installer from the certified OpenShell tag to a
temporary file, verifies its checked-in SHA-256, and invokes the official
installer with OPENSHELL_VERSION=v0.0.80. It refuses root, non-Apple-Silicon
hosts, and ambiguous OpenShell installations. It never pipes a download into a
shell, creates an AI-Q tap, launches a raw gateway, disables TLS, or stores
credentials.
OpenShell’s release installer stages its formula in a local nvidia/openshell
tap created with Homebrew’s --no-git mode. Because that tap has no Git remote,
brew upgrade openshell cannot fetch a newly released formula; it can leave the
packaged gateway on 0.0.72 while AI-Q’s virtual-environment CLI/SDK is 0.0.80.
Rerun the explicit pinned installer wrapper when directed. Do not copy a formula
manually, create a custom AI-Q tap, use launchctl setenv, or keep multiple
OpenShell service identities.
You can inspect the safe version report directly:
.venv/bin/python scripts/openshell/check_versions.py
.venv/bin/python scripts/openshell/check_versions.py --json
Then validate the standard config, validate the gateway, and start AI-Q with the explicit local-demo environment override:
source .venv/bin/activate
AIQ_OPENSHELL_REQUIRE_HARD_LANDLOCK=false \
nat validate --config_file configs/config_openshell.yml
./scripts/openshell/start_openshell_gateway.sh \
--gateway-name openshell \
--image-name aiq-openshell-demo:latest \
--policy-file configs/openshell/generated/aiq-openshell-policy.yaml
AIQ_OPENSHELL_POLICY_FILE=configs/openshell/generated/aiq-openshell-policy.yaml \
AIQ_OPENSHELL_IMAGE=aiq-openshell-demo:latest \
AIQ_OPENSHELL_EXPECTED_GATEWAY_VERSION=0.0.80 \
AIQ_OPENSHELL_REQUIRE_HARD_LANDLOCK=false \
./scripts/start_e2e.sh --config_file configs/config_openshell.yml \
2>&1 | tee e2e-openshell-0.0.80.log
Run the same mechanics through the convenience wrapper with the explicit demo opt-in:
.venv/bin/python scripts/openshell/smoke_openshell_isolation.py \
--gateway openshell \
--policy configs/openshell/generated/aiq-openshell-policy.yaml \
--image aiq-openshell-demo:latest \
--expected-gateway-version 0.0.80 \
--allow-best-effort-landlock
A passing macOS run is useful local evidence, but it does not satisfy Linux production acceptance.
Existing Remote Gateway#
The remote gateway must already be registered over HTTPS with mTLS, OIDC, or trusted edge authentication. The launcher validates the registration and refuses to substitute a local gateway if the remote service is unavailable:
./scripts/openshell/start_openshell_gateway.sh \
--gateway-name enterprise \
--reuse-existing \
--image-name aiq-openshell-demo:latest \
--policy-file configs/openshell/generated/aiq-openshell-policy.yaml
The disposable strict capability probe is mandatory. After it passes,
export AIQ_OPENSHELL_GATEWAY_NAME=enterprise and run the live suite. Never fall
back to a plaintext registration, insecure TLS, or a local raw gateway.
Expected Runtime Behavior#
The human-readable contract is:
One running deep-research job creates one physical OpenShell sandbox.
Two concurrent jobs create two distinct sandbox names and physical IDs.
Active jobs are discoverable with
--selector aiq=deep-research, with a distinctaiq-job-idgateway label for each job.Attestation succeeds before the execution adapter is exposed.
Success, command failure, timeout, and cancellation delete owned sandboxes.
Cancelling one job does not delete or replace another job’s sandbox.
sandbox.attestationreports sanitized status, policy version, hash, source, assurance, and reason code.sandbox.cleanupreportsstarted,succeeded, orfailed, with stablereason_codeson failure.Credentials, policy contents, SDK response bodies, and exception messages are not emitted in lifecycle events or failure logs.
The final job state is separate from physical cleanup: verify the cleanup event and absence from the gateway rather than assuming a terminal job status deleted the resource.
Artifact Capture#
configs/config_openshell.yml enables durable sandbox artifact capture. Successful
execute calls checkpoint declared artifacts, and terminal finalization performs
one idempotent scan before sandbox cleanup. Metadata is stored in the job database;
bytes use the configured SQL or S3-compatible artifact blob provider. Clients
receive artifact.update metadata with a content_url, never raw bytes in SSE.
For validation, storage configuration, event payloads, and report rendering, see the developer artifact runtime and production artifact storage guides.
Acceptance Tests#
The canonical acceptance entry point is pytest:
AIQ_OPENSHELL_LIVE_TESTS=1 \
AIQ_OPENSHELL_GATEWAY_NAME=openshell \
AIQ_OPENSHELL_POLICY_FILE=configs/openshell/generated/aiq-openshell-policy.yaml \
AIQ_OPENSHELL_IMAGE=aiq-openshell-demo:latest \
AIQ_OPENSHELL_EXPECTED_GATEWAY_VERSION=0.0.80 \
.venv/bin/python -m pytest -m integration -vv \
tests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.py
The suite contains three independently reported tests:
test_live_per_job_isolation_attestation_and_cancellationproves distinct sandboxes, authoritative source/content/hash/revision attestation, isolated cancellation, selector membership, continued execution, and terminal deletion.test_live_failure_cleanup_and_log_redactionproves cleanup after a deterministic failed command and verifies that a credential-shaped exception canary reaches neither logs nor events.test_live_shared_policy_mismatch_is_rejectedproves that a structurally different claimed policy cannot attach successfully, while the directly owned shared sandbox remains usable until fixture teardown.
Every fixture registers resources immediately, tears them down in reverse order,
and verifies deletion through the gateway. A teardown failure fails the test even
when the test body also failed. Without AIQ_OPENSHELL_LIVE_TESTS=1, all three
tests are collected and skipped before optional OpenShell imports or gateway
connections.
scripts/openshell/smoke_openshell_isolation.py is a convenience wrapper only. It maps its
arguments to the environment contract, enables the live gate, and returns pytest’s
exit code unchanged. Pytest owns every assertion and cleanup fixture.
Record the non-secret gateway version, policy path, image tag, platform, and Landlock mode with acceptance results. Do not record registrations, environment values, policy contents, response bodies, or credentials.
Inspection and Troubleshooting#
Inspect only registered resources and sanitized AI-Q lifecycle events:
.venv/bin/openshell status
.venv/bin/openshell gateway list -o json
.venv/bin/openshell sandbox list
.venv/bin/openshell sandbox list --selector aiq=deep-research -o json
The selected gateway registration must be HTTPS and report mtls, oidc, or
trusted edge authentication. During a job, the selector must show one owned
sandbox per active deep-research job. After termination, each owned name must be
absent from direct and selector listings. Use the sandbox name from sanitized sandbox.attestation or
sandbox.cleanup events; do not expose full SDK payloads to logs.
Failure |
Safe action |
|---|---|
Generated policy or image is missing |
Run |
|
Run |
|
Run |
|
Remove the obsolete OpenShell formula/service identity through Homebrew before retrying. Do not let AI-Q guess which service to replace. |
|
Coordinate an upgrade with the registered gateway owner. AI-Q never replaces a remote service with a local one. |
|
Start or verify the packaged local service with |
CLI, SDK, and gateway versions differ |
Do not start AI-Q or create a probe sandbox. Align all reported components to the certified version and rerun the launcher. |
|
The installed Python SDK cannot persist gateway labels required for AI-Q ownership and selectors; install a supported release. |
|
The effective policy matches but its revision never became |
|
Regenerate the policy with |
|
The probe was not discoverable through gateway metadata. Do not rely on Docker/template labels as a substitute. |
Registration is plaintext or unauthenticated |
Register an HTTPS gateway with mTLS, OIDC, or trusted edge authentication. Do not bypass the launcher check. |
Docker daemon is unavailable |
Start the operator-owned Docker service and rerun provisioning/probe. |
Podman is selected |
Follow upstream OpenShell guidance; do not report the path as AI-Q production-accepted. |
Landlock policy/config mismatch |
Pair |
Policy is broader than |
Remove the endpoint or add its exact normalized hostname to the declared upper bound. Do not add CIDR exceptions. |
Sandbox never becomes Ready |
Inspect the owning gateway/runtime service, image availability, and sanitized sandbox status; do not dump SDK bodies. |
Probe or job deletion cannot be verified |
Treat acceptance as failed, identify the exact sandbox, and retry explicit deletion through the registered gateway. |
macOS reports Bash 3.2 incompatibility |
Install Bash 5 and invoke the setup script with its absolute path. |
For a named sandbox that this operator owns, use explicit cleanup and verify its absence:
.venv/bin/openshell sandbox delete <identified-sandbox-name>
.venv/bin/openshell sandbox list
Manage a packaged gateway only through its owner:
brew services restart nvidia/openshell/openshell
systemctl --user restart openshell-gateway
Never use broad pkill, launch the raw gateway binary, enable insecure TLS, or
perform destructive cleanup without first identifying the owned resource. Do not
stop an externally managed gateway from AI-Q shutdown logic.