# ocx_sdk

*module*

Python SDK for [OCX](https://github.com/ocx-sh/ocx).

`ocx-sdk` drives the ocx binary rather than reimplementing it: ocx owns
resolution, verification, and the identifier grammar, and this package gives
you typed, CWD-independent handles over the commands it exposes.

```python
from ocx_sdk import Ocx, bootstrap

ocx = Ocx(exe=bootstrap.ensure())
project = ocx.project("/srv/build")
project.pull()
project.exec(["task", "verify"])
```

**This module is the API.** Everything listed in `__all__` is the stable
surface; every other module is underscored and package-private, and the one
public submodule is `ocx_sdk.bootstrap`. Reaching into an underscored path
means the next release may move it without notice — pre-1.0, breaking
changes ship without shims.

Start at `Ocx` for the runtime API and `bootstrap.ensure` for provisioning.

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/__init__.py#L1-L1)

## ocx_sdk.OcxConfig

*class* · *dataclass*

```python
class OcxConfig
```

Re-exported from: `ocx_sdk._config`

Policy for every ocx process a handle spawns.

Containers are snapshotted at construction — the mappings as read-only
proxies, `insecure_registries` as a tuple — so neither mutating the argument
afterwards nor reaching into the field can rewrite a config a handle already
holds. Derive a variant with `Ocx.with_config(...)`
rather than reaching for a mutation that the frozen dataclass refuses.

**Attributes**

- `home` (`Path | None`) — `$OCX_HOME` for the spawned process; `None` keeps ocx's default.
- `offline` (`bool`) — Refuse every network access.
- `frozen` (`bool`) — Refuse any lockfile change.
- `config` (`Path | None`) — Explicit `config.toml`; `None` keeps ocx's discovery chain.
- `no_config` (`bool`) — Hermetic mode — drops the discovered chain and the managed tier. Wins over `managed_config` when both are set, which is ocx's own precedence, not an SDK invention.
- `managed_config` (`str | None`) — OCI reference of the managed config tier. `MANAGED_CONFIG_DISABLED` force-disables an ambient one; `None` leaves the ambient setting alone.
- `auth` (`Mapping[str, Auth]`) — Credentials per registry, keyed lowercase. An entry wins over an ambient `OCX_AUTH_<SLUG>_*` for the same registry, whatever case that variable spells the slug in.
- `insecure_registries` (`Collection[str] | None`) — Registries reachable over plaintext HTTP. `None` inherits the ambient set; any explicit value replaces it entirely, so `()` blocks an ambient re-enable (fail-closed).
- `sigstore_trusted_root` (`str | Path | None`) — The Sigstore trusted root to verify signatures against, for air-gapped verification against a private root of trust. `None` is **not** "leave the ambient value" here, unlike the other path-shaped fields: it means ocx's own default root, and `_env` pops an ambient `OCX_SIGSTORE_TRUSTED_ROOT` to enforce that. The variable redefines what "trusted" means and outranks the config file, so an ambient one could otherwise repoint Fulcio, CT, and Rekor at an attacker's material while every install still reported verified. The path travels verbatim, as `home`, `config`, `index` and `docker_config` do: `~` is not expanded, and a relative path resolves against the *child's* working directory, not the caller's. That is acceptable rather than sloppy, because a path ocx cannot read is not a path it falls back from — an explicit override is read with `?`, so an unreadable one aborts the command at exit 74 (`IoError`) instead of quietly verifying against the default root. A wrong value fails loudly; it never becomes a silent trust substitution, which is the hazard this field exists to close. Pass an absolute path when the call must not depend on where the child starts.
- `docker_config` (`Path | None`) — Directory for `DOCKER_CONFIG`. Keep it `0700` — it holds registry credentials.
- `index` (`Path | None`) — Explicit index path; `None` keeps ocx's default.
- `jobs` (`int | None`) — Parallel job cap; `None` keeps ocx's default.
- `log_level` (`LogLevel | None`) — ocx trace verbosity. At `'trace'` ocx may print secrets of its own; the SDK can only redact the values it was given.
- `mirrors` (`Mapping[str, str] | None`) — Registry host to mirror host, serialized into `OCX_MIRRORS`.
- `no_update_check` (`bool`) — Suppress ocx's update check. `True` by default — an SDK call is a program step, not an interactive session.
- `no_config_refresh` (`bool | None`) — Suppress the managed-config refresh; `None` leaves the decision to whoever owns that tier.
- `consent` (`bool`) — Let the project-tier mutators (`add`, `lock`, `pull`, `exec`, `update`, `init`) record the per-project consent stamp that lets a shell prompt in that directory activate the project. `False` by default — an SDK call is a program step, and a stamp it left behind would make the project live at the developer's next prompt without anyone having run `ocx shell allow`. Travels as `OCX_NO_CONSENT`, written either way so an ambient value cannot decide it. This is the per-project stamp, not the shell-activation consent `ocx shell allow` records — that command is not wrapped.
- `records_dir` (`Path | None`) — Where `exec` and `package exec` write execution records — `OCX_RECORDS_DIR`. `None` leaves it to `[records] dir` in the project (or an ambient value); without any, no record is written. The per-call `records_dir=` on the exec verbs outranks this.
- `records_name` (`str | None`) — The record filename template — `OCX_RECORDS_NAME`. `None` leaves it to `[records] name` or ocx's default.
- `toolchain_dir` (`Path | None`) — The root ocx renders project toolchains under — `OCX_TOOLCHAIN_DIR` — instead of `<project>/.ocx/toolchain/`. `None` keeps ocx's default. `OCX_TOOLCHAIN_PINNED` and `OCX_TOOLCHAIN_ACTIVATE` are deliberately not modelled: the first is the weakest tier of a choice `pinned=` makes at the call site, the second governs shell activation the SDK never performs.
- `forge_token` (`str | None`) — The forge API credential `package.announce` and `package.claim` open their request with — `OCX_ANNOUNCE_TOKEN`. `None` leaves an ambient one alone; explicit wins over ambient, as `auth` does. Redacted from every log and error surface.
- `forge_git_token` (`str | None`) — The push secret for `transport="git"` — `OCX_ANNOUNCE_GIT_TOKEN`. Falls back to `forge_token` upstream when unset. Redacted likewise.
- `forge_git_username` (`str | None`) — The username the `git` transport pushes as — `OCX_ANNOUNCE_GIT_USERNAME`. Not a secret.
- `retry` (`RetryPolicy | None`) — Retry policy for failures ocx marked transient; `None` disables retrying.
- `timeout` (`float | None`) — Per-attempt budget in seconds; `None` waits indefinitely.

> **Example**
>
> >>> OcxConfig().insecure_registries is None
> True
> >>> OcxConfig(insecure_registries=()).insecure_registries
> ()

[View source](https://github.com/ocx-sh/ocx-sdk-python/blob/main/src/ocx_sdk/_config.py#L98-L262)
