Skip to content

feat(commerce): verify released UCP purchase accountability - #108

Merged
imran-siddique merged 1 commit into
agentrust-io:mainfrom
noah-ing:feat/released-ucp-accountability
Sep 8, 2026
Merged

feat(commerce): verify released UCP purchase accountability#108
imran-siddique merged 1 commit into
agentrust-io:mainfrom
noah-ing:feat/released-ucp-accountability

Conversation

@noah-ing

@noah-ing noah-ing commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #89 and the distinction from the illustrative example in #90: add a separate agentic-commerce-accountability/released_ucp verification harness. The existing example and its fixtures keep their behavior; its README links to this upgrade.

The example connects a merchant-authenticated checkout, a signed local spending grant, a platform-authenticated completion request, one local SQL effect, and an independently rerunnable evidence audit. It deliberately handles one fictional USD reservation and no real payment.

Acceptance evidence

  • Released schemas: checkout response and completion request validated first against offline JSON Schema bundles generated from UCP v2026-08-25, then against official ucp-sdk==0.5.0 models. The release commit, resolver version, hashes, license, and regeneration procedure are included. Original JSON is checked before SDK coercion.
  • Signed protocol artifacts: actual ES256 HTTP signatures using http-message-signatures==2.0.1, required component coverage, signed UCP-Agent declaration, and RFC 9530 raw-body digest checks. Response transport annotations are not mistaken for authenticated request identity.
  • Authority and evidence: a closed, strictly typed example-local grant and decision JWS use RFC 8785 and joserfc; released agentrust-trace==0.10.0 validates/signs/verifies the software policy record. The outer decision binds that record to the grant, checkout, request, response, policy, and configured key digests.
  • Negative cases: correctly signed overspend, merchant/platform/key/route substitutions, stale authenticated checkout state, broken artifact links, expiry, conflicting retry bytes, fresh-key grant replay, and untrusted audit keys. Authorization refusals are checked against actual private-handler entry, not only committed counters.
  • Concurrency/retry: identical retries return the same signed cached result; eight concurrent fresh idempotency keys for one grant produce one handler invocation and seven GRANT_SPENT refusals. Separate concurrent purchases retain their own invocation IDs. A post-handler failure is a transaction abort, never a denial claiming that no handler ran.

The README contains a short allow trace, trust-boundary diagram, test-name matrix, and exact source links. The CI job runs the same Docker smoke as documented, with no runtime network, root user, writable image layers, capabilities, secrets, or publication.

Validation

On commit db6d6f75daa3659a7e0fac5e49d01b41d8c10c62:

  • New host suite: 229 passed, no skips; 94.92% combined statement/branch coverage.
  • Existing commerce suite: 4 passed; regenerated fixtures unchanged.
  • Ruff checks and formatting, mypy (6 source files), Bandit, dependency audit, and exact-patch Gitleaks scan passed. Detect-secrets candidates were inspected: public source/artifact hashes, a synthetic idempotency value, and an intentionally rejected example URL—not credentials.
  • Source review findings were corrected and regression-tested, including timestamp rechecks, abort-versus-denial reporting, and strict single-public-key PEM input.
  • Clean-commit, no-cache Docker build and isolated smoke passed on linux/arm64: 229 passed, no skips; Ruff/formatting, mypy, Bandit and OFFLINE_SMOKE_PASSED. The image ran as UID/GID 10001:10001 with the documented network-none/read-only security flags. Image ID: sha256:378b760b77362091352cb952c8026472ca21ef3a4ea6e4e69b06a2d8bee4d34d.

Deliberate limits

This is cap-based local authorization, not AP2 exact-price consent: the platform signs the checkout route/body, while the merchant authenticates terms and compares them against its current state. The grant permits one supported purchase under its cap.

No merchant network, payment instrument, settlement, discovery/enrollment service, hardware attestation, runtime-integrity proof, or full UCP/AP2 conformance is claimed. All roles run in one process with distinct keys. TRACE is software-only; a trusted controller's signed observation is not independent proof of physical execution. SQLite can roll back the effect here because the effect is entirely local SQL; this does not provide exactly-once external payment execution. The short-lived harness does not implement a production 24-hour retry SLA or historical archive verification. The pinned base, hash-verified bootstrap and Python lock do not imply bit-reproducible image output.

This PR supplies reviewable acceptance evidence for #89 without treating a local example as a protocol certification or automatically declaring the broader issue closed.

@noah-ing
noah-ing requested review from a team and carloshvp as code owners September 7, 2026 23:00
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

🟡 Contributor Check: MEDIUM

Check Result
Profile MEDIUM
Credential LOW
Overall MEDIUM

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor check flagged MEDIUM risk label Sep 7, 2026

@carloshvp carloshvp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed commit db6d6f7. No blocking findings.

Validated the host smoke: 229 tests passed with 94.92% combined statement/branch coverage, plus Ruff, formatting, mypy, and Bandit. Additional checks passed for restart persistence, cross-checkout grant replay, signed-header removal, and grant expiry. The four existing commerce tests passed, and regenerated fixtures were unchanged.

The implementation checks signature coverage and raw-body digests, enforces authorization before handler invocation, and commits the local SQL effect and cached evidence together. The documentation appropriately distinguishes authenticated observations from independent execution proof and limits the payment and consent claims.

Docker was unavailable locally; the container smoke passed in CI.

Approved within the documented scope of this offline, local verification harness.

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Taking the four acceptance criteria from #89 in turn, because that is what this PR answers and each one is separately checkable.

Schema validation. Offline bundles generated from UCP v2026-08-25 with provenance.json, the upstream licence, and a regeneration script, validated before SDK coercion and then against ucp-sdk==0.5.0. Checking the original JSON first is the right order: validating only the SDK-parsed object would test the SDK's tolerance rather than the wire artifact.

Signed protocol artifacts. Real ES256 HTTP message signatures rather than a shaped placeholder, twenty tests in test_wire.py.

The four negative cases. Each exists by name. test_correctly_signed_overspend_is_policy_denial is the one that matters most, because a correctly signed request being denied on policy is the whole point of the example. Substitution is covered from three directions: test_authenticated_target_substitution_is_denied, test_request_target_substitutions_are_denied, and test_auditor_rejects_cross_bound_signed_artifact_substitution. Stale digest is test_stale_signed_quote_is_rejected_against_current_checkout alongside test_digest_checks_raw_bytes_not_equivalent_json. Replay is the idempotency cluster, and test_equivalent_json_with_same_idempotency_key_still_conflicts is the one I would have asked for: it pins the comparison to raw bytes rather than to parsed equivalence, which is the failure mode that lets a re-serialized replay through.

Trust boundaries. Stated in the README rather than implied, and stated against the example's own interest: four disposable keys in one local process, roles cryptographically separate and not deployed as separate security domains, one fictional USD reservation and no real payment.

Two notes, neither blocking. The ci.yml addition is a per-example job, which is this repository's convention, and it runs the demo under --network none --read-only --cap-drop ALL --security-opt no-new-privileges with a pinned action SHA, so the harness proves itself without network. And this is the only uv.lock in the repository; that deviation is earned here because the --no-cache image build is what CI actually executes, so the lock is load-bearing rather than incidental.

On CI: this PR reported two checks until today because eight workflow runs sat in action_required and nobody could see them. Released, it is green on all nineteen, including released-ucp-accountability, five smoke jobs and every other example. 102 tests. The existing example and its fixtures are untouched, which is what makes this additive rather than a replacement.

@imran-siddique
imran-siddique merged commit c2a9508 into agentrust-io:main Sep 8, 2026
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:MEDIUM Contributor check flagged MEDIUM risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants