Metadata-Version: 2.5
Name: eniyan
Version: 0.3.0
Summary: Eniyan SDK — govern your AI agents wherever they run: identity, scoped authority, JIT windows, and self-reported run telemetry.
Project-URL: Homepage, https://eniyantrust.com
Project-URL: Documentation, https://eniyantrust.com/docs
Author-email: Eniyan <stevland@eniyantrust.com>
License-Expression: Apache-2.0
Keywords: agent-identity,ai-agents,governance,mcp,rbac
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Provides-Extra: browser
Requires-Dist: mcp>=1.0; extra == 'browser'
Requires-Dist: playwright>=1.46; extra == 'browser'
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# Eniyan SDK (Python)

Govern your AI agents wherever they run. This SDK wraps the Eniyan API —
agent identity verification, live scope decisions, JIT credential windows,
short-lived OAuth tokens, and self-reported run telemetry — so any agent
loop becomes Eniyan-governed in a few lines. Eniyan never hosts or observes
your loop; your agent reports metadata-only telemetry (names, scopes, token
counts, outcomes — never prompts, arguments, results, or model output).

```bash
pip install eniyan
```

## Quickstart

```python
import os
from eniyan import EniyanClient, governed_run, EniyanScopeRefused

client = EniyanClient(
    base_url="https://api.eniyantrust.com",
    api_key=os.environ["ENIYAN_API_KEY"],
    credential_token=os.environ["ENIYAN_CREDENTIAL_TOKEN"],
    agent_id=os.environ["ENIYAN_AGENT_ID"],
)

with governed_run(client, harness="my-loop", jit=True, scopes=["crm:read"]) as run:
    fetch = run.tool("fetch_accounts", scope="crm:read")(fetch_accounts)
    accounts = fetch()                      # refused under block mode BEFORE it runs
    run.model_call("claude-sonnet-5", input_tokens=1200, output_tokens=300)
```

On exit — success or crash — the run is finished, buffered steps are
flushed, and the JIT window is completed and attested.

## What each piece maps to

| SDK call | API |
|---|---|
| `client.verify()` / `client.check_scope()` | `POST /v1/credentials/verify` |
| `client.mint_token()` / `client.introspect()` | `POST /v1/oauth/token` / `/introspect` |
| `client.validate_grant()` | `POST /v1/agents/credentials/validate` |
| `client.create_task()` / `complete_task()` / `attest_task()` | JIT task lifecycle |
| `client.start_run()` / `append_steps()` / `finish_run()` | `POST /v1/runs` |

Advisory-mode scope violations do not raise — the call proceeds and the
violation is flagged server-side (`GovernanceDecision.advisory_flagged`).
Block mode raises `EniyanScopeRefused` before the tool executes.

## MCP server

`pip install "eniyan[mcp]"` and run `eniyan-mcp` to expose the same
governance operations as MCP tools for Claude Code or any MCP-capable
harness. See the gated docs for configuration.

## Development

```bash
pip install -e ".[dev]"
pytest
```

`tests/integration_local.py` runs the full flow against a local Eniyan
stack (`docker compose up` from the repo root).


## eniyan-fs — govern your agents' access to local files

A second MCP server in the same package: agents reach files ONLY inside
the roots you configure, every access is policy-checked and audited by
Eniyan (metadata only — root aliases + path hashes, never contents,
never full paths), and revoking the agent's credential is the kill
switch.

```json
{
  "mcpServers": {
    "eniyan-fs": {
      "command": "eniyan-fs",
      "env": {
        "ENIYAN_API_KEY": "...",
        "ENIYAN_CREDENTIAL_TOKEN": "...",
        "ENIYAN_AGENT_ID": "...",
        "ENIYAN_FS_ROOTS": "projects=/abs/path:notes=/abs/other"
      }
    }
  }
}
```

Writes and deletes fail closed when Eniyan is unreachable; deletion is
double-gated (org scope `fs:delete` AND `ENIYAN_FS_ALLOW_DELETE=1`).
Until the PyPI release: `pip install "git+https://github.com/Eniyan-Inc/eniyan.git#subdirectory=sdk/python"`.

## eniyan-web — govern your agents' web access

A third MCP server in the same package: agents reach ONLY the websites
allowlisted on the agent's Eniyan dashboard page, with per-site read /
write / edit / download abilities (mapped to request effect: GET, POST,
PUT/PATCH/DELETE). Every access is policy-checked and audited (metadata
only — domains and URL hashes, never page content, never full URLs),
page content is screened locally for prompt injections and always
delivered wrapped as untrusted data, and revoking the credential is the
kill switch.

```json
{
  "mcpServers": {
    "eniyan-web": {
      "command": "eniyan-web",
      "env": {
        "ENIYAN_API_KEY": "...",
        "ENIYAN_CREDENTIAL_TOKEN": "...",
        "ENIYAN_AGENT_ID": "..."
      }
    }
  }
}
```

No sites live in the config — the allowlist is managed in the dashboard
and syncs to the gate within a minute. Writes/edits/downloads fail
closed when Eniyan is unreachable; downloads are double-gated (the
dashboard switch AND `ENIYAN_WEB_ALLOW_DOWNLOADS=1` +
`ENIYAN_WEB_DOWNLOAD_DIR`). The real-browser engine (Playwright) is the
enterprise default and needs `pip install "eniyan[browser]"` plus the
dashboard engine set to browser.
