Skip to content

Repository files navigation

Ruhusa — Authorization for Agentic AI

Ruhusa

Continuous authorization for multi-agent AI systems.

Ruhusa means permission in Swahili.

LLMs decide what action to propose. Ruhusa determines whether they have permission to execute it.

Authority should narrow as agents delegate—not expand.

Table of Contents

Overview

Ruhusa is an open-source authorization framework and research artifact for continuous, least-privilege authorization across AI agents, tools, and multi-agent workflows.

The framework separates agent reasoning from authorization:

Agent / LLM
    |
    | proposes an action
    v
Ruhusa
    |
    | deterministic authorization
    v
ALLOW | DENY | REQUIRE_APPROVAL
    |
    v
Protected Tool / API / Resource

Ruhusa is not an agent framework, workflow engine, identity provider, LLM gateway, or general-purpose IAM system. Its purpose is to provide a small, inspectable authorization boundary in which trusted policy and provenance determine whether an agent-proposed action may proceed.

Project Status

Current package version: 0.7.0 Current milestone: v0.7 — production interfaces and durable persistence Release status: stable release

v0.7 preserves the frozen v0.6 research behavior while adding stable dependency protocols and optional PostgreSQL-backed durable security state.

PostgreSQL-backed implementations are available for grants, revocations, invocations, trusted tool registrations, execution lifecycle, and audit.

Ruhusa remains pre-1.0. Public APIs and guarantees may still evolve before 1.0.

Current Capabilities

Ruhusa currently includes research implementations for:

  • deterministic default-deny authorization
  • least-privilege, multi-hop delegation
  • delegation-chain identity continuity
  • task-bound authority
  • cross-task replay protection
  • action, resource, and argument constraints
  • human-approval decisions
  • continuous grant revocation
  • fail-closed policy and security-store failures
  • hash-chained authorization audit records
  • trusted canonical grant issuance via InMemoryGrantStore
  • canonical grant-content integrity checks
  • invocation provenance via InMemoryInvocationStore
  • operation-bound invocation records
  • invocation expiry
  • tool identity and implementation identity via InMemoryToolRegistry
  • canonical invocation verification for delegated and direct requests
  • fail-closed handling of missing canonical tool identity when tool verification is required
  • execution lifecycle state via InMemoryExecutionStore
  • atomic process-local execution claims and ExecutionPermit
  • replay blocking after a claimed invocation is completed
  • safe release when failure is known to occur before an external side effect
  • fail-closed UNKNOWN state for uncertain external outcomes
  • execution-time authorization revalidation before protected side effects
  • terminal CANCELLED state when live authority becomes invalid before execution
  • stale-claim quarantine from CLAIMED to fail-closed UNKNOWN
  • explicit UNKNOWN reconciliation to COMPLETED or AVAILABLE through trusted recovery infrastructure
  • process-local single-winner reconciliation semantics
  • stale-permit protection across recovered execution attempts
  • adversarial attack benchmarks

Production persistence capabilities include:

  • stable persistence protocols independent of concrete backends
  • optional PostgreSQL installation through ruhusa[postgres]
  • immutable PostgreSQL grant registration
  • monotonic PostgreSQL revocation
  • immutable PostgreSQL invocation provenance
  • immutable trusted tool implementation registration
  • database-authoritative execution claims
  • cross-process single-winner execution semantics
  • stale-permit execution fencing
  • durable UNKNOWN execution recovery
  • serialized PostgreSQL audit writes
  • tamper-evident PostgreSQL audit-chain verification
  • fail-closed PostgreSQL backend failure behavior

Not every configuration provides the same security guarantees. Self-asserted identity fields in weak mode remain intentionally benchmarked as forgeable.

Research Method

Ruhusa uses an attack-first development process.

Define attack
    |
    v
Run against baseline
    |
    +---- blocked ----> document existing invariant
    |
    +---- succeeds ---> record GAP
                         |
                         v
                    identify root cause
                         |
                         v
                    implement smallest
                    targeted control
                         |
                         v
                    rerun attack

