vouch-protocol 2.0.0__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. {vouch_protocol-2.0.0/vouch_protocol.egg-info → vouch_protocol-2.0.2}/PKG-INFO +11 -5
  2. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/README.md +5 -3
  3. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/pyproject.toml +4 -2
  4. vouch_protocol-2.0.2/tests/test_caveats.py +169 -0
  5. vouch_protocol-2.0.2/tests/test_deliberation.py +179 -0
  6. vouch_protocol-2.0.2/tests/test_provenance.py +169 -0
  7. vouch_protocol-2.0.2/tests/test_reasoning.py +279 -0
  8. vouch_protocol-2.0.2/tests/test_robot_access.py +181 -0
  9. vouch_protocol-2.0.2/tests/test_robot_consent.py +200 -0
  10. vouch_protocol-2.0.2/tests/test_robot_fusion.py +121 -0
  11. vouch_protocol-2.0.2/tests/test_robot_wear.py +124 -0
  12. vouch_protocol-2.0.2/tests/test_transparency.py +224 -0
  13. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/__init__.py +166 -1
  14. vouch_protocol-2.0.2/vouch/caveats.py +305 -0
  15. vouch_protocol-2.0.2/vouch/deliberation.py +457 -0
  16. vouch_protocol-2.0.2/vouch/integrations/a2a.py +81 -0
  17. vouch_protocol-2.0.2/vouch/integrations/goose.py +132 -0
  18. vouch_protocol-2.0.2/vouch/integrations/langgraph.py +87 -0
  19. vouch_protocol-2.0.2/vouch/integrations/mlflow.py +101 -0
  20. vouch_protocol-2.0.2/vouch/integrations/safetensors.py +126 -0
  21. vouch_protocol-2.0.2/vouch/provenance.py +291 -0
  22. vouch_protocol-2.0.2/vouch/reasoning.py +502 -0
  23. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/__init__.py +66 -0
  24. vouch_protocol-2.0.2/vouch/robotics/access.py +268 -0
  25. vouch_protocol-2.0.2/vouch/robotics/consent.py +309 -0
  26. vouch_protocol-2.0.2/vouch/robotics/fusion.py +200 -0
  27. vouch_protocol-2.0.2/vouch/robotics/wear.py +191 -0
  28. vouch_protocol-2.0.2/vouch/transparency.py +432 -0
  29. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2/vouch_protocol.egg-info}/PKG-INFO +11 -5
  30. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch_protocol.egg-info/SOURCES.txt +23 -0
  31. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch_protocol.egg-info/requires.txt +7 -1
  32. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/LICENSE +0 -0
  33. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/MANIFEST.in +0 -0
  34. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/setup.cfg +0 -0
  35. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_accountability.py +0 -0
  36. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_attribution.py +0 -0
  37. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_audio_bridge.py +0 -0
  38. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_audit_trail.py +0 -0
  39. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_auditor.py +0 -0
  40. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_autosign.py +0 -0
  41. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_behavioral_attestation.py +0 -0
  42. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_bridge.py +0 -0
  43. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_budget.py +0 -0
  44. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_c2pa_formats.py +0 -0
  45. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_cache.py +0 -0
  46. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_canary.py +0 -0
  47. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_cli_init.py +0 -0
  48. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_conformance.py +0 -0
  49. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_credential_gate.py +0 -0
  50. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_data_integrity.py +0 -0
  51. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_dx_sugar.py +0 -0
  52. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_fastapi_credential_gate.py +0 -0
  53. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_fleet.py +0 -0
  54. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_git_workflow.py +0 -0
  55. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_grade.py +0 -0
  56. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_hasura_integration.py +0 -0
  57. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_heartbeat.py +0 -0
  58. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_hybrid.py +0 -0
  59. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_hybrid_interop.py +0 -0
  60. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_jcs.py +0 -0
  61. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_jcs_interop.py +0 -0
  62. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_liveness_conformance.py +0 -0
  63. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_media_badge.py +0 -0
  64. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_media_native.py +0 -0
  65. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_merkle.py +0 -0
  66. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_multikey.py +0 -0
  67. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_nonce.py +0 -0
  68. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_proof_of_integration.py +0 -0
  69. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_quorum.py +0 -0
  70. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_receipts.py +0 -0
  71. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_recovery.py +0 -0
  72. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_reputation.py +0 -0
  73. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_reputation_aggregate.py +0 -0
  74. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_reputation_disputes.py +0 -0
  75. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_reputation_ledger.py +0 -0
  76. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_reputation_policy.py +0 -0
  77. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_reputation_portability.py +0 -0
  78. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_revocation.py +0 -0
  79. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_conformance.py +0 -0
  80. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_custody.py +0 -0
  81. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_embodiment.py +0 -0
  82. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_handshake_blackbox_passport.py +0 -0
  83. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_identity.py +0 -0
  84. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_lease_quorum.py +0 -0
  85. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_lifecycle.py +0 -0
  86. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_liveness_revocation.py +0 -0
  87. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_perception.py +0 -0
  88. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_pq.py +0 -0
  89. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_provenance_capability.py +0 -0
  90. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_robot_safety_record.py +0 -0
  91. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_scan.py +0 -0
  92. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_secure_key_custody.py +0 -0
  93. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_security_hardening.py +0 -0
  94. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_shield_guard.py +0 -0
  95. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_signer_vc.py +0 -0
  96. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_status_list.py +0 -0
  97. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_threshold.py +0 -0
  98. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_tool_registry.py +0 -0
  99. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_transport.py +0 -0
  100. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_transport_http_rendezvous.py +0 -0
  101. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_transport_rendezvous.py +0 -0
  102. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_transport_udna_sdk.py +0 -0
  103. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_trifecta.py +0 -0
  104. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_trust_check.py +0 -0
  105. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_trust_entropy.py +0 -0
  106. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_validator_server.py +0 -0
  107. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_vc.py +0 -0
  108. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/tests/test_verifier_vc.py +0 -0
  109. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/accountability.py +0 -0
  110. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/agent.py +0 -0
  111. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/async_verifier.py +0 -0
  112. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/attribution.py +0 -0
  113. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/attribution_cli.py +0 -0
  114. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/audio.py +0 -0
  115. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/audit_trail.py +0 -0
  116. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/auditor.py +0 -0
  117. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/autosign.py +0 -0
  118. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/behavioral_attestation.py +0 -0
  119. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/bridge/__init__.py +0 -0
  120. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/bridge/audio_routes.py +0 -0
  121. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/bridge/config.py +0 -0
  122. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/bridge/server.py +0 -0
  123. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/budget.py +0 -0
  124. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/cache.py +0 -0
  125. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/canary.py +0 -0
  126. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/cli.py +0 -0
  127. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/config.py +0 -0
  128. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/conformance.py +0 -0
  129. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/credential.py +0 -0
  130. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/data_integrity.py +0 -0
  131. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/data_integrity_hybrid.py +0 -0
  132. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/did_web.py +0 -0
  133. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/fleet.py +0 -0
  134. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/gate.py +0 -0
  135. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/grade.py +0 -0
  136. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/heartbeat.py +0 -0
  137. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/_common.py +0 -0
  138. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/adk.py +0 -0
  139. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/amnesia.py +0 -0
  140. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/autogen/__init__.py +0 -0
  141. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/autogpt/__init__.py +0 -0
  142. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/autogpt/commands.py +0 -0
  143. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/claude-code/settings.hooks.json +0 -0
  144. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/crewai/__init__.py +0 -0
  145. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/crewai/tool.py +0 -0
  146. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/fastapi.py +0 -0
  147. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/google.py +0 -0
  148. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/hasura/__init__.py +0 -0
  149. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/hasura/webhook.py +0 -0
  150. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/langchain/__init__.py +0 -0
  151. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/langchain/tool.py +0 -0
  152. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/mcp/__init__.py +0 -0
  153. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/mcp/server.py +0 -0
  154. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/n8n.py +0 -0
  155. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/streamlit/__init__.py +0 -0
  156. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/streamlit/seal.py +0 -0
  157. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/integrations/vertex_ai/__init__.py +0 -0
  158. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/jcs.py +0 -0
  159. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/keys.py +0 -0
  160. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/keystore.py +0 -0
  161. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/kms.py +0 -0
  162. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/liveness_conformance.py +0 -0
  163. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/mcp_guard.py +0 -0
  164. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/media/__init__.py +0 -0
  165. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/media/badge.py +0 -0
  166. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/media/c2pa.py +0 -0
  167. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/media/native.py +0 -0
  168. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/merkle.py +0 -0
  169. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/metrics.py +0 -0
  170. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/multikey.py +0 -0
  171. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/nonce.py +0 -0
  172. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/onboard.py +0 -0
  173. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/proof_of_integration.py +0 -0
  174. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/quorum.py +0 -0
  175. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/ratelimit.py +0 -0
  176. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/receipts.py +0 -0
  177. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/recovery.py +0 -0
  178. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/registry.py +0 -0
  179. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/reputation.py +0 -0
  180. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/reputation_aggregate.py +0 -0
  181. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/reputation_disputes.py +0 -0
  182. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/reputation_ledger.py +0 -0
  183. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/reputation_policy.py +0 -0
  184. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/reputation_portability.py +0 -0
  185. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/revocation.py +0 -0
  186. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/_signing.py +0 -0
  187. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/blackbox.py +0 -0
  188. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/capability.py +0 -0
  189. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/conformance.py +0 -0
  190. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/custody.py +0 -0
  191. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/embodiment.py +0 -0
  192. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/handshake.py +0 -0
  193. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/identity.py +0 -0
  194. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/lease.py +0 -0
  195. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/lifecycle.py +0 -0
  196. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/liveness.py +0 -0
  197. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/passport.py +0 -0
  198. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/perception.py +0 -0
  199. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/physical_quorum.py +0 -0
  200. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/pq.py +0 -0
  201. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/provenance.py +0 -0
  202. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/revocation.py +0 -0
  203. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/robotics/safety_record.py +0 -0
  204. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/scan/__init__.py +0 -0
  205. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/scan/detector.py +0 -0
  206. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/scan/patterns.py +0 -0
  207. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/seal.py +0 -0
  208. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/shield/__init__.py +0 -0
  209. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/shield/demo.py +0 -0
  210. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/shield/flight_recorder.py +0 -0
  211. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/shield/permissions.py +0 -0
  212. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/shield/shield.py +0 -0
  213. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/shield/trust_registry.py +0 -0
  214. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/signer.py +0 -0
  215. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/ssrf.py +0 -0
  216. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/status_list.py +0 -0
  217. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/status_list_fetcher.py +0 -0
  218. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/threshold.py +0 -0
  219. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/tool_registry.py +0 -0
  220. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/__init__.py +0 -0
  221. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/base.py +0 -0
  222. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/did_key.py +0 -0
  223. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/envelope.py +0 -0
  224. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/http_rendezvous.py +0 -0
  225. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/http_transport.py +0 -0
  226. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/manager.py +0 -0
  227. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/rendezvous.py +0 -0
  228. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/transport/udna.py +0 -0
  229. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/trifecta.py +0 -0
  230. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/trust_check.py +0 -0
  231. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/trust_entropy.py +0 -0
  232. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/validator_server.py +0 -0
  233. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/vc.py +0 -0
  234. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/verification.py +0 -0
  235. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch/verifier.py +0 -0
  236. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch_protocol.egg-info/dependency_links.txt +0 -0
  237. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch_protocol.egg-info/entry_points.txt +0 -0
  238. {vouch_protocol-2.0.0 → vouch_protocol-2.0.2}/vouch_protocol.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vouch-protocol
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: The Identity & Reputation Standard for AI Agents
5
5
  Author-email: Vouch Protocol Contributors <hello@vouch-protocol.com>
