Skip to content
ocx
install

Hermetic CI

Three defaults, each individually reasonable for a dev library, add up to a specific fact worth stating plainly: the ambient environment is a trusted input unless you turn it off.

  • OCX_INSTALL_* is honored from the ambient environment by bootstrap.ensure() — it chooses what gets downloaded and from where.
  • OCX_AUTH_* passes through to a spawned ocx untouched — it chooses which credentials attach to a registry call.
  • Binary discovery walks PATH — it chooses which ocx actually runs.

None of this is a bug; a CI runner’s ambient environment usually is trustworthy. But a build that wants to say so explicitly, or one running in an environment it does not fully trust, has levers for every one of the three — all opt-in, all composable.

Trust boundary Default Hardening lever
What bootstrap.ensure() downloads Ambient OCX_INSTALL_* honored (which ones) Pass version=, dist=, mirror_url=, ca_bundle= explicitly; construct DistSource with an explicit sha256=
Which credentials a spawn carries Ambient OCX_AUTH_* passes through Ocx(host_env=HostEnv.clean()) or .without(...); explicit OcxConfig.auth always wins over ambient for the same registry
Which binary runs PATH search Ocx(exe=...) — the hardened form: it trusts a location without inspecting how it was reached
Registry transport Whatever insecure_registries the ambient env allows OcxConfig(insecure_registries=()) — fail-closed: an explicit value, including the empty tuple, replaces the ambient set entirely rather than merging with it
Cache integrity Re-hashed on every cache hit Already default-on; trust_cache=True is the (explicit) way to opt back out

Explicit configuration always wins over the ambient environment — that precedence is uniform across every lever above, not something to remember per field.

from ocx_sdk import OcxConfig
hermetic = OcxConfig(insecure_registries=())
assert hermetic.insecure_registries == ()

A CI image that exports OCX_INSECURE_REGISTRIES cannot re-enable plaintext through this config — the empty tuple is the fail-closed answer, not the same as leaving insecure_registries unset (None, which inherits whatever the ambient environment allows).

# illustrative: needs a real binary; substitute your discovery/pin strategy.
from ocx_sdk import HostEnv, Ocx, OcxConfig, bootstrap
ocx = Ocx(
exe=bootstrap.ensure(version="0.6.2"), # pinned, not "latest"
host_env=HostEnv.minimal(), # PATH/HOME/TMPDIR only
config=OcxConfig(
insecure_registries=(), # fail-closed
no_config=True, # ignore any discovered config.toml
),
)

HostEnv.minimal() rather than HostEnv.clean() here: a spawned toolchain still needs PATH to find its own dependencies, and clean() drops it — see Bootstrap for the full tier list.

Section titled “What a spawn leaves behind: the consent stamp”

One default runs the other way — it is on regardless of the levers above. ocx stamps state/projects/<key>/consent.json on every add, lock, pull, exec, update and init, and the stamp is what lets a shell prompt in that directory activate the project at the developer’s next cd. A program step should not leave that behind, so the SDK writes OCX_NO_CONSENT=1 on every spawn (ocx exec forwards it to nested ocx) and an ambient OCX_NO_CONSENT=0 cannot switch it back. A pipeline that does want the project live afterwards says so on the handle:

from ocx_sdk import OcxConfig
assert OcxConfig().consent is False
consenting = OcxConfig(consent=True) # `handle.with_config(consent=True)` in practice
assert consenting.consent is True

That is the per-project stamp only; the shell-activation consent ocx shell allow records is not wrapped.

Continues on Hermetic CI: The forge identity ladder.