openbox-sdk-python 1.2.0__tar.gz → 1.3.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 (160) hide show
  1. openbox_sdk_python-1.3.0/CHANGELOG.md +116 -0
  2. openbox_sdk_python-1.3.0/LICENSE +21 -0
  3. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/PKG-INFO +76 -7
  4. openbox_sdk_python-1.3.0/README.md +124 -0
  5. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/__init__.py +1 -1
  6. openbox_sdk_python-1.3.0/openbox_core/bootstrap.py +296 -0
  7. openbox_sdk_python-1.3.0/openbox_core/client.py +1279 -0
  8. openbox_sdk_python-1.3.0/openbox_core/config.py +563 -0
  9. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/errors.py +64 -3
  10. openbox_sdk_python-1.3.0/openbox_core/identity_okta.py +302 -0
  11. openbox_sdk_python-1.3.0/openbox_core/identity_transitions.py +134 -0
  12. openbox_sdk_python-1.3.0/openbox_core/identity_types.py +74 -0
  13. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/db.py +20 -3
  14. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/http.py +17 -0
  15. openbox_sdk_python-1.3.0/openbox_core/jwk_thumbprint.py +78 -0
  16. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/runtime.py +6 -1
  17. openbox_sdk_python-1.3.0/openbox_core/workload_identity.py +383 -0
  18. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/pyproject.toml +2 -2
  19. openbox_sdk_python-1.3.0/tests/client/identity_fixtures.py +71 -0
  20. openbox_sdk_python-1.3.0/tests/client/test_auth_failure_classification.py +158 -0
  21. openbox_sdk_python-1.3.0/tests/client/test_bootstrap.py +713 -0
  22. openbox_sdk_python-1.3.0/tests/client/test_keycloak_workload_identity.py +414 -0
  23. openbox_sdk_python-1.3.0/tests/client/test_transition_preflight.py +239 -0
  24. openbox_sdk_python-1.3.0/tests/client/test_v2_endpoint_selection.py +194 -0
  25. openbox_sdk_python-1.3.0/tests/config/test_bootstrap_mode.py +235 -0
  26. openbox_sdk_python-1.3.0/tests/config/test_identity_verification_method.py +145 -0
  27. openbox_sdk_python-1.3.0/tests/signing/identity_v2/MANIFEST.sha256 +45 -0
  28. openbox_sdk_python-1.3.0/tests/signing/identity_v2/approval.json +33 -0
  29. openbox_sdk_python-1.3.0/tests/signing/identity_v2/auth-validate.json +33 -0
  30. openbox_sdk_python-1.3.0/tests/signing/identity_v2/evaluate.json +33 -0
  31. openbox_sdk_python-1.3.0/tests/signing/identity_v2/handoff.json +33 -0
  32. openbox_sdk_python-1.3.0/tests/signing/identity_v2/keypair.json +54 -0
  33. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/README.md +71 -0
  34. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/alg-none.json +14 -0
  35. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/alg-symmetric.json +14 -0
  36. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/body-tampered.json +14 -0
  37. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/embedded-jku.json +14 -0
  38. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/embedded-jwk.json +14 -0
  39. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/embedded-x5u.json +14 -0
  40. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/empty-jti.json +14 -0
  41. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/excessive-lifetime.json +14 -0
  42. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/expired.json +14 -0
  43. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/future-iat.json +14 -0
  44. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/htu-with-host.json +14 -0
  45. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/htu-with-query.json +14 -0
  46. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/malformed-jwt-truncated.json +14 -0
  47. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/malformed-jwt.json +14 -0
  48. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/missing-assertion.json +14 -0
  49. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/missing-exp.json +14 -0
  50. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/normal-assertion-on-transition-proof-route.json +14 -0
  51. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/paired-with-different-api-key.json +14 -0
  52. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/signature-by-other-key.json +14 -0
  53. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/transition-proof-on-validate-route.json +14 -0
  54. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/transition-proof-signed-by-other-credential.json +14 -0
  55. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/undersized-rsa-key.json +14 -0
  56. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/unknown-kid.json +14 -0
  57. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/v1-path.json +14 -0
  58. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-audience.json +14 -0
  59. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-deployment-id.json +14 -0
  60. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-http-method.json +14 -0
  61. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-iss.json +14 -0
  62. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-path.json +14 -0
  63. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-signed-agent-id.json +14 -0
  64. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-signed-organization-id.json +14 -0
  65. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-sub.json +14 -0
  66. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-transition-challenge.json +14 -0
  67. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-transition-purpose.json +14 -0
  68. openbox_sdk_python-1.3.0/tests/signing/identity_v2/tamper/wrong-typ.json +14 -0
  69. openbox_sdk_python-1.3.0/tests/signing/identity_v2/transition-proof.json +36 -0
  70. openbox_sdk_python-1.3.0/tests/signing/test_identity_okta_unit.py +222 -0
  71. openbox_sdk_python-1.3.0/tests/signing/test_identity_v2_fixtures.py +163 -0
  72. openbox_sdk_python-1.3.0/tests/signing/test_identity_v2_signing.py +160 -0
  73. openbox_sdk_python-1.3.0/tests/signing/test_jwk_thumbprint.py +127 -0
  74. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/uv.lock +1 -1
  75. openbox_sdk_python-1.2.0/CHANGELOG.md +0 -41
  76. openbox_sdk_python-1.2.0/README.md +0 -55
  77. openbox_sdk_python-1.2.0/openbox_core/client.py +0 -298
  78. openbox_sdk_python-1.2.0/openbox_core/config.py +0 -260
  79. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/.github/instructions/openbox-sdk-python.instructions.md +0 -0
  80. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/.github/workflows/ci.yml +0 -0
  81. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/.github/workflows/publish.yml +0 -0
  82. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/.gitignore +0 -0
  83. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/.python-version +0 -0
  84. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/adapters/__init__.py +0 -0
  85. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/adapters/base.py +0 -0
  86. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/approvals.py +0 -0
  87. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/conformance/__init__.py +0 -0
  88. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/conformance/fake_core.py +0 -0
  89. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/conformance/hook_preflight.py +0 -0
  90. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/conformance/instrumentation.py +0 -0
  91. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/context.py +0 -0
  92. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/contracts/__init__.py +0 -0
  93. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/contracts/context.py +0 -0
  94. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/contracts/events.py +0 -0
  95. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/contracts/otel_spans.py +0 -0
  96. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/contracts/results.py +0 -0
  97. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/gate.py +0 -0
  98. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/hooks/__init__.py +0 -0
  99. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/hooks/events.py +0 -0
  100. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/hooks/preflight.py +0 -0
  101. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/hooks/wrappers.py +0 -0
  102. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/identity.py +0 -0
  103. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/__init__.py +0 -0
  104. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/file.py +0 -0
  105. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/function.py +0 -0
  106. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/llm.py +0 -0
  107. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/manager.py +0 -0
  108. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/instrumentation/shared.py +0 -0
  109. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/otel/__init__.py +0 -0
  110. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/otel/propagation.py +0 -0
  111. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/otel/provider.py +0 -0
  112. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/otel/setup.py +0 -0
  113. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/otel/span_processor.py +0 -0
  114. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/otel/trace_context.py +0 -0
  115. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/py.typed +0 -0
  116. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/sdk_version.py +0 -0
  117. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/serialization.py +0 -0
  118. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/validation/__init__.py +0 -0
  119. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/validation/diagnostics.py +0 -0
  120. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/validation/event_rules.py +0 -0
  121. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/validation/registry.py +0 -0
  122. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/validation/span_normalization.py +0 -0
  123. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/wire/__init__.py +0 -0
  124. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/wire/core_span.py +0 -0
  125. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/openbox_core/wire/evaluate_payload.py +0 -0
  126. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/client/test_approval_poll.py +0 -0
  127. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/client/test_fail_modes.py +0 -0
  128. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/config/test_resolution_order.py +0 -0
  129. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/conformance/test_required_cases.py +0 -0
  130. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/context/test_bind_reset.py +0 -0
  131. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/context/test_trace_key.py +0 -0
  132. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/contracts/test_approval_parsing.py +0 -0
  133. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/contracts/test_event_classify.py +0 -0
  134. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/contracts/test_patch.py +0 -0
  135. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/contracts/test_result_parsing.py +0 -0
  136. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/gate/test_diagnostics.py +0 -0
  137. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/gate/test_strict_failures.py +0 -0
  138. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/conftest.py +0 -0
  139. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/instrumented_env.py +0 -0
  140. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_db_block.py +0 -0
  141. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_db_redis_mongo.py +0 -0
  142. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_file_function_block.py +0 -0
  143. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_hook_failclosed_and_sync_approval.py +0 -0
  144. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_hook_runtime.py +0 -0
  145. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_http_preflight_block.py +0 -0
  146. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_http_urllib.py +0 -0
  147. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/instrumentation/test_manager_and_otel_lifecycle.py +0 -0
  148. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/runtime/test_runtime_delegation.py +0 -0
  149. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/signing/generate_golden_fixture_from_temporal_signer.py +0 -0
  150. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/signing/golden_temporal_signed_request.json +0 -0
  151. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/signing/test_golden_signing.py +0 -0
  152. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/test_import_safety.py +0 -0
  153. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/test_sdk_version.py +0 -0
  154. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/go_spandata_compat/go.mod +0 -0
  155. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/go_spandata_compat/main.go +0 -0
  156. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/span_fixtures.py +0 -0
  157. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/test_backend_compat.py +0 -0
  158. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/test_core_span_projection.py +0 -0
  159. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/test_flat_hook_contract.py +0 -0
  160. {openbox_sdk_python-1.2.0 → openbox_sdk_python-1.3.0}/tests/wire/test_hex_ids.py +0 -0
@@ -0,0 +1,116 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **Okta identity bootstrap** — an `okta_ai_agent` runtime now needs only
12
+ `OPENBOX_API_URL`, `OPENBOX_API_KEY`, and `OPENBOX_OKTA_AGENT_PRIVATE_KEY`. The SDK
13
+ fetches the remaining identity metadata (agent id, organization id, deployment id,
14
+ assertion audience, external Okta agent id, credential `kid`, algorithm) from
15
+ `GET /api/v2/auth/bootstrap` on first use and caches it in memory for the lifetime of
16
+ the client. The signed assertion and its verification are unchanged — only how the SDK
17
+ obtains its non-secret configuration.
18
+ - Before the first governed request the SDK derives its private key's RFC 7638 public-key
19
+ thumbprint and compares it (constant-time) with the one Core reports for the agent's
20
+ selected credential. A mismatch raises `OpenBoxConfigError` with actionable guidance;
21
+ no governed request is sent.
22
+ - `EvaluationClient.refresh_identity_metadata()` / `arefresh_identity_metadata()` for
23
+ long-running agents whose selected credential rotated. The thumbprint is re-verified
24
+ *before* cached metadata is replaced, so a credential rotated to a key this process
25
+ does not hold fails loudly and leaves the client on its previous identity. The client
26
+ never refreshes automatically after an auth failure — a blind retry cannot repair a key
27
+ mismatch and would hide it.
28
+ - `EvaluationClient.identity_metadata()` exposes the validated bootstrap document
29
+ (non-secret).
30
+ - New modules `openbox_core.bootstrap` and `openbox_core.jwk_thumbprint`.
31
+
32
+ ### Changed
33
+ - Okta configuration now resolves into exactly one of three modes, never a blend:
34
+ **bootstrap** (private key only), **legacy explicit** (every metadata field set, works
35
+ unchanged), or **invalid mixed** (only some metadata fields set) — which raises
36
+ `OpenBoxConfigError` naming the offending fields rather than merging stale local values
37
+ over what Core would have supplied.
38
+ - `OPENBOX_DEPLOYMENT_ID` is no longer required in bootstrap mode. Core returns its own
39
+ deployment id, which prevents a runtime from signing for one deployment while calling
40
+ another.
41
+ - No failure path downgrades to v1, to an unsigned request, or to a different identity
42
+ method. A Core that predates the endpoint (`404`) produces actionable upgrade guidance.
43
+
44
+ ## [1.3.0] - 2026-07-30
45
+
46
+ ### Added
47
+ - v2 (Okta AI Agent) identity verification support, alongside existing v1 (OpenBox DID)
48
+ and inferred `legacy_unsigned` support: tagged identity configuration
49
+ (`OpenBoxDidIdentityConfig` / `OktaAiAgentIdentityConfig` in
50
+ `openbox_core.identity_types`), RS256 assertion signing
51
+ (`openbox_core.identity_okta`), and version-aware endpoint selection —
52
+ `okta_ai_agent` identities route to `/api/v2/governance/evaluate`,
53
+ `/api/v2/governance/approval`, `GET /api/v2/auth/validate`, and
54
+ `/api/v2/handoffs`; there is no cross-version retry.
55
+ - New env vars: `OPENBOX_AGENT_IDENTITY_METHOD`, `OPENBOX_OKTA_AGENT_ID`,
56
+ `OPENBOX_OKTA_AGENT_KEY_ID`, `OPENBOX_OKTA_AGENT_PRIVATE_KEY`,
57
+ `OPENBOX_OKTA_AGENT_ALGORITHM`, `OPENBOX_AGENT_ID`, `OPENBOX_ORGANIZATION_ID`,
58
+ `OPENBOX_DEPLOYMENT_ID`, `OPENBOX_AGENT_PROOF_AUDIENCE`. DID and Okta identity
59
+ fields are mutually exclusive; the canonical Okta private-key encoding is PKCS8 PEM.
60
+ - `EvaluationClient.emit_handoff` / `aemit_handoff` — source-authenticated handoff
61
+ (`POST /api/v1/handoffs` for openbox_did, `/api/v2/handoffs` for okta_ai_agent).
62
+ Inferred unsigned mode raises `OpenBoxConfigError` (no source identity to prove)
63
+ instead of silently falling back to the legacy receiver-authenticated event.
64
+ - `EvaluationClient.validate_okta_identity_transition` /
65
+ `avalidate_okta_identity_transition` and `.validate_openbox_did_identity_transition` /
66
+ `avalidate_openbox_did_identity_transition` — transition-preflight helpers that sign
67
+ with an **explicit candidate identity** and never fall back to the client's active
68
+ signer, even if the client happens to already use the same identity method.
69
+
70
+ ### Fixed
71
+ - **BREAKING (security fix):** `EvaluationClient.evaluate` / `aevaluate` and
72
+ `.poll_approval` / `.apoll_approval` now raise an actionable
73
+ `OpenBoxAuthError`/`OpenBoxSigningError` on HTTP 401/403, regardless of
74
+ `on_api_error`. Previously, evaluate routed every `>= 400` response — including
75
+ 401/403 — through the network-failure path, which under the default `fail_open`
76
+ produced a fallback `ALLOW`; an agent whose key was revoked kept operating,
77
+ ungoverned. Approval polling treated 401/403 the same as a transient poll failure
78
+ (returned `None`, read by callers as "still pending"), so a revoked agent's
79
+ approval wait never terminated. Both are now hard authentication failures: they
80
+ fail closed unconditionally and are never retried against the other API version.
81
+ A genuine network error or 5xx is unaffected and still respects `on_api_error`.
82
+
83
+ ## [1.2.0] - 2026-07-23
84
+
85
+ ### Changed
86
+ - **BREAKING:** Renamed the public BLOCK remediation contract from `retry_plan` to `patch`.
87
+ `RetryPlan` is now `Patch`, `RetryDirective` is now `PatchDirective`, and
88
+ `handle_retryable_block()` is now `handle_patch()`. `EvaluationResult.retry_plan` and
89
+ `ApprovalResult.retry_plan` are now `.patch`. The parser now reads only the canonical outer
90
+ wire key `patch`; the old `retry_plan` key is no longer recognized.
91
+ - The verdict gate is unchanged: a directive is still surfaced only for an exact `BLOCK` verdict
92
+ carrying a valid patch — never for HALT/`should_stop()`, and never for an expired approval
93
+ result.
94
+
95
+ ### Notes
96
+ - This is a breaking release: the old public names (`RetryPlan`, `RetryDirective`,
97
+ `handle_retryable_block`, `.retry_plan`) are removed, not aliased. Consumers still pinned to
98
+ `1.1.0` remain fail-safe — they ignore the unknown `patch` field but still enforce the `BLOCK`
99
+ verdict.
100
+
101
+ ## [1.1.0] - 2026-07-21
102
+
103
+ ### Added
104
+ - `RetryPlan` and `RetryDirective` dataclasses in `openbox_core.contracts.results`.
105
+ - Optional `retry_plan` directive parsing on both `EvaluationResult` and `ApprovalResult`.
106
+ A `_MISSING` sentinel keeps a present `new_input: null` distinct from an absent field; falsy
107
+ values (`null`, `""`, `0`, `[]`, `{}`) are preserved; a boolean `new_input` is rejected; and every
108
+ number (recursively) must be finite and, if integral, a JS-safe integer (`|n| <= 2^53 - 1`).
109
+ - `handle_retryable_block(result)` — an opt-in, pure inspector that returns a `RetryDirective` only
110
+ for a `BLOCK` verdict carrying a valid plan. Returns `None` for a plain BLOCK, every non-BLOCK
111
+ verdict (including HALT), a pending verdict, and an expired `ApprovalResult`.
112
+
113
+ ### Notes
114
+ - Default enforcement is unchanged: a `BLOCK` verdict still raises `GovernanceBlockedError`. The new
115
+ helper is opt-in and never triggers an automatic retry; malformed or ineligible plans are treated
116
+ as absent (never fail open).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenBox AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,13 +1,13 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: openbox-sdk-python
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: OpenBox base SDK - governance contracts, strict gate, identity/signing, evaluate client, context runtime, OTel span wire serialization, and generic instrumentation shared by every OpenBox framework SDK
5
5
  Author-email: OpenBox Team <tino@openbox.ai>
