A Corviont stack runs maps, routing, and search fully offline on your device. Once started, your application talks to a single local HTTP entrypoint - no external map APIs required.
The stack includes:
linux/amd64 and linux/arm64.To check your architecture on Linux / Raspberry Pi:
uname -m
# x86_64 or aarch64 => OK
getconf LONG_BIT
# 64 => OK
Quick sanity check:
# Both should output a version
docker version
docker compose version
Corviont ships as a self-contained pack (data + Docker Compose stack). Once you have a pack, you can run it locally on your machine or edge device in a few minutes.
If you received a .zip/.tar.gz pack, extract it and cd into the extracted folder.
If you're using the public sample repo, clone it and cd into the repo.
# Example (archive)
tar -xzf corviont-pack.tar.gz
cd corviont-pack
# Example (repo)
git clone https://github.com/corviont/monaco-demo.git
cd monaco-demo
Set the port you want the stack to listen on (3000 is the default used
in this documentation). This creates a .env file that
Docker Compose will pick up automatically:
echo "CORVIONT_PORT=3000" > .env
If you want to call Corviont APIs from a browser app running on a different origin (e.g. http://localhost:3001), append this to your .env:
# Supports a comma-separated allowlist (e.g. http://localhost:3001,http://localhost:5173) or "*" to allow any origin
echo "CORVIONT_CORS_ALLOWED_ORIGINS=http://localhost:3001" >> .env
Start all services in the background. On some Linux / Raspberry Pi setups
you may need to prefix commands with sudo.
docker compose up -d
Once the stack starts, open http://localhost:3000/ (or whatever you chose for CORVIONT_PORT) in your browser. You should see:
cafe)To stop all services and free ports:
docker compose down
See the FAQ below for common issues
around missing CORVIONT_PORT or Docker permissions.
http://localhost:3000 (or whatever you chose for CORVIONT_PORT). All examples below assume that base URL.
Use this if you want the "it just works" path. Corviont provides a ready-to-use MapLibre style based on OpenMapTiles that already references the correct tiles and assets for the dataset version.
<html>
<head>
<!--
NOTE: For simplicity, this example uses unpkg for MapLibre.
Corviont deployments bundle MapLibre (and all other assets) locally for fully offline use.
-->
<script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' rel='stylesheet' />
<style>
html, body, #map { height: 100%; width: 100%; padding: 0; margin: 0; }
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
async function initMap() {
// dataset.json provides the active dataset version (used for versioned asset URLs)
const dataset = await (await fetch("/dataset.json")).json();
new maplibregl.Map({
container: "map",
center: [dataset.ui_center.lon, dataset.ui_center.lat], // pack-defined UI center
zoom: 15,
transformRequest: (url) => ({ url: /^https?:\/\//i.test(url) ? url : absolutizeUrl(url) }),
}).setStyle(`map-style/${dataset.version}.json`, {
// make sprite/glyph URLs absolute (style contains relative paths)
transformStyle: (_, next) => ({
...next,
sprite: absolutizeUrl(next.sprite),
glyphs: absolutizeUrl(next.glyphs),
})
});
}
// Corviont styles use relative URLs; MapLibre requires absolute URLs.
// rewrite any relative request to an absolute one.
function absolutizeUrl (url) {
return [window.location.origin, window.location.pathname, url]
.map(s => s.replace(/^\/+|\/+$/g, '')) // trim slashes
.filter(Boolean) // remove empty parts
.join('/');
}
initMap().catch(console.error);
</script>
</body>
</html>
Use this if you want a custom style (based on Corviont's default, another OpenMapTiles style, or something you build from scratch) wired directly to the raw vector tiles.
<html>
<head>
<!--
NOTE: For simplicity, this example uses unpkg for MapLibre.
Corviont deployments bundle MapLibre (and all other assets) locally for fully offline use.
-->
<script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' rel='stylesheet' />
<style>
html, body, #map { height: 100%; width: 100%; padding: 0; margin: 0; }
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
async function initMap() {
const dataset = await (await fetch("/dataset.json")).json();
new maplibregl.Map({
container: "map",
center: [dataset.ui_center.lon, dataset.ui_center.lat],
zoom: 15,
style: {
version: 8,
sources: {
corviont: {
type: "vector",
tiles: [absolutizeUrl(`tiles/${dataset.version}/{z}/{x}/{y}.mvt`)]
}
},
layers: [
// minimal example, displays roads only
{ id: "roads", type: "line", source: "corviont", "source-layer": "transportation" }
]
}
});
}
initMap().catch(console.error);
</script>
</body>
</html>
Use this to draw routes in your UI or power navigation features.
// replace with coordinates inside your pack's region
const from = { lat: 999, lon: 999 };
const to = { lat: 999, lon: 999 };
const input = {
locations: [from, to],
costing: "auto",
directions_options: { units: "kilometers" },
// Easiest to render in MapLibre: OSRM response + GeoJSON route geometry
format: 'osrm',
shape_format: "geojson"
};
const url = "/router/route?json=" + encodeURIComponent(JSON.stringify(input));
const output = await (await fetch(url)).json();
console.log(output);
/router proxies the Valhalla API (see the
full Valhalla API overview).
In the current Corviont stack, only
/router/route
is officially supported. Other Valhalla endpoints may be reachable but should be considered experimental.
Use this for a search box (forward geocoding).
// default limit is 5, max is 10
const query = "cafe";
const limit = 7;
const output = await (await fetch(`/geocoder/search?query=${encodeURIComponent(query)}&limit=${limit}`)).json();
console.log(output);
/*
Output: Array<GeocoderResult>
type GeocoderResult = {
id: number;
type: "poi" | "street" | "place";
name: string;
country: string;
city: string;
street: string;
housenumber: string;
postcode: string;
lat: number;
lon: number;
};
*/
Use this for "tap to get address" or "what's here?" interactions.
// default limit is 5, max is 10
// replace with coordinates inside your pack's region
const lat = 999;
const lon = 999;
const limit = 10;
const radius_m = 100;
const output = await (await fetch(`/geocoder/reverse?lat=${lat}&lon=${lon}&limit=${limit}&radius_m=${radius_m}`)).json();
// output has the same shape as /geocoder/search
console.log(output);
CORVIONT_PORT is missing
When running the stack, make sure you have a .env file
in the root of the cloned repository and that it contains:
CORVIONT_PORT=3000
Then restart the stack:
docker compose down
docker compose up -d
no matching manifest for linux/arm/v7
Images are built only for 64-bit architectures
(linux/amd64 and linux/arm64). If your
system is 32-bit (for example armv7l), Docker cannot
pull these images.
Check your architecture:
uname -m
# x86_64 or aarch64 => OK
getconf LONG_BIT
# 64 => OK
permission denied for /var/run/docker.sockOn some Linux / Raspberry Pi setups, Docker requires root access. You can either:
docker group (recommended):
sudo groupadd docker 2>/dev/null || true
sudo usermod -aG docker "$USER"
# Log out and log back in so the new group is applied
groups # should now include "docker"
Then retry:
docker compose up -d
sudo:
sudo docker compose up -d
After the initial image and data download, Corviont runs fully offline: tiles, routing, and geocoding are all served from containers on your host or device. Your UI talks only to the local HTTP entrypoint.
Public sample: github.com/corviont/monaco-demo.
Recommended evaluation path: request a pilot to receive the Vienna offline eval pack by email.
The form has been successfully submitted.