Skip to main content

Reva AI Authorization SDK

reva-ai-authz protects inbound agent/tool boundaries and outbound Agent, API, and MCP hops with Reva Trust Gateway (RTG/PDP) authorization. It also carries authenticated caller identity, W3C tracing, and sanitized multi-hop intent history across supported integrations.

Release state

Item Version
Version described by this source tree 1.0.3
Public PyPI baseline last verified on 2026-08-12 1.0.2; 1.0.3 was not published at that check
Authoritative current state installed package metadata and the public PyPI release files

This packaged README is immutable release documentation, not a live package index. Check the public PyPI project before changing a production pin. If the reviewed 1.0.3 wheel and sdist are not both published and public-index verification has not passed, remain on the verified baseline:

python3.10 -m pip install "reva-ai-authz==1.0.2"

The intended annotated tag is lowercase v1.0.3 with message Release reva-ai-authz 1.0.3. Creating that tag builds a candidate; it does not publish until an authorized maintainer runs the protected manual job.

To test the 1.0.3 candidate, install the reviewed wheel artifact from the tag pipeline, not a similarly named file from an ad hoc build:

python3.10 -m pip install ./reva_ai_authz-1.0.3-py3-none-any.whl
python3.10 -c 'from importlib.metadata import version; import reva_ai; assert version("reva-ai-authz") == reva_ai.__version__ == "1.0.3"'

Once PyPI shows the reviewed 1.0.3 wheel and sdist and public-index verification passes, install with:

python3.10 -m pip install "reva-ai-authz==1.0.3"

Python 3.10–3.13 are supported by the 1.0.3 CI matrix. Framework packages such as LangChain, LangGraph, CrewAI, AutoGen, and MCP are application dependencies; this distribution does not install them.

Migrating from 1.0.2

  1. Deploy the coordinated 1.0.3 PDP and RTG evaluator changes before changing the customer wheel. An older PDP ignores rtg/a2a.context and cannot carry it between Issue Token and Validate Token.
  2. Audit every proxy.mcp(...) call. Protection now defaults on; use endpoint_protected=False only for an intentionally local, non-policy tool.
  3. Restrict direct cedar_context and every value resolved by cedar_context_map to exact schema keys with string, boolean, safe integer, or homogeneous scalar-list values. Null, float, record, nested, and mixed-list values fail validation. Prefer the declarative map for values in the current schema-validated business payload. Keep the encoded mapping within 512 bytes; transaction JWEs over 4,096 bytes and complete PDP header carriers over 6,144 bytes fail closed. Complete compact PDP JSON is limited to 1,048,576 bytes; keep PDP PDP_AI_REQUEST_BODY_MAX_BYTES=1048576 to match that fixed SDK preflight. History is rejected—not truncated—above 65,536 bytes, depth 32, 32 logical events, 2,048 JSON values, or 16,384 UTF-8 bytes in one string.
  4. Handle PDPRequestError, PDPUnavailableError, and PDPProtocolError separately from a definitive RevaAuthorizationError deny.
  5. Use HTTPS for RTG_URL. Plain HTTP requires the explicit local/private/ in-cluster development escape hatch described below.
  6. Final-turn recording now requires an explicit FastAPI-route opt-in and an approved same-RTG-origin HTTPS endpoint. The old mock_rtg_record_final_turn import remains temporarily compatible but only emits a deprecation warning and transmits nothing.
  7. Replace direct context/history mutation with the documented decorators, proxies, adapters, and one isolated standalone_work_item per authenticated worker message.

Do not change a production pin until the candidate wheel and the coordinated PDP/RTG deployment pass the acceptance checklist. The presence of this migration guide is not evidence of publication; use the public index and installed package metadata as the authority.

What must exist before code integration

The SDK cannot invent control-plane metadata. Register and test all of the following in the customer's policy store before running these examples:

  • the exact agent_name used by each inbound route;
  • every destination Agent, API, MCP server, and tool resource ID;
  • each source-to-destination topology edge and action (invokeAgent, invokeTool, read, write, or delete);
  • the static destination URL and endpoint type used for resource resolution;
  • every Cedar context attribute used by policy, with identical spelling and type; and
  • the resource skill catalog used by PDP's promptIntent matcher.

An ID mismatch is expected to deny. A Cedar policy on context.HSNewApp_mrn does not match a payload field named mrn unless the application maps it with cedar_context={"HSNewApp_mrn": value} or declaratively with cedar_context_map={"HSNewApp_mrn": "mrn"}.

SDK 1.0.3's managed context and intent-consistency guarantees require the coordinated 1.0.3 PDP and RTG evaluator changes. An older PDP parses the request but ignores rtg/a2a.context; an older evaluator may accept ALIGNED for a proven action change. Record the deployed SDK wheel hash plus PDP, RTG, gateway, policy-schema, and guardrail-template versions during customer acceptance.

Environment

Set configuration before importing reva_ai or defining protected routes:

export RTG_URL="https://rtg.customer.example"
export POLICYSTORE_ID="customer-policy-store-id"

# Optional platform credential, injected from the customer's secret manager.
export RTG_AUTH_TOKEN="..."

# Logs may contain business data. Use a writable protected path and a safe level.
export REVA_AI_LOG_FILE="/var/log/customer-agent/reva-ai-sdk.log"
export REVA_AI_LOG_LEVEL="WARNING"
export REVA_AI_LOG_STDOUT="false"

# PDP may spend up to five minutes on authoritative guardrail retries.
export REVA_AI_PDP_TIMEOUT_SECONDS="330"

The SDK does not create a file unless REVA_AI_LOG_FILE is explicitly set. If an explicitly configured file cannot be opened, file logging is disabled with a warning rather than failing package import. Application logging handlers or REVA_AI_LOG_STDOUT=true can be used in read-only containers.

Do not set RTG_DISABLED or REVA_AI_ALLOW_EXTERNAL_CONTEXT_WRITES in a protected environment. RTG_DISABLED is an explicit local-test bypass.

RTG_URL must be an absolute HTTPS base URL without embedded credentials, query parameters, fragments, or path-traversal segments. A base path is allowed. For local/private/in-cluster development only, plain HTTP also requires REVA_AI_ALLOW_INSECURE_RTG_HTTP=1 and emits a warning. Keep that escape hatch unset in production; it does not apply to final-turn delivery.

The SDK sends RTG_AUTH_TOKEN to RTG/PDP as X-API-Token; it does not replace the end-user access credential or transaction token. Never put either token in a prompt, business body, cedar_context, history, log field, or model-visible tool argument.

REVA_AI_PDP_TIMEOUT_SECONDS is the timeout for PDP authorization calls only; it defaults to 330 seconds and accepts finite values from 1 to 900. The default exceeds PDP's five-minute total guardrail budget by 30 seconds. Keep gateway, ingress, load-balancer, and application request deadlines above this value, or reduce the coordinated PDP retry budget. Downstream agent/API/MCP transport timeouts remain separate.

If the application uses reva_ai_discover, treat it as optional startup telemetry: it stores application metadata and performs a synchronous /discovery-logs POST. It does not provide missing RTG_URL or POLICYSTORE_ID to authorization; protected paths read those values from the environment and fail closed when either is absent.

Minimal FastAPI boundary

Use a strict object schema, validate a nonblank prompt, and decorate the route inside the normal FastAPI decorator:

from typing import Annotated

from fastapi import FastAPI, Request
from pydantic import BaseModel, ConfigDict, StringConstraints

from reva_ai.sdk.ai import reva_ai_authorise


NonBlank = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)]


class InvokeBody(BaseModel):
    model_config = ConfigDict(extra="forbid")

    message: NonBlank
    conversation_id: NonBlank


app = FastAPI()


@app.post("/invoke")
@reva_ai_authorise(
    agent_name="support-agent",
    action="invokeAgent",
    auth_key="Authorization",
    prompt_key="message",
    principal_claim="sub",
    cedar_context_map={"SupportApp_conversationId": "conversation_id"},
)
async def invoke(body: InvokeBody, request: Request) -> dict[str, str]:
    # Customer code runs only after PDP allows.
    return {"answer": f"Accepted {body.conversation_id}"}

Pass exactly one of agent_name, tool_name, or crew_name. The body key rtg/a2a is SDK-owned and is removed before a strict downstream handler runs. Do not catch RevaAuthorizationError and continue protected work.

The whole-app middleware helper intentionally skips GET, HEAD, and OPTIONS. Decorate sensitive read routes directly; do not claim that the middleware protects them.

Agent-to-agent HTTP

Call the proxy inside a decorated request or an isolated worker context. URLs must come from static allowlisted configuration, never unchecked user/model output:

from reva_ai.sdk.ai.proxy import proxy


async def call_fulfilment(message: str, order_id: str) -> dict:
    response = await proxy.agent(
        endpoint_url="https://fulfilment-agent.internal.example/invoke",
        method="POST",
        body={"message": message, "order_id": order_id},
        prompt_key="message",
        resource_id="fulfilment-agent",
        cedar_context_map={"OrderApp_orderId": "order_id"},
    )
    response.raise_for_status()
    return response.json()

cedar_context and cedar_context_map are PDP-only assertion channels. The SDK places the combined validated result under managed rtg/a2a.context for authorization, but does not add the Cedar target aliases to the downstream Agent/API/MCP business payload or customer handler arguments. Existing source business fields remain unchanged.

Use cedar_context={"Cedar_key": value} when the application already holds a validated value. Use cedar_context_map={"Cedar_key": "business.path"} to resolve a value from the current invocation on every call. The latter is available on validated inbound @reva_ai_authorise route/tool boundaries, outbound Agent/API/protected-MCP proxies, inner-call helpers, and supported framework adapters. A path is case-sensitive and may be an exact top-level key, a dotted path such as patient.mrn, or an indexed path such as items[0].code:

cedar_context_map = {
    "HSNewApp_mrn": "patient.mrn",
    "HSNewApp_primaryCode": "items[0].code",
}

The mapper is declarative; it does not execute a callback. A missing path, a resolved null, a target also supplied in cedar_context, or a path selecting a credential-shaped segment, history, onBehalfOf, promptIntent, or rtg/a2a raises ValidationError before PDP. The combined direct and mapped result then uses the limits below.

The mapping accepts at most 64 attributes and 512 bytes of encoded UTF-8 JSON. Keys must be nonblank strings of at most 128 characters. Values must be a string, boolean, integer in [-(2**53 - 1), 2**53 - 1], or a list/tuple whose items all have one of those same scalar types. Empty lists are accepted. Null, every float (including 1.0), records/objects, nested collections, mixed-type lists, unsafe integers, identity fields such as onBehalfOf, promptIntent, subject, or principal, routing/prompt/history fields, credential-like keys, credential-shaped values, and non-JSON values are rejected.

Explicit/resolved context wins over a same-named root business field at Issue Token. On allow, PDP binds only that validated managed mapping—not arbitrary business fields—into the encrypted transaction token. At the receiving Validate Token PEP, PDP combines the new business body with the authenticated token mapping; a colliding body field or caller-written rtg/a2a.context cannot replace the token-bound value. A later authorized Issue Token call may supply a new mapping for its replacement token; if it supplies none, the prior authenticated mapping is preserved. This two-PEP behavior requires the coordinated 1.0.3 PDP and is not supplied by the published 1.0.2 wheel alone.

Treat these values as request assertions, not proof of user identity or role. The route decorator maps only the FastAPI/Pydantic-validated value, and the MCP tool decorator maps the value actually passed to the tool; raw transport JSON cannot supply a different policy value. reva_ai_authorise_request and whole-app middleware run before route-model validation, so they reject nonempty cedar_context_map. The sub-app compatibility decorator exposes only fixed cedar_context. Use fixed context on those boundaries or a direct route decorator for dynamic mapping:

validated_mrn = body.mrn  # validated by the application schema
cedar_context = {"HSNewApp_mrn": validated_mrn}

Never copy arbitrary model output into cedar_context, and never derive a cedar_context_map from caller/model input.

The PDP endpoints have related but non-identical contracts:

Endpoint Context shape Role
POST /pdp/access/v1/token/enrich managed rtg/a2a.context Outbound proxies authorize Issue Token and mint the encrypted transaction token that carries the explicit mapping.
POST /pdp/access/v1/agent/evaluation managed rtg/a2a.context; authenticated token claim wins during transaction-token validation Inbound decorators and inner-call adapters authorize Validate Token/logical invocations.
POST /pdp/access/v1/ai/evaluation top-level context Runs the same strict managed-context, principal-last, history, Cedar/AVP, and guardrail rules over the direct body shape. It does not mint the proxy transaction token and is not an identical fallback or a second opinion after deny.

For direct evaluation, exact context.chatHistory, context.conversation, and context.hops members are separated as guardrail-history metadata before the remaining context members are validated as managed Cedar attributes. This keeps the existing direct-client history shapes working without admitting nested records through the strict Cedar-value channel. A direct principal.type and principal.id become the same authoritative typed onBehalfOf entity used by Cedar/AVP and guardrails.

All three endpoints reject a complete compact UTF-8 JSON body larger than 1,048,576 bytes before authorization, audit, or guardrail work. SDK-managed calls measure and send the exact same byte sequence. A direct client should do the same, as below; chunked transfer and omitted Content-Length do not bypass the PDP limit. Keep PDP PDP_AI_REQUEST_BODY_MAX_BYTES=1048576 for exact SDK parity. PDP permits an explicitly configured value from 131,072 through 8,388,608 bytes, but changing it is a deployment-contract change: a lower value can reject an SDK-preflighted request and a higher value does not raise the SDK's fixed 1 MiB limit. PDP independently caps the canonical JSON sent to an external guardrail endpoint at 262,144 bytes before the first HTTP attempt. Oversized authoritative evaluator input fails closed and is never truncated or retried; an informational endpoint records the failure without becoming authority.

Call direct AI evaluation only through the authenticated gateway. Pass secrets and tenant configuration from a trusted secret/config provider:

import json
from typing import Any
from urllib.parse import urlsplit

import httpx


async def evaluate_direct_ai(
    *,
    gateway_url: str,
    api_token: str,
    policy_store_id: str,
    prompt: str,
    mrn: str,
    caller_agent_id: str,
    principal_user_id: str,
    audit_authorization: str | None = None,
) -> dict[str, Any]:
    gateway_url = gateway_url.strip().rstrip("/")
    api_token = api_token.strip()
    policy_store_id = policy_store_id.strip()
    prompt = prompt.strip()
    mrn = mrn.strip()
    caller_agent_id = caller_agent_id.strip()
    principal_user_id = principal_user_id.strip()
    parsed_gateway = urlsplit(gateway_url)
    if parsed_gateway.scheme != "https" or not parsed_gateway.netloc:
        raise ValueError("gateway_url must use HTTPS")
    if not all(
        (api_token, policy_store_id, prompt, mrn, caller_agent_id, principal_user_id)
    ):
        raise ValueError("all direct AI evaluation inputs are required")

    headers = {
        "Content-Type": "application/json",
        "X-API-Token": api_token,
        "policyStoreId": policy_store_id,
    }
    if audit_authorization and audit_authorization.strip():
        headers["Authorization"] = audit_authorization.strip()

    body = {
        "subject": {"type": "Agent", "id": caller_agent_id},
        "action": {"name": "invokeAgent"},
        "resource": {"type": "Agent", "id": "records-review-agent"},
        "principal": {"type": "User", "id": principal_user_id},
        "context": {"HSNewApp_mrn": mrn},
        "transmission": {
            "promptKey": "content",
            "role": "user",
            "content": prompt,
        },
    }
    payload_bytes = json.dumps(
        body,
        ensure_ascii=False,
        separators=(",", ":"),
        allow_nan=False,
    ).encode("utf-8")
    if len(payload_bytes) > 1_048_576:
        raise ValueError("direct AI evaluation body exceeds 1 MiB")

    # Must exceed PDP's five-minute authoritative guardrail budget.
    async with httpx.AsyncClient(timeout=330.0) as client:
        response = await client.post(
            f"{gateway_url}/pdp/access/v1/ai/evaluation",
            headers=headers,
            content=payload_bytes,
        )
    if response.status_code == 403:
        raise PermissionError("PDP denied direct AI evaluation")
    response.raise_for_status()
    result = response.json()
    if result.get("decision") is not True:
        raise PermissionError("PDP denied direct AI evaluation")
    return result

subject is the asserted calling workload, while principal is the asserted originating user/on-behalf-of identity. Today the direct endpoint authenticates the gateway request with mandatory X-API-Token, but it does not itself cryptographically bind body subject, body principal, or policyStoreId to that credential. Treat /ai/evaluation as a trusted-service assertion API: never expose it to an untrusted caller, never populate those fields from raw request/model input, scope the service credential to the tenant, and source the identities and policy-store ID only from authenticated service/user context and fixed trusted configuration. They must also match registered policy metadata. An optional Authorization value is captured only as sanitized decision-audit context; it does not authenticate this endpoint or affect the decision. Do not send promptIntent: it is ignored as caller input and remains server-owned. A deny ends the operation. Direct evaluation neither mints an X-Transaction-Token nor permits /token/enrich or another endpoint as fallback after denial.

For protected MCP, acceptance testing must prove the attribute at both calls: the client-side /token/enrich Issue Token decision and the server decorator's /agent/evaluation Validate Token decision.

Trusted external API

proxy.api keeps the downstream body unchanged, maps HTTP methods to read/write/delete, and sends the newly issued transaction token to the target. Use it only with a registered, trusted HTTPS service that is permitted to receive that token:

from reva_ai.sdk.ai.proxy import proxy


async def payment_status(payment_id: str) -> dict:
    response = await proxy.api(
        endpoint_url="https://payments-api.internal.example/v1/status-lookups",
        method="POST",
        endpoint_type="Api",
        body={"payment_id": payment_id},
        prompt_key="payment_id",
        cedar_context={"Payments_paymentId": payment_id},
    )
    response.raise_for_status()
    return response.json()

Do not use this helper to send Reva transaction credentials to a public SaaS endpoint. Query parameters are sent downstream but are not part of the current PDP business body; place authorization-relevant fields in the object body or explicit cedar_context.

Protected MCP client

MCP proxying is protected by default in 1.0.3. Supply the exact registered MCP resource ID; the JSON-RPC tool name and the policy resource ID are not assumed to be interchangeable:

from reva_ai.sdk.ai.proxy import proxy


async def get_labs(mrn: str):
    return await proxy.mcp(
        tool="get_lab_results_lg",
        arguments={
            "message": f"Review the latest lab results for {mrn}",
            "mrn": mrn,
        },
        mcp_client="https://healthsphere-mcp.internal.example/mcp",
        resource_id="healthsphere-mcp_lg/get_lab_results_lg",
        transport="streamable_http",
        cedar_context_map={"HSNewApp_mrn": "mrn"},
    )

The agent-to-tool operation makes two security-relevant calls:

  1. Agent → PDP POST /pdp/access/v1/token/enrich (invokeTool / MCP), where Cedar and synchronous guardrails run and a transaction token is issued.
  2. Agent → MCP server tools/call, where the server's @reva_ai_authorise(tool_name=...) validates the token.

rtg/a2a is the name of the managed metadata key shared by Agent and MCP envelopes; it is not the Agent-to-Agent URL path.

Managed context is capped at 512 bytes before encryption so its JWE/base64url expansion plus existing claims stays within deployed HTTP header budgets. PDP and the SDK fail closed if a newly issued compact transaction JWE exceeds 4,096 UTF-8 bytes. The SDK also rejects the complete PDP request-header set before network I/O when its serialized size exceeds 6,144 bytes. Reduce bounded policy context or metadata; never truncate a credential or bypass authorization to fit it.

Credentials remain HTTP transport headers and are not mirrored into model-visible params.arguments. Protected stdio is therefore rejected while RTG is enabled because it has no reviewed transport-isolated credential channel. Use streamable HTTPS for protected MCP. An intentionally unprotected local tool requires the visible opt-out endpoint_protected=False.

Unknown proxy.mcp keyword arguments raise TypeError; misspelled security options no longer disappear silently.

Protected MCP server

The server decorator uses the physical MCP HTTP transport credential. Customer tool functions should expose only business arguments:

from mcp.server.fastmcp import FastMCP

from reva_ai.sdk.ai import reva_ai_authorise


mcp = FastMCP("healthsphere-mcp")


@mcp.tool()
@reva_ai_authorise(
    tool_name="get_lab_results_lg",
    action="invokeTool",
    prompt_key="message",
    principal_claim="sub",
)
async def get_lab_results_lg(message: str, mrn: str) -> dict:
    return {"mrn": mrn, "results": []}

Verify the exact FastMCP version exposes the current physical HTTP request to the decorator. Test tools/call; a successful initialize or tools/list does not prove tool authorization.

Standalone workers

Use one context per work item. The context manager clears identity, credentials, history, and tracing even if customer code raises.

A true first-hop queue message may establish one sanitized originating prompt:

from reva_ai.sdk.ai.adapters import (
    authorise_inner_call_async,
    standalone_work_item,
)


async def process_first_hop(message) -> None:
    payload = message.json()
    with standalone_work_item(
        agent_name="batch-supervisor-agent",
        agent_type="Agent",
        auth_token=message.headers["Authorization"],
        incoming_headers=message.headers,
        principal_claim="sub",
        originating_payload=payload,
        originating_prompt_key="message",
    ):
        await authorise_inner_call_async(
            resource_name="billing-agent",
            resource_type="Agent",
            action="invokeAgent",
            current_payload=payload,
            prompt_key="message",
            cedar_context={"Billing_accountId": payload["account_id"]},
        )
        await process_authorized_payload(payload)

originating_payload creates only a minimal root prompt hop. It does not import caller-supplied raw history, identity, or credentials. It requires a fresh context and cannot be combined with a transaction token.

A delegated queue message must carry the authenticated managed envelope instead:

from reva_ai.sdk.ai.adapters import (
    authorise_inner_call_async,
    standalone_work_item,
)


