vouch-protocol 2.2.0__tar.gz → 3.0.0__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 (299) hide show
  1. {vouch_protocol-2.2.0/vouch_protocol.egg-info → vouch_protocol-3.0.0}/PKG-INFO +1 -1
  2. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/pyproject.toml +1 -1
  3. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_authority_state.py +32 -17
  4. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_autosign.py +22 -6
  5. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_caveats.py +121 -0
  6. vouch_protocol-3.0.0/tests/test_ccg_demo.py +29 -0
  7. vouch_protocol-3.0.0/tests/test_delegation_chain_vectors.py +43 -0
  8. vouch_protocol-3.0.0/tests/test_delegation_verify.py +471 -0
  9. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_dx_sugar.py +64 -2
  10. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_fleet.py +62 -2
  11. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_heartbeat.py +57 -48
  12. vouch_protocol-3.0.0/tests/test_heartbeat_hardening.py +207 -0
  13. vouch_protocol-3.0.0/tests/test_mcp_check_trust.py +81 -0
  14. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_merkle.py +4 -4
  15. vouch_protocol-3.0.0/tests/test_policy_limits.py +134 -0
  16. vouch_protocol-3.0.0/tests/test_pq_verify.py +155 -0
  17. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_quorum.py +36 -9
  18. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_signer_vc.py +21 -10
  19. vouch_protocol-3.0.0/tests/test_trust_check.py +277 -0
  20. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_validator_server.py +70 -11
  21. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_verifier_vc.py +8 -2
  22. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_vouch_mcp.py +25 -0
  23. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/__init__.py +1 -1
  24. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/attenuation.py +117 -5
  25. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/caveats.py +117 -7
  26. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/conformance.py +74 -41
  27. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/did_web.py +28 -0
  28. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/fleet.py +31 -16
  29. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/heartbeat.py +198 -13
  30. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/mcp/server.py +17 -1
  31. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/mcp/__init__.py +28 -10
  32. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/merkle.py +41 -17
  33. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/quorum.py +2 -0
  34. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/signer.py +87 -7
  35. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/trust_check.py +141 -28
  36. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/trust_entropy.py +83 -1
  37. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/validator_server.py +29 -2
  38. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/vc.py +31 -2
  39. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/verifier.py +377 -30
  40. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0/vouch_protocol.egg-info}/PKG-INFO +1 -1
  41. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/SOURCES.txt +7 -0
  42. vouch_protocol-2.2.0/tests/test_trust_check.py +0 -114
  43. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/LICENSE +0 -0
  44. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/MANIFEST.in +0 -0
  45. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/README.md +0 -0
  46. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/setup.cfg +0 -0
  47. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_accountability.py +0 -0
  48. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_agent_enroll.py +0 -0
  49. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_attribution.py +0 -0
  50. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_audio_bridge.py +0 -0
  51. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_audit_trail.py +0 -0
  52. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_auditor.py +0 -0
  53. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_authority_state_vectors.py +0 -0
  54. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_behavioral_attestation.py +0 -0
  55. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_bridge.py +0 -0
  56. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_budget.py +0 -0
  57. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_c2pa_formats.py +0 -0
  58. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_cache.py +0 -0
  59. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_canary.py +0 -0
  60. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_cli_init.py +0 -0
  61. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_cli_root.py +0 -0
  62. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_conformance.py +0 -0
  63. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_credential_gate.py +0 -0
  64. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_data_integrity.py +0 -0
  65. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_delegation_attenuation.py +0 -0
  66. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_deliberation.py +0 -0
  67. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_disconnected_edge.py +0 -0
  68. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_dtn_pads.py +0 -0
  69. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_examples_robotics.py +0 -0
  70. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_fastapi_credential_gate.py +0 -0
  71. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_git_workflow.py +0 -0
  72. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_grade.py +0 -0
  73. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_halos_evidence.py +0 -0
  74. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_hardware_seam.py +0 -0
  75. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_hasura_integration.py +0 -0
  76. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_hybrid.py +0 -0
  77. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_hybrid_interop.py +0 -0
  78. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_intent_recheck.py +0 -0
  79. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_jcs.py +0 -0
  80. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_jcs_interop.py +0 -0
  81. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_liveness_conformance.py +0 -0
  82. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_mcp_server_shield.py +0 -0
  83. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_media_badge.py +0 -0
  84. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_media_native.py +0 -0
  85. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_mint_contributor_credential.py +0 -0
  86. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_multikey.py +0 -0
  87. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_nonce.py +0 -0
  88. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_orbital_accumulator.py +0 -0
  89. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_proof_of_integration.py +0 -0
  90. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_provenance.py +0 -0
  91. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reasoning.py +0 -0
  92. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_receipts.py +0 -0
  93. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_recovery.py +0 -0
  94. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reputation.py +0 -0
  95. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reputation_aggregate.py +0 -0
  96. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reputation_disputes.py +0 -0
  97. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reputation_ledger.py +0 -0
  98. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reputation_policy.py +0 -0
  99. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_reputation_portability.py +0 -0
  100. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_revocation.py +0 -0
  101. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_access.py +0 -0
  102. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_conformance.py +0 -0
  103. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_consent.py +0 -0
  104. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_custody.py +0 -0
  105. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_embodiment.py +0 -0
  106. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_fusion.py +0 -0
  107. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_handover.py +0 -0
  108. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_handshake_blackbox_passport.py +0 -0
  109. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_identity.py +0 -0
  110. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_lease_quorum.py +0 -0
  111. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_lifecycle.py +0 -0
  112. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_liveness_revocation.py +0 -0
  113. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_odd.py +0 -0
  114. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_perception.py +0 -0
  115. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_pq.py +0 -0
  116. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_provenance_capability.py +0 -0
  117. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_root_identity.py +0 -0
  118. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_safety_record.py +0 -0
  119. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_swarm.py +0 -0
  120. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_teleop.py +0 -0
  121. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_robot_wear.py +0 -0
  122. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_root_of_trust.py +0 -0
  123. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_root_of_trust_vectors.py +0 -0
  124. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_ros2_action_gate.py +0 -0
  125. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_runtime_vectors.py +0 -0
  126. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_scan.py +0 -0
  127. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_secure_key_custody.py +0 -0
  128. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_security_hardening.py +0 -0
  129. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_shield_guard.py +0 -0
  130. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_shield_rules.py +0 -0
  131. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_shield_vectors.py +0 -0
  132. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_status_list.py +0 -0
  133. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_threshold.py +0 -0
  134. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_threshold_loader.py +0 -0
  135. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_tool_registry.py +0 -0
  136. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_transparency.py +0 -0
  137. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_transport.py +0 -0
  138. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_transport_http_rendezvous.py +0 -0
  139. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_transport_rendezvous.py +0 -0
  140. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_transport_udna_sdk.py +0 -0
  141. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_trifecta.py +0 -0
  142. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_trust_entropy.py +0 -0
  143. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/tests/test_vc.py +0 -0
  144. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/accountability.py +0 -0
  145. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/agent.py +0 -0
  146. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/async_verifier.py +0 -0
  147. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/attribution.py +0 -0
  148. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/attribution_cli.py +0 -0
  149. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/audio.py +0 -0
  150. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/audit_trail.py +0 -0
  151. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/auditor.py +0 -0
  152. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/authority_state.py +0 -0
  153. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/autosign.py +0 -0
  154. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/behavioral_attestation.py +0 -0
  155. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/bridge/__init__.py +0 -0
  156. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/bridge/audio_routes.py +0 -0
  157. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/bridge/config.py +0 -0
  158. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/bridge/server.py +0 -0
  159. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/budget.py +0 -0
  160. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/cache.py +0 -0
  161. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/canary.py +0 -0
  162. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/cli.py +0 -0
  163. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/config.py +0 -0
  164. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/credential.py +0 -0
  165. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/data_integrity.py +0 -0
  166. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/data_integrity_hybrid.py +0 -0
  167. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/deliberation.py +0 -0
  168. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/gate.py +0 -0
  169. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/grade.py +0 -0
  170. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/_common.py +0 -0
  171. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/a2a.py +0 -0
  172. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/adk.py +0 -0
  173. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/amnesia.py +0 -0
  174. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/autogen/__init__.py +0 -0
  175. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/autogpt/__init__.py +0 -0
  176. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/autogpt/commands.py +0 -0
  177. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/claude-code/settings.hooks.json +0 -0
  178. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/crewai/__init__.py +0 -0
  179. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/crewai/tool.py +0 -0
  180. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/fastapi.py +0 -0
  181. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/google.py +0 -0
  182. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/goose.py +0 -0
  183. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/hasura/__init__.py +0 -0
  184. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/hasura/webhook.py +0 -0
  185. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/langchain/__init__.py +0 -0
  186. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/langchain/tool.py +0 -0
  187. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/langgraph.py +0 -0
  188. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/mcp/__init__.py +0 -0
  189. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/mlflow.py +0 -0
  190. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/n8n.py +0 -0
  191. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/openai.py +0 -0
  192. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/safetensors.py +0 -0
  193. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/streamlit/__init__.py +0 -0
  194. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/streamlit/seal.py +0 -0
  195. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/integrations/vertex_ai/__init__.py +0 -0
  196. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/intent_recheck.py +0 -0
  197. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/jcs.py +0 -0
  198. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/keys.py +0 -0
  199. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/keystore.py +0 -0
  200. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/kms.py +0 -0
  201. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/liveness_conformance.py +0 -0
  202. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/mcp/_guard.py +0 -0
  203. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/mcp_guard.py +0 -0
  204. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/media/__init__.py +0 -0
  205. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/media/badge.py +0 -0
  206. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/media/c2pa.py +0 -0
  207. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/media/native.py +0 -0
  208. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/metrics.py +0 -0
  209. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/multikey.py +0 -0
  210. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/nonce.py +0 -0
  211. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/onboard.py +0 -0
  212. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/proof_of_integration.py +0 -0
  213. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/provenance.py +0 -0
  214. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/ratelimit.py +0 -0
  215. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reasoning.py +0 -0
  216. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/receipts.py +0 -0
  217. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/recovery.py +0 -0
  218. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/registry.py +0 -0
  219. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reputation.py +0 -0
  220. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reputation_aggregate.py +0 -0
  221. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reputation_disputes.py +0 -0
  222. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reputation_ledger.py +0 -0
  223. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reputation_policy.py +0 -0
  224. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/reputation_portability.py +0 -0
  225. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/revocation.py +0 -0
  226. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/__init__.py +0 -0
  227. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/_signing.py +0 -0
  228. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/_verify.py +0 -0
  229. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/access.py +0 -0
  230. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/accumulator.py +0 -0
  231. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/blackbox.py +0 -0
  232. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/bundle.py +0 -0
  233. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/capability.py +0 -0
  234. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/conformance.py +0 -0
  235. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/consent.py +0 -0
  236. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/custody.py +0 -0
  237. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/dtn_revocation.py +0 -0
  238. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/edge_trust.py +0 -0
  239. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/embodiment.py +0 -0
  240. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/freshness.py +0 -0
  241. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/fusion.py +0 -0
  242. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/geoscope.py +0 -0
  243. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/halos.py +0 -0
  244. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/handover.py +0 -0
  245. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/handshake.py +0 -0
  246. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/hardware.py +0 -0
  247. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/identity.py +0 -0
  248. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/lease.py +0 -0
  249. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/lifecycle.py +0 -0
  250. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/liveness.py +0 -0
  251. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/localization.py +0 -0
  252. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/odd.py +0 -0
  253. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/orbital.py +0 -0
  254. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/passport.py +0 -0
  255. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/perception.py +0 -0
  256. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/perception_consensus.py +0 -0
  257. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/physical_quorum.py +0 -0
  258. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/pq.py +0 -0
  259. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/presence.py +0 -0
  260. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/provenance.py +0 -0
  261. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/quorum_trust.py +0 -0
  262. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/revocation.py +0 -0
  263. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/root_identity.py +0 -0
  264. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/safety_record.py +0 -0
  265. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/swarm.py +0 -0
  266. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/teleop.py +0 -0
  267. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/robotics/wear.py +0 -0
  268. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/root_of_trust.py +0 -0
  269. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/scan/__init__.py +0 -0
  270. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/scan/detector.py +0 -0
  271. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/scan/patterns.py +0 -0
  272. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/seal.py +0 -0
  273. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/shield/__init__.py +0 -0
  274. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/shield/demo.py +0 -0
  275. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/shield/flight_recorder.py +0 -0
  276. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/shield/rules.py +0 -0
  277. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/shield/shield.py +0 -0
  278. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/shield/trust_registry.py +0 -0
  279. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/ssrf.py +0 -0
  280. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/status_list.py +0 -0
  281. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/status_list_fetcher.py +0 -0
  282. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/threshold.py +0 -0
  283. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/tool_registry.py +0 -0
  284. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transparency.py +0 -0
  285. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/__init__.py +0 -0
  286. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/base.py +0 -0
  287. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/did_key.py +0 -0
  288. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/envelope.py +0 -0
  289. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/http_rendezvous.py +0 -0
  290. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/http_transport.py +0 -0
  291. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/manager.py +0 -0
  292. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/rendezvous.py +0 -0
  293. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/transport/udna.py +0 -0
  294. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/trifecta.py +0 -0
  295. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch/verification.py +0 -0
  296. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/dependency_links.txt +0 -0
  297. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/entry_points.txt +0 -0
  298. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/vouch_protocol.egg-info/requires.txt +0 -0
  299. {vouch_protocol-2.2.0 → vouch_protocol-3.0.0}/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.2.0
