Skip to content

Releases: TA3HRJ/aprs-agent

v3.2.198 — person-to-person bridges, STATUS from the radio, and Pillow 13

Choose a tag to compare

@TA3HRJ TA3HRJ released this 27 Oct 06:00

Eighteen releases since v3.2.180.

Security

  • Telegram bridge: only the configured chat is relayed to APRS. Until
    v3.2.191 anyone who found the bot and wrote to it was put on air under the
    operator's callsign. Update if you run the Telegram bridge with polling on.
  • Pillow 13.0.0 (closes the advisories open on 12.3.0), certifi
    2026.10.19
    (GHSA-6q48-6c7w-9gw2), setuptools 84.2.1.

Bridges between licensed amateurs

  • Telegram and e-mail, person to person, both off by default
    (allow_links, "Person to person" in the panel). A Telegram chat or an
    e-mail address is linked to a callsign only when its owner proves that
    callsign from the radio with a six-digit code (/link CALL in the bot or
    LINK address to the e-mail alias, then LINK <code> from the radio).
    Linked users then send and receive under their own callsign, 20 messages
    an hour. /unlink or UNLINK from the radio removes the link at once.
    Links are kept in bridge_links.json beside the config: callsign, chat id
    or address, time.
  • E-mail for links goes through its own server setting
    (links_smtp_server, links_from_email, default localhost:25), never
    through the operator's mail account.
  • Who appears as the sender is chosen per bridge: the originator's own
    callsign (default), or the gateway's callsign with "CALL: " opening the
    text. Either way the originator is named.
  • The status row shows all six bridges, the ones that are off in grey;
    bridge traffic is always kept in the Messages history.