async def process_delegated_message(message) -> None:
    payload = message.json()
    with standalone_work_item(
        agent_name="batch-supervisor-agent",
        agent_type="Agent",
        auth_token=message.headers["X-Transaction-Token"],
        incoming_headers=message.headers,
        inbound_rtg_a2a=payload["rtg/a2a"],
    ):
        await authorise_inner_call_async(
            resource_name="batch-supervisor-agent",
            resource_type="Agent",
            action="invokeAgent",
            current_payload=payload,
            prompt_key="message",
        )
        await process_delegated_payload(payload)

standalone_work_item only isolates and seeds SDK context. It does not authenticate the queue message, verify broker provenance, or authorize business work. The consumer must authenticate/trust the ingress transport, keep the managed envelope bound to its transaction token, and obtain an allow from PDP before any business side effect.

Applications must not call internal history setters or enable external context writes to manufacture a more favorable guardrail baseline.

Framework adapters

Adapters require a decorated outer request or standalone_work_item.

LangChain:

from reva_ai.sdk.ai.adapters.langchain import protect_runnable, protect_tool

protected_chain = protect_runnable(
    chain,
    agent_name="summarizer-agent",
    prompt_key="message",
)
protected_tool = protect_tool(
    account_tool,
    tool_name="account-mcp/lookup-account",
    resource_type="MCP",
    prompt_key="account_id",
)

batch and abatch authorize every item before executing any item.

LangGraph:

from reva_ai.sdk.ai.adapters.langgraph import protect_node

@protect_node(agent_name="planner-agent", prompt_key="message")
async def planner_node(state: dict, config: dict) -> dict:
    return await plan(state)

AutoGen:

from reva_ai.sdk.ai.adapters.autogen import protect_agent

protected_assistant = protect_agent(
    assistant,
    agent_name="research-assistant-agent",
    prompt_key="content",
)

CrewAI factories must retain their return annotations and perform construction only. Keep network calls, writes, and tool execution out of a factory body.

Errors and fallback behavior

Import exceptions from reva_ai.sdk.ai.utils:

from reva_ai.sdk.ai.utils import (
    ConfigurationError,
    HistoryValidationError,
    PDPProtocolError,
    PDPRequestBodyTooLargeError,
    PDPRequestError,
    PDPUnavailableError,
    RevaAuthorizationError,
)
Exception Meaning Retry/fallback guidance
RevaAuthorizationError PDP returned 401/403 or an explicit deny Do not retry through another authorization path
PDPRequestError Non-denial 4xx; request/config contract is invalid Fix the request or deployment configuration
PDPUnavailableError Timeout, network failure, 408/425/429, or 5xx Protected work stays stopped; bounded infrastructure retry only for an idempotent authorization call
PDPProtocolError Successful HTTP response is malformed or missing token/decision Stop; SDK/PDP versions are incompatible or PDP is faulty
HistoryValidationError Security history is malformed or exceeds the shared history limits Safe HTTP 400; fix the input; do not truncate history
PDPRequestBodyTooLargeError Compact SDK-to-PDP JSON exceeds 1,048,576 bytes Safe HTTP 413 before network access; reduce the payload
ConfigurationError Required local SDK configuration is missing Fix startup configuration

FastAPI's default response detail is safe-by-default: it omits raw PDP bodies, policy reasons, and internal error codes. The exception object deliberately retains reason, code, pdp_response, and (where applicable) upstream_status_code for trusted diagnostics. Do not serialize the exception object or those fields into customer responses or ordinary logs. After PDP allows and execution enters the customer handler, the decorator re-raises the application's exception unchanged so existing FastAPI exception handlers keep their normal semantics.

Fallback to an alternate endpoint is permitted only for PDPUnavailableError, when Reva has explicitly defined that endpoint as the same authorization authority. Never catch RevaAuthorizationError and ask a different path for a second opinion.

The SDK does not retry business side effects automatically. A lost downstream response is not evidence that the operation did not happen.

Intent history, promptIntent, and guardrail authority

These concepts are intentionally separate:

  • rtg/a2a.history is the sanitized recursive hop chain. The RTG evaluator selects the deepest prompt-bearing history node as originating intent and compares it with the current hop.
  • context.promptIntent is generated by PDP from the current prompt and the destination resource's registered skill catalog. It is a list of matched Skill entity references, not originating natural language. The SDK never sets it. An empty list can mean no usable skills/match or matcher failure; it does not prove that history was lost.
  • Exactly one guardrail endpoint is the synchronous decisionMaker. In the standard split IBAC template, /ibac/score is authoritative, while /ibac/intent and /ibac/drift are post-decision informational endpoints. Changing a rule to ENFORCE does not make an informational BLOCK response authoritative.

