# 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.PackageCommands

*class* · *dataclass*

```python
class PackageCommands
```

Re-exported from: `ocx_sdk._client`

The `ocx package` command group — machine tier.

Package operations act on the `$OCX_HOME` store and its candidate and
current symlinks. They take no project path and are CWD-independent by
construction; a path appears only where the CLI itself takes one.

Every method is multi-identifier native, mirroring the CLI's `PKG...`
with one shared resolution.

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

### ocx_sdk.PackageCommands.sign

*method*

```python
def sign(ref: PackageLike, *, tags: Iterable[str] | None = None, tags_file: str | Path | None = None, platform: str | None = None, signature_format: SignatureFormat | None = None, key: str | None = None, rekor_upload: bool | None = None, fulcio_url: str | None = None, rekor_url: str | None = None, identity_token_file: str | Path | None = None, identity_token_stdin: bool = False, no_tty: bool = False, no_cache: bool = False, timeout: MaybeTimeout = UNSET, retry: MaybeRetry = UNSET) -> SignatureReport | SweepReport
```

Sign a package reference with cosign/Sigstore (C-011).

Three call shapes, one method (D2): `tags` or `tags_file` sweeps
every matching tag and returns a `SweepReport`; neither signs `ref`
alone and returns a `SignatureReport`.

`mutating=True` (D5) — retries are off by default; auto-retrying a
Rekor-unavailable failure would amplify the public instance's rate
limiting (D6).

A `--signature-format both` run where one leg lands and one fails
exits non-zero carrying a full report: catch `OcxProcessError`,
recover it with `partial_report(err)`, and parse with
`SignatureReport.from_json` (D10) — `SignatureLegReport.error` names
which leg died.

`key` and keyless signing are mutually exclusive with each other's
machinery, not just in spirit. **Four** flags refuse `key` outright —
`fulcio_url`, `identity_token_file`, `identity_token_stdin`, and
`no_tty`. Separately, `rekor_upload=False` (`--no-rekor-upload`)
*requires* `key` — the opposite direction: it is meaningless for
keyless signing, which cannot skip Rekor. And independently of
`key` entirely, `identity_token_file` and `identity_token_stdin`
conflict with each other — two ways to supply one token.

**Parameters**

- `ref` (`PackageLike`) — The package reference to sign.
- `tags` (`Iterable[str] | None`) (default: `None`) — Sweep these tags instead of signing `ref` directly. Mutually exclusive with `platform`. Unions with `tags_file` when both are given — sweeping is triggered by either, not a choice between them. An empty sequence is refused: `tags=None` is how you act on `ref` itself.
- `tags_file` (`str | Path | None`) (default: `None`) — Sweep the tags listed in this file. Mutually exclusive with `platform`. Unions with `tags` — see above.
- `platform` (`str | None`) (default: `None`) — Sign one platform's manifest. Refused alongside `tags` or `tags_file`.
- `signature_format` (`SignatureFormat | None`) (default: `None`) — Which signature format(s) to produce.
- `key` (`str | None`) (default: `None`) — A key reference — `file://` or `env://`, the two backends ocx 0.6 implements. A bare path is read as `file://`. `awskms://`, `gcpkms://`, `azurekms://`, `hashivault://` and `k8s://` parse and are then refused with exit 85 (`UnsupportedKeyBackendError`), so no configuration makes them work. `None` signs keyless, against Fulcio. Conflicts with `fulcio_url`, `identity_token_file`, `identity_token_stdin`, and `no_tty`.
- `rekor_upload` (`bool | None`) (default: `None`) — Upload the signature to the transparency log. `False` (`--no-rekor-upload`) is valid only alongside `key`.
- `fulcio_url` (`str | None`) (default: `None`) — A non-default Fulcio instance. Conflicts with `key`.
- `rekor_url` (`str | None`) (default: `None`) — A non-default Rekor instance.
- `identity_token_file` (`str | Path | None`) (default: `None`) — Read the OIDC identity token from this file, for keyless signing without an interactive browser flow. Conflicts with `key` and with `identity_token_stdin`.
- `identity_token_stdin` (`bool`) (default: `False`) — Read the OIDC identity token from stdin. Conflicts with `key` and with `identity_token_file`.
- `no_tty` (`bool`) (default: `False`) — Suppress the interactive TTY prompt. Conflicts with `key`.
- `no_cache` (`bool`) (default: `False`) — Skip ocx's signing cache.
- `timeout` (`MaybeTimeout`) (default: `UNSET`) — Seconds per attempt. Omitted takes the config's.
- `retry` (`MaybeRetry`) (default: `UNSET`) — Retry policy. Defaults to no retries.

**Returns**

- (`SignatureReport | SweepReport`) — A `SignatureReport` for a single signing, or a `SweepReport`
- (`SignatureReport | SweepReport`) — when `tags`/`tags_file` swept multiple.

**Raises**

- `ValueError` — `tags` was empty; `platform` was given alongside
`tags`/`tags_file`;
`key` was given alongside `fulcio_url`,
`identity_token_file`, `identity_token_stdin`, or `no_tty`;
`identity_token_file` and `identity_token_stdin` were both
given; or `rekor_upload=False` was given without `key`.
- `OcxProcessError` — A non-zero exit — see the partial-failure note
above for how to recover a report from one.

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