feat(experimentalist): run in OpenShell with an external Harbor bridge - #931
feat(experimentalist): run in OpenShell with an external Harbor bridge#931ryana wants to merge 9 commits into
Conversation
|
| # sandbox by design. Once the platform-evaluator adapter lands, this same | ||
| # invocation becomes the full sandboxed control-plane path. | ||
| openshell sandbox exec --name "$sandbox_name" --workdir /sandbox/project -- \ | ||
| nemo experimentalist run --experiment-dir /sandbox/output "$@" |
There was a problem hiding this comment.
Should we do it the other way around? i.e., have nemo experimentalist run orchestrate the creation of the sandbox and trigger the agent in a safe way, by default.
There was a problem hiding this comment.
yeah that's definitely better UX. would require a larger refactor. I'm trying to thread the needle.
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
b0fd582 to
9c1ba11
Compare
| class HarborBridgeRequest(BaseModel): | ||
| """Bounded Harbor run parameters accepted from an OpenShell sandbox.""" | ||
|
|
||
| model_config = ConfigDict(extra="forbid") |
There was a problem hiding this comment.
This will reject valid overrides (e.g. retry configs). Not sure if inheriting from harbor job config is possible in this case
| self.options = options | ||
| self.experiment_dir = experiment_dir | ||
|
|
||
| def prepare_dataset(self, dataset: Dataset) -> Dataset: |
There was a problem hiding this comment.
What would be the disadvantage of making HarborDataset backend-aware at construction time? For example, could HarborDataset.from_path() accept a local or bridge backend and construct the correct dependency runtime directly? We could then propagate the selected backend through DatasetFactory and the Eval Author dataset construction paths (stage(), promote_local(), and finalize()). Would that let us remove prepare_dataset(), avoid mutating datasets after construction, and ensure sandboxed paths never temporarily contain local Docker runtimes?
| ValueError: If the evaluator type is not supported. | ||
| TypeError: If the evaluator config is not an EvaluatorConfig or dict. | ||
| """ | ||
| bridge_url = os.environ.get(BRIDGE_URL_ENV) |
There was a problem hiding this comment.
Is selecting RemoteHarborEvaluator implicitly from BRIDGE_URL_ENV intentional? Since this is Harbor-specific, a general backend abstraction may not be worthwhile, but could the Harbor config explicitly select local or bridge execution? That would make evaluator selection visible in effective configuration and avoid the same type: harbor config hanging behavior based on process environment.
|
|
||
| def _harden_task(task_dir: Path) -> None: | ||
| if any(any(task_dir.rglob(name)) for name in _COMPOSE_FILENAMES): | ||
| raise ValueError(f"Harbor bridge does not accept Docker Compose tasks: {task_dir.name}") |
There was a problem hiding this comment.
Could we support Docker Compose tasks here instead of rejecting them? What additional security risk would accepting these tasks introduce? Rejecting them prevents existing Harbor suites such as Tau2 from running through the bridge.
| rules: | ||
| - allow: | ||
| method: GET | ||
| path: /health/ready |
There was a problem hiding this comment.
Shouldn't we allow the Platform API calls the run makes here, and mirror the same rules in policy.docker-desktop.yaml? The experimenter needs POST /apis/intake/v2/workspaces/*/experiments, GET and PUT /apis/intake/v2/workspaces/*/experiments/*, and POST /apis/intake/v2/workspaces/*/ingest/otlp/v1/traces. With the readiness-only policy, experiment projection and OTLP trace ingestion fail with 403 policy_denied.
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
|
Superseded by #1008, rebuilt fresh from main under AIRE-823 with the task-envelope bridge contract and preserved Harbor verifier behavior. |
Summary
nemo experimentalist runanddoctorgit,gh, andglabclients that Experimentalist invokes directlyThere is no
--runtimeoption, host-execution mode, or automatic local fallback. The in-process CLI path is enabled only inside the marked Experimentalist container image.Implemented trust boundary
inference.local; the inference credential is not attached to the sandbox.python -m mainentrypoint.The sandbox cannot select a Harbor import path, Docker command, image runtime flag, verifier mode, or arbitrary bridge command.
Default command and image behavior
nemo experimentalist runandnemo experimentalist doctoralways dispatch through OpenShell on the host. Existing local inputs are translated into the uploaded sandbox workspace; paths outside the selected working directory fail closed. Experiment output stays host-side and is downloaded after the sandbox run.The CLI uses
local/nmp-experimentalist:localby default. It reuses the image only when the required runtime compatibility label is present; otherwise a source checkout builds the current host architecture through the existing Bake target.NEMO_EXPERIMENTALIST_IMAGEcan select a published or differently tagged image.Source-control clients and providers
Experimentalist directly calls:
gitfor clone, fetch, checkout, commit, and pushgh auth statusandgh pr createglab auth statusandglab mr createThe image includes all three clients. Git uses
GIT_ASKPASSto receive only an OpenShell placeholder for HTTPS authentication.The launcher supports
none,github-read,github-publish,gitlab-read, andgitlab-publish. It attaches at most one source-control provider to a sandbox. Publish profiles add only HTTPS branch push and draft PR/MR creation to their read counterparts.Plan forward
This MR is the research-preview implementation for AIRE-823. The local bridge is deliberately small and lives with the plugin so we can validate the boundary before committing to a platform service.
For production, preserve this API shape but move it to a dedicated NeMo Evaluator worker. Add approved task sources/images, per-job CPU/memory/storage/time quotas, scoped task-network policy, short-lived inference credentials, durable job state/cancellation, and audit records.
Remaining risk
The bridge process still has host-root-equivalent Docker authority. Harbor tasks may contain arbitrary Dockerfiles, and candidate code receives its inference credential inside the Harbor task container. This removes Docker authority from Experimentalist but does not defend against Docker/Harbor escapes, resource exhaustion, or credential exfiltration from the candidate container. Those are release-gating concerns for production, not claims this preview makes.
Docker Desktop also lacks Landlock in the tested environment. The explicit Docker Desktop policy retains process and network enforcement but marks filesystem isolation best-effort; the strict policy fails closed when Landlock is unavailable.
OpenShell's current static-provider placeholder resolution is sandbox-wide rather than cryptographically bound to a target hostname. Attaching only one source-control provider prevents GitHub-to-GitLab token substitution. A source-control placeholder can still be presented to the trusted bridge endpoint, and the bridge placeholder can be presented to the selected source host; neither endpoint reflects the resolved Authorization header. Target-bound placeholder resolution in OpenShell would strengthen this defense.
OpenShell applies
.gitignorefiltering to uploads inside Git repositories, but the agent can read every uploaded file. Secrets must remain ignored or provider-managed rather than tracked in the selected workspace.Git LFS, SSH Git remotes, GitHub Enterprise, and registry download are outside the current policy.
Validation
578 passedty, lock drift, licenses, policy checks, and UI lint--runtimeoption and fails closed when OpenShell is unavailablelinux/arm64andlinux/amd64git/gh/glabavailability, absence of Docker, and the container-only in-process CLI path1.0gh auth statusand authenticated HTTPSgit ls-remotethrough the GitHub read provider401and authenticated GitLab behavior remains unverifiedNo branch push, pull request creation, or merge request creation was performed during source-provider validation.
Related