SDK 1.0.3 and the coordinated PDP reject history that exceeds 65,536 compact UTF-8 JSON bytes, depth 32, 32 logical events, 2,048 total JSON values, or 16,384 UTF-8 bytes in one string. A linked history node and each chatHistory item count once; conversation.messages and hops are unioned, with entries sharing the same numeric seq counted once. The rtg/a2a metadata block and promptless conversation/hops compatibility wrappers do not count independently. A canonical linked wrapper retained by the guardrail builder does count. Cycles, non-JSON values, and non-finite numbers also fail with a safe HTTP 400. Security history is never truncated to pass authorization.

The 1.0.3 RTG evaluator applies a server-owned 0.85 deny floor when explicit operation-family or polarity replacement is proven over the same business object. The regression case is “review MRN-1004 lab results” followed by “place Furosemide order for MRN-1004”; /score can no longer accept ALIGNED / 0.05 for that canonical input.

Optional final-turn recording

Conversation metadata alone does not send content. Recording is disabled by default and requires all of the following:

  • record_final_turn=True on a FastAPI route decorator;
  • conversation_key and conversation_key_location (header or body);
  • an explicit absolute HTTPS final_turn_url, or REVA_AI_FINAL_TURN_URL; and
  • customer approval for payload content, retention, residency, tenancy, and failure handling.

The endpoint must use exactly the same HTTPS scheme, host, and port as RTG_URL, with no embedded credentials, query, or fragment, because the POST carries RTG_AUTH_TOKEN. The insecure RTG development escape hatch does not apply. Recording runs only for an edge request without X-Transaction-Token; chained-agent responses are not recorded by this path.

@app.post("/chat")
@reva_ai_authorise(
    agent_name="support-agent",
    prompt_key="message",
    conversation_key="conversation_id",
    conversation_key_location="body",
    record_final_turn=True,
    final_turn_url="https://rtg.customer.example/v1/final-turn",
)
async def chat(body: InvokeBody, request: Request) -> dict:
    return {"answer": "..."}

The POST is background-dispatched and does not delay a non-streaming response. Delivery is best effort: it has a 10-second HTTP timeout, no retry/durable queue/shutdown drain, and a maximum of 100 concurrent recording tasks; excess turns are dropped. Responses are capped at 50,000 characters (the rolling tail for a stream). Recording failures never turn an allowed business response into an error. The recorder adds no dedicated prompt/response log, but optional TRACE logging elsewhere remains payload-capable. There is no derived /mock/... endpoint in 1.0.3. The deprecated mock_rtg_record_final_turn helper remains importable as a warning-emitting, non-transmitting no-op.

Security boundary and current limitations

  • Gateway verification is mandatory. The SDK can inspect access-token claims without locally verifying their signature. The API gateway must use an algorithm allowlist and trusted JWKS, validate token use/type, issuer, audience/client, exp, and nbf, strip forbidden caller metadata, and block direct workload/PDP bypass. A shared RTG_AUTH_TOKEN alone is not proof of the asserted workload identity.
  • Outbound headers require trust-domain review. Proxy construction starts from a broad inbound header snapshot. Until strict per-target allowlists are implemented, strip unrelated cookies, proxy authorization, CSRF values, and customer secrets at ingress; call only approved targets and assert the exact destination headers in tests. The SDK strips its RTG platform token and replaces session credentials, but that is not a general header allowlist.
  • Destination URL allowlisting is application-owned. Public proxy helpers do not enforce a complete HTTPS host allowlist or prevent DNS rebinding. Endpoints must be static trusted configuration, never user/model output; review environment-proxy behavior as well.
  • Some async proxy paths block for PDP. Agent/API/MCP async calls perform synchronous token enrichment before downstream async I/O. Load-test event loop latency and use bounded concurrency.
  • Logs are sensitive. The SDK eagerly creates a non-rotating file handler. INFO can include service/action/resource/tool names, paths, and URLs; explicit TRACE can additionally include redacted business bodies, prompts, history, arguments, and responses. Use WARNING in production where possible, protect the file, rotate/retain it deliberately, and do not treat heuristic redaction as removal of all customer data.
  • Framework boundaries are specific. Whole-app middleware skips GET/HEAD/OPTIONS. Protected MCP stdio is rejected; prefer static streamable HTTPS. Legacy SSE requires live same-origin/security testing, and WebSocket is unsupported. Slow MCP initialization can consume encrypted transaction-token TTL; PDP, not the SDK's local three-part-JWT check, is authoritative for the five-part JWE expiry.
  • Context does not authorize detached work. A detached task can inherit ContextVar identity, token, and history. Enqueue sanitized data and start a fresh authenticated standalone_work_item rather than launching detached protected business work from a request.
  • CrewAI factories execute before their PDP check. Keep construction pure: no network call, write, or tool side effect in a factory body. Keep required return annotations and test against the pinned framework version.
  • Guardrail outage behavior is server policy. Verify the effective synchronous decisionMaker and its failure mode for every ENFORCE template; SDK exception names do not prove that evaluator outage is fail closed.
  • The optional top-level ai name is not an invocation API. reva_ai.sdk.ai.ai may be None; use the documented decorators, proxies, and adapters.

