Buckets:

download
raw
114 kB
<meta charset="utf-8" /><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;Harbor Environment&quot;,&quot;local&quot;:&quot;harbor-environment&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Overview&quot;,&quot;local&quot;:&quot;overview&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;What you get back&quot;,&quot;local&quot;:&quot;what-you-get-back&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;The intercept&quot;,&quot;local&quot;:&quot;the-intercept&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Prerequisites&quot;,&quot;local&quot;:&quot;prerequisites&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;What startup checks about agents , not just capture&quot;,&quot;local&quot;:&quot;what-startup-checks-about-agents--not-just-capture&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Why --logprobs-mode processed_logprobs is not optional&quot;,&quot;local&quot;:&quot;why---logprobs-mode-processedlogprobs-is-not-optional&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Quick Start&quot;,&quot;local&quot;:&quot;quick-start&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;1. See what this machine can do&quot;,&quot;local&quot;:&quot;1-see-what-this-machine-can-do&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;2. Run one rollout, no server&quot;,&quot;local&quot;:&quot;2-run-one-rollout-no-server&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;3. Serve it&quot;,&quot;local&quot;:&quot;3-serve-it&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;The web UI&quot;,&quot;local&quot;:&quot;the-web-ui&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Deployment settings&quot;,&quot;local&quot;:&quot;deployment-settings&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;CLI reference&quot;,&quot;local&quot;:&quot;cli-reference&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;openenv harbor info&quot;,&quot;local&quot;:&quot;openenv-harbor-info&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;openenv harbor rollout&quot;,&quot;local&quot;:&quot;openenv-harbor-rollout&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;openenv harbor serve&quot;,&quot;local&quot;:&quot;openenv-harbor-serve&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;openenv harbor push&quot;,&quot;local&quot;:&quot;openenv-harbor-push&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Migration notes for the new UI&quot;,&quot;local&quot;:&quot;migration-notes-for-the-new-ui&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Supported harnesses&quot;,&quot;local&quot;:&quot;supported-harnesses&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Supported sandboxes&quot;,&quot;local&quot;:&quot;supported-sandboxes&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Environment details&quot;,&quot;local&quot;:&quot;environment-details&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Where the proxy runs&quot;,&quot;local&quot;:&quot;where-the-proxy-runs&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Sandboxes and providers&quot;,&quot;local&quot;:&quot;sandboxes-and-providers&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Rewards&quot;,&quot;local&quot;:&quot;rewards&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Reading a result&quot;,&quot;local&quot;:&quot;reading-a-result&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Deploying to Hugging Face Spaces&quot;,&quot;local&quot;:&quot;deploying-to-hugging-face-spaces&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Troubleshooting&quot;,&quot;local&quot;:&quot;troubleshooting&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;References&quot;,&quot;local&quot;:&quot;references&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2}],&quot;depth&quot;:1}"/>
<link href="/docs/openenv/main/en/_app/immutable/entry/start.C6uwgA56.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/CSTtbhsA.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/wvPyzM3A.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/entry/app.DdtfYW14.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/T1rQByR4.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/Cs50_Xpk.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/DsnmJJEf.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/BeYo49jn.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/nodes/0.CNKDp9Lu.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/nodes/2.DJle2NMl.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/CSuk9luw.js" rel="modulepreload">
<!--gg71d6--><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;Harbor Environment&quot;,&quot;local&quot;:&quot;harbor-environment&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Overview&quot;,&quot;local&quot;:&quot;overview&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;What you get back&quot;,&quot;local&quot;:&quot;what-you-get-back&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;The intercept&quot;,&quot;local&quot;:&quot;the-intercept&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Prerequisites&quot;,&quot;local&quot;:&quot;prerequisites&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;What startup checks about agents , not just capture&quot;,&quot;local&quot;:&quot;what-startup-checks-about-agents--not-just-capture&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Why --logprobs-mode processed_logprobs is not optional&quot;,&quot;local&quot;:&quot;why---logprobs-mode-processedlogprobs-is-not-optional&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Quick Start&quot;,&quot;local&quot;:&quot;quick-start&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;1. See what this machine can do&quot;,&quot;local&quot;:&quot;1-see-what-this-machine-can-do&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;2. Run one rollout, no server&quot;,&quot;local&quot;:&quot;2-run-one-rollout-no-server&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;3. Serve it&quot;,&quot;local&quot;:&quot;3-serve-it&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;The web UI&quot;,&quot;local&quot;:&quot;the-web-ui&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Deployment settings&quot;,&quot;local&quot;:&quot;deployment-settings&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;CLI reference&quot;,&quot;local&quot;:&quot;cli-reference&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;openenv harbor info&quot;,&quot;local&quot;:&quot;openenv-harbor-info&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;openenv harbor rollout&quot;,&quot;local&quot;:&quot;openenv-harbor-rollout&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;openenv harbor serve&quot;,&quot;local&quot;:&quot;openenv-harbor-serve&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;openenv harbor push&quot;,&quot;local&quot;:&quot;openenv-harbor-push&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Migration notes for the new UI&quot;,&quot;local&quot;:&quot;migration-notes-for-the-new-ui&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Supported harnesses&quot;,&quot;local&quot;:&quot;supported-harnesses&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Supported sandboxes&quot;,&quot;local&quot;:&quot;supported-sandboxes&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Environment details&quot;,&quot;local&quot;:&quot;environment-details&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Where the proxy runs&quot;,&quot;local&quot;:&quot;where-the-proxy-runs&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Sandboxes and providers&quot;,&quot;local&quot;:&quot;sandboxes-and-providers&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Rewards&quot;,&quot;local&quot;:&quot;rewards&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Reading a result&quot;,&quot;local&quot;:&quot;reading-a-result&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Deploying to Hugging Face Spaces&quot;,&quot;local&quot;:&quot;deploying-to-hugging-face-spaces&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Troubleshooting&quot;,&quot;local&quot;:&quot;troubleshooting&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;References&quot;,&quot;local&quot;:&quot;references&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2}],&quot;depth&quot;:1}"/><!---->
<link href="/docs/openenv/main/en/_app/immutable/assets/0.tn0RQdqM.css" rel="modulepreload"> <!--[--><!--[0--><!--[--><!--[0--><!--[--><p></p> <div class="items-center shrink-0 min-w-[100px] max-sm:min-w-[50px] justify-end ml-auto flex" style="float: right; margin-left: 10px; display: inline-flex; position: relative; z-index: 10;"><div class="inline-flex rounded-md max-sm:rounded-sm"><button class="inline-flex items-center gap-1 h-7 max-sm:h-7 px-2 max-sm:px-1.5 text-sm font-medium text-gray-800 border border-r-0 rounded-l-md max-sm:rounded-l-sm border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-live="polite"><span class="inline-flex items-center justify-center rounded-md p-0.5 max-sm:p-0 hover:text-gray-800 dark:hover:text-gray-200"><svg class="sm:size-3.5 size-3" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----></span> <span>Copy page</span></button> <button class="inline-flex items-center justify-center w-6 max-sm:w-5 h-7 max-sm:h-7 disabled:pointer-events-none text-sm text-gray-500 hover:text-gray-700 dark:hover:text-white rounded-r-md max-sm:rounded-r-sm border border-l transition border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-haspopup="menu" aria-expanded="false" aria-label="Open copy menu"><svg class="transition-transform text-gray-400 overflow-visible sm:size-3.5 size-3 rotate-0" width="1em" height="1em" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1L6 6L11 1" stroke="currentColor"></path></svg><!----></button></div> <!--[-1--><!--]--></div><!----> <!--[0--><h1 class="relative group"><a id="harbor-environment" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#harbor-environment"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Harbor Environment</span></h1><!--]--><!----> <p><strong>Train one policy against many coding agents.</strong> Pick a Harbor dataset, pick an agent, pick a
sandbox, and get back the exact token ids and per-token logprobs of every model call the agent made,
plus the task’s own reward.</p> <!--[1--><h2 class="relative group"><a id="overview" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#overview"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Overview</span></h2><!--]--><!----> <p>An agent harness is a moving part you probably do not want to own. opencode, codex, claude-code and
gemini-cli each have their own loop, their own tool surface and their own wire format, and a policy
trained against exactly one of them learns that one’s habits.</p> <p>The usual cost of supporting several is one integration per agent. Here it is one integration total:</p> <table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>16 harnesses</strong></td><td>validated end to end, across 4 wire dialects</td></tr><tr><td><strong>23 sandbox backends</strong></td><td>from Harbor, 4 with credential checks wired in</td></tr><tr><td><strong>any Harbor dataset</strong></td><td>HF repo, local directory, or Harbor registry name</td></tr></tbody></table> <p>All three are chosen <strong>per rollout</strong>, so one server covers the whole matrix and rotating the harness
during training is a config change rather than a new environment.</p> <p>Harbor supplies the tasks, sandboxes, agents and verifiers. This environment adds the OpenEnv
surface: dataset discovery over the Task API, one <code>run_rollout</code> MCP tool, a capture proxy, and a UI.</p> <!--[2--><h3 class="relative group"><a id="what-you-get-back" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#what-you-get-back"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>What you get back</span></h3><!--]--><!----> <p>Every rollout carries the reward from the task’s verifier and the full trace: each conversation with
its messages, and per turn the text, tool calls and finish reason.</p> <p>Against vLLM or SGLang you also get the training contract, per model call:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!---->turn.prompt_token_ids <span class="hljs-comment"># the engine&#x27;s own tokenisation of everything before this turn</span>
turn.completion_token_ids <span class="hljs-comment"># what it sampled</span>
turn.per_token_logps <span class="hljs-comment"># the behaviour-policy logprob of each sampled token</span><!----></pre></div><!----> <p>Which of the two you got is on the result as <code>rollout_type</code> (<code>"train"</code> or <code>"eval"</code>) and <code>capture_level</code>, decided by probing the endpoint before the server starts. Against a hosted provider
the token fields are empty and <code>rollout_type == "eval"</code>; nothing pretends otherwise, and <code>to_turn_records</code> raises rather than handing a trainer empty lists.</p> <!--[1--><h2 class="relative group"><a id="the-intercept" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#the-intercept"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>The intercept</span></h2><!--]--><!----> <p>The agent is a black box. It is a real CLI tool running in a sandbox, and it was never written with
training in mind. So instead of modifying it, an OpenAI-spec proxy is placed between it and your
model, and every call is recorded as it passes.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class=" "><!---->agent <span class="hljs-keyword">in</span> a sandbox
| base URL points <span class="hljs-keyword">at</span> the proxy, API key <span class="hljs-keyword">IS</span> the capture session id
v
capture proxy <span class="hljs-comment">--normalise to chat, ask for token ids--&gt; your endpoint</span>
^ |
| replay <span class="hljs-keyword">in</span> the agent<span class="hljs-symbol">&#x27;s</span> own dialect &lt;<span class="hljs-comment">----------------------+</span>
|
+<span class="hljs-comment">-- every call becomes a node in a rollout graph, linked by token prefix</span>
(by message prefix <span class="hljs-keyword">when</span> the endpoint returns no token ids)<!----></pre></div><!----> <p>Three properties make this work across agents rather than for one:</p> <p><strong>Nothing is tokenised locally.</strong> The engine tokenises each prompt in order to serve it and hands
back <code>prompt_token_ids</code>, so turn <em>k+1</em>’s prompt is by construction the canonical tokenisation of
everything before it, tool results included. Re-rendering a prompt offline with a chat template
drifts from what the model actually saw, and a prompt that differs by one token silently splits one
long conversation into several short ones.</p> <p><strong>Four wire dialects.</strong> Coding agents did not converge on one API. chat-completions, OpenAI
Responses, Anthropic Messages and Google <code>generateContent</code> are all translated to a single upstream
shape and replayed in the dialect the agent expects, streaming included. That is what makes codex,
claude-code and gemini-cli work rather than only the chat-completions agents.</p> <p><strong>The API key is the session id.</strong> One proxy serves many concurrent rollouts with no port each, and
a caller without a registered session is rejected, so the proxy is safe to expose to a sandbox.</p> <p>Turns are linked into a graph by <strong>exact token prefix</strong>: a call whose <code>prompt_token_ids</code> begin with
an existing node’s full sequence becomes its child. Nothing else is consulted, because request ids
and timestamps are per-agent and the prefix is not. Conversations, retries and subagent branches
fall out of that for free, and a branch the agent abandoned is marked discarded so it is never
trained with the reward the main path earned.</p> <!--[1--><h2 class="relative group"><a id="prerequisites" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#prerequisites"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Prerequisites</span></h2><!--]--><!----> <p>You need an OpenAI-compatible endpoint. Which <em>kind</em> of rollout you get depends on what it can
return, and that is probed at startup rather than configured:</p> <table><thead><tr><th></th><th>eval</th><th>train</th></tr></thead><tbody><tr><td>reward, <code>rewards</code> dict, step results</td><td>yes</td><td>yes</td></tr><tr><td>full trace: conversations, per-turn text, tool calls</td><td>yes</td><td>yes</td></tr><tr><td><code>prompt_token_ids</code>, <code>completion_token_ids</code>, <code>per_token_logps</code></td><td>no</td><td>yes</td></tr><tr><td><code>contract.json</code>, TRL rollout func</td><td>no</td><td>yes</td></tr></tbody></table> <p><strong>For trainable rollouts</strong> the endpoint must return token ids <em>and</em> the sampling distribution’s
logprobs. Both are checked by probing, because both fail silently:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->vllm serve &lt;model&gt; --return-tokens-as-token-ids --logprobs-mode processed_logprobs
<span class="hljs-comment"># or SGLang built from git main (sgl-project/sglang#30917); no serving flag needed</span><!----></pre></div><!----> <p>Without them the endpoint answers every request normally and returns no token ids. Rollouts would
look perfect and contain nothing trainable, and that failure has no loud edge — so the level is
probed before the server binds a port, printed as <code>[EVAL ONLY]</code>, stamped on every result, and no
training contract is ever built from it.</p> <p><strong>For eval rollouts</strong> any reachable OpenAI-spec endpoint works, including hosted ones:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor serve --llm-url https://api.openai.com/v1 --api-key <span class="hljs-variable">$OPENAI_API_KEY</span> --model gpt-5.6-sol
openenv harbor serve --llm-url https://router.huggingface.co/v1 --api-key <span class="hljs-variable">$HF_API_KEY</span> --model Qwen/Qwen3.6-35B-A3B
openenv harbor serve --llm-url https://api.anthropic.com/v1 --api-key <span class="hljs-variable">$ANTHROPIC_API_KEY</span> --model claude-sonnet-5<!----></pre></div><!----> <p>Anthropic needs no <code>--auth-header</code>: its OpenAI-compatible <code>/v1/chat/completions</code> accepts <code>Authorization: Bearer</code>. Its <code>/v1/models</code> does not — that route wants <code>x-api-key</code> <em>and</em> <code>anthropic-version</code> — so the model list comes back empty and <code>--model</code> is required. An endpoint that
publishes no usable model list is not treated as unreachable for exactly this reason; the completion
probe is what decides.</p> <p><code>--api-key</code> (or <code>$OPENENV_LLM_API_KEY</code>) authenticates the proxy to the endpoint; <code>--auth-header</code> changes the header name when a provider wants something other than <code>Authorization: Bearer</code>. This is
not the key the agent receives — that one is a capture session id, minted per rollout — and it never
leaves the server process.</p> <p>A hosted provider also rejects parameters a local vLLM accepts, per model rather than per endpoint.
Every current OpenAI model refuses <code>max_tokens</code> (wants <code>max_completion_tokens</code>), any <code>temperature</code> other than 1, and <code>logprobs</code> outright, while opencode, codex and qwen-coder all send the first two. <code>gpt-5.6</code> additionally refuses <strong>function tools</strong> on <code>/v1/chat/completions</code> unless <code>reasoning_effort</code> is <code>"none"</code> — which for a coding agent is every call that matters; left unhandled
it presents as a rollout that makes one model call and then idles until the agent timeout.</p> <p>The proxy reads each such 400, applies the one edit the provider names, retries, and caches the fix —
so the agents work unchanged. Every applied fix is reported on the result, at startup and in the UI,
because these are changed experiments rather than cosmetic rewrites: dropping <code>temperature</code> alters
the sampling distribution, and <code>reasoning_effort: "none"</code> turns reasoning off. For <code>gpt-5.6</code> the
better answer is the Responses route the error message itself recommends.</p> <!--[2--><h3 class="relative group"><a id="what-startup-checks-about-agents--not-just-capture" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#what-startup-checks-about-agents--not-just-capture"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>What startup checks about agents , not just capture</span></h3><!--]--><!----> <p>The capture probe sends no tools. Every validated harness sends a tool manifest on every call, so a
second, non-fatal probe asks the way a harness asks and reports:</p> <table><thead><tr><th>finding</th><th>meaning</th></tr></thead><tbody><tr><td><code>tools: ok</code></td><td>a tool call came back; agents can work here</td></tr><tr><td><code>no_tool_calling</code> (FATAL)</td><td>the endpoint refuses a manifest outright; no agent rollout is possible</td></tr><tr><td><code>no_tool_call_emitted</code> (WARN)</td><td>manifest accepted, but it answered in prose</td></tr><tr><td><code>behaviour_changed</code> (WARN)</td><td>a compat fix changed how the MODEL behaves, not just a field name</td></tr></tbody></table> <p>The last one is the reason this probe exists. <code>gpt-5.6</code> accepts function tools on <code>/v1/chat/completions</code> only if <code>reasoning_effort</code> is <code>"none"</code>, and with reasoning off it emits one
valid tool call and then agentic loops die: goose and codex each managed a single model call and 0/3
tasks, while both scored 3/3 against a non-reasoning model on the same endpoint. Without a tool in the
probe body that demand is never made, so <code>harbor info</code> looked healthy and the failure only appeared
minutes into a rollout. Now it is reported before a sandbox is booted.</p> <p>Truncation is deliberately treated as inconclusive rather than a failure: a reasoning model spends
output tokens thinking before it calls anything, and a small cap made <code>Qwen3.6-35B-A3B</code> look
tool-incapable when it in fact worked with all 16 harnesses.</p> <!--[2--><h3 class="relative group"><a id="why---logprobs-mode-processedlogprobs-is-not-optional" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#why---logprobs-mode-processedlogprobs-is-not-optional"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Why --logprobs-mode processed_logprobs is not optional</span></h3><!--]--><!----> <p><code>token_ids</code> comes from the <code>return_token_ids</code> <strong>request</strong> parameter, not from either serving flag, so
a vLLM started with neither flag still returns aligned, negative, correctly-counted logprobs and would
grade as fully trainable. But vLLM’s <code>logprobs_mode</code> defaults to <code>raw_logprobs</code> — the values <em>before</em> temperature and top-k/top-p are applied — and GRPO’s importance ratio needs the logprob under the
policy that actually sampled the token.</p> <p>Startup measures which you have, rather than inferring it: the gap between the top two logprobs is
requested at temperature 1.0 and 2.0. Processed logprobs are <code>logsoftmax(logits / T)</code>, so the gap
scales by <code>1/T</code> while the normalising constant cancels; raw logprobs cannot move at all. Measured on
two live Qwen3.5-4B servers:</p> <table><thead><tr><th></th><th>gap @T=1.0</th><th>gap @T=2.0</th><th>verdict</th></tr></thead><tbody><tr><td><code>--logprobs-mode processed_logprobs</code></td><td>6.7500</td><td>3.3750</td><td><code>processed</code></td></tr><tr><td>default</td><td>6.7500</td><td>6.7500</td><td><code>raw</code></td></tr></tbody></table> <p>A measured <code>raw</code> endpoint is downgraded to EVAL rather than refused — it is still a perfectly good
eval backend — and <code>OPENENV_ALLOW_RAW_LOGPROBS=1</code> overrides that if you know better than the probe.
The gap is compared rather than the values themselves because a data-parallel engine answers
consecutive calls from different replicas; comparing values directly misread one such engine.</p> <p>Install the extra, which brings Harbor and every sandbox backend:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->pip install <span class="hljs-string">&quot;openenv[harbor]&quot;</span> <span class="hljs-comment"># needs Python 3.12 or newer</span><!----></pre></div><!----> <!--[1--><h2 class="relative group"><a id="quick-start" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#quick-start"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Quick Start</span></h2><!--]--><!----> <!--[2--><h3 class="relative group"><a id="1-see-what-this-machine-can-do" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#1-see-what-this-machine-can-do"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>1. See what this machine can do</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor info \
--llm-url <span class="hljs-variable">$LLM</span> \
--dataset AdithyaSK/data_agent_rl_environment_eval<!----></pre></div><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class=" "><!---->llm <span class="hljs-symbol">Qwen</span>/<span class="hljs-symbol">Qwen3</span><span class="hljs-number">.5</span><span class="hljs-number">-9</span><span class="hljs-symbol">B</span> [ok]
sandboxes <span class="hljs-number">2</span> of <span class="hljs-number">4</span> usable
[ok] e2b
[ok] modal
[--] docker <span class="hljs-symbol">Docker</span> daemon is not running.
[--] daytona <span class="hljs-symbol">SDK</span> not installed (daytona).
datasets <span class="hljs-number">1</span> split(s), <span class="hljs-number">366</span> tasks
harnesses <span class="hljs-number">16</span> validated of <span class="hljs-number">30</span> known<!----></pre></div><!----> <p>Read-only, boots nothing. It tells you which sandboxes have working credentials <strong>and</strong> an
importable SDK, so you find out here rather than 90 seconds into a rollout.</p> <!--[2--><h3 class="relative group"><a id="2-run-one-rollout-no-server" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#2-run-one-rollout-no-server"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>2. Run one rollout, no server</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor rollout \
--llm-url <span class="hljs-variable">$LLM</span> \
--dataset AdithyaSK/data_agent_rl_environment_eval \
--task-index 0 --harness opencode --sandbox e2b \
--out rollout.json<!----></pre></div><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class=" "><!---->[opencode / e2b] task 0: 0000_369_369503_qa_1 <span class="hljs-built_in">..</span>.
ok <span class="hljs-attribute">reward</span>=1.00 <span class="hljs-attribute">turns</span>=9 <span class="hljs-attribute">roots</span>=2 multi-turn <span class="hljs-attribute">tokens</span>=1043 <span class="hljs-attribute">atif</span>=match 48s<!----></pre></div><!----> <p>This path involves no env server, which makes it the one to reach for when something breaks: if <code>rollout</code> works and <code>serve</code> does not, the fault is in the serving layer and nothing below it.</p> <!--[2--><h3 class="relative group"><a id="3-serve-it" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#3-serve-it"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>3. Serve it</span></h3><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor serve --llm-url <span class="hljs-variable">$LLM</span> --dataset org/train,org/eval<!----></pre></div><!----> <p>You get a Task API for discovery, one long-running <code>run_rollout</code> MCP tool, and a UI at <code>/web</code> for
reading tasks, running agents on them and reading what they did (see <a href="#the-web-ui">The web UI</a>). <code>--llm-url</code> is optional: without it, whoever uses the UI connects a model of their own.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!----><span class="hljs-keyword">from</span> harbor_env <span class="hljs-keyword">import</span> HarborEnv
<span class="hljs-keyword">with</span> HarborEnv(base_url=<span class="hljs-string">&quot;http://localhost:8000&quot;</span>) <span class="hljs-keyword">as</span> env:
split = env.splits()[<span class="hljs-number">0</span>][<span class="hljs-string">&quot;name&quot;</span>]
result = env.run_rollout(split=split, task_index=<span class="hljs-number">0</span>, harness=<span class="hljs-string">&quot;opencode&quot;</span>, sandbox=<span class="hljs-string">&quot;e2b&quot;</span>)
<span class="hljs-built_in">print</span>(result.reward, result.n_turns)
<span class="hljs-keyword">for</span> turn <span class="hljs-keyword">in</span> result.turns:
<span class="hljs-built_in">print</span>(<span class="hljs-built_in">len</span>(turn.completion_token_ids), <span class="hljs-built_in">sum</span>(turn.per_token_logps))<!----></pre></div><!----> <p><code>harness</code> and <code>sandbox</code> are per call, so consecutive rollouts against the same server can use
different agents and different backends.</p> <!--[1--><h2 class="relative group"><a id="the-web-ui" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#the-web-ui"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>The web UI</span></h2><!--]--><!----> <p><code>serve</code> (and a Space made with <code>push</code>) serves a UI at <code>/web</code>, in four tabs.</p> <p><strong>Tasks.</strong> Every task of every served dataset, as cards you can search and filter by dataset,
category, difficulty and tag. Opening one shows exactly what the agent will receive, the task’s files
(with a full-window viewer), the environment and verifier settings from <code>task.toml</code>, and the runs of
that task. Metadata fields that hold the answer (<code>gold_answer</code>, <code>solution</code>, …) are left out of the
summary. <strong>Add from the Hub</strong> lists public datasets tagged <code>harbor</code>, each with its task count and
size, and adds one in the background with its progress shown; a dataset added this way can be
removed again. Only datasets in Harbor’s <code>tasks/&lt;name>/</code> layout (flat or grouped) can be added.</p> <p><strong>Running a task.</strong> The run card on a task picks the model, the agent and the sandbox:</p> <ul><li><strong>This server</strong>: the endpoint <code>serve</code> was started with. Its key stays in the capture proxy.</li> <li><strong>Hugging Face</strong>: a model on Inference Providers, chosen from a searchable list with providers,
context length and price, reached with a Hugging Face token (locally, this machine’s token; on a
Space, optionally by signing in with Hugging Face).</li> <li><strong>Your endpoint</strong>: any OpenAI-compatible URL (vLLM, SGLang, …) or an Anthropic endpoint, probed
before use. Training capture needs vLLM with <code>--return-tokens-as-token-ids</code>.</li></ul> <p>A rollout runs on the server whether or not the page stays open. Model usage is billed to the
account or token selected on the card; sandbox compute is billed to the server operator, including
Hugging Face Sandbox on a Space.</p> <p><strong>Runs.</strong> Every run you may see, filterable by status. A run shows what the agent did as a timeline
(its prompt, thinking, each tool call with its input and output, terminal sessions, the final
answer), the result and reward, the trace checks, and downloads of the result JSON and, for a
trainable rollout, the <strong>Training trace</strong> and <strong>Capture audit</strong>. The training trace is the same
validated <code>TrainingTrace</code> returned by <code>HarborSession.fetch_training_trace()</code>: selected agent calls,
engine token IDs, logprobs and explicit loss masks. Load it with <code>TrainingTrace.model_validate_json(Path("run.training_trace.json").read_text())</code> after importing <code>Path</code> from <code>pathlib</code> and <code>TrainingTrace</code> from <code>openenv.core.harness</code>. Task rewards and the full
rollout remain in Result JSON. Capture audit preserves the existing <code>contract.json</code> export,
including excluded turns. Older runs without explicit masks can still download their audit but
cannot produce the typed training trace. Tick two to four runs to compare them.</p> <p><strong>Setup.</strong> The server’s endpoint, which sandboxes have working credentials, the deployment settings
below, the datasets and the agents.</p> <!--[2--><h3 class="relative group"><a id="deployment-settings" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#deployment-settings"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Deployment settings</span></h3><!--]--><!----> <p>The same UI runs on a laptop and as a public Space, and what a visitor may do differs. The defaults
depend on where the server runs: <strong>local</strong> is <code>serve --host 127.0.0.1</code>; <strong>network</strong> is any other bind
address, including the default <code>0.0.0.0</code>, since everyone who can reach it is then a visitor; a <strong>Space</strong> is detected from <code>SPACE_ID</code>. Each setting is an environment variable (a Space variable on
a Space), and some have a flag on <code>serve</code> and <code>push</code> (<code>--private-urls</code> is on <code>serve</code> only).</p> <table><thead><tr><th>setting</th><th>variable</th><th>flag</th><th>local</th><th>network</th><th>Space</th></tr></thead><tbody><tr><td>Visitors may start rollouts</td><td><code>OPENENV_HARBOR_UI_ROLLOUTS</code></td><td><code>--rollouts</code></td><td>on</td><td>on</td><td>on</td></tr><tr><td>Visitors may use the server’s endpoint</td><td><code>OPENENV_HARBOR_UI_SERVER_ENDPOINT</code></td><td><code>--share-endpoint</code></td><td>on</td><td>on</td><td>off</td></tr><tr><td>Visitors may connect their own</td><td><code>OPENENV_HARBOR_UI_VISITOR_ENDPOINTS</code></td><td><code>--visitor-endpoints</code></td><td>on</td><td>on</td><td>on</td></tr><tr><td>A visitor’s URL may be private or local</td><td><code>OPENENV_HARBOR_UI_PRIVATE_URLS</code></td><td><code>--private-urls</code></td><td>on</td><td>off</td><td>off</td></tr><tr><td>Offer this machine’s HF token</td><td><code>OPENENV_HARBOR_UI_LOCAL_TOKEN</code></td><td></td><td>on</td><td>off</td><td>never</td></tr><tr><td>Add and remove Hub datasets from the page</td><td><code>OPENENV_HARBOR_UI_ADD_DATASETS</code></td><td><code>--add-datasets</code></td><td>on</td><td>off</td><td>off</td></tr><tr><td>Who sees runs (<code>all</code> or <code>own</code>)</td><td><code>OPENENV_HARBOR_RUN_VISIBILITY</code></td><td><code>--run-visibility</code></td><td>all</td><td>all</td><td>own</td></tr><tr><td>Keep finished runs across restarts</td><td><code>OPENENV_HARBOR_RUN_HISTORY</code></td><td><code>--run-history</code></td><td>on</td><td>on</td><td>off</td></tr><tr><td>Headline reward of a task with several</td><td><code>OPENENV_HARBOR_REWARD_KEY</code></td><td><code>--reward-key</code></td><td>unset</td><td>unset</td><td>unset</td></tr><tr><td>Rollouts at once</td><td><code>OPENENV_HARBOR_UI_MAX_RUNS</code></td><td></td><td>4</td><td>4</td><td>4</td></tr><tr><td>Rollouts at once per visitor</td><td><code>OPENENV_HARBOR_UI_MAX_RUNS_PER_VISITOR</code></td><td></td><td>4</td><td>4</td><td>2</td></tr></tbody></table> <p><code>own</code> visibility ties runs to a random id kept in the visitor’s browser; a run stores only a digest
of it. That keeps visitors’ runs apart, but it is not sign-in: a visitor who clears the browser’s
storage loses their runs, and it is no substitute for access control over traces you consider
private. The per-visitor limit counts a signed-in visitor’s Hugging Face account, and otherwise that
browser id, so for anonymous visitors it stops one page from taking every slot, not someone set on
it; <code>OPENENV_HARBOR_UI_MAX_RUNS</code> bounds the total. Run history goes to <code>OPENENV_HARBOR_RUNS_DIR</code>, by default <code>~/.cache/openenv/harbor/runs</code>, or <code>/data/harbor-runs</code> on a Space with the bucket mounted. <code>OPENENV_HARBOR_UI_MAX_ADD_GB</code> (default <code>5</code>)
caps the size of a dataset added from the page.</p> <p>What the UI guarantees whatever the settings:</p> <ul><li>A key or token typed into the page is sent to that endpoint by the server, held in server memory
for that page only, and never written to disk, to run history or back to the page.</li> <li>With private URLs off, a visitor’s URL, and every redirect it answers with, must resolve to public
addresses, and is checked again before each rollout.</li> <li>Every dataset, task and file the page asks for is one the server serves or that was added from
the page. Every file it reads, for a card, the task view, the file viewer or the environment
check, must resolve inside the dataset’s own folder (a link to that folder itself is followed),
so a task reached through a link to anywhere else shows nothing and cannot run on a visitor’s
model. The Task API’s instruction preview follows the same rule, and a registry dataset is
anchored on Harbor’s cache. A dataset added from the page may not contain a symbolic link at all.</li> <li>A task that reads the server’s environment variables (<code>${VAR}</code> in <code>task.toml</code> or a compose file,
or a bare name under a compose <code>environment:</code>), which is where the server’s keys are, or its files
(a compose <code>env_file</code>, <code>include</code>, <code>extends</code>, or a host path in a mount, secret, build context,
cache, watch rule or device), or asks for more of the host than a folder (<code>privileged</code>, <code>cap_add</code>, the host’s namespaces, the Docker socket, another container’s volumes, a named volume
or network with settings, the build’s SSH agent) runs only on the server’s own endpoint, never on
a model a visitor connects: that model does what the visitor asks, printing the sandbox’s
environment included, into a trace the visitor reads. A dataset added from the page may not do
either at all. Both files are checked as parsed, and one that doesn’t parse counts as reading.</li> <li>A request that changes something in the UI (a rollout, an added dataset) is refused when a
browser sends it from another site, so another page can’t act through a visitor’s browser. Behind
a proxy that rewrites <code>Host</code>, list the public host in <code>OPENENV_HARBOR_UI_HOSTS</code> (comma-separated).</li> <li>Everything a model, a task or a tool produced is escaped before it is shown.</li></ul> <!--[1--><h2 class="relative group"><a id="cli-reference" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#cli-reference"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>CLI reference</span></h2><!--]--><!----> <p>Four commands. Every flag below is the complete set, with its type and default. <code>openenv harbor &lt;command> --help</code> prints the same thing.</p> <p>Exit codes:</p> <table><thead><tr><th>code</th><th>meaning</th></tr></thead><tbody><tr><td><code>0</code></td><td>success</td></tr><tr><td><code>1</code></td><td>ran, but failed. <code>rollout</code> returns this if <strong>any</strong> rollout in the batch was unusable</td></tr><tr><td><code>2</code></td><td>usage error: a missing or invalid flag. Nothing ran</td></tr></tbody></table> <p>The <code>1</code> and <code>2</code> split matters if you are scripting this: <code>2</code> means the command never started, so
retrying it unchanged will fail the same way.</p> <!--[2--><h3 class="relative group"><a id="openenv-harbor-info" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#openenv-harbor-info"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>openenv harbor info</span></h3><!--]--><!----> <p>Report what this machine can run. Read-only: boots no sandbox, starts no server, makes no rollout.</p> <table><thead><tr><th>flag</th><th>type</th><th>default</th><th>meaning</th></tr></thead><tbody><tr><td><code>--llm-url</code></td><td>str</td><td><code>""</code></td><td>OpenAI-spec endpoint. Optional here; without it the LLM section is skipped and the rest still reports</td></tr><tr><td><code>--model</code></td><td>str</td><td><code>""</code></td><td>Served model id. Auto-detected when the endpoint serves exactly one</td></tr><tr><td><code>--dataset</code></td><td>str</td><td>none</td><td>Dataset spec. Repeatable, or comma-separated</td></tr><tr><td><code>--env-file</code></td><td>path</td><td><code>""</code></td><td>dotenv with provider credentials, loaded before the checks</td></tr><tr><td><code>--verbose</code></td><td>flag</td><td>off</td><td>List all 30 harnesses, not only the 16 validated</td></tr><tr><td><code>--json</code></td><td>flag</td><td>off</td><td>Emit machine-readable JSON instead of the text report</td></tr></tbody></table> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor info --llm-url <span class="hljs-variable">$LLM</span> --dataset org/tasks --json<!----></pre></div><!----> <p>The JSON has four top-level keys: <code>llm</code>, <code>sandboxes</code>, <code>datasets</code>, <code>harnesses</code>. Each sandbox entry is <code>{name, available, detail}</code>, so a script can select a backend without parsing prose:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor info --llm-url <span class="hljs-variable">$LLM</span> --json \
| jq -r <span class="hljs-string">&#x27;.sandboxes[] | select(.available) | .name&#x27;</span><!----></pre></div><!----> <!--[2--><h3 class="relative group"><a id="openenv-harbor-rollout" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#openenv-harbor-rollout"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>openenv harbor rollout</span></h3><!--]--><!----> <p>Run rollouts with no env server involved. This is the debugging path: if <code>rollout</code> works and <code>serve</code> does not, the fault is in the serving layer and nothing below it.</p> <table><thead><tr><th>flag</th><th>type</th><th>default</th><th>meaning</th></tr></thead><tbody><tr><td><code>--llm-url</code></td><td>str</td><td><strong>required</strong></td><td>OpenAI-spec endpoint. No default and no env fallback, on purpose</td></tr><tr><td><code>--dataset</code></td><td>str</td><td><strong>required</strong></td><td>Dataset spec. Only the first is used by this command</td></tr><tr><td><code>--task-index</code></td><td>int</td><td><code>0</code></td><td>Index into the split. Stable: index is a task’s identity</td></tr><tr><td><code>-n</code>, <code>--n-tasks</code></td><td>int</td><td><code>1</code></td><td>Run this many consecutive tasks from <code>--task-index</code></td></tr><tr><td><code>--harness</code></td><td>str</td><td><code>opencode</code></td><td>A validated seam name, or <code>module:Class</code> for your own agent</td></tr><tr><td><code>--sandbox</code></td><td>str</td><td><code>e2b</code></td><td>Harbor environment type</td></tr><tr><td><code>--model</code></td><td>str</td><td><code>""</code></td><td>Served model id. Auto-detected when unambiguous</td></tr><tr><td><code>--port</code></td><td>int</td><td><code>8100</code></td><td>Local port for the capture proxy. One per concurrent process</td></tr><tr><td><code>--expose</code></td><td>str</td><td><code>gradio</code></td><td>How the sandbox reaches the proxy: <code>gradio</code>, <code>cloudflare</code>, <code>direct</code></td></tr><tr><td><code>--reward-key</code></td><td>str</td><td><code>""</code></td><td>Which reward key is the training signal, for multi-reward tasks</td></tr><tr><td><code>--trials-dir</code></td><td>path</td><td>tmp</td><td>Where Harbor writes trial artifacts</td></tr><tr><td><code>--keep-sandbox</code></td><td>flag</td><td>off</td><td>Leave sandboxes alive for debugging</td></tr><tr><td><code>--force-build</code></td><td>flag</td><td>off</td><td>Rebuild the sandbox image, bypassing the content-hash cache</td></tr><tr><td><code>--env-file</code></td><td>path</td><td><code>""</code></td><td>dotenv with provider credentials</td></tr><tr><td><code>--out</code></td><td>path</td><td><code>""</code></td><td>Write the full result JSON, token ids and logprobs included</td></tr></tbody></table> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor rollout --llm-url <span class="hljs-variable">$LLM</span> --dataset org/tasks \
--task-index 0 -n 5 --harness codex --sandbox modal --out results.json<!----></pre></div><!----> <p><code>-n</code> runs tasks sequentially in one process, reusing one proxy and one forward. To parallelise, run
several processes and <strong>give each its own <code>--port</code></strong>. Two processes sharing a port is refused with
an error naming the process that holds it.</p> <p>Use <code>--force-build</code> when a task has never been built on this account, or when a cached image has
drifted because the task pins its dependencies loosely.</p> <!--[2--><h3 class="relative group"><a id="openenv-harbor-serve" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#openenv-harbor-serve"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>openenv harbor serve</span></h3><!--]--><!----> <p>Start the env server: Task API for discovery, one long-running <code>run_rollout</code> MCP tool, and a UI at <code>/web</code>.</p> <table><thead><tr><th>flag</th><th>type</th><th>default</th><th>meaning</th></tr></thead><tbody><tr><td><code>--llm-url</code></td><td>str</td><td><code>""</code></td><td>OpenAI-spec endpoint. Optional: without one, UI visitors connect their own</td></tr><tr><td><code>--dataset</code></td><td>str</td><td>none</td><td>Dataset specs to serve as splits. Repeatable</td></tr><tr><td><code>--model</code></td><td>str</td><td><code>""</code></td><td>Served model id</td></tr><tr><td><code>--host</code></td><td>str</td><td><code>0.0.0.0</code></td><td>Bind address. <code>127.0.0.1</code> gives the UI its local defaults, see <a href="#deployment-settings">Deployment settings</a></td></tr><tr><td><code>--port</code></td><td>int</td><td><code>8000</code></td><td>Env server port. Faces the trainer and the browser</td></tr><tr><td><code>--capture-port</code></td><td>int</td><td><code>8100</code></td><td>Capture proxy port. Faces the sandbox</td></tr><tr><td><code>--expose</code></td><td>str</td><td><code>gradio</code></td><td>How the sandbox reaches the proxy</td></tr><tr><td><code>--env-file</code></td><td>path</td><td><code>""</code></td><td>dotenv with provider credentials</td></tr><tr><td><code>--api-key</code></td><td>str</td><td><code>$OPENENV_LLM_API_KEY</code></td><td>Credential for the endpoint, for a hosted provider</td></tr><tr><td><code>--auth-header</code></td><td>str</td><td><code>Authorization</code></td><td>Header to send it under, e.g. <code>x-api-key</code></td></tr><tr><td><code>--share-endpoint/--no-share-endpoint</code></td><td>flag</td><td>unset</td><td>UI visitors may use this endpoint and its key</td></tr><tr><td><code>--visitor-endpoints/--no-visitor-endpoints</code></td><td>flag</td><td>unset</td><td>UI visitors may connect their own model</td></tr><tr><td><code>--run-visibility</code></td><td>str</td><td>unset</td><td><code>all</code> or <code>own</code>: who sees runs in the UI</td></tr><tr><td><code>--run-history/--no-run-history</code></td><td>flag</td><td>unset</td><td>Keep finished UI runs across restarts</td></tr><tr><td><code>--add-datasets/--no-add-datasets</code></td><td>flag</td><td>unset</td><td>UI visitors may add and remove Hub datasets</td></tr><tr><td><code>--rollouts/--no-rollouts</code></td><td>flag</td><td>unset</td><td>UI visitors may start rollouts</td></tr><tr><td><code>--private-urls/--no-private-urls</code></td><td>flag</td><td>unset</td><td>UI visitors may connect an endpoint on a private or local address</td></tr><tr><td><code>--reward-key</code></td><td>str</td><td>unset</td><td>The reward UI runs report for tasks with several and none named <code>reward</code>. Unset, such a run lists each one</td></tr></tbody></table> <p>An unset UI flag keeps the default for where the server runs.</p> <p>Refuses to start only if the endpoint is unreachable. One that cannot return token ids starts as an eval deployment and says so.</p> <!--[2--><h3 class="relative group"><a id="openenv-harbor-push" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#openenv-harbor-push"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>openenv harbor push</span></h3><!--]--><!----> <p>Deploy the same server to a Hugging Face Space.</p> <table><thead><tr><th>flag</th><th>type</th><th>default</th><th>meaning</th></tr></thead><tbody><tr><td><code>--llm-url</code></td><td>str</td><td><code>""</code></td><td>Endpoint the deployed Space will use. Optional: without one, visitors connect their own</td></tr><tr><td><code>--repo-id</code></td><td>str</td><td><strong>required</strong></td><td>Target Space, e.g. <code>you/harbor-env</code></td></tr><tr><td><code>--dataset</code></td><td>str</td><td>none</td><td>Dataset specs. Repeatable</td></tr><tr><td><code>--model</code></td><td>str</td><td><code>""</code></td><td>Served model id</td></tr><tr><td><code>--bucket</code></td><td>str</td><td>Space name</td><td>Storage bucket holding the task suites. <code>none</code> disables the mount and downloads instead</td></tr><tr><td><code>--public-bucket/--private-bucket</code></td><td>flag</td><td>private</td><td>Visibility of a new bucket. Given for an existing bucket, it changes it; not given, an existing bucket keeps its visibility</td></tr><tr><td><code>--hf-login/--no-hf-login</code></td><td>flag</td><td>on</td><td>Let visitors sign in with Hugging Face to use their own account for Inference Providers</td></tr><tr><td><code>--hardware</code></td><td>str</td><td><code>""</code></td><td>Space hardware, e.g. <code>cpu-basic</code></td></tr><tr><td><code>--private</code></td><td>flag</td><td>off</td><td>Create the Space private. Rollouts then cannot work, see below</td></tr><tr><td><code>--recreate</code></td><td>flag</td><td>off</td><td>Delete the Space first, then deploy fresh</td></tr><tr><td><code>--dry-run</code></td><td>flag</td><td>off</td><td>Print exactly what would be sent and stop</td></tr><tr><td><code>--env-file</code></td><td>path</td><td><code>""</code></td><td>dotenv whose provider keys become Space <strong>secrets</strong></td></tr></tbody></table> <p><code>push</code> also takes the UI flags of <code>serve</code> (<code>--share-endpoint</code>, <code>--visitor-endpoints</code>, <code>--run-visibility</code>, <code>--run-history</code>, <code>--add-datasets</code>, <code>--rollouts</code>, <code>--reward-key</code>) and sets them as Space
variables. Unlike <code>serve</code>, it doesn’t share the endpoint with UI visitors unless <code>--share-endpoint</code> is
given. Each push reconciles these UI variables: an omitted flag removes an earlier override and
returns to the Space default, so a one-time <code>--share-endpoint</code> does not stay enabled forever.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor push --llm-url <span class="hljs-variable">$LLM</span> --dataset org/train,org/eval \
--repo-id you/harbor-env --env-file .<span class="hljs-built_in">env</span> --dry-run<!----></pre></div><!----> <p><code>--private</code> is supported but rollouts will not work on a private Space: the capture proxy is served
at <code>&lt;space-url>/capture</code>, and a private Space requires an auth header the sandboxed agent does not
send. Use it only to park a deployment.</p> <!--[2--><h3 class="relative group"><a id="migration-notes-for-the-new-ui" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#migration-notes-for-the-new-ui"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Migration notes for the new UI</span></h3><!--]--><!----> <ul><li><code>serve</code> binds <code>0.0.0.0</code> by default and therefore uses the network-safe UI defaults. Visitor
endpoints such as <code>http://localhost:8000/v1</code> are refused. Bind the whole server to <code>--host 127.0.0.1</code>, or pass <code>--private-urls</code> when the server still needs a remote trainer.</li> <li>An existing Space re-pushed with <code>--llm-url</code> no longer shares that endpoint with UI visitors
unless <code>--share-endpoint</code> is passed. <code>--hf-login</code> also writes <code>hf_oauth: true</code> and the <code>inference-api</code> OAuth scope into the Space README.</li> <li>Capture <code>/health</code> still returns status and aggregate fields publicly, but its per-session <code>upstreams</code> list is empty when an admin key is configured. Set <code>OPENENV_CAPTURE_ADMIN_KEY</code> and send it as a bearer token to inspect that field.</li></ul> <!--[1--><h2 class="relative group"><a id="supported-harnesses" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#supported-harnesses"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Supported harnesses</span></h2><!--]--><!----> <p>16 of the 30 known agents are validated end to end. “Validated” means a real rollout produced token
ids and logprobs, and where the agent emits a trajectory, its own record agreed with the capture.</p> <table><thead><tr><th>harness</th><th>dialect</th><th>runs</th></tr></thead><tbody><tr><td><code>opencode</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>goose</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>qwen-coder</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>swe-agent</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>mini-swe-agent</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>openhands-sdk</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>openclaw</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>hermes</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>kimi-cli</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>pi</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>vibe</code></td><td>chat-completions</td><td>in sandbox</td></tr><tr><td><code>terminus-2</code></td><td>chat-completions</td><td><strong>host side</strong></td></tr><tr><td><code>codex</code></td><td>OpenAI Responses</td><td>in sandbox</td></tr><tr><td><code>trae-agent</code></td><td>OpenAI Responses</td><td>in sandbox</td></tr><tr><td><code>claude-code</code></td><td>Anthropic Messages</td><td>in sandbox</td></tr><tr><td><code>gemini-cli</code></td><td>Google generateContent</td><td>in sandbox</td></tr></tbody></table> <p>Supporting four dialects rather than chat-completions alone is what makes the last four rows work.</p> <p><code>terminus-2</code> runs in the server process rather than inside the sandbox, so it reaches the proxy on
localhost and needs no public URL.</p> <p>The other 14 known agents have a seam but are untested; run <code>openenv harbor info --verbose</code> to list
them. Anything Harbor supports can be reached with <code>--harness module:Class</code>.</p> <!--[1--><h2 class="relative group"><a id="supported-sandboxes" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#supported-sandboxes"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Supported sandboxes</span></h2><!--]--><!----> <p><code>openenv harbor info</code> checks these four by default and reports why any is unusable:</p> <table><thead><tr><th>sandbox</th><th>credentials</th><th>validated</th></tr></thead><tbody><tr><td><code>e2b</code></td><td><code>E2B_API_KEY</code></td><td>yes, extensively</td></tr><tr><td><code>modal</code></td><td><code>MODAL_TOKEN_ID</code> + <code>MODAL_TOKEN_SECRET</code>, or <code>~/.modal.toml</code></td><td>yes, extensively</td></tr><tr><td><code>docker</code></td><td>none, but the daemon must be running</td><td>works, not swept</td></tr><tr><td><code>daytona</code></td><td><code>DAYTONA_API_KEY</code>, or <code>DAYTONA_JWT_TOKEN</code> + <code>DAYTONA_ORGANIZATION_ID</code></td><td>not swept</td></tr></tbody></table> <p>e2b and modal were compared on identical tasks and came out indistinguishable, which is the check
that matters: a backend-specific capture bug is exactly what a single-backend test hides.</p> <p>Harbor registers 23 backends in total. Any of them can be passed to <code>--sandbox</code>; the four above are
the ones with a credential check wired in.</p> <!--[1--><h2 class="relative group"><a id="environment-details" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#environment-details"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Environment details</span></h2><!--]--><!----> <!--[2--><h3 class="relative group"><a id="where-the-proxy-runs" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#where-the-proxy-runs"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Where the proxy runs</span></h3><!--]--><!----> <p>Locally there are two ports: the env server faces the trainer and the browser, the capture proxy
faces the sandbox and is the only one published. Sharing one port would expose the env server the
moment the proxy became reachable.</p> <p>Hosted, that inverts. A Space has one port and one public URL, so the proxy is mounted on the env
server’s own app at <code>/capture</code> and nothing is forwarded. It still rejects callers without a
registered session id, which is what keeps a public mount from being an open relay.</p> <!--[2--><h3 class="relative group"><a id="sandboxes-and-providers" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#sandboxes-and-providers"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Sandboxes and providers</span></h3><!--]--><!----> <p>Two different things share the word “sandbox”, and mixing them up is a common early confusion:</p> <ul><li><strong>Harbor backends</strong> are where the <em>agent</em> runs. That is what <code>--sandbox</code> selects.</li> <li><strong>OpenEnv providers</strong> (<code>local_docker</code>, <code>hf_sandbox</code>, <code>modal</code>, <code>aca</code>, …) host the <em>env server</em> itself and have no <code>exec</code>.</li></ul> <p>A backend counts as usable only if its class imports <strong>and</strong> Harbor’s own preflight passes.
Credentials alone are not enough: a provider with valid keys but no SDK installed would otherwise
report available and fail at rollout time.</p> <!--[2--><h3 class="relative group"><a id="rewards" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#rewards"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Rewards</span></h3><!--]--><!----> <p>The verifier produces a dictionary. OpenEnv wants one number. The dictionary is forwarded unchanged
and the scalar is chosen by an explicit rule:</p> <ol><li>one key, use it</li> <li>a key named <code>reward</code>, use it</li> <li>otherwise fail and ask for <code>--reward-key</code></li></ol> <p>Combining several keys automatically would be inventing reward semantics, so it refuses instead.
All shaping belongs in the trainer.</p> <p><strong><code>reward=None</code> is not zero.</strong> It means the verifier never ran. A dead sandbox scored as zero looks
like a wrong answer, which is how an infrastructure failure gets mistaken for a model result.</p> <!--[2--><h3 class="relative group"><a id="reading-a-result" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#reading-a-result"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Reading a result</span></h3><!--]--><!----> <table><thead><tr><th>field</th><th>meaning</th></tr></thead><tbody><tr><td><code>ok</code></td><td>the rollout is usable. <code>False</code> means something failed, and <code>error</code> says what</td></tr><tr><td><code>reward</code></td><td>the verifier’s number, or <code>None</code> if it never ran</td></tr><tr><td><code>n_turns</code></td><td>model calls captured</td></tr><tr><td><code>n_roots</code></td><td>independent conversations. More than one means subagents or auxiliary calls</td></tr><tr><td><code>turns[]</code></td><td>per-call token ids, logprobs, text and tool calls</td></tr><tr><td><code>conversations[]</code></td><td>the full message list per conversation, system prompt included</td></tr><tr><td><code>atif</code></td><td><code>match</code>, <code>MISMATCH</code>, or <code>none</code>. See below</td></tr><tr><td><code>findings</code></td><td>warnings worth reading before training on the rollout</td></tr></tbody></table> <p><code>atif</code> is an independent cross-check. Harbor’s own trajectory file records what the <em>harness</em> thought happened; the capture records what crossed the wire. Two measurements of the same rollout
through completely different paths. <code>match</code> means they agree call for call.</p> <p><strong>A failed rollout returns a result, never an exception.</strong> That is deliberate. In an in-process
design one exception on one rank hangs every rank at the distributed barrier, so behind an HTTP
boundary a failure has to come back as <code>ok=False</code> instead.</p> <!--[1--><h2 class="relative group"><a id="deploying-to-hugging-face-spaces" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#deploying-to-hugging-face-spaces"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Deploying to Hugging Face Spaces</span></h2><!--]--><!----> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->openenv harbor push \
--llm-url <span class="hljs-variable">$LLM</span> \
--dataset org/train,org/eval \
--repo-id you/harbor-env \
--env-file .<span class="hljs-built_in">env</span><!----></pre></div><!----> <p>Configuration travels as Space variables, provider credentials as Space secrets. Add <code>--dry-run</code> to print exactly what would be sent first, and <code>--recreate</code> to delete and redeploy for a clean test.</p> <p>Two details that matter:</p> <p><strong>Task suites are mounted, not downloaded.</strong> A Harbor suite is thousands of small files and Space
disk is ephemeral, so a download is re-paid on every restart. <code>push</code> syncs the suites into a storage
bucket named after the Space and mounts it at <code>/data</code>. The copy is server side, and re-running <code>push</code> copies only what is new. The bucket is created private (<code>--public-bucket</code> to change that),
since with run history on it also holds every visitor’s runs. With <code>--add-datasets</code>, the <code>tasks/</code> folder of a dataset added from the UI is copied into the same bucket, server side, and read through
the mount, so it survives restarts; removing it deletes it from the bucket.</p> <p><strong>Visitors can sign in with Hugging Face.</strong> <code>push</code> turns on OAuth for the Space (<code>hf_oauth: true</code>,
scope <code>inference-api</code>), and the UI offers “sign in with Hugging Face” as a way to use Inference
Providers on the visitor’s own account instead of pasting a token. It is optional and not needed
for anything else; <code>--no-hf-login</code> leaves it out.</p> <p><strong>Visitors bring their own model.</strong> On a Space, a visitor runs on a model they connect: signing in
with Hugging Face, a token, or their own endpoint. <code>--share-endpoint</code> lets them use the Space’s own
endpoint and key too, at your cost; the Task API and MCP use it either way. On that endpoint a
served task that reads the Space’s environment does run, and the visitor who starts it reads its
trace, so don’t combine <code>--share-endpoint</code> with such tasks on a public Space.</p> <p><strong>Rollouts on a Space use the Space’s credentials for sandboxes.</strong> An <code>hf-sandbox</code> rollout is billed
to the <code>HF_TOKEN</code> the Space holds, whoever started it. <code>--no-rollouts</code> makes a public deployment a
read-only task browser.</p> <p><strong>The Space must be public.</strong> The capture proxy is served at <code>&lt;space-url>/capture</code>, and a private
Space requires an auth header that the agent inside the sandbox does not send. This is safe because
the proxy rejects any caller without a registered session id, so a public mount is not an open
relay.</p> <!--[1--><h2 class="relative group"><a id="troubleshooting" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#troubleshooting"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Troubleshooting</span></h2><!--]--><!----> <p><strong>Rollout finishes with zero model calls.</strong> The agent never reached the proxy. Usually the endpoint
URL is wrong, the model name did not resolve, or auth was rejected. Check the <code>findings</code> field,
which names the likely cause.</p> <p><strong><code>llm ... [FAILED]</code> at startup.</strong> The endpoint is unreachable — wrong URL, wrong model name, or
a missing <code>--api-key</code> for a provider that needs one. The findings name which.</p> <p><strong><code>llm ... [EVAL ONLY]</code> at startup.</strong> The endpoint answers but returns no token ids, so rollouts
carry the reward and the trace and nothing trainable. Expected for OpenAI, Anthropic and HF
Inference Providers. If you meant to train, restart the engine with <code>--return-tokens-as-token-ids --logprobs-mode processed_logprobs</code>, or build SGLang from git main —
released SGLang carries only <code>return_prompt_token_ids</code>, which is not enough.</p> <p><strong><code>contract.json</code> is missing after a rollout.</strong> The rollout was an eval rollout; check <code>rollout_type</code> and <code>capture_level</code> on the result. There is deliberately no all-zero contract to
download, because a file named <code>contract.json</code> containing no contract is more convincing than an
empty list.</p> <p><strong>A sandbox shows <code>[--]</code> in <code>info</code>.</strong> The detail column says why, and it is usually a missing
credential or a missing SDK. Install everything with <code>pip install "openenv[harbor]"</code>.</p> <p><strong><code>atif=none</code>.</strong> That harness writes no trajectory file, so no cross-check is possible. Capture is
unaffected.</p> <p><strong>Many roots for one rollout.</strong> Normal for agents that run subagents or auxiliary calls. Each root
is a separate conversation, and only agent conversations are counted as trainable.</p> <p><strong>A dataset is greyed out in “Add from the Hub”.</strong> It has no <code>tasks/&lt;name>/</code> folder, which is how
Harbor datasets are laid out, or it is over <code>OPENENV_HARBOR_UI_MAX_ADD_GB</code>.</p> <p><strong>A URL is refused as “private or local”.</strong> The server does not call private addresses for visitors
unless <code>OPENENV_HARBOR_UI_PRIVATE_URLS</code> is on, which by default it is only for <code>--host 127.0.0.1</code>; <code>serve --private-urls</code> turns it on for any bind address. To reach a vLLM on your own network from a
Space, run the UI yourself.</p> <p><strong>Exit code 137.</strong> The agent was killed inside the sandbox, almost always by the OOM killer on a
large input. That is a task failure, not a capture failure.</p> <!--[1--><h2 class="relative group"><a id="references" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#references"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>References</span></h2><!--]--><!----> <ul><li><a href="https://github.com/laude-institute/harbor" rel="nofollow">Harbor</a>, which provides the datasets, sandboxes,
agents and verifiers</li> <li><a href="https://github.com/NVIDIA-NeMo/ProRL-Agent-Server" rel="nofollow">Polar</a> (<a href="https://arxiv.org/abs/2605.24220" rel="nofollow">paper</a>), the black-box approach this capture layer follows,
and the source of the vendored dialect transformers</li> <li><a href="https://github.com/willccbb/verifiers" rel="nofollow">verifiers</a>, whose <code>Dialect</code> model informed the auxiliary
route and streaming handling</li></ul> <a class="!text-gray-400 !no-underline text-sm flex items-center not-prose mt-4" href="https://github.com/huggingface/openenv/blob/main/docs/source/environments/harbor.md" target="_blank"><svg class="mr-1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M31,16l-7,7l-1.41-1.41L28.17,16l-5.58-5.59L24,9l7,7z"></path><path d="M1,16l7-7l1.41,1.41L3.83,16l5.58,5.59L8,23l-7-7z"></path><path d="M12.419,25.484L17.639,6.552l1.932,0.518L14.351,26.002z"></path></svg><!----> <span><span class="underline">Update</span> on GitHub</span></a><!----> <p></p><!--]--><!----><!--]--><!--]--><!--]--> <!--[-1--><!--]--><!--]-->
<script>
{
__sveltekit_8myjr3 = {
base: "/docs/openenv/main/en",
assets: "/docs/openenv/main/en"
};
const element = document.currentScript.parentElement;
Promise.all([
import("/docs/openenv/main/en/_app/immutable/entry/start.C6uwgA56.js"),
import("/docs/openenv/main/en/_app/immutable/entry/app.DdtfYW14.js")
]).then(([kit, app]) => {
kit.start(app, element, {
node_ids: [0, 2],
data: [null,null],
form: null,
error: null
});
});
}
</script>

Xet Storage Details

Size:
114 kB
·
Xet hash:
5a2e045b07c9a98907f0a57933e35958224cc146346c20f2e5c578775e4e73eb

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.