Omnigraph
그래프 엔진으로 컨텍스트를 조립하고, 객체 저장소 기반에서 에이전트가 코드처럼 그래프 데이터를 분기·타입·검토·병합·감사·거버넌스합니다
Vigie 공식 서비스가 아닌 개인 세계 지도로 경보와 재난 출처를 보여 줍니다
World map of official warnings (weather, floods) and disasters (earthquakes, cyclones, volcanoes, fires), with the source of each alert. A personal project, not an official service.
데이터GitHub @Axellum온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
저장소 정보는 아직 수집하지 않았습니다.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
World natural hazards and official weather warnings (MeteoAlarm, DWD, ECCC, NWS, MetService, Vigicrues, WMO registry) plus point weather, marine, floods, fires, air quality. Made to feed a standalone ESP32-P4 alert screen, a Home Assistant wall panel and Home Assistant itself (integration). GeoJSON RFC 7946, OASIS CAP 1.2. FastAPI. Made with AI.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Live demo: vigie.auxfilsdesidees.fr, the Vigie website, runs on this API (vigie-web).
A personal project, made with the help of AI and shared in case it helps someone. It comes as is, with no promise of support, availability or accuracy.
It gathers world natural hazards (earthquakes, tsunamis, volcanoes, cyclones, tornadoes, floods, fires, geomagnetic storms), weather (current conditions, 15-minute rain, forecast, marine, river discharge, air quality) and the official weather warnings of the national services (MeteoAlarm in Europe, the Deutscher Wetterdienst in Germany, Environment and Climate Change Canada, MetService in New Zealand, the National Weather Service in the United States, and Vigicrues for the rivers of France) from public sources, and serves them to:
docs/embedded-frame.md), see
vigie-display (Waveshare 7B);custom_components/vigie, see Home Assistant):
sensors and events to start automations;It sticks to the international standards: GeoJSON (IETF RFC 7946), OASIS CAP 1.2 for the warnings, the levels and words of the issuing services left as they are, the national services listed in the WMO registry, UTC timestamps.
This is not an official warning service. The alerts are relayed from public feeds. They can be late, incomplete or wrong, and must not be the only thing a decision rests on. GDACS alerts are automatic and do not replace official warnings. Follow the warnings of your national authorities.
No fabricated data. When a provider does not supply a value, the API reports it as unknown (
nullin JSON, an empty field in the frame). It never shows a plausible default. See ADR 0001.
The code is under the MIT licence. The data is not covered by it: it belongs to each
provider and stays under that provider's own terms. SOURCES.md lists, for each source,
the licence, the terms, the credit it asks for, its quota, and what could not be verified.
attribution field with the credit its source
asks for (the compact frame has no such field). The texts are declared in
app/attribution.py.app/commercial.py and listed in
SOURCES.md: allowed, forbidden or not verified. A new source
cannot be added without its line (a test fails).VIGIE_ENABLE_RAINVIEWER=0; the Docker image does).VIGIE_ENABLE_METSERVICE=0; on by default, the Docker
image keeps it on) (SOURCES.md).app/data/gem-active-faults.geojson.gz, used for near_places) is CC BY-SA 4.0
(GEM Global Active Faults Database, Styron and Pagani 2020), not MIT: share-alike, with its notice beside it
(app/data/README.md, ).| Endpoint | Purpose |
|---|---|
GET /v1/embedded/frame | Compact text frame for the displays (contract). |
GET /v1/snapshot | Watched zones (weather) and world hazards, compact JSON. |
GET /v1/point?lat=&lon= | Weather at one GPS position, marine (marine=true), river discharge, air quality, space weather, nearby hazards. |
GET /v1/geojson/hazards | Active hazards as GeoJSON (RFC 7946): one point each. has_forecast says whether a storm or a tsunami has a forecast; near_places and derived say what an earthquake or a volcanic event is close to (calculated by Vigie). See "Places near a hazard" below. |
GET /v1/geojson/hazards/{id}/forecast | The forecast of a hazard as GeoJSON: the cone, track and positions of a tropical cyclone, the epicentre and arrival times of a tsunami. See "Storms and tsunamis" below. |
GET /v1/earthquakes?lat=&lon= | The earthquakes (USGS and EMSC, merged) within radius_km of a position over the last hours, the strongest first, with their distance; min_magnitude filters them. No weather provider is asked. Not an official warning. |
GET /v1/sky/aurora?lat=&lon= | The aurora: NOAA SWPC's data as it gives it (OVATION summary, Kp observed and predicted for 3 days, solar wind, geomagnetic messages) and, for a place, Vigie's own estimate of the chance to see one hour by hour over the coming night, with its best window (magnetic latitude, Kp, darkness, Moon, forecast clouds; ADR 0013, measured on past nights). Not an official forecast. |
GET /v1/sky/aurora.geojson | SWPC's OVATION aurora forecast (30 to 90 minutes ahead) as map bands (GeoJSON). |
GET /v1/sky/eclipses?year_from=&year_to=&lat=&lon= | NASA's solar eclipses (2001 to 2100; by default the next ten years) as NASA states them and, for a place, what it sees of each: contacts, share of the Sun hidden, totality or annularity, computed by Vigie from NASA's Besselian elements and checked against NASA's tables (). |
The API has no authentication by default. Run it on a trusted network. Do not expose it to the Internet as is:
anyone who can reach it can add or delete watched zones and watched points and trigger collection cycles. Put a reverse proxy
that authenticates in front of it if you must. VIGIE_ADMIN_TOKEN is a first step: when it is set, every route that changes something
(POST, PUT, PATCH, DELETE: today /v1/zones, /v1/watched-points, /v1/scheduler/trigger and /v1/simulate) needs
Authorization: Bearer <token> and answers 401 without it; the reads never need it. Not set, nothing changes. The token is a shared secret in
a header: put TLS in front of an instance that is not on a trusted network (docs/architecture.md, ADR 0009).
On a public instance, POST and DELETE of /v1/watched-points must not be reachable from the Internet, like POST /v1/zones and
POST /v1/simulate: put the instance behind a reverse proxy that blocks them (the reads can stay open). Because a watched point is personal
(a home, for example), these two routes are also off by default: they answer 403 until the instance sets VIGIE_ENABLE_WATCHED_POINTS_WRITE=1
(GET /v1/health reports watched_points_write_enabled), which is for an instance that trusted clients alone can reach.
GET /v1/vigilances gives the warnings that the national weather services publish: MeteoAlarm (EUMETNET) for
39 European countries, the Deutscher Wetterdienst for Germany, Environment and Climate Change Canada,
MetService for New Zealand, the National Weather Service for the United States, and Vigicrues for the
river floods of France, and, for more countries, the national services of the WMO registry (below). It relays them; it does not issue any. The words of the issuer (headline, description and
instruction, in every language it wrote) are returned untouched.
One source answers for a country. Germany is the DWD (MeteoAlarm's copy of the German warnings is not asked for,
unless the DWD gives no answer); Canada and New Zealand have their own service; the other European countries are
MeteoAlarm's. France has two, which say two things: MeteoAlarm a colour per département, Vigicrues a colour per
section of river. A MeteoAlarm "flooding" warning that a Vigicrues section of the same answer says at the same
level or higher is left out; otherwise it stays (docs/data-sources.md).
GET /v1/vigilances?lat=44.9334&lon=4.8924 the warnings whose area contains this exact point
GET /v1/vigilances?country=FR&lang=fr every warning of a country, the texts in French only
GET /v1/vigilances.geojson?lat=..&lon=.. the same, as GeoJSON (the shape of each area is the geometry)
GET /v1/vigilances?lat=..&lon=..&river_radius_km=10 how far a river section still counts (default 5, at most 50)
Real answer for a point in the Drôme (France), cut: the feeds of 2026-10-20 played back by the tests.
{
"query": {"lat": 44.9334, "lon": 4.8924, "country": null, "river_radius_km": 5.0},
"countries": ["FR"],
"vigilances": [{
"id": "meteoalarm:b77d3bc0-76a2-4886-8da8-bac8d99f57fc:6-0-1",
"source": "meteoalarm", "country": "FR",
"attribution": "Source: EUMETNET – MeteoAlarm (https://meteoalarm.org/en/live/), ... | Issued by METEO-FRANCE",
"identifier": "2.49.0.0.250.1.FR.20261020114209.041024",
"sent_utc": 1792489329, "onset_utc": 1792533600, "expires_utc": 1792620000,
"message_type": "Alert", "status": "new",
"level": "YELLOW", "level_original": "Yellow",
"phenomenon": "flood", "phenomenon_original": "Flooding", "event": "Moderate flooding warning",
"issuer": "METEO-FRANCE",
"texts": [{"language": "fr-FR", "headline": "Vigilance jaune inondations", "description": "...", "instruction": "..."}],
"area": {"description": "Drôme", "geocodes": [{"scheme": "NUTS3", "value": "FR713"}, ...], "has_polygon": false, "localization": "geocode"},
"link": "https://feeds.meteoalarm.org/api/v1/warnings/feeds-france/b77d3bc0-...",
"derived": ["status", "level", "phenomenon"]
}],
"coverage": [{"source": "meteoalarm", "country": "FR", "status": "ok", "warnings_in_feed": 15, "warnings_not_matchable": 0}],
"attribution": "...", "disclaimer": "Time delays between this website and the www.meteoalarm.org website are possible. ..."
}
A river section is a line: the same route for Nîmes (Gard), 3.9 km from the Vistre, which Vigicrues had in orange that day (cut; the official bulletin of the territory said the same: "Vistre et Vidourle restent en vigilance orange"). The département's own MeteoAlarm "flooding", also orange, is left out of this answer because the section says it, finer.
{
"vigilances": [{
"id": "vigicrues:GA32", "source": "vigicrues", "country": "FR",
"attribution": "Source: Vigicrues, Service central d'hydrométéorologie et d'appui à la prévision des inondations (SCHAPI), https://www.vigicrues.gouv.fr. Licence Ouverte 2.0 | Last update of the flow: 2026-10-20T07:55:15+00:00",
"level": "ORANGE", "level_original": "3", "phenomenon": "flood",
"area": {"description": "Vistre", "geocodes": [{"scheme": "VIGICRUES_SECTION", "value": "GA32"}, ...], "has_polygon": false, "localization": "line"},
"distance_km": 3.93,
"web": "https://www.vigicrues.gouv.fr/bulletin_local/20#GA32",
"derived": ["level", "phenomenon"]
}],
"coverage": [{"source": "meteoalarm", "country": "FR", "status": "ok", ...}, {"source": "vigicrues", "country": "FR", "status": "ok", ...}]
}
level (GREEN, YELLOW, ORANGE, RED), phenomenon
(wind, rain, thunderstorm, snow-ice, heat, cold, flood, avalanche, coastal, fire, fog, other) and status
(new, update, cancel) are the API's own and are named in derived; the tables are in
docs/data-sources.md. A word no table covers gives null. level_original,
phenomenon_original, event and texts are the source's.attribution, and the answer has the disclaimer
that the terms of MeteoAlarm oblige a redistributor to publish. This is not an official warning service. Only the
sources that answered are credited.river_radius_km, distance_km in
the answer). Warnings of France, Belgium, Germany, Canada and of the countries that send polygons (for example
Switzerland and Norway) are matched. Warnings that name only MeteoAlarm EMMA_ID codes (Italy,
Austria, Poland...) are listed by country= but not matched to a point: the shapes of these codes have no stated
licence and are not bundled (SOURCES.md). For a point, coverage[].warnings_not_matchable counts them, so an empty
list is not read as "nothing to fear". The same goes for the NWS alerts that name zones instead of a polygon.
An operator can build the EMMA_ID shapes for his own instance (tools/build_geometries.py --emma, then
VIGIE_EMMA_GEOMETRIES).WS /v1/stream, send {"action": "follow", "lat": 44.9334, "lon": 4.8924}: the answer is a
vigilance_state (what the route would give), then, with VIGIE_SCHEDULER=1, a vigilance_push each time a
warning for that point appeared, was upgraded (previous_level says from what) or ended.
{"action": "unfollow", ...} stops it. The watched zones are followed by the scheduler too and their changes go to
every client. Nothing is pushed for what was already there when a point is first followed.The World Meteorological Organization keeps a registry of the national services that publish CAP warnings (the Severe
Weather Information Centre: 140 services in 135 countries on 2026-10-20). For a country that has no source of its own
the API reads the service of that registry, with the same rules (one source answers, the issuer's words untouched, the
fields of the API named in derived). VIGIE_WMO_SOURCES picks which services are read, because each issuer's own terms
count and most were not found or restrict copying (SOURCES.md, one row for each of the 28 services that were
looked at):
verified (the default): only the 7 services whose own terms were read and allow relaying them: Algeria, Argentina,
Barbados, Jamaica, Mexico, the Philippines, and Spain (AEMET instead of MeteoAlarm, because AEMET's feed gives polygons
where MeteoAlarm's gives codes: 34 against 0 on 2026-10-20, ADR 0006).all: every service that is not excluded, 95 services in 94 countries. Only for an instance that only its owner uses: 88 of
them have terms that are unknown or restrict copying. Hong Kong and China are never read.off: none.GET /v1/vigilances/countries the countries this instance serves with its settings: the sources, and whether the licence is verified
GET /v1/vigilances?country=MX the warnings of Mexico (SMN), polygons, the texts in Spanish as written
GET /v1/vigilances?lat=38.9067&lon=1.4206 Ibiza: the AEMET warnings whose polygon contains the point
Real answer for Ibiza at 18:34 UTC on 2026-10-20 (the feeds were read live; cut):
{
"query": {"lat": 38.9067, "lon": 1.4206, "country": null, "river_radius_km": 5.0},
"countries": ["ES"],
"vigilances": [{
"id": "swic:es-aemet-es:2.49.0.0.724.0.ES.20261020172954.645301PRP120201792517394:0.0",
"source": "swic", "country": "ES",
"attribution": "Source: AEMET (Agencia Estatal de Meteorología), Spain. Información elaborada por la Agencia Estatal de Meteorología. © AEMET | Warnings of a national meteorological service, from the registry of the WMO Severe Weather Information Centre (https://severeweather.wmo.int). The issuer's own terms apply to each warning",
"identifier": "2.49.0.0.724.0.ES.20261020172954.645301PRP120201792517394",
"onset_utc": 1792515600, "expires_utc": 1792526399, "message_type": "Alert", "status": "new",
"level": "YELLOW", "level_original": "amarillo",
"phenomenon": "rain", "phenomenon_original": "Aviso de lluvias de nivel amarillo",
"event": "Aviso de lluvias de nivel amarillo", "issuer": "AEMET. Agencia Estatal de Meteorología",
"texts": [{"language": "es-ES", "headline": "Aviso de lluvias de nivel amarillo. Ibiza y Formentera", "description": "Precipitación acumulada en una hora: 20 mm. ...", "instruction": "..."},
{"language": "en-GB", "headline": "Moderate rain warning. Ibiza y Formentera", "description": "...", "instruction": "..."}],
"area": {"description": "Ibiza y Formentera", "geocodes": [{"scheme": "AEMET-Meteoalerta zona", "value": "645301"}], "has_polygon": true, "localization": "polygon"},
"derived": ["status", "level", "phenomenon"]
}],
"coverage": [{"source": "swic", "source_id": "es-aemet-es", "country": "ES", "status": "ok", "warnings_in_feed": 19, "warnings_not_matchable": 0}]
}
coverage (source: "swic",
source_id): ok, stale (its last answer, marked), or unavailable. On the same day the Philippines' feed did not
answer in time from the test PC, and the API said unavailable for it, never an empty list.country= and counted in
coverage[].warnings_not_matchable; they are not placed on a point. A warning with a polygon or a circle is matched.level and phenomenon are derived from a colour the issuer writes (amarillo, Alerta Roja...) or the CAP
severity (Minor gives no level), and from the event words in English, Spanish, Portuguese and French; anything else is
null. The issuer's event, texts in every language and level_original stay as written.304) where the server gives a
validator, and each CAP document is read once. A simulated day of the scheduler (tests/test_swic_sobriety.py): 2,010
requests and 10.6 MB for the 7 services of the default, 36,924 requests and 51 MB for all with a followed point
in each of the 94 countries (the worst case; the number of clients adds nothing). A ceiling of 50,000 requests a day
covers the whole set. docs/data-sources.md has the rhythm and its reasons.The borders hold every country (Natural Earth, 244 of them), so a position in Japan, Australia or China finds its country. The answer names it
and says why nothing is read there, in uncovered and in note, with a stable reason_code that a client can translate:
GET /v1/vigilances?lat=35.6762&lon=139.6503 Tokyo (cut)
{
"countries": [], "vigilances": [], "coverage": [],
"uncovered": [{"country": "JP", "name": "Japan", "covered": false, "reason_code": "law_restricts",
"reason": "Japanese law reserves the giving of warnings to the Japan Meteorological Agency (Meteorological Service Act, article 23): this instance reads none of its warnings."}],
"note": "Japan (JP): Japanese law reserves ... The countries this instance reads: GET /v1/vigilances/countries."
}
GET /v1/vigilances/countries?uncovered=true every country, the ones with a source unchanged
The codes: no_source (no official warning feed is read for the country, and nothing more is known), licence_unverified, terms_restrict,
terms_contradictory, redistribution_forbidden, law_restricts and source_off. A reason is written only when it was read: where, and what is
not verified, is in SOURCES.md, "Countries with no source". A position at sea, further than about 25 km from a coast, is in no country and
says so. ADR 0009.
Every warning a source lists when the instance reads it is archived, in force or not (a cancellation, a warning past its end), so
that the warnings of the past can be read again. The archive keeps the warning as GET /v1/vigilances served it: the issuer's
words untouched in every language, level and phenomenon the API's own (named in derived), and when the instance first and last
saw it. One item per message and per area: a warning seen again is the same item, an update of the source is a new one. It sends
no request of its own to any provider.
GET /v1/vigilances/archive?country=ME&min_level=ORANGE the orange and red warnings of Montenegro the instance saw
GET /v1/vigilances/archive?area=FR813&since=1792400000 the warnings of the Hérault (NUTS3 code) since a date
GET /v1/vigilances/archive?phenomenon=wind&source=dwd&limit=50 the DWD's wind warnings, 50 at a time (`offset` for the next page)
GET /v1/vigilances/archive/stats?days=30 what the archive took in per day, and what it weighs on disk
Filters: country, area (a code, NUTS3:FR101 or FR101, or a part of the area's name), source (swic:<service> for one service
of the WMO registry), phenomenon, level, min_level, and since / until (UTC epoch seconds: the warnings whose period, from
the onset to the later of end and expiry, overlaps them). The newest first seen come first. A real item, archived on 2026-10-28 (cut):
{
"first_seen_utc": 1793220818, "last_seen_utc": 1793220818,
"vigilance": {
"id": "meteoalarm:cb8fa408-3c1c-4166-87d4-8bc4419bf9f3:0-0-1", "source": "meteoalarm", "country": "ME",
"identifier": "2.49.0.1.499.0.1793173001904", "onset_utc": 1793170800, "expires_utc": 1793228340,
"level": "ORANGE", "level_original": "Orange", "phenomenon": "wind", "phenomenon_original": "Wind",
"issuer": "Zavod za hidrometeorologiju i seizmologiju",
"texts": [{"language": "cnr", "headline": "Upozorenje na vjetar", "description": "Udari vjetra >17 m/s (>50km/h ili oko 8 Bofora)"},
{"language": "en-GB", "headline": "Wind Warning", "description": "Wind gusts >17 m/s (>50km/h or about 8 Beauforts)"}],
"area": {"description": "Adriatic coast", "geocodes": [{"scheme": "EMMA_ID", "value": "ME002"}]},
"derived": ["status", "level", "phenomenon"]
}
}
reads says, per UTC day, in which hours each
source was read with success for the country asked.last_seen_utc is right to within ten minutes. The shape of an area, when the source gives one, is kept once whatever the number
of warnings that share it.VIGIE_ARCHIVE_KEEP_DAYS (0, the default, keeps everything); keep_days in the answers. GET /v1/health reports
vigilance_archive (how many warnings, since when, the last write or error).GET /v1/events groups what the other routes list one by one (ADR 0012):
other or unknown) and one level
are one event when their periods overlap and their areas are close (each within 300 km of another). Areas whose shape this
instance does not know make an event of their own, split by state for the UGC zones of the NWS, never a guessed place.GET /v1/events the hazards of the world and the warnings of every country this instance serves
GET /v1/events?country=CH the warnings and hazards of one country (404 for a country with no source)
GET /v1/events?compact=true without the warnings and hazards themselves (their ids stay in `alert_ids`)
GET /v1/events?lang=fr,en the texts of the warnings in these languages only (a filter, never a translation)
GET /v1/events?min_impact=60 a world view of what counts (see the impact below)
GET /v1/events?families=wind,rain-flood only these families of phenomena
GET /v1/families the sixteen families and what each holds
The contract. An event is ours: family, impact and impact_reasons, title, level, areas_count, the period and bbox are
worked out by Vigie and listed in derived; alerts holds the warnings exactly as GET /v1/vigilances serves them, hazards the hazards as GET /v1/geojson/hazards
describes them, untouched. The title is English and ours: a client writes its own in its language from phenomenon (or category),
country, areas_count and area_names; the source's words for the phenomenon are in phenomenon_words, as given. The strongest impact
comes first, then the highest level, then the broadest. Two real events of the world view on 2026-10-28 at 21:15 UTC (cut):
{
"events": [
{
"id": "warnings:f47b33705da9b6af", "kind": "warnings", "phenomenon": "cold", "category": null, "family": "cold",
"impact": 23, "impact_reasons": ["official yellow warning", "13 areas"], "level": "YELLOW", "title": "Cold · Switzerland · 13 areas", "country": "CH", "areas_count": 13,
"area_names": ["Vispertal - Nanztal", "Lötschental - Naters", "Val d'Hérens", "Turtmanntal", "Locarnese", "..."],
"phenomenon_words": ["Low-temperature"], "start_utc": 1793228400, "end_utc": 1793257200,
"bbox": [7.3399, 45.8198, 9.3, 46.4988], "sources": ["meteoalarm"],
"alert_ids": ["meteoalarm:0345d287-62ca-4d59-8424-02388eb1d661:0-0-0", "..."],
"alerts": [{"id": "meteoalarm:0345d287-62ca-4d59-8424-02388eb1d661:0-0-0", "source": "meteoalarm", "level": "YELLOW",
"level_original": "Yellow", "phenomenon": "cold", "phenomenon_original": "Low-temperature",
"area": {"description": "Vispertal - Nanztal"}, "...": "every field of GET /v1/vigilances"}],
"hazards": [], "attribution": "Source: EUMETNET – MeteoAlarm ...",
"derived": ["family", "impact", "impact_reasons", "level", "title", "areas_count", "start_utc", "end_utc", "bbox"]
},
{
"id": "earthquakes:9293c7eacac23a2f", "kind": "earthquakes", "phenomenon": null, "category": "EARTHQUAKE", "family": "earthquake",
"impact": 35, "impact_reasons": ["earthquake of magnitude 5.5 (yellow)", "7 earthquakes in the sequence"], "level": "YELLOW",
"title": "7 earthquakes · strongest: M 5.5 - Reykjanes Ridge", "country": null, "areas_count": null,
"start_utc": 1793187469, "end_utc": 1793216732, "bbox": [-32.44, 58.056, -32.01, 58.95], "sources": ["USGS", "EMSC"],
"alerts": [], "hazards": [{"id": "usgs_us7000ttnc", "title": "M 5.5 - Reykjanes Ridge", "value_primary": 5.5, "...": "..."}],
"derived": ["family", "impact", "impact_reasons", "level", "title", "start_utc", "end_utc", "bbox"]
}
],
"counts": {"events": 95, "by_family": {"...": "..."}, "by_level": {"RED": 5, "ORANGE": 19, "YELLOW": 54, "GREEN": 16, "unknown": 1}},
"coverage": ["... one line per source and country, as in GET /v1/vigilances ..."],
"hazard_sources_answered": 7, "hazard_sources_total": 7
}
That evening the world view held 95 events instead of 528 warnings in force and 46 hazards: the 13 Swiss "Low-temperature" areas
are one event, the 7 earthquakes of the Reykjanes Ridge one sequence. counts.by_level counts the green apart, so that a client
leaves it out of its counters. coverage says which source did not answer: its warnings are missing, which is not "nothing in force".
The impact score, 0 to 100, ranks the events; it is ours, not a probability and not an official level (the table and its reasons:
ADR 0012). An official warning is 90 red, 60 orange, 20 yellow, 0 green (10 when its level
is unknown), plus one point per doubling of its areas, up to 9; a tsunami message 100 to 30; an earthquake 85, 60, 30 or 5 by the API's
severity (PAGER, magnitude, tsunami flag), plus one per earthquake of its sequence, up to 5; a tropical cyclone 60 at least; a volcano
85 at warning and 60 at watch; a GDACS alert 90 red and 65 orange. min_impact=60 is the world view: the official orange and red, GDACS
orange and red, an earthquake rated orange or red, every cyclone, a volcano at watch or warning, every tsunami message. "Felt" is not
read by this API, so it does not count.
The families (family, GET /v1/families, families=): wind, rain-flood, thunderstorm, snow-ice, heat, cold, fog,
fire, coastal, earthquake, volcano, cyclone, tsunami, air (empty: no air-quality warning is read), space, and other. They group
the API's phenomena and hazard categories, each in exactly one family; the source's words stay in each warning.
What a world view costs: without country, every country the instance serves is read (39 MeteoAlarm feeds, the national services,
the 7 services of the WMO registry), through the same caches as GET /v1/vigilances?country=: a feed is asked at most once per its
cache lifetime (180 s for MeteoAlarm) whatever the number of clients, and at most 40 new CAP documents per request
(tests/test_events.py). The first world view after a quiet spell waits for the slowest feed (66 s on the PC on 2026-10-28; 7 to 18 s
afterwards): ask it from a server that keeps the answer, not from each browser.
GET /v1/travel and GET /v1/travel/{country} relay what five foreign ministries advise their citizens: the FCDO (United Kingdom,
GOV.UK), the Department of State (United States), Global Affairs Canada, Smartraveller (Australia) and NederlandWereldwijd (the
Netherlands), each read from its open feed, whole, an hour at a time. Each level stays in the ministry's own words, its text untouched,
with the date of its version (published_utc) and when Vigie read it (checked_utc). Germany and France are listed with the reason
they are not read (ministries[].why_not_read).
GET /v1/travel every country, each ministry's level, and the comparison
GET /v1/travel/UG Uganda: the texts, the comparison and the archive
{
"country": "UG",
"comparison": {"compared": ["au", "ca", "nl", "us"], "disagree": true, "gap": 2, "strictest": ["us"], "laxest": ["au", "nl"],
"derived": ["compared", "disagree", "gap", "strictest", "laxest"]},
"advices": [{
"ministry": "us", "title": "Uganda", "level": 4, "level_texts": {"en": "Level 4: Do Not Travel"},
"common_min": 4, "common_max": 4, "published_utc": 1780531200, "checked_utc": 1793220512,
"url": "https://travel.state.gov/content/tsg_aem/us/en/home/international-travel/travel-advisories/destination.uga.html",
"summary": [{"language": "en", "format": "html", "content": "..."}],
"derived": ["country", "part", "common_min", "common_max"], "attribution": "Source: Bureau of Consular Affairs, U.S. Department of State (https://travel.state.gov)"
}]
}
derived: the country code (each ministry names its destinations its own way), the part flag (an
advice for "Gaza" or the "Azores" is not the advice of the whole country), and a common scale (common_min, common_max: 1 normal
precautions to 4 avoid all travel) that lets the ministries be compared. It is not an official level. The FCDO has no numbered scale:
its statuses give a range ("no advice against travel" is 1 to 2). Two ministries disagree when their ranges cannot meet.history) starts the day the instance first reads the ministries: each new version of an advice is kept, the first
one marked first. The FCDO publishes its own history of changes: it is in source_history, as given.loading. The tables, the costs and the licences: docs/data-sources.md and SOURCES.md.GET /v1/travel/health relays the latest publications of the ECDC (European Centre for Disease Prevention and Control) on
communicable disease threats, each with its title, summary, date and link as given (CC BY 4.0). Its feeds name no country:
countries is matched by Vigie on the names in the title and the summary (in derived), and a country can be named for another
reason than a threat there. GET /v1/travel/{country} carries the items that name the country (health). The WHO and the CDC are
listed with the reason they are linked, not read.GET /v1/geojson/hazards also lists the tropical cyclones of the National Hurricane Center (NHC) and the tsunami
messages of the U.S. Tsunami Warning Centers (the NTWC for the United States and Canada, the PTWC for the Pacific and
the Caribbean). Each is one point like every other hazard (a storm at its current position, a tsunami at the
epicentre), with the usual properties and has_forecast. The forecast is another route, so that a map fetches it only for
the storm a visitor opens:
GET /v1/geojson/hazards/nhc_al092026/forecast the cone, track and forecast positions of a storm
GET /v1/geojson/hazards/tsunami_pheb_26282000/forecast the epicentre and the arrival times of a tsunami
GET /v1/vigilances?source=tsunami every tsunami message in force, the centre's words untouched
source, issued_utc, advisory, advisory_url and attribution;
each feature has properties.kind: cone (Polygon), track (LineString), position (Point with valid_utc, wind_kt,
pressure_hpa...) and watch_warning for a storm; epicentre and eta (Point with eta_utc) for a tsunami. A cone across
the date line is cut in two as RFC 7946 asks. Coordinates have 3 decimals; a pressure the NHC does not give is null.
Hurricane Isaias's advisory 13A weighs 22.7 kB. A hazard with no forecast answers an empty collection and a reason; an
unknown id is a 404.docs/data-sources.md.country=US
or CA, or by source=tsunami, and never matched to a point.304 of a few
hundred bytes), 230 kB and 116 kB on a quiet day; a message once, a bundle once per advisory.
ADR 0005.GET /v1/fires and GET /v1/fires/grid serve the active fires seen by the VIIRS instruments of NOAA-20 and NOAA-21 (the near
real-time product of NASA FIRMS, 375 m pixels). They need no key: the
instance reads the static files that FIRMS publishes. An instance that turned the source off (VIGIE_ENABLE_FIRMS=0, see "Configuration")
answers 404 and GET /v1/health lists the source in disabled_sources. The fires are not in GET /v1/geojson/hazards, which a map loads
whole.
GET /v1/fires?bbox=28,-22,38,-12 the detections of a box, the strongest first (at most 1,000)
GET /v1/fires?bbox=170,-25,-170,-10&hours=12&min_confidence=nominal across the antimeridian, the last 12 hours
GET /v1/fires/grid?cell_deg=5 the world counted per cell of 5 degrees
GET /v1/point?lat=-15.4&lon=28.3&fire_radius_km=50 fires_nearby: count, nearest distance, highest power
acq_utc, satellite, instrument, confidence (low, nominal, high), frp_mw (fire radiative
power), bright_ti4_k, bright_ti5_k, scan_km, track_km, daynight, version. A value the file does not give is null.bbox is west,south,east,north (a west greater than the east crosses the antimeridian; latitudes within -90
and 90, south < north); hours up to the window of the files the instance reads (48 by default, VIGIE_FIRMS_WINDOW_HOURS; more is a 422); limit 1,000 by default and 5,000 at most (the grid: 5,000 and 20,000); cell_deg 0.25,
0.5, 1, 2, 5 or 10. Anything else is a 422. metadata says how many detections matched and whether the answer was cut.fires_nearby is named in derived and
never raises local_alert_level.attribution) and its disclaimer (disclaimer) are in every answer.VIGIE_FIRMS_REFRESH_MINUTES), only when someone asks (and once at start-up), with one request that is closed after its headers when
nothing changed, and reads only the bytes added to a file that grew (checked against its ETag). A day with 1,000 clients is about 86
requests and 56 MB by simulation, with no key and no transaction (the first version: 60 reads, 2,160 transactions, 170 MB). A read that
gets no byte for 25 seconds fails; that source is asked again after 1, 2, 5, then every 10 minutes, and its last reading is still served,
with stale true. A request never waits for FIRMS once something was read.
.GET /v1/tides and GET /v1/tides/coast compute the tide locally, from the harmonic constants of the EOT20 ocean tide model
(DGFI-TUM, CC BY 4.0) bundled in app/data (2.2 MB, built by tools/build_tides.py, which checks the result against pyTMD to within
4 mm). No provider is asked and no quota is spent (ADR 0015).
GET /v1/tides?lat=43.48&lon=-1.56 the next two days at Biarritz, every 30 minutes
GET /v1/tides?lat=48.64&lon=-2.03&start=2026-11-03&days=3&interval_minutes=10
GET /v1/tides/coast?bbox=-6,43,3,51.5 the tide now along the coasts of a box, for a map
interval_minutes (10, 15, 30 or 60) over days (up to 7) from start (default: now,
rounded down to the hour), the high and low waters with the range of each tide, the tide now (height, rising or falling, next high and
low water), and an estimated French coefficient (100 times the range of each tide at Brest divided by 6.10 m: ours, unofficial; the
official coefficient is SHOM's and is not used). Times are UTC epoch seconds; heights are relative to the model's mean sea level,
not to the chart datum of a port.limits). Not for navigation.GET /v1/surf/spots lists the 51 spots of app/data/surf-spots.json, built once from OpenStreetMap by tools/build_spots.py
(ODbL 1.0, "© OpenStreetMap contributors", app/data/surf-spots.NOTICE.md). GET /v1/surf/spots/{id}?days=3 (1 to 7) gives the
forecast of one, hour by hour.
GET /v1/surf/spots the spots, with the direction each beach faces
GET /v1/surf/spots/soorts-hossegor-plage-sud-point-d-or the next three days at Hossegor
GET /v1/surf/overview every spot now, and its best slot of the next three days
derived): the direction the beach faces (from the OpenStreetMap coastline), the swell's angle to it and its
power, the wind relative to the coast (offshore, onshore, cross-shore), the tide at the spot (EOT20, as GET /v1/tides), and a
rating of ours, 2 points at most per criterion, every threshold in rules and every criterion shown with its value. The best slot
is the daylight hour of the next 72 with the best share of points.null: the bottom of a spot (sand, reef), the tide and the swell it works best with. No open source gives them and
none is guessed; the tide does not count in the rating until a spot's best tide is given with a source.Not for safety and not for navigation: look at the sea on the spot, the flags and the lifeguards before going in.
An earthquake or a volcanic event says what it is close to. Each hazard of /v1/geojson/hazards (and of /v1/history, and of
/v1/snapshot and /v1/point with compact=false) carries near_places, nearest first, and names it in derived:
"near_places": [
{"name": "Azuero Fault Zone", "kind": "fault", "distance_km": 4.3, "sources": ["GEM"]}
],
"derived": ["near_places"]
M 7.7 of Panama, 9 October 2026, USGS us6000u18k: the distance to the mapped surface trace, checked against a second computation
(Vincenty on the ellipsoid, to the original trace: 4.19 km).
severity. Show it as "calculated by Vigie".
[] means nothing is near; null means it was not worked out (a hazard that is compared with nothing, such as a storm or a flood; the fault
file cannot be read; a simulated alert; or the compact profile of /v1/snapshot and /v1/point, which keeps to 16 KiB, and so the
hazards pushed on /v1/stream).near_places. The hazards are shared with every client, a public website included, and a watched point
is personal (a home, for example): its name would leak, and anyone who could write a point could make any text appear in everyone's hazards.
A watched point is listed by /v1/watched-points and its status, and nowhere else.VIGIE_NEAR_* below).app/data/README.md,
SOURCES.md. The credit is in the attribution of the hazard that names one.POST /v1/watched-points {"name": "Etna", "kind": "volcano", "lat": 37.751, "lon": 14.994, "radius_km": 30}
GET /v1/watched-points/status?days=7
status lists, for each point, the events within its radius over the last days (an earthquake by the time it happened, any other hazard by the last
time the API saw it active) and a state: events; quiet (nothing happened, and every hazard source answered and has been read without a gap since
quiet_since_utc); or unknown, with unreachable_sources. The API only knows what it saw: an instance started an hour ago says so
(window_covered: false) instead of "nothing in the last week".
Like /v1/zones, the watched-point routes have no authentication, and the list and the status are readable by anyone who can reach the API:
do not watch your home on an instance that others can read. POST and DELETE are off unless VIGIE_ENABLE_WATCHED_POINTS_WRITE=1 (403
with a message otherwise; the reads always work), and on a public instance they must be blocked by a reverse proxy, as for /v1/zones and
/v1/simulate. Details, numbers and what is not verified: docs/data-sources.md and
ADR 0008.
custom_components/vigie is a Home Assistant integration that reads these warnings for a place and gives a
maximum-level sensor, a "warning active" binary sensor and an event entity (new, upgraded, ended) to start
automations. It needs an instance of this API that Home Assistant can reach, and nothing else. Install, options, the
rules of "unknown is not none", an automation example and the limits: docs/home-assistant.md.
It comes as is, like the rest of the project, and it is not an official warning service either.
Python 3.12 or newer (the CI covers 3.12 and 3.13).
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt (Windows)
.venv\Scripts\python -m uvicorn app.main:app --host 0.0.0.0 --port 8080
On Windows, scripts\run.ps1 does all of this (see below). Then open
http://localhost:8080/dashboard or http://localhost:8080/v1/embedded/frame.
docker build -t vigie-api .
docker run --rm -p 8080:8080 -v vigie-data:/data -e [email protected] vigie-api
The image (python:3.13-slim, built for linux/amd64 by the CI) runs as a non-root user, keeps its
database in the /data volume and has a health check on /v1/health. RainViewer is off in the image; add
-e VIGIE_ENABLE_RAINVIEWER=1 for a personal instance. NASA FIRMS (the active fires) is on, also in the image:
it downloads about 56 MB a day when someone asks the fire routes, and -e VIGIE_ENABLE_FIRMS=0 turns it off. The publication workflow pushes
ghcr.io/axellum/vigie-api when a version tag (v*) is created; until a tag exists, build the image
yourself.
All settings are optional environment variables.
| Variable | Default | Meaning |
|---|---|---|
VIGIE_DB_PATH | vigie.db in the repository root (git-ignored); /data/vigie.db in the Docker image | SQLite file (watchlist, history, last snapshot). |
VIGIE_SCHEDULER | 0 | 1 starts the background scheduler (refresh every 90 s, WebSocket push). |
VIGIE_ARCHIVE_KEEP_DAYS | 0 | How many days an archived official warning is kept after the instance last saw it (GET /v1/vigilances/archive); 0 keeps everything. Older warnings, the shapes no warning uses any more and the older reads are deleted once a day. |
VIGIE_ENABLE_SIMULATION | 0 | 1 enables POST /v1/simulate (and the dashboard buttons). The route has no authentication and writes the simulated alerts into the hazard history: enable it only on an instance that trusted clients alone can reach. |
VIGIE_ENABLE_WATCHED_POINTS_WRITE | 0 | 1 enables POST and DELETE of /v1/watched-points (403 otherwise; the reads always work). The routes have no authentication and a watched point is personal: enable it only on an instance that trusted clients alone can reach, and block the two routes in the reverse proxy of a public instance. |
VIGIE_ADMIN_TOKEN | none | When set, every request that is not a read (POST, PUT, PATCH, DELETE) must carry Authorization: Bearer <token>, else the answer is 401; the reads never need it. Unset or blank: nothing changes (the network is trusted). It stacks with the two switches above: a disabled route stays 403. Compared in constant time; it appears in no log and no answer (GET /v1/health only says admin_token_set). A shared secret in a header: put TLS in front of any instance that is not on a trusted network. |
VIGIE_ENABLE_RAINVIEWER | 1; 0 in the Docker image | 0 stops asking RainViewer (the radar fields of /v1/point stay unknown). Its free API is for personal and educational use: set on an instance that serves other people. |
No API key is needed by any provider (NASA FIRMS accepts an optional one: VIGIE_FIRMS_MAP_KEY). A grid value outside 0.0001 to 10 degrees is ignored (the
default is used). Only the request sent to the provider and the cache key use the snapped point: the
response still reports the exact position of the client, and the distances to the hazards use it.
The calls to the providers should not grow with the number of clients: they depend on the number of
distinct 5 km cells asked for and on how fast each source really changes (grids, one cache entry per
cell, one request for simultaneous callers, TTLs from each source's rhythm, a call budget; see
docs/architecture.md and docs/data-sources.md).
tests/test_call_sobriety.py plays a simulated day with the scheduler on and its 5 default zones,
and 1,000 clients on 50 points (20 each, polling every 30 minutes, 10 points are harbours that also ask
for the marine conditions; no two points share a cell, so the grids do not help), against a simulated
upstream, and counts the requests each source receives:
| Source | Calls a day, before | after |
|---|---|---|
| Open-Meteo forecast | 17,212 | 6,167 |
| Open-Meteo air quality | 7,182 | 1,320 |
| Open-Meteo river discharge | 6,122 | 200 |
| Open-Meteo marine | 2,216 | 240 |
| Open-Meteo, weighted (the free quota is 10,000 a day) | 46,502 | 8,412 |
| USGS earthquakes, EMSC | 1,980 each | 1,393 each |
| NOAA NWS alerts | 1,069 | 705 |
| GDACS | 676 | 474 |
| RainViewer | 662 | 144 |
| NOAA SWPC | 403 | 286 |
| tsunami.gov (list of bulletins, conditional) | n/a | 1,393 (330 kB) |
| NOAA National Hurricane Center (active storms, conditional) | n/a | 286 (140 kB) |
| USGS volcanoes | 212 | 144 |
| MeteoAlarm feeds (France and Iceland: the countries of two of the five zones) | n/a | 640 |
| MeteoAlarm CAP documents | n/a | 0 |
| Vigicrues (Paris is in France): reads of the flow, 2.2 MB each, only when a publication changed it (was 138, 310 MB, while a section is orange; 46, 103 MB, on a calm day) | n/a | 4 (9.1 MB) |
| Vigicrues: looks at the publication image, about 330 bytes each (a section orange: every 5 minutes; calm: 15) | n/a | 240 (88 on a calm day) |
| DWD, ECCC, MetService (no zone in Germany, Canada or New Zealand) |
"Before" is main at commit 1864e25 (same simulation, measured once); "after" is this code. The
heaviest Open-Meteo hour weighs 2,305 before and 412 after (quota 5,000), the heaviest minute 497
and 231 (quota 600). With clients polling every 15 minutes instead, before becomes 54,766 and after
8,667: the load of the clients hardly changes the "after". The 55 distinct cells of this scenario use
84 % of the free quota; beyond that the budget stops the requests and serves the cache, flagged stale.
Play it yourself: python -m tests.simulation (about 20 seconds).
The official warnings add one MeteoAlarm feed per needed country, 320 requests a day when the scheduler keeps it
fresh (a 180 s cache, asked again at the next 90 s cycle), and nothing for the NWS: its answer is the hazards' own,
which the same simulated day confirms (705 calls with or without the warnings).
tests/test_vigilance_sobriety.py plays 500 clients on 50 positions of four countries for 6 hours: 6,000 answers cost
320 feed requests (80 per country) and 4 CAP documents.
A zone, a followed point or a client in Germany costs the DWD 480 requests a day (479 of them 304, a few
hundred bytes) and no longer the German MeteoAlarm feed (320 a day); in Canada ECCC 240 (31 KB each with
gzip); in New Zealand MetService 240 (239 304). However many people read the warnings, a source is asked at
most once per cache lifetime (720, 288, 288 and 288 a day for DWD, ECCC, MetService and Vigicrues, whose requests are
330-byte looks at an image). tests/test_national_sobriety.py plays these days.
Vigicrues is the heavy one: its flow weighs 2.2 MB, is not compressed, has no validator, ignores Range and takes
no filter, and no lighter endpoint carries the level. So the collector asks (HEAD) the national map image that the
same publication regenerates, every 15 minutes (5 while a section is orange or red), and reads the flow only when the
image changed, and at the latest after 12 hours. On the simulated day of the five default zones that is 4 reads and
9.1 MB (9.0 MB on a calm day; 6.8 MB a day once started: the two publications, at 10 a.m. and 4 p.m., and one safety
read) against 138 reads and 310 MB (46 and 103 MB calm) before. A flood published at 4 p.m. is served at the next
look: 16.5 minutes later at most when calm, 6 in a flood (test_a_flood_published_in_the_afternoon_is_served_within_one_look_and_nothing_is_lost).
docs/data-sources.md has the measures.
The display fetches http://<PC address>:8080/v1/embedded/frame. Give the PC a fixed address in the
router so this URL does not change. Below, <repository folder> is the folder where you cloned this
repository.
Start the API, from any folder, in a normal (not administrator) PowerShell:
powershell -NoProfile -ExecutionPolicy Bypass -File <repository folder>\scripts\run.ps1
The script creates .venv in the repository on the first run, installs requirements.txt
(again only when the file changes) and starts uvicorn app.main:app --host 0.0.0.0 --port 8080.
Options: -Port, -BindAddress, -LogFile <file>, -NoInstall. Stop it with Ctrl+C.
Allow the display to reach it. Windows Firewall blocks inbound connections by default. Run this one line once, in PowerShell opened as administrator (it opens TCP 8080 on the Private network profile, for the local subnet only):
New-NetFirewallRule -DisplayName "Vigie API 8080" -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow -Profile Private -RemoteAddress LocalSubnet
The home network must be set as Private in Windows (Settings, Network and Internet). To
remove the rule later: Remove-NetFirewallRule -DisplayName "Vigie API 8080".
Check from another device on the same network:
Invoke-WebRequest http://<PC address>:8080/v1/health -UseBasicParsing
Start it at logon with Task Scheduler (nothing is created by the repository). Task Scheduler, Create Task... (not Basic Task):
Vigie API; "Run only when user is logged on".powershell.exe. Arguments:
-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "<repository folder>\scripts\run.ps1" -LogFile "<repository folder>\logs\vigie-api.log".
Start in: <repository folder>.logs\vigie-api.log (git-ignored); stop the server by ending the task in Task Scheduler.python -m pytest # no test touches the network
python -m ruff check . # lint
python tools/capture_frame.py frame.txt # write a REAL frame (uses the live providers)
python tools/fake_instance.py # a fake instance whose warnings you choose (to try the Home Assistant integration)
python tools/check_live_feeds.py # ask each real provider once, check the parsers still read it
python tools/build_geometries.py # rebuild app/data (borders and NUTS shapes) from Natural Earth and Wikidata
tools/capture_frame.py starts the application in-process, fetches
/v1/embedded/frame?device_type=waveshare-7b from the real providers and writes it to the given
file. It is used to produce the test fixture of the display repository.
CI (.github/workflows/):
ci.yml, on every push to main and every pull request: ruff and pytest on Python 3.12 and 3.13, and a
job that builds the Docker image, runs it and asks /v1/health and /v1/embedded/frame.secret-scan.yml: gitleaks over the whole git history, on every pull request and push to main.live-feeds.yml: every Monday and on demand, never in a pull request, tools/check_live_feeds.py asks
each real provider once (MeteoAlarm, the NWS warnings, the DWD, ECCC, MetService, Vigicrues, the NHC and the tsunami centres included). A red run means a provider changed its
format, removed an endpoint, or sent a level or phenomenon word the tables do not know.home-assistant.yml: the Home Assistant integration (custom_components/vigie), only when it changes, and weekly:
its tests (tests_ha/, in an environment of their own: Python 3.14 and the Home Assistant release they pin), hassfest
and the HACS validation.publish-image.yml: pushes the Docker image to ghcr.io/axellum/vigie-api when a v* tag is pushed.| Domain | Source |
|---|---|
| Earthquakes | USGS, EMSC / SeismicPortal, GDACS |
| Disasters | GDACS, NOAA NWS, USGS Volcano Hazards Program, NOAA National Hurricane Center (tropical cyclones), NOAA U.S. Tsunami Warning Centers (tsunami messages) |
| Official weather warnings | MeteoAlarm (EUMETNET, 39 European countries), Deutscher Wetterdienst (Germany), Environment and Climate Change Canada, MetService (New Zealand), NOAA NWS (United States), Vigicrues (river floods of France), the national services of the WMO Severe Weather Information Centre registry (7 read by default: Algeria, Argentina, Barbados, Jamaica, Mexico, the Philippines, Spain) |
Active faults (near_places) | GEM Global Active Faults Database, the traces that have a name (CC BY-SA 4.0, bundled) |
| Space weather | NOAA SWPC |
| Weather | Open-Meteo (primary), MET Norway (fallback), RainViewer (radar) |
| Marine, river discharge, air quality | Open-Meteo Marine, Flood (GloFAS), Air Quality (CAMS) |
| Tides (computed) | EOT20 ocean tide model (DGFI-TUM, CC BY 4.0, bundled); measured against REFMAR (SHOM) and NOAA CO-OPS |
Licence, terms, credit, quota and clauses of each source, and what is not verified: SOURCES.md.
How each one is cached and what it costs in calls: docs/data-sources.md.
SOURCES.md: the sources, their licences, credits and quotas.docs/architecture.md: components, data flow, limits.docs/embedded-frame.md: the frame contract (single source of truth).docs/data-sources.md: how each source is used, cache and call volume.docs/home-assistant.md: the Home Assistant integration.docs/decisions/: architecture decision records.CHANGELOG.md, AGENTS.md (guide for AI coding agents).SOURCES.mdVIGIE_WMO_SOURCES): the terms of each issuer count, and most were not found or
restrict copying. The default reads only the 7 whose own terms were read and allow relaying them; all is for an instance
that only its owner uses. Hong Kong and China are never read (SOURCES.md).GET /v1/sky/eclipses/{date}?lat=&lon= | One eclipse; for a place, also the Moon on the Sun's disc from the first contact to the last and, within 48 hours, the forecast clouds of the hour of the maximum. |
GET /v1/sky/eclipses/{date}.geojson | Its map (GeoJSON), drawn by Vigie: bands of the share of the Sun hidden, the path of totality or annularity and its central line. |
GET /v1/fires?bbox=w,s,e,n, GET /v1/fires/grid | The active fires seen by NASA FIRMS (VIIRS, NOAA-20 and NOAA-21): the detections of a box as GeoJSON, or counted per cell for small zooms. No key needed (404 only on an instance that turned the source off with VIGIE_ENABLE_FIRMS=0). See "Active fires" below. |
GET /v1/tides?lat=&lon=, GET /v1/tides/coast?bbox=w,s,e,n | The tide at a coastal point (heights, high and low waters, ranges, the tide now, an estimated French coefficient), or the tide now along the coasts of a box: computed by Vigie from the EOT20 ocean tide model, no provider asked. Not for navigation. See "Tides" below. |
GET /v1/surf/spots, GET /v1/surf/spots/{id}?days=, GET /v1/surf/overview | The surf spots of the French Atlantic and Channel coasts (OpenStreetMap), and the hourly forecast of one: swell (Météo-France MFWAM), wind, tide, and a rating of ours, explained criterion by criterion, with the best slot of the next three days; the overview gives every spot's current hour and best slot at once. Not for safety. See "Surf" below. |
GET /v1/cap/alerts | Active hazards as OASIS CAP 1.2. |
GET /v1/vigilances?lat=&lon= or ?country=FR or ?source=tsunami | Official weather warnings that cover a point, or all those of a country (MeteoAlarm, DWD, ECCC, MetService, NWS, Vigicrues, and the national services of the WMO registry), or the tsunami messages in force, the issuer's words untouched. See below. |
GET /v1/vigilances/countries | The countries this instance serves with its settings, the sources that answer for each, and whether their licence is verified. With ?uncovered=true, every other country of the world too, each with the reason nothing is read for it. |
GET /v1/vigilances.geojson | The same warnings as GeoJSON, the shape of each area as geometry. |
GET /v1/events or ?country=CH | Events instead of lines: the warnings of one phenomenon, level and region grouped, the earthquakes of one sequence, each other hazard, the warnings and hazards themselves inside, untouched; an impact score (min_impact=60: what counts in a world view) and a family (families=). See "Events instead of lines" below. |
GET /v1/families | The sixteen families of phenomena (wind, rain and floods, ..., space weather) and what each holds. |
GET /v1/vigilances/archive, GET /v1/vigilances/archive/stats | Every official warning this instance saw, kept as it was served, filtered by country, area, source, phenomenon, level and period, with the hours each source was read; and what the archive weighs per day. See "The archive of the official warnings" below. |
GET /v1/travel | The travel advice of five foreign ministries (United Kingdom, United States, Canada, Australia, the Netherlands): each one's level for each country in its own words, and whether they disagree (Vigie's comparison, not an official level). See "Travel advice" below. |
GET /v1/travel/{country} | One country: every ministry's advice with its text untouched, the date of its version, when Vigie read it, and the versions this instance has archived. |
GET /v1/travel/health | The ECDC's recent publications on communicable disease threats, in its words; the countries each one names are matched by Vigie. The WHO and the CDC are linked, not read. |
GET/POST /v1/zones, DELETE /v1/zones/{id} | Watched zones (the watchlist). A zone needs its utc_offset_s. |
GET/POST /v1/watched-points, DELETE /v1/watched-points/{id} | The points you watch (a place, a fault, a volcano...), each with a radius. Personal: listed here and in the status, never in the hazards. POST and DELETE are off (403) unless VIGIE_ENABLE_WATCHED_POINTS_WRITE=1. |
GET /v1/watched-points/status?days=7 | For each watched point, the events within its radius over the last days, and quiet only when every hazard source answered and was read without a gap. |
GET /v1/history | Hazard history stored in SQLite. |
GET /v1/health, GET /v1/providers | Health, circuit breaker and cache state of each provider, and its calls, weight and quota over 24 hours. |
GET/POST /v1/scheduler/... | State of the optional background scheduler, run one cycle now. |
POST /v1/simulate | Inject a simulated scenario for testing (always labelled as a simulation). Off by default: it answers 403 unless VIGIE_ENABLE_SIMULATION=1. |
WS /v1/stream | WebSocket with the initial state and alert pushes; a client can also follow a point and be told when its official warnings change. |
GET /dashboard | Web supervision page. |
GET /docs | Interactive OpenAPI documentation. |
VIGIE_FIRMS_MAP_KEY, a source whose file could not be read is read from the area API
instead. The key is in the URL of those requests: it is redacted from every error, from last_error and from the logs. Keep it out of
the repository and of the commands you share.0VIGIE_ENABLE_METSERVICE | 1 (also in the Docker image) | 0 stops asking MetService (New Zealand): a position there then gets no source, and the answer says it is turned off. Its feed says CC BY 4.0 but the website terms it points to allow personal use only and forbid resupply, and which one governs is not verified: set 0 on an instance that serves other people unless MetService agrees. |
VIGIE_WMO_SOURCES | verified | Which national services of the WMO registry are read for the countries that have no source of their own: verified (only those whose own terms were read and allow relaying them), all (every service that is not excluded, for an instance only its owner uses) or off. An unknown value means verified. See "More countries, through the WMO registry". |
VIGIE_CONTACT | none | E-mail address or URL added to the User-Agent sent to the providers. MET Norway and NOAA require an identifying contact: set it. |
VIGIE_CAP_SENDER | vigie-api | sender of the CAP alerts. |
VIGIE_GRID_FORECAST_DEG | 0.05 | Grid (degrees, about 5 km) the position is snapped to before the Open-Meteo forecast and MET Norway are asked. |
VIGIE_GRID_AIR_QUALITY_DEG | 0.1 | Same, for Open-Meteo Air Quality (about 11 km). |
VIGIE_GRID_FLOOD_DEG | 0.1 | Same, for Open-Meteo Flood (GloFAS). |
VIGIE_GRID_MARINE_DEG | 0.1 | Same, for Open-Meteo Marine. |
VIGIE_EMMA_GEOMETRIES | none | Path of a gzipped GeoJSON of MeteoAlarm EMMA_ID shapes that you built yourself (python tools/build_geometries.py --emma; by default app/data/local/emma_id.geojson.gz is read when it exists). It lets warnings that name only EMMA_ID codes be matched to a point. Not bundled: see SOURCES.md. |
VIGIE_NEAR_FAULT_KM | 10 | An earthquake or volcanic event within this distance (km) of the mapped trace of a named active fault is near it (0.1 to 100). A setting, not a published fact. |
VIGIE_NEAR_FAULT_MAX_DEPTH_KM | 70 | An earthquake deeper than this (km) is not compared with the faults (1 to 1000). |
VIGIE_NEAR_VOLCANO_KM | 20 | An earthquake within this distance (km) of a volcano that a hazard source reports is near it (0.1 to 500). |
VIGIE_NEAR_MAX_PLACES | 5 | At most this many places under a hazard, the nearest first (1 to 20). |
VIGIE_WATCHED_POINTS_MAX | 50 | The most watched points the instance keeps (1 to 500). A new one over the limit is answered 409. |
VIGIE_QUIET_MAX_GAP_S | 900 | A hazard source not read for longer than this (seconds, 60 to 86,400) breaks the run of readings that a watched point's quiet rests on. |
VIGIE_BUDGET_FRACTION | 0.9 | Part of each published provider quota (Open-Meteo 10,000 calls a day, MET Norway, RainViewer) the API lets through. Beyond it the cache is served, flagged stale. |
VIGIE_ENABLE_FIRMS | 1 | 0 turns the active fires off: GET /v1/fires and GET /v1/fires/grid answer 404, fires_nearby of /v1/point is null and GET /v1/health lists the source in disabled_sources. On by default: the instance then downloads about 56 MB a day when someone asks the fire routes (docs/data-sources.md). |
VIGIE_FIRMS_MAP_KEY | none | Optional. The free MAP_KEY of NASA FIRMS. The static files need no key, so without it nothing is missing; with it, a source whose file could not be read is read from the area API instead (today and yesterday, 72 of the 5,000 transactions the key is allowed in 10 minutes). The key is in the URL of every such request: the API never prints it (errors, last_error, logs), but keep it out of the repository, of your shell history and of the commands you share. A value that is not letters and digits is not used, and the log says so. |
VIGIE_FIRMS_SOURCES | VIIRS_NOAA20_NRT,VIIRS_NOAA21_NRT | The FIRMS products read: one of them, or both (one halves the volume). Anything else is ignored (Suomi NPP stops on 2026-11-02, MODIS has another layout). |
VIGIE_FIRMS_WINDOW_HOURS | 48 | The file read: 24 or 48 hours. It is also how far back hours can go on this instance (more is a 422). The files of 24 hours cost about two thirds of the bytes (36 MB a day against 56). Anything else is ignored. |
VIGIE_PREALERT_TR | 0 | 1 runs the unofficial pre-alert pilot for Turkey (ADR 0011 and its amendment): each new ECMWF open data run (2 a day, 144 requests to ECMWF's mirror on AWS, about 110 MB) is turned into a level per province, stored in the database, and answered by the experimental route GET /v1/experimental/prealert?lat=&lon= (a test for testers, not an official warning; 404 when off). Off, nothing is downloaded. Needs the optional eccodes (in the Docker image; pip install .[prealert] otherwise). |
VIGIE_FIRMS_REFRESH_MINUTES | 60 | How long a reading of the fires is kept before the next check, 10 to 360. FIRMS writes its files once an hour, so 30 minutes cost more requests (134 a day against 86) and no more bytes (docs/data-sources.md). Once the start-up reading is done, nobody asking costs nothing. |
| n/a |
| 0 |