Metadata-Version: 2.5
Name: pulr-sdk
Version: 0.5.0
Summary: Official Python SDK for pulr.ai — secure file exchange for AI agents: upload, share, fetch, approve, and verify files with provenance, scanning, capability URLs, and signed receipts.
Project-URL: Homepage, https://pulr.ai
Project-URL: Repository, https://github.com/taleilon/pulr
Project-URL: Issues, https://github.com/taleilon/pulr/issues
Author: Tal Eilon
License-Expression: MIT
Keywords: ai-agents,artifacts,capability-urls,file-exchange,langchain,provenance,pulr
Requires-Python: >=3.9
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1; extra == 'langchain'
Provides-Extra: verify
Requires-Dist: cryptography>=41; extra == 'verify'
Description-Content-Type: text/markdown

# pulr-sdk

Official Python SDK for [pulr.ai](https://pulr.ai) — secure file exchange for AI agents. Zero runtime dependencies; downloads are SHA-256-verified client-side.

```bash
pip install pulr-sdk            # core (stdlib only)
pip install pulr-sdk[verify]    # + Ed25519 receipt verification
pip install pulr-sdk[langchain] # + LangChain tools
```

```python
from pulr import PulrClient, verify_receipt

pulr = PulrClient()  # reads PULR_API_KEY

artifact = pulr.upload(
    file_path="q3-report.pdf",
    expires_in="48h",
    approval=True,  # link stays dormant until a human approves on the review page
    provenance={"workflow": "quarterly-report", "model": "claude-fable-5"},
    restrictions={"allow_countries": ["US"]},
)
print(artifact["review_url"])

# once approved — anyone with the link fetches, integrity-verified against the hash you expect
data, sha256 = pulr.fetch_capability(artifact["download_url"], expected_sha256=artifact["sha256"])

# Ed25519-signed proof of hash + provenance + scan + approval.
# Pin the signing key from /.well-known/pulr-receipt-key instead of trusting the receipt's own copy:
receipt = pulr.receipt(artifact["artifact_id"])
assert pulr.verify_receipt_with_server_key(receipt)  # needs [verify] extra; False on tampering, never raises
```

LangChain agents:

```python
from pulr.langchain_tool import pulr_tools
tools = pulr_tools(PulrClient())  # pulr_upload / pulr_fetch_capability / pulr_share StructuredTools
```

Full API: `upload`, `get`, `download`, `delete_artifact`, `fetch_capability`, `share`, `list`, `revoke`, `receipt` / `verify_receipt` / `fetch_receipt_key` / `verify_receipt_with_server_key`, `whoami`, `usage`, `audit` / `artifact_audit`, `create_key`, `set_webhook`, `register_agent`, plus sealed handoff (`send`, `offer`, `inbox`, `accept`, `reject`, `address`, `set_address`, `rotate_address`) and workspaces (`workspaces`, `create_workspace`, `update_workspace`, `delete_workspace`). Errors are `PulrError` with `.status`, `.code`, `.body`. `fetch_capability` accepts only `http(s)` capability URLs on the client's own host unless `allow_any_host=True` (never `file://`), and refuses to return bytes it cannot verify. Get a workspace + key at [pulr.ai/app](https://pulr.ai/app).

**0.5.0 change:** `inbox()`, `list()` and `workspaces()` return the server's page dict (`{"offers" | "artifacts" | "workspaces": [...], "total", "limit", "offset"}`) instead of a bare list; `set_address` needs an explicit boolean `paused` (use `rotate_address()` to rotate); the LangChain fetch tool is named `pulr_fetch_capability`.

MIT © Tal Eilon

## Sealed handoff — no link at all

When the recipient has a pulr workspace, send straight to their address (`pa_...`):

```python
pulr.send("pa_...", file_path="deck.pdf", message="final version")

# recipient side
offers = pulr.inbox()["offers"]       # sender, agent id, Ed25519 status, scan, sha256
pulr.accept(offers[0]["offer_id"])    # integrity-verified copy enters the workspace
# or: pulr.reject(offers[0]["offer_id"])

pulr.address()                        # your own workspace address (receive + sender identity)
pulr.rotate_address()                 # old address dies instantly
```

No capability URL is ever created; both sides' receipts carry a `handoff` block.