6
6
  License-Expression: MIT
7
+ License-File: LICENSE
7
8
  Keywords: governance,observability,openbox,opentelemetry,sdk
8
9
  Classifier: Development Status :: 3 - Alpha
9
10
  Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
@@ -46,10 +46,14 @@ framework SDK builds on. It owns:
46
46
  `EvaluationResult`, `ApprovalResult`, `Verdict`).
47
47
  - **Always-strict governance gate** — malformed event/runtime contracts raise
48
48
  `ContractError` before send; there are no configurable gate modes.
49
- - **Identity & signing** — AIP DID validation + Ed25519 request signing,
50
- byte-compatible with the Temporal SDK signing contract.
51
- - **Evaluate client** — sync + async calls to OpenBox Core
52
- (`/api/v1/governance/evaluate`, `/approval`, `/auth/validate`).
49
+ - **Identity & signing** — v1 OpenBox DID: AIP DID validation + Ed25519 request
50
+ signing, byte-compatible with the Temporal SDK signing contract. v2 Okta AI
51
+ Agent: tagged identity configuration + RS256 JWT assertion signing
52
+ (`openbox_core.identity_okta`, `openbox_core.identity_types`).
53
+ - **Evaluate client** — sync + async calls to OpenBox Core, routed by
54
+ configured identity: `/api/v1/governance/evaluate` (`/approval`,
55
+ `/auth/validate`, `/handoffs`) for OpenBox DID / inferred unsigned mode, or
56
+ the `/api/v2/*` equivalents for Okta AI Agent — no cross-version retry.
53
57
  - **Context & runtime** — `ActivityContext`, `ContextStore`, trace
