Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,18 @@ If you're implementing a new language port, **start from the fixtures, not the s

## Where to go next

[`references/`](references/README.md) is the open-source home for executable
integrations with agent frameworks, transports, and runtimes. These profiles
prove interoperability and document the receiver-side trust boundary. They do
not replace **Ratify Verify**, the managed commercial surface for production
trust configuration, revocation, policy, audit, observability, availability,
and supported deployment adapters. Both use the same portable protocol and
verification semantics—there is no managed-only wire format.

| You want to… | Go to |
|---|---|
| **Run the demo and see the protocol work** | [`demos/README.md`](demos/README.md) |
| **See integrations with specific agent platforms** | [`references/README.md`](references/README.md) |
| **See the measured performance numbers** | [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) |
| **Understand the threat model** | [`docs/EXPLAINED.md`](docs/EXPLAINED.md) |
| **Read the normative spec** | [`SPEC.md`](SPEC.md) |
Expand Down Expand Up @@ -441,6 +450,8 @@ ratify-protocol/
├── demos/ End-to-end narrative demos: go/ python/ typescript/ rust/
├── references/ Platform-specific profiles, threat models, and executable gates
└── docs/
├── EXPLAINED.md Architecture + threat model + real-time patterns
├── AGENT_TO_AGENT.md A2A patterns (mutual auth, sub-delegation, receipts)
Expand Down
18 changes: 13 additions & 5 deletions docs/EXPLAINED.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,19 @@ This is the build-vs-buy boundary, in one diagram:

A bundle moves freely across all five SDKs. Where verifiers differ is in operational surface — latency, compliance posture, integration ergonomics — not in cryptography.

### Surface adapters (out of scope for this repository)

The integration code that turns a `ProofBundle` into a "Zoom auth gate," "Twilio SIP attestation," "AWS API Gateway authorizer," etc. — the **surface adapters** — lives in separate repositories (`ratify/zoom-sdk`, `ratify/voice-sdk`, …). Those are the home of proprietary "last-mile" integration code and are not addressed by this specification.

The protocol's contract stops at the `ProofBundle` wire format and the verifier algorithm. Anything above that — how a third-party platform's signaling layer is intercepted, how middleware is wired into a specific framework, how an incumbent product's auth model is mapped onto Ratify scopes — is integration work, not protocol work. Ratify Verify ships those adapters as commercial product; the specification does not prevent a third party from writing their own.
### Open references and managed surface adapters

Open, executable profiles under [`references/`](../references/README.md) show how
Ratify composes with specific agent frameworks, transports, and runtimes. They
are interoperability references: inspectable patterns, deterministic gates,
and evidence—not hosted services or production support commitments.

The protocol contract stops at the `ProofBundle` wire format and verifier
semantics. Production last-mile adapters—such as supported Zoom, Twilio, or API
gateway deployments—plus managed trust configuration, revocation, policy,
audit, observability, and availability belong to Ratify Verify's commercial
surface. Ratify Verify does not introduce a proprietary proof format, and the
specification does not prevent anyone from building or operating an adapter.

---

Expand Down
43 changes: 43 additions & 0 deletions references/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Ratify reference profiles

Reference profiles show how Ratify composes with a specific agent framework,
transport, runtime, or platform without changing the Ratify verifier contract.
They are larger and more platform-specific than the small examples in
[`demos/`](../demos/README.md).

These are open-source interoperability references, not hosted services or
production support commitments. They make the integration pattern inspectable,
portable, and reproducible. **Ratify Verify** is the separate managed commercial
surface for operating the same protocol at scale: managed trust configuration,
revocation, policy, audit retention, observability, availability, and supported
deployment adapters. The proof bytes and verifier semantics remain portable;
customers choose whether to operate them themselves or use the managed service.

Every accepted profile should contain:

- an explicit trust boundary and layer-separation statement;
- exact tested dependency versions;
- a deterministic path that does not depend on model judgment;
- adversarial allow/deny cases with protected-handler invocation evidence;
- a one-command published-package gate;
- limitations and non-goals;
- evidence generated from executed tests, not projected results; and
- a disclosure of endorsement, partnership, and standardization status.

## Registry

| Profile | Status | Ratify version | Platform version | Gate |
|---|---|---|---|---|
| [Google ADK](google-adk/README.md) | Independent draft; 31/31 gate green | `1.0.0a16` | `google-adk==2.6.3` | `./scripts/google-adk-reference-check.sh` |

## Lifecycle

Profiles begin as independent drafts on feature branches. Passing tests do not
make a profile official for the named platform. The registry entry must state
whether a platform reviewed, contributed to, or endorsed the work.

A profile remains in this repository while Ratify owns its maintenance and it
shares the protocol release cadence. It may move to a dedicated or jointly
maintained repository when it needs independent releases, external maintainers,
or partner-owned governance. A move should leave a compatibility pointer at the
old path so public evidence links do not silently break.
4 changes: 4 additions & 0 deletions references/google-adk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.venv/
.local/
__pycache__/
.pytest_cache/
216 changes: 216 additions & 0 deletions references/google-adk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Proof-carrying authority for Google ADK agents

**Status:** independent draft reference implementation. Not a Google
partnership, Google-approved integration, or Google reference architecture.

This reference answers one narrow question:

> When a Google ADK agent crosses an MCP, A2A, tool, or organizational
> boundary, can the system carrying the consequence independently verify who
> authorized the agent for that exact action and which bounds still apply?

The visible result is intentionally simple:

```text
ALLOW -> tool invoked once
DENY -> tool invocation count does not change
```

The model may request more authority. It cannot grant that authority to
itself.

## Run the published-package gate

From the Ratify repository root:

```bash
./scripts/google-adk-reference-check.sh
```

The script creates a disposable demo virtual environment, installs the exact
published packages in `requirements.txt`, refuses to run if Ratify resolves to
this repository's local Python SDK, runs the deterministic adversarial suite,
and then runs the three-case demonstration.

Tested pins:

- `google-adk==2.6.3`
- `mcp==1.29.0`
- `ratify-protocol==1.0.0a16`
- `pytest==8.4.1`

The deterministic path needs no API key, Google Cloud project, or paid
service. It drives the real ADK runner with a scripted model double, so ADK
still performs model-turn handling, tool selection, tool execution, and
function-response delivery. The authorization result cannot depend on model
judgment.
The recorded run is in
[`evidence/reference-evidence.md`](evidence/reference-evidence.md).

## What the reference implements

```text
Principal
signs root -> ADK commander
scope: custom:infra:provision + identity:delegate
|
v
ADK commander
signs commander -> infrastructure specialist
scope: custom:infra:provision
resource: gcp:projects/customer-project/regions/us-central1
extension constraint: max_nodes = 1
|
v
Google ADK native McpToolset
exposes only ordinary business arguments to the model
obtains an operation-bound challenge after tool selection
signs it with the specialist key and injects the proof
|
v
Independent Streamable HTTP MCP receiver
pins the accepted principal root out of band
reconstructs the operation and payload digest
binds the challenge to verifier, workspace, agent, session, invocation,
and operation hash
atomically consumes the single-use challenge
checks revocation, chain signatures, scope, resource, node count, and expiry
invokes the protected tool only after ALLOW
```

The `ai.identities.ratify.adk.max_nodes` extension is a draft Ratify integration
profile. It is deliberately not placed in a Google namespace and does not
claim that Google defines or endorses it.

## Layer separation