6
6
  License: Apache License 2.0
@@ -229,6 +229,10 @@ Provides-Extra: langchain
229
229
  Requires-Dist: langchain>=0.1.0; extra == "langchain"
230
230
  Provides-Extra: crewai
231
231
  Requires-Dist: crewai>=0.1.0; extra == "crewai"
232
+ Provides-Extra: langgraph
233
+ Requires-Dist: langgraph>=0.1.0; extra == "langgraph"
234
+ Provides-Extra: goose
235
+ Requires-Dist: pyyaml>=6.0; extra == "goose"
232
236
  Provides-Extra: autogen
233
237
  Requires-Dist: pyautogen>=0.2.0; extra == "autogen"
234
238
  Provides-Extra: mcp
@@ -273,7 +277,7 @@ Requires-Dist: base58>=2.1.0; extra == "dev"
273
277
  Requires-Dist: qrcode>=7.4.0; extra == "dev"
274
278
  Requires-Dist: c2pa-python>=0.5.0; extra == "dev"
275
279
  Provides-Extra: all
276
- Requires-Dist: vouch-protocol[autogen,crewai,dev,enterprise,langchain,mcp,server,streamlit,tracing,vertex]; extra == "all"
280
+ Requires-Dist: vouch-protocol[autogen,crewai,dev,enterprise,goose,langchain,langgraph,mcp,server,streamlit,tracing,vertex]; extra == "all"
277
281
  Dynamic: license-file
278
282
 
279
283
  # Vouch Protocol™
@@ -282,9 +286,9 @@ Dynamic: license-file
282
286
  <img src="docs/assets/vouch-wordmark.png" alt="Vouch Protocol" width="400">
283
287
  </p>
284
288
 
285
- <sub><em>Vouch Protocol™ — common-law trademark.</em></sub>
289
+ <sub><em>Vouch Protocol™ is a common-law trademark.</em></sub>
286
290
 
287
- <p align="center">The Open Standard for Identity & Provenance of AI Agents</strong>
291
+ <p align="center"><strong>The Open Standard for Identity & Provenance of AI Agents</strong>
288
292
  </p>
289
293
 
290
294
  <p align="center">
@@ -377,8 +381,10 @@ Vouch is not one tool, it is a set of them. Here is the whole map.
377
381
  ### Framework integrations (new in v1.6.2)
378
382
  Standalone packages that drop Vouch into the agent framework you already use. Each one issues a verifiable credential for a tool call, with optional delegation back to a human principal.
379
383
  - **`vouch-langchain`** a LangChain tool that signs each tool call before it leaves the agent.
384
+ - **`vouch-langgraph`** signs LangGraph tool calls and graph nodes, so a signed trail runs across the whole graph.
380
385
  - **`vouch-crewai`** a CrewAI tool, with supervisor-to-worker delegation that can only narrow authority, never widen it.
381
386
  - **`vouch-a2a`** binds an A2A (Agent2Agent) Agent Card to a Vouch identity, so two agents can verify each other before they collaborate.
387
+ - **`vouch-goose`** registers the Vouch MCP server as an extension for Block's Goose agent, so a Goose session can sign and verify out of the box.
382
388
  - **`vouch-mlflow`** signs an MLflow model artifact at registration time, bound to a content digest so any change to the weights breaks the signature.
383
389
  - **`vouch-safetensors`** embeds a credential in a `.safetensors` header, complementary to OpenSSF Model Signing, so a model carries who produced it.
384
390
 
@@ -388,7 +394,7 @@ The standalone **`vouch-mcp`** package above ships alongside these in v1.6.2.
388
394
  Python, TypeScript, and Go are the full reference implementations. A Rust core with idiomatic Swift, JVM (Java and Kotlin), .NET, and C wrappers shares one codebase, so every language produces byte-identical output, verified against shared test vectors. A WebAssembly build is included for the browser and the edge. See the table further down for status per language.
389
395
 
390
396
  ### Robots and embodied agents
391
- A robot is an agent with a body, so identity and accountability matter even more once it can cause physical harm. The `vouch.robotics` module ships six capabilities on the same `eddsa-jcs-2022` credentials as the rest of Vouch: hardware-rooted identity (bound to a TPM or secure element, so it cannot be cloned to other hardware), model and config provenance (re-signable on every OTA update), physical capability scope (force, speed, a tighter cap near humans, allowed zones, and shift windows, checked before each actuation, with narrow-only delegation), a robot-to-robot trust handshake, an encrypted tamper-evident black box with a verifiable kill switch, and a scannable offline passport. All six are implemented in Python, TypeScript, Go, and the Rust core, which flows to the Swift, Kotlin/JVM, .NET, C/C++, and WebAssembly wrappers, so a robotics credential signed in one language verifies in every other. Eleven further capabilities round out the set: a living-trust heartbeat (a signed per-interval motion summary whose trust decays unless it is renewed in-envelope), two-level credential revocation (per-credential status lists and whole-DID kill), an accountable safety record (a tamper-evident incident ledger summarized into a portable signed record), signed perception provenance (each captured sensor frame's hash bound to the robot's key and hash-linked, so a robot can prove what its sensors saw and a substituted frame is detectable), an offline delegation lease (a short-lived, scope-bounded grant a disconnected robot verifies and acts on with no network call, nesting across vendors), a physical quorum (a cryptographic two-person rule requiring M of N attested approvers for a high-consequence action), lifecycle credentials (ownership transfer that chains into a verifiable chain of custody, key rotation that forms a key history, and a signed decommission that retires the robot), a regulatory conformance profile (a machine-checkable mapping from a robot's credentials to the clauses of ISO 10218 and 15066, the EU Machinery Regulation, the EU AI Act, and UL 3300, with a deterministic checker and a signed conformance attestation), post-quantum signing by default (robot credentials sign with the hybrid classical-plus-ML-DSA-44 cryptosuite, with backward-compatible verification, so a robot stays unforgeable across its decade-long life), and cross-embodiment identity continuity (an agent identity that moves between robot bodies, with an embodiment credential re-binding to each body's hardware root and a continuity chain that proves the same accountable agent persisted, plus a fork check), and a physical custody handoff chain (a signed record of who accepted custody of a task or object at each hop across human and robot actors, so an incident traces to the exact hop and an attested condition localizes damage to the holder responsible), implemented across the same languages and pinned by the shared interop vector. See [docs/robotics.md](docs/robotics.md) and the defensive disclosures PAD-064 through PAD-070 and PAD-076 through PAD-084.
397
+ A robot is an agent with a body, so identity and accountability matter even more once it can cause physical harm. The `vouch.robotics` module ships six capabilities on the same `eddsa-jcs-2022` credentials as the rest of Vouch: hardware-rooted identity (bound to a TPM or secure element, so it cannot be cloned to other hardware), model and config provenance (re-signable on every OTA update), physical capability scope (force, speed, a tighter cap near humans, allowed zones, and shift windows, checked before each actuation, with narrow-only delegation), a robot-to-robot trust handshake, an encrypted tamper-evident black box with a verifiable kill switch, and a scannable offline passport. All six are implemented in Python, TypeScript, Go, and the Rust core, which flows to the Swift, Kotlin/JVM, .NET, C/C++, and WebAssembly wrappers, so a robotics credential signed in one language verifies in every other. Fifteen further capabilities round out the set: a living-trust heartbeat (a signed per-interval motion summary whose trust decays unless it is renewed in-envelope), two-level credential revocation (per-credential status lists and whole-DID kill), an accountable safety record (a tamper-evident incident ledger summarized into a portable signed record), signed perception provenance (each captured sensor frame's hash bound to the robot's key and hash-linked, so a robot can prove what its sensors saw and a substituted frame is detectable), an offline delegation lease (a short-lived, scope-bounded grant a disconnected robot verifies and acts on with no network call, nesting across vendors), a physical quorum (a cryptographic two-person rule requiring M of N attested approvers for a high-consequence action), lifecycle credentials (ownership transfer that chains into a verifiable chain of custody, key rotation that forms a key history, and a signed decommission that retires the robot), a regulatory conformance profile (a machine-checkable mapping from a robot's credentials to the clauses of ISO 10218 and 15066, the EU Machinery Regulation, the EU AI Act, and UL 3300, with a deterministic checker and a signed conformance attestation), post-quantum signing by default (robot credentials sign with the hybrid classical-plus-ML-DSA-44 cryptosuite, with backward-compatible verification, so a robot stays unforgeable across its decade-long life), and cross-embodiment identity continuity (an agent identity that moves between robot bodies, with an embodiment credential re-binding to each body's hardware root and a continuity chain that proves the same accountable agent persisted, plus a fork check), a physical custody handoff chain (a signed record of who accepted custody of a task or object at each hop across human and robot actors, so an incident traces to the exact hop and an attested condition localizes damage to the holder responsible), bounded infrastructure access (an operator-signed grant naming a resource, its permitted operations, an optional zone, and a time window, which a robot pairs with a signed request the resource authorizes offline, so a robot can open a door or dock at a charger with a tamper-evident, attributable, shrink-only record), fused-sensor provenance (a signed attestation binding a robot's fused world model to the exact set of input frame hashes and the fusion method that produced it, so a manipulated fusion result or a dropped input is detectable and every fused input traces to a frame the robot recorded), wear and degradation attestation (a robot signs its own wear level bound to its identity and hash-linked over time, and a deterministic rule narrows its physical capability scope as it degrades, so a worn robot operates inside a tighter, verifiable envelope than the static limit it shipped with), and bystander-consent evidence (a robot binds the basis for capturing people, an explicit token, posted notice, a legitimate interest, or a redaction it applied, to the capture itself by its hash, holding only hashes and never anyone's identifying data, and a bystander can sign consent bound to one capture so it cannot be replayed to another), implemented across the same languages and pinned by the shared interop vector. See [docs/robotics.md](docs/robotics.md) and the defensive disclosures PAD-064 through PAD-070, PAD-076 through PAD-084, and PAD-087 through PAD-094.
392
398
 
393
399
  ### Inside your AI tools
394
400
  - **Claude Skill**, **OpenAI Custom GPT**, and **Gemini Gem** packages that teach your AI assistant how to add Vouch to your code, running on your own AI subscription.
@@ -4,9 +4,9 @@
4
4
  <img src="docs/assets/vouch-wordmark.png" alt="Vouch Protocol" width="400">
5
5
  </p>
6
6
 
7
- <sub><em>Vouch Protocol™ — common-law trademark.</em></sub>
7
+ <sub><em>Vouch Protocol™ is a common-law trademark.</em></sub>
8
8
 
9
- <p align="center">The Open Standard for Identity & Provenance of AI Agents</strong>
9
+ <p align="center"><strong>The Open Standard for Identity & Provenance of AI Agents</strong>
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -99,8 +99,10 @@ Vouch is not one tool, it is a set of them. Here is the whole map.
99
99
  ### Framework integrations (new in v1.6.2)
100
100
  Standalone packages that drop Vouch into the agent framework you already use. Each one issues a verifiable credential for a tool call, with optional delegation back to a human principal.
101
101
  - **`vouch-langchain`** a LangChain tool that signs each tool call before it leaves the agent.
102
+ - **`vouch-langgraph`** signs LangGraph tool calls and graph nodes, so a signed trail runs across the whole graph.
102
103
  - **`vouch-crewai`** a CrewAI tool, with supervisor-to-worker delegation that can only narrow authority, never widen it.
103
104
  - **`vouch-a2a`** binds an A2A (Agent2Agent) Agent Card to a Vouch identity, so two agents can verify each other before they collaborate.
105
+ - **`vouch-goose`** registers the Vouch MCP server as an extension for Block's Goose agent, so a Goose session can sign and verify out of the box.
104
106
  - **`vouch-mlflow`** signs an MLflow model artifact at registration time, bound to a content digest so any change to the weights breaks the signature.
105
107
  - **`vouch-safetensors`** embeds a credential in a `.safetensors` header, complementary to OpenSSF Model Signing, so a model carries who produced it.
106
108
 
@@ -110,7 +112,7 @@ The standalone **`vouch-mcp`** package above ships alongside these in v1.6.2.
110
112
  Python, TypeScript, and Go are the full reference implementations. A Rust core with idiomatic Swift, JVM (Java and Kotlin), .NET, and C wrappers shares one codebase, so every language produces byte-identical output, verified against shared test vectors. A WebAssembly build is included for the browser and the edge. See the table further down for status per language.
111
113
 
112
114
  ### Robots and embodied agents
113
- A robot is an agent with a body, so identity and accountability matter even more once it can cause physical harm. The `vouch.robotics` module ships six capabilities on the same `eddsa-jcs-2022` credentials as the rest of Vouch: hardware-rooted identity (bound to a TPM or secure element, so it cannot be cloned to other hardware), model and config provenance (re-signable on every OTA update), physical capability scope (force, speed, a tighter cap near humans, allowed zones, and shift windows, checked before each actuation, with narrow-only delegation), a robot-to-robot trust handshake, an encrypted tamper-evident black box with a verifiable kill switch, and a scannable offline passport. All six are implemented in Python, TypeScript, Go, and the Rust core, which flows to the Swift, Kotlin/JVM, .NET, C/C++, and WebAssembly wrappers, so a robotics credential signed in one language verifies in every other. Eleven further capabilities round out the set: a living-trust heartbeat (a signed per-interval motion summary whose trust decays unless it is renewed in-envelope), two-level credential revocation (per-credential status lists and whole-DID kill), an accountable safety record (a tamper-evident incident ledger summarized into a portable signed record), signed perception provenance (each captured sensor frame's hash bound to the robot's key and hash-linked, so a robot can prove what its sensors saw and a substituted frame is detectable), an offline delegation lease (a short-lived, scope-bounded grant a disconnected robot verifies and acts on with no network call, nesting across vendors), a physical quorum (a cryptographic two-person rule requiring M of N attested approvers for a high-consequence action), lifecycle credentials (ownership transfer that chains into a verifiable chain of custody, key rotation that forms a key history, and a signed decommission that retires the robot), a regulatory conformance profile (a machine-checkable mapping from a robot's credentials to the clauses of ISO 10218 and 15066, the EU Machinery Regulation, the EU AI Act, and UL 3300, with a deterministic checker and a signed conformance attestation), post-quantum signing by default (robot credentials sign with the hybrid classical-plus-ML-DSA-44 cryptosuite, with backward-compatible verification, so a robot stays unforgeable across its decade-long life), and cross-embodiment identity continuity (an agent identity that moves between robot bodies, with an embodiment credential re-binding to each body's hardware root and a continuity chain that proves the same accountable agent persisted, plus a fork check), and a physical custody handoff chain (a signed record of who accepted custody of a task or object at each hop across human and robot actors, so an incident traces to the exact hop and an attested condition localizes damage to the holder responsible), implemented across the same languages and pinned by the shared interop vector. See [docs/robotics.md](docs/robotics.md) and the defensive disclosures PAD-064 through PAD-070 and PAD-076 through PAD-084.
115
+ A robot is an agent with a body, so identity and accountability matter even more once it can cause physical harm. The `vouch.robotics` module ships six capabilities on the same `eddsa-jcs-2022` credentials as the rest of Vouch: hardware-rooted identity (bound to a TPM or secure element, so it cannot be cloned to other hardware), model and config provenance (re-signable on every OTA update), physical capability scope (force, speed, a tighter cap near humans, allowed zones, and shift windows, checked before each actuation, with narrow-only delegation), a robot-to-robot trust handshake, an encrypted tamper-evident black box with a verifiable kill switch, and a scannable offline passport. All six are implemented in Python, TypeScript, Go, and the Rust core, which flows to the Swift, Kotlin/JVM, .NET, C/C++, and WebAssembly wrappers, so a robotics credential signed in one language verifies in every other. Fifteen further capabilities round out the set: a living-trust heartbeat (a signed per-interval motion summary whose trust decays unless it is renewed in-envelope), two-level credential revocation (per-credential status lists and whole-DID kill), an accountable safety record (a tamper-evident incident ledger summarized into a portable signed record), signed perception provenance (each captured sensor frame's hash bound to the robot's key and hash-linked, so a robot can prove what its sensors saw and a substituted frame is detectable), an offline delegation lease (a short-lived, scope-bounded grant a disconnected robot verifies and acts on with no network call, nesting across vendors), a physical quorum (a cryptographic two-person rule requiring M of N attested approvers for a high-consequence action), lifecycle credentials (ownership transfer that chains into a verifiable chain of custody, key rotation that forms a key history, and a signed decommission that retires the robot), a regulatory conformance profile (a machine-checkable mapping from a robot's credentials to the clauses of ISO 10218 and 15066, the EU Machinery Regulation, the EU AI Act, and UL 3300, with a deterministic checker and a signed conformance attestation), post-quantum signing by default (robot credentials sign with the hybrid classical-plus-ML-DSA-44 cryptosuite, with backward-compatible verification, so a robot stays unforgeable across its decade-long life), and cross-embodiment identity continuity (an agent identity that moves between robot bodies, with an embodiment credential re-binding to each body's hardware root and a continuity chain that proves the same accountable agent persisted, plus a fork check), a physical custody handoff chain (a signed record of who accepted custody of a task or object at each hop across human and robot actors, so an incident traces to the exact hop and an attested condition localizes damage to the holder responsible), bounded infrastructure access (an operator-signed grant naming a resource, its permitted operations, an optional zone, and a time window, which a robot pairs with a signed request the resource authorizes offline, so a robot can open a door or dock at a charger with a tamper-evident, attributable, shrink-only record), fused-sensor provenance (a signed attestation binding a robot's fused world model to the exact set of input frame hashes and the fusion method that produced it, so a manipulated fusion result or a dropped input is detectable and every fused input traces to a frame the robot recorded), wear and degradation attestation (a robot signs its own wear level bound to its identity and hash-linked over time, and a deterministic rule narrows its physical capability scope as it degrades, so a worn robot operates inside a tighter, verifiable envelope than the static limit it shipped with), and bystander-consent evidence (a robot binds the basis for capturing people, an explicit token, posted notice, a legitimate interest, or a redaction it applied, to the capture itself by its hash, holding only hashes and never anyone's identifying data, and a bystander can sign consent bound to one capture so it cannot be replayed to another), implemented across the same languages and pinned by the shared interop vector. See [docs/robotics.md](docs/robotics.md) and the defensive disclosures PAD-064 through PAD-070, PAD-076 through PAD-084, and PAD-087 through PAD-094.
114
116
 
115
117
  ### Inside your AI tools
116
118
  - **Claude Skill**, **OpenAI Custom GPT**, and **Gemini Gem** packages that teach your AI assistant how to add Vouch to your code, running on your own AI subscription.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vouch-protocol"
7
- version = "2.0.0"
7
+ version = "2.0.2"
8
8
  description = "The Identity & Reputation Standard for AI Agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -43,6 +43,8 @@ dependencies = [
43
43
  # Framework integrations
44
44
  langchain = ["langchain>=0.1.0"]
45
45
  crewai = ["crewai>=0.1.0"]
46
+ langgraph = ["langgraph>=0.1.0"]
47
+ goose = ["pyyaml>=6.0"]
46
48
  autogen = ["pyautogen>=0.2.0"]
47
49
  mcp = ["mcp>=0.1.0"]
48
50
  vertex = ["google-cloud-aiplatform>=1.0.0"]
@@ -107,7 +109,7 @@ dev = [
107
109
 
108
110
  # All optional dependencies
109
111
  all = [
110
- "vouch-protocol[langchain,crewai,autogen,mcp,vertex,streamlit,server,enterprise,tracing,dev]"
112
+ "vouch-protocol[langchain,crewai,langgraph,goose,autogen,mcp,vertex,streamlit,server,enterprise,tracing,dev]"
111
113
  ]
112
114
 
113
115
  [project.urls]
@@ -0,0 +1,169 @@
1
+ """Tests for executable caveats in delegation chains."""
2
+
3
+ import pytest
4
+
5
+ from vouch import Signer, generate_identity
6
+ from vouch.caveats import (
7
+ REASON_BROKEN_CHAIN,
8
+ REASON_BUDGET_EXCEEDED,
9
+ REASON_INVALID_LINK_PROOF,
10
+ REASON_UNKNOWN_TYPE,
11
+ REASON_UNROOTED,
12
+ CaveatError,
13
+ allowlist,
14
+ build_capability,
15
+ caveat,
16
+ chain_caveats,
17
+ evaluate_caveat,
18
+ flag_true,
19
+ incident_gate,
20
+ rate_limit,
21
+ running_total_ceiling,
22
+ time_window,
23
+ value_ceiling,
24
+ verify_capability,
25
+ )
26
+
27
+
28
+ def _id(domain):
29
+ kp = generate_identity(domain=domain)
30
+ return kp, Signer(private_key=kp.private_key_jwk, did=kp.did)
31
+
32
+
33
+ class TestEvaluateCaveat:
34
+ def test_value_ceiling(self):
35
+ c = value_ceiling("c", field="amount", limit=200)
36
+ assert evaluate_caveat(c, {"amount": 200}) == (True, None)
37
+ assert evaluate_caveat(c, {"amount": 201})[0] is False
38
+
39
+ def test_running_total_ceiling(self):
40
+ c = running_total_ceiling("c", limit=1000)
41
+ assert evaluate_caveat(c, {"amount": 100, "runningTotal": 800}) == (True, None)
42
+ assert evaluate_caveat(c, {"amount": 300, "runningTotal": 800})[0] is False
43
+
44
+ def test_time_window(self):
45
+ c = time_window("c", start=9, end=17)
46
+ assert evaluate_caveat(c, {"hour": 9})[0] is True
47
+ assert evaluate_caveat(c, {"hour": 17})[0] is False
48
+ assert evaluate_caveat(c, {"hour": 8})[0] is False
49
+
50
+ def test_allowlist(self):
51
+ c = allowlist("c", field="region", values=["eu", "us"])
52
+ assert evaluate_caveat(c, {"region": "eu"})[0] is True
53
+ assert evaluate_caveat(c, {"region": "apac"})[0] is False
54
+
55
+ def test_flag_and_incident(self):
56
+ assert evaluate_caveat(flag_true("c", field="shipped"), {"shipped": True})[0] is True
57
+ assert evaluate_caveat(flag_true("c", field="shipped"), {"shipped": False})[0] is False
58
+ assert evaluate_caveat(incident_gate("c"), {"incidentActive": True})[0] is True
59
+ assert evaluate_caveat(incident_gate("c"), {})[0] is False
60
+
61
+ def test_rate_limit(self):
62
+ c = rate_limit("c", max_count=3)
63
+ assert evaluate_caveat(c, {"recentCount": 2})[0] is True
64
+ assert evaluate_caveat(c, {"recentCount": 3})[0] is False
65
+
66
+ def test_unknown_type_denies(self):
67
+ allow, reason = evaluate_caveat({"id": "x", "type": "does_not_exist", "params": {}}, {})
68
+ assert allow is False and reason == REASON_UNKNOWN_TYPE
69
+
70
+ def test_deterministic(self):
71
+ c = value_ceiling("c", limit=10)
72
+ ctx = {"amount": 5}
73
+ assert evaluate_caveat(c, ctx) == evaluate_caveat(c, ctx)
74
+
75
+ def test_caveat_builder_validates(self):
76
+ with pytest.raises(CaveatError):
77
+ caveat("", "value_ceiling")
78
+
79
+
80
+ class TestChainAndAccumulation:
81
+ def _chain(self):
82
+ skp, store = _id("store.example.com")
83
+ mkp, mgr = _id("manager.example.com")
84
+ akp, agent = _id("agent.example.com")
85
+ link1 = build_capability(
86
+ store,
87
+ to=mgr.get_did(),
88
+ attenuation={"action": "refund"},
89
+ caveats=[flag_true("shipped-only", field="shipped")],
90
+ )
91
+ link2 = build_capability(
92
+ mgr,
93
+ to=agent.get_did(),
94
+ attenuation={"action": "refund", "resource": "usd:<=200"},
95
+ caveats=[value_ceiling("under-200", field="amount", limit=200)],
96
+ parent=link1,
97
+ )
98
+ keys = {
99
+ store.get_did(): skp.public_key_jwk,
100
+ mgr.get_did(): mkp.public_key_jwk,
101
+ agent.get_did(): akp.public_key_jwk,
102
+ }
103
+ return [link1, link2], keys, store.get_did()
104
+
105
+ def test_accumulates_both_caveats(self):
106
+ chain, _, _ = self._chain()
107
+ assert [c["id"] for c in chain_caveats(chain)] == ["shipped-only", "under-200"]
108
+
109
+ def test_all_caveats_satisfied_allows(self):
110
+ chain, keys, root = self._chain()
111
+ assert (
112
+ verify_capability(chain, keys.get, {"shipped": True, "amount": 120}, root_issuer=root)
113
+ is None
114
+ )
115
+
116
+ def test_ancestor_caveat_binds_descendant(self):
117
+ chain, keys, root = self._chain()
118
+ # 'shipped-only' came from the STORE (root); it still blocks the agent.
119
+ r = verify_capability(chain, keys.get, {"shipped": False, "amount": 120}, root_issuer=root)
120
+ assert r == "caveat_denied:shipped-only"
121
+
122
+ def test_own_caveat_blocks(self):
123
+ chain, keys, root = self._chain()
124
+ r = verify_capability(chain, keys.get, {"shipped": True, "amount": 5000}, root_issuer=root)
125
+ assert r == "caveat_denied:under-200"
126
+
127
+ def test_non_removal_via_root_check(self):
128
+ chain, keys, root = self._chain()
129
+ # Present only the leaf link, hoping 'shipped-only' vanishes.
130
+ r = verify_capability(
131
+ [chain[1]], keys.get, {"shipped": False, "amount": 120}, root_issuer=root
132
+ )
133
+ assert r == REASON_UNROOTED
134
+
135
+ def test_broken_parent_linkage(self):
136
+ # Two validly-signed links whose parent pointer does not match the
137
+ # predecessor actually presented: linkage is broken though both proofs pass.
138
+ skp, store = _id("store.example.com")
139
+ mkp, mgr = _id("manager.example.com")
140
+ akp, agent = _id("agent.example.com")
141
+ real_parent = build_capability(store, to=mgr.get_did(), attenuation={"action": "refund"})
142
+ other_parent = build_capability(store, to=mgr.get_did(), attenuation={"action": "read"})
143
+ leaf = build_capability(
144
+ mgr, to=agent.get_did(), attenuation={"action": "refund"}, parent=real_parent
145
+ )
146
+ keys = {store.get_did(): skp.public_key_jwk, mgr.get_did(): mkp.public_key_jwk}
147
+ # Present other_parent (valid) in place of real_parent: leaf.parent.digest mismatches.
148
+ r = verify_capability([other_parent, leaf], keys.get, {}, root_issuer=store.get_did())
149
+ assert r == REASON_BROKEN_CHAIN
150
+
151
+ def test_wrong_key_fails_link_proof(self):
152
+ chain, keys, root = self._chain()
153
+ bad = dict(keys)
154
+ # point the store's DID at a different key
155
+ okp, _ = _id("other.example.com")
156
+ bad[chain[0]["issuer"]] = okp.public_key_jwk
157
+ r = verify_capability(chain, bad.get, {"shipped": True, "amount": 120}, root_issuer=root)
158
+ assert r == REASON_INVALID_LINK_PROOF
159
+
160
+ def test_budget_exceeded(self):
161
+ chain, keys, root = self._chain()
162
+ r = verify_capability(
163
+ chain, keys.get, {"shipped": True, "amount": 120}, root_issuer=root, max_caveats=1
164
+ )
165
+ assert r == REASON_BUDGET_EXCEEDED
166
+
167
+ def test_empty_chain_rejected(self):
168
+ with pytest.raises(CaveatError):
169
+ verify_capability([], {}.get, {})
@@ -0,0 +1,179 @@
1
+ """Tests for Proof of Deliberation (two-phase deliberated execution)."""
2
+
3
+ from datetime import datetime, timedelta, timezone
4
+
5
+ import pytest
6
+
7
+ from vouch import Signer, generate_identity
8
+ from vouch.deliberation import (
9
+ CLASS_IRREVERSIBLE_FINANCIAL,
10
+ CLASS_REVERSIBLE,
11
+ EXECUTE_TYPE,
12
+ INTENT_TYPE,
13
+ REASON_INTENT_MISMATCH,
14
+ REASON_INVALID_PROOF,
15
+ REASON_UNAUTHORIZED_EXECUTOR,
16
+ REASON_VETOED,
17
+ REASON_WINDOW_NOT_ELAPSED,
18
+ VETO_TYPE,
19
+ DeliberationError,
20
+ action_digest,
21
+ check_execution,
22
+ commit_intent,
23
+ execute,
24
+ requires_window,
25
+ veto_intent,
26
+ verify_execution,
27
+ verify_intent,
28
+ )
29
+
30
+ WIRE = {"action": "transfer_funds", "target": "acct:vendor-1", "resource": "usd:5000"}
31
+
32
+
33
+ def _identity(domain="agent.example.com"):
34
+ kp = generate_identity(domain=domain)
35
+ return kp, Signer(private_key=kp.private_key_jwk, did=kp.did)
36
+
37
+
38
+ def _committed(agent, controller_did, opened, min_seconds=900):
39
+ return commit_intent(
40
+ agent,
41
+ intent=WIRE,
42
+ reversibility_class=CLASS_IRREVERSIBLE_FINANCIAL,
43
+ min_seconds=min_seconds,
44
+ veto_authorities=[controller_did],
45
+ opens_at=opened,
46
+ )
47
+
48
+
49
+ class TestClasses:
50
+ def test_reversible_needs_no_window(self):
51
+ assert requires_window(CLASS_REVERSIBLE) is False
52
+ assert requires_window(CLASS_IRREVERSIBLE_FINANCIAL) is True
53
+
54
+ def test_irreversible_requires_positive_window(self):
55
+ _, agent = _identity()
56
+ with pytest.raises(DeliberationError):
57
+ commit_intent(
58
+ agent, intent=WIRE, reversibility_class=CLASS_IRREVERSIBLE_FINANCIAL, min_seconds=0
59
+ )
60
+
61
+ def test_unknown_class_rejected(self):
62
+ _, agent = _identity()
63
+ with pytest.raises(DeliberationError):
64
+ commit_intent(agent, intent=WIRE, reversibility_class="whatever", min_seconds=10)
65
+
66
+ def test_intent_needs_action_target(self):
67
+ _, agent = _identity()
68
+ with pytest.raises(DeliberationError):
69
+ commit_intent(agent, intent={"action": "x"}, reversibility_class=CLASS_REVERSIBLE)
70
+
71
+
72
+ class TestIntentAndVeto:
73
+ def test_commit_and_verify_intent(self):
74
+ akp, agent = _identity()
75
+ _, ctrl = _identity("controller.example.com")
76
+ intent = _committed(agent, ctrl.get_did(), datetime.now(timezone.utc))
77
+ assert INTENT_TYPE in intent["type"]
78
+ ok, subject = verify_intent(intent, akp.public_key_jwk)
79
+ assert ok and subject["actionDigest"]["digest"] == action_digest(WIRE)
80
+
81
+ def test_veto_binds_to_intent(self):
82
+ _, agent = _identity()
83
+ _, ctrl = _identity("controller.example.com")
84
+ intent = _committed(agent, ctrl.get_did(), datetime.now(timezone.utc))
85
+ veto = veto_intent(ctrl, intent_credential=intent, reason="no")
86
+ assert VETO_TYPE in veto["type"]
87
+ assert veto["credentialSubject"]["intentDigest"]["digest"] == action_digest(WIRE)
88
+
89
+
90
+ class TestExecution:
91
+ def test_clean_window_accepted(self):
92
+ akp, agent = _identity()
93
+ _, ctrl = _identity("controller.example.com")
94
+ opened = datetime.now(timezone.utc)
95
+ intent = _committed(agent, ctrl.get_did(), opened)
96
+ ex = execute(agent, intent_credential=intent, closed_at=opened + timedelta(seconds=901))
97
+ assert EXECUTE_TYPE in ex["type"]
98
+ assert check_execution(ex, intent, akp.public_key_jwk) is None
99
+ ok, subject = verify_execution(ex, intent, akp.public_key_jwk)
100
+ assert ok and subject["intent"] == WIRE
101
+
102
+ def test_execute_too_early_rejected(self):
103
+ akp, agent = _identity()
104
+ _, ctrl = _identity("controller.example.com")
105
+ opened = datetime.now(timezone.utc)
106
+ intent = _committed(agent, ctrl.get_did(), opened)
107
+ ex = execute(agent, intent_credential=intent, closed_at=opened + timedelta(seconds=60))
108
+ assert check_execution(ex, intent, akp.public_key_jwk) == REASON_WINDOW_NOT_ELAPSED
109
+
110
+ def test_veto_blocks_even_after_window(self):
111
+ akp, agent = _identity()
112
+ ckp, ctrl = _identity("controller.example.com")
113
+ opened = datetime.now(timezone.utc)
114
+ intent = _committed(agent, ctrl.get_did(), opened)
115
+ veto = veto_intent(ctrl, intent_credential=intent, reason="over threshold")
116
+ ex = execute(agent, intent_credential=intent, closed_at=opened + timedelta(seconds=1200))
117
+ reason = check_execution(
118
+ ex,
119
+ intent,
120
+ akp.public_key_jwk,
121
+ vetoes=[veto],
122
+ veto_public_keys={ctrl.get_did(): ckp.public_key_jwk},
123
+ )
124
+ assert reason == REASON_VETOED
125
+
126
+ def test_veto_from_unlisted_authority_ignored(self):
127
+ akp, agent = _identity()
128
+ _, ctrl = _identity("controller.example.com")
129
+ rkp, rogue = _identity("rogue.example.com")
130
+ opened = datetime.now(timezone.utc)
131
+ intent = _committed(agent, ctrl.get_did(), opened) # only ctrl may veto
132
+ rogue_veto = veto_intent(rogue, intent_credential=intent, reason="I object")
133
+ ex = execute(agent, intent_credential=intent, closed_at=opened + timedelta(seconds=1000))
134
+ reason = check_execution(
135
+ ex,
136
+ intent,
137
+ akp.public_key_jwk,
138
+ vetoes=[rogue_veto],
139
+ veto_public_keys={rogue.get_did(): rkp.public_key_jwk},
140
+ )
141
+ assert reason is None # rogue is not in vetoAuthorities
142
+
143
+ def test_unauthorized_executor_rejected(self):
144
+ akp, agent = _identity()
145
+ _, ctrl = _identity("controller.example.com")
146
+ bkp, bob = _identity("bob.example.com")
147
+ opened = datetime.now(timezone.utc)
148
+ intent = _committed(agent, ctrl.get_did(), opened)
149
+ # Bob (not the intent issuer) signs an execute for the same intent.
150
+ ex = execute(bob, intent_credential=intent, closed_at=opened + timedelta(seconds=1000))
151
+ assert check_execution(ex, intent, bkp.public_key_jwk) == REASON_UNAUTHORIZED_EXECUTOR
152
+
153
+ def test_tampered_action_rejected(self):
154
+ akp, agent = _identity()
155
+ _, ctrl = _identity("controller.example.com")
156
+ opened = datetime.now(timezone.utc)
157
+ intent = _committed(agent, ctrl.get_did(), opened)
158
+ ex = execute(agent, intent_credential=intent, closed_at=opened + timedelta(seconds=1000))
159
+ # Re-sign an execute whose embedded intent was changed to a bigger amount.
160
+ ex["credentialSubject"]["intent"] = {
161
+ "action": "transfer_funds",
162
+ "target": "acct:vendor-1",
163
+ "resource": "usd:999999",
164
+ }
165
+ from vouch import data_integrity # noqa: PLC0415
166
+
167
+ ex["proof"] = data_integrity.build_proof(
168
+ ex, agent._raw_priv, agent.verification_method_id()
169
+ )
170
+ assert check_execution(ex, intent, akp.public_key_jwk) == REASON_INTENT_MISMATCH
171
+
172
+ def test_wrong_executor_key_fails_proof(self):
173
+ akp, agent = _identity()
174
+ okp, _ = _identity("other.example.com")
175
+ _, ctrl = _identity("controller.example.com")
176
+ opened = datetime.now(timezone.utc)
177
+ intent = _committed(agent, ctrl.get_did(), opened)
178
+ ex = execute(agent, intent_credential=intent, closed_at=opened + timedelta(seconds=1000))
179
+ assert check_execution(ex, intent, okp.public_key_jwk) == REASON_INVALID_PROOF
@@ -0,0 +1,169 @@
1
+ """Tests for inference provenance (bind an output to its model and context)."""
2
+
3
+ import pytest
4
+
5
+ from vouch import Signer, generate_identity
6
+ from vouch.provenance import (
7
+ PROVENANCE_TYPE,
8
+ REASON_CONTEXT_ROOT_MISMATCH,
9
+ REASON_MISSING_BINDING,
10
+ REASON_OUTPUT_MISMATCH,
11
+ REASON_WEIGHTS_MISMATCH,
12
+ ProvenanceError,
13
+ check_replay,
14
+ context_root,
15
+ output_digest,
16
+ sign_inference_provenance,
17
+ verify_context,
18
+ verify_inference_provenance,
19
+ weights_hash,
20
+ )
21
+
22
+ CONTEXT = [
23
+ {"source": "policy://refunds/v4", "text": "Refunds allowed within 30 days."},
24
+ {"source": "order://A-1007", "text": "Delivered 2026-06-20. Amount 120 USD."},
25
+ ]
26
+ OUTPUT = {"action": "approve_refund", "order": "A-1007", "amount": "usd:120"}
27
+
28
+
29
+ def _identity(domain="agent.example.com"):
30
+ kp = generate_identity(domain=domain)
31
+ return kp, Signer(private_key=kp.private_key_jwk, did=kp.did)
32
+
33
+
34
+ class TestHelpers:
35
+ def test_output_digest_deterministic(self):
36
+ assert output_digest(OUTPUT) == output_digest(dict(OUTPUT))
37
+
38
+ def test_context_root_order_sensitive(self):
39
+ assert context_root(CONTEXT) != context_root(list(reversed(CONTEXT)))
40
+
41
+ def test_context_root_empty_rejected(self):
42
+ with pytest.raises(ProvenanceError):
43
+ context_root([])
44
+
45
+ def test_weights_hash_multibase(self):
46
+ assert weights_hash(b"abc").startswith("u")
47
+
48
+ def test_to_bytes_type_error(self):
49
+ with pytest.raises(ProvenanceError):
50
+ output_digest(123)
51
+
52
+
53
+ class TestSignAndVerify:
54
+ def test_full_flow(self):
55
+ kp, agent = _identity()
56
+ wh = weights_hash(b"model-weights")
57
+ cred = sign_inference_provenance(
58
+ agent,
59
+ output=OUTPUT,
60
+ model_weights_hash=wh,
61
+ context_chunks=CONTEXT,
62
+ sampler={"seed": 42, "temperature": 0.0},
63
+ )
64
+ assert PROVENANCE_TYPE in cred["type"]
65
+ ok, subject = verify_inference_provenance(cred, kp.public_key_jwk)
66
+ assert ok
67
+ assert subject["provenance"]["modelWeightsHash"] == wh
68
+ assert subject["provenance"]["contextRoot"]["root"] == context_root(CONTEXT)
69
+ assert subject["outputDigest"]["digest"] == output_digest(OUTPUT)
70
+
71
+ def test_requires_a_binding(self):
72
+ _, agent = _identity()
73
+ with pytest.raises(ProvenanceError):
74
+ sign_inference_provenance(agent, output=OUTPUT) # no weights, no context
75
+
76
+ def test_weights_only_is_valid(self):
77
+ kp, agent = _identity()
78
+ cred = sign_inference_provenance(
79
+ agent, output=OUTPUT, model_weights_hash=weights_hash(b"m")
80
+ )
81
+ ok, _ = verify_inference_provenance(cred, kp.public_key_jwk)
82
+ assert ok
83
+
84
+ def test_context_only_is_valid(self):
85
+ kp, agent = _identity()
86
+ cred = sign_inference_provenance(agent, output=OUTPUT, context_chunks=CONTEXT)
87
+ ok, _ = verify_inference_provenance(cred, kp.public_key_jwk)
88
+ assert ok
89
+
90
+ def test_include_output_false_hides_plaintext(self):
91
+ kp, agent = _identity()
92
+ cred = sign_inference_provenance(
93
+ agent, output=OUTPUT, context_chunks=CONTEXT, include_output=False
94
+ )
95
+ ok, subject = verify_inference_provenance(cred, kp.public_key_jwk)
96
+ assert ok and "output" not in subject and subject["outputDigest"]["digest"]
97
+
98
+ def test_wrong_key_fails(self):
99
+ _, agent = _identity()
100
+ other_kp, _ = _identity("other.example.com")
101
+ cred = sign_inference_provenance(agent, output=OUTPUT, context_chunks=CONTEXT)
102
+ ok, _ = verify_inference_provenance(cred, other_kp.public_key_jwk)
103
+ assert ok is False
104
+
105
+ def test_tamper_breaks_proof(self):
106
+ kp, agent = _identity()
107
+ cred = sign_inference_provenance(agent, output=OUTPUT, context_chunks=CONTEXT)
108
+ cred["credentialSubject"]["output"]["amount"] = "usd:999999"
109
+ ok, _ = verify_inference_provenance(cred, kp.public_key_jwk)
110
+ assert ok is False
111
+
112
+
113
+ class TestContextVerification:
114
+ def _subject(self):
115
+ kp, agent = _identity()
116
+ cred = sign_inference_provenance(agent, output=OUTPUT, context_chunks=CONTEXT)
117
+ _, subject = verify_inference_provenance(cred, kp.public_key_jwk)
118
+ return subject
119
+
120
+ def test_same_context_reproduces(self):
121
+ ok, reason = verify_context(CONTEXT, self._subject())
122
+ assert ok and reason is None
123
+
124
+ def test_substituted_context_rejected(self):
125
+ forged = [{"source": "policy://refunds/v4", "text": "Refunds any time."}, CONTEXT[1]]
126
+ ok, reason = verify_context(forged, self._subject())
127
+ assert ok is False and reason == REASON_CONTEXT_ROOT_MISMATCH
128
+
129
+ def test_reordered_context_rejected(self):
130
+ ok, reason = verify_context(list(reversed(CONTEXT)), self._subject())
131
+ assert ok is False and reason == REASON_CONTEXT_ROOT_MISMATCH
132
+
133
+ def test_missing_binding_when_no_context_root(self):
134
+ kp, agent = _identity()
135
+ cred = sign_inference_provenance(
136
+ agent, output=OUTPUT, model_weights_hash=weights_hash(b"m")
137
+ )
138
+ _, subject = verify_inference_provenance(cred, kp.public_key_jwk)
139
+ ok, reason = verify_context(CONTEXT, subject)
140
+ assert ok is False and reason == REASON_MISSING_BINDING
141
+
142
+
143
+ class TestReplay:
144
+ def _subject(self, wh):
145
+ kp, agent = _identity()
146
+ cred = sign_inference_provenance(
147
+ agent, output=OUTPUT, model_weights_hash=wh, context_chunks=CONTEXT
148
+ )
149
+ _, subject = verify_inference_provenance(cred, kp.public_key_jwk)
150
+ return subject
151
+
152
+ def test_matching_replay_passes(self):
153
+ wh = weights_hash(b"model")
154
+ assert check_replay(self._subject(wh), output=OUTPUT, model_weights_hash=wh) is None
155
+
156
+ def test_output_mismatch(self):
157
+ wh = weights_hash(b"model")
158
+ bad = {"action": "approve_refund", "order": "A-1007", "amount": "usd:999999"}
159
+ assert check_replay(self._subject(wh), output=bad) == REASON_OUTPUT_MISMATCH
160
+
161
+ def test_weights_mismatch(self):
162
+ wh = weights_hash(b"model")
163
+ assert (
164
+ check_replay(self._subject(wh), model_weights_hash=weights_hash(b"other"))
165
+ == REASON_WEIGHTS_MISMATCH
166
+ )
167
+
168
+ def test_no_checks_supplied_passes(self):
169
+ assert check_replay(self._subject(weights_hash(b"model"))) is None