# Truck and ADR routing

What a lorry may legally drive is not a preference, it is a set of rules.
This page states exactly which of those rules our routing enforces, which
it does not, and where the data comes from, so you can check our answers
against the standards rather than take them on trust.

Two sources are normative here:

- **ADR 8.6.4**, the tunnel restriction provisions of the European
  Agreement concerning the International Carriage of Dangerous Goods by
  Road (UNECE, public).
- **Council Directive 96/53/EC**, which sets the maximum authorised
  dimensions and weights for vehicles in international traffic.

## What is enforced

Send `costing: "truck"` with a `truck` profile. Every field below is passed
to the routing engine and applied to the graph search, so a restricted way
is not merely penalised, it is excluded.

| Field | Unit | Sent |
|---|---|---|
| `height` | metres | always |
| `width` | metres | always |
| `length` | metres | always |
| `weight` | tonnes, gross combination | always |
| `axle_load` | tonnes, heaviest single axle | when supplied |
| `axle_count` | count | when supplied |
| `hazmat` | boolean | always |
| `adr_tunnel_code` | ADR 8.6.4 code | when supplied |

Omit `axle_load` or `axle_count` and no axle constraint is applied. We do
not guess them, because a guessed axle load is worse than none: it would
silently exclude legal roads or admit illegal ones, and you would have no
way to tell which.

### Defaults

An omitted dimension falls back to the Directive 96/53/EC maximum for a
standard articulated vehicle: 4.0 m height, 2.55 m width, 16.5 m length,
40 t gross weight. These are deliberately the legal maxima rather than a
typical vehicle, so an unspecified request errs towards over-restriction.
A route we return for the defaults is drivable by anything smaller.

## ADR tunnel categories

Tunnel categories run A to E. Category A carries no restriction. Categories
B to E progressively exclude loads by their tunnel restriction code.

`check_adr_tunnel` answers one question: may a load carrying tunnel code X
pass through a category Y tunnel? It is a pure lookup against the ADR 8.6.4
matrix. It performs no network call, consults no map data, and is therefore
free to run and identical for everyone.

Two deliberate choices, both conservative:

- **A declared code is read at its worst case.** Where a code covers
  several transport categories, we apply the most restrictive reading.
- **`hazmat: true` with no declared code is treated as code B**, the
  broadest restriction, rather than assumed unrestricted.

Both choices can refuse a passage that a more precise declaration would
permit. That is the intended direction of error. If you need the less
restrictive answer, declare the code.

## Which engine, and what each needs

The gateway runs on one of two routing engines, and the ADR support
differs. This matters if you self-host.

- **GraphHopper backend:** ADR tunnel enforcement needs no modified
  engine. Forbidden categories are emitted as custom-model rules against
  GraphHopper's own `hazmat_tunnel` encoded value, using the stock server.
- **Valhalla backend:** dimensional and `hazmat` constraints work on a
  stock build, but **`adr_tunnel_code` requires our bounded ADR costing
  fork**. On an unmodified upstream Valhalla the field is ignored, and no
  tunnel-category restriction is applied.

That fork is the single piece of engine code we modify, it is scoped to ADR
tunnel-category costing alone, and the change has been offered upstream.
See [licensing](/docs/licensing) and the lineage table in the repository
for how every component is consumed.

If you self-host on stock Valhalla and need tunnel-category enforcement,
either run the GraphHopper backend or use our distro build. Do not assume
the field took effect: check that a code D load is refused a category E
tunnel on a route you know.

## Limits, stated plainly

**Restrictions come from OpenStreetMap, and OSM truck tagging is uneven.**
Height, weight and hazmat tags are well populated on some networks and
sparse on others. Where a restriction is not tagged, no engine can honour
it, including ours. We publish a truck-attribute completeness audit for
exactly this reason, so the gaps are measurable rather than assumed away.

**This is a routing engine, not a compliance certificate.** We tell you
what the map and the ADR matrix say. We do not certify that a vehicle,
load or journey is lawful, and we do not issue clearance documents. The
duty holder remains the operator and, where appointed under ADR 1.8.3,
their Dangerous Goods Safety Adviser.

**Speed limits and restrictions are never fabricated.** If the graph does
not carry a value, nothing is surfaced rather than a plausible guess.

## Using it

- `POST /route` with `costing: "truck"`, see the
  [API reference](/docs/api-reference).
- `POST /optimise` carries the same constraints through multi-vehicle
  planning, see [optimisation](/docs/optimisation).
- Agents can call `route` and `check_adr_tunnel` as tools over the
  [MCP server](/docs/mcp).
- Tunnel code tables and parameter conventions are on the
  [conventions page](/docs/conventions).
