Skip to content
ocx
install

Launcher Forwarding

This page explains how a generated launcher embeds its package root and how a running ocx forwards its configuration to every ocx it spawns.

When ocx package install generates launchers for a package’s declared entrypoints, each launcher delegates to the internal ocx launcher exec subcommand with the package-root path baked at install time.

Unix (POSIX sh):

#!/bin/sh
# Generated by ocx at install time. Do not edit.
exec "${OCX_BINARY_PIN:-ocx}" launcher exec '/home/alice/.ocx/packages/ocx.sh/sha256/ab/c123…' -- "${0##*/}" "$@"

Windows (native .exe shim + .shim sidecar):

# entrypoints/cmake.shim — one UTF-8 line, the absolute package root
C:\Users\alice\.ocx\packages\ocx.sh\sha256\ab\c123…

The companion cmake.exe is a compiled shim. It derives its entry-point name from its own filename and reads cmake.shim for the package root. It then spawns ocx launcher exec "<pkg-root>" -- "cmake" <args…> directly through CreateProcessW — never cmd.exe.

ocx launcher exec forces the self view internally — no flag needs to be baked into the launcher. The stable wire ABI is the launcher exec subcommand name pair and positional shape.

See the Entry Points in-depth page for the full launcher generation pipeline, character rejection rules, and cross-platform launcher behavior.

Whenever ocx spawns a subprocess, it materializes the running ocx’s resolution-affecting policy as OCX_* env vars on the child. The most common case is the child process under ocx package exec, which may itself invoke a generated entrypoint launcher that re-shells back into ocx. This is the chokepoint that keeps configuration coherent across the chain.

VariableSourcePurpose
OCX_BINARY_PINResolved path of the running ocx executablePins the inner ocx to the same binary that installed the package
OCX_HOMEcrate::home::default_ocx_root() — the outer ocx’s own resolved data rootSet-always, even under --clean: a child that strips ambient state still resolves the same object store, not ~/.ocx from the passwd database
OCX_OFFLINE--offline flag on the outer invocationChild ocx stays offline if outer was offline
OCX_FROZEN--frozen flag on the outer invocationChild ocx stays frozen if outer was frozen
OCX_REMOTE--remote flag on the outer invocationChild ocx uses the remote index if outer did
OCX_CONFIG--config flag on the outer invocationChild ocx loads the same explicit config file
OCX_INDEX--index flag on the outer invocationChild ocx reads the same local index directory
OCX_GLOBAL--global flag on the outer invocationChild ocx selects the same tier
OCX_PROJECT--project flag on the outer invocationChild ocx loads the same project file
OCX_NO_CONFIGHermetic outer invocationA hermetic parent’s child does not re-read the full config chain
OCX_MIRRORS, OCX_PATCHES, OCX_PATCH_SNAPSHOTThe resolved [mirrors] and [patches] tiers, and the active snapshotChild ocx sees the same mirror map, companion overlay and frozen companion digests
OCX_MANAGED_CONFIGThe resolved [managed] sourceChild ocx resolves the same managed tier
OCX_RECORDS_DIR, OCX_RECORDS_NAMEThe resolved [records] sinkA launcher re-entry, which takes no flags of its own, writes to the same sink
OCX_TOOLCHAIN_DIRThe resolved toolchain rootEvery composed path moves with it, so the child must agree; with no root, an inherited value is removed
OCX_NO_VERIFY, OCX_NO_CONSENTThe flag or the ambient variableA launcher-spawned child install keeps the CI-wide verification and stamp decisions, across --clean too

Six more settings are read from the environment with no command-line flag behind them: OCX_DEFAULT_REGISTRY, OCX_INSECURE_REGISTRIES, OCX_ALLOW_YANKED, OCX_NO_PROJECT, OCX_NO_CONFIG_REFRESH and OCX_NO_UPDATE_CHECK. Two of them also have a config counterpart: OCX_DEFAULT_REGISTRY overlays [registry] default, and OCX_INSECURE_REGISTRIES unions with [registries.<name>] insecure.

They are forwarded as the invocation itself read them. A --clean child starts from an empty environment, so this keeps it from falling back to defaults the parent overrode. An empty value clears an inherited setting rather than travelling onward. OCX_NO_PROJECT is withheld when the invocation also carries an explicit --project, because a child reads the prune before the path. OCX_ENV is always cleared first and then written from the composed project and group [env] entries plus any --env override; OCX_LAUNCH_IDENTITIES is covered under execution records.

The running ocx’s parsed flags are authoritative. They overwrite any inherited value from the parent shell, so a stale exported OCX_OFFLINE=1 cannot override an outer ocx invoked without --offline. The same rule holds under ocx package exec --clean: the child env starts empty, then OCX_* keys are written explicitly from the outer ocx’s parsed state. No ambient shell export can bypass this.

Some variables are deliberately outside the forwarded set. OCX_LAZY_MODE, OCX_LAZY_REPORT and the three toolchain settings OCX_TOOLCHAIN_ACTIVATE, OCX_TOOLCHAIN_PINNED and OCX_TOOLCHAIN_UPDATE change when or how content reaches PATH, never which digest resolves, so a child reads its own environment. The same holds for OCX_SELF_UPDATE, OCX_UPDATE_CHECK_INTERVAL, OCX_CEILING_PATH and OCX_SIGSTORE_TRUSTED_ROOT. OCX_EXTRA_CA_CERTS is not part of the set either, so a CA supplied only through it does not reach an ocx exec --clean child. The extra_ca_certs config form survives, because the child re-reads config.toml itself. Four credentials are stripped from every child, plugins (ocx-<name>) included: OCX_IDENTITY_TOKEN, OCX_KEY_PASSWORD, OCX_SIGNING_KEY and OCX_ANNOUNCE_GIT_TOKEN. The runner variables that --ci and --ci-annotations read are not forwarded either, because the CI sink files are runner-managed paths.

OCX_AUTH_<REGISTRY>_* is not part of the forwarded set. Launcher children re-enter via ocx launcher exec using a local package root rather than a registry, so they never need credentials. Future deliberate ocx-spawns-ocx call sites that do need auth will declare that forwarding rule at their own boundary.