| Fibonacci wave, Matheus Hoffmann (@h0ffmann), 2026 | Ammonite, Harry Chen (@harry7557558), 1st place, 2021 Desmos Global Math Art Contest |
Desmos source: wave.desmos.json |
Desmos source: desmos.com/calculator/z7zooq9zsh |
22nd century alerts · in one minute · sources · channels · who uses it · architecture · cite · português
- alerts is a generic alert platform. It collects alerts from official sources, filters the alerts, and delivers the alerts. A hazard, a region or a channel is a module: the core does not change when a module is added.
- Only official sources. An alert comes from an authoritative agency. alerts never invents an alert, an area, a time or an action that the source does not state.
- The first hazard is floods in Brazil. v0 serves the South region: Paraná (PR), Santa Catarina (SC) and Rio Grande do Sul (RS).
- v0 delivers a Discord service and an Atom feed with CAP 1.2 alerts, with the same alerts
and the same text. The Python package
piabrota-alertsexists from the start, for an AstrBot plugin. - Open by design. Open standards (Atom, CAP 1.2), FOSS dependencies only, local models only. A paid API is never required.
- Status: bootstrap. One agency source is implemented: the CEMADEN municipal alerts
(
alerts poll cemaden). The service sends a test message, andalerts poll mocksends simulated test alerts through the harness to Discord and the feed. The Discord setup, step by step, is indocs/DISCORD.md.
Brazilian data is scattered. Each source is its own module: a new source does not change the core.
Checked on 2026-10-22. Each endpoint was requested twice: from inside Brazil (curl) and from
outside Brazil (an HTTP client on a server abroad). No source blocks access by country. No source
publishes a rule about foreign access. The endpoint, the format and the authentication of each
source are in docs/SOURCES.md.
| Source | Official API | Format | Authentication | Outside Brazil | Version |
|---|---|---|---|---|---|
| Defesa Civil (IDAP) | yes, not documented ⚠ | Atom with CAP 1.2 | none | reachable | v0 |
| INMET | yes | RSS and CAP 1.2; undocumented JSON | none (warnings); token by email (stations) | reachable | v0 |
| CEMADEN | yes (observations) | REST JSON; undocumented alerts JSON | none (alerts); free registration, JWT (observations) | reachable | v0, alerts done |
| ANA | yes | REST JSON; legacy SOAP | registration with CPF/CNPJ (REST); none (SOAP) | reachable; registration can require a CPF/CNPJ ⚠ | planned |
| CPTEC/INPE | yes, forecasts only | XML | none | reachable; non-commercial use | planned |
| Epagri/Ciram (SC) | partial | WordPress RSS and JSON, free text | none (warnings); CPF (station data) | reachable | planned |
| SGB (SACE) | no | HTML; PDF bulletins | none | reachable | planned |
| Defesa Civil RS | no | PDF; undocumented ClimaRS JSON | none | reachable | planned |
| SIMEPAR (PR) | none found | — | — | blocked from every country (Cloudflare) | planned |
Find a source, in Brazil or in the world. alerts sources searches a curated catalog of
official alert sources: Brazil first, then national weather services abroad (NWS, MeteoAlarm,
Météo-France, AEMET, DWD, JMA, ECCC, SMN Argentina) and GDACS for the world. Each entry says the
endpoint, the format, and whether it needs a token. The MCP tool search_sources gives the same
search to an agent.
alerts sources --country BR --alerts # the Brazilian alert sources
alerts sources flood --no-token # flood sources that need no token
alerts tokens # which of your tokens are set (never their values)Bring your own token (BYOT). alerts never ships a token. A source that needs one (CEMADEN
PED, INMET stations, ANA, Météo-France, AEMET) reads your own token from ALERTS_TOKEN_<ID>, in
the environment or in .env (chmod 600 .env). A token never reaches a message, a log or an
error. To subscribe a server to sources, list them in ALERTS_SOURCES, for example
ALERTS_SOURCES=cemaden, and run alerts poll. ALERTS_UFS selects the states (all, or for
example PR,SC,RS, the default). The catalog is in
docs/SOURCES.md.
What each source requires from the code:
- Defesa Civil (IDAP): the feed
idapfile.mdr.gov.br/idap/api/rss/capkeeps about 12 hours of alerts ⚠ and is about 10 MB. The module must poll the feed often and store the alerts. The alerts of PR, SC and RS appear in this feed. The SC Civil Defense also publishes a free-text RSS. - INMET:
apiprevmet3.inmet.gov.brdrops the connection when the User-Agent is curl's default (curl/...). alerts identifies itself by its own name (piabrota-alerts/<version>). INMET accepted this User-Agent with the former project name on 2026-10-22. Nobody checked the new name ⚠. The RSS declares public domain, with citation of the source. - CEMADEN: the PED API (
sws.cemaden.gov.br/PED) gives observations, not alerts. The municipal alerts exist only in an undocumented JSON (painelalertas.cemaden.gov.br/wsAlertas2).alerts-source-cemadenreads it: floods and mass movements, at levels moderate, high and very high. A field it does not know fails the poll, so a change of the panel never drops an alert in silence. The limit is 12 requests per minute for an external user ⚠. CEMADEN requires citation. - ANA: the REST token is valid for 60 minutes. Frequent token requests can block the IP.
- CPTEC/INPE: the terms forbid commercial use without authorization and require the citation "CPTEC/INPE". The service has no warnings.
- Defesa Civil RS: it replaced the SEMA-RS Sala de Situação in February 2025. The ClimaRS JSON (ANA river levels) can change without notice.
- SIMEPAR: no automatic client passes the Cloudflare challenge. The source gets no module until SIMEPAR publishes an API.
Some sources publish free text (bulletins, PDFs). For these sources, alerts will use open models that run on the local machine. A paid API is never required.
| Runtime | License | Use in alerts | Status |
|---|---|---|---|
| Ollama | MIT | Read free text and extract fields (place, time, river level) into an Alert. |
planned |
| OLLAYA | Apache-2.0 | Decide: typed questions (is this a flood alert? which severity?) with a calibrated probability. It does not generate text. | soon |
A model never invents an alert. When the model is not sure, the alert goes back to the deterministic harness and is marked for human review.
| Channel | Standard | Delivery |
|---|---|---|
| Discord | Discord webhook | push |
| Feed | Atom (RFC 4287) with CAP 1.2 (OASIS) in each entry | pull |
Both channels read the same harness output: an alert that reaches Discord also reaches the feed,
with the same text. The feed depends on no company: Atom and CAP are open standards. The feed has
the same shape as the national Civil Defense feed (IDAP), and any feed reader opens the file. A
test alert has <status>Test</status>.
| Consumer | What it is | How it reads alerts | Status |
|---|---|---|---|
| Discord | The v0 server, which already exists | webhook (push) | v0 |
| Any feed reader | Atom (RFC 4287) with CAP 1.2 in each entry | the feed file (pull) | v0 |
| Obsidian | The plugin WeatherDisaster: one note per alert, for a second brain | the feed (pull) | new |
| AstrBot | An open-source chatbot platform for messaging apps (AGPL-3.0) | the Python package piabrota-alerts in a plugin |
planned |
| astrbot_plugin_disaster_warning | A disaster warning plugin for AstrBot: earthquakes, tsunamis, weather (AGPL-3.0) | the Python package or the feed, for alerts from Brazil | planned |
A plugin calls the package: alerts copies no AGPL code. More in
docs/REFERENCES.md (pt-BR).
%% figure: crates
%% title: Which crates exist, and which crate depends on which?
flowchart LR
S["alerts-source-*<br/>(one per source)"] --> C["alerts-core<br/>Alert, Uf, Harness"]
D["alerts<br/>service"] --> C
D --> F["alerts-feed<br/>Atom + CAP 1.2"] --> C
P["alerts-py<br/>Python package"] --> C
M["alerts-mcp<br/>MCP server"] --> C
R["alerts-crud<br/>models, controllers"] --> C
R --> X["alerts-rss<br/>RSS rules"] --> F
T["desktop/app<br/>Tauri 2"] --> R
How to read this figure
In one sentence. Every crate depends on alerts-core. The core depends on no crate.
How to read. Each arrow goes from the crate that uses to the crate that is used.
Not shown. The source crates do not exist yet. The alerts-scaffold generator writes code into alerts-crud and depends on no crate. The harness composes pure stages
(Fn(Alert) -> Option<Alert>): see GROUND_TRUTH.md.
Evidence. The Cargo.toml of each crate in crates/ and desktop/.
How to read this figure
Takeaway. An official alert goes through one path, and Discord and the feed get the same text.
How to read. Left to right. An orange box is an official source. Each arrow is labelled with what happens.
Not shown. The planned sources, the local models and the alerts that the harness drops.
Evidence. The Sources, Channels and Architecture sections of this README. No source crate
exists yet. The source: docs/archify/en/fluxo-alerta.json.
nix develop
just check # formatting, clippy, tests, licenses
DISCORD_WEBHOOK_URL=... just test-message # send a test message to Discord
FEED_PATH=alerts.atom just test-message # write the Atom + CAP 1.2 feed with a test alert
MAROLA_DISCORD_WEBHOOK_URL=... just marola-discord # a test alert to the official Marola Discord
just obsidian # build the Obsidian plugin WeatherDisaster
just mcp # MCP server on stdio
just py-build # build the Python package
just reading-time # write the reading time of every file for humans
just figure # draw docs/archify/*.json with Archify
just graph # the code graph: build/graph/graph.html
just site # the static site for piabrota.art/alerts/
just desktop-dev # the desktop app (Tauri 2), with live reload
just generate scaffold rivers slug:slug name:string # a new CRUD resource in alerts-crudThe desktop app configures apps in a window: the sources, the channels and the apps. It follows the CRUD conventions of Loco: each resource has a model, a controller and a migration. The app also lists the alerts that the sources delivered, next to a map. The app never creates an alert.
crates/alerts-crud: the models, the controllers and the migrations, on SQLite. Every operation goes throughcontrollers::dispatch.crates/alerts-rss: the RSS combinator. Each user has areas, projects and tasks, and each of them owns RSS feeds and rules that compose the feeds. Seedocs/RSS.md.desktop/app: Tauri 2. Its two commands (modules,crud) callalerts-crud.desktop/ui: the interface, in Rust (Leptos, compiled to wasm32). It draws each page from the spec of a resource, so a resource fromjust generate scaffoldgets its page with no interface code.- The map uses MapLibre GL JS 5.24.0 and OpenFreeMap tiles: no key and no account.
More in docs/DESKTOP.md. The way to the App Store and Google Play, as
"marola alertas", is mapped in docs/STORES.md.
WeatherDisaster (id piabrota-alerts) feeds official alerts into an Obsidian vault. It reads an
Atom or RSS feed of CAP 1.2 alerts and writes one note per alert, with the CAP fields in the
frontmatter and the issuer's text verbatim. A CAP Update rewrites the note, and a CAP Cancel
marks it as cancelled. Filters: UF, IBGE municipality and lowest severity. A failed read shows as a
failure, never as "no alert". The logic is Rust (crates/alerts-obsidian), built to WebAssembly.
More in docs/OBSIDIAN.md.
The documentation exists in four languages: pt-BR, English, Chinese and Japanese. The pages are
in wiki/. The GitHub Wiki gets the pages only after the repository is public (issue #1). Ideas
to host the site at piabrota.art/alerts are in docs/HOSPEDAGEM.md (pt-BR).
-
The reading-time calculator, the figure workflow and the site toolchain come from ptbr-writer and picasso's labs (ptbr-tools, code-graph, gleam-website). Figures are drawn with Archify, by @tt-a1i (MIT). The code graph uses the schema of graphify.
-
The layout of this README comes from picasso.
-
The drawing is by Matheus Hoffmann (@h0ffmann). It is rendered from its Desmos expressions. The wave joins the Fibonacci spiral and Katsushika Hokusai's The Great Wave off Kanagawa (c. 1831). Other artists drew Fibonacci waves before: credits in
docs/drawings/README.md. -
The app conventions (generators, doctor, scheduler, tasks) follow Loco (Apache-2.0). Screenshots of its bootstrap are in
docs/screenshots/loco-bootstrap/. The CRUD layout ofalerts-crud(models, controllers, migrations, scaffold) also follows Loco. -
The desktop map uses MapLibre GL JS (BSD-3-Clause), OpenFreeMap tiles and OpenStreetMap data (ODbL). The desktop app uses Tauri (Apache-2.0 or MIT) and Leptos (MIT).
-
The functional harness translates the Object-Oriented Harness (arXiv 2610.28380) into function composition.
-
The pt-BR writing follows ptbr-writer. The
open-prverb is itsbiprskill, adapted (MPL-2.0). -
The English writing follows the model of ASD-STE100.
-
The Japanese follows Easy Japanese (Yasashii Nihongo).
Use CITATION.cff, with the commit or the tag you used. REUSE.toml
gives the license and the copyright of each file, and the citation rules for agents.
| What | Terms |
|---|---|
| The code and the documentation | MPL-2.0 |
The drawing in docs/drawings/ |
MPL-2.0; an original work for this repository: a Fibonacci spiral that just wave draws (Rust), also as a Desmos file. The idea follows Hokusai's Great Wave and earlier Fibonacci waves by other artists: see docs/drawings/README.md |
| The source data | the terms of each agency (docs/SOURCES.md): INMET is public domain with citation; CPTEC/INPE forbids commercial use without authorization |
| AstrBot and its plugins | AGPL-3.0, their own repositories; nothing here vendors their code |
REUSE.toml gives the license and the copyright of each file.