54
58
  correlation, `FrameworkAdapter` protocol, `OpenBoxRuntime`.
55
59
  - **OTel span wire serialization** — OTel spans are the internal source of
@@ -78,6 +82,64 @@ uv sync --extra http # + HTTP instrumentation targets
78
82
  uv sync --extra db # + DB instrumentation targets
79
83
  ```
80
84
 
85
+ ## Okta AI Agent identity (`okta_ai_agent`)
86
+
87
+ An agent whose OpenBox identity is verified against an Okta AI Agent credential
88
+ signs every request with an RS256 assertion. That assertion binds seven values
89
+ OpenBox Core already owns, so the runtime does **not** configure them — it fetches
90
+ them from Core after authenticating with its API key:
91
+
92
+ ```dotenv
93
+ OPENBOX_API_URL=https://core.example.com
94
+ OPENBOX_API_KEY=obx_live_...
95
+ OPENBOX_OKTA_AGENT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
96
+ ...
97
+ -----END PRIVATE KEY-----"
98
+ ```
99
+
100
+ That is the complete configuration. The private key is the only value Core can
101
+ never supply — **OpenBox never receives, stores, or returns it.** Everything else
102
+ (agent id, organization id, deployment id, assertion audience, external Okta agent
103
+ id, credential `kid`, algorithm) comes from `GET /api/v2/auth/bootstrap` on first
104
+ use and is cached in memory for the lifetime of the client.
105
+
106
+ Before the first governed request the SDK derives its private key's RFC 7638
107
+ public-key thumbprint and compares it with the one Core reports for the agent's
108
+ selected credential. A mismatch raises `OpenBoxConfigError` immediately, rather
109
+ than sending an assertion that could only be rejected:
110
+
111
+ > The configured private key does not match the selected Okta credential for this
112
+ > OpenBox agent. Export the private key associated with the selected credential,
113
+ > or rotate the agent credential.
114
+
115
+ **Credential rotation.** Long-running agents can refresh explicitly:
116
+
117
+ ```python
118
+ document = client.refresh_identity_metadata() # async: await client.arefresh_identity_metadata()
119
+ print(document.okta.credential_kid)
120
+ print(document.authority.activation_version)
121
+ ```
122
+
123
+ The document also carries required, non-secret assignment, provider-generation,
124
+ activation, credential, and projection identifiers. The SDK fails closed when an
125
+ IAM-v3 Core deployment omits or returns malformed authority metadata.
126
+ The refresh re-runs the thumbprint check *before* replacing cached metadata, so a
127
+ credential that rotated to a key this process does not hold fails loudly and leaves
128
+ the client on its previous identity. The SDK never refreshes automatically after an
129
+ auth failure: rotation may have selected a new public key while the process still
130
+ holds the old private key, and a silent retry would hide that rather than fix it.
131
+
132
+ **Requirements.** The key must be a PKCS8 PEM RSA key of at least 2048 bits, and
133
+ its public half must already be registered in Okta for the selected credential.
134
+
135
+ **Explicit configuration** (every metadata field set locally) remains supported for
136
+ compatibility. The two styles cannot be mixed: a configuration carrying only *some*
137
+ metadata fields raises `OpenBoxConfigError` naming the offending fields, rather than
138
+ quietly merging stale local values over what Core would have supplied. If Core
139
+ answers `404`, the SDK reports that the deployment predates bootstrap and asks you
140
+ to upgrade Core or supply the complete explicit configuration — it never downgrades
141
+ to an unsigned request or to a different identity method.
142
+
81
143
  ## Import safety
82
144
 
83
145
  `openbox_core.__init__` and all `openbox_core.contracts.*` modules import
@@ -85,6 +147,9 @@ without pulling in `httpx`, `cryptography`, `requests`, or OTel
85
147
  instrumentation — safe for constrained framework paths such as the Temporal
86
148
  workflow sandbox. `tests/test_import_safety.py` enforces this in CI.
87
149
 
150
+ `openbox_core.bootstrap` and `openbox_core.jwk_thumbprint` follow the same rule —
151
+ both keep `httpx`/`cryptography` behind function-local imports.
152
+
88
153
  ## Development
89
154
 
90
155
  ```bash