A passing test can represent either a blocked attack or a successfully reproduced vulnerability. See docs/attack-benchmarks.md for the GAP, BLOCKS, and CONTROL conventions.

Security Model

The central architectural distinction is between agent-controlled claims and trusted authorization state.

UNTRUSTED / SELF-ASSERTED
--------------------------------
LLM reasoning
AuthorizationRequest fields
caller identity claims
tool identity claims
action/resource/arguments
presented delegation objects

TRUSTED / CANONICAL
--------------------------------
Ruhusa authorization core
PolicyStore
GrantStore
RevocationStore
InvocationStore
ToolRegistry
ExecutionStore
AuditLog

Reference implementations:
- in-memory stores for local/research use
- PostgreSQL stores for durable production state

trusted orchestration state

A recurring research finding is:

Security-relevant identity claims must be grounded in trusted provenance, not merely represented as matching strings inside an agent-controlled request.

This principle emerged first with grant provenance in v0.4 and now extends to invocation and tool identity in v0.5.

Weak and Strong Provenance Modes

Weak mode

Without an invocation store, Ruhusa may evaluate self-asserted request fields such as:

invoking_principal_id
tool_id
implementation_id

These checks can detect missing or obviously unregistered values, but a compromised executing agent can forge values that look legitimate.

Weak mode is therefore a compatibility and consistency mode, not a trusted provenance boundary.

Strong mode

With a trusted InvocationStore, a trusted orchestration layer creates a canonical InvocationRecord that binds:

invoker
executor
task
action
resource
arguments digest
tool identity
implementation identity
recorded_at
expires_at

Canonical invocation verification applies to both delegated and direct/non-delegated requests.

For delegated requests, Ruhusa additionally verifies that the canonical invoking principal matches the grantor of the leaf delegation grant.

When a tool registry is configured, canonical tool identity must be present and trusted for the requested action.

Authorization Flow

The current Ruhusa.authorize() flow is approximately:

AuthorizationRequest
        |
        v
1. Task validity
        |
        v
2. Structural delegation validation
   - chain origin
   - identity continuity
   - task binding
   - temporal validity
   - scope attenuation
        |
        v
3. Canonical invocation verification
   - applies to direct and delegated requests
   - invoker
   - executor
   - task
   - action
   - resource
   - arguments digest
   - expiry
        |
        v
4. Tool identity verification
   - canonical runtime identity
   - fail closed if required identity is missing
   - registered implementation
   - action permitted by implementation
        |
        v
5. Canonical grant provenance
        |
        v
6. Revocation
        |
        v
7. Effective delegated scope
        |
        v
8. Policy evaluation
        |
        v
ALLOW | DENY | REQUIRE_APPROVAL
        |
        v
Audit decision

See docs/architecture.md for the detailed architecture.

v0.6 Experimental State

The current benchmark contains 44 implemented experiments across delegation, provenance, tool identity, execution lifecycle, execution-time authority, and recovery.

v0.6-A — Execution Lifecycle

Experiments 18–27 preserve the v0.5 exact-replay baseline and add a separate execution lifecycle.

Key observed results:

Exp 18  exact replay through authorize()                   GAP
Exp 19  second execution claim                            BLOCKS
Exp 20  concurrent process-local claim race               BLOCKS
Exp 21  replay after completion                           BLOCKS
Exp 22  known pre-side-effect release and retry           CONTROL
Exp 23  uncertain external outcome retry                  BLOCKS
Exp 24  stale/forged execution permit                     BLOCKS
Exp 25  expired execution authority                       BLOCKS
Exp 26  authorization DENY does not consume execution     CONTROL
Exp 27  execution-store failure                           BLOCKS

The research distinction is:

Operation-bound provenance is not execution uniqueness.

Ruhusa.authorize() intentionally remains non-consuming so the v0.5 baseline stays reproducible. Side-effecting integrations opt into the execution lifecycle through ExecutionController.

v0.6-B — Execution-Time Authority Validity

Experiments 28–35 test whether authority that was valid when execution was claimed is still valid immediately before an external side effect.

Observed results:

