openbadgeslib 3.9.0__tar.gz → 3.11.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 (135) hide show
  1. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/Changelog.txt +120 -0
  2. {openbadgeslib-3.9.0/openbadgeslib.egg-info → openbadgeslib-3.11.0}/PKG-INFO +4 -2
  3. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/__init__.py +24 -9
  4. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/badge.py +4 -4
  5. openbadgeslib-3.11.0/openbadgeslib/issue.py +516 -0
  6. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob1/__init__.py +15 -14
  7. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/__init__.py +5 -2
  8. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/__init__.py +11 -0
  9. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/credential.py +188 -14
  10. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/ldp.py +98 -9
  11. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/openbadges_publish.py +74 -6
  12. openbadgeslib-3.11.0/openbadgeslib/openbadges_signer.py +473 -0
  13. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/openbadges_verifier.py +25 -3
  14. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/signer.py +4 -4
  15. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/util.py +1 -1
  16. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/verifier.py +4 -4
  17. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0/openbadgeslib.egg-info}/PKG-INFO +4 -2
  18. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib.egg-info/SOURCES.txt +11 -0
  19. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib.egg-info/requires.txt +4 -1
  20. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/pyproject.toml +16 -4
  21. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/conformance/test_official_validators.py +71 -0
  22. openbadgeslib-3.11.0/tests/test_baking.py +104 -0
  23. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_cli_json.py +35 -0
  24. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_cli_smoke.py +2 -1
  25. openbadgeslib-3.11.0/tests/test_examples.py +31 -0
  26. openbadgeslib-3.11.0/tests/test_issue.py +252 -0
  27. openbadgeslib-3.11.0/tests/test_keys.py +70 -0
  28. openbadgeslib-3.11.0/tests/test_mail.py +85 -0
  29. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob1_deprecation.py +14 -13
  30. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob1_signer.py +1 -1
  31. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob2_cli.py +46 -6
  32. openbadgeslib-3.11.0/tests/test_ob2_error_branches.py +173 -0
  33. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_conformance_schema.py +31 -0
  34. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_credential.py +105 -1
  35. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_ldp.py +6 -2
  36. openbadgeslib-3.11.0/tests/test_ob3_ldp_ecdsa_sd.py +142 -0
  37. openbadgeslib-3.11.0/tests/test_ob3_ldp_errors.py +123 -0
  38. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_publish_cli.py +82 -0
  39. openbadgeslib-3.11.0/tests/test_signer_batch.py +182 -0
  40. openbadgeslib-3.11.0/tests/test_verifier_cli_coverage.py +284 -0
  41. openbadgeslib-3.9.0/openbadgeslib/openbadges_signer.py +0 -500
  42. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/LICENSE.txt +0 -0
  43. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/MANIFEST.in +0 -0
  44. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/README.md +0 -0
  45. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/SECURITY.md +0 -0
  46. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/docs/README.md +0 -0
  47. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/_jws/__init__.py +0 -0
  48. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/_jws/exceptions.py +0 -0
  49. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/_jws/utils.py +0 -0
  50. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/baking.py +0 -0
  51. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/config.ini.example +0 -0
  52. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/confparser.py +0 -0
  53. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/errors.py +0 -0
  54. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/keys.py +0 -0
  55. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/logs.py +0 -0
  56. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/mail.py +0 -0
  57. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob1/badge.py +0 -0
  58. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob1/signer.py +0 -0
  59. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob1/verifier.py +0 -0
  60. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob2/__init__.py +0 -0
  61. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob2/models.py +0 -0
  62. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob2/signer.py +0 -0
  63. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob2/verifier.py +0 -0
  64. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/credentials-v2.json +0 -0
  65. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/ob-v3p0-context-3.0.1.json +0 -0
  66. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/ob-v3p0-context-3.0.2.json +0 -0
  67. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/ob-v3p0-context-3.0.3.json +0 -0
  68. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/security-data-integrity-v2.json +0 -0
  69. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/contexts/security-multikey-v1.json +0 -0
  70. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/did.py +0 -0
  71. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/eudi.py +0 -0
  72. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/signer.py +0 -0
  73. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/status.py +0 -0
  74. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/status_list.py +0 -0
  75. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/status_registry.py +0 -0
  76. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/ob3/verifier.py +0 -0
  77. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/openbadges_init.py +0 -0
  78. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/openbadges_keygenerator.py +0 -0
  79. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib/py.typed +0 -0
  80. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib.egg-info/dependency_links.txt +0 -0
  81. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib.egg-info/entry_points.txt +0 -0
  82. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/openbadgeslib.egg-info/top_level.txt +0 -0
  83. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/setup.cfg +0 -0
  84. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/config1.ini +0 -0
  85. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/conformance/Dockerfile.ob3 +0 -0
  86. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/conformance/README.md +0 -0
  87. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/conformance/conftest.py +0 -0
  88. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/conformance/docker-compose.yml +0 -0
  89. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/conftest.py +0 -0
  90. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/ob_schemas/README.md +0 -0
  91. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/ob_schemas/ob_v3p0_achievementcredential_schema.json +0 -0
  92. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/ob_schemas/ob_v3p0_profile_schema.json +0 -0
  93. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/ob_schemas/refresh.sh +0 -0
  94. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/vc_di_eddsa/README.md +0 -0
  95. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/vc_di_eddsa/credentials-examples-v2.json +0 -0
  96. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/vc_di_eddsa/doc-hash.txt +0 -0
  97. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/vc_di_eddsa/key-pair.json +0 -0
  98. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/vc_di_eddsa/proof-config-hash.txt +0 -0
  99. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/fixtures/vc_di_eddsa/signed-credential.json +0 -0
  100. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/images/sample1.png +0 -0
  101. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/images/sample1.svg +0 -0
  102. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/images/userimage01.svg +0 -0
  103. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/ldp_helpers.py +0 -0
  104. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/logo Python Espan/314/203a.svg" +0 -0
  105. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/logo Python Espa/303/261a.svg" +0 -0
  106. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_confparser.py +0 -0
  107. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_docs.py +0 -0
  108. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_eddsa.py +0 -0
  109. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_jws.py +0 -0
  110. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_key_operation.py +0 -0
  111. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob1_badge_io.py +0 -0
  112. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob1_verifier.py +0 -0
  113. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob2_models.py +0 -0
  114. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob2_signer.py +0 -0
  115. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob2_verifier.py +0 -0
  116. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_contexts.py +0 -0
  117. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_did.py +0 -0
  118. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_did_doc.py +0 -0
  119. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_endorsement.py +0 -0
  120. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_eudi_sd_jwt.py +0 -0
  121. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_ldp_cli.py +0 -0
  122. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_ldp_sign.py +0 -0
  123. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_signer.py +0 -0
  124. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_signer_cli_ldp.py +0 -0
  125. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_status.py +0 -0
  126. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_status_list.py +0 -0
  127. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_status_registry.py +0 -0
  128. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_ob3_verifier.py +0 -0
  129. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_sign_ecc.pem +0 -0
  130. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_sign_rsa.pem +0 -0
  131. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_util.py +0 -0
  132. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_verify_ecc.pem +0 -0
  133. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/test_verify_rsa.pem +0 -0
  134. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/withoutxmlheader.svg +0 -0
  135. {openbadgeslib-3.9.0 → openbadgeslib-3.11.0}/tests/withxmlheader.svg +0 -0
@@ -4,6 +4,126 @@ OpenBadgesLib - Changelog
4
4
  Newest first. Dates are ISO 8601 (YYYY-MM-DD).
5
5
 
6
6
 
7
+ * v3.11.0 - 2026-07-09
8
+
9
+ - feat(ldp): verify OpenBadges 3.0 credentials secured with the
10
+ **ecdsa-sd-2023** (selective disclosure) Data Integrity cryptosuite — the
11
+ verify-only slice of #171. A derived proof is verified by delegating the
12
+ large, security-sensitive selective-disclosure crypto (CBOR base/derived
13
+ proofs, HMAC label maps, per-statement P-256 signatures) to openvc-core's
14
+ audited `EcdsaSdProofSuite`; openbadgeslib keeps the OB3 model, the trust
15
+ binding (proof `verificationMethod` ↔ credential issuer) and the lifecycle
16
+ checks around it, and hands the delegate its pinned OB3 `@context`
17
+ allowlist so canonicalization stays fail-closed and never touches the
18
+ network. It plugs into the `_CRYPTOSUITES` registry beside eddsa-rdfc-2022,
19
+ so `OB3LdpVerifier` accepts both transparently. Verify-only for Displayer
20
+ parity (1EdTech OB3 certification requires verifying both suites); issuing
21
+ ecdsa-sd-2023 stays out of scope. Needs the new `[ldp-sd]` extra
22
+ (`openvc-core[data-integrity]`, self-contained — pulls its own pyld); a
23
+ missing extra fails closed with an actionable install hint. (Closes #171)
24
+
25
+ * v3.10.0 - 2026-07-08
26
+
27
+ - feat(publish): `openbadges-publish -V 3 --check-live` completes #164. After
28
+ (re)publishing, it downloads each written artifact — `did.json`, the signed
29
+ status lists, `verify.pem` — from `publish_url` and byte-compares it against
30
+ the local copy, turning the "re-upload so the change takes effect" reminder
31
+ into a verifiable guarantee. Exit 2 (`--json`, listed in `live_check.stale`)
32
+ or 1 (human) if any artifact is stale or missing on the server. The rest of
33
+ #164 (status-list `validUntil` + opt-in list-proof verification + issuer
34
+ binding) shipped in v3.8.0. (Closes #164)
35
+
36
+ - docs(examples): #168 library integration tutorial + runnable examples. New
37
+ `examples/` scripts — issue+verify an OB 3.0 JWT-VC, issue with a Data
38
+ Integrity (LDP) proof, and issue from a config section via the high-level API
39
+ (single + batch) — each self-contained and executed in CI (`test_examples.py`,
40
+ anti-drift, same philosophy as `test_docs.py`). Two new wiki pages: the
41
+ "Library Integration Tutorial" narrating them end to end (issue → sign →
42
+ publish → revoke → verify) and a "Certification Cookbook" on passing the
43
+ official 1EdTech OB 3.0 conformance tests without the paid membership.
44
+ (Closes #168)
45
+
46
+ - test(coverage): #169 targeted-coverage round. New dedicated suites for the
47
+ shared token carrier (`test_baking.py` — embed/extract round-trips per
48
+ format, compression and malformed-iTXt edges), mail (`test_mail.py` — SMTP
49
+ mocked) and the key helpers (`test_keys.py`), plus error-branch coverage for
50
+ the OB2 signer/verifier, the OB3 Data Integrity (LDP) path and the verifier
51
+ CLI (`openbadges_verifier.py` 74% → 95%). Total coverage crosses 93% and the
52
+ enforced `fail_under` floor is raised from 91 to 93. An opt-in OB2
53
+ **SignedBadge** conformance case joins the existing hosted one (the
54
+ crypto-sensitive path, Docker-gated). (Closes #169)
55
+
56
+ - docs(ob1): OpenBadges 1.0 is reclassified from "deprecated, removal in
57
+ 4.0.0" to a **supported legacy surface with no removal planned**. Removing a
58
+ working, dependency-free legacy path (OB1 has carried no unique dependencies
59
+ since #167) was judged excessive; OB1 stays fully functional and maintained,
60
+ simply superseded by OB 2.0/3.0 for new work. The library
61
+ `DeprecationWarning`s and the `-V 1` CLI notices are reworded to steer new
62
+ work to OB2/OB3 without promising removal, and the "OpenBadges 1.0
63
+ lifecycle" wiki page records the policy — including why a `[legacy]`/`[ob1]`
64
+ install extra stays rejected: a Python extra gates *dependencies*, not code,
65
+ and OB1 has none to gate (it would install the same bytes). Reverses the
66
+ removal side of #159/#170.
67
+
68
+ - feat(cli): batch issuance — `openbadges-signer` now issues to many recipients
69
+ in one run. Repeat `-r EMAIL`, or pass `--recipients-file FILE` (one email per
70
+ line or comma-separated; blank lines and `#`-comments ignored); a single `-r`
71
+ keeps the historical single-badge behaviour byte-for-byte. For a revocable OB3
72
+ badge every status-list index is allocated in a SINGLE registry transaction
73
+ (load once, allocate N, save once) rather than N load/save cycles — the piece
74
+ that makes thousands-scale issuance practical. An already-existing output file
75
+ is skipped (batch) or aborts (single) unless the new `--force`/`--overwrite`
76
+ overwrites it, and one bad recipient does not abort the rest. `--json` emits a
77
+ per-recipient summary `{signed, skipped, failed}` under the shared exit
78
+ contract (0 all signed, 2 some skipped/failed, 1 a batch-level error). The
79
+ library entry point is `openbadgeslib.issue.issue_batch_from_conf`, returning
80
+ one `BatchResult` per recipient. (Closes #165)
81
+
82
+ - feat(api): extract the issuance orchestration from the CLI into a reusable
83
+ library API — the single biggest adoption gap. `openbadgeslib.issue` exposes
84
+ `issue_from_conf(conf, badge, recipient, ob_version, ...)` (also re-exported
85
+ as `openbadgeslib.issue_from_conf`), returning a `SignResult` dataclass with
86
+ the signed badge bytes and metadata and doing no user-facing I/O — no
87
+ prints, no sys.exit, no file write; a config or policy problem raises
88
+ `IssuanceError`. The ~230 lines of business logic that lived in
89
+ openbadges-signer (credential construction from config, salt generation, the
90
+ OB2 hosted-vs-signed decision, the OB3 status-registry→sign transactional
91
+ order and the Data Integrity verificationMethod policy — did:web trusted vs
92
+ a derived self-asserted did:key) now live there and are unit-tested
93
+ directly, not only reachable by driving the CLI. The signer entrypoints are
94
+ reduced to flag parsing, I/O and display over that API; behaviour — including
95
+ the historical per-version exit conventions — is unchanged. Prerequisite for
96
+ batch signing (#165) and any unified CLI. OpenBadges 1.0 issuance stays
97
+ CLI-only (it is deprecated). (Closes #160)
98
+
99
+ - feat(ob3): complete the broadened OB 3.0 credential model started in
100
+ v3.9.0 — closing the remaining data-model gap versus the spec. New `Result`
101
+ (a measured outcome: value, status, achievedLevel and the linked
102
+ resultDescription), `ResultDescription` (an achievement's possible results:
103
+ resultType, allowedValue, valueMin/valueMax, requiredValue/requiredLevel)
104
+ and `IdentityObject` (a hashed or plaintext `credentialSubject.identifier`,
105
+ the alternative to `credentialSubject.id`) dataclasses, plus `creditsEarned`
106
+ on the subject and a `resultDescription` list on `Achievement`. All follow
107
+ the house style (dataclasses + mypy, no Pydantic), parse from a verified
108
+ credential, round-trip through `to_vc`, and satisfy the offline conformance
109
+ schema — a fully-populated credential (identifier-only subject, result +
110
+ linked resultDescription, alignment, credits) validates against 1EdTech's
111
+ AchievementCredential schema. `openbadges-verifier` now surfaces the
112
+ broadened fields — achievementType, creditsAvailable/creditsEarned and the
113
+ alignment/result/identifier counts — in both `--json` and `--show`. Two
114
+ round-trip fixes fall out: a subject conveying identity solely through
115
+ `identifier` now re-serialises with it (the identifier was silently dropped
116
+ on parse before), and a lone `alignment` object not wrapped in an array is
117
+ no longer discarded. (Closes #162)
118
+
119
+ - fix(cli): `openbadges-publish -V 1/-V 2` now exits with a clear message when
120
+ the `[issuer]` section is missing `publish_url` or `revocationList`, instead
121
+ of raising a raw `KeyError` traceback partway through — after the output
122
+ directory had already been created. The OB3 path already validated this; the
123
+ hosted OB1/OB2 paths now share the same guard. Last item of the quick-fix
124
+ round. (Closes #156)
125
+
126
+
7
127
  * v3.9.0 - 2026-07-08
8
128
 
9
129
  - feat(ob3): broaden the credential data model toward the OB 3.0 spec — the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openbadgeslib
3
- Version: 3.9.0
3
+ Version: 3.11.0
4
4
  Summary: Production-ready Python library & CLI for Open Badges 3.0 (W3C Verifiable Credentials): issue, verify, and revoke JWT-VC credentials with Bitstring Status Lists and did:web — plus strict OB 2.0 and legacy OB 1.0.
5
5
  Author-email: Luis González Fernández <luisgf@luisgf.es>, Jesús Cea Avión <jcea@jcea.es>
6
6
  License: LGPLv3
@@ -36,7 +36,9 @@ Requires-Dist: defusedxml>=0.7
36
36
  Provides-Extra: ldp
37
37
  Requires-Dist: pyld>=2.0.4; extra == "ldp"
38
38
  Provides-Extra: eudi
39
- Requires-Dist: openvc-core<2,>=1.8; extra == "eudi"
39
+ Requires-Dist: openvc-core<2,>=1.13; extra == "eudi"
40
+ Provides-Extra: ldp-sd
41
+ Requires-Dist: openvc-core[data-integrity]<2,>=1.13; extra == "ldp-sd"
40
42
  Provides-Extra: dev
41
43
  Requires-Dist: pytest>=8.0; extra == "dev"
42
44
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
@@ -39,13 +39,23 @@ from .keys import KeyFactory, KeyRSA, KeyECC # noqa: F401
39
39
  from .util import __version__ # noqa: F401
40
40
 
41
41
 
42
+ # ── Issuance API ─────────────────────────────────────────────────────────────
43
+ # "Issue badge X to Y per config" as a library call — the orchestration the CLI
44
+ # wraps, returning a SignResult instead of writing files (openbadgeslib.issue).
45
+ # Resolved lazily (PEP 562, below) because openbadgeslib.issue pulls in the
46
+ # shared Badge model from the ob1 leaf module; a bare `import openbadgeslib`
47
+ # must not drag that in. `from openbadgeslib.issue import ...` works directly.
48
+ _ISSUE_API = ('IssuanceError', 'SignResult', 'issue_from_conf')
49
+
50
+
42
51
  # ── OpenBadges 1.0 (legacy) ──────────────────────────────────────────────────
43
52
  # The unprefixed OB1 names (Signer, Verifier, Badge, …) stay importable from
44
- # the top-level package for backward compatibility, but they are the legacy
45
- # OpenBadges 1.0 surface: accessing one now emits a DeprecationWarning
46
- # (removal in 4.0.0). Modern code uses openbadgeslib.ob2 / openbadgeslib.ob3.
47
- # They are resolved lazily from the ob1 leaf modules (PEP 562), so a bare
48
- # `import openbadgeslib` neither warns nor drags in the ob1 package.
53
+ # the top-level package for backward compatibility. They are the legacy
54
+ # OpenBadges 1.0 surface: accessing one emits a DeprecationWarning steering new
55
+ # work to openbadgeslib.ob2 / openbadgeslib.ob3. OB 1.0 itself remains supported
56
+ # (no removal planned). They are resolved lazily from the ob1 leaf modules (PEP
57
+ # 562), so a bare `import openbadgeslib` neither warns nor drags in the ob1
58
+ # package.
49
59
  _OB1_API = {
50
60
  'Signer': 'signer', 'Verifier': 'verifier', 'VerifyInfo': 'verifier',
51
61
  'Badge': 'badge', 'BadgeSigned': 'badge', 'Assertion': 'badge',
@@ -55,6 +65,11 @@ _OB1_API = {
55
65
 
56
66
 
57
67
  def __getattr__(name: str) -> Any:
68
+ if name in _ISSUE_API:
69
+ # The modern issuance API — lazy so a bare import stays ob1-free, but
70
+ # warning-free (unlike the OB1 names below).
71
+ import importlib
72
+ return getattr(importlib.import_module('.issue', __name__), name)
58
73
  module = _OB1_API.get(name)
59
74
  if module is None:
60
75
  raise AttributeError(
@@ -62,9 +77,9 @@ def __getattr__(name: str) -> Any:
62
77
  import importlib
63
78
  import warnings
64
79
  warnings.warn(
65
- 'openbadgeslib.%s is the legacy OpenBadges 1.0 API and is deprecated; '
66
- 'it will be removed in openbadgeslib 4.0.0. Use openbadgeslib.ob2 '
67
- '(strict OB 2.0) or openbadgeslib.ob3 (OB 3.0). See the "OpenBadges '
68
- '1.0 lifecycle" wiki page.' % name,
80
+ 'openbadgeslib.%s is the legacy OpenBadges 1.0 API; prefer '
81
+ 'openbadgeslib.ob2 (strict OB 2.0) or openbadgeslib.ob3 (OB 3.0) for '
82
+ 'new work. OpenBadges 1.0 remains supported (no removal planned). See '
83
+ 'the "OpenBadges 1.0 lifecycle" wiki page.' % name,
69
84
  DeprecationWarning, stacklevel=2)
70
85
  return getattr(importlib.import_module('.ob1.' + module, __name__), name)
@@ -8,10 +8,10 @@ from .ob1.badge import (
8
8
  )
9
9
 
10
10
  warnings.warn(
11
- 'openbadgeslib.badge is a legacy OpenBadges 1.0 compatibility shim and is '
12
- 'deprecated; it will be removed in openbadgeslib 4.0.0. Import from '
13
- 'openbadgeslib.ob2 (strict OB 2.0) or openbadgeslib.ob3 (OB 3.0) instead. '
14
- 'See the "OpenBadges 1.0 lifecycle" wiki page.',
11
+ 'openbadgeslib.badge is a legacy OpenBadges 1.0 compatibility shim; prefer '
12
+ 'openbadgeslib.ob2 (strict OB 2.0) or openbadgeslib.ob3 (OB 3.0) for new '
13
+ 'work. OpenBadges 1.0 remains supported (no removal planned). See the '
14
+ '"OpenBadges 1.0 lifecycle" wiki page.',
15
15
  DeprecationWarning, stacklevel=2)
16
16
 
17
17
  __all__ = [