Metadata-Version: 2.4
Name: syncanix-sdk
Version: 0.1.0
Summary: Syncanix intent verification SDK for Python (Django, Flask, FastAPI)
Author: Syncanix
License: MIT
Project-URL: Homepage, https://syncanix.com
Project-URL: Repository, https://github.com/lvl41/Syncanix
Keywords: syncanix,intent-verification,django,flask,fastapi
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Framework :: Django
Classifier: Framework :: FastAPI
Classifier: Framework :: Flask
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: django
Requires-Dist: django>=3.2; extra == "django"
Provides-Extra: flask
Requires-Dist: flask>=2.0; extra == "flask"
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100; extra == "fastapi"
Requires-Dist: starlette>=0.27; extra == "fastapi"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: httpx>=0.24; extra == "dev"
Requires-Dist: django>=3.2; extra == "dev"
Requires-Dist: flask>=2.0; extra == "dev"
Requires-Dist: fastapi>=0.100; extra == "dev"
Requires-Dist: starlette>=0.27; extra == "dev"
Requires-Dist: uvicorn>=0.20; extra == "dev"
Dynamic: license-file

## syncanix-sdk

Python intent verification SDK for Syncanix (Django, Flask, FastAPI).

Install: `pip install syncanix-sdk[django]` or `[flask]` or `[fastapi]`.

See the [Syncanix docs](https://syncanix.com/docs) for usage.

### v2 (act-as-user) binds

The per-route helpers — `syncanix_verify_intent` (Flask/Django) and
`require_syncanix_intent` (FastAPI) — accept optional keyword args to enforce the
v2 argsHash / audience / single-use-nonce binds (omit them and only the v1
coordinate is checked, unchanged):

```python
@syncanix_verify_intent(
    secret=SECRET,
    # CANONICAL args — NOT the raw body. Reconstruct from every source
    # (ADR-0138): REST → {**view_args, **query, **body}; GraphQL → variables.
    get_args=lambda request: canonical_args(request),
    expected_audience="syncanix:tenant:<tenantId>",  # or omit to skip
    nonce_store=InMemoryNonceStore(),                 # or omit to skip
)
def refund(request): ...
```

Each bind is opt-in. Replay protection needs a `nonce_store`
(`InMemoryNonceStore` is single-instance only — use a shared store for a
horizontally scaled API). The v2 `sub` is surfaced on the verified payload's
`.sub`. Passing the bare request body as `get_args` would falsely reject
path-param / GET / GraphQL calls — reconstruct the canonical args.

<!-- B27.4 note: the previous /docs/sdk-python deep link pointed at a route
     that does not exist; re-point when the per-SDK reference pages ship. -->
