Shani Architecture

June 16, 2026 · View on GitHub

Overview

Shani is a Decision Governance Layer that sits between an AI agent's intent and its execution. It does not replace authentication, authorization, or network security. It governs decisions — discrete, purposeful actions that agents propose and must justify.

Agent                    Shani                     World
  │                        │                          │
  │── DecisionProposal ──► │                          │
  │                        │  RiskPipeline            │
  │                        │  ├─ EvidenceEvaluator    │
  │                        │  ├─ RiskAssessor         │
  │                        │  ├─ RuleEngine           │
  │                        │  ├─ DecisionSpaceAnalyzer│
  │                        │  └─ DSALMapper           │
  │                        │                          │
  │                        │  [HITL if D-SAL ≥ threshold]
  │                        │                          │
  │◄── ADO ───────────────│                          │
  │    (signed, one-time)  │                          │
  │                        │                          │
  │  ExecutionBoundary     │                          │
  │  issue_capability(ado) │                          │
  │                        │                          │
  │── cap.http_get() ─────────────────────────────── ►│
  │                        │                          │
  │── register_executed()─►│  (nonce consumed)        │

Components

DecisionProposal

The only input Shani accepts from an agent. The agent describes what it wants to do, why, and with what evidence. The agent does NOT declare its own oversight level — that is Shani's job.

RiskPipeline

Four independent components evaluated in sequence:

EvidenceEvaluator    → quality_score (0.0–1.0)

RiskAssessor         → RiskScore (aggregate + dimensions)

RuleEngine           → DENY / OVERRIDE / PASS

DecisionSpaceAnalyzer→ framing_risk_score

DSALMapper           → effective_dsal

Key design decision: risk_score and D-SAL are separate concepts. risk_score measures reality (how dangerous is this). D-SAL measures governance (who must approve). The DSALMapper's threshold table is the configurable policy that connects them.

Policy

All governance parameters live in policy/decision_policy.yaml. Nothing is hardcoded:

decision_policy:       # DecisionType → base D-SAL
capability_matrix:     # DecisionType → allowed operations
environment_rules:     # high_risk_keywords list
authority_roles:       # D-SAL → human role name
agent_registry:        # agent ID → granted D-SAL + allowed types

ADO (Authorized Decision Object)

A cryptographically signed token. Every security-relevant field is covered by the signature. Mutation of any field breaks verification.

proposal_hash    → binds ADO to exact proposal (fake ADO detection)
signature        → HMAC-SHA256 of all fields except signature itself
nonce            → one-time token (replay prevention)
delegation_rules → max_child_dsal < authorized_dsal (escalation prevention)
exec_context     → signed (execution drift prevention)

ExecutionBoundary

The only place where ADOs become executable Capabilities. No ADO → no Capability → no execution.

# The only way to get a Capability
cap = boundary.issue_capability(ado, proposal)

# The only way to execute
result = cap.http_get(url)    # or http_post, read_file, etc.

The allowed operations per DecisionType are defined in policy.yaml capability_matrix, not in code.

NonceStore

Persistent replay prevention. A nonce consumed once is never accepted again, even after process restart.

InMemoryNonceStore  → development/testing
FileNonceStore      → production (append-only JSONL)
Custom              → implement NonceStore protocol (Redis, PostgreSQL, etc.)

HITL Gate

Wraps the evaluator and inserts human approval when effective_dsal >= threshold.

gate = HITLGate(
    evaluator=ShaniEvaluator(...),
    channel=CallbackApprovalChannel(),  # or Slack, webhook, CLI
    approval_required_at_dsal=2,
)

The authority role name displayed to humans comes from authority_provider.resolve_authority() — not from a hardcoded dict.

DIS (Decision Integrity State)

Tracks system integrity. If integrity is VIOLATED, all proposals are denied until a named human resets it with justification.


Data Flow

Happy Path

