Sernixa Python SDK
Sernixa gates Python function execution through a deterministic approval engine. The backend decides whether an action is allowed, auto-approved, blocked, or pending human review; the SDK executes the wrapped business function locally only after an approved-style decision.
The SDK is intentionally small: it does not execute business logic in the Sernixa backend, and it does not pretend to be a full agent framework. It gives you low-friction interception points for plain Python, LangChain, CrewAI, and MCP-style tool boundaries.
Before You Start
You need:
- A Sernixa workspace.
- A Sernixa API base URL, for example
https://api.sernixa.comfor hosted environments orhttp://localhost:8000for local development. - A browser-issued access token or an organization API key for automation.
- One function, tool, or agent action to protect first.
The SDK sends governance metadata to Sernixa. Your function still runs in your process, and only after Sernixa returns an approved-style decision.
Quickstart
Install from PyPI:
python3 -m pip install --upgrade sernixa
The current public Python SDK release is 0.6.9. The upgrade command selects
the latest release; verify it with python3 -m pip show sernixa.
For local development from this checkout:
pip install -e packages/sernixa
Configure the API endpoint and key:
export SERNIXA_BASE_URL=https://api.sernixa.com
export SERNIXA_API_KEY="<your-org-api-key>"
For local development, use the local FastAPI backend instead:
export SERNIXA_BASE_URL=http://localhost:8000
import sernixa
@sernixa.intercept(
intent_id="customer-notes",
risk_level="LOW",
operation_class="read",
data_sensitivity="internal",
systems_touched=["postgres"],
metadata={"ticket": "DEMO-1"},
)
def summarize_customer(customer_id: str) -> str:
return f"summary for {customer_id}"
print(summarize_customer("cus_123"))
If Sernixa returns approved, auto_approved, or executed, the function runs
locally. If it returns pending_review, the SDK polls until a reviewer approves
or rejects the action, or until the configured timeout expires.
First Run Checklist
- Create or choose a Sernixa workspace.
- Sign in interactively or create an organization API key for automation.
- Export
SERNIXA_BASE_URLand the selected credential. - Wrap one low-risk function with
@sernixa.intercept(...). - Run the function and confirm the decision appears in the Command Center.
- Increase risk levels only after the low-risk path is working.
Approval Memory does not silently auto-approve high or critical actions. Supported host integrations can still allow exact, bounded work covered by a verified direct human request when organization policy permits it. New or unclear scope remains reviewable, and hard policy still blocks.
Universal Intent Contracts
Call client.complete_intent_contract(contract_id) to explicitly finish task
authority. Completion preserves signed history and prevents further authority
use; ending a host turn does not automatically complete the task.
Compile one signed, expiring task authority and reuse it across ordinary steps. The backend—not the SDK—normalizes and matches every call. Established contracts take the deterministic fast path and do not invoke RAG or Gemini for each action.
contract = client.compile_intent_contract(
prompt="Fix the failing tests and push it.",
session_id="task-123",
trusted_context={
"organization_id": "org_123",
"workspace_root": "/workspace/project",
"repository": "github.com/acme/project",
"remote": "github.com/acme/project",
"branch": "main",
"commit": "abc123",
"environment": "local",
},
)
decision = client.evaluate_intent_action(
contract["contract_id"],
tool_name="git",
action="git.push",
arguments={"argv": ["git", "push", "origin", "HEAD:main"]},
resource={"repo": "github.com/acme/project", "branch": "main", "commit": "abc123"},
)
Use correct_intent_contract(..., expected_version=...) only for a new human instruction. The
optimistic version check prevents a stale correction from replacing newer intent. Revocation is
append-only through revoke_intent_contract().
Configuration
SERNIXA_BASE_URL: Sernixa API URL. Defaults tohttp://localhost:8000.SERNIXA_ACCESS_TOKEN: revocable browser session token. Takes precedence over an API key.SERNIXA_API_KEY: bearer token for the API.SERNIXA_POLL_INTERVAL_SECONDS: approval poll interval. Defaults to2.SERNIXA_POLL_TIMEOUT_SECONDS: approval wait timeout. Defaults to600.SERNIXA_TIMEOUT_SECONDS: per-request HTTP timeout. Defaults to10.SERNIXA_MAX_RETRIES: retries for transient network errors,429, and5xx. Defaults to2.SERNIXA_MAX_ASYNC_IN_FLIGHT: maximum concurrent async SDK HTTP requests per event loop. Defaults to20.SERNIXA_SDK_DLP_MODE: metadata DLP mode. Defaults tomask; usewarn,block, oroffintentionally.SERNIXA_CAPTURE_ARGUMENTS: set totrueto include function argument values in submitted metadata. Defaults tofalse.SERNIXA_LOG_LEVEL: optional SDK logger level such asINFOorDEBUG.SERNIXA_DEBUG: set totruefor verbose SDK request/decision logging.
You can also configure a client directly:
import os
from sernixa import Client
client = Client(
base_url="http://localhost:8000",
access_token=os.environ.get("SERNIXA_ACCESS_TOKEN"),
api_key=os.environ.get("SERNIXA_API_KEY"),
poll_interval=1,
poll_timeout=120,
timeout_seconds=10,
max_retries=2,
max_async_in_flight=20,
)
Validate the configured key and evaluate a policy input directly:
identity = client.whoami()
decision = client.governance_test({"operation_class": "read"})
Backend-authoritative approval continuity and evidence export are also available without constructing raw HTTP requests:
approval = client.approval_detail("approval_123")
bundle = client.export_evidence_bundle(approval_id="approval_123")
verification = client.verify_evidence_bundle(bundle)
if verification.get("valid") is not True:
raise RuntimeError("Evidence bundle verification failed")
For org or time-range evidence, omit approval_id or provide both date_from
and date_to. Ambiguous approval/team/date scopes and limits outside
1..5000 fail locally before a request is sent. These methods return backend
records; they do not reinterpret a cached local status as an approval or a valid
evidence artifact.
For an interactive application, use the browser device flow and then create a client from its revocable session token:
pending = client.start_cli_device_login(device_name="Developer workstation")
print(pending.verification_uri, pending.user_code)
login = client.poll_cli_device_login(pending.device_code)
session = Client(access_token=login.access_token)
plan = session.current_plan()
flight = session.feature_availability("flight_recorder")
The backend remains authoritative for roles, plans, and feature availability.
Call logout_cli_session() to revoke a browser session. API keys remain the
recommended credential for unattended automation.
If whoami() fails, fix the base URL or API key before wrapping production
tools.
Intercepting Functions
@client.intercept(
intent_id="billing-adjustment",
risk_level="HIGH",
operation_class="financial",
data_sensitivity="financial",
systems_touched=["stripe", "postgres"],
)
def adjust_invoice(invoice_id: str, amount_cents: int) -> None:
...
High and critical risk actions are not auto-approved from Approval Memory. This does not turn a verified direct human request into a mandatory second approval: exact, bounded work may proceed when organization policy permits it.
Async Functions
The same decorator works on coroutines:
@client.intercept(
intent_id="agent-read",
risk_level="LOW",
operation_class="read",
data_sensitivity="internal",
systems_touched=["vectordb"],
)
async def run_agent(query: str) -> str:
return await agent.ainvoke(query)
Async clients reuse one httpx.AsyncClient per event loop and cap concurrent SDK
requests with SERNIXA_MAX_ASYNC_IN_FLIGHT. Long-running async services should
close reusable sessions during shutdown:
async with Client() as client:
await protected_tool()
# or
client = Client()
...
await client.aclose()
Decision Handling
from sernixa import is_approved_status, is_terminal_status
assert is_approved_status("auto_approved")
assert is_terminal_status("rejected")
The SDK raises explicit exceptions for reviewer and policy outcomes:
from sernixa.exceptions import (
SernixaBlockedError,
SernixaConfigurationError,
SernixaExpiredError,
SernixaRateLimitError,
SernixaRejectedError,
SernixaTimeoutError,
SernixaValidationError,
)
try:
adjust_invoice("inv_123", 1000)
except SernixaBlockedError as exc:
print(f"Blocked by policy: {exc.reason}")
except SernixaRejectedError as exc:
print(f"Rejected by reviewer: {exc.reason}")
except SernixaExpiredError:
print("Approval expired before a reviewer decided.")
except SernixaTimeoutError:
print("SDK timed out waiting for a decision.")
Configuration and payload mistakes fail before the business function runs:
SernixaConfigurationError: invalid base URL, timeout, poll, or retry settings.SernixaValidationError: malformed action metadata such as an emptyintent_id, invalidrisk_level, or non-JSON metadata.SernixaRateLimitError: the backend kept returning429after retries.
Extra Review Metadata
Use metadata for non-sensitive context that helps the approver understand the action:
@sernixa.intercept(
intent_id="support-note",
risk_level="LOW",
operation_class="update",
data_sensitivity="internal",
systems_touched=["postgres"],
metadata={"change_ticket": "SUP-1842", "env": "local-demo"},
)
def write_support_note(...):
...
Core governance fields such as risk_level and idempotency_key cannot be overridden by extra metadata.
By default, the SDK does not upload function argument values. It sends argument
counts and keyword names only, and masks sensitive-looking metadata before the
request leaves the process. To include full argument values for a trusted local
debugging session, set SERNIXA_CAPTURE_ARGUMENTS=true; keep the default for
shared development and production-like runs.
V3 Delegation Context
For local multi-agent workflows, create a delegation token through Sernixa and attach it while any delegatee agent runs protected tools:
from sernixa import Client, delegation_scope, with_delegation
client = Client()
token = client.create_delegation_token(
delegator_agent_id="orchestrator-agent",
delegatee_agent_id="worker-agent",
scope=delegation_scope(
max_risk_level="low",
allowed_operation_classes=["read"],
allowed_data_sensitivities=["internal"],
resources={"repo": ["sernixa"]},
),
tool_subset=["view-details"],
)
with with_delegation(
agent_id="worker-agent",
token_id=token["token_id"],
signing_secret=os.environ["SERNIXA_REQUEST_SIGNING_SECRET"],
chain_id=token["chain_id"],
runtime_id="local-worker-runtime",
service_identity="spiffe://local/agent/worker-agent",
):
view_details()
The SDK signs each delegated request with a canonical envelope, timestamp, nonce, request body hash, key ID, and runtime identity metadata. Sernixa verifies the request signature, replay status, token signature, hash chain, expiry, delegatee identity, and scope before the existing approval logic runs.
LangChain Adapter
For current LangChain agents, add one middleware instance instead of wrapping every tool:
from langchain.agents import create_agent
from sernixa import Client
from sernixa.adapters import sernixa_middleware
agent = create_agent(
model="openai:gpt-5",
tools=[search_customers, update_customer],
middleware=[
sernixa_middleware(
client=Client(),
environment="production",
data_sensitivity="internal",
systems_touched=["crm"],
)
],
)
The middleware uses LangChain's current wrap_tool_call and awrap_tool_call lifecycle. It calls the handler exactly once for allow, raises SernixaReviewRequiredError for review, and raises SernixaBlockedError for deny. Tool arguments and adapter metadata are redacted before evaluation.
Use the decorator when you control the tool function:
from langchain.tools import tool
from sernixa.adapters import langchain_tool
@tool
@langchain_tool(
intent_id="finance-tool",
risk_level="HIGH",
operation_class="financial",
data_sensitivity="financial",
systems_touched=["stripe"],
)
def transfer_funds_tool(amount_cents: int, destination_account: str) -> str:
"""Transfer funds after Sernixa approval."""
...
Use the object proxy when a tool object already exists:
from sernixa.adapters import secure_langchain_tool
protected_tool = secure_langchain_tool(
existing_tool,
intent_id="customer-lookup",
risk_level="LOW",
operation_class="read",
data_sensitivity="internal",
systems_touched=["crm"],
)
result = protected_tool.invoke({"customer_id": "cus_123"})
Install LangChain separately if you use the adapter:
pip install "sernixa[langchain]"
The dependency-free proxy remains available for older or custom tool pipelines. It guards invoke, ainvoke, run, arun, and direct calls where the underlying tool exposes them, and forwards unknown attributes to the wrapped tool.
CrewAI Adapter
Use the decorator for plain functions that become CrewAI tools:
from sernixa.adapters import crewai_tool
@crewai_tool(
intent_id="ticket-update",
risk_level="HIGH",
operation_class="update",
data_sensitivity="internal",
systems_touched=["ticketing"],
)
def update_ticket(ticket_id: str, note: str) -> str:
return "updated"
Use the object proxy for existing CrewAI-style tool objects:
from sernixa.adapters import secure_crewai_tool
protected_tool = secure_crewai_tool(
existing_tool,
intent_id="crew-ticket-update",
risk_level="HIGH",
operation_class="update",
data_sensitivity="internal",
systems_touched=["ticketing"],
)
protected_tool.run("SEC-1842")
Install CrewAI separately if you use CrewAI itself:
pip install "sernixa[crewai]"
The SDK proxy supports the common run, _run, and direct-call execution
shapes without requiring CrewAI as a hard dependency.
MCP Boundary Helpers
The local SDK helper governs an in-process MCP-style tool boundary:
from sernixa.adapters import McpToolBoundary
boundary = McpToolBoundary(server_name="workspace-mcp", toolset_id="toolset-prod")
def read_file(path: str) -> str:
return open(path).read()
result = boundary.invoke(
tool_name="read_file",
arguments={"path": "/workspace/report.md"},
handler=read_file,
intent_id="mcp-read-file",
risk_level="LOW",
operation_class="read",
data_sensitivity="internal",
client_name="local-agent-host",
)
Place this at the host/router boundary before dispatching a tool call to the
underlying MCP server/tool implementation. For registered remote and stdio MCP
servers, the backend's strict gateway owns protocol initialization, the
initialized notification, session/version continuity, and tools/call.
Gateway Run Wrapper
SernixaGateway is the target high-level developer surface for governing an
existing agent or MCP/tool run call without rewriting the agent. It wraps the
current approval oracle and records gateway metadata on the action request.
import os
from sernixa import RuntimeSensorConfig, SernixaGateway
sensor = RuntimeSensorConfig(
# Explicit argv only; the SDK never invokes a shell.
command=("/opt/sernixa/bin/sernixa-host-sensor",),
api_url=os.environ["SERNIXA_BASE_URL"],
org_id=os.environ["SERNIXA_ORG_ID"],
collector_id=os.environ["SERNIXA_COLLECTOR_ID"],
ingest_secret=os.environ["KERNEL_COLLECTOR_INGEST_SECRET"],
)
with SernixaGateway(
api_key=os.environ["SERNIXA_API_KEY"],
mcp_profile="prod-tools",
enforce_ebpf=True,
auto_start_runtime_sensor=True,
runtime_sensor_config=sensor,
) as gateway:
result = gateway.run(agent.run, input=user_task)
With auto_start_runtime_sensor=True, the SDK validates and starts the installed
host sensor, consumes its JSONL stream, signs Flight Recorder batches, tracks
backend acceptance, privately spools failed non-empty batches, and terminates
the process on close() or context exit. Required evidence fails closed unless
the backend accepts a sensor event or heartbeat before the bounded startup
timeout. The ingest secret remains in the SDK process and is never copied into
the privileged sensor environment.
The SDK does not silently install or privilege a kernel component. Linux eBPF,
macOS Endpoint Security, and Windows ETW/eBPF sensors still require the
platform's explicit installation, entitlement, signing, and privilege steps.
If automatic lifecycle is not requested, enforce_ebpf=True only checks
backend readiness, preserving the existing deployment contract.
Agent Plan Guards
Use the plan guard when a chat agent, Claude-style hook runner, or Codex-style wrapper can declare intended tool calls before execution:
from sernixa import Client
from sernixa.adapters import SernixaAgentGuard, chat_plan_step
guard = SernixaAgentGuard(
Client(),
agent_type="chat",
user_identity={"email": "operator@example.com"},
context={"workspace": "support", "channel": "slack"},
)
guard.declare_plan([
chat_plan_step(
tool_name="calendar.create",
action="calendar.write",
arguments={"title": "Review"},
resource="calendar:primary",
)
])
guard.execute_tool(
calendar_create,
tool_name="calendar.create",
action="calendar.write",
arguments={"title": "Review"},
resource="calendar:primary",
)
declare_plan() calls the shared backend endpoint
/api/controls/governance/agent-plan/evaluate. Every step is evaluated through
the same governance engine and audit chain as governance_test(). Execution is
blocked if no plan was declared, the plan is denied/review-only, the requested
tool is unplanned, or a plan step is reused.
For a host integration that performs execution itself, explicitly consume the backend-signed step proof immediately before dispatch:
client = Client(plan_session_file=".sernixa/passport.json")
intent = client.governance_issue_intent_proof(
provider="codex",
session_id="session-123",
turn_id="turn-123",
cwd_digest=sha256_of_cwd,
prompt=direct_user_prompt,
workspace_context={
"repository": "https://github.com/acme/project.git",
"branch": "main",
"packages": [
{"name": "@acme/sdk", "registry": "npm", "version": "1.4.0"},
],
},
)
passport = client.governance_evaluate_plan(
[{
"step_id": "read-1",
"tool_name": "Read",
"action": "file.read",
"arguments": {"path": "README.md"},
"resource": "README.md",
}],
agent_type="codex",
intent_proof=intent["intent_proof"],
)
authorization = client.governance_authorize_plan_step(
tool_name="Read",
action="file.read",
arguments={"path": "README.md"},
resource="README.md",
provider="codex",
)
The API verifies the signed plan and exact hashes, checks organization/principal
binding and expiry, and atomically consumes the proof. A changed argument,
undeclared tool, or replay raises SernixaBlockedError. Authorization does not
execute the tool and does not replace host permissions.
For high-impact release work, workspace_context lets Sernixa bind an explicit
human instruction to the exact repository, branch, package, registry, version,
and deployment environment. Matching actions can be auto-allowed and audited
as human-authorized and policy-validated. Unknown targets, unrelated packages,
unrequested deployment, force-push without separate authorization, and
secret/permission changes remain reviewable or denied.
Exact actions and targets in a verified direct human prompt can proceed without
redundant review while retaining their risk classification and evidence. Scope
introduced by the agent instead returns review; retry the unchanged plan_id
and plan with its approval_id after approval. Destructive system/history
operations, cross-scope release actions, secret or privilege changes bundled
into release work, hard organization policy, failed security infrastructure,
invalid or expired proof, and proof replay remain blocked.
State Model
executed: Sernixa allowed the action and the SDK executed the local function.auto_approved: Sernixa policy allowed execution without human review.pending_review: the SDK is waiting for a reviewer and polling the approval.rejected: a reviewer denied the action; the SDK raisesSernixaRejectedError.blocked: policy/security denied the action; the SDK raisesSernixaBlockedError.expired: approval TTL elapsed; the SDK raisesSernixaExpiredError.failed: backend replay/execution evidence failed; the SDK raisesSernixaError.
Troubleshooting
Action blocked: inspectexc.reasonand the approval/audit page. Dangerous primitives and invalid signatures fail closed.SernixaValidationError: fix empty IDs, invalid risk levels, emptysystems_touched, or non-JSON metadata.SernixaConfigurationError: checkSERNIXA_BASE_URL, timeout, poll, and retry values.SernixaRateLimitError: reduce agent concurrency or increase backend limits for the environment.SernixaTimeoutError: the approval is still pending afterSERNIXA_POLL_TIMEOUT_SECONDS.- Browser works but SDK fails: verify
SERNIXA_API_KEYand that the backend URL is reachable from the Python process.
Local And Hosted Configuration
Local demo:
export SERNIXA_BASE_URL=http://localhost:8000
export SERNIXA_API_KEY=e2e-admin
export SERNIXA_POLL_INTERVAL_SECONDS=1
Hosted or shared environment:
export SERNIXA_BASE_URL=https://sernixa.example.com
export SERNIXA_API_KEY="$SERNIXA_SERVICE_TOKEN"
export SERNIXA_TIMEOUT_SECONDS=10
export SERNIXA_MAX_RETRIES=2
Use real bearer tokens, HTTPS, shared nonce/rate-limit storage, and production KMS/HSM signing before treating a hosted environment as production.
Current backend handoff note: local SDK examples target a configured API. A production-like Sernixa backend must run behind the Auth.js/Google web session boundary, accept only the short-lived API JWT minted by the web app, and use Convex plus shared Redis for production runtime state.
TypeScript handoff note: the monorepo now includes packages/sdk as a real
Node client for action gating, approval polling, typed errors, and gateway
readiness checks, and the package is published on npm as @sernixa/sdk.
Python remains the fuller SDK for delegation request-envelope signing and
framework adapters.
Release
The package is published on PyPI as sernixa. Use editable install from this
repository only when developing the SDK itself.
For local repository development, use editable install from the repo root:
pip install -e packages/sernixa
Local Examples
examples/basic_auto_approval/: local walkthrough showing low-risk, repeated approval memory, and high-risk pending behavior.examples/multi_agent_delegation/: generic orchestrator/worker delegation token flow.examples/sernixa-sdk/: lower-level sync, async, LangChain, CrewAI, MCP boundary, Node gateway, and compose smoke examples.
Metadata
Release files for sernixa 0.6.9
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| sernixa-0.6.9.tar.gz | 72.0 kB | With provenance | Details for sernixa-0.6.9.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| sernixa-0.6.9-py3-none-any.whl | Python 3 | none | any | Details for sernixa-0.6.9-py3-none-any.whl |
Total release size: 125.7 kB
Release files / sernixa-0.6.9.tar.gz
| Download URL | sernixa-0.6.9.tar.gz |
|---|---|
| Size | 72.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4a684fcc98303d91591cee08ce748b100bdd3ba7a670d79b59a147bf431d696e
|
|
BLAKE2b-256 checksum How to use checksums |
b42622b1a6ec63c21441db433bca1cb298124dcd9a39bce39a7fabd6bb7b1249
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / sernixa-0.6.9-py3-none-any.whl
| Download URL | sernixa-0.6.9-py3-none-any.whl |
|---|---|
| Size | 53.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
939bc40db5f3394016393156308e2b554557399a454868268ed1594166da39c7
|
|
BLAKE2b-256 checksum How to use checksums |
7fcc14ec7e4cc492e67528901c512be344bcd593314e983305b02e4706e0f8c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for sernixa 0.6.9
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for sernixa-0.6.9.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.