Badge Verification_
Every Chapa badge carries a verification hash that proves the data has not been tampered with. Here is exactly how it works.
Why verification exists
Chapa badges are embeddable SVGs that live on READMEs, portfolios, and resumes. Because they display outside of Chapa, anyone could theoretically edit an SVG to inflate their score, change their archetype, or fabricate metrics. The verification hash makes that detectable.
Every badge includes a short hex code on its right edge. That code is a cryptographic fingerprint of the badge data. Anyone can paste it into the verification page to confirm the badge is authentic and see the original values.
How it works
The hash is generated using HMAC-SHA256, a widely used, industry-standard message authentication code. The process has three steps:
- Build a deterministic payload — the badge data fields are concatenated in a fixed order, separated by pipes. The same data always produces the same payload string.
- Sign the payload — the payload is signed with a secret key that only the Chapa server knows. This produces a 64-character hex digest.
- Truncate to 32 characters — the first 32 hex characters (128 bits) are used as the verification code. This provides strong collision resistance while remaining compact enough to display on the badge.
When a badge is generated, the verification record (hash + original data) is stored server-side. To verify, anyone can look up the hash and compare the stored values against what the badge displays.
What data is signed
The HMAC payload includes every meaningful field displayed on the badge, in this exact order:
| Field | Example | Why included |
|---|---|---|
| Handle | juan294 | Ties the badge to a specific developer |
| Adjusted composite | 72 | The headline impact score shown on the badge |
| Confidence | 85 | Ensures confidence rating cannot be inflated |
| Tier | High | Prevents tier from being changed |
| Archetype | Builder | Locks the archetype label to the data |
| Delivery | 80 | Each dimension score is individually signed |
| Quality | 55 | |
| Consistency | 68 | |
| Breadth | 45 | |
| Total commits | 312 | Key metrics visible on the badge |
| PRs merged | 47 | |
| Reviews submitted | 89 | |
| Date | 2026-02-14 | Same data on different days produces different hashes |
Floating-point dimension scores are rounded to integers before signing. Handles are lowercased. This ensures the payload is fully deterministic — the same badge data on the same day always produces the same hash.
What it guarantees
- Data integrity — if any field on the badge has been altered (score, tier, archetype, dimensions, metrics), the hash will not match and verification will fail.
- Authenticity — only the Chapa server can produce a valid hash because only it knows the signing secret. No one else can forge a hash that passes verification.
- Date binding — the date is part of the payload, so a valid hash from January cannot be reused in February. This ensures the verification reflects a specific point in time.
What it does not guarantee
Transparency means being honest about limitations too:
- Not a blockchain — verification records are stored in a database with a 30-day TTL. After 30 days, the record expires and the hash can no longer be verified. The badge itself remains valid; only the lookup expires.
- Trust in Chapa — the system proves the badge was generated by Chapa and has not been modified since. It does not independently prove that the underlying platform data is accurate — it trusts GitHub, Bitbucket, and Codeberg as data sources.
- Not tamper-proof at the SVG level — anyone can edit an SVG file. The hash does not prevent editing; it makes editing detectable. A modified badge will fail verification.
How to verify a badge
- Find the 32-character hex code on the right edge of any Chapa badge.
- Go to the verification page and enter the code.
- The result will show the original badge data — compare it against what the badge displays.
You can also use the API directly: GET /api/verify/<hash> returns a JSON response with the full verification record. Rate-limited to 30 requests per minute per IP.
Design decisions
| Decision | Rationale |
|---|---|
| HMAC-SHA256 | Industry standard, widely audited, and supported natively in Node.js. Security relies on key secrecy, not algorithm secrecy. |
| 32-character truncation | 128 bits of the hash — strong collision resistance (2^64 birthday resistance) while remaining compact enough to print on a badge. |
| 30-day TTL | Badges are regenerated daily. A 30-day window is generous for verification while keeping storage bounded. |
| Fire-and-forget storage | If the database is temporarily unavailable, the badge still renders. Verification is a bonus, not a blocker. |
| Date in payload | Prevents indefinite reuse of old hashes. Same developer, same data, different day = different hash. |
Try it yourself
Every Chapa badge has a verification hash. Paste it in to see the authenticated data behind any badge.
Verify a Badge