1. Agent constructs DecisionProposal (no requested_dsal)
2. RiskPipeline computes effective_dsal from context
3. If effective_dsal >= HITL threshold → wait for human
4. Agent authorization check (granted_dsal >= effective_dsal)
5. ADO issued (signed, nonce set)
6. Agent calls ExecutionBoundary.issue_capability(ado, proposal)
7. Boundary verifies: signature, proposal_hash, nonce not consumed, not expired
8. Capability issued with allowed_operations from capability_matrix
9. Agent calls cap.http_get(url) or similar
10. register_executed(ado) consumes nonce

Denial Path

At any step, failure produces DeniedDecision with:
  - reason (human-readable)
  - pipeline_result (risk_score, rules_triggered, evidence_flags)
  - proposal snapshot
  - to_human_summary() → JSON for HITL notification

Integration Patterns

Pattern A: Tool-level (zero graph changes)

governed_tools = shani_tools(raw_tools, gate=hitl_gate, proposed_by="agent/v1")
agent = create_react_agent(llm, tools=governed_tools)

Pattern B: Node-level (LangGraph)

builder.add_node("isolate", governed_node(fn=isolate_node, gate=gate, ...))

Pattern C: Sidecar (OpenClaw, any framework)

OpenClaw Skill ──POST /approve──► Shani Sidecar ──► token
OpenClaw Skill ──POST /execute──► Shani Sidecar ──► result

File Structure

shani/
├── schemas/
│   ├── decision.py     DecisionProposal, ADO v5, DelegationRules, ExecContext
│   └── state.py        DIS, DSAL state machine
├── core/
│   └── evaluator.py    ShaniEvaluator — orchestrates pipeline
├── risk/
│   ├── assessor.py     RiskAssessor — multi-dimensional risk scoring
│   ├── dsal_mapper.py  DSALMapper — risk_score → D-SAL
│   ├── rules.py        RuleEngine — hard rules
│   ├── evidence.py     EvidenceEvaluator — epistemic quality
│   ├── decision_space.py DecisionSpaceAnalyzer — framing detection
│   └── pipeline.py     RiskPipeline — orchestrates all four
├── authority/
│   ├── policy.py       DecisionPolicyProvider, CapabilityMatrix, AgentIdentity
│   ├── dsal_calculator.py DSALCalculator (legacy; pipeline replaces this)
│   └── provider.py     YAMLAuthorityProvider, StaticAuthorityProvider
├── boundary/
│   ├── capability.py   ExecutionBoundary, Capability
│   └── hook.py         DecisionBoundary, DecisionFirewall, DenialContext
├── hitl/
│   ├── approval/
│   │   ├── gate.py     HITLGate
│   │   └── request.py  ApprovalRequest state machine
│   ├── channel/
│   │   └── channels.py CLI, Callback, Webhook, Slack channels
│   └── mid_execution/
│       └── monitor.py  MidExecutionMonitor (pause/resume/abort)
├── security/
│   └── replay_store.py InMemoryNonceStore, FileNonceStore
├── integrity/
│   └── monitor.py      DISIntegrityMonitor
├── adapters/
│   ├── langchain/      ShaniLangChainTool, patch_langchain_tools
│   ├── langgraph/      shani_tools, governed_node, ShaniLangGraph
│   └── autogen/        shani_autogen_tool, patch_autogen_agent
└── _compat.py          pydantic shim (stdlib-only fallback)

policy/
└── decision_policy.yaml  Single Source of Truth for all policy

spec/
├── shani-v0.4.md        Normative specification (takes precedence over code)
├── ado-schema.json      Normative JSON Schema for AuthorizedDecisionObject
├── proposal-schema.json Normative JSON Schema for DecisionProposal
├── posture-schema.json  Normative JSON Schema for UserPosture
├── canonicalization.md  Canonical serialization format for signatures
├── threat-model.md      Threat catalog and residual risk analysis
└── interoperability/    Cross-implementation conformance profiles