Exp 28  revocation before execution claim                 BLOCKS
Exp 29  revocation after claim without revalidation       GAP
Exp 30  post-claim revocation with revalidation           BLOCKS
Exp 31  task expiry after claim                           BLOCKS
Exp 32  policy change after claim                         BLOCKS
Exp 33  stale/forged permit at revalidation               BLOCKS
Exp 34  execution-state failure during revalidation       BLOCKS
Exp 35  revocation after successful revalidation          GAP

Experiment 35 is intentionally preserved as a residual TOCTOU boundary. v0.6-B narrows the authorization-to-use window but does not make authorization state atomic with a remote side effect.

The research distinction is:

Authorization-time validity is not execution-time validity, and execution-time revalidation is not atomic authorization plus side effect.

v0.6-C — Fail-Closed Recovery for Uncertain Execution

Experiments 36–44 evaluate stale execution claims and explicit recovery from UNKNOWN.

Observed results:

Exp 36  stale CLAIMED execution becomes UNKNOWN             BLOCKS unsafe retry
Exp 37  live claim cannot be recovered before threshold     BLOCKS claim stealing
Exp 38  confirmed side effect resolves UNKNOWN              COMPLETED / replay blocked
Exp 39  confirmed no side effect permits a fresh claim      CONTROL
Exp 40  reconciliation outside UNKNOWN                      BLOCKS
Exp 41  concurrent reconciliation                           BLOCKS / one winner
Exp 42  old permit after recovery                           BLOCKS
Exp 43  invalid stale-recovery window                       BLOCKS invalid configuration
Exp 44  empty reconciliation reason                         BLOCKS malformed recovery

v0.6-C establishes a fail-closed recovery lifecycle, but it does not authenticate the source of a reconciliation outcome. reconcile_unknown() is therefore a trusted-infrastructure API: an agent must not be allowed to self-assert SIDE_EFFECT_CONFIRMED or SIDE_EFFECT_NOT_APPLIED.

The research distinction is:

Execution-attempt uniqueness does not imply side-effect uniqueness, and safe recovery requires trustworthy knowledge of the external outcome.

Validation Baselines

Frozen v0.5.0 release baseline:

ruff format: 27 files left unchanged
ruff check:  All checks passed
pytest:      91 passed
build:       dist/ruhusa-0.5.0.tar.gz
build:       dist/ruhusa-0.5.0-py3-none-any.whl

Validated v0.6-C development baseline before the release-version bump:

ruff check:  All checks passed
pytest:      118 passed
build:       dist/ruhusa-0.5.0.tar.gz
build:       dist/ruhusa-0.5.0-py3-none-any.whl

The final v0.6.0 release validation is recorded below.

v0.6.0 Release Validation

ruff check:  All checks passed
pytest:      118 passed
build:       dist/ruhusa-0.6.0.tar.gz
build:       dist/ruhusa-0.6.0-py3-none-any.whl

The v0.6 threat model is frozen at docs/threat-model/v0.6.md.

Documentation

The repository separates architecture, security assumptions, and experimental evidence:

Requirements

  • Python 3.12+
  • PostgreSQL for durable production persistence
  • uv for repository development

Installation

Core installation:

pip install ruhusa

PostgreSQL persistence:

pip install "ruhusa[postgres]"

The core package does not require PostgreSQL dependencies.

Development

Clone and install:

git clone https://github.com/Claire56/ruhusa.git
cd ruhusa
uv sync

Run the test suite:

uv run pytest

Run the attack benchmarks:

uv run pytest tests/test_replanning_attacks.py -v
uv run pytest tests/test_tool_identity_attacks.py -v
uv run pytest tests/test_execution_lifecycle_attacks.py -v
uv run pytest tests/test_execution_time_authority_attacks.py -v
uv run pytest tests/test_idempotency_recovery.py -v

Format and lint:

uv run ruff format .
uv run ruff check .

Build:

uv build

Before committing:

uv run ruff format .
uv run ruff check .
uv run pytest
uv build

Core Usage

At its simplest:

from ruhusa import Ruhusa

gate = Ruhusa(policy_store=policies)
decision = gate.authorize(request)

if decision.allowed:
    execute_tool()

Strong provenance requires a trusted orchestration layer to populate canonical runtime state rather than allowing executing agents to self-assert that state.

For side-effecting integrations using the v0.6 execution lifecycle:

from ruhusa import ExecutionController

controller = ExecutionController(gate)

claim = controller.begin(request)
if claim.allowed and claim.permit is not None:
    live = controller.revalidate_before_execution(request, claim.permit)
    if live.allowed:
        # Execute the protected side effect here.
        controller.complete(claim.permit)

If the external outcome becomes uncertain after the request is sent, use mark_unknown() rather than automatically retrying. v0.6 does not claim atomic authorization plus external side effect or exactly-once downstream execution.

Milestones

v0.1 — Deterministic Authorization Core

Established default-deny authorization, scoped delegation, human-approval decisions, fail-closed policy evaluation, and hash-chained audit logging.

v0.2 — Continuous Revocation

Added mid-workflow revocation, fail-closed revocation checks, and earlier emergency revocation semantics.

v0.3 — Task-Bound Delegation

Bound grants to originating tasks and blocked cross-task replay and chain splicing.

v0.4 — Replanning and Trusted Grant Provenance

Introduced adversarial replanning tests, discovered the fresh-grant-remint gap, and added canonical grant issuance and content-integrity verification.

v0.5 — Invocation Provenance and Tool Identity

Added trusted invocation provenance, tool/implementation identity, operation binding, direct-request mediation, and fail-closed canonical tool identity verification.

The v0.5 milestone intentionally retains one documented limitation: exact same-operation invocation replay is not prevented by one-shot authorization consumption.

v0.6 — Execution Lifecycle, Execution-Time Authority, and Recovery

v0.6-A adds execution claims, replay controls, completion/unknown/cancelled lifecycle state, and process-local concurrency protection.

v0.6-B adds execution-time revalidation so revocation, task expiry, and policy changes that occur after a claim can be observed immediately before use.

v0.6-C adds fail-closed stale-claim recovery and explicit reconciliation of UNKNOWN outcomes. A confirmed external effect becomes COMPLETED; confirmed non-execution may return the invocation to AVAILABLE for a newly authorized claim.

The remaining boundary is deliberate: Ruhusa does not make authorization, recovery state, and a remote side effect transactionally atomic. It also does not authenticate reconciliation evidence, provide durable distributed recovery, or guarantee downstream idempotency or exactly-once execution.

v0.7 — Production Interfaces and Durable Persistence

Introduced stable persistence protocols and optional PostgreSQL-backed implementations for security state.

v0.7 adds database-authoritative execution fencing, durable uncertain execution recovery, serialized audit-chain persistence, and explicit backend failure contracts while preserving the frozen v0.6 research artifact.

Research Direction

The working research question is:

Under what workflow transformations does authorization cease to represent the authority originally delegated by a principal, and what runtime invariants are required to preserve that authority across delegation, revocation, replanning, concurrency, tool invocation, and information propagation?

The research progression now extends beyond provenance into execution semantics:

identity claim != provenance
provenance != complete mediation
operation binding != execution uniqueness
authorization-time validity != execution-time validity
execution-time revalidation != atomic authorization + side effect

Current and future research areas include:

  • authorization/execution atomicity
  • downstream idempotency and side-effect deduplication
  • distributed concurrency and durable execution state
  • authenticated/provenanced recovery evidence
  • durable reconciliation of uncertain outcomes
  • authority leases / epochs and TOCTOU
  • authorization propagation across branch/merge workflows
  • multi-agent collusion
  • descendant revocation
  • durable approval evidence
  • cryptographic agent and tool identity
  • information provenance and derived-data authority
  • LangGraph, MCP, and A2A integrations
  • external PDP and IAM integrations

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md.

See docs/production/release-readiness.md for the v0.7 production deployment requirements and explicit non-guarantees.

License

Apache License 2.0.

About

Open-source authorization framework for enforcing least privilege, secure delegation, and per-action policy controls across AI agents, tools, MCP, and multi-agent workflows.

Resources

Contributing

Security policy

Stars

20 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages