Shani

June 16, 2026 · View on GitHub

Decision Governance Layer for Autonomous AI Agents

"Shani does not make agents smarter. It makes their actions accountable."

License Python Spec CI PyPI


The problem

When an autonomous agent takes an action in production, you can log what happened. But you can't prove it was authorized — who approved it, under what scope, with what evidence.

Observability tools answer "what did the agent do?" Shani answers "was the agent allowed to do it — and who said so?"


How it works

Shani sits between an agent's intent and its execution. An agent proposes a decision. Shani evaluates it against your policy. If authorized, Shani issues a signed ADO (Authorized Decision Object). The agent may only act through a Capability issued from a valid ADO.

Agent ──DecisionProposal──► Shani ──ADO──► ExecutionBoundary ──Capability──► World

No ADO → no Capability → no execution.

Every action — authorized or denied — produces a tamper-evident audit entry:

{
  "session": "ef134ae5-f5f9-4ebb-8108-04e74b715fd8",
  "scenario": "LangGraph HITL — security incident response",
  "actions": [
    {
      "step": "isolate",
      "status": "DENIED",
      "reason": "Production network operations require at least 2 evidence items (current count: 1)",
      "timestamp": "2026-05-27T01:01:26.153310+00:00"
    },
    {
      "step": "mid_execution",
      "event": "paused",
      "authority": "alice@example.com",
      "detail": "reviewing step 2 output",
      "timestamp": "2026-05-27T01:01:26.506321+00:00"
    }
  ]
}

The denied entry tells you why the agent was stopped, not just that it was stopped. The pause entry records who intervened and when. This is the audit trail you hand to your security team, your compliance officer, or your incident response runbook.


Quick start

pip install "shani[core]"

shani check    # end-to-end ADO issuance check
shani demo     # HITL demo (auto-approve)

Or run the LangGraph security incident response demo:

git clone https://github.com/kmori-source/shani
cd shani && pip install -e ".[all]"

# Approve all actions automatically (CI-safe)
SHANI_HITL_AUTO=approve python examples/langgraph_hitl/scenario.py

# Deny all — see what the audit log captures when an agent is stopped
SHANI_HITL_AUTO=deny python examples/langgraph_hitl/scenario.py
cat audit_langgraph.json

Drop-in integration — no agent logic changes

LangGraph

from shani.adapters.langgraph import shani_tools, governed_node

# Wrap tools — zero changes to your graph
governed = shani_tools(tools, gate=hitl_gate, proposed_by="agent/v1")
agent = create_react_agent(llm, tools=governed)

# Or wrap individual nodes
builder.add_node("remediate", governed_node(fn=remediate_node, gate=gate, ...))

LangChain

from shani.adapters.langchain import patch_langchain_tools

governed = patch_langchain_tools(tools, gate=hitl_gate, proposed_by="agent/v1")

AutoGen

from shani.adapters.autogen import shani_autogen_tool

governed_fn = shani_autogen_tool(
    fn=my_tool_fn,
    gate=hitl_gate,
    decision_type=DecisionType.REMEDIATION,
    blast_radius=BlastRadius.LIMITED,
    proposed_by="agent/v1",
)

Generic (any agent framework)

from shani.adapters.generic import governed_tool

@governed_tool(gate=hitl_gate, decision_type=DecisionType.REMEDIATION,
               blast_radius=BlastRadius.LIMITED, proposed_by="agent/v1")
def my_tool(**kwargs):
    ...

OpenClaw (or any HTTP-based agent)

python examples/openclaw_integration/shani_sidecar/server.py
const token = await fetch('/approve', { method: 'POST', body: JSON.stringify({...}) })
const result = await fetch('/execute', { method: 'POST', body: JSON.stringify({ token, ...}) })

Chrome Extension

from shani.adapters.chrome import ChromeAdapter

adapter = ChromeAdapter(gate=hitl_gate, proposed_by="chrome-extension/v1")

Ollama

OLLAMA_MODEL=llama3.2 python examples/langgraph_api/demo.py

Human-in-the-Loop

Configure which risk level requires human approval. Wire in your Slack bot or webhook. Shani blocks execution until a human explicitly approves.

from shani.hitl import HITLGate
from shani.hitl.channel.channels import CallbackApprovalChannel

channel = CallbackApprovalChannel(
    on_new_request=lambda req: notify_slack(req.to_display_dict())
)
gate = HITLGate(
    evaluator=ShaniEvaluator(...),
    channel=channel,
    approval_required_at_dsal=2,   # D-SAL 2+ requires human sign-off
)

# In your Slack bot / webhook handler:
channel.approve(request_id, authority="alice@example.com", note="reviewed alert")

Mid-execution pause and resume are also supported — an agent already running can be stopped and held for review without terminating the session:

mid_monitor.pause(session_id, authority="alice@example.com", reason="reviewing output")
# ... review ...
mid_monitor.resume(session_id, authority="alice@example.com")

When denied, DeniedDecision.to_human_summary() returns JSON with risk_score, rules_triggered, evidence_flags, and proposal snapshot — so humans understand why the agent was stopped.


Policy as Code

All governance parameters live in policy/decision_policy.yaml. No code changes needed:

# What each action type requires
decision_policy:
  remediation:          1
  configuration_change: 2
  network_action:       3
  policy_update:        4   # always D-SAL 4, hardcoded rule

# What each ADO can do
capability_matrix:
  data_access:
    operations: [http_get, read_file]
  configuration_change:
    operations: [http_post, http_put, write_file]

# What counts as a high-risk environment
environment_rules:
  high_risk_keywords: [prod, production, live, customer-data, main-cluster]

# Who approves at each level
authority_roles:
  1: "SRE"
  2: "Security-Engineer"
  3: "CISO"
  4: "Board-Level"

Risk pipeline

Effective D-SAL is computed from proposal context — the agent does not declare its own risk level.

EvidenceEvaluator    → quality score (source trust × confidence)
RiskAssessor         → RiskScore (blast_radius, reversibility, environment, evidence, intent)
RuleEngine           → hard rules (DENY / OVERRIDE before scoring)
DecisionSpaceAnalyzer→ framing detection (alternatives declared?)
DSALMapper           → risk_score → effective_dsal (configurable thresholds)

risk_score (0.0–1.0) and D-SAL (0–4) are separate concepts. The threshold table in policy.yaml converts between them.


Security guarantees

ThreatDefense
Fake ADOproposal_hash — ADO bound to exact proposal
Replay attacknonce — one-time, persists across restarts (FileNonceStore)
D-SAL self-declarationrequested_dsal field removed — Shani computes from context
Delegation escalationmax_child_dsal < authorized_dsal (schema invariant)
Fan-out attackmax_children — bounded descendants
Execution driftexec_context fully signed
Framing attackDecisionSpaceAnalyzer — alternatives required for high-risk
Evidence fabricationSource trust multipliers — SELF_REPORTED = 0.35×

ADO structure (v5)

AuthorizedDecisionObject
 ├── decision_id        identity
 ├── proposal_hash      integrity: SHA-256 of exact proposal
 ├── signature          HMAC-SHA256 of canonical payload (all fields)
 ├── authority          who approved (from authority_provider)
 ├── authorized_dsal    governance level granted
 ├── delegation_rules   escalation + fan-out prevention
 ├── nonce              replay prevention (one-time, 32 bytes)
 ├── issued_at / expires_at
 └── exec_context       signed execution metadata

Every field is covered by the signature. Any mutation breaks verification.


Installation

pip install shani                        # core (stdlib only, no dependencies)
pip install "shani[core]"               # + pydantic + pyyaml (recommended)
pip install "shani[langchain]"          # + LangChain/LangGraph adapters
pip install "shani[all]"                # everything

Optional dependencies for production:

pip install "pydantic>=2.5" pyyaml   # schema validation + policy files
pip install cryptography              # Ed25519 signatures
pip install langgraph langchain-core  # LangGraph integration
pip install langchain-ollama          # Ollama support

A pydantic shim (shani/_compat.py) is included — everything runs without it.


Running tests

pip install -e ".[dev]"

shani check                # quick ADO issuance + verification
shani demo                 # HITL demo (auto-approve)
pytest                     # full test suite

pytest tests/unit/
pytest tests/security/
pytest tests/security/test_signature_coverage.py   # 19 field mutations
pytest tests/security/test_dsal_calculator.py      # D-SAL context computation
pytest tests/security/test_risk_pipeline.py        # 4-component risk pipeline

Examples

DirectoryDescription
examples/langgraph_hitl/LangGraph security incident response with full audit log
examples/hitl_approval/Human-in-the-loop approval flow
examples/remediation/Basic proposal → ADO → execution
examples/delegation/Orchestrator → specialist with escalation blocking
examples/dis_violation/Replay attack → VIOLATED state → manual reset
examples/firewall_chain/Risk levels and RuleEngine DENY/OVERRIDE
examples/langgraph_api/LangGraph + Ollama integration
examples/openclaw_integration/HTTP sidecar for non-Python agents
examples/agent_integration/Generic agent integration
examples/chrome_extension/Chrome extension adapter

Documents

Tutorials

Reference


License

Apache 2.0 — see LICENSE