Building a Community Contribution System for VIN Decoding Patterns
Source: Dev.to
Overview

VIN decoding sounds straightforward until you realize every manufacturer implements it differently, and the authoritative database (NHTSA VPIC) only covers US‑market vehicles.
We maintain Corgi, an open‑source offline VIN decoder. To handle international vehicles, we built a community contribution system. Here’s how it works.
The Problem
A VIN (Vehicle Identification Number) is 17 characters:
- Positions 1‑3: WMI (World Manufacturer Identifier)
- Positions 4‑8: Vehicle attributes (model, engine, etc.)
- Position 9: Check digit
- Position 10: Model year
- Position 11: Plant code
- Positions 12‑17: Serial number
NHTSA’s VPIC database maps WMIs to manufacturers and defines how to interpret positions 4‑8, but it only covers vehicles sold in the US.
Examples:
- Tesla’s Shanghai factory uses WMI
LRW. - Tesla’s Berlin factory uses WMI
XP7.
Neither WMI exists in VPIC.
Our Solution: YAML Patterns
Contributors add a YAML file describing a new WMI:
wmi: LRW
manufacturer: Tesla
make: Tesla
country: China
vehicle_type: Passenger Car
years:
from: 2020
to: null
sources:
- type: service_manual
description: Tesla Model 3/Y VIN decoder
url: https://example.com/source
patterns:
- pattern: "3E****"
element: Model
value: Model 3
- pattern: "YG****"
element: Model
value: Model Y
- pattern: "*D****"
element: Drive Type
value: AWD
- pattern: "*C****"
element: Drive Type
value: RWD
test_vins:
- vin: LRW3E7FA6NC433523
expected:
make: Tesla
model: Model 3
drive_type: RWDValidation Pipeline
When a PR is opened, CI runs several checks.
Schema Validation (Zod)
const wmiFileSchema = z.object({
wmi: z.string().length(3).regex(/^[A-HJ-NPR-Z0-9]{3}$/),
manufacturer: z.string().min(1),
make: z.string().min(1),
country: z.string().min(1),
vehicle_type: z.enum(VALID_VEHICLE_TYPES),
years: yearsSchema,
patterns: z.array(patternSchema).min(1),
test_vins: z.array(testVinSchema).min(1),
});Check Digit Verification
Every test VIN must have a valid check digit (position 9):
function validateCheckDigit(vin: string): boolean {
const weights = [8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2];
const transliteration = { A:1, B:2, /* … */ };
let sum = 0;
for (let i = 0 - npm: