Metadata-Version: 2.4
Name: iceberg-subzero
Version: 1.0.4
Summary: Python client for the Subzero tokenization vault
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28.0
Provides-Extra: dev
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: respx>=0.22.0; extra == 'dev'
Description-Content-Type: text/markdown

# Subzero Python SDK

Thin Python client for the [Subzero](../subzero-api/) tokenization vault.

Configure entity types, API keys, and reveal policies in the **dashboard** first. This SDK is for server-side vault operations only.

See **[docs/vault-sdk.md](docs/vault-sdk.md)** for the full integration guide.

## Install

```bash
pip install iceberg-subzero
```

## Quick start

```python
from subzero import SubzeroClient

client = SubzeroClient(
    tokenize_key="sz_live_...",   # tokenize + search + batch
    reveal_key="sz_live_...",     # reveal
)
client.ready()

token = client.tokenize("SSN", "123-45-6789")
found = client.search("SSN", "123-45-6789")   # blind index lookup
value = client.reveal(token)
resolved = client.resolve("123-45-6789")  # legacy plaintext passthrough
```

Or from environment variables:

```python
client = SubzeroClient.from_env()  # SUBZERO_API_KEY or split keys; SUBZERO_BASE_URL
```

A single `api_key` works when one key has both scopes (e.g. `admin`):

```python
client = SubzeroClient(api_key="sz_live_...")
```

## API key scopes

| Scope | SDK methods |
|-------|-------------|
| `tokenize` | `tokenize`, `search`, `tokenize_batch`, `proxy.scan`, `proxy.discover`, `proxy.restructure` |
| `proxy` | `proxy.scan`, `proxy.discover`, `proxy.restructure` |
| `reveal` | `reveal`, `resolve` (requires a matching reveal policy in the dashboard) |
| `reveal_grant` | `create_reveal_grant` (BFF for Elements click-to-reveal) |
| `admin` | All of the above (bypasses reveal policy) |

Use separate `tokenize_key`, `reveal_key`, and `proxy_key` in production for least privilege.

## Reveal caller context (audit)

By default, `reveal`, `resolve`, and `create_reveal_grant` attach optional **`caller_context`** for audit logging. The SDK auto-captures the first stack frame outside `subzero/` when `capture_caller_context=True` (default).

```python
from subzero import RevealCallerContext

client = SubzeroClient(reveal_key="...", capture_caller_context=False)
client.reveal(token, caller_context=RevealCallerContext(file="custom.py", line=10, sdk="python"))
```

**Privacy:** auto-capture may include file paths in audit logs — disable or pass opaque refs if needed.

## LLM proxy preview

Dry-run scan (pre-call tokenize), discover (find-only with scores), and restructure (post-call detokenize). No upstream LLM call.

```python
# Pre-call: detect PII and preview tokenization
scan = client.proxy.scan(
    messages=[{"role": "user", "content": "Patient SSN 123-45-6789"}],
)
print(scan.messages[0].tokenized)
print(scan.messages[0].tokens_by_entity_type["SSN"])  # grouped tokens by entity type

# Find-only: detect PII with confidence scores (no vault writes)
discover = client.proxy.discover(
    messages=[{"role": "user", "content": "Patient SSN 123-45-6789"}],
)
print(discover.messages[0].matches[0].score)

# Provider payload (OpenAI multimodal, Anthropic, Gemini, etc.)
scan = client.proxy.scan(
    body={
        "messages": [
            {"role": "user", "content": [{"type": "text", "text": "SSN 123-45-6789"}]},
        ],
    },
)
print(scan.body)

# Post-call: resolve embedded vault tokens to plaintext (requires reveal policy on proxy key)
result = client.proxy.restructure(
    messages=[{"role": "assistant", "content": "SSN on file: [SSN_abc12345]"}],
)
print(result.messages[0].restructured)
```

`proxy_key` falls back to `api_key` or `tokenize_key` if omitted. Set `SUBZERO_PROXY_API_KEY` for `from_env()`.

## Batch tokenize

For ETL pipelines. Max **100** items per API request; the SDK auto-chunks larger lists.

```python
from subzero import TokenizeBatchItem, TokenizeBatchContext

results = client.tokenize_batch(
    [
        TokenizeBatchItem(index=0, entity_type="SSN", value="123-45-6789"),
        TokenizeBatchItem(index=1, entity_type="SSN", value="987-65-4321"),
    ],
    context=TokenizeBatchContext(source="dbt", pipeline_id="contacts"),
)

for item in results:
    if item.ok:
        print(item.index, item.token)
    else:
        print(item.index, item.error)  # per-item errors do not fail the batch
```

## Example

With the API running and keys configured in the dashboard:

```bash
export SUBZERO_TOKENIZE_API_KEY=sz_live_...
export SUBZERO_REVEAL_API_KEY=sz_live_...
python examples/vault_demo.py
```

## Tests

```bash
pytest
```

## PyPI

Local editable install only for now.
