ToolsEnabled Fleet for OpenShell beta 3 (Linux x64)
Pre-releaseToolsEnabled Fleet for OpenShell, beta 3 (version 1.4.2), for an existing NVIDIA OpenShell sandbox on Linux x86_64, and on Windows through WSL 2 with Docker Desktop's Linux engine (the same Linux archive; there is no native Windows runtime). ToolsEnabled Fleet is agent fleet management and mediation from ToolsEnabled, Inc.
Fleet is a terminal MCP server for the Codex and Claude Code CLIs. It adds a shared work record, memory, coordinated file edits and an optional tree of worker agents. OpenShell provides filesystem and network confinement, access approvals and credential custody.
What's new in beta 3
- A two-command install. One command on your host verifies and transfers the release. A second command, which the first one prints, installs Fleet inside your sandbox and runs setup and status.
- Upgrades from beta 2 replace the verified runtime and keep your setup, state and registrations. An unknown file or a changed path stops the upgrade and keeps evidence for recovery.
- Uninstall after a restart. A pinned
uninstall --keep-stateworks after an OpenShell sandbox restart. It keeps state, services and workspace data. - Stricter checks. Runtime ownership and file permissions are checked more strictly, and common permission differences from archive extraction are handled without accepting writable runtime files.
Install
Your sandbox needs Node.js 22.19.0 or newer, Python 3.9 or newer at /usr/bin/python3, and Codex and/or Claude Code, installed by you. Your Linux or WSL host needs OpenShell 0.1.2, Python 3.9 or newer, Bash and curl. The install does not change your provider installation, your credentials, your OpenShell policy or your shell startup files.
- On your host (Linux, or the Bash shell in WSL), copy the command below. Replace
GATEWAY_NAME,WORKSPACE_NAMEandSANDBOX_NAMEat its end with your OpenShell gateway, workspace (usuallydefault) and sandbox, then run it. It downloads the release helper from this release and checks its SHA-256, downloads and checks the archive andSHA256SUMS, checks the target sandbox twice, and uploads the archive. It installs nothing. It ends withTransfer completed; installation has not run.and prints a second command. - In your sandbox's Bash shell (for example
openshell sandbox connect SANDBOX_NAME), paste and run that printed command. It rechecks the archive, installs Fleet to~/.local/toolsenabled, runs setup and status, and makestoolsenabledavailable in that shell. In a new shell, runsource ~/.local/toolsenabled/env.shfirst.
Upgrading from beta 2: at the end of the command, replace --tier guided --providers codex,claude with --upgrade. The tier and worker choices apply only to fresh installs. The installer guide covers options, upgrades, recovery and uninstall. The Windows (WSL 2) guide covers the Windows setup; its dated status section predates this release, so use these notes for current results.
Host command (the same bytes on Linux and in WSL):
python3 -I -c 'import contextlib
import hashlib
import json
import math
import os
from pathlib import Path
import selectors
import signal
import stat
import subprocess
import sys
import tempfile
import time
class FetchError(Exception):
def __init__(self, phase, message, attempted=False):
super().__init__(message)
self.phase = phase
self.attempted = attempted
def escaped(value, limit=2048):
return json.dumps(str(value)[:limit], ensure_ascii=True)
class HostInterrupted(KeyboardInterrupt):
def __init__(self, message, attempted=False):
super().__init__(message)
self.attempted = attempted
class Cancellation:
def __init__(self):
self.number = None
def latch(self, number, _frame):
self.number = self.number or number
def checkpoint(self, attempted=False):
if self.number is not None:
raise HostInterrupted('"'"'Host signal '"'"' + str(self.number), attempted=attempted)
_host_cancellation = None
def cancellation_checkpoint(attempted=False):
if _host_cancellation is not None:
_host_cancellation.checkpoint(attempted)
@contextlib.contextmanager
def host_signals():
# Catchable cancellation must not interrupt Popen'"'"'s return/assignment,
# descriptor cleanup, or outcome recording. No mask reaches the child.
global _host_cancellation
if _host_cancellation is not None:
yield _host_cancellation
return
cancellation, previous = Cancellation(), {}
try:
_host_cancellation = cancellation
for number in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP):
previous[number] = signal.signal(number, cancellation.latch)
yield cancellation
finally:
for number, handler in previous.items():
signal.signal(number, handler)
_host_cancellation = None
# Check after cleanup/handler restoration as well; preserve a primary
# exception on exceptional exit. SIGKILL remains outside this contract.
cancellation.checkpoint()
def require_pidfds():
if sys.platform != '"'"'linux'"'"' or sys.version_info < (3, 9):
raise FetchError('"'"'PREFLIGHT'"'"', '"'"'Linux/WSL with Python 3.9 or later is required'"'"')
if not callable(getattr(os, '"'"'pidfd_open'"'"', None)) or not callable(getattr(signal, '"'"'pidfd_send_signal'"'"', None)):
raise FetchError('"'"'PREFLIGHT'"'"', '"'"'Python and kernel pidfd support is required'"'"')
if not callable(getattr(os, '"'"'waitid'"'"', None)) or not all(hasattr(os, name) for name in ('"'"'P_PID'"'"', '"'"'WEXITED'"'"', '"'"'WNOHANG'"'"', '"'"'WNOWAIT'"'"')):
raise FetchError('"'"'PREFLIGHT'"'"', '"'"'Non-reaping child status observation is required'"'"')
if signal.getsignal(signal.SIGCHLD) != signal.SIG_DFL:
raise FetchError('"'"'PREFLIGHT'"'"', '"'"'Default SIGCHLD handling is required to retain child ownership'"'"')
fd = None
try:
fd = os.pidfd_open(os.getpid())
signal.pidfd_send_signal(fd, 0)
except OSError as error:
raise FetchError('"'"'PREFLIGHT'"'"', '"'"'Working kernel pidfds are required; no PID-only fallback'"'"') from error
finally:
if fd is not None:
os.close(fd)
def identity(pid):
try:
value = Path('"'"'/proc'"'"', str(pid), '"'"'stat'"'"').read_text()
fields = value[value.rindex('"'"')'"'"') + 2:].split()
return (int(fields[19]), int(fields[2]), int(fields[3]), fields[0])
except FileNotFoundError:
return None
def owned_members(pid, birth):
result = {}
for entry in Path('"'"'/proc'"'"').iterdir():
if entry.name.isdigit():
observed = identity(int(entry.name))
if observed and observed[3] != '"'"'Z'"'"' and observed[1:3] == (pid, pid):
if observed[0] < birth:
raise FetchError('"'"'CLEANUP'"'"', '"'"'Owned process-session identity changed'"'"')
result[int(entry.name)] = observed
return result
def signal_owned(pid, expected, sig):
fd = None
try:
current = identity(pid)
if current is None or current[:3] != expected[:3]:
return
fd = os.pidfd_open(pid)
current = identity(pid)
if current is None or current[:3] != expected[:3]:
return
signal.pidfd_send_signal(fd, sig)
except ProcessLookupError:
pass
finally:
if fd is not None:
os.close(fd)
def cleanup_owned(child, birth):
deadline = time.monotonic() + 20
soft_until = time.monotonic() + 1
while True:
members = owned_members(child.pid, birth)
if not members:
child.wait(timeout=max(0.01, deadline - time.monotonic()))
return
if time.monotonic() >= deadline:
raise FetchError('"'"'CLEANUP'"'"', '"'"'Owned command children survived cleanup; local evidence retained'"'"')
for pid, expected in members.items():
signal_owned(pid, expected, signal.SIGTERM if time.monotonic() < soft_until else signal.SIGKILL)
time.sleep(0.025)
def run_owned(argv, env, cwd, timeout, max_stdout=262144, max_stderr=65536, sink=None):
"""Bound both streams independently, retain identity, and clean owned session.
This supervises ordinary OpenShell/curl child sessions. It does not claim
control of remote processes or of deliberately daemonized hostile children.
"""
with host_signals() as cancellation:
return _run_owned(argv, env, cwd, timeout, max_stdout, max_stderr, sink, cancellation)
def _run_owned(argv, env, cwd, timeout, max_stdout, max_stderr, sink, cancellation):
child = None
birth = None
leader_fd = None
streams = None
members_settled = False
output = {'"'"'stdout'"'"': bytearray(), '"'"'stderr'"'"': bytearray()}
counts = {'"'"'stdout'"'"': 0, '"'"'stderr'"'"': 0}
deadline = time.monotonic() + timeout
try:
require_pidfds()
cancellation.checkpoint()
child = subprocess.Popen(argv, env=env, cwd=cwd, stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True)
# Keep the direct child unreaped until every owned-session check is
# complete. Its zombie reserves the PID/session number after exit;
# a pidfd alone does not reserve that numeric identity after reaping.
leader_fd = os.pidfd_open(child.pid)
observed = identity(child.pid)
if not observed or observed[1:3] != (child.pid, child.pid):
raise FetchError('"'"'PROCESS'"'"', '"'"'Cannot bind launched command to its private session'"'"', attempted=True)
birth = observed[0]
cancellation.checkpoint(attempted=True)
streams = selectors.DefaultSelector()
for label, pipe in [('"'"'stdout'"'"', child.stdout), ('"'"'stderr'"'"', child.stderr)]:
os.set_blocking(pipe.fileno(), False)
streams.register(pipe, selectors.EVENT_READ, label)
exited = False
while True:
cancellation.checkpoint(attempted=True)
if not exited:
observed_exit = os.waitid(os.P_PID, child.pid, os.WEXITED | os.WNOHANG | os.WNOWAIT)
exited = observed_exit is not None and observed_exit.si_pid == child.pid
if exited and not streams.get_map():
break
remaining = deadline - time.monotonic()
if remaining <= 0:
raise FetchError('"'"'TIMEOUT'"'"', '"'"'Command deadline reached'"'"', attempted=True)
for key, _ in streams.select(min(remaining, 0.05)):
chunk = os.read(key.fileobj.fileno(), 65536)
if not chunk:
streams.unregister(key.fileobj)
continue
label = key.data
counts[label] += len(chunk)
if counts[label] > (max_stdout if label == '"'"'stdout'"'"' else max_stderr):
raise FetchError('"'"'OUTPUT_LIMIT'"'"', '"'"'Command output limit exceeded'"'"', attempted=True)
if label == '"'"'stdout'"'"' and sink is not None:
sink.write(chunk)
else:
output[label].extend(chunk)
if owned_members(child.pid, birth):
raise FetchError('"'"'PROCESS'"'"', '"'"'Command exited with owned children'"'"', attempted=True)
members_settled = True
status = child.wait(timeout=20)
cancellation.checkpoint(attempted=True)
except BaseException as error:
try:
if child is None:
pass
elif members_settled:
# No session scans/signals may occur after the final reap.
child.wait(timeout=20)
elif birth is not None:
cleanup_owned(child, birth)
else:
# The direct Popen child has not been polled/reaped; use its pidfd.
if leader_fd is None:
leader_fd = os.pidfd_open(child.pid)
signal.pidfd_send_signal(leader_fd, signal.SIGKILL)
child.wait(timeout=20)
except Exception as cleanup_error:
raise FetchError('"'"'CLEANUP'"'"', '"'"'Owned command cleanup was not verified'"'"', attempted=True) from cleanup_error
if isinstance(error, KeyboardInterrupt):
raise HostInterrupted(str(error), attempted=child is not None) from error
if isinstance(error, (FetchError, KeyboardInterrupt, SystemExit)):
raise
raise FetchError('"'"'PROCESS'"'"', '"'"'Command launch or I/O failed'"'"', attempted=child is not None) from error
finally:
closing = ([streams.close] if streams is not None else [])
if leader_fd is not None:
closing.append(lambda: os.close(leader_fd))
if child is not None:
closing.extend([child.stdout.close, child.stderr.close])
close_error = None
for close in closing:
try:
close()
except BaseException as error:
close_error = error
if close_error is not None:
raise FetchError('"'"'CLEANUP'"'"', '"'"'Owned command descriptor cleanup failed'"'"', attempted=child is not None) from close_error
cancellation.checkpoint(attempted=True)
return status, bytes(output['"'"'stdout'"'"']), bytes(output['"'"'stderr'"'"'])
def download(url, target, ceiling, curl, env, cwd, runner=run_owned):
"""Fresh bounded attempts; independently cap bodies without Content-Length."""
deadline = time.monotonic() + 180
for attempt in range(3):
remaining = deadline - time.monotonic()
if remaining <= 0:
raise FetchError('"'"'DOWNLOAD'"'"', '"'"'Download deadline reached'"'"')
fd = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
created = os.fstat(fd)
try:
with os.fdopen(fd, '"'"'wb'"'"') as sink:
result = runner([curl, '"'"'-q'"'"', '"'"'--fail'"'"', '"'"'--silent'"'"', '"'"'--show-error'"'"', '"'"'--location'"'"',
'"'"'--proto'"'"', '"'"'=https'"'"', '"'"'--proto-redir'"'"', '"'"'=https'"'"', '"'"'--max-redirs'"'"', '"'"'3'"'"',
'"'"'--connect-timeout'"'"', '"'"'15'"'"', '"'"'--max-time'"'"', str(max(1, math.ceil(remaining))),
'"'"'--retry'"'"', '"'"'0'"'"', '"'"'--max-filesize'"'"', str(ceiling), '"'"'--url'"'"', url],
env, cwd, remaining, max_stdout=ceiling, sink=sink)
sink.flush()
os.fsync(sink.fileno())
if result[0] == 0:
return
if result[0] not in (5, 6, 7, 18, 22, 28, 35, 52, 56) or attempt == 2:
raise FetchError('"'"'DOWNLOAD'"'"', '"'"'HTTPS download failed, exit '"'"' + str(result[0]))
except FetchError as error:
if error.phase == '"'"'CLEANUP'"'"':
raise
raise FetchError('"'"'DOWNLOAD'"'"', str(error)) from error
current = os.lstat(target)
if (current.st_dev, current.st_ino) != (created.st_dev, created.st_ino) or not stat.S_ISREG(current.st_mode):
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Owned download file changed before retry'"'"')
os.unlink(target)
def digest_file(path, ceiling):
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
try:
info = os.fstat(fd)
if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_size > ceiling:
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Downloaded file identity/type/size is invalid'"'"')
digest = hashlib.sha256()
size = 0
with os.fdopen(os.dup(fd), '"'"'rb'"'"') as source:
for chunk in iter(lambda: source.read(65536), b'"'"''"'"'):
size += len(chunk)
if size > ceiling:
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Downloaded file exceeded byte limit'"'"')
digest.update(chunk)
return digest.hexdigest()
finally:
os.close(fd)
import re
import unicodedata
def parse_arguments(argv):
values = {'"'"'workspace'"'"': '"'"'default'"'"', '"'"'prefix'"'"': None, '"'"'tier'"'"': '"'"'unrestricted'"'"', '"'"'providers'"'"': '"'"'codex,claude'"'"', '"'"'upgrade'"'"': False}
seen = set()
while argv:
flag, *argv = argv
if flag not in ('"'"'--gateway'"'"', '"'"'--sandbox'"'"', '"'"'--workspace'"'"', '"'"'--prefix'"'"', '"'"'--tier'"'"', '"'"'--providers'"'"', '"'"'--upgrade'"'"') or flag in seen:
raise FetchError('"'"'USAGE'"'"', '"'"'Unknown or repeated option'"'"')
seen.add(flag)
name = flag[2:]
if flag == '"'"'--upgrade'"'"':
values[name] = True
continue
if not argv or not argv[0] or argv[0].startswith('"'"'-'"'"'):
raise FetchError('"'"'USAGE'"'"', '"'"'Missing option value'"'"')
value, *argv = argv
if len(value) > 4096 or any(unicodedata.category(char).startswith('"'"'C'"'"') for char in value):
raise FetchError('"'"'USAGE'"'"', '"'"'Invalid option value'"'"')
values[name] = value
for name in ('"'"'gateway'"'"', '"'"'sandbox'"'"', '"'"'workspace'"'"'):
if not re.fullmatch(r'"'"'[A-Za-z0-9][A-Za-z0-9_.-]{0,62}'"'"', values.get(name, '"'"''"'"')):
raise FetchError('"'"'USAGE'"'"', '"'"'Explicit bounded gateway, workspace and sandbox names are required'"'"')
if values['"'"'prefix'"'"'] is not None and (not values['"'"'prefix'"'"'].startswith('"'"'/'"'"') or any(piece in ('"'"'.'"'"', '"'"'..'"'"') for piece in values['"'"'prefix'"'"'].split('"'"'/'"'"'))):
raise FetchError('"'"'USAGE'"'"', '"'"'Prefix must be an absolute sandbox path without dot components'"'"')
if values['"'"'tier'"'"'] not in ('"'"'unrestricted'"'"', '"'"'standard'"'"', '"'"'guided'"'"') or values['"'"'providers'"'"'] not in ('"'"'codex,claude'"'"', '"'"'codex'"'"', '"'"'claude'"'"'):
raise FetchError('"'"'USAGE'"'"', '"'"'Invalid tier or providers'"'"')
if values['"'"'upgrade'"'"'] and seen.intersection({'"'"'--tier'"'"', '"'"'--providers'"'"'}):
raise FetchError('"'"'USAGE'"'"', '"'"'Tier and providers apply only to fresh installs'"'"')
return values
HELPER_URL = '"'"'https://github.com/ToolsEnabled/toolsenabled-engine/releases/download/openshell-beta3-20261001/fleet-fetch.py'"'"'
HELPER_SHA256 = '"'"'5a5677cc9250e816dc00081e7c20ed89679b4ef0e3b49715ed5f4cf3a1c84bdf'"'"'
HELPER_LIMIT = 524288
with host_signals():
import shutil
os.umask(0o077)
stage = None
try:
parse_arguments(list(sys.argv[1:]))
require_pidfds()
curl = shutil.which('"'"'curl'"'"')
if not curl:
raise FetchError('"'"'PREFLIGHT'"'"', '"'"'curl is required'"'"')
stage = Path(tempfile.mkdtemp(prefix='"'"'fleet-bootstrap-'"'"', dir='"'"'/tmp'"'"'))
os.chmod(stage, 0o700)
helper = stage / '"'"'fleet-fetch.py'"'"'
env = {'"'"'PATH'"'"': os.environ.get('"'"'PATH'"'"', '"'"''"'"'), '"'"'LANG'"'"': '"'"'C'"'"', '"'"'LC_ALL'"'"': '"'"'C'"'"'}
download(HELPER_URL, helper, HELPER_LIMIT, curl, env, str(stage))
fd = os.open(helper, os.O_RDONLY | os.O_NOFOLLOW)
info = os.fstat(fd)
if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_uid != os.getuid() or info.st_size > HELPER_LIMIT:
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Downloaded helper identity is invalid'"'"')
content = bytearray()
while True:
piece = os.read(fd, 65536)
if not piece:
break
content.extend(piece)
if len(content) > HELPER_LIMIT:
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Downloaded helper exceeds byte ceiling'"'"')
if hashlib.sha256(content).hexdigest() != HELPER_SHA256:
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Downloaded helper differs from the trusted bootstrap pin'"'"')
cancellation_checkpoint()
current = os.lstat(helper)
if set(os.listdir(stage)) != {'"'"'fleet-fetch.py'"'"'} or (current.st_dev, current.st_ino) != (info.st_dev, info.st_ino):
raise FetchError('"'"'INTEGRITY'"'"', '"'"'Private helper download changed'"'"')
os.lseek(fd, 0, os.SEEK_SET)
os.unlink(helper)
os.rmdir(stage)
stage = None
os.set_inheritable(fd, True)
cancellation_checkpoint()
os.execve(sys.executable, [sys.executable, '"'"'-I'"'"', '"'"'/proc/self/fd/'"'"' + str(fd), *sys.argv[1:]], dict(os.environ))
except FetchError as error:
print(error.phase + '"'"': '"'"' + escaped(error), file=sys.stderr)
if stage is not None:
print('"'"'Owned local evidence retained at '"'"' + escaped(stage), file=sys.stderr)
sys.exit(1)
except BaseException as error:
print('"'"'BOOTSTRAP: '"'"' + escaped(type(error).__name__), file=sys.stderr)
if stage is not None:
print('"'"'Owned local evidence retained at '"'"' + escaped(stage), file=sys.stderr)
sys.exit(1)
' --gateway GATEWAY_NAME --sandbox SANDBOX_NAME --workspace WORKSPACE_NAME --tier guided --providers codex,claudeRelease files
toolsenabled-openshell-linux-x64.tar.gz: 14,642,925 bytes, SHA-256ef1fe25cd4d251859e1c773e11c94b57839a4108b01602979c723c2e041801cd. Itsmanifest.jsonrecords version 1.4.2 and development commit8cc22f7.SHA256SUMS: the archive's checksum line.fleet-fetch.py: the host helper. The host command pins it to SHA-2565a5677cc9250e816dc00081e7c20ed89679b4ef0e3b49715ed5f4cf3a1c84bdfand refuses any other bytes.
The public source for this release contains a byte-identical copy of all 1,242 ToolsEnabled Fleet files in the archive. Rebuilding from that source gives a different archive checksum, because the build records the export commit and its time; use the archive above for an exact install.
Tested on this exact archive
- Full Linux installs under umasks 0022, 0002 and 0077: setup, status, upgrade, state-preserving uninstall, and refusal of a scope whose state was kept.
- A Linux two-hour soak: 37 complete rounds, each including a real beta 2 to beta 3 upgrade.
- The frozen hand test, run twice: 42 checks passed and 0 failed in each run. Its one scripted pending item is covered by a separate passing proof with real Codex and Claude Code tool calls.
- A host rehearsal on a Linux host with a fresh sandbox: the exact host command, with this release's files served from local copies, then the printed command pasted into the sandbox's Bash shell. Install, setup and status completed, and both CLIs showed the Fleet registration. After publication, the same install ran from this release page's downloads into another fresh sandbox, and it completed.
- The Windows two-hour soak on Windows 10 with WSL 2 and Docker Desktop's Linux engine: 32 complete rounds, 288 checks passed, 0 failed. Its long-running process series lasted 7,388 seconds on the system's monotonic clock; the WSL wall clock showed 6,955 seconds for the same span, and the qualification record discloses that difference. The Windows hand-test pair is still in progress.
- A file-by-file check of the archive against the public source.
- An independent security review of the install, upgrade and uninstall changes. Its findings were fixed and re-reviewed before this build.
Known limits
- Uninstall does not delete state yet; use
--keep-state. - A fresh setup refuses a scope whose state was kept. Use upgrade, or a new scope.
- Changed or unexpected runtime contents, and interrupted install or removal work, are kept with their evidence for review. Keep the reported recovery files.
- A Claude Code profile that already has settings needs a separately reviewed registration path; the install expects a fresh Claude profile.
- Provider sign-in stays with each provider's own CLI.
- There is no native Windows runtime installer.
- These results cover the tested OpenShell (0.1.2), Codex (0.158.0) and Claude Code (2.1.284) versions, not every host or model endpoint.
MIT licensed. ToolsEnabled is not affiliated with or endorsed by NVIDIA, OpenAI or Anthropic.