Skip to main content

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). The package also ships four MCP servers — eniyan-mcp (governance operations), eniyan-fs (local files), eniyan-web (websites, and, where the org opts in, transaction execution at web checkouts), and eniyan-mail (email) — plus eniyan-a2a, an inbound agent-to-agent gate that runs as a reverse proxy in front of your agent, and eniyan-scan, a local discovery tool for personal accounts.

pip install eniyan

Quickstart

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.mint_consumer_token() / mint_trading_token() POST /v1/oauth/token (delegation_id / mandate_id)
client.report_delegation_usage() / report_trading_usage() POST /v1/consumer/delegations/{ref}/usage / POST /v1/trading/mandates/{ref}/usage
client.validate_grant() POST /v1/agents/credentials/validate
client.get_web_config() / report_web_injection() POST /v1/agents/credentials/web-access-config / /web-injection-events
client.create_purchase_request() / get_purchase_status() / consume_purchase_request() / report_purchase_outcome() POST /v1/agents/credentials/purchase-requests[...]
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

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.

{
  "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).

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) plus a Pay / Transact ability that lets the browser engine execute purchases and transactions the org has authorized — see "Web transaction execution (0.5.0)" below (shopping/transaction tools register on the browser engine; the local card vault is set up with eniyan-web --setup-card). 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.

{
  "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.

Web transaction execution (0.5.0) — delegations & mandates at web checkouts

When the organization opts in (dashboard toggles), the browser gate executes the SAME transaction authority agents already exercise via API:

  • Consumer delegations — authorize_transaction with the delegation ref mints a 5-minute transaction-constrained token bound to the page the gate is actually on (the observed host is the counterparty). A step-up pauses locally: relay the consumer's challenge link through your platform, then poll check_transaction. Caps, budgets, counterparty allow-lists, and approvals are identical on either surface.
  • Trading mandates — full parity: web orders ride mandate math only; a refusal is final.
  • Personal purchases — an approved purchase now authorizes the SUBMIT itself.

Fill-less first-class (0.5.0 behavior change): the authorization opens the submit window with no card fill — merchant-saved cards, wallet buttons (Apple/Google Pay on the site), and payment methods on file all ride it, and Eniyan touches no payment instrument of any kind. The locally-vaulted card remains only for stores where the card must be typed (fill_payment); the vault is cross-platform (macOS Keychain built in, Windows/Linux via pip install "eniyan[card]"). The minted access token never appears in tool results or model context. In 0.4.0 the submit window opened only after a card fill — 0.5.0 opens it at authorization.

eniyan-scan — see which agents are set up on your computer, and what they can reach (personal accounts)

Not an MCP server and not a gate: a command-line tool for macOS, Linux and Windows that lists the AI agents and MCP servers configured on your own computer, and what each of them can reach, and reports them to your Eniyan dashboard, where you approve each one or mark it not allowed. It is visibility only — Eniyan records what it finds and what you decide, and it does not stop anything. It is available to personal accounts only.

What it reads (read-only; it never changes a configuration file):

  • the MCP configuration of the AI coding tools, desktop assistants and editors installed for your user, including their extensions and plugins;
  • the projects your AI coding tools record in their own settings files, at most 200: Claude Code's first (the projects listed in ~/.claude.json and the folders under ~/.claude/projects), then Codex's, Gemini CLI's trusted folders, and the folders VS Code and Cursor have opened. Claude Code's are also read first. In a project only another tool recorded, a tool's settings file counts only if that tool is set up on your computer. In each it reads the MCP configuration and the dependency manifests (for agent frameworks and SDKs), and notes other agent configuration files by presence without opening them. A project only Windsurf or Zed knows about is not walked;
  • your own background jobs, to find always-on agent runtimes: LaunchAgents on macOS; systemd user units, autostart entries and your crontab on Linux; your Task Scheduler tasks, Startup folder and Run key on Windows;
  • what your agents can reach: each AI coding tool's saved permission settings (what it may do without asking), the configuration files where developer command-line tools keep their sign-ins (GitHub, GitLab, git, SSH, deploy and cloud tools, package registries, secrets managers, AI CLIs), the NAMES exported in your shell startup files, and the CI workflow files of the same projects.

It never walks your whole home folder, only those projects, and it reads no process list and no logs. It never runs anything to learn a credential: no gh auth status, no cloud CLI sign-in check, no keychain lookup of another tool's secret, no git credential helper. Reading configuration files is the only method. It never opens a private key (a key file counts by its existence), and "a token is saved here" is a yes or no, never the token, its length or a hash of it.

macOS privacy-protected folders. macOS treats the background jobs as their own program, so the access you gave Terminal does not carry over. Without Full Disk Access they cannot read projects in ~/Documents, ~/Desktop, ~/Downloads, iCloud Drive or another volume; those projects are skipped, and eniyan-scan --status says how many were.

What it sends (metadata only): for each finding, its kind, its name, which tool's configuration it came from, its transport (stdio, HTTP, SSE or WebSocket), the name of the program that launches it (never its arguments), its package name when that is a well-known one, which well-known dependencies matched, a remote server's hostname when it looks public (never an IP address or a name under a well-known local suffix such as .local, .internal, .lan, .home or .corp), whether the tool has it switched off, and a device-salted hash in place of each project folder, profile, job label or CI job. Settings, sign-ins and MCP servers that apply to your whole user account carry a hash of your computer itself, so the dashboard shows each computer on its own. What an agent can reach is sent only as identifiers from a fixed list Eniyan serves to the scanner — which tool, which service (GitHub, AWS, npm, ...), which kind of permission ("runs shell commands without asking"), how a sign-in is stored — plus the public hostname a sign-in is for. Each report also carries the device token, what started the run, the scanner and corpus versions, and when it ran. Never file contents, paths, usernames, account or profile names, command arguments, permission rule text, environment variables or secret names (names or values), tokens, workflow, job, branch or repository names, SSH host aliases, IP addresses, headers, or full URLs; a server name that looks like a path is sent as an opaque unnamed-<hash> instead. The salt stays on your computer: eniyan-scan --where <hash>, run on the computer the dashboard names with the hash it shows, prints locally what it stands for: a folder, a profile, a job, or "this computer (user level)".

An approval covers what it saw. The dashboard approves exactly what the finding showed you, and if the finding changed while the page was open, the approval is refused and nothing is recorded. After that, if a later report shows an approved agent can now do more (a new service, a wider permission, or an MCP server whose tools stopped asking first), the finding comes back to you as "Asks again" instead of staying approved. A narrower report never re-opens anything. The dashboard's reach view shows each computer as it last reported: something a computer stops reporting leaves its reach at the next report, and stays in the findings list until you forget it.

pip install --upgrade "eniyan[scan]>=0.9"   # eniyan-scan ships in 0.9
eniyan-scan --install     # paste the device token from the dashboard when asked
eniyan-scan --dry-run     # print the JSON a run sends; sends nothing, needs no token
eniyan-scan --status      # installed jobs, last run, skipped projects, any unsent report
eniyan-scan --uninstall   # remove everything --install created

The scan extra installs keyring (the token store on Linux and Windows), pyyaml (for the AI tools that keep settings in YAML) and, before Python 3.11, tomli. Plain pip install eniyan works too; it skips the YAML settings and counts them as unread.

--dry-run is the exact report only once --install has run. Before that no signature list is cached (fetching one takes the device token), so it shows the harness configs only: no package names, no background job runtimes, no agent frameworks or instruction files from your projects, no agent reach, and project refs from a throwaway salt. After install it matches what a run sends, except that on macOS, run from Terminal, it can read the privacy-protected folders the background jobs cannot.

--install takes a device token you create in the dashboard (Settings → Agent Directory → Add this computer; it is shown once). It reads the token from a prompt, or from stdin with --token-stdin, never from the command line, checks it with Eniyan, and stores it in your system's credential store — never in an argument, a log, an environment variable or a job definition. It then sets up two per-user background jobs, with no admin rights: one that runs when an AI tool's configuration changes, and a daily one that catches anything the first misses. It also records, locally, the config-folder overrides set in the terminal it runs in (such as CLAUDE_CONFIG_DIR, CODEX_HOME, GH_CONFIG_DIR, KUBECONFIG and the XDG_* folders; paths only, from a fixed list), because the background jobs never see that terminal's environment. Every run reads those folders; --status lists the recorded names, and running --install again records them afresh after you change one. Before it exits it sends the first report itself, so the dashboard shows the computer straight away, even after a reinstall with a new token. It prints exactly what it installed. --uninstall stops the jobs and deletes them, the stored token and ~/.eniyan/scan/; it is safe to run when nothing is installed.

What --install sets up, per system:

Token Change-triggered job Daily job
macOS Keychain LaunchAgent on WatchPaths LaunchAgent on StartCalendarInterval
Linux Secret Service, through keyring systemd user .path unit systemd user .timer (Persistent=true)
Windows Credential Manager, through keyring Task Scheduler task every 15 minutes (Windows has no per-user file trigger; an unchanged run exits at once) Task Scheduler daily task that runs late if the computer was off

On Linux without a systemd user manager (some containers), it adds one hourly line to your crontab instead, inside a marked block that --uninstall removes exactly; that run does a daily scan at most once per 20 hours, and there is no job that runs when a configuration changes. A crontab line needs a cron daemon to run it: on WSL without systemd, which starts none, --install stops when none is running and says how to turn one on; elsewhere it installs and tells you if it saw no daemon, and --status says so too. If Linux has no usable keyring, or the jobs would run from cron (which cannot reach your desktop keyring), --install stops and says so; --token-file stores the token in ~/.eniyan/scan/device-token (owner-only) instead, and says that it is a plaintext file. To run eniyan-scan --run from a scheduler of your own instead of --install, set ENIYAN_SCAN_DEVICE_TOKEN in that job's environment; it is read only when no token is stored.

Nothing keeps running without --install: pip install starts no background job, and --run and --dry-run scan once and exit. The device token can add findings and refresh what they say (switched off or not, the matched dependencies, what they can reach), but it cannot approve, remove or read your decisions, and it cannot change what a finding runs: the same name running a different program or host arrives as a new finding for you to decide. An account holds at most 2000 findings; a report that would go past that is refused whole and nothing from it is stored, so forget the findings you no longer need in the dashboard. If you revoke the device in the dashboard, the next run exits with status 2 and stops reporting until you reinstall with a new token.

Release files for eniyan 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for eniyan 0.9.0
File Size Uploaded
eniyan-0.9.0.tar.gz 324.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for eniyan 0.9.0
File Interpreter ABI Platform
eniyan-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 681.7 kB

Release files / eniyan-0.9.0.tar.gz

Download URL eniyan-0.9.0.tar.gz
Size 324.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7c71c47731491bc6f5660019a6fb72e8979bc066161a6e7dccc98e5f6639abac
BLAKE2b-256 checksum
How to use checksums
e8c464c81c29a0a0d922b964d5c6f5bf471ed7bd7f33315b3b223a21d36ad079
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / eniyan-0.9.0-py3-none-any.whl

Download URL eniyan-0.9.0-py3-none-any.whl
Size 357.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
09707a0c7cc88ff66042be35d1539e20e048e1fd6daa286d9509de2844300e55
BLAKE2b-256 checksum
How to use checksums
c92cc29cb11ec1f028fecd6a0619f6e68cba680cf072c34777ddca0932c26407
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.5.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page