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

*function*

```python
def partial_report(error: OcxProcessError) -> str | None
```

Re-exported from: `ocx_sdk._results`

Return the report a non-zero-exit stdout carried, else `None` (D10).

Several ocx failures still print a full JSON report before exiting
non-zero: a `--signature-format both` sign or attest losing one leg, a
partially-failed tag sweep, a `copy` refused on a sidecar conflict, and a
push whose inline signing failed. `OcxProcessError` never parses its own
stdout, so a caller recovers the report explicitly:

```python
try:
    result = ocx.package.sign(ref, key="file://k.pem", rekor_upload=False)
except OcxProcessError as exc:
    raw = partial_report(exc)
    if raw is not None:
        result = SignatureReport.from_json(raw)
    else:
        raise
```

**Parameters**

- `error` (`OcxProcessError`) — The caught process failure.

**Returns**

- (`str | None`) — `None` for a hard failure (an error envelope carrying an `error` key
- (`str | None`) — and no `data`, or stdout that is empty or unparseable as JSON) —
- (`str | None`) — there is nothing to recover. Otherwise `error.stdout` verbatim, ready
- (`str | None`) — for the matching `from_json`.

> **Note**
>
> **The enveloped shapes come back enveloped**, not pre-unwrapped. D10's
> text called for handing back the envelope's `data` sub-document, which
> cannot work: `SignatureReport`/`AttestationReport`/`SweepReport`'s own
> `from_json` calls `_envelope` (D11), so a pre-unwrapped payload makes
> it unwrap twice and raise on a missing `data` key — the example above
> would not run. Returning stdout whole is what makes one `from_json`
> serve the success path and the recovery path, which is the property
> D10 and S-009 both ask for; the discriminator is unchanged.

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

## ocx_sdk.tolerated_report

*function*

```python
def tolerated_report(result: CommandResult) -> str
```

Re-exported from: `ocx_sdk._results`

Return the report a report-then-fail command wrote, or raise its failure.

`package test`, `cascade check` and `cascade repair` exit non-zero *with*
their report on stdout when the outcome is a finding rather than a fault —
the client tolerates that code (`ok_codes`) so the report comes back as a
result. The same code with an error envelope, or with no JSON at all, is a
fault the tolerance must not swallow: this raises it as the exception the
exit code maps to, stdout preserved for `error_envelope(err)`.

**Parameters**

- `result` (`CommandResult`) — The finished call, exit code and both streams.

**Returns**

- (`str`) — `result.stdout`, for the matching `from_json`.

**Raises**

- `OcxProcessError` — The exit was non-zero and stdout carried no report —
the subclass `_errors` maps the code to, or the base class for a
code ocx never assigns.

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