AI gateway (DMWGPT)

  • SERVICES / SERVIS answers from the configuration which bridges this
    gateway carries, the alias to send to, and whether they are open to the
    asker; with Telegram links open it names the bot. HELP points to it.
  • STATUS [grid] / DURUM [grid]: the silence state of your locator
    square (or the one you name) and any quake ring reaching it, from the
    server's own data.
  • Message text never carries { (APRS 1.2: it starts the message number);
    { and } become ( and ).
  • AI providers: Google Gemini is the default (free tier,
    gemini-3.5-flash-lite); current models for Groq, OpenRouter, OpenAI and
    Anthropic; reasoning models get their reasoning settings. DeepSeek is
    unchanged.
  • Delivery counts: each answer is counted as fully acknowledged or not, per
    day, and /api/counters reports the last 30 days.

The map and its feeds

  • Atom feed /feed/silence.atom: announced silence alerts and quake
    rings, newest first, filterable by locator (?grid=KM,KN41), in Turkish
    with ?lang=tr.
  • Embeddable status card /embed/status?grid=KM38 for an iframe: silence
    state, a quake ring reaching the square, nearby propagation openings.
    No scripts; &lang=tr, &theme=dark.
  • /?q=<square> opens the map on that square.

Fixes

  • The repeater database matches a listing to the right station: exactly
    with an SSID, and without one only to a station whose own symbol is a
    repeater; a beaconed position is never overwritten.
  • The station analysis no longer stores "null" or a bare callsign as an
    organisation (stored rows are cleaned once).

Everything else is as in v3.2.180.

v3.2.180 - compressed log rotation keeps every file

Choose a tag to compare

@TA3HRJ TA3HRJ released this 20 Oct 12:05

Fixes one fault found while preparing v3.2.179.

  • Compressed packet log (log_compress = true). A rotation that arrived
    before the previous file had finished compressing reused its temporary name
    and target, and one of the two copies was lost. Each rotated file now gets
    a name of its own and one worker compresses them in order. Only affects
    installations with log_compress on and a small log_max_mb; it is off by
    default.

Everything else is as in v3.2.179.

v3.2.179 - a registered tocall, a quake ring, and a gateway that does its own arithmetic

Choose a tag to compare

@TA3HRJ TA3HRJ released this 20 Oct 10:09

Fourteen releases since v3.2.165.

On the air

  • Registered tocall. Every packet the agent sends — replies, acks,
    bridged messages, status and the fixed beacon — now uses APRAGT,
    registered to APRS-Agent in the
    APRS device identification list.
    It replaces the generic APRS and the unregistered AP4GNT.
  • Bridge acks (Telegram, WhatsApp, Bluesky, Twitter, e-mail) are sent as
    an ordinary message from the alias to the sender.

AI gateway

  • A provider call is limited to 30 seconds and one retry; a response without
    an answer fails cleanly, and the sender's retry of the same message is
    asked again.
  • Computed, not asked: what an SWR figure means (reflected power,
    mismatch loss, a one-line verdict), and the nearest digipeaters heard in
    the last 7 days. Questions on how to lower SWR still go to the model.

The map

  • Quake ring. Around every quake USGS PAGER rates yellow or above, a
    dashed ring counts for 3 hours the fixed stations that went quiet, against
    the rest of the world at the same moment; orange when that is more than
    chance explains. Drawn for 24 hours, and kept across a restart.
  • Badges count every station of the view heard in the last 24 hours,
    counted on the server; close up, every station ever heard is drawn, fainter
    the longer it has been silent.
  • Positions that are not places are no longer drawn: 0/0, the 90/180
    pair, north of the map, the Pi-Star default (50 N 3 W) and latitudes read
    out of malformed digit runs. Such a station reads "position unreadable" in
    the search.
  • A scale bar; stations stacked on one point open as a list of names.
  • The page opens in the light paper theme of the aprsagent.com sites; the
    dark look is one click away and remembered.

Silence alerts

  • The hourly digest names the strongest nearby quake, and the AI note counts
    how many silent stations had stopped before it.
  • One mains failure with stragglers on battery is no longer called
    "unknown" when the onset facts agree.

Public page

  • Leaflet is served by the agent itself, not by a third-party script host.
  • robots.txt, sitemap.xml, llms.txt and an HTML 404 page.
  • New settings: public_base_url (canonical address) and
    public_privacy_url (a privacy-notice link in the About box).

Security

Documentation

  • README and HELP list what the gateway answers without the model.

v3.2.165 - a map that fills the screen, alerts that know when they cannot hear

Choose a tag to compare

@TA3HRJ TA3HRJ released this 08 Oct 20:44

Thirty-five releases since v3.2.130. The download catches up with the live
demo again.

The map

  • Search by callsign the way aprs.fi does (TA3HX* for every SSID, ?
    for one character) across the whole registry, or by Maidenhead locator
    (KM, KM38, KM38nk), which frames the square and opens its silence
    cell. Every row of the alert list leads to its cell; Messages callsigns
    lead to the station.
  • The world view fills the map. Zoomed all the way out it used to leave
    dark bands around a small world; the least zoom now follows the size of the
    map.
  • On a phone the map starts at about a quarter of the screen instead of
    half way down: the caption and the amber status bar are one line until
    tapped, and the stats bar is one row.
  • The map follows its own size, so folding the settings column no longer
    leaves a strip without tiles.

Silence alerts

  • An alert must hold for 10 minutes before it is announced, and an
    episode survives a dip shorter than an hour: about a third fewer alerts,
    most of them flickers.
  • Alerts state when their stations stopped - the spread, the opening,
    stations that came back - and that stations on UPS, battery or solar stop
    later than the rest after one power cut. The AI note weighs a power cut and
    independent drop-outs equally, and says "unknown" unless the facts agree.
  • Stations injected over the internet under another station's login are no
    longer counted as radio witnesses.
  • Time the agent did not listen is never anyone's silence. If the
    program starts without a network, or the feed breaks, nothing is judged
    while it cannot hear; when the feed returns, stations not heard since are
    judged only after one beacon interval of listening, and the amber bar says
    how many are waiting.
  • The still-off-the-air list shows the last 7 days by default, links to the
    map, and can be grouped by square.
  • HELP now explains how an alert and an opening are decided, in Turkish and
    English.

Propagation

  • The distance floor is 250 km (was 300). On the same hours of feed it
    finds about 70 % more openings.
  • A link from a station whose position jumped faster than anything moves, or
    from a balloon, is suspect: drawn grey and dotted, never counted toward
    an opening or a gate's baseline, and hideable from the legend.

The AI gateway

  • Asked for the date or the time, it answers from the clock, not the model.
  • A part of a long answer that draws no acknowledgement is sent again, and a
    radio is given a moment to switch from transmit to receive.
  • A station heard through a digipeater is told it was heard on RF.
  • The model is told its answer may go out on amateur radio and must be fit
    for it.
  • A place name it cannot read is said to be unread; a distant weather
    reading says it is not local.

Running unattended

  • A background loop that fails is restarted, and the notify channel hears
    when the agent cannot hear the feed for 15 minutes.
  • New setting log_compress gzips rotated packet logs (about 2.6:1).
  • E-mail says when it cannot send.
  • tweepy (Twitter) is optional.
  • Dependency updates for published advisories (urllib3, multidict).

v3.2.130 - all fourteen days, and a map that does not wait

Choose a tag to compare

@TA3HRJ TA3HRJ released this 24 Sep 09:59

Five releases since v3.2.125. Most of them came from screenshots sent in by
the people using the program, and the rest from an audit of the running
server.

The Messages tab shows what it promises

The tab says it keeps fourteen days, and the database did hold them: 257
gateway messages. The tab showed 29. It asked for the newest 2,000 rows of
all traffic, and on a busy feed ordinary APRS messages had pushed the
gateway's own conversation out of that window. It now asks for the gateway's
conversation separately and merges the two, and all fourteen days are there.

Smaller things the screenshots showed

  • A position answer for a station that arrived over the internet named the
    core server that relayed it, as if it were an igate. It now says
    "internet-connected".
  • "1 operators today" is "1 operator today".
  • The public map offered a link to a Messages tab that visitors do not have.
    It is gone.
  • The usage line is there from the first second after a restart instead of
    after the first minute.

A settings column that folds away

The gear button already opened and closed the settings on a phone. It now
works at every width: on a wide screen it folds the column away and gives the
space to the map, and the choice is remembered.

The map no longer waits for the database

Once a minute the program writes its whole station registry back to disk,
which takes 10 to 18 seconds on a quarter of a million stations. Until now
every read queued behind that write, so a request that needs 4 ms could take
8 or 9 when it landed at the wrong moment. The database now runs in SQLite's
WAL mode, where reads never wait for a writer. After the change, 90 requests
from outside all answered within 1.3 s.

The database file is now accompanied by -wal and -shm files while the
program runs. If you back it up by copying files, stop the program first or
copy all three.

Quieter where nothing is wrong

An idle Telegram bot no longer writes an error every few minutes. The long
poll now has a real margin, a single failed poll is logged as what it is, and
only an outage of three polls or more counts as an error — once, however long
it lasts.

Under the hood

  • httpx2, which the AI providers' client library pulls in, moves from 2.10.0
    to 2.13.1, past one high and two moderate advisories.
  • The server logs its memory use once a day.
  • The guard rail is thirty-three checks, thirty-two of which run offline.
    Two are new (check_telegram_poll, check_db_wal), and each was watched
    failing against the code it replaced before being trusted.

v3.2.125 - the day it met strangers

Choose a tag to compare

@TA3HRJ TA3HRJ released this 21 Sep 05:27

Six releases since v3.2.119, all of them in the AI gateway, and every one of
them written from traffic rather than from testing. The service was announced
in two APRS groups on 20 September and the people who tried it found more in
an afternoon than months of use by its author had.

It answers from the registry instead of sending you elsewhere

The station database holds a quarter of a million stations, and the gateway
used to answer questions about them with "I have no live data". Now, without
a model call:

  • Which igate heard you — from the path of your own packet, and if you
    came in over the internet it says so rather than naming the core server.
  • The nearest igates or repeaters, with distances, measured from a grid
    you name or from your own last beacon.
  • Openings near you, from the propagation the program itself measured.
  • Another station's last position, with its age and its distance from
    you — one observation, the same fact the map draws. Never a name, a licence
    record or an address. A station that sends NOLOOKUP is left out of
    other people's answers; LOOKUP puts it back. HELP.html says so in
    both languages.

Refusals now name their own reason: no data, no place names, or a station
that asked to be left out. A UK postcode is recognised as a postcode instead
of being read as a Maidenhead square on the other side of the world.

It holds a conversation

The last three exchanges with a station travel with its next question, for
ten minutes, in memory only. Follow-ups that name nothing — "how long would
it take by car?" — now land.

It knows what it is

"What can you do" is answered from the code rather than invented by the
model, before the rate limiter and at most once per sender per ten minutes.
A message meant for another station is answered as what it is, rather than
the model greeting you by somebody else's name. And it no longer talks to
other machines: a sender whose callsign is not callsign-shaped is ignored
before the acknowledgement, after two services spent forty seconds answering
each other.

It costs the channel less

A re-sent question is answered from cache rather than bought twice, a
duplicate that arrives in the same second draws no second copy, and a
question asked a third time is answered rather than met with silence.

Usage

The About panel now shows how many operators have used the gateway today and
in total, and how many questions it has answered, linked to the Messages tab.
Aggregates only.

Under the hood

The guard rail is thirty-one checks, thirty of which run offline.
Four are new in this release line (check_gateway_intent, check_context,
check_lookup, check_gateway_stats), and each was watched failing against the
code it replaced before being trusted.

v3.2.119 - the gateway learns to hold a conversation

Choose a tag to compare

@TA3HRJ TA3HRJ released this 20 Sep 13:09

Three releases since v3.2.116, all of them in the AI gateway, and all of them
written from one afternoon's worth of strangers using it for the first time.

It remembers the sentence before

A station asked for the best way from Bremen to Berlin, got an answer, then
asked "How long would it take by car?" — and was told to name the two
places. Every question went to the model on its own.

The last three exchanges with a station now travel with its next question,
for ten minutes, in memory only and never written to disk. Follow-ups that
name nothing — which is how people write them — now land.

It answers "what can you do"

That is the first thing a newcomer sends, and it was the one question with no
answer: it went to the model, which described the service in its own words,
and if the sender had spent their burst on earlier questions the rate limiter
refused it outright. It is now answered from the code, before the limiter,
costing neither a model call nor a token — and at most once per sender per ten
minutes, so a free answer cannot be used to make a distant igate transmit on
demand.

It stops mishearing the question

"Tell me a joke about the weather" came back as a temperature reading from a
station 28 km away: the weather shortcut matched the word anywhere in the
text. It now stands down for a joke, poem, song, story or riddle in either
language, and a real weather question still reaches the registry without
touching the model.

Asking twice no longer costs twice, and asking a third time is not met with silence

A client that gives up waiting numbers its re-send afresh, so the same
question arrived as two, was paid for twice, and went out twice. A question is
now indexed by its words as well as its number for two minutes.

At the other end, an answer could be replayed twice and after that the sender
got nothing — indistinguishable from a dead gateway. The cached answer now
goes out again instead, still without a second model call, and each copy costs
a rate token, so how often someone may ask is the bucket's decision rather
than a flat cap.

Under the hood

The guard rail is twenty-nine checks, twenty-eight of which run offline.
Two are new — check_gateway_intent.py and check_context.py — and each was
watched failing against the code it replaced before being trusted.

v3.2.116 - TA3HRJ is now TA3HX

Choose a tag to compare

@TA3HRJ TA3HRJ released this 19 Sep 12:25

The maintainer's callsign changed from TA3HRJ to TA3HX on 16 September
2026. This release carries the new callsign everywhere the program names its
author: the licence, the README, the help page footer and the desktop About
dialog. The licence and README keep "formerly TA3HRJ" so the earlier copyright
stays traceable. Sample callsigns in hints and examples are now TA1ABC.

The GitHub address stays github.com/TA3HRJ/aprs-agent; links and the update
source are unchanged.

No change in behaviour. If you run v3.2.115 there is nothing here you need.

v3.2.115 - nothing written, nothing answered, silently lost

Choose a tag to compare

@TA3HRJ TA3HRJ released this 19 Sep 08:51

Four versions since v3.2.111. Two of them fix things that were lost without a
word: propagation openings that never reached the history, and gateway answers
that arrived but were never shown.

Propagation openings were dropped on a busy database

Over a week on the live server, 13 of 247 detected openings never reached the
propagation history, so they were missing from the map timeline and from the
evidence lookup. Each one was logged as history write failed: database is locked.

Once a minute the program saves every station it knows, in one transaction.
With a quarter of a million stations that save held the database for ten to
eighteen seconds, and every other write gave up after five. Every database
connection now waits up to sixty seconds.
Reads that used to happen on the
web server's own event loop now run in the background, so a longer wait can
never freeze the interface.

The AI gateway's first answer after a restart could vanish

APRS messages carry a number, and a receiving app uses it to recognise a message
it has already shown: a repeat is acknowledged and then not displayed. The
gateway numbered its replies from a counter that started again at every
restart, so the first answer after any restart was always numbered 2 and 4,
whoever it went to. A station that had heard from the gateway before could
acknowledge a new answer and never see it. This was found when an iPhone showed
one line of a two-line reply.

The last number used is now kept next to the config file and continued after a
restart. Where there is nowhere to keep it, the numbering starts from the clock,
so two runs never start from the same place. Numbers also step by one now, not
two.

About panel

The maintainer's callsign changed from TA3HRJ to TA3HX. The About panel
shows the new callsign and contact address.

Under the hood

The guard rail is twenty-seven checks, twenty-six of which run offline. Two
are new in this release, one for each fix above, and each was watched failing
against the code it replaced before being trusted.

v3.2.111 - security fixes, and your own hotspot was hiding from you

Choose a tag to compare

@TA3HRJ TA3HRJ released this 15 Sep 21:19

Four versions since v3.2.107. The newest is a security update; the two before
it fix something no error message would ever have told you.

Security updates

aiohttp 3.14.3. The web server library had a request-smuggling bug in how
it handled WebSocket upgrades (CVE-2026-69243), plus two client-side issues
(CVE-2026-59881, CVE-2026-69244). aiohttp is what serves the Web GUI and its
live updates, so this is the one to take.

cryptography 48.0.1 in this Windows build, 50.0.1 on the server. Version
46 and earlier carry a vulnerable copy of OpenSSL (GHSA-537c-gmf6-5ccf) and
three further advisories (CVE-2026-69247, -69248, -69249). It could not be
updated sooner because the Bluesky library, atproto, capped it; atproto 0.0.72
lifts that cap, so both move together.

Stated plainly: this Windows build closes the OpenSSL advisory but not the
other three.
Those are fixed only from cryptography 49, and cryptography
publishes no 32-bit Windows packages from 49 onward. The library is used by the
Bluesky extension; if you do not use Bluesky, it is never loaded.

Other dependencies moved by patch releases only. Larger upgrades are held back
on purpose.

Your own hotspot could be invisible to your own agent

If the agent logs in to APRS-IS with your bare callsign — TA1ABC rather than
TA1ABC-5 — and another device of yours, a hotspot or an igate, also sends to
APRS-IS under that same bare callsign, the server never delivers that
device's packets to the agent.
APRS-IS treats a packet as belonging to the
connection whose login matches it and withholds it.

Nothing errors and nothing is logged. The device is simply missing from the
map, from silence detection and from the station registry, while it is plainly
visible on aprs.fi. On the station where this was found, a hotspot beaconing
every ten minutes produced zero packets in over five hours; with an SSID added
to the agent's login, its next beacon arrived 51 seconds later.

  • The login callsign is now printed at startup, in the Live Log and in the
    service journal.
  • A login without an SSID produces a warning naming it.
  • Adding an SSID costs nothing: the APRS-IS passcode is calculated from the
    base callsign and does not change.
  • aprsconfig.toml.template explains the same, next to the callsign setting.

If you log in with a bare callsign and own other APRS-IS gear, add an SSID.

"Where am I?" is answered from the registry

Asking the AI gateway "my location?" or "where am I?" used to reach the
language model, which has no access to the station registry and said it did
not know — even when the asking station had beaconed minutes earlier.

The gateway now answers first-person position questions itself, from the
registry, using the sender's full callsign including its SSID. Turkish phrasing
is recognised too. Questions that only look similar, such as "where is the
nearest digi", still go to the model.

One known limit, recorded rather than hidden: a compound question such as
"time, date and my location?" is answered in part. The gateway answers the
first thing it can answer deterministically and stops there.

Under the hood

The guard rail is twenty-five checks, twenty-four of which run offline.
Each was written from a live failure and watched failing against the broken
code before being trusted.