Inspiration

Around the world, automated listening stations constantly report which shortwave signals they just heard — "station A was heard by station B, on this band, this strong." These reports stream in publicly, around the clock, by the hundreds per minute. Hams have always known the feeds exist. What doesn't exist is the picture: surfers get a swell map, but a radio operator asking "can anyone hear me right now?" gets tables.

Skywave is that picture. A dark Earth with the day/night line, every fresh reception report blooming as an arc of light between transmitter and listener. Type your callsign and the planet redraws to your signal's actual reach. The planet visibly breathes with radio.

What it does

  • The living planet — the public WSPR, PSKReporter and DX-cluster firehoses pour into Elasticsearch, and the map is a rolling live query against that index. Never a canned animation; every number on screen is measured and labeled so.
  • Callsign focus + 24h scrub — the firehose filtered to one signal: every station that heard you, how far, on which band, with a scrubbable trail of the last day.
  • The operator — a command line at the bottom of the screen. Ask in plain words ("when's my best chance of reaching japan on 20 meters?") and a Gemini agent answers — but its only senses are Elastic tools exposed over Elastic Agent Builder's MCP server. The answer lands as paint on the map (an hour-by-hour glow along the great-circle path) plus two sentences that cite exactly what was queried, always ending "observation, not prophecy."
  • The openings ticker — every 10 minutes the agent compares each band × region pair against its trailing baseline. Openings it detects are written back into the index in plain words; every visitor's ticker updates at the same moment, and future answers search this growing memory.

How we built it

One Node process serves the app, ingests the three feeds, fans fresh reports out to every open browser over SSE, runs the openings sweep, and hosts the ask endpoint. It deploys as a single container on Cloud Run.

The Elastic side is the agent's entire brain:

  • skywave-spots — append-only time-series data stream of every reception report (7-day rolling window).
  • skywave-chatter — DX-cluster free-text comments with semantic_text embeddings: hybrid semantic + keyword search over messy radio slang ("loud into EU, big QSB").
  • skywave-insights — the agent's own findings, written back in prose.
  • Five custom ES|QL + search tools created in Elastic Agent Builder at boot, callable over its built-in MCP server: paths-by-hour, callsign reach, band activity, chatter search, insights recall.

The Google side: Gemini on Vertex AI in a function-calling loop whose only tools are those five MCP tools. The agent cannot see the world except through the index.

Challenges

  • Feed manners. PSKReporter asks for considerate use, so ingestion is one filtered MQTT connection with a politeness cap — except focused callsigns, which are never dropped. DX-cluster nodes only accept licensed callsigns as login, so without one that feed honestly shows cold instead of being faked.
  • Honesty as a feature. An inactive callsign gets a dark planet and a plain sentence saying so. The agent is forbidden from prophecy: answers are observations over accumulated reports, and the citation line proves it. Making emptiness feel trustworthy instead of broken was a design problem, not a code problem.
  • An agent you can audit. Streaming each MCP tool call to the browser as it happens — so you literally watch the agent look things up before it answers — turned out to be the difference between "AI feature" and "operator you trust."

What we learned

The reception-report firehose is logs — append-only, time-stamped, high-rate — which is exactly the shape Elasticsearch was built for. ES|QL aggregations answer "when does this path open?" in one query. And hybrid search over semantic_text reads radio slang that no regex ever could. Strip Elastic out and the map dims to dumb dots with no one to ask.

What's real

Everything. Open the URL (no login), watch live reports land, type any currently active callsign, ask the operator. A REPLAY toggle exists for judging resilience — it replays the last UTC day through the same pipeline with the word REPLAY printed on the planet the whole time. Never silently substituted.

Built With

Share this project:

Updates