| Layer | Question answered | This reference does not claim |
|---|---|---|
| Google Agent Identity / IAM | Which deployed agent workload is calling, and which Google Cloud permissions does it have? | That the workload carries a principal-signed grant for this exact cross-boundary action |
| Google ADK | How does the agent reason and invoke a tool? | That MCP transport alone proves delegated authority |
| MCP / A2A / tool transport | How does the request cross the boundary? | That transport authentication proves the principal's bounded intent |
| Ratify | Who delegated authority, for which scope/resource/bounds, and is the presentation fresh and unrevoked? | That the receiver must execute |
| Receiver policy and tool | Is the verified request acceptable now, and should the action execute? | That verifier-supplied context becomes trustworthy without receiver validation |

## Security boundary

The receiver is the security boundary. It performs five actions the presenting
agent is not trusted to perform:

1. Pins the accepted principal root. A valid self-issued chain is denied.
2. Parses and validates the requested operation.
3. Constructs the operation and session bindings itself.
4. Issues and atomically consumes a single-use challenge.
5. Verifies the proof and local policy before the protected handler runs.

The ADK tool is presentation code. It injects the proof so the model never sees
private keys or proof bytes. Moving `verify_bundle` into an ADK callback inside
the agent process would be a useful fail-fast check, but not a security control:
a compromised agent could skip its own callback.

## Deterministic acceptance matrix

The suite encodes why the boundary matters:

| Case | Expected result | Protected tool |
|---|---|---|
| Correct agent, one node, `us-central1` | `allow` | Invoked once |
| Three nodes under a one-node grant | `constraint_denied` | Not invoked |
| `us-east1` under a `us-central1` grant | `constraint_denied` | Not invoked |
| Expired delegation | `expired` | Not invoked |
| Revoked leaf delegation | `revoked` | Not invoked |
| Replayed presentation | `invalid` / consumed challenge | Not invoked again |
| Operation changed after challenge issuance | `operation_binding_failed` | Not invoked |
| Different agent answers the challenge | `agent_binding_failed` | Not invoked |
| Valid chain under an untrusted root | `untrusted_root` | Not invoked |
| Non-integral, zero, negative, boolean, or excessive node count | Input rejected | Not invoked |

## Optional live Gemini path

The deterministic suite is authoritative. To let Gemini select and invoke the
same ADK tool interactively:

```bash
cd references/google-adk
source .venv/bin/activate
python bootstrap_live.py
python -m authority_reference.mcp_server \
--trust-config .local/receiver-trust.json --port 8765
```

In a second shell:

```bash
cd references/google-adk
source .venv/bin/activate
export GOOGLE_API_KEY=your_key
export RATIFY_PRESENTER_CONFIG=.local/presenter.json
export RATIFY_MCP_RECEIVER_URL=http://127.0.0.1:8765/mcp
adk run adk_app
```

Example prompt:

```text
Provision one n2-standard-4 node in us-central1. Use request id demo-1.
```

Then request three nodes or change the region and observe the receiver denial.
The app defaults to `gemini-3.6-flash`, Google's current stable Flash model as
of this evidence date. The optional live path demonstrates orchestration; it
adds no authorization guarantee beyond the deterministic receiver tests.

## Evidence tiers

| Tier | Executed here | Meaning |
|---|---|---|
| Receiver verification | Yes | Cryptographic and local-policy allow/deny matrix |
| ADK `FunctionTool` | Yes | Baseline in-process composition |
| Native ADK `McpToolset` | Yes | Ordinary schema; hidden proof injection; independent HTTP receiver |
| ADK runner loop | Yes | Model turn → MCP function call → gated receiver → function response |
| Gemini 3.6 Flash | Configuration-ready | Requires an operator API key; not part of recorded evidence |
| A2A / Agent Engine | Not yet | Proposed follow-on, not claimed as executed |

## Limitations

- The receiver and challenge store are in-memory inside one MCP server process.
- The protected provisioner is a counter, not Google Compute Engine. No cloud
resources are created.
- Trust-root distribution, durable revocation, shared challenge storage, key
custody, authorization receipts, rate limits, and production audit retention
are deployment responsibilities not solved by this draft.
- The logical `gcp:` resource name is an integration profile. Verification
proves authorization against the receiver-supplied logical resource; the
execution layer must still ensure the real cloud operation matches it.
- This reference composes with Agent Identity conceptually but does not deploy
to Vertex AI Agent Engine or exercise preview IAM Agent Identity APIs.
- Proof injection uses a small pinned-version `McpTool` adapter because ADK does
not expose operation-specific hidden MCP metadata as a stable public hook.
The adapter is isolated and tested, but should be mapped with the ADK team
before claiming forward compatibility.
- The internal challenge tool remains MCP-discoverable to authenticated clients
but is excluded from the model toolset. Authentication, bounded receiver
state, and receiver verification—not client-side hiding—are the controls.
- This is deliberately one concrete infrastructure-tool profile, not a claim
that arbitrary MCP schemas can be wrapped without an explicit authority map.
- Dependencies are version-pinned but not installed with artifact hashes; the
evidence records the requirements file hash, not a supply-chain attestation.
- The local profile uses one static transport token. Any holder can consume the
bounded 128-operation pending capacity until its five-minute TTL expires;
production deployments need per-workload authentication and rate limits.
- Protected execution is at-most-once, not exactly-once. If the response is
lost after execution, replay is denied; a production tool needs an
idempotency/result ledger before an operator retries the business action.
- The executed draft uses the real ADK runner and native `McpToolset` across an
independently started Streamable HTTP MCP receiver with receiver-owned trust
configuration. It does not yet execute A2A, TLS workload authentication,
Agent Engine, or Agent Identity deployment.

## Sources

- Google Agent Identity: <https://docs.cloud.google.com/iam/docs/auth-agent-own-identity>
- Google ADK: <https://github.com/google/adk-python>
- ADK MCP tools: <https://google.github.io/adk-docs/tools-custom/mcp-tools/>
- Gemini API release notes: <https://ai.google.dev/gemini-api/docs/changelog>
- Ratify Protocol: <https://github.com/identities-ai/ratify-protocol>
- Agent Relay integration note: <https://ratifyprotocol.com/writing/agent-relay-phase1-technical-note>
3 changes: 3 additions & 0 deletions references/google-adk/adk_app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .agent import root_agent

__all__ = ["root_agent"]
25 changes: 25 additions & 0 deletions references/google-adk/adk_app/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Optional live Gemini + native MCP entry point for ``adk run adk_app``."""

import os

from google.adk.agents import LlmAgent

from authority_reference import build_mcp_toolset
from authority_reference.deployment_config import load_presenter, load_transport_token

authority = load_presenter(os.environ["RATIFY_PRESENTER_CONFIG"])
transport_token = load_transport_token(os.environ["RATIFY_PRESENTER_CONFIG"])
root_agent = LlmAgent(
name="ratify_mcp_infrastructure_specialist",
description="Provisions cloud nodes through an authority-gated MCP receiver.",
model="gemini-3.6-flash",
instruction=(
"Use provision_cloud_node for infrastructure changes. Report receiver "
"denials exactly; never claim an action succeeded when decision is deny."
),
tools=[build_mcp_toolset(
authority,
receiver_url=os.environ["RATIFY_MCP_RECEIVER_URL"],
transport_token=transport_token,
)],
)
17 changes: 17 additions & 0 deletions references/google-adk/authority_reference/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Independent Google ADK delegated-authority reference."""

from .adk_agent import build_adk_agent, build_provision_tool
from .adk_mcp import RatifyMcpToolset, build_mcp_toolset
from .authority import AuthorityFixture, issue_authority
from .receiver import InfrastructureReceiver, OperationRequest

__all__ = [
"AuthorityFixture",
"InfrastructureReceiver",
"OperationRequest",
"build_adk_agent",
"build_mcp_toolset",
"build_provision_tool",
"issue_authority",
"RatifyMcpToolset",
]
Loading