| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A production-ready Python library & CLI for the full Open Badges 3.0 issuer lifecycle — issue W3C Verifiable Credentials as JWT-VC or W3C Data Integrity (LDP) proofs, bake them into SVG/PNG, verify them, and revoke or suspend them with W3C Bitstring Status Lists and did:web. It also ships strict OpenBadges 2.0 (JWS / hosted assertions) and a frozen OpenBadges 1.0 legacy format, selected with -V {1,2,3} (default 3).
4.0.0 is released (2026-07-22). It groups the breaking changes so upgrades stay rare: every CLI unifies on one 0/1/2 exit-code contract (a valid-but-untrusted badge now exits 2, and the 255s are gone), and the legacy pycryptodome/python-ecdsa compat shim is removed — a live key object from either is no longer accepted, though key files are plain PEM and round-trip unchanged. OpenBadges 1.0 and the openbadges-<command> scripts stay, and a new unified openbadges <command> front-end joins them. Python 3.10 is still supported (requires-python >= 3.10, tested in CI on 3.10–3.14); dropping it is deferred to its end of life. See Upgrading to 4.0 before pinning a major.
Best-effort comparison from each project's public documentation as of July 2026; "not documented" is shown as —. Corrections welcome via issue/PR. The Python Open Badges landscape splits into three groups: 1EdTech's verify-only OB 2.0 reference validator, heavyweight Django server platforms (the Badgr lineage), and a thin tail of standalone libraries — of which openbadgeslib is the only actively-maintained, pip-installable, offline one that covers all three OB versions, both native OB 3.0 proof formats (VC-JWT and Data Integrity), and an additive EUDI SD-JWT VC wallet track.
| Capability | openbadgeslib | validator-core | Badgr / open-educational-badges (a) | pyopenbadges | didkit (b) |
|---|---|---|---|---|---|
| Open Badges versions | 1.0, 2.0, 3.0 | 0.5–2.0 | 1.1, 2.0, 3.0 | 3.0 only | generic VC |
| Issue / verify | ✅ both | verify only | issue; partial verify | both | both (VC + VP) |
| OB 3.0 proof: VC-JWT (JOSE) | ✅ RS/ES/EdDSA | — | — | roadmap | ✅ not OB-aware |
| OB 3.0 proof: Data Integrity / LDP | ✅ issue + verify (eddsa-rdfc-2022) | — | issue only (Ed25519) | home-grown, non-conformant | older suites; no rdfc-2022 in wheel |
| OB 3.0 as EUDI SD-JWT VC (selective disclosure) | ✅ issue + verify (Ed25519/P-256/P-384, [eudi] extra) | — | — | — | — |
| Revocation / suspension | ✅ W3C Bitstring Status List | hosted check | 1EdTechRevocationList | ad-hoc flag | — |
| did:web (generate + resolve) | ✅ | — | — | — | resolve only |
| Image baking (SVG + PNG) | ✅ | unbake only | ✅ | — | — |
| Form factor | library + CLI (openbadges + 5 aliases) | library + CLI | Django server | library | binding |
| Typing / CI | mypy --strict, CI 3.10–3.14 | — | — | Pydantic | — |
| License | LGPLv3 / BSD-2 | Apache-2.0 | AGPL-3.0 | MIT/LGPL | Apache-2.0 |
(a) The actively-maintained community fork (open-educational-badges): it genuinely issues OB 3.0 Data Integrity credentials, but only as a server (not a pip library) and only with Ed25519 (no VC-JWT). The classic Concentric Sky badgr-server is OB 2.0 and its canonical repo is gone; SURF's edubadges-server delegates OB 3.0 signing to external agents. (b) Generic W3C VC/DID toolkit (Rust ssi bindings), not Open Badges-aware, and archived (read-only since July 2025).
For authoritative OB 2.0 validation semantics, 1EdTech's Python openbadges-validator-core and Node openbadges-validator remain the reference; openbadgeslib focuses on the full issuer lifecycle across all three OB versions.
pip install openbadgeslibAll dependencies are installed automatically. The two OB 3.0 side-tracks ship as optional extras (the base install stays lean):
pip install "openbadgeslib[ldp]" # W3C Data Integrity issuance/verification
pip install "openbadgeslib[ldp-sd]" # verify ecdsa-sd-2023 (selective disclosure)
pip install "openbadgeslib[eudi]" # EUDI SD-JWT VC (pulls openvc-core)
pip install "openbadgeslib[oid4vci]" # issue to wallets (pre-authorized code flow)For a development checkout with the test suite and linters:
pip install -e ".[dev]"# 1. Initialize a configuration directory
openbadges-init ./config/
# 2. Generate a key pair for a badge
openbadges-keygenerator -c ./config/config.ini -g 1
# 3a. Sign a badge — OpenBadges 3.0 (default)
openbadges-signer -c ./config/config.ini -b 1 -r recipient@example.com -o /tmp/ -E
# 3b. Sign a badge — strict OpenBadges 2.0
openbadges-signer -c ./config/config.ini -b 1 -r recipient@example.com -o /tmp/ -E -V 2
# 4a. Verify — OpenBadges 3.0
openbadges-verifier -i /tmp/badge_1_recipient@example.com.svg \
-r recipient@example.com -V 3 -k ./config/keys/verify_rsa_key_1.pem
# 4b. Verify — strict OpenBadges 2.0 (pin a trusted key with -l/--local or -k/--pubkey)
openbadges-verifier -i /tmp/badge_1_recipient@example.com.svg \
-r recipient@example.com -V 2 -l 1
# 5. OpenBadges 3.0 revocation (opt-in: set 'status_lists = revocation, suspension'
# in the badge section before signing). Publish the issuer's did.json and the
# signed Bitstring Status Lists, then revoke and re-publish.
openbadges-publish -c ./config/config.ini -o ./public -V 3
openbadges-publish -c ./config/config.ini -o ./public -V 3 \
--revoke recipient@example.com --reason "issued in error"See the Quick Start and CLI Reference wiki pages for the full walkthrough and every flag.
from datetime import datetime, timezone
from openbadgeslib.ob2 import OB2Signer, Assertion, IdentityObject, Verification
with open('sign.pem', 'rb') as f:
priv_pem = f.read()
with open('badge.svg', 'rb') as f:
image = f.read()
assertion = Assertion(
recipient=IdentityObject.create('recipient@example.com', salt='s4lt3d'),
badge='https://example.com/badge_1/badge.json',
verification=Verification(type='SignedBadge',
creator='https://example.com/badge_1/key.json'),
issued_on=datetime(2026, 1, 1, tzinfo=timezone.utc),
)
signer = OB2Signer(privkey_pem=priv_pem, algorithm='RS256')
baked_svg = signer.sign_into_svg(assertion, image)
with open('/tmp/signed_badge.svg', 'wb') as f:
f.write(baked_svg)For the frozen OpenBadges 1.0 legacy API (Badge / Signer / Verifier), import from openbadgeslib.ob1 instead.
OB 3.0 credentials can be secured with either of the two proof formats the spec allows: a compact VC-JWT (OB3Signer, RS256/ES256/EdDSA) or an embedded Data Integrity proof (OB3LdpSigner, cryptosuite eddsa-rdfc-2022, Ed25519 only — needs the [ldp] extra). Both bake into the same SVG/PNG carriers and the verifier auto-detects the format.
from openbadgeslib.ob3 import (
Issuer, Achievement, OpenBadgeCredential, OB3Signer, OB3Verifier,
)
issuer = Issuer(id='https://example.com/issuer', name='Example Org')
achievement = Achievement(
id='https://example.com/achievements/python',
name='Python Developer',
description='Awarded for Python proficiency',
criteria_narrative='Must pass the Python assessment',
)
credential = OpenBadgeCredential(
issuer=issuer,
recipient_id='mailto:recipient@example.com',
achievement=achievement,
)
with open('sign.pem', 'rb') as f:
priv_pem = f.read()
signer = OB3Signer(privkey_pem=priv_pem, algorithm='RS256')
# Bake the signed JWT-VC into a badge image
with open('badge.svg', 'rb') as f:
baked_svg = signer.sign_into_svg(credential, f.read())
# Verify
with open('verify.pem', 'rb') as f:
verifier = OB3Verifier(pubkey_pem=f.read())
token = OB3Verifier.extract_token_from_svg(baked_svg)
restored = verifier.verify(token, expected_recipient='recipient@example.com')
print('Recipient:', restored.recipient_id)Same credential, an embedded JSON-LD proof instead of a JWT. Requires an Ed25519 signing key and the [ldp] extra (pip install openbadgeslib[ldp]). Reuse the credential built above:
from openbadgeslib.ob3 import OB3LdpSigner, OB3LdpVerifier
with open('sign_ed25519.pem', 'rb') as f:
priv_pem = f.read()
# Bake a credential carrying an eddsa-rdfc-2022 DataIntegrityProof into an SVG
signer = OB3LdpSigner(priv_pem)
with open('badge.svg', 'rb') as f:
baked_svg = signer.sign_into_svg(credential, f.read())
# Verify (the LDP credential travels as JSON in the baked image)
with open('verify_ed25519.pem', 'rb') as f:
verifier = OB3LdpVerifier(pubkey_pem=f.read())
document = OB3Verifier.extract_token_from_svg(baked_svg)
restored = verifier.verify(document, expected_recipient='recipient@example.com')
print('Recipient:', restored.recipient_id)From the CLI, select the format with openbadges-signer -P ldp (OB 3.0 only), or set proof_format = ldp in the badge's INI section; the default stays vc-jwt. Status lists remain VC-JWT regardless of the badge's proof format.
An additive track: issue the same badge as an IETF SD-JWT VC — the format the EU Digital Identity Wallet / ARF converges on — delegating the crypto to the generic openvc-core library. It does not touch the native VC-JWT / Data Integrity issuance above: it is a separate credential format for wallet flows, not a third image proof. Needs the [eudi] extra (pip install "openbadgeslib[eudi]") and an Ed25519 (EdDSA), NIST P-256 (ES256) or P-384 (ES384) key — SD-JWT's algorithm set (RSA is rejected). HAIP profiles the P-256 family, so P-256 stays the safe default for EUDI wallets; P-384 may be refused by HAIP-strict verifiers. The achievement is always disclosed; the recipient identity is selectively disclosable, so a holder can prove the badge while withholding who they are.
from openbadgeslib.ob3.eudi import issue_badge_sd_jwt, verify_badge_sd_jwt
with open('sign_ed25519.pem', 'rb') as f:
priv_pem = f.read()
# Issue the compact SD-JWT VC (<issuer-jwt>~<disclosure>~…). Reuse `credential`.
token = issue_badge_sd_jwt(credential, privkey_pem=priv_pem)
# Verify the issuer form (or a later holder presentation)
with open('verify_ed25519.pem', 'rb') as f:
pub_pem = f.read()
result = verify_badge_sd_jwt(token, pubkey_pem=pub_pem)
print('Achievement:', result.claims['achievement']['name'])Pass holder_jwk= at issuance to bind the credential to a holder key, then build a Key-Binding presentation (SdJwtVcProofSuite.create_presentation, bound to an audience + nonce) for the EUDI wallet flow. This track is library-only — there is no CLI tool for it.
pytest
pytest --cov=openbadgeslib # with coverage report
flake8 openbadgeslib tests # lint
mypy # type check (config in pyproject.toml)Bug reports, capability requests (the roadmap's demand signal), and pull requests are welcome — see CONTRIBUTING.md for the workflow and the supported-Python policy, and GOVERNANCE.md for maintenance and release governance. Security issues go through SECURITY.md, never a public issue.
See Changelog.txt for the full history, and the GitHub Releases page for release notes.
The library (openbadgeslib/ package) is licensed under the GNU Lesser General Public License v3 (LGPLv3). The command-line wrapper tools are licensed under the BSD 2-Clause license.
| Back | FazBrowse Home | New Git URL |