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.5
Prior compatible SDK release 1.0.4
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. Until the reviewed 1.0.5 wheel and sdist are both published and public-index verification has passed, remain on the verified baseline:

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

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

To test the 1.0.5 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.5-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.5"'

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

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

Python 3.10–3.13 are supported by the 1.0.5 CI matrix. Framework packages such as LangChain, LangGraph, CrewAI, AutoGen, and MCP are application dependencies; this distribution does not install them. A release-candidate smoke was run against the official Python MCP package mcp==1.27.1, including a real FastMCP Streamable HTTP tools/call through the protected decorator. Optional MCP packages are not in the base CI jobs, so applications must pin and run the same live acceptance test for their exact framework version; this is not a claim of compatibility with every MCP/FastMCP distribution or with MCP 2.x.

On Python 3.14, an unpinned install may silently select an older compatible release because this package intentionally declares Python <3.14. Use Python 3.10–3.13 and verify the installed version rather than accepting a resolver downgrade. Before public 1.0.5 verification, production remains pinned to 1.0.4 and acceptance uses the reviewed candidate artifact; after verification, pin reva-ai-authz==1.0.5.

Migrating from 1.0.4

  1. Deploy the coordinated PDP MCP compatibility fix before changing the SDK. The SDK continues to send transport type MCP; PDP resolves the canonical <mcpServerId>/<toolName> resource to Cedar Tool and verifies the server URL matches that resource prefix.
  2. Pass the canonical resource_id on every protected MCP client call and on the protected MCP server decorator. A protected client may upgrade the legacy server-only value when the raw tool is known, but it never emits a bare-tool resource. A receiver requires the exact canonical value; the old tool_name="<server>/<tool>" spelling remains the only receiver fallback.
  3. Migrate any MCP-native authentication previously passed through proxy.mcp(headers=...). Although 1.0.4 merged those values into transport, 1.0.5 intentionally isolates headers=... to Reva/PDP. Put native values in connection_headers=... (or the selected connection's headers map).
  4. Treat the connection that supplies the selected URL as one indivisible target record. If an inferred tool connection has a URL, a separate mcp_client is ignored entirely; if only mcp_client has the URL, fields from an incomplete tool connection are ignored. Put intentional per-call native header overrides in connection_headers=....
  5. Protected remote MCP requires HTTPS. Local loopback HTTP requires the explicit allow_insecure_loopback=True development-only option.
  6. Use proxy.mcp for Agent-to-protected-MCP hops. Framework protect_tool wrappers are in-process PDP prechecks and do not mint/deliver child tokens.
  7. Create/verify Cedar Tool entities, invokeTool policies, and topology for AutoGen function tools and LangChain protect_tool(resource_type="MCP") before upgrading. They now correctly authorize as Tool/invokeTool; an old Agent/invokeTool or transport-MCP policy can otherwise change from allow to deny.
  8. Test protocol negotiation. Omitted protocol_version now selects 2025-06-18 for the default Streamable HTTP transport and 2024-11-05 for explicit legacy SSE.

Migrating from 1.0.3 to 1.0.4

  1. Deploy the coordinated 1.0.4 PDP with transaction-token issuance kept at 3 seconds and validation raised to 60 seconds. After every old PDP replica has drained, raise issuance to 60 seconds as a configuration-only rollout; only then change the customer wheel.
  2. Keep the inbound parent transaction token immutable across sibling tool calls. Each authorized outbound hop receives its own audience-bound child; never promote a child token into the identity used for a different sibling.
  3. For SigV4/AgentCore, gRPC, queues, and other transports not driven by proxy.agent, use the transport-neutral preparation API documented below. Carry its body and transaction token together over an authenticated transport, then import the managed block at the receiving boundary before calling PDP.
  4. Treat HTTP 400/422 as request defects. SDK exceptions now retain the PDP's safe nested context.reason; they remain distinct from policy denial.
  5. Direct clients may keep the bounded legacy correlation-only context.environment record. A scalar environment remains a policy attribute; arbitrary records continue to fail validation.

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="0"

# 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=1 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.

For 1.0.4 compatibility, direct evaluation also recognizes the legacy context.environment correlation-metadata object used by older reference payloads. It may contain only bounded string values for requestId, time, traceparent, sourceIp, and gateway; PDP retains it for sanitized audit correlation but does not expose it to Cedar/AVP. A scalar value such as "PROD" remains a normal managed policy attribute. Unknown, nested, credential-shaped, or oversized environment objects return HTTP 400, not a policy denial. The HTTP traceparent header remains the authoritative tracing carrier. A legacy context.onBehalfOf entity is accepted as a compatibility fallback, while an explicit top-level principal takes precedence.

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. Supply the exact registered MCP Tool ID. The public wire contract remains resource.type=MCP, endpointType=MCP, action.name=invokeTool, resource.id=<mcpServerId>/<toolName>, and endpoint_url=<registered MCP server URL>. MCP is the transport/resolution hint; PDP evaluates the resolved Cedar Tool:

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",
        # Explicit MCP-native auth belongs here. headers= remains the legacy
        # Reva/PDP credential channel and never reaches initialize/initialized.
        connection_headers={"Authorization": "Bearer <mcp-native-token>"},
        transport="streamable_http",
        protocol_version="2025-06-18",
        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=..., resource_id=...) validates the token.

Initialization, notifications/initialized, and SSE discovery finish before the short-lived child token is minted. They never receive the inherited Reva parent credential; the child token is attached only to tools/call. An explicit connection-native Authorization value may remain on the MCP transport and coexist with the child header, but it never replaces the inbound Reva identity sent to PDP.

Header origin is explicit and compatibility-preserving:

  • Exactly one connection record owns the selected URL and all target-bound headers, transport, command, and environment fields. An inferred tool connection with a URL is authoritative and excludes mcp_client; otherwise the selected mcp_client record is authoritative and excludes incomplete tool-connection fields. This isolation applies even if two records happen to contain the same URL. Explicit public arguments are applied separately.
  • In 1.0.5, headers=... is exclusively Reva/PDP input. The 1.0.4 implementation also merged explicit values into MCP transport; migrate any native authentication that depended on that behavior. Inherited and explicit Reva access/transaction credentials, X-API-Token, continuation proof, and pair IDs never reach MCP lifecycle requests.
  • A selected connection's headers map and the additive connection_headers=... argument are MCP-native. Their Authorization value may be an OAuth JWT and is kept on lifecycle and tools/call; Reva/PDP-only transaction (X-Transaction-Token and Txn-Token), API, policy-store, principal-claim, AuthZEN subject, continuation, binding, and pair header names are stripped from these layers too. connection_headers has final, case-insensitive precedence among native layers. Generic domain/username metadata and W3C trace headers remain transport-safe.

Protocol negotiation is fail closed. The supported handshake revisions are 2024-11-05, 2025-03-26, 2025-06-18, and 2025-11-25. Streamable HTTP defaults to 2025-06-18; explicit legacy transport="sse" defaults to 2024-11-05. A successful initialize must return a nonblank supported result.protocolVersion. For a negotiated revision of 2025-06-18 or later, the SDK sends that exact MCP-Protocol-Version on notifications/initialized and tools/call. Unknown/future revisions, including the new stateless/dispatch semantics identified as 2026-07-28, are unsupported in 1.0.5 and fail before a child token is minted.

mcp_session_id is an advanced caller-managed reuse option. Supply the exact protocol_version originally negotiated for that session; on HTTP 404 the SDK discards the stale ID and performs a fresh initialize before reminting. A new session ID created by an ordinary one-shot proxy.mcp call is used for that call only, is not returned as a reusable handle, and 1.0.5 does not send a session DELETE. Use servers with bounded idle-session cleanup or manage an existing session explicitly and test its lifecycle.

Mcp-Session-Id and MCP-Protocol-Version are SDK-owned. Values injected through headers, connection configuration, or connection_headers are stripped; only the validated option/server result above can produce them. The registered MCP URL is preserved exactly on the PDP and managed downstream wires, including HTTPS private-address URLs and query values ending in /.

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. The explicit legacy SSE mode keeps the GET response stream open, resolves the standard relative endpoint event against the registered GET URL, POSTs requests only to that same origin, and reads correlated responses from the persistent stream. Loopback HTTP is available only with the explicit local-development option allow_insecure_loopback=True. An intentionally unprotected local tool requires the visible opt-out endpoint_protected=False.

When a MultiServerMCPClient has more than one HTTP server, bind an actual tool object/connection or use a canonical resource whose server prefix matches the named connection. The SDK does not select the first server implicitly.

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",
    resource_id="healthsphere-mcp_lg/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": []}

tool_name is the raw executable JSON-RPC name. resource_id is trusted local configuration and binds the receiving server/tool to the canonical policy identity and is required for protected execution. The legacy canonical tool_name="<server>/<tool>" spelling remains accepted, but a bare tool_name can no longer trust a caller-supplied managed resource ID. The decorator rejects a different raw params.name, managed resource ID, type, endpoint type, or action before PDP. MCP tool execution is locked to exactly invokeTool; listTools applies to an MCPServer, never a Tool. 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.

Transport-neutral Agent handoffs

Use prepare_agent_hop when the SDK must authorize an Agent call but the application owns the transport (for example Bedrock AgentCore/SigV4, gRPC, or a short-delay queue). It performs PDP enrichment but does not contact the target:

from reva_ai.sdk.ai import prepare_agent_hop_async


prepared = await prepare_agent_hop_async(
    endpoint_uri="arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/claims",
    resource_id="claims-agent",
    payload={"message": "Review claim 42"},
    prompt_key="message",
)

# Adapt these two artifacts to the physical transport as one unit.
await broker.publish(
    body=prepared.payload,
    headers=prepared.headers(),
    content_type=prepared.content_type,
)

endpoint_uri is an authorization locator, not an arbitrary delivery address. Register that exact AgentCore ARN (or canonical gRPC/queue locator) on the same policy-store Agent entity named by resource_id; PDP cross-checks both before issuing the child token. A physical destination that is not registered to that entity fails authorization.

prepared.payload is the exact compact JSON byte sequence covered by PDP's A2A binding and contains the SDK-managed rtg/a2a envelope. prepared.carrier is read-only and contains only X-Transaction-Token, X-Reva-A2A-Binding-Version: 1, and available W3C trace fields. It never puts the transaction token into model-visible payload data. The preparation fails closed if PDP does not return a2aBindingVersion: 1; an older PDP cannot silently produce an unbound handoff.

After a delegated body passes bound /agent/evaluation, PDP returns exactly one short-lived X-Reva-A2A-Continuation-Token response header. The SDK keeps that proof only in private request context and sends it only to PDP on every child /token/enrich request (Agent, MCP, API, or tool). It never appears in prepared.payload, the destination carrier, logs, or repr. The proof is bound to the verified parent and may support sibling fan-out only until that parent expires. A raw transaction delivery or legacy imported inbound_rtg_a2a cannot prepare a bound child without this proof.

The binding covers every business field and managed envelope field except the exact PDP-only rtg/a2a.context member. That context is deliberately absent from the returned transport payload and is separately integrity-protected by the transaction token's encrypted Cedar-context claim; PDP restores it during validation. A protected receiver rejects any transported copy of that private field before contacting PDP. No other business or managed field is excluded from binding.

The optional headers argument accepts only traceparent, tracestate, and baggage. Session credentials, principal selectors, tenant/audit metadata, and managed history always come from the active trusted SDK context; callers cannot replace them through this API.

Do not parse, rebuild, or independently retry one artifact. For AgentCore, allowlist the returned custom headers and inject them into the same SigV4-signed InvokeAgentRuntime request whose payload is prepared.payload. For gRPC, put the carrier in per-call metadata and the exact bytes in a reserved request field. For a queue, publish body and message attributes atomically and exclude both credentials and managed history from logs.

Transaction tokens are short-lived online credentials. A durable queue, delayed workflow, or redelivery window that can exceed token expiry needs a separate PDP-managed asynchronous delegation grant; increasing the global transaction-token TTL is not a safe substitute.

Existing proxy integrations remain wire-compatible during the coordinated rollout. A legacy token that has no encrypted a2a_bnd claim is not bound to its prompt or history, however, and must never be described as authenticating those body fields. Only the prepared handoff contract above requires PDP's version-1 binding capability and fails closed when it is absent.

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 bound delegated message must use the protected receiver boundary:

from reva_ai.sdk.ai.adapters import (
    protected_work_item_async,
)


async def process_delegated_message(message) -> None:
    async with protected_work_item_async(
        agent_name="batch-supervisor-agent",
        carrier=message.headers,
        payload=message.body_bytes,
    ) as business_payload:
        await process_delegated_payload(business_payload)

protected_work_item_async requires the transaction token, managed envelope, and X-Reva-A2A-Binding-Version: 1 together. It submits the exact received bytes to /agent/evaluation, requires PDP to confirm binding version 1, yields only after allow plus valid continuation proof, and clears SDK context on every exit. It does not authenticate broker provenance; the application must still enforce broker IAM/TLS and atomic message delivery.

The lower-level standalone_work_item(..., inbound_rtg_a2a=...) form remains only for rolling compatibility with legacy unbound traffic. It isolates SDK state but does not bind or authenticate prompt/history and must not be used as the protected receiver contract for new integrations.

A receiver holding a bound parent must use prepare_agent_hop again for every subsequent delegated Agent hop. PDP permits a continuation-proven parent to use rolling-compatible legacy non-Agent paths, but that child is intentionally unbound and cannot later be promoted to version 1. An unbound next-Agent enrichment is rejected, so the ordinary proxy.agent/FastAPI decorator path must not be used from a protected chain.

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(
    local_account_function,
    tool_name="lookup-account",
    resource_type="Tool",
    prompt_key="account_id",
)

batch and abatch authorize every item before executing any item. protect_tool is a logical in-process evaluation wrapper. It does not perform the remote MCP lifecycle or mint/deliver an audience-bound child token. Use proxy.mcp for an Agent calling a protected MCP server. resource_type="MCP" remains available as a case-normalized legacy selector, but the logical PDP request now emits Cedar-compatible Tool/invokeTool; it must not be presented as the protected remote transport.

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, protect_function_tool

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

protected_local_tool = protect_function_tool(
    lookup_customer,
    tool_name="lookup-customer",
)

AutoGen function tools evaluate as Cedar Tool/invokeTool, not Agent/invokeTool. Remote MCP calls still go through proxy.mcp.

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.

Safe guardrails diagnostics

Successful inbound evaluation, adapter/inner-call evaluation, and outbound token enrichment may include a PDP-owned, sanitized guardrails aggregate. Proxy return types are unchanged; inspect the latest aggregate in the current async task:

from reva_ai.sdk.ai.utils import RequestContext

diagnostic = RequestContext.get_guardrails_aggregate()

The optional dictionary contains only status, outcome, health, reason, and optional score. The first three fields use closed enums; score is a finite number from 0 through 100; reason must be one of the SDK/PDP-owned bounded reason strings. Unknown keys, unknown enum/reason values, evaluator text, malformed scores, and oversized reasons cause the whole aggregate to be ignored. Raw prompts, policy or guardrail code, evaluator errors, IDs, URLs, and decision chains are never exposed through this getter.

This value is diagnostic only and is never authorization input. It is cleared before every PDP attempt and remains None after an older-PDP response, field absence, malformed signal, deny, timeout, protocol error, or local preflight failure. Context is task-local: sequential calls replace the value, while an async child receives a snapshot and cannot mutate its parent or siblings. An explicitly unprotected direct MCP invocation makes no PDP attempt and does not replace the latest protected-call diagnostic. Inside a protected_work_item scope the getter reflects that inbound allow; leaving the scope clears the request context, so the value does not leak into later work.

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. SDK 1.0.5 may transparently annotate a hop with the actor already known from its authenticated request context (for example the access-token subject or inbound managed Agent). It never accepts or emits a caller-supplied authorization role; PDP remains authoritative for identity provenance.
  • 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 installs only a NullHandler by default and creates no file unless REVA_AI_LOG_FILE is explicitly configured. A configured SDK file handler is non-rotating. 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 and rotate any configured file, 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 uses one persistent GET plus a same-origin relative/absolute POST route and still requires live server testing. WebSocket and MCP 2026-07-28 stateless dispatch are unsupported. The SDK completes MCP initialization before minting the child transaction token, so initialization does not consume its TTL. PDP, not the SDK's local three-part-JWT check, remains authoritative for the five-part JWE expiry during the subsequent tool call.
  • 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.5.tar.gz (193.1 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.5-py3-none-any.whl (179.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for reva_ai_authz-1.0.5.tar.gz
Algorithm Hash digest
SHA256 1fc3b91ad651b5b93200bd34246ba571ce27ac8878bcb7cf17e3c727b8460c00
MD5 b48050bda56290d7a2d8e40176f80a2a
BLAKE2b-256 2d69dafb350b867ccf62acfa362f7ec3dd77291ee4ae93f2c0cb7e6e85227ae3

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for reva_ai_authz-1.0.5-py3-none-any.whl
Algorithm Hash digest
SHA256 371052fff1c0d982db161e5fa003f2b6394c8e6f2aee9789b52b0fa3c4c948ea
MD5 9131aeb53f564ae3b9124db66c5c363a
BLAKE2b-256 1837b3a08be74c75dae03a94013ee3b67aa7c90906f52b46c18cc7f4763370ed

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.6

2 files

This release

1.0.5 This release

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page