Managed CLI launch pilot
Agent runs normally retain the legacy launch path. Operators can select exact crew IDs with the server environment variableCREWSHIP_MANAGED_LAUNCH_CREWS
(comma-separated, read at server construction). Removing an ID requires a
server restart and is an explicit return to legacy behavior. This selection
applies in common RunAgent admission, including assignment and chat dispatch;
requests cannot disable it. Interactive web terminals remain a separate path.
The first implementation supports image-resident static Linux ELF executables
including static PIE without an interpreter or external dependencies,
for the Codex adapter. Artifact reads are bounded to 512 MiB. Dynamically linked binaries, scripts,
env-shebang launchers, mise shims and executables in home/workspace/tmp are
unsupported and refuse the run. This narrow pilot does not qualify OAuth,
model runs, dynamic Claude packaging or complete runtime isolation.
Interpreter and library qualification is a subsequent capability (A2). Pilot
selection is restricted to Codex-only crews. Claude dispatch in a selected crew
returns an explicit dynamic-ELF/A2 capability error before creation.
The pilot requires a host-native server running under a UID other than 1001
or 1002. The official server Docker image runs as UID 1001 and is unsupported
for this pilot; recreating crew runtimes cannot correct that host UID collision.
Before selecting a crew, use the existing CLI environment flow:
- Set an exact Codex pin in
crew config <crew> --mise <file>and resolve/apply its native lock withcrew lock-resolve <file>/crew lock-apply <file> <resolution>. - Build the environment with
crew startorcrew provisionand inspect its immutable build revision withcrew revisions <crew>. - The executable must reside at a canonical image path below
/optor/usr, owned by root or UID 1001, executable, and not writable by group/others. The read-only root and mount checks prevent UID 1001 from replacing image files. Sealing a shim does not turn it into a supported executable. Old images without captured executable evidence require a rebuild. - Check the revision’s
toolchain.tools[].launch_artifactand image-bound startup qualification. Then select that crew ID on the server and restart. - Recreate selected crew runtimes so they bind the immutable launcher generation. Existing fixed-name binds are refused with a recreation requirement.
ai_cli_check: required
retains its existing build-publication meaning and does not select this pilot.
The host captures executable bytes/hash through Docker’s file-copy API from a
stopped container of the immutable image; no image entrypoint runs during this
capture. The launch descriptor binds the image, immutable revision, complete
native lock bundle digest, exact selector version, executable path and hash.
Changes to the current environment definition invalidate the old revision.
An unavailable resolver, missing reservation or unsupported runtime fails
closed before any legacy launch fallback.
Docker admission checks the exact reserved container, immutable image,
read-only root, privilege/capability restrictions, the host’s read-only launcher
bind and executable/launcher mount overlap, including HostConfig tmpfs
destinations. When the pilot is enabled, Docker
stages the launcher under a full SHA-256 filename using atomic create-if-absent,
verifies existing bytes and host ownership/permissions, and never replaces that
path or eagerly deletes old generations. Enabling the pilot stages the same
launcher bytes in a digest-named generation for all newly created crew runtimes,
including non-pilot crews; existing container binds remain unchanged.
Old-generation or fixed-name binds
fail closed. A container archive comparison supplies secondary consistency
verification; Docker archives can remount the current host source and do not
prove the running bind inode. Read-only overlays are rejected
too. Parent-directory symlinks that could alias an image path into writable
home are rejected. Authority and runtime are checked again after preflight,
before the durable exec creation gate. The reservation survives execution.
The first process is the static host-bound crewship-sidecar --managed-launch
helper. It checks native format, canonical path, ownership/permissions and
SHA-256 before execveat(AT_EMPTY_PATH) of that same open descriptor, without
shell, stdbuf, agent PATH, tmux or writable
argument/environment files. The trusted launcher creates a new session and an exclusive 0600
/tmp/crewship-direct-<run>.pid file containing its PID and kernel start time
before artifact validation. Exec preserves that identity for the existing
stop, liveness and restart recovery probes; existing files or symlinks refuse
the attempt rather than overwriting another identity. This writable PID file
is lifecycle evidence, never authority to launch an executable. The existing
host-side RunState.ManagedLaunch marker must be persisted before exec.
Managed prompt assembly and command preparation failures finalize the run as
error before workload creation; they do not leave unresolved running occupancy.
The marker is retained by restart recovery. If that managed PID file disappears, probes return
UNKNOWN and retain the hold instead of falling through to tmux absence; an
authoritatively stopped or removed container can still prove absence. Lost
identity in a running container requires operator reconciliation/recreation.
Same-RunID re-execution refuses an existing identity file, matching legacy
direct-exec parity: retries must mint a new attempt ID.
Probes read durable run state even for legacy runs; a failed state lookup
refuses the probe. Process-group calibration also applies to legacy direct runs.
Process-group signal probing calibrates against the probe’s own group using
portable explicit-signal syntax. Unsupported utilities, permission errors and
failed calibration return UNKNOWN; only a definite no-such-process error
proves group absence. PID/starttime checks prevent accidental PID reuse. They do not establish an
adversarial isolation boundary between processes sharing UID 1001: that UID can
rewrite its PID file to a valid foreign PID/starttime in the same container.
The durable marker prevents false tmux absence and cross-run state confusion;
it does not attest the PID file against such deliberate forgery. This pilot
retains that legacy lifecycle limitation and does not qualify agent isolation.
Its child environment contains only names supplied
by host admission, with a fixed system PATH (/usr/local/bin:/usr/bin:/bin). Host-admitted
lowercase http_proxy, https_proxy and no_proxy are retained. Crew mise
and feature PATH additions are excluded: shell tools must use canonical
image paths when absent from the fixed PATH. This is a pilot limitation. Image-only variables, LD_*,
DYLD_*, NODE_OPTIONS, loader/interpreter options and tmux variables are
discarded. ENV and BASH_ENV are discarded too. In particular, applications
that rely on LD_PRELOAD (including jemalloc preload configuration), shell
startup hooks or image-only variables must not assume those settings survive.
Use an image-native supported CLI and explicit host-admitted safe variables;
keep a crew on the legacy path if it requires loader or shell-hook behavior
until a separately qualified capability supports it. No allocator equivalence
or performance benefit from removing preload is claimed.
Existing host-admitted HOME, proxy and credential values remain;
moving credentials outside agents belongs to the later gateway work.
agent_runs state and exec.command payloads contain managed_launch admission
evidence. version is read from a single canonical native mise v3 locked entry and
must match the exact configured pin
and build revision; session self-reported versions remain separate observations.
This evidence describes admitted artifacts, not successful authentication or a
successful process start. Refusals before creation emit no CLI output.
The required managed-environment CI suite runs TestManagedLaunchRealDocker:
only this new fixture runs under sudo on the host; existing Docker fixtures retain
their non-root host execution. The production Codex command builder uses the
real static launcher against
synthetic native executables, with stale CLI/node/tmux in persistent home and
injected image environment. Wrong hashes exit 126. Unit regressions cover common
run admission, missing evidence, current-lock drift, tenant scope, mount aliases,
unsafe formats, immutable launcher generations and static PIE dependencies. No subscription account or model call is used.
For explicit upstream packaging qualification, provide a local mise image
using the test-only selector documented in
codex_packaging_integration_test.go.
Export that test-only image selector before running the command below.
A missing selector fails this explicit gate and supplies no packaging qualification:
--locked --force, records the native mise path and inspects the committed
image without starting it for inspection. A separate read-only, network-disabled
runtime executes the actual native binary through the static launcher with
--version. It makes no authentication or model requests.
Legacy inventory path/version observations remain PATH-first and keep their
existing publication-gate meaning. Managed evidence separately resolves the
canonical native mise installation, probes that exact binary’s version and
captures its bytes from the stopped image. A shim or mise executable cannot
become the managed candidate; its independently observed version must match
the native lock and exact selector. Older revisions without independent
managed_path and managed_version evidence require a rebuild.
Artifact validation currently buffers up to 512 MiB per reader; operators must
budget memory for launcher validation in addition to the native CLI. The bound
limits individual allocations, not aggregate concurrency or an OOM guarantee.
Attestation uses a 20-second provider bound within 30-second admission.
Immutable launcher generations are retained. Conservative collection is tracked
in issue #2906; it must
preserve running and stopped container references and coordinate with launcher
publication and runtime creation before removing any generation.
Upstream Codex packaging qualification is an explicit opt-in test, separate from
the required synthetic managed-environment CI gate.
The once-run Linux amd64 Codex 0.160.0 native packaging qualification used
a 289,101,384-byte executable and a 1 GiB runtime memory limit. That result
does not qualify smaller memory limits, other platforms, authentication or
model calls. No crew memory defaults are changed by this pilot.
Independent component conformance and overhead
internal/managedlaunch is the runtime core. It imports only Go’s standard
library and golang.org/x/sys/unix; Crewship DB, HTTP API, UI and Docker provider
adapters depend on this core, not the reverse. cmd/crewship-launcher is a
minimal standalone entrypoint for the same production launch function. It
accepts --managed-launch <base64url-descriptor> <argv...>, writes refusals to
stderr and exits 126. It supplies no authority resolver: its caller must perform
the immutable image/mount/host-launcher admission checks described above.
The descriptor has a 64 KiB encoded limit, one JSON object, canonical field
names, no duplicate fields (including escaped duplicates), no unknown fields,
and no trailing values. The core exposes side-effect-free descriptor and
artifact/archive validation functions for fuzzing. These are the functions
used by production launch and Docker adapters; fuzzing never calls exec.
Run standalone Linux component conformance with a local Docker daemon:
The scenario labels refer to the evolving whole-runtime conformance contract;
partial component evidence must never be published as a full-scenario PASS.
For each native size (the compiled small fixture and 32 MiB padded fixture),
the standalone test alternates 30 direct-image baselines and 30 managed starts.
It logs raw samples, mean and standard deviation for Docker start-to-exit time,
process CPU and process peak RSS. Linux
getrusage(RUSAGE_SELF) includes the
launcher and child across exec; it excludes Docker daemon CPU and is not
cgroup memory. Container creation/image build are excluded from start timing.
Padded bytes exercise actual validation/hash/read costs, not realistic Codex
behavior. These sizes do not qualify the approximately 289 MiB upstream binary,
OAuth/model requests or whole-runtime C10 budgets. Host load can affect timing;
compare paired profiles, retain all samples and record platform/Go/Docker
versions with execution evidence.
Parser/hash allocation benchmarks are also reproducible independently:
FuzzManaged* target in
internal/managedlaunch and internal/devcontainer, one target per invocation:
Code origin and licensing
The managed-launch runtime core and standalone entrypoint are original Crewship implementations licensed under the repository’s Apache-2.0 license. No OpenShell/OpenSandbox implementation or third-party parser source is copied into this component. The implementation uses the Go standard library (BSD-3-Clause) and the existinggolang.org/x/sys/unix dependency
(BSD-3-Clause); dependency attribution is retained in
THIRD-PARTY-NOTICES.md. This delivery adds no
module dependency. The separately fetched upstream Codex executable has its
own provenance/license; its packaging observation does not transfer its code
or any authentication entitlement into this component.