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

*method*

```python
def attest(ref: PackageLike, *, predicate: str | Path, predicate_type: str, 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) -> AttestationReport | SweepReport
```

Attach an in-toto attestation to a package reference (C-013).

Same three call shapes as `sign` (D2): `tags`/`tags_file` sweeps and
returns a `SweepReport`; neither attests `ref` alone and returns an
`AttestationReport`. `mutating=True` (D5), retries off by default
(D6).

Carries the identical `--key` conflict set `sign` has — this is
enumerated per command rather than delegated to a shared note,
because it is `attest`'s own guard, declared independently in
`package_attest.rs` alongside `package_sign.rs`'s: **four** flags
refuse `key` outright (`fulcio_url`, `identity_token_file`,
`identity_token_stdin`, `no_tty`); `rekor_upload=False`
(`--no-rekor-upload`) *requires* `key`, the opposite direction; and
independently of `key`, `identity_token_file` conflicts with
`identity_token_stdin`.

**Parameters**

- `ref` (`PackageLike`) — The package reference to attest.
- `predicate` (`str | Path`) — The predicate document to attest.
- `predicate_type` (`str`) — The predicate type URI or its short alias — ocx's `--type`. The returned report's `predicate_type` is the *resolved* URI, which may differ from what was passed here.
- `tags` (`Iterable[str] | None`) (default: `None`) — Sweep these tags instead of attesting `ref` directly. 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. Unions with `tags` — see above.
- `platform` (`str | None`) (default: `None`) — Attest 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. `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 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. 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**

- (`AttestationReport | SweepReport`) — An `AttestationReport` for a single attestation, or a
- (`AttestationReport | SweepReport`) — `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. Recover a partial report with
`partial_report(err)` and `AttestationReport.from_json`
(D10).

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