Linking DVIR Events to ELD Driver Identity
A DVIR arrives claiming a driver, but the claim is just a string until you can tie it to an authenticated electronic logging device (ELD) identity that the driver actually logged into. This guide implements that resolution concretely: take a DVIR’s claimed driver_id, vehicle_vin, and capture timestamp, join them against the ELD driver directory and the Hours-of-Service log within a tolerance window, verify the credential, hash the license number before it is ever stored, and reject or quarantine anything that does not match. Skip this and you accept certifications that no auditor can attribute to a real person — a direct failure of the driver-attribution intent behind 49 CFR § 396.11©(2)(iii) and the identity discipline of Part 395.
This is the implementation detail beneath ELD Identity and HOS Record Linkage, and it sits in the Telematics, ELD & Maintenance Platform Integration section whose DvirComplianceEvent carries the driver_id resolved here.
Prerequisites
Anchor link to "Prerequisites"- Python 3.10+ — the code uses
X | Yunions anddataclasses. - Pydantic 2.x — the
EldIdentityLinkmodel from the parent guide. hashlib(stdlib) — SHA-256 hashing of the license number, which is PII.- Access to the ELD driver directory and HOS log — via the vendor-neutral adapter layer so the same code works across Samsara, Geotab, and Motive.
- The inbound
DvirComplianceEvent— carryingdriver_id,vehicle_vin, and the capture timestamp.
Step-by-Step Implementation
Anchor link to "Step-by-Step Implementation"The resolution is a deterministic pipeline: normalize the claim, look up the identity, join the HOS window, verify, then hash and persist or quarantine.
Step 1 — Normalize the claimed identity off the event
Anchor link to "Step 1 — Normalize the claimed identity off the event"Pull the claim from the event and treat it as untrusted input. Do not assume the driver_id corresponds to a real login yet:
from dataclasses import dataclass
from datetime import datetime
@dataclass(frozen=True)
class DvirClaim:
dvir_id: str
claimed_driver_id: str
vehicle_vin: str
inspection_ts: datetime # captured on the mobile device, may be skewed
def claim_from_event(event: dict) -> DvirClaim:
return DvirClaim(
dvir_id=event["dvir_id"],
claimed_driver_id=event["driver_id"],
vehicle_vin=event["vehicle_vin"],
inspection_ts=datetime.fromisoformat(event["emitted_at"]),
)
Step 2 — Resolve the ELD identity and hash the license number
Anchor link to "Step 2 — Resolve the ELD identity and hash the license number"Look up the authenticated identity in the ELD directory. If the claim resolves to nothing, stop — an unauthenticated driver cannot certify a DVIR. Hash the license number the moment you receive it, so the raw value never reaches storage or logs:
import hashlib
def hash_license(license_number: str) -> str:
"""SHA-256 the license number (PII) before it is stored anywhere."""
return hashlib.sha256(license_number.strip().upper().encode()).hexdigest()
def resolve_eld_identity(directory, claimed_driver_id: str) -> dict | None:
"""Return the authenticated ELD identity or None. None means the claimed
driver is not a real ELD login and the certification must be rejected."""
identity = directory.lookup(claimed_driver_id)
if identity is None:
return None
return {
"eld_driver_id": identity["eld_driver_id"],
"license_number_hash": hash_license(identity["license_number"]),
"vehicle_ems_id": identity.get("assigned_vehicle_ems_id"),
}
Step 3 — Join the HOS record within a tolerance window
Anchor link to "Step 3 — Join the HOS record within a tolerance window"Confirm the driver was on duty in that vehicle around the capture time. Use a tolerance window, not an exact match, to absorb the clock skew between the mobile app and the ELD:
from datetime import timedelta
TOLERANCE = timedelta(minutes=15) # tune from observed mobile-vs-ELD skew
def find_hos_window(hos_log, eld_driver_id: str, vehicle_vin: str,
inspection_ts: datetime) -> dict | None:
"""Return the on-duty HOS window covering inspection_ts (± tolerance) for
this driver in this vehicle, or None if no such window exists."""
for window in hos_log.windows_for(eld_driver_id):
if window["vehicle_vin"] != vehicle_vin:
continue
if window["status"] not in ("on_duty", "driving"):
continue
if (window["start"] - TOLERANCE) <= inspection_ts <= (window["end"] + TOLERANCE):
return window
return None
Step 4 — Verify, then build the link or quarantine
Anchor link to "Step 4 — Verify, then build the link or quarantine"Combine the resolved identity and the HOS window into a single verdict. On success, build the EldIdentityLink; on any failure, quarantine with enough context for a human to reconcile:
from pydantic import BaseModel
class EldIdentityLink(BaseModel):
dvir_id: str
eld_driver_id: str
license_number_hash: str
vehicle_ems_id: str
hos_status: str
verified_at: datetime
def link_or_quarantine(claim: DvirClaim, directory, hos_log) -> EldIdentityLink | dict:
identity = resolve_eld_identity(directory, claim.claimed_driver_id)
if identity is None:
# Unauthenticated driver — reject (49 CFR 396.11(c)(2)(iii)).
return {"quarantine": "unauthenticated_driver", "dvir_id": claim.dvir_id}
window = find_hos_window(
hos_log, identity["eld_driver_id"], claim.vehicle_vin, claim.inspection_ts
)
if window is None:
# Resolved identity but no matching on-duty window in this vehicle.
return {
"quarantine": "hos_window_mismatch",
"dvir_id": claim.dvir_id,
"eld_driver_id": identity["eld_driver_id"],
"vehicle_vin": claim.vehicle_vin,
}
return EldIdentityLink(
dvir_id=claim.dvir_id,
eld_driver_id=identity["eld_driver_id"],
license_number_hash=identity["license_number_hash"],
vehicle_ems_id=identity["vehicle_ems_id"] or window["vehicle_ems_id"],
hos_status=window["status"],
verified_at=datetime.now().astimezone(),
)
Verification and Testing
Anchor link to "Verification and Testing"Prove three properties: an unauthenticated claim is rejected, a matched claim within tolerance links, and skew at the edge of the window is absorbed rather than rejected.
import pytest
from datetime import datetime, timedelta, timezone
def _ts(minutes=0):
return datetime(2026, 7, 16, 8, 0, tzinfo=timezone.utc) + timedelta(minutes=minutes)
class FakeDirectory:
def __init__(self, known): self.known = known
def lookup(self, did): return self.known.get(did)
class FakeHos:
def __init__(self, windows): self._w = windows
def windows_for(self, eld_id): return self._w.get(eld_id, [])
def test_unauthenticated_driver_is_quarantined():
claim = DvirClaim("D-1", "ghost", "1VIN", _ts())
out = link_or_quarantine(claim, FakeDirectory({}), FakeHos({}))
assert isinstance(out, dict) and out["quarantine"] == "unauthenticated_driver"
def test_matched_claim_links():
directory = FakeDirectory({"drv7": {
"eld_driver_id": "E7", "license_number": "S1234", "assigned_vehicle_ems_id": "V7"}})
hos = FakeHos({"E7": [{"vehicle_vin": "1VIN", "vehicle_ems_id": "V7",
"status": "on_duty", "start": _ts(-5), "end": _ts(30)}]})
out = link_or_quarantine(DvirClaim("D-2", "drv7", "1VIN", _ts()), directory, hos)
assert isinstance(out, EldIdentityLink)
assert out.hos_status == "on_duty"
assert len(out.license_number_hash) == 64 # stored hashed, never raw
def test_edge_of_tolerance_is_absorbed():
directory = FakeDirectory({"drv7": {
"eld_driver_id": "E7", "license_number": "S1234", "assigned_vehicle_ems_id": "V7"}})
hos = FakeHos({"E7": [{"vehicle_vin": "1VIN", "vehicle_ems_id": "V7",
"status": "driving", "start": _ts(0), "end": _ts(60)}]})
# Capture 14 minutes before the window start — inside the 15-min tolerance.
out = link_or_quarantine(DvirClaim("D-3", "drv7", "1VIN", _ts(-14)), directory, hos)
assert isinstance(out, EldIdentityLink)
Common Failure Modes and Gotchas
Anchor link to "Common Failure Modes and Gotchas"- Clock skew between mobile capture and the ELD. The DVIR timestamp is set by the driver’s phone, which can be minutes off the ELD clock and occasionally set to a wrong time zone. An exact-timestamp join rejects legitimate records. Use the tolerance window from Step 3, and if the observed skew regularly exceeds the window, widen it from measured data rather than assuming the record is fraudulent. Persist the actual skew per match so the window is tuned, not guessed.
- Team-driving handoffs. Two drivers share a vehicle across a shift, and the DVIR may be certified by the co-driver while the HOS log shows the other driver as
driving. Do not auto-reject: check both drivers assigned to the vehicle in the window, and accept the link if either authenticated identity was on duty. Record which driver matched so an auditor can follow the handoff. - Driver logged into the wrong vehicle. A driver who selects the wrong asset in the ELD app produces an HOS window whose
vehicle_vinwill not match the DVIR’s. This is a genuine mismatch, but a common and correctable one. Quarantine with both VINs attached so a human can confirm whether the driver simply mis-selected the vehicle or whether the certification is against a vehicle they never operated — the latter invalidates the certification.
Frequently Asked Questions
Anchor link to "Frequently Asked Questions"Should the raw license number ever be stored?
No. Hash it with SHA-256 the moment you receive it and store only the digest. The hash is sufficient to match the identity and to prove attribution during an audit without holding personally identifiable information in the clear. Keep the raw value out of logs, events, and error payloads as well, not just the primary store.
How wide should the tolerance window be?
Start around 15 minutes and tune it from your fleet’s measured skew between mobile capture and the ELD clock. Record the actual skew for every successful match; if legitimate records cluster just outside the window, widen it from that data. A window that is too tight rejects valid certifications; one that is too wide lets a genuinely mismatched certification slip through, so treat the size as a tuned parameter.
What is the difference between rejecting and quarantining a certification?
Reject when the claim resolves to no authenticated ELD identity at all — there is no real driver to attribute the certification to, so it cannot stand. Quarantine when an identity resolves but the HOS window does not match, because that is often a correctable situation such as a team handoff or a wrong-vehicle selection that a human can reconcile. Quarantine preserves the record for review; rejection refuses it outright.
Related
Anchor link to "Related"- ELD Identity and HOS Record Linkage — the schema, correlation algorithm, and rejection rules this implements.
- Samsara, Geotab and Motive Platform Adapters — normalizes the directory and HOS queries across vendors.
- Encrypting DVIR Data at Rest and in Transit — the PII protections behind the license-hash step.
- Certifying Repairs Under 396.11©(2) — the driver review that depends on a verified identity.
This guide is part of ELD Identity and HOS Record Linkage. Back to the Telematics, ELD & Maintenance Platform Integration section.