Skip to content
Draft
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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,27 @@
- name: full test suite (79 fixtures)
run: pytest -v

nooa-reference:
name: NVIDIA NOOA reference (integration required, not skipped)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
# nooa declares requires-python >=3.12,<3.14.
python-version: '3.12'

- name: NVIDIA reference gate (authoritative; fails on any skip)
# One script owns the environment, the pins, and the counts, so CI and a
# local run cannot drift apart. It sets RATIFY_REQUIRE_NOOA=1 and
# RATIFY_REQUIRE_MCP=1, so a missing optional dependency is a failure
# rather than a skip: an ordinary pytest run on 3.11 reported
# "91 passed, 1 skipped" and exited 0 while the whole MCP module had not
# run.
run: ./scripts/nvidia-reference-check.sh

rust:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: Rust SDK conformance
runs-on: ubuntu-latest
defaults:
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ dist/
.env.*
!.env.example

# Python caches, demo suites are run from the repo root
__pycache__/
*.py[cod]
.pytest_cache/

# OpenShell profile artifacts are per-run evidence, not source
/openshell-profile-*.json

# Build artifacts at repo root
/ratify
/ratify-verifier
Expand Down
43 changes: 43 additions & 0 deletions demos/nvidia-nooa-delegated-authority/Dockerfile.nooa-sandbox
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# syntax=docker/dockerfile:1
# SPDX-License-Identifier: Apache-2.0
#
# A sandbox image for the unified NOOA path, and only for that.
#
# Why this exists at all. The unified path needs nooa==0.0.8 running *inside*
# the OpenShell-governed sandbox, and the sandbox has no egress once its policy
# is active, so the dependencies have to be present before the test starts.
# Delivering them with the released `openshell sandbox upload` was tried first
# and does not hold: a single 63-76 MB transfer failed with "error reading a
# body from connection ... broken pipe", and splitting it into 8 MB chunks still
# failed intermittently, across several runs. Baking the tree into an image
# removes every runtime transfer, which is the difference between a profile that
# usually passes and one that does.
#
# What this image is not. It is not a redistribution, not published to any
# registry, and not required by any other part of the reference. Every other
# group in the profile runs on the unmodified pinned community sandbox image.
#
# FROM is pinned by immutable digest, so the supervisor injection contract, the
# interpreter, and the filesystem layout are exactly the base the rest of the
# profile uses. /usr/bin/python3 in that base is CPython 3.12.3, which is inside
# nooa's declared >=3.12,<3.14 range; the default `python3` on PATH is 3.14 from
# a uv venv and cannot run nooa, which is why the profile always names the
# interpreter by absolute path.
ARG SANDBOX_BASE=ghcr.io/nvidia/openshell-community/sandboxes/base@sha256:aeef1c63f00e2913ea002ccb3aaf925f338b5c5d70e63576f0d95c16a138044e
FROM ${SANDBOX_BASE}

# The dependency tree, resolved by uv against this same base image's
# /usr/bin/python3 so the compiled wheels are ones that interpreter can load.
# Built by run-openshell-profile.sh into a staging directory; nothing is
# fetched here, so the build needs no network.
#
# Contains no keys, no proofs, no JWTs, no delegations, and no test vectors. It
# is third-party libraries and the Ratify Python SDK from this checkout, and
# nothing else.
#
# Owned by root and left at its copied modes, which is already read-only to the
# unprivileged `sandbox` user the policy runs as. An explicit `RUN chmod` was
# tried and fails: the base image's default user is not root, so the build step
# cannot alter root-owned files, and the chmod added nothing that ownership had
# not already established.
COPY --chown=root:root site /opt/ratify-nooa/site
224 changes: 224 additions & 0 deletions demos/nvidia-nooa-delegated-authority/README.md

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions demos/nvidia-nooa-delegated-authority/agent_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# SPDX-License-Identifier: Apache-2.0
"""The agent side: carry the proof, answer the challenge, report the verdict.

There is deliberately no authorization logic in this file. The client asks the
service what it intends to do, signs the challenge the service issues, and
returns whatever the service decided. It has no ability to allow anything, and
no knowledge of what the delegation permits.
"""

from __future__ import annotations

import base64
import json
import time
import urllib.request

from ratify_protocol import ProofBundle, sign_challenge
from ratify_protocol.wire import encode_proof_bundle


def post_json(url: str, payload: dict) -> dict:
"""POST JSON and decode the JSON response.

Proof bundles are serialized through the protocol's own canonical wire
codec rather than an ad-hoc encoding, so what crosses the boundary here is
the real interop format.
"""
body = json.dumps(payload, default=_encode).encode("utf-8")
request = urllib.request.Request(
url, data=body, headers={"Content-Type": "application/json"}, method="POST"
)
with urllib.request.urlopen(request) as response: # noqa: S310, loopback demo
out = json.loads(response.read())
for field in ("challenge", "session_context"):
if isinstance(out.get(field), str):
out[field] = base64.b64decode(out[field])
return out


def _encode(value):
if isinstance(value, ProofBundle):
return json.loads(encode_proof_bundle(value))
if isinstance(value, bytes):
return base64.b64encode(value).decode("ascii")
raise TypeError(f"not JSON-serializable: {type(value).__name__}")


class RefundClient:
"""Presents delegated authority to the refund service."""

def __init__(self, base_url, agent_id, agent_pub, agent_priv, delegations):
self.base_url = base_url.rstrip("/")
self.agent_id = agent_id
self.agent_pub = agent_pub
self.agent_priv = agent_priv
# Chain order is [leaf, ... root].
self.delegations = list(delegations)

def fetch_challenge(self, order_id: str, amount: float, currency: str = "USD") -> dict:
"""Phase 1, describe the intended action and receive a challenge
bound to the *receiver's* canonical reading of it."""
return post_json(
self.base_url + "/refunds/challenge",
{
"order_id": order_id,
"amount": amount,
"currency": currency,
"agent_id": self.agent_id,
},
)

def present(self, challenge: dict) -> dict:
"""Phase 2, prove possession of the agent key and submit the chain."""
at = int(time.time())
bundle = ProofBundle(
agent_id=self.agent_id,
agent_pub_key=self.agent_pub,
delegations=self.delegations,
challenge=challenge["challenge"],
challenge_at=at,
challenge_sig=sign_challenge(
challenge["challenge"], at, self.agent_priv, challenge["session_context"]
),
session_context=challenge["session_context"],
)
return post_json(
self.base_url + "/refunds",
{"challenge": challenge["challenge"], "bundle": bundle},
)

def request_refund(self, order_id: str, amount: float, currency: str = "USD") -> dict:
"""Both phases. Returns the service's decision verbatim."""
return self.present(self.fetch_challenge(order_id, amount, currency))
16 changes: 16 additions & 0 deletions demos/nvidia-nooa-delegated-authority/image-deps-check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pathlib
import sys

import mcp
import nooa
import ratify_protocol

# The module path matters as much as the version: it is how a run states whether
# the SDK came from the staged, locked dependency tree or from somewhere else.
print(
"IMAGE_DEPS_OK",
sys.version.split()[0],
nooa.__version__,
ratify_protocol.__version__,
pathlib.Path(ratify_protocol.__file__).resolve().parent,
)
Loading