No SDK can guarantee an error-free or universally secure deployment. Customer readiness requires the actual gateway, broker, policy store, PDP/RTG versions, framework versions, endpoints, logs, and data-handling controls to pass the acceptance tests below.

Acceptance checklist

Before customer handoff, prove in the deployed environment:

  1. Missing/invalid credentials deny before customer code runs.
  2. Cedar allow and deny policies work for exact registered IDs and schema keys. For direct /ai/evaluation, prove the gateway/service boundary prevents an untrusted caller from selecting subject, principal, or policyStoreId, and that the API credential is tenant-scoped. The endpoint does not perform that binding itself.
  3. Direct cedar_context and nested/indexed cedar_context_map values reach Issue Token, win over same-name business keys, cannot override server-owned identity/intent, remain absent from the downstream body, and reach Validate Token through the authenticated transaction-token claim; missing, null, protected, or colliding mapper configuration fails before PDP, and a forged downstream body value cannot replace token-bound context. Exact and over-limit checks pass at 512-byte context, 4,096-byte compact-token, and 6,144-byte complete-PDP-header boundaries; all three PDP body shapes pass at the exact 1,048,576-byte compact UTF-8 boundary and fail above it; canonical external-guardrail JSON passes at 262,144 bytes and fails above it before any HTTP attempt. Keep PDP PDP_AI_REQUEST_BODY_MAX_BYTES=1048576 so its configured streamed-body limit matches the SDK's fixed preflight.
  4. Agent → Agent and Agent → MCP chains preserve principal, current prompt, originating history, canonical resource ID, thread ID, and trace ID; exact 32-event history passes and 33 events, excessive depth/value/string/byte counts, cycles, and non-finite data fail without truncation.
  5. A PDP deny never triggers a fallback call; an unavailable failure is typed separately.
  6. Protected MCP is the default, credentials remain off tool arguments, and strict tool schemas accept the call.
  7. Worker ingress is authenticated and PDP-authorized before business work; consecutive items cannot see one another's token/history/context.
  8. The exact lab-review → medication-order regression denies at authoritative score, while approved retrieval/format/order substeps remain aligned.
  9. Logs, traces, safe HTTP error details, and final-turn behavior satisfy the customer's data-handling requirements; final-turn delivery uses the exact RTG HTTPS origin and tested overload/drop behavior.
  10. The tested wheel hash and all compatible platform versions are recorded.

License

Proprietary — © Reva AI, Inc. All rights reserved. Use requires a valid commercial agreement with Reva. See LICENSE or contact legal@reva.ai.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reva_ai_authz-1.0.3.tar.gz (153.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reva_ai_authz-1.0.3-py3-none-any.whl (148.4 kB view details)

Uploaded Python 3

File details

Details for the file reva_ai_authz-1.0.3.tar.gz.

File metadata

  • Download URL: reva_ai_authz-1.0.3.tar.gz
  • Upload date:
  • Size: 153.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.20

File hashes

Hashes for reva_ai_authz-1.0.3.tar.gz
Algorithm Hash digest
SHA256 b0d8831a9b237c43817881dd4a0a6b7b30abf9932e2f3cd4d5c7c2c0898fba65
MD5 84e0295ec7724dca1ad4a6d00e821874
BLAKE2b-256 45bc2e986b2e9aae8aa49c2cb406d853452dcbb1ae61f8f06de76e775013a8ef

See more details on using hashes here.

File details

Details for the file reva_ai_authz-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: reva_ai_authz-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 148.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.20

File hashes

Hashes for reva_ai_authz-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 3fdcbaa92e1091fc21b706a145db30287f7638e35d03056c49399f003d952825
MD5 b42f15e9b5e13f46648ab044894eddde
BLAKE2b-256 82a059f7432be8f41b5e4e018a43a95f2cde1812d4914842c334172c70bf6508

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page