Repository navigation
SDKs
Two typed REST clients talk to a running IronVise server (ironvise serve or the desktop app's backend). Both are Apache-2.0, unlike the server (BUSL-1.1), so you can embed them freely.
| Python | TypeScript / JavaScript | |
|---|---|---|
| Package |
ironvise on PyPI |
ironvise on npm |
| Install | pip install "ironvise>=0.8.0" |
npm install ironvise@^0.8.0 |
| Import | ironvise_sdk |
ironvise |
| Client |
LedgerClient (async) |
IronViseClient |
| Framework adapters | LangChain, AutoGen | none (by design) |
| Runs on | Python with httpx
|
Node 18+, Deno, Bun, browsers with fetch
|
| Full reference | sdk/python/README.md | sdk/typescript/README.md |
Use 0.8.0 or newer: earlier releases predate verify_intent / verifyIntent, which lets an agent ask permission before acting instead of only recording.
The server issues bearer tokens with one of three roles:
| Role | Can |
|---|---|
admin |
Everything, including tokens, webhooks, policies, config and approval decisions |
agent |
Create sessions, append events, ask verify-intent
|
auditor |
Read only: every GET endpoint and the event stream; refused on anything that writes |
Create agent and auditor tokens on the desktop app's Access screen, or with create_token / createToken using an admin token. The hooks and proxies read the token from ECTOLEDGER_TOKEN and the server address from ECTOLEDGER_URL.
pip install "ironvise>=0.8.0"
pip install "ironvise[langchain]>=0.8.0" # with the LangChain adapter
pip install "ironvise[autogen]>=0.8.0" # with the AutoGen adapterimport asyncio
from ironvise_sdk import LedgerClient
async def main():
async with LedgerClient("http://localhost:3000", bearer_token="your_token") as client:
session = await client.create_session(goal="audit-run-42", record=True)
await client.append_event(session.session_id, {"observation": "read config"})
await client.seal_session(session.session_id)
result = await client.verify_session(session.session_id)
print(result.ok, result.signatures)
asyncio.run(main())record=True opens a recorder session that needs no model on the server and stays open until you seal it. Without it, the server runs its own audit loop.
verify_chain returns True only when the chain is intact and every signature verified; verify_session gives the two verdicts separately.
verdict = await client.verify_intent(sid, "run_command", {"command": cmd}, justification="...")
verdict.raise_if_denied()
subprocess.run(cmd, shell=True)Runs the same policy, tripwire and guard checks as the agent loop. Nothing is executed or recorded by asking.
from ironvise_sdk.langchain import LedgerCallbackHandler
handler = LedgerCallbackHandler(session_id="your-session-uuid", base_url="http://localhost:3000")
agent.invoke({"input": "..."}, config={"callbacks": [handler]})Fail-closed: a recording failure raises RecordingFailed and stops the tool call. Pass fail_open=True to accept gaps deliberately.
from ironvise_sdk.autogen import LedgerHook
with LedgerHook(session_id="your-session-uuid") as hook:
agent = AssistantAgent("auditor", model_client=model_client,
tools=hook.wrap_tools([read_file, run_scan], agent_name="auditor"))
await agent.run(task="...")Each call is recorded before it runs, so a recording failure prevents the action. AutoGen then continues with an error in the agent's context; it is not a halt.
- Non-2xx responses raise
LedgerSdkErrorwithstatus_codeandbody. The message ends with the server's explanation. - Connection errors and timeouts reach you as the
httpxexception. -
retries(default 2) applies to GET requests only, on connection errors and 502/503/504. POST, PUT, DELETE and streams are never retried.
npm install ironvise@^0.8.0import { IronViseClient, assertAllowed } from "ironvise";
const client = new IronViseClient({ baseUrl: "http://localhost:3000", bearerToken: "my-api-token" });
const session = await client.createSession({ goal: "audit-run-42", record: true });
await client.appendEvent(session.id, { event_type: "model_invocation", payload: { model: "gpt-4o" } });
const verdict = await client.verifyIntent(session.id, {
action: "run_command",
params: { command: "ls" },
justification: "...",
});
assertAllowed(verdict);
await client.sealSession(session.id);Live events:
for await (const evt of client.streamEvents({ session_id: "abc" })) {
console.log(evt.event_type, evt.payload);
}- Non-2xx responses throw
IronViseApiErrorwith.statusand.body. - Misuse the client can detect (an id of
"","."or"..") throwsIronViseUsageErrorbefore any request. - Network failures and timeouts propagate as
TypeError/AbortError. -
timeoutMs(default 30000) andretries(default 2, GET only) behave like the Python client.
Events appended through either SDK are hash-chained but not signed: only events the server creates carry an Ed25519 signature. verify_session / verifySession report signed and unsigned counts. See Concepts and Architecture.
Each webhook receiver gets its own key from ironvise webhook-key --url <url>. Both SDKs ship a verifier that refuses deliveries signed for another receiver, timestamps more than 300 seconds off, and replays.
from ironvise_sdk import MemoryReplayCache, verify_webhook
cache = MemoryReplayCache() # one per process
verdict = verify_webhook(receiver_key=os.environ["WEBHOOK_KEY"], receiver_url="https://siem.example/ironvise",
headers=request.headers, body=raw_body, cache=cache)import { MemoryReplayCache, verifyWebhook } from "ironvise";
const verdict = verifyWebhook({ receiverKey: process.env.WEBHOOK_KEY!, receiverUrl: "https://siem.example/ironvise",
headers: req.headers, body: rawBody, cache });verdict.reason is missing_headers, stale, bad_signature or replayed. Pass the raw body exactly as received. The TypeScript verifier uses node:crypto (Node, Deno, Bun).
- Python:
python -m pip uninstall ectoledger-sdk, then installironviseand change imports fromectoledger_sdktoironvise_sdk. This does not touch ledger data or keys. - TypeScript: the
ectoledger-sdkpackage andEctoLedgerClientaliases are gone. UseIronViseClient,IronViseApiErrorandIronViseUsageError.
IronVise, by EctoSpace. Repository | Website | Releases | Report a vulnerability. The server is licensed under BUSL-1.1; the SDKs under Apache-2.0. Compliance mappings are aids, not legal advice or certification.
Get going
Understand
Use
Operate
Project