PathCraft is an extensible Go routing engine for maps, transit, simulations, and custom graphs.
It ships working routing today: OSM street routes via A*, GTFS transit routes via RAPTOR, timetable-aware walk+transit journeys, a 3D air-routing reference, GeoJSON export, a CLI, an HTTP debug/demo server, and one compile-time registry for custom routing modes, loaders, algorithms, exporters, and cost models.
- Street routing: parse OSM XML /
.osm.gzfor one-file graphs, or stream regional and global OSM PBF snapshots into versioned sparse XYZ stores for bounded local routing. - Transit routing: ingest GTFS
stop_times.txt,trips.txt, optionaltransfers.txt, and optionalstops.txt; query earliest arrivals with RAPTOR. - Time-dependent multimodal journeys: compare direct walking against walk → scheduled transit → walk, with timed journey legs.
- Plugin registry:
core.Mode,core.Algorithm,core.GraphLoader,core.Exporter, andcore.CostModelextension points underpkg/plugins. - Engine configuration: validated primitive walking speed and per-highway penalties; routing-mode policy lives in plugins.
- Multiple surfaces: Go SDK, CLI, HTTP, protobuf/gRPC, browser WASM, and a Leaflet routing demo.
- Opt-in CORS: exact browser origins, disabled by default.
- GeoJSON output: route and graph visualization through FeatureCollections.
- Scale foundations: exact directed degree-two contraction, source-safe atomic graph caches, and concurrent read-only routing.
- Tests and benchmarks: coverage for graph, OSM, GTFS, A*, RAPTOR, HTTP, engine, registry, plugin pipelines, allocations, and parallel queries.
- Go
1.25.12(seego.mod) makecurlonly if you usemake fetch-osm- Node.js 24 only for frontend and WASM smoke-test development
Go dependencies are pinned in go.mod and go.sum.
make build
./bin/pathcraft help./bin/pathcraft plugins listBuilt-ins currently include:
| Name | Kind | Wraps / source |
|---|---|---|
osm |
loader | OSM XML / .osm.gz parser |
worldgraph |
loader | Versioned local XYZ chunk store |
gtfs |
loader | GTFS directory |
astar |
algorithm | internal A* walking router |
raptor |
algorithm | internal RAPTOR transit router |
geojson |
exporter | GeoJSON FeatureCollection |
zap |
logger | Development structured logger |
walk / bike / car |
mode | OSM street-routing policies |
gtfs |
mode | Timetable-aware multimodal routing |
air |
mode | N-dimensional direct air route example |
Walking A* on OSM, exported as GeoJSON:
./bin/pathcraft pipeline \
--loader osm --graph testdata/example.osm \
--algorithm astar --from 1 --to 6 \
--export geojsonTransit RAPTOR on GTFS:
./bin/pathcraft pipeline \
--loader gtfs --graph testdata/mini_gtfs \
--algorithm raptor --from START_STOP --to END_STOP \
--opt departure_time=05:00:00./bin/pathcraft parse --file testdata/example.osm
./bin/pathcraft preprocess --file testdata/example.osm
./bin/pathcraft route --file testdata/example.osm --from 1 --to 6 --coords
./bin/pathcraft route --file testdata/example.osm \
--from-lat -8.05428 --from-lon -34.88130 \
--to-lat -8.05480 --to-lon -34.88030 --coords
./bin/pathcraft transit --gtfs testdata/mini_gtfs --from START_STOP --to END_STOP --time 05:00:00
./bin/pathcraft journey --file testdata/example.osm --gtfs testdata/mini_gtfs \
--from-lat -8.05428 --from-lon -34.88130 \
--to-lat -8.05480 --to-lon -34.88030 --time 05:00:00
./bin/pathcraft serve --file testdata/example.osm --gtfs testdata/mini_gtfs --addr 127.0.0.1:8080
# Network exposure is explicit; add authentication/rate limiting at the reverse proxy.
./bin/pathcraft serve --file testdata/example.osm --addr :8080 \
--cors-origin https://app.example
./bin/pathcraft grpc --file testdata/example.osm --addr 127.0.0.1:9090
# Self-contained 3D plugin; no street graph required.
./bin/pathcraft route --mode air \
--from-position '12.5648,55.6726,100' \
--to-position '12.5717,55.6833,200' \
--opt cruise_altitude_m=1500 --coordsBuild a replaceable regional store from one OSM PBF extract, or build a restartable sparse packed store from one global snapshot. Both use existing mode IDs and the same runtime:
./bin/pathcraft chunks build --pbf region.osm.pbf --store world --region demo
./bin/pathcraft chunks build-global --pbf planet.osm.pbf --store world-global \
--work-dir .worldgraph-build
./bin/pathcraft route --chunks world-global --mode walk \
--from-position '12.5683,55.6761' --to-position '12.5685,55.6762'
./bin/pathcraft serve --chunks world-global --addr 127.0.0.1:8080make demo-chunks PBF=region.osm.pbf REGION=demo STORE=world remains the
thin regional build-and-serve flow. Global builds use fixed routing zoom 12,
zoom-8 shard indexes, segmented packs, bounded sequential sort/merge joins,
radix-partitioned compact way fragments, and resumable stage checkpoints. See
World graph operations
for resource flags, storage planning, recovery, and acceptance commands.
Runtime defaults are routing zoom 12, one-tile initial halo, 256 route tiles,
three expansions, and a 512 MiB decoded cache.
Longitude wraps at the antimeridian; latitude is limited to Web Mercator
±85.05112878°. Packed global builds preserve valid OSM nodes beyond that
range to resolve source references, but omit their adjacent road segments.
Each build publishes an immutable generation. Regional reimports replace prior region provenance and remove deleted roads; global builds represent one source snapshot and never materialize a global zoom-12 tile list. Already-open routers remain pinned to their generation. Uncovered origins, destinations, or required corridor tiles return an error—there is no straight-line or partial route fallback. Historical generations are retained; no built-in garbage collector removes them, so operators must clean only generations no active reader or manifest needs. This MVP targets bounded local/regional trips, not arbitrary intercontinental routing or a continental hierarchy.
Generated .pcg, .idx, and .pack stores are trusted local artifacts,
never HTTP uploads. Each build hashes and scans one private regular-file PBF
snapshot; concurrent stale publishers fail instead of replacing newer
manifests. Before decoding, imports
reject unsupported protobuf wire layouts; headers cap at 1 MiB, while data
blocks cap at 500,000 entities, 1,000,000 tags, 250,000 string entries, and
64 MiB decompressed. Accepted entities stream through one ordered decoder
worker and bounded application batches instead of accumulating decoded blocks.
Routing labels and per-tile contribution bytes are also bounded. Chunks cap at
250,000 nodes, 500,000 edges, and 64 MiB encoded payload.
These are fixed MVP resource limits, so split denser extracts when needed. Keep
OpenStreetMap attribution visible when rendering derived data:
© OpenStreetMap contributors, under the ODbL. The HTTP server is
unauthenticated and binds loopback by default; explicit non-loopback exposure
needs a trusted reverse proxy, authentication, TLS, and rate limits.
pathcraft grpc is plaintext and unauthenticated. It binds loopback by default; add trusted TLS/auth infrastructure before any untrusted-network exposure.
Use pkg/pathcraft/engine for the public orchestration API. The registry-backed pipeline lets callers choose which plugins to activate with blank imports.
package main
import (
"context"
"fmt"
"github.com/danielscoffee/pathcraft/pkg/pathcraft/core"
pcengine "github.com/danielscoffee/pathcraft/pkg/pathcraft/engine"
_ "github.com/danielscoffee/pathcraft/pkg/plugins/astar"
_ "github.com/danielscoffee/pathcraft/pkg/plugins/geojson"
_ "github.com/danielscoffee/pathcraft/pkg/plugins/osm"
)
func main() {
res, err := pcengine.Run(context.Background(), pcengine.PipelineRequest{
LoaderName: "osm",
Source: "testdata/example.osm",
AlgorithmName: "astar",
ExporterName: "geojson",
Route: core.RouteRequest{From: "1", To: "6"},
})
if err != nil {
panic(err)
}
defer res.Close()
fmt.Printf("path nodes: %d, cost: %.1f\n", len(res.Result.Path), res.Result.Cost)
fmt.Println(string(res.Output))
}The engine.Engine facade supplies data loading and primitive street/transit operations to plugins. engine.New() keeps a walking primitive default; engine.NewWithConfig(engine.Config{...}) validates primitive speed and per-highway penalty multipliers. Applications select discoverable routing behavior through core.Mode plugins.
Full guide: Go SDK.
- JavaScript/WASM SDK — synchronous in-browser street routing from plain OSM XML.
- gRPC API — generated
pathcraft.v1client/server contract for street routes and multimodal journeys. - Plugin system — compile-time routing modes, algorithms, loaders, exporters, cost models, and loggers.
These interfaces remain pre-release. WASM does not load GTFS; gRPC has no built-in TLS or authentication.
Run the Leaflet demo, then click two points on the map. The UI discovers registered modes compatible with its geographic renderer, builds controls from manifest options, resolves modes independently, and draws plugin-supplied segments. Leaflet projects longitude/latitude; 3D altitude remains present in air-route API results.
make demo
# open http://localhost:8080/graph-visualUse a real OSM extract for a richer map:
make fetch-osm BBOX="40.7480,-74.0050,40.7620,-73.9700" OUT=examples/manhattan.osm
make demo OSM_FILE=examples/manhattan.osmFull guide: docs/tutorials/demo.md.
The HTTP server is a GET-only debug/demo interface, not a stable production API. CORS is disabled by default; --cors-origin accepts comma-separated exact HTTP(S) origins and enables browser preflight without credentials. Wildcard, opaque, and path-bearing origins are ignored.
GET /health,GET /statusGET /graph— loaded one-file walking graph as GeoJSON linesGET /graph/chunks/{generation}/{z}/{x}/{y}— immutable owned-edge GeoJSON for a covered world chunkGET /config— map bootstrap data plus chunk generation, URL template, zooms, and coverage-derived viewport when availableGET /nodes?bbox=minLon,minLat,maxLon,maxLat&limit=200&min_degree=3— graph nodes as GeoJSON pointsGET /nearest?lat=...&lon=...— nearest graph node and snap distanceGET /modes— manifests for every registered routing modeGET /mode-route?mode=air&from=lon,lat,alt&to=lon,lat,alt— generic N-dimensional plugin routingGET /route?from=<id>&to=<id>— node-id walking route as GeoJSONGET /route?from_lat=...&from_lon=...&to_lat=...&to_lon=...— coordinate walking route as GeoJSONGET /journey?from_lat=...&from_lon=...&to_lat=...&to_lon=...&time=HH:MM:SS— timetable-aware walk+transit journey JSONGET /transit/stops,GET /transit/trips,GET /transit/trip?trip_id=...GET /graph-visual— Leaflet viewer
api/pathcraft/v1 protobuf contract and generated Go client/server types
cmd/pathcraft CLI entrypoint
cmd/pathcraft-wasm browser WebAssembly entrypoint
pkg/pathcraft/core dimension-neutral plugin interfaces and value types
pkg/pathcraft/engine public engine facade and pipeline runner
pkg/plugins registry, standard plugins, nearest-node index, and worldgraph chunk runtime/builder
internal/graph private graph, contraction index, and cache format
internal/osm OSM parser and graph builder
internal/gtfs GTFS parsers and RAPTOR-ready indexes
internal/routing A* and RAPTOR implementations
internal/http HTTP demo/debug adapter
internal/grpcapi protobuf/gRPC adapter
internal/wasmapi JSON bridge for JavaScript/WASM
sdk/js JavaScript WebAssembly loader
web/app embedded React/Leaflet app
testdata tracked small OSM and GTFS fixtures
See also:
- Documentation index
- Architecture overview
- Current system audit
- Plugin system
- Go SDK
- JavaScript/WASM SDK
- gRPC API
- World graph operations
- Roadmap
engine.Configsets primitive walking speed in m/s and OSM-highway penalty multipliers (>= 1); registered mode plugins own mode defaults/policy.- CLI flags configure OSM/PBF inputs, world chunk stores/cache/route caps, GTFS directories, endpoints, server address, and optional exact CORS origins.
Makefilevariables:OSM_FILE(defaulttestdata/example.osm)GTFS_DIR(defaulttestdata/mini_gtfs)ADDR(default127.0.0.1:8080)BBOX/OUTformake fetch-osmPBF/REGION/STORE/CHUNK_ZOOMformake demo-chunks
- Parsed graph caches are written as
<osm-file>.cache. World stores publishmanifest.jsonplus immutable regional.pcgfiles or packedshards/<prefix>/<x>/<y>.idxand<y>-NNN.packfiles undergenerations/<generation>/. All are trusted local artifacts, not upload/network input; versions, checksums, provenance, and atomic manifest replacement reject mixed or stale generations.
make test # go test ./... -v -cover
make build # build ./bin/pathcraft
make clean # remove ./bin/pathcraft
go test ./... # faster local test run without verbose coverageBenchmarks live next to their packages and use standard Go tooling:
go test ./internal/routing/astar -bench=. -benchmem
go test ./pkg/pathcraft/engine -run '^$' -bench='(Preprocess|Parallel)' -benchmemContraction results and memory-profile workflow: docs/performance.md.
PathCraft is a prototype routing engine. Library, HTTP, timetable-routing, SDK, WASM, gRPC, plugins, and versioned regional/global graph stores work at documented scope, but production hardening remains. Chunk routing is bounded corridor A*, not a planet-scale hierarchy; one-file graphs remain resident, gRPC is local plaintext by default, and WASM street routing is synchronous. Known next steps include GTFS calendars/realtime, richer stop access, global-scale profiling, API security, and deployment packaging.
See CONTRIBUTING.md. Keep changes deterministic, formatted, tested, and aligned with the internal/public package boundaries.