3
+ Version: 3.0.0
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vouch-protocol"
7
- version = "2.2.0"
7
+ version = "3.0.0"
8
8
  description = "The Identity & Reputation Standard for AI Agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -37,6 +37,7 @@ from vouch.status_list import (
37
37
  from vouch.heartbeat import HeartbeatRequest, HeartbeatSession, HeartbeatValidator
38
38
  from vouch.vc import build_session_voucher
39
39
  from vouch.trust_check import verify_agent_call
40
+ from _heartbeat_keys import resolve, signed_session # noqa: E402
40
41
 
41
42
 
42
43
  AUTHORITY_DID = "did:web:treasury.example.com"
@@ -312,7 +313,7 @@ def test_session_voucher_carries_epoch():
312
313
 
313
314
 
314
315
  def test_heartbeat_request_roundtrips_epoch():
315
- session = HeartbeatSession(subject_did="did:web:agent.example.com", authority_epoch=6)
316
+ session = signed_session("did:web:agent.example.com", authority_epoch=6)
316
317
  req = session.build_request()
317
318
  assert req.authority_epoch == 6
318
319
  d = req.to_dict()
@@ -322,8 +323,8 @@ def test_heartbeat_request_roundtrips_epoch():
322
323
 
323
324
 
324
325
  def test_heartbeat_validator_tracks_highest_epoch_and_mints_voucher():
325
- validator = HeartbeatValidator(validator_did="did:web:v.example.com")
326
- session = HeartbeatSession(subject_did="did:web:agent.example.com", authority_epoch=2)
326
+ validator = HeartbeatValidator(key_resolver=resolve, validator_did="did:web:v.example.com")
327
+ session = signed_session("did:web:agent.example.com", authority_epoch=2)
327
328
  res = validator.validate(session.build_request().to_dict())
328
329
  assert res.ok
329
330
  assert res.session_voucher["credentialSubject"]["authorityEpoch"] == 2
@@ -349,21 +350,29 @@ def agent_call():
349
350
  intent = {"action": "transfer", "target": "vendor-1", "resource": "https://bank.example.com/x"}
350
351
  cred = signer.sign(intent=intent, valid_seconds=300)
351
352
  # A voucher minted under authority epoch 5, still well within its time window.
352
- voucher = build_session_voucher(
353
- subject_did=ident.did,
354
- validator_dids=["did:web:v.example.com"],
355
- decay_lambda=0.001,
356
- initial_trust=1.0,
357
- max_ttl_seconds=3600,
358
- scope=["agent_actions"],
359
- valid_seconds=300,
360
- authority_epoch=5,
353
+ # The voucher is signed by its validator; an unsigned voucher never counts.
354
+ from vouch.vc import sign_session_voucher
355
+
356
+ v_ident = generate_identity(domain="v.example.com")
357
+ v_signer = Signer(private_key=v_ident.private_key_jwk, did=v_ident.did)
358
+ voucher = sign_session_voucher(
359
+ build_session_voucher(
360
+ subject_did=ident.did,
361
+ validator_dids=[v_signer.did],
362
+ decay_lambda=0.001,
363
+ initial_trust=1.0,
364
+ max_ttl_seconds=3600,
365
+ scope=["agent_actions"],
366
+ valid_seconds=300,
367
+ authority_epoch=5,
368
+ ),
369
+ v_signer,
361
370
  )
362
- return signer, cred, voucher
371
+ return signer, cred, voucher, v_signer.get_public_key_multikey()
363
372
 
364
373
 
365
374
  def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
366
- signer, cred, voucher = agent_call
375
+ signer, cred, voucher, vkey = agent_call
367
376
  pub = signer.get_public_key_multikey()
368
377
 
369
378
  # Routine: time-decay only. The voucher is time-valid, so the call passes.
@@ -371,6 +380,7 @@ def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
371
380
  cred,
372
381
  public_key=pub,
373
382
  session_voucher=voucher,
383
+ voucher_public_key=vkey,
374
384
  trust_threshold=0.5,
375
385
  consequence=CONSEQUENCE_ROUTINE,
376
386
  )
@@ -382,6 +392,7 @@ def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
382
392
  cred,
383
393
  public_key=pub,
384
394
  session_voucher=voucher,
395
+ voucher_public_key=vkey,
385
396
  trust_threshold=0.5,
386
397
  consequence=CONSEQUENCE_SENSITIVE,
387
398
  last_seen_authority_epoch=7,
@@ -394,12 +405,13 @@ def test_same_voucher_accepted_routine_rejected_when_epoch_moves(agent_call):
394
405
 
395
406
 
396
407
  def test_sensitive_passes_when_epoch_current(agent_call):
397
- signer, cred, voucher = agent_call
408
+ signer, cred, voucher, vkey = agent_call
398
409
  pub = signer.get_public_key_multikey()
399
410
  v = verify_agent_call(
400
411
  cred,
401
412
  public_key=pub,
402
413
  session_voucher=voucher,
414
+ voucher_public_key=vkey,
403
415
  trust_threshold=0.5,
404
416
  consequence=CONSEQUENCE_SENSITIVE,
405
417
  last_seen_authority_epoch=5,
@@ -409,13 +421,14 @@ def test_sensitive_passes_when_epoch_current(agent_call):
409
421
 
410
422
 
411
423
  def test_critical_requires_live_cosign(agent_call):
412
- signer, cred, voucher = agent_call
424
+ signer, cred, voucher, vkey = agent_call
413
425
  pub = signer.get_public_key_multikey()
414
426
  # Even with a current epoch, critical demands a live co-sign.
415
427
  without = verify_agent_call(
416
428
  cred,
417
429
  public_key=pub,
418
430
  session_voucher=voucher,
431
+ voucher_public_key=vkey,
419
432
  trust_threshold=0.5,
420
433
  consequence=CONSEQUENCE_CRITICAL,
421
434
  last_seen_authority_epoch=5,
@@ -427,6 +440,7 @@ def test_critical_requires_live_cosign(agent_call):
427
440
  cred,
428
441
  public_key=pub,
429
442
  session_voucher=voucher,
443
+ voucher_public_key=vkey,
430
444
  trust_threshold=0.5,
431
445
  consequence=CONSEQUENCE_CRITICAL,
432
446
  last_seen_authority_epoch=5,
@@ -437,13 +451,14 @@ def test_critical_requires_live_cosign(agent_call):
437
451
 
438
452
 
439
453
  def test_voucher_epoch_read_from_credential_subject(agent_call):
440
- signer, cred, voucher = agent_call
454
+ signer, cred, voucher, vkey = agent_call
441
455
  pub = signer.get_public_key_multikey()
442
456
  # Do not pass voucher_authority_epoch explicitly; it is read from the voucher.
443
457
  v = verify_agent_call(
444
458
  cred,
445
459
  public_key=pub,
446
460
  session_voucher=voucher,
461
+ voucher_public_key=vkey,
447
462
  trust_threshold=0.5,
448
463
  consequence=CONSEQUENCE_SENSITIVE,
449
464
  last_seen_authority_epoch=6,
@@ -217,10 +217,16 @@ class TestDelegation:
217
217
 
218
218
  kp, psigner = principal
219
219
  grant = vouch.delegate(
220
- action="charge", target="api.bank", resource="invoices", signer=psigner
220
+ action="charge",
221
+ target="api.bank",
222
+ resource="invoices",
223
+ to=env_identity.did,
224
+ signer=psigner,
221
225
  )
222
226
 
223
- @signed(parent=grant)
227
+ # The call's intent must sit inside the grant: same action and target,
228
+ # a sub-resource of the granted resource.
229
+ @signed(action="charge", target="api.bank", resource="invoices/42", parent=grant)
224
230
  def charge_invoice(invoice_id):
225
231
  return current_credential()
226
232
 
@@ -229,15 +235,21 @@ class TestDelegation:
229
235
  assert chain and len(chain) == 1
230
236
  assert chain[0]["issuer"] == kp.did
231
237
  assert chain[0]["subject"] == env_identity.did
232
- # The agent's own credential still verifies under its own key.
233
- assert _verify(cred, pubkey)[0]
238
+ # The agent's credential verifies under its own key, and its chain
239
+ # verifies against the principal's key.
240
+ ok, _ = Verifier.verify(cred, pubkey, trusted_roots={kp.did: kp.public_key_jwk})
241
+ assert ok
234
242
 
235
243
  def test_narrowing_is_enforced_no_widened_credential(self, env_identity, principal):
236
244
  kp, psigner = principal
237
245
  import vouch
238
246
 
239
247
  grant = vouch.delegate(
240
- action="charge", target="api.bank", resource="invoices", signer=psigner
248
+ action="charge",
249
+ target="api.bank",
250
+ resource="invoices",
251
+ to=env_identity.did,
252
+ signer=psigner,
241
253
  )
242
254
 
243
255
  # Attempt to act OUTSIDE the granted resource. Signing fails the
@@ -257,7 +269,11 @@ class TestDelegation:
257
269
  import vouch
258
270
 
259
271
  grant = vouch.delegate(
260
- action="charge", target="api.bank", resource="invoices", signer=psigner
272
+ action="charge",
273
+ target="api.bank",
274
+ resource="invoices",
275
+ to=env_identity.did,
276
+ signer=psigner,
261
277
  )
262
278
 
263
279
  @signed(action="charge", target="api.bank", resource="invoices/42", parent=grant)
@@ -167,3 +167,124 @@ class TestChainAndAccumulation:
167
167
  def test_empty_chain_rejected(self):
168
168
  with pytest.raises(CaveatError):
169
169
  verify_capability([], {}.get, {})
170
+
171
+
172
+ # ---------------------------------------------------------------------------
173
+ # Linkage, narrowing, expiry, and revocation of capability links
174
+ # ---------------------------------------------------------------------------
175
+
176
+
177
+ class TestLinkSecurity:
178
+ def _parties(self):
179
+ skp, store = _id("store.example.com")
180
+ mkp, mgr = _id("manager.example.com")
181
+ xkp, stranger = _id("stranger.example.com")
182
+ akp, agent = _id("agent.example.com")
183
+ keys = {
184
+ store.get_did(): skp.public_key_jwk,
185
+ mgr.get_did(): mkp.public_key_jwk,
186
+ stranger.get_did(): xkp.public_key_jwk,
187
+ agent.get_did(): akp.public_key_jwk,
188
+ }
189
+ return store, mgr, stranger, agent, keys
190
+
191
+ def _root(self, store, mgr, **kw):
192
+ return build_capability(
193
+ store,
194
+ to=mgr.get_did(),
195
+ attenuation={"action": "refund"},
196
+ caveats=[value_ceiling("cap", limit=200)],
197
+ **kw,
198
+ )
199
+
200
+ def test_only_the_named_delegatee_can_issue_the_next_link(self):
201
+ """The probe: a stranger (not the delegatee) issues a widened child."""
202
+ from vouch.caveats import REASON_NOT_DELEGATEE
203
+
204
+ store, mgr, stranger, agent, keys = self._parties()
205
+ root = self._root(store, mgr)
206
+ child = build_capability(
207
+ stranger, to=agent.get_did(), attenuation={"action": "*"}, parent=root
208
+ )
209
+ r = verify_capability([root, child], keys.get, {"amount": 100}, root_issuer=store.get_did())
210
+ assert r == REASON_NOT_DELEGATEE
211
+
212
+ def test_child_attenuation_cannot_widen_parent(self):
213
+ store, mgr, stranger, agent, keys = self._parties()
214
+ root = self._root(store, mgr)
215
+ child = build_capability(mgr, to=agent.get_did(), attenuation={"action": "*"}, parent=root)
216
+ r = verify_capability([root, child], keys.get, {"amount": 100}, root_issuer=store.get_did())
217
+ assert r == "scope_exceeds_parent:action"
218
+
219
+ def test_links_carry_expiry(self):
220
+ store, mgr, *_ = self._parties()
221
+ root = self._root(store, mgr, valid_seconds=600)
222
+ assert "validFrom" in root and "validUntil" in root
223
+
224
+ def test_expired_link_fails(self):
225
+ from datetime import datetime, timedelta, timezone
226
+
227
+ from vouch.caveats import REASON_EXPIRED
228
+
229
+ store, mgr, stranger, agent, keys = self._parties()
230
+ root = self._root(store, mgr, valid_seconds=60)
231
+ later = datetime.now(timezone.utc) + timedelta(hours=2)
232
+ r = verify_capability(
233
+ [root], keys.get, {"amount": 10}, root_issuer=store.get_did(), now=later
234
+ )
235
+ assert r == REASON_EXPIRED
236
+
237
+ def test_link_without_expiry_fails(self):
238
+ from vouch.caveats import REASON_MALFORMED_LINK, _attach_proof
239
+
240
+ store, mgr, stranger, agent, keys = self._parties()
241
+ root = self._root(store, mgr)
242
+ undated = {k: v for k, v in root.items() if k not in ("proof", "validUntil")}
243
+ undated = _attach_proof(undated, store)
244
+ r = verify_capability([undated], keys.get, {"amount": 10}, root_issuer=store.get_did())
245
+ assert r == REASON_MALFORMED_LINK
246
+
247
+ def test_child_cannot_outlive_parent(self):
248
+ store, mgr, stranger, agent, keys = self._parties()
249
+ root = self._root(store, mgr, valid_seconds=60)
250
+ child = build_capability(
251
+ mgr,
252
+ to=agent.get_did(),
253
+ attenuation={"action": "refund"},
254
+ parent=root,
255
+ valid_seconds=86400,
256
+ )
257
+ # The builder clamps the child to its parent's window ...
258
+ assert child["validUntil"] <= root["validUntil"]
259
+ assert (
260
+ verify_capability([root, child], keys.get, {"amount": 10}, root_issuer=store.get_did())
261
+ is None
262
+ )
263
+
264
+ def test_revoked_link_or_issuer_fails(self):
265
+ from vouch.caveats import REASON_REVOKED
266
+
267
+ store, mgr, stranger, agent, keys = self._parties()
268
+ root = self._root(store, mgr)
269
+ child = build_capability(
270
+ mgr, to=agent.get_did(), attenuation={"action": "refund"}, parent=root
271
+ )
272
+ chain = [root, child]
273
+ ctx = {"amount": 10}
274
+ assert verify_capability(chain, keys.get, ctx, root_issuer=store.get_did()) is None
275
+ assert (
276
+ verify_capability(
277
+ chain, keys.get, ctx, root_issuer=store.get_did(), revoked={root["id"]}
278
+ )
279
+ == REASON_REVOKED
280
+ )
281
+ assert (
282
+ verify_capability(
283
+ chain,
284
+ keys.get,
285
+ ctx,
286
+ root_issuer=store.get_did(),
287
+ revoked=lambda i: i == mgr.get_did(),
288
+ )
289
+ == REASON_REVOKED
290
+ )
@@ -0,0 +1,29 @@
1
+ """The CCG demo scripts run offline and every case matches its expected outcome."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+
12
+ ROOT = Path(__file__).resolve().parent.parent
13
+ DEMO = ROOT / "examples" / "ccg_demo"
14
+
15
+
16
+ @pytest.mark.parametrize("script", ["01_delegation.py", "02_trust_decay.py", "03_heartbeat.py"])
17
+ def test_demo_script_passes(script):
18
+ env = dict(os.environ, PYTHONPATH=str(ROOT), PYTHONDONTWRITEBYTECODE="1")
19
+ proc = subprocess.run(
20
+ [sys.executable, str(DEMO / script)],
21
+ cwd=str(ROOT),
22
+ env=env,
23
+ capture_output=True,
24
+ text=True,
25
+ timeout=60,
26
+ )
27
+ assert proc.returncode == 0, proc.stdout + proc.stderr
28
+ assert "UNEXPECTED" not in proc.stdout
29
+ assert "matched the expected outcome" in proc.stdout
@@ -0,0 +1,43 @@
1
+ """Python against the shared delegation-chain vectors (the interop contract)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import json
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+
12
+ from vouch.verifier import Verifier, verify_delegation_chain
13
+
14
+ ROOT = Path(__file__).resolve().parent.parent
15
+ VECTOR = ROOT / "test-vectors" / "delegation-chain" / "vector.json"
16
+ CASES = json.loads(VECTOR.read_text(encoding="utf-8"))["cases"]
17
+
18
+
19
+ def _at(iso: str) -> datetime:
20
+ return datetime.fromisoformat(iso.replace("Z", "+00:00"))
21
+
22
+
23
+ def test_vector_file_matches_generator():
24
+ spec = importlib.util.spec_from_file_location(
25
+ "gen_delegation_chain_vectors", ROOT / "scripts" / "gen_delegation_chain_vectors.py"
26
+ )
27
+ gen = importlib.util.module_from_spec(spec)
28
+ spec.loader.exec_module(gen)
29
+ assert gen.render() == VECTOR.read_text(encoding="utf-8")
30
+
31
+
32
+ @pytest.mark.parametrize("case", CASES, ids=[c["name"] for c in CASES])
33
+ def test_chain_verdict(case):
34
+ assert verify_delegation_chain(case["credential"], now=_at(case["now"])) == case["expected"]
35
+
36
+
37
+ @pytest.mark.parametrize("case", CASES, ids=[c["name"] for c in CASES])
38
+ def test_full_verify_agrees(case):
39
+ cred = case["credential"]
40
+ issuer = cred["issuer"]
41
+ leaf_key = issuer.split("did:key:", 1)[1]
42
+ ok, _ = Verifier.verify(cred, public_key=leaf_key, now=_at(case["now"]))
43
+ assert ok is (case["expected"] is None)