Metadata-Version: 2.4
Name: nevarn-sdk
Version: 0.1.1
Summary: Thin client for the Nevarn platform API: a bot's intents, executions under Decionis policy, bounds, receipts and halt.
Author: Binaries Fort AB
License-Expression: Apache-2.0
Project-URL: Documentation, https://nevarn.com/docs/sdk
Project-URL: Repository, https://github.com/orepos/Nevarn
Keywords: nevarn,trading,agents,decionis,bots
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: httpx<0.29,>=0.28.1
Dynamic: license-file

# nevarn-sdk (Python)

A thin client for the Nevarn platform API, for an operator's own bot and
servers. It adds no authority: a bot sends intents, never orders or keys, and
every method calls a route an `nvk_` API key is already scoped for. Contract:
`docs/strategy/Contract.md`; design: `docs/workplans/agent-sdk.design.md`.

Free to download, under the Apache License 2.0 (`LICENSE`, `NOTICE`). Using
the Nevarn platform it calls is licensed on the platform, by governed actions
or by subscription.

```python
from datetime import datetime, timezone

import nevarn

client = nevarn.Client(baseUrl="https://<your platform API>")  # key from NEVARN_API_KEY
print(client.getMandate().intents)  # shadow mode, and the Decionis policy for each strategy
ruling = client.proposeIntent(
    strategy="polymarket",
    intentKey="momentum-1",  # your idempotency key: reuse it to retry, never for other terms
    intent={"token_id": "7132…", "side": "BUY", "maker_amount": 5_000_000,
            "taker_amount": 10_000_000, "observed_at": datetime.now(timezone.utc)},
)
print(ruling.verdict, ruling.wouldExecute, ruling.reasons)  # executed is always False in shadow

# With an `execute` key: on paper in the tenant's Sandbox, live in Production.
execution = client.executeIntent(strategy="polymarket", intentKey="momentum-2", intent={...})
print(execution.environment, execution.state, execution.paper)  # "sandbox", "paper_filled", the fill
print(client.getExecution(execution.id).state)                 # Production: queued, then submitted, confirmed
```

| Method | Route | Key scope | Today |
| --- | --- | --- | --- |
| `proposeIntent` | `POST /machine/intents` | `intents` | Shadow ruling; nothing is sent to a venue |
| `executeIntent` | `POST /machine/executions` | `execute` | Sandbox: a paper fill and shadow rulings. Production: queued, ruled in ENFORCEMENT at the venue, sent with the tenant's venue credentials |
| `getExecution` | `GET /machine/executions/{id}` | `execute` | One execution and its current state |
| `getMandate` | `GET /machine/mandate` | `read` | Limits, signing grants, payables policy, intent policies, the execution environment and key limits, this month's governed actions |
| `getReceipt` | `GET /machine/receipts/{id}` | `read` | One execution receipt, by the execution's id |
| `halt` | `POST /machine/halt` | `halt` | Turns all signing off and stops the bots' unfinished executions; there is no resume |
| `requestSpend` | `POST /machine/invoices` | `invoices` | Files a supplier invoice |

`proposeIntent` refuses, before sending, an intent with a field named like a
secret (`private_key`, `signature`, `apiKey`, …) or a value shaped like a
private key. A reused intent key with other terms raises `IntentConflict`
(409), as does an intent sent while the deployment has shadow mode off.

`executeIntent` takes the same terms and runs the same local check. Where the
order goes is the tenant's environment, never the bot's choice. An `execute`
key is minted by an owner with its own limits (orders a minute, the largest
order, a 24-hour total, in USDC minor units), and it executes `polymarket` and
`sports` intents. A refusal raises `ExecutionRefused` (409) whose `code` says
why: `key_order_limit_exceeded`, `key_daily_limit_exceeded`,
`tenant_order_limit_exceeded`, `tenant_halted`, `intent_replayed`,
`intent_key_reused`, `key_minted_without_wallet` (a key minted from a password
sign-in asked for Production; it executes in Sandbox only), `strategy_not_executable`,
`policy_not_published`, `policy_mapping_unpublished`, `runtime_adapter_unpinned` or
`authority_not_configured`. The key's minute window raises `RateLimited` (429).

`requestSpend` files a supplier invoice; it is not a request for raw funds. The
platform's payables policy decides the outcome: the vendor must already be
verified and the destination must be its payout address; tiers 1–2 are paid
automatically, tier 3 waits for an owner, tier 4 is paid by the customer. A
duplicate raises `Conflict` (409).

## Guarantees

- The base URL is required and must be `https` (plain `http` only on
  `127.0.0.1`, `localhost` or `[::1]`). There is no default.
- The key comes only from the constructor or `NEVARN_API_KEY`, must be an
  `nvk_` key, and never appears in `repr`, errors or logs. Session tokens are
  refused.
- Redirects are refused, the timeout is 5 seconds, and errors carry only the
  status, the platform error code and the path template.
- There is no method for limits, resume, keys, vendors or approvals, and
  `halt` takes only an optional reason.

## Tests

```bash
python -m pytest tests -q
```

The platform contract tests are `apps/platform/tests/test_sdk_contract.py`
and `test_shadowbot_example.py`, which runs `examples/shadowbot` through the
real app.

## Licence and release

Apache License 2.0 (`LICENSE`; `NOTICE` names Binaries Fort AB). The licence
covers this client only; the platform's use is licensed on the platform, by
governed actions or subscription.

Publishing stays the owner's: PyPI ownership of `nevarn-sdk` and the release
workflow. Until then the `Private :: Do Not Upload` classifier makes PyPI
refuse an upload; the owner removes it in the release change.