@@ -92,3 +157,7 @@ uv sync --all-extras
92
157
  uv run pytest
93
158
  uv run ruff check .
94
159
  ```
160
+
161
+ ## License
162
+
163
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,124 @@
1
+ # openbox-sdk-python
2
+
3
+ OpenBox base SDK (`import openbox_core`) — the standalone core every OpenBox
4
+ framework SDK builds on. It owns:
5
+
6
+ - **Contracts** — event and result dataclasses (`EventEnvelope`,
7
+ `EvaluationResult`, `ApprovalResult`, `Verdict`).
8
+ - **Always-strict governance gate** — malformed event/runtime contracts raise
9
+ `ContractError` before send; there are no configurable gate modes.
10
+ - **Identity & signing** — v1 OpenBox DID: AIP DID validation + Ed25519 request
11
+ signing, byte-compatible with the Temporal SDK signing contract. v2 Okta AI
12
+ Agent: tagged identity configuration + RS256 JWT assertion signing
13
+ (`openbox_core.identity_okta`, `openbox_core.identity_types`).
14
+ - **Evaluate client** — sync + async calls to OpenBox Core, routed by
15
+ configured identity: `/api/v1/governance/evaluate` (`/approval`,
16
+ `/auth/validate`, `/handoffs`) for OpenBox DID / inferred unsigned mode, or
17
+ the `/api/v2/*` equivalents for Okta AI Agent — no cross-version retry.
18
+ - **Context & runtime** — `ActivityContext`, `ContextStore`, trace
19
+ correlation, `FrameworkAdapter` protocol, `OpenBoxRuntime`.
20
+ - **OTel span wire serialization** — OTel spans are the internal source of
21
+ truth, projected to the current Core `SpanData` wire shape before send.
22
+ - **Generic instrumentation** — HTTP/DB/file/function wrappers with a
23
+ started/completed hook runtime enforcing preflight before real operations.
24
+ - **Conformance kit** — reusable fixtures framework SDKs run to prove parity.
25
+
26
+ Framework SDKs (Temporal, LangGraph, LangChain, DeepAgent, CrewAI) are thin
27
+ adapters: they bind framework lifecycle into `ActivityContext`, emit lifecycle
28
+ events, and translate `EvaluationResult` into native behavior.
29
+
30
+ ## Framework SDK integration
31
+
32
+ New framework SDK developers and agents should start with
33
+ [the OpenBox Python Framework SDK Integration guide](.github/instructions/openbox-sdk-python.instructions.md).
34
+ It describes how to build a framework SDK on top of this base package without
35
+ reimplementing config, signing, governance gates, hook wire payloads, or
36
+ conformance fixtures.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ uv sync # core
42
+ uv sync --extra http # + HTTP instrumentation targets
43
+ uv sync --extra db # + DB instrumentation targets
44
+ ```
45
+
46
+ ## Okta AI Agent identity (`okta_ai_agent`)
47
+
48
+ An agent whose OpenBox identity is verified against an Okta AI Agent credential
49
+ signs every request with an RS256 assertion. That assertion binds seven values
50
+ OpenBox Core already owns, so the runtime does **not** configure them — it fetches
51
+ them from Core after authenticating with its API key:
52
+
53
+ ```dotenv
54
+ OPENBOX_API_URL=https://core.example.com
55
+ OPENBOX_API_KEY=obx_live_...
56
+ OPENBOX_OKTA_AGENT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
57
+ ...
58
+ -----END PRIVATE KEY-----"
59
+ ```
60
+
61
+ That is the complete configuration. The private key is the only value Core can
62
+ never supply — **OpenBox never receives, stores, or returns it.** Everything else
63
+ (agent id, organization id, deployment id, assertion audience, external Okta agent
64
+ id, credential `kid`, algorithm) comes from `GET /api/v2/auth/bootstrap` on first
65
+ use and is cached in memory for the lifetime of the client.
66
+
67
+ Before the first governed request the SDK derives its private key's RFC 7638
68
+ public-key thumbprint and compares it with the one Core reports for the agent's
69
+ selected credential. A mismatch raises `OpenBoxConfigError` immediately, rather
70
+ than sending an assertion that could only be rejected:
71
+
72
+ > The configured private key does not match the selected Okta credential for this
73
+ > OpenBox agent. Export the private key associated with the selected credential,
74
+ > or rotate the agent credential.
75
+
76
+ **Credential rotation.** Long-running agents can refresh explicitly:
77
+
78
+ ```python
79
+ document = client.refresh_identity_metadata() # async: await client.arefresh_identity_metadata()
80
+ print(document.okta.credential_kid)
81
+ print(document.authority.activation_version)
82
+ ```
83
+
84
+ The document also carries required, non-secret assignment, provider-generation,
85
+ activation, credential, and projection identifiers. The SDK fails closed when an
86
+ IAM-v3 Core deployment omits or returns malformed authority metadata.
87
+ The refresh re-runs the thumbprint check *before* replacing cached metadata, so a
88
+ credential that rotated to a key this process does not hold fails loudly and leaves
89
+ the client on its previous identity. The SDK never refreshes automatically after an
90
+ auth failure: rotation may have selected a new public key while the process still
91
+ holds the old private key, and a silent retry would hide that rather than fix it.
92
+
93
+ **Requirements.** The key must be a PKCS8 PEM RSA key of at least 2048 bits, and
94
+ its public half must already be registered in Okta for the selected credential.
95
+
96
+ **Explicit configuration** (every metadata field set locally) remains supported for
97
+ compatibility. The two styles cannot be mixed: a configuration carrying only *some*
98
+ metadata fields raises `OpenBoxConfigError` naming the offending fields, rather than
99
+ quietly merging stale local values over what Core would have supplied. If Core
100
+ answers `404`, the SDK reports that the deployment predates bootstrap and asks you
101
+ to upgrade Core or supply the complete explicit configuration — it never downgrades
102
+ to an unsigned request or to a different identity method.
103
+
104
+ ## Import safety
105
+
106
+ `openbox_core.__init__` and all `openbox_core.contracts.*` modules import
107
+ without pulling in `httpx`, `cryptography`, `requests`, or OTel
108
+ instrumentation — safe for constrained framework paths such as the Temporal
109
+ workflow sandbox. `tests/test_import_safety.py` enforces this in CI.
110
+
111
+ `openbox_core.bootstrap` and `openbox_core.jwk_thumbprint` follow the same rule —
112
+ both keep `httpx`/`cryptography` behind function-local imports.
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ uv sync --all-extras
118
+ uv run pytest
119
+ uv run ruff check .
120
+ ```
121
+
122
+ ## License
123
+
124
+ MIT — see [`LICENSE`](LICENSE).
@@ -37,7 +37,7 @@ from .errors import (
37
37
  # governance; eagerly it can deadlock package init as a circular import, and
38
38
  # lazily it can recurse unboundedly when a per-request header builder resolves
39
39
  # the version. Keep in sync with pyproject.toml on release.
40
- __version__ = "1.2.0"
40
+ __version__ = "1.3.0"
41
41
 
42
42
  __all__ = [
43
43
  "__version__",