- integrations
- Python
- Hermetic CI: The forge identity ladder
Hermetic CI: The forge identity ladder
Part 2 of 2 of Hermetic CI.
The forge identity ladder
Section titled “The forge identity ladder”announce and
claim write to a
forge, not a registry, and ocx resolves that identity from a ladder of
ambient variables: OCX_ANNOUNCE_TOKEN first; on GitLab, CI_JOB_TOKEN
(when GITLAB_CI is set and the transport is git); OCX_ANNOUNCE_GIT_TOKEN
and OCX_ANNOUNCE_GIT_USERNAME for the push leg; CI_PROJECT_PATH,
GITLAB_USER_LOGIN/GITLAB_USER_ID or GITHUB_ACTOR/GITHUB_ACTOR_ID for
the owner identity a claim records.
HostEnv.minimal() drops every rung. A hermetic handle therefore announces
with credential_kind: "none" and fails with AuthError (80) before any
network — the right default for a step that was not meant to publish. The
step that is meant to publish says so on the handle, either by letting the
named rungs through or by configuring the credential explicitly:
from ocx_sdk import HostEnv, OcxConfig
runner = HostEnv({"PATH": "/usr/bin", "GITLAB_CI": "true", "CI_JOB_TOKEN": "glcbt-...", "OCX_AUTH_X_TOKEN": "t"})let_ci_through = runner.only("PATH", "HOME", "TMPDIR", "GITLAB_CI", "CI_JOB_TOKEN", "CI_PROJECT_PATH")assert set(let_ci_through.source) == {"PATH", "GITLAB_CI", "CI_JOB_TOKEN"} # `.ambient().only(...)` in practice
explicit = OcxConfig(forge_token="glpat-secret")assert "glpat" not in repr(explicit)OcxConfig.forge_token, forge_git_token and forge_git_username reach
the child as OCX_ANNOUNCE_TOKEN, OCX_ANNOUNCE_GIT_TOKEN and
OCX_ANNOUNCE_GIT_USERNAME, explicit winning over ambient like every other
lever. The three token values — ambient or configured — are redacted from
stderr, on_log, logged argv and exception text exactly as OCX_AUTH_* is.
Exit 86 (ForgeCapabilityUnavailableError)
is the one forge failure a credential change cannot fix: the token is valid
but the target project does not allow job-token push, and only an
administrator’s allowlist entry changes that.
The one thing hardening does not cover: OCX_AUTH_* under exec
Section titled “The one thing hardening does not cover: OCX_AUTH_* under exec”ocx does not scrub non-forwarded variables from a spawned child’s
environment — “non-forwarded is not the same as scrubbed.” That means a
tool started through
Project.exec or
package.exec
inherits whatever OCX_AUTH_* the handle’s environment carries, whether
that came from ambient env or explicit OcxConfig.auth. This is ocx’s
behavior, not the SDK’s, and hardening the levers above does not change it
by itself — HostEnv.clean()/.minimal() does, because it drops the
ambient OCX_AUTH_* before the SDK ever sees it, but an explicit
OcxConfig.auth you configured is deliberately still there for ocx itself
to use.
The credential-free pattern for a step that should not see the token at all: pull first, authenticated, then run through a config with the credentials cleared.
# illustrative: needs a real Project handle.project.pull() # authenticated — needs the tokenproject.with_config(auth={}).exec(["task", "build"]) # the build step does notSee Errors & credentials for the full credential-handling picture, including why secrets never appear in a repr, a log line, or an exception message even when they do reach the child process.