Metadata-Version: 2.4
Name: routing-earth-utils
Version: 0.2.2
Summary: Utilities for routing.earth services
Author-Email: Nils Nolde <nilsnolde@proton.me>
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
License-File: THIRD-PARTY-NOTICES.md
Project-URL: Repository, https://github.com/routing-earth/routing-earth-utils
Requires-Python: >=3.12
Requires-Dist: pyvalhalla>=3.8.3
Requires-Dist: cryptography>=42
Requires-Dist: backports.zstd>=1.0; python_version < "3.14"
Description-Content-Type: text/markdown

# Getting started

[![routing-earth-utils version](https://img.shields.io/pypi/v/routing-earth-utils?label=)](https://pypi.org/project/routing-earth-utils/)

> Read this online at [docs.routing-earth.com](https://docs.routing-earth.com/routing-earth-utils/getting-started/).

`routing-earth-utils` (CLI is `re`) is the client software to initialize a local [Valhalla](https://github.com/valhalla/valhalla) graph extract and to consume daily/weekly graph **delta** updates with up-to-date OSM road data.

## Requirements

- **Python ≥ 3.12** (binary wheels for Linux x86_64/aarch64, macOS arm64,
  Windows amd64)
- A paid **routing-earth.com API key** with an entitlement for your scope & cadence
  (get one at [routing.earth](https://routing-earth.com/settings/api-keys))
- **Disk**: syncing briefly holds two copies of the extract plus the
  downloaded bundles (~2× the extract size)

## Install

=== "uv"

    ```sh
    uv tool install routing-earth-utils
    # or, inside a project:
    uv add routing-earth-utils
    ```

=== "pip"

    ```sh
    pip install routing-earth-utils
    ```

This installs the `re` command.

## Authentication

The CLI reads your API key from the environment (or pass `--api-key`):

```sh
export RE_API_KEY="re_..."
```

The key is only ever sent as a Bearer header to the routing.earth API.
`401` means the key is invalid, `403` means it has no entitlement for
the requested scope & cadence.

## `re init` - Seed the extract

Pick a **scope** (`planet` or a region id, e.g. `germany`) and a **cadence**
(`daily`, `weekly` or `monthly`) matching your plan, then:

```sh
re init --tar-extract tiles.tar --scope germany --cadence daily
```

This downloads the current full snapshot, verifies its signature, and writes a
ready-to-serve extract:

```text
initialized tiles.tar at dataset_id 20260718 (germany/daily)
```

## Serve it

Point Valhalla at the tar in your `valhalla.json`:

```json
{
  "mjolnir": {
    "tile_extract": "/path/to/tiles.tar"
  }
}
```

## `re sync` - Keep it current

```sh
re sync --tar-extract tiles.tar
```

This downloads the diff bundle chain, applies it tile by tile, and atomically 
swaps the new extract into place. If your extract is too far behind, we might
decide to pull a fresh full graph.

Every step along the chain is verified via our bundle signatures (Ed25519), and a
per-tile checksum check adds additional security. Any failure aborts the sync and
leaves your current extract untouched. 

Operationally, run `re sync` on a scheduler with e.g. a 15 mins cadence:

```sh
# e.g. crontab, for a daily cadence
*/15 * * * * RE_API_KEY=re_... re sync --tar-extract /srv/valhalla/tiles.tar
```

Exits with code:
- `0` on success (including "already current")
- `1` on any failure

!!! note "Picking up the new extract"
    The swap is an atomic rename. A running Valhalla keeps serving the *old*
    extract from its memory map until it reopens the file — restart or reload
    the service after a successful sync to serve the new data.

## `re status` - Check local graph's update status

```sh
re status --tar-extract tiles.tar
```

```json
{"tar_extract": "tiles.tar", "dataset_id": 20260718, "scope": "germany", "cadence": "daily", "verdict": "diff-chain", "latest_dataset_id": 20260719, "osm_data_timestamp": "2026-07-19T02:00:00Z"}
```

Add `--offline` to simply print the local graph status without requesting our API.

Exits with code:
- `0` on `verdict = current`
- `1` on error
- `2` on `verdict = diff-chain`, i.e. graph needs an update

## Using the Python API

Everything the CLI does is available as a library — see the
[API reference](api/index.md), starting with
[`client`](api/client.md):

```python
from pathlib import Path
from routing_earth_utils.client import init, sync, tar_status

extract = Path("tiles.tar")
if not extract.exists():
    init(extract, scope="germany", cadence="daily")
result = sync(extract)
print(result.verdict, result.to_dataset_id)
```

## Troubleshooting

| Symptom | Cause / fix |
| :-- | :-- |
| `resolver: HTTP 401 (invalid API key)` | `$RE_API_KEY` unset or wrong — check the export and your account. |
| `resolver: HTTP 403 (no entitlement for this scope/cadence)` | Your plan doesn't cover this scope × cadence — re-check `init` arguments against your subscription. |
| `not an initialized extract` | The tar wasn't created by `re init` (or is some other file). Seed it with `init` first. |
| `already exists — use sync` | `init` never overwrites; advance an existing extract with `sync`, or remove it to reseed. |
| Signature / verification errors | Fail-closed by design: the download was rejected and your extract is untouched. The `<extract>.work` dir is kept for diagnosis (safe to delete; the next run starts clean). Retry; if it persists, contact support. |
| Sync fails with a disk-space error | Copy-on-sync needs roughly the extract's size in free space next to it. |

Increase verbosity with `-v` (progress) or `-vv` (debug); diagnostics go to
stderr.
