Changelog
What changed in EzFD, newest first. Read from the top down to the date you last updated.
There are no version numbers — EzFD is deployed by pulling the repository — so
entries are dated by when they landed on master.
Each entry says what the change does for you, grouped by whether it added something, fixed something, changed existing behaviour, removed something, or was a security update. Entries that affect what you submit or see also carry a tag:
| Tag | Meaning |
|---|---|
Scoring |
A claimed score moves. If you already submitted, check the number |
Exports |
A Cabrillo, ADIF, summary sheet or backup file changes |
Display |
An operator sees or does something different on screen |
Setup |
Deployment, the admin console, or how the server is run |
Untagged entries are internal — tests, refactors, CI — and safe to skip if you only run events.
2026-10-01
Added
- Docker Compose install
Setup— EzFD can now be installed on any 64-bit machine with Docker, including the distributionsdeploy.shrefuses: copy.env.exampleto.env, fill in three secrets, anddocker compose up -dbuilds the app from GitHub, at the branchEZFD_REFnames, and runs it, PostgreSQL and a Caddy proxy that handles TLS.compose.yamland.envare all it needs on disk, and it can sit behind a proxy the machine already runs. The admin console works on it from the host, including backups, exports, the clock tools and a safe update. (#105) Docs: Deployment → Docker Compose, Deployment → Behind your own reverse proxy, Administration → On a Docker install, Configuration → The Docker Compose .env
2026-09-23
Changed
- CI actions moved to their current major versions —
actions/checkoutandactions/setup-nodego from v4 to v6. v4 runs on GitHub's Node 20 runtime, which is being retired, and would eventually have stopped CI from running at all. (#103)
Fixed
- Restarting the server took a minute and a half
Setup— Any browser with the logger or dashboard open held the old process up, so everydeploy.shrun, every Update application and everysystemctl restart ezfdsat on "Configuring systemd service" until systemd force-killed it at 90 seconds. The server now ends its live-update streams when asked to stop and restarts in a second or two; open tabs reconnect by themselves and send anything they logged in the gap. (#104) Docs: Deployment → Service management, Architecture → Real-time updates - Servers left on Node.js 20 through every update
Setup—deploy.shonly installed Node on a fresh install, so a server set up earlier stayed on Node 20 however often it was redeployed, and Node 20 stopped receiving security fixes in April 2026. Re-runningdeploy.shnow moves any server below Node 24 up to it, and the admin console's Update application warns when the server is behind. CI anddeploy.shnow both read the version from.nvmrc, so a server can no longer run a Node release that CI never tested. (#103) Docs: Deployment → Updating, Offline field servers → Updating later, Administration → Updating the application
Security
- Next.js 16.3.6
Setup— Fixes two critical remote code execution advisories in 16.3.2, one of them in the image optimisation endpoint that every install serves, plus a high-severity flaw in thesharpimage library it bundles. Anyone with a server reachable from the internet should update. (#103) Docs: Deployment → Updating
2026-09-16
Added
- A dashboard view to put on a screen at the site
Display— The new Overview tab is laid out to be read from across a tent rather than scanned: contacts, rate, claimed score and sections in large type, the section map, the join code, and a board showing every band in use with which modes have somebody on them, which are booked and which are free. A band somebody is actually on outranks a booking, so the gaps it shows are real gaps rather than paperwork. It is the one view without the side panel — it prints the join code itself. (#102) Docs: Operating → The Overview, Offline field servers → At the site
Fixed
- The band breakdown silently dropped four bands on a special event
Display— 60m, 30m, 17m and 12m are the bands only a special event can log, and the Bands view kept its own band list that omitted all four. A special event that worked 30m had those contacts logged, scored and counted everywhere else, and then missing from the band table with nothing on screen to say a row was gone. Contest events were never affected. (#102) Docs: Operating → The dashboard - The dashboard ignored band claims on Field Day and Winter Field Day — It only ever fetched them for special events, because until now nothing on a contest dashboard displayed one. The new Overview reads them, so a claimed band would have drawn as free there. No effect on any earlier release, where nothing on that screen read them. (#102)
2026-09-14
Changed
- The map draws its own coastlines instead of fetching them
Display— The land under the sections used to come from a tile service: CARTO's, then OpenStreetMap's. It now ships with the application, as a 56 KB outline of the world's coastlines built from Natural Earth's public-domain data, so the map is complete the moment the app loads and no EzFD install fetches anything from a third party. Nothing to configure, no account, no key. The background is coastlines and land only — there were never roads or towns on it at the zooms this map uses, and there are none now. (#101) Docs: Operating → The Map view, Troubleshooting → The map has no roads, towns or terrain
Fixed
- The map was jerky to pan in Firefox
Display— Panning dropped 11 of 319 frames at a worst case of 30ms, enough to feel like the map was fighting you, while the same map in a Chromium browser was smooth. The cause was Firefox repainting the raster background tiles as the map moved; with the background now drawn as shapes rather than images, the same measurement is 0 dropped frames of 517 at 6.1ms. (#101) Docs: Operating → The Map view - The map had no ground under it on a field server with no internet
DisplaySetup— Sections drew correctly but floated on empty grey, because the background was fetched and there was nothing to fetch it from. The whole map now ships with the app, so a site with no uplink gets the same map as a hosted instance. Worth one look when you test yours offline before an event. (#101) Docs: Offline field servers → Verify it actually works offline - The sample data script only half-did what it documented — It said it spread the demo contacts across a weekend and did not, leaving that as a manual step; a screenshot taken hours later showed a rate of 0 QSO/hr over a 260-contact log. Only affects documentation screenshots. (#101)
2026-09-13
Changed
- The map stops stacking labels it cannot draw legibly
Display— At the zoom the map opens at, 54 of its 85 section labels overlapped another one, worst through the Northeast where a dozen sections are smaller than their own abbreviations, so what should have been a reference was a pile of boxes. A label is now drawn only where it fits, and the rest appear as you zoom in — nothing you can already see disappears as you go further in. No section becomes unidentifiable either, because hovering or tapping the shape itself now names it and says whether you have worked it, which it never did before: the only way to identify a section used to be hitting its 9px label. Docs: Operating → The Map view
Fixed
- Section labels for what you still need were the hardest to read
Display— Unworked labels were dark grey on a near-black box, at a contrast of 2.32:1 for 9px text, while worked labels sat at 10.48:1 — so the labels an operator is actually hunting were the ones they could not read, and only in dark mode, which is the default. Both are legible now. Docs: Operating → The Map view - The map could keep drawing a stale set of worked sections
Display— It redrew when the number of worked sections changed, so a recompute that removed one section and added another — one operator deleting a mistaken contact while another logs a new section — left the map showing the old set with nothing to say it was out of date. It now tracks the sections themselves. - Worked sections on the map ran together into one amber blob
Display— The border between two worked sections was amber over an amber fill, and because that fill is translucent over a basemap whose lightness inverts between themes, in dark mode — the default — the border ended up at exactly the same lightness as the fill it was meant to separate. It was not faint, it was invisible, and a run of worked sections stopped being countable. Borders are now a neutral that flips with the theme, with worked sections carrying the stronger one so the outline of what you have done is what stands out. A second cause was quieter still: each section's fill was painting over its neighbour's border, so half of every boundary on the map was washed out even in light mode. Docs: Operating → The Map view
2026-09-11
Changed
deploy.shsupports Debian, Ubuntu and Raspberry Pi OS, and stops on anything elseSetup— It used to carry on, installing nothing and checking the prerequisites were present. The trouble was everything it does after that check: the nginx server block, the database cluster, the firewall and the service user all assume Debian's layout, and where those assumptions do not hold they fail silently rather than loudly. The clearest case is nginx — a server block written tosites-availableon a distribution whosenginx.confonly includesconf.d/*.confis never read, andnginx -tstill passes, because a file nobody includes is not a syntax error. The deploy reported success and the site served nginx's welcome page. It now stops at the pre-flight, before asking you anything, and prints what a by-hand deployment involves. EzFD itself is unchanged and still runs anywhere: what is narrow is the script. (#96) Docs: Deployment → Other distributions
2026-09-10
Added
- Run a whole event on a Pi with no internet
Setup— A machine you carry to the site can be the central log for the weekend with no uplink at all. Operators join over local WiFi and everything behaves normally, because from the app's point of view nothing is offline — dupes, band coordination and the live score are all server-side and the server is at the site. The install is the samedeploy.shas any other server, run in a different order: run it at home while you still have a network, leave the domain blank so it skips Let's Encrypt, and nothing it needs afterwards is on the internet. A Pi 4 or 5 builds the app itself; below 2 GB of RAM it leans on the swap filedeploy.shadds and takes longer, which is a one-time cost paid at home. A new guide covers the parts that only bite offline — the clock, plain HTTP, giving the machine a name, power cuts, and merging the log back afterwards. (#95) Docs: Offline field servers → Set it up at home, Offline field servers → What the machine has to be - The admin console knows what is keeping time
Setup— Server time / clock now reports a hardware RTC or a GPS receiver alongside NTP, and callsfake-hwclockout by name rather than counting it. It also offers to write the system clock back to the RTC, which is the step people forget after setting the time by hand — without it the correction lives only in RAM and the next reboot restores the old, wrong value over the top of it. (#95) Docs: Offline field servers → The clock is the part that bites, Administration → Server time - The app says when the server's clock cannot be trusted
DisplaySetup— Two new warnings on the logging page, both about the thing that cannot be repaired after an event: QSOs are stamped by the server, so a wrong server clock quietly corrupts every contact's time. It now says when nothing is holding the clock — no network time, no GPS, no hardware clock — and when whatever is holding it has gone a long time unset. Those show before anyone logs anything, which is the point: a wrong clock is silent until it has already cost you contacts. Neither fires merely because NTP is off, which on an offline field server is true by design. Docs: Offline field servers → The clock is the part that bites, Troubleshooting → Nothing is holding this server's clock - The skew banner now names which clock is wrong
Display— It could only ever say the server and your device disagreed, and deliberately would not say which was right — honest, and no help to the operator deciding what to fix. With three or more devices connected it compares what all of them report and says so outright: "9 of 11 connected devices agree". A dozen phones, most synchronised by a carrier within the last day, are collectively a better authority than a field server asking itself. With one or two devices it keeps the old both-ways wording, because that genuinely is not enough to convict either side. Docs: Troubleshooting → This server's clock is N ahead of / behind this device, API → GET /api/time
Changed
deploy.shruns on more than Ubuntu and DebianSetup— It refused outright on anything else, which ruled out the old laptops and spare SBCs a club is most likely to have available for a field server. It now separates "can I install packages here" from "can this machine run EzFD": Debian derivatives such as Raspberry Pi OS, Mint and Pop!_OS are recognised throughID_LIKE, and anything else installs nothing and is told precisely which of Node, PostgreSQL, nginx, rsync and openssl it still needs. systemd is now the one hard requirement, since the service unit is what brings EzFD back after a power cut. Docs: Deployment → Requirements
Fixed
- A field server with an RTC fitted was told its clock was broken
Setup— the clock screen asked one question, whether NTP was synchronised, and told anyone who answered no to fit an RTC module. That is aimed squarely at the operator most likely to have already fitted one: an offline field server with a working RTC answers no by definition. It now says the clock is being held by the RTC and what to check instead. (#95) Docs: Offline field servers → The clock is the part that bites
2026-09-06
Added
- The map fills sections instead of pinning them
Display— Sections are drawn as their real boundaries and fill amber once worked, so what you have and where the gaps are is one glance rather than a count of label boxes. 81 of the 85 have a boundary; Ontario's four are carved by census division under RAC rather than ARRL rules and ship as one dashed outline until those are transcribed, which is honest rather than a guessed line drawn as confidently as a real one. The boundaries are a file the app ships, so they draw on a field server with no internet — only the basemap under them needs the network. (#92) Docs: Operating → The Map view - Ontario's four sections are drawn
Display— GH, ONE, ONN and ONS were the last four without a boundary, shipping as one dashed outline over the whole province. They are now drawn from RAC's own published table, so all 85 sections fill. Nipissing District stays dashed and is labelled ONE or ONN: RAC splits it along Algonquin Park, which is not a census-division line, and a guessed line would look exactly as confident as the 84 real ones. Nothing about what a contact counts for changes — log the section the other station sent you, as before. (#94) Docs: Operating → The Map view
Fixed
Canada's provinces sat slightly apart from Ontario on the map
Display— Drawing Ontario's sections from Statistics Canada while its neighbours came from a coarser world dataset left a strip belonging to neither along the Ottawa River and the Manitoba border. Every Canadian boundary now comes from the one source, so the borders meet exactly; provincial outlines are more accurate as a side effect. Nothing outside the map reads these shapes. (#94) Docs: Operating → The Map viewThe menu opens over the map, not under it
Display— On the dashboard's Map view, opening ☰ drew the map straight through the panel: the map stayed bright where the rest of the screen dimmed, and its zoom buttons and the OpenStreetMap credit sat on top of the menu entries. The map now takes one place in the stacking order like any other panel, so the menu, the sticky header and the log's filter dropdowns all sit above it. (#93) Docs: Operating → The menu
2026-09-05
Added
- Merge an event that ran in two places into one log
Setup— A field server at the site and the hosted instance can both hold real contacts from the same weekend. Importing that export used to make a third event, so the two logs had to be reconciled by hand.POST /api/import/event?merge_into=now reconciles them: contacts you don't have are added, duplicate flags are recomputed across the whole log — each instance computed its own against a different subset, so both were wrong for the union — and the roster and checkout history come across. It reports what it did rather than just succeeding, and it will not resolve a contact edited on both sides, undo a deletion, or change the event's settings. Running it twice is safe. (#90) Docs: Administration → Merging two instances of one event, API → POST /api/import/event - Rig control is a button in the header
Display— It shows whether a radio is connected and what it is tuned to, and with none connected it readsRig offand opens the panel you connect from. That panel used to be reachable only from the band list further down, which is not where anyone looks for "how do I hook up my radio". (#86) Docs: Operating → Reading the header, Rig control → Setup - One menu, behind ☰
Display— Everything you can do on the logger and the dashboard now lives in a slide-out menu, grouped and with a line under each entry saying what it is. The header keeps only what you read at a glance. On a phone the dashboard header was four rows of buttons before the filter bar even started; it is two now. (#84) Docs: Operating → The menu - Who worked what
Display— A new Operators view on the dashboard, reading the log you already have: contacts, points, best hour, bands, sections, and which sections that operator was the first to reach. Sort by any column. The totals row is meant to be checked against the scoreboard, so contacts imported without an operator get their own row rather than disappearing out of the count. The per-hour average stays blank until an operator's contacts span an hour, since dividing by less than one projects a partial hour out to a whole one. (#82) Docs: Operating → The Operators view - A read of the log before you submit
ScoringDisplay— The summary sheet now opens with anything worth settling first: unrecognised sections, a station that sent two different exchanges, a bonus claimed that rule 7.3 does not list for your class, and Winter Field Day objectives the log contradicts — those multiply, so a wrong tick moves the whole score. It does not check callsigns against any list: Field Day brings out operators no contest list has heard of, and flagging them would bury the findings that are real. (#81) Docs: Field Day → Before you submit - The GOTA station is a real station now
ScoringExportsDisplay— Set a GOTA callsign on the event and operators can sign in there; their contacts are signed with that call, count for the entry as normal, and earn their 5 bonus points each from the log rather than from a number somebody remembers to type. There is no cap and no per-operator limit — the app used to apply a 1,000-point cap that was never in the rules. (#80, closes #23) Docs: Field Day → The GOTA station, Database → qsos
Changed
- Text is a size larger, and the small sizes are one size
Display— The body of the interface goes from 12px to 13px and headings from 14 to 15, while the four near-identical sizes below 12px — 9, 10, 10.5 and 11 — become one. Thirteen distinct sizes, nine now. Easier to read outdoors at two in the morning, and the side panel still fits a 1366x768 laptop without a scrollbar. (#87) - Every control on a phone is thumb-sized
Display— Eleven were under 44px, some as small as 15px tall. They are all at least 44px now on a touchscreen, and unchanged on a laptop, where a logger wants the density and a mouse can hit a small button. (#87) - The logging screen's side panel fits without scrolling
Display— On a 1366x768 laptop it ran 307px past the bottom of the screen. The score panel now shows the mode split and the score, with the full breakdown on the dashboard, and the call checkout starts folded on a contest, where claiming a band is opt-in. It stays open on a special event, and a claim you hold is shown either way. (#86) Docs: Operating → The dashboard - Quick Log is offered on special events only
Exports— It logs a contact straight from the callsign box, which is what a special event wants and what a contest cannot use: Field Day and Winter Field Day need a class and section from every station, and logging without them filed whatever was left in the boxes. With a callsign the call-history file knew, that was a plausible exchange the station never sent, and it went to Cabrillo unchallenged. (#86) Docs: Special event stations → Quick Log - The rig control panel is short now
Display— Status, a connect button and the three steps that start the bridge. The per-platform install walls and the troubleshooting section it used to carry are in the rig control guide, linked from the panel. (#86) Docs: Rig control → Setup - Every control is reachable at every screen size
Display— Which controls you got used to depend on how wide your browser was, and not on purpose. The guides were reachable only on a phone; Import ADIF, both exports and the second-radio window only at tablet width and up. If you have been logging from a phone and could not find the export, that is why. (#84) Docs: Operating → The menu - The rate beside each operator is now their best hour
Display— The dashboard's Operators panel divided an operator's contacts by however long they had been sitting there, so a good run read lower and lower as the band went quiet around it. It now shows the most they worked in any 60 minutes, which is the same figure the new Operators view prints — the two would otherwise have disagreed on the same screen. (#82) Docs: Operating → The Operators view
Fixed
- The map draws again
Display— Every tile read "API key required". The map came from CARTO's basemap CDN, which used to be open to anyone and is not any more, and the refusal arrives as a picture rather than an error — so the map still drew, still put every section in the right place, and said nothing about why it was unreadable. It now uses OpenStreetMap's own tiles, which need no account and no key, so there is nothing to configure and a field server with no credentials works the same as a hosted one. Dark mode filters the tiles rather than loading a second style. (#91) Docs: Troubleshooting → The map says "API key required" - The dashboard scrolls like a page on a phone
Display— It was two stacked panes that each scrolled inside a screen that did not: a window onto the log, and under it a window about two lines tall onto the score, bonuses, sections worked, operators and the join code. The page scrolls now, with the view tabs pinned at the top, and only the log and the operators table keep a scroll of their own. Nothing changes on a laptop. (#88) Docs: Operating → The dashboard - Field Day says band coordination, not call checkout
Display— The logging panel was worded for a special event whatever the event was, so a Field Day screen offered to "check out" the callsign and reported that "nobody has the call checked out" — about a callsign nobody was competing for, since every station sends the club call all weekend. It now says what is actually being coordinated on a contest: the transmitter, one signal per band and mode, held by a station rather than a person. Claiming is still optional there, and the panel no longer prints an empty list saying so. (#89) Docs: Operating → Claiming a band and mode, Quick start → Pick where you're operating - A busy club no longer pushes the logging panel off the screen
Display— The Operators list grew with the roster: ten people signed in made it 368px tall and ran the side panel 143px past the bottom of a 900px display. It shows five at a time and scrolls inside itself now, with you on top. (#87) Docs: Operating → Who else is on - The CW keying button came back to the header
Display— Opening the keying window is something you do at the start of a CW shift, so putting it in the menu made it look as though it had been removed. It is a button again, beside Rig, and whether it appears comes from the same rule the menu uses. (#86) Docs: Operating → Reading the header - The header printed
\u00d7instead of×Display— Between the sections worked and the score. A unicode escape had been written into the markup as text rather than as the character it stands for. (#86) - The logger kept two disagreeing copies of its own controls — One for the header and one for a phone bar, each hand-maintained, which is why the two had drifted apart. There is one list now, read by both screens, and a test fails if a second copy appears or an entry is added that nothing wires up. (#84)
- Changelog entries citing a pull request rendered as literal text — A
citation like
[#80]needs a matching definition at the foot of this file to become a link; two entries were missing theirs and showed the brackets instead. The link test now checks citations as well as guide anchors. (#82)
2026-08-30
Added
Rig control without installing anything, on Chromium
Display— Chrome and Edge can open the radio's CAT port directly, so band and mode follow the VFO with no Python, no Hamlib and no second process to keep running — and a second radio needs no second bridge. Kenwood and Elecraft commands for now, which also covers FlexRadio SmartCAT, and reading only: CW keying still uses the bridge. The bridge is unchanged and stays the default, and is still the only option on Firefox, Safari, iOS and a plain-HTTP server. (#79) Docs: Rig control → Two ways to connectA
LICENSEfile — The licence was stated in the README and nowhere a tool looks, so GitHub reported the project as unlicensed and anyone checking before deploying it at a club found nothing. The text is unchanged and now lives in the file that governs, with CI failing if the README's copy drifts from it. (#78)Every changelog entry points at its documentation — Each entry now names the guide and the section covering it, so reading about a change and reading how to use it is one click rather than a search. A test checks every link and every anchor, because a renamed section silently leaves the link resolving to the top of the page. (#76) Docs: Development → Tests
The documentation sidebar is grouped, and shows where you are
Display— It listed all seventeen guides alphabetically with nothing to say which was yours and no mark on the one you were reading. It now follows the index's own audience grouping, highlights the current guide, and carries previous/next links so the set can be read straight through. On a phone it collapses, so a guide starts at the top of the screen rather than below a list of links. (#77) Docs: EzFD documentation → Reading these in the app
Fixed
- The development guide described a CI that no longer existed — It said
lint and
shellcheckwere "deliberately not gated" and listed four test suites. Both are gated and there are twelve. A contributor reading it would have expected a pull request to pass that CI now fails. (#76) Docs: Development → Tests, Development → CI - Links to a section whose heading has an apostrophe
Display— At/docsthose headings were given an id with a stray39in it, so every link to one opened the right page at the top rather than at the section. It worked on GitHub and failed only in the app, which is the direction nothing was watching. (#76) Docs: Development → Tests - Links to the documentation index
Display— A guide pointing at the index sent a reader to/docs/README, which is not a page. The index is served at/docs. (#76) Docs: EzFD documentation
2026-08-28
Added
- The station number in the logger header
Display— The station you pick at sign-in is now shown beside your callsign, in the main window and the CW keying window alike. Nothing on screen used to confirm it, so a mis-click at sign-in tagged a whole shift's contacts with the wrong transmitter and the first sign of it was the Cabrillo export. (#72) Docs: Operating → Reading the header - The operating position picker opens on a band
Display— Rather than forty-five blank buttons, it preselects one you have checked out, or failing that the band this browser was last logging on, and says which of the two it used. Clicking any other band changes it. The memory is per station and survives signing out, so the next operator at a club laptop is offered the radio's own band. (#73) Docs: Quick start → Pick where you're operating, Operating → Getting to the logger
Fixed
- Checkouts appear in the position picker as they happen
Display— The picker refreshed on a timer, so for up to fifteen seconds it could show a band as free that somebody had already taken — the exact window in which two operators pick the same one. It now listens on the same live stream the logging screen uses. Its clock was the other half: a checkout made this second was arriving and being drawn as not yet started. (#74) Docs: Operating → Getting to the logger, Architecture → Real-time updates - Leaving visitor mode goes home
Display— ← Exit on a visitor dashboard landed on the event's operator sign-in, the one screen a visitor had deliberately not chosen — and if the browser still held a sign-in for that event, it went straight on into a logging window. (#75) Docs: Getting started → While it runs
2026-08-25
Added
- An operating position step when you sign in
Display— After your callsign you are asked where you are sitting, with every band and mode shown as free, on air, or checked out, and the option to claim it there. The logger used to open on a hard-coded 20m phone, which on a busy site is a guess — and on a special event could put you on a band somebody else had booked. (#71) Docs: Quick start → Pick where you're operating, Operating → Getting to the logger - Dashboard log view
Display— Every contact in one table, narrowed by operator, station, band, mode, section, time or duplicates, with the columns you choose. Answers a question about the log without exporting it mid-event, and gives a club something worth putting on a screen at the site. (#66) Docs: Operating → The Log view - This changelog — So you can tell at a glance whether an update moves a score, changes an export or alters the screen, instead of reading commits. (#67, #68)
Changed
- The dashboard opens on the log
Display— Contacts arriving is what a dashboard is usually put up to show, and the log is the only view that answers a question about a specific QSO. The map and rate chart are one tab away. (#70) Docs: Operating → The dashboard - Changelog layout — Entries are grouped by what they did (added, fixed,
changed, removed, security) with the subject in front, so a date's worth of
work can be skimmed rather than read. Adds a
Setuptag for deployment and admin-console changes. (#69)
2026-08-24
Added
- Contest rules reference —
rules-reference.mdtranscribes the scoring rules for both contests with their rule numbers, so a value can be checked against the source rather than against the code that uses it. (#60) Docs: Contest rules reference - Documentation as part of every change —
AGENTS.mdrecords which guide owns which kind of change, so a feature and its documentation land together. (#62) Docs: Development → Conventions
Fixed
- Winter Field Day scoring
ScoringDisplay— WFD was scored with Field Day's formula, which the WFD rules cannot produce by any route. It usesQSO points × (Objective Multiplier + 1), with no bonus points and no power multiplier; the bonus panel is now an objectives list. (#60, closes #58) Docs: Field Day → Winter Field Day, Contest rules reference → Scoring - Field Day bonus schedule
ScoringExports— Ten of eighteen bonus values were wrong, three were missing, and two were awarded that no rule contains. Any entry claiming bonuses was submitting a wrong total, and emergency power alone was over-claiming by thousands of points on a large log. (#59) Docs: Field Day → Bonus points, Contest rules reference → Bonus points (7.3) - Mobile stations in Winter Field Day
Display— ClassMwas missing from the entry form's copy of the WFD class letters, so every contact with a mobile station was flagged as an invalid class. (#60) Docs: Contest rules reference → Exchange and classes - The
MXexchangeDisplay— Mexican stations send it in WFD, and it was being counted as a probable typo. (#60) Docs: Contest rules reference → Exchange and classes - Stale Cabrillo guidance
Exports— The guides told clubs to hand-correct the claimed score months after that bug was fixed. Following them would have introduced the error they warned about. (#57) Docs: Field Day → Submitting, Troubleshooting → Cabrillo claimed score looks wrong - Admin console event list
Setup— A long list was unusable, and some failed actions printed success. (#63) Docs: Administration → The event list - README licence and AI disclaimer — Both were lost when the file was rewritten rather than edited. (#61)
Removed
- The Worked All Sections bonus
Scoring— Field Day has never had one, and the 100 points it awarded went onto entries that never earned them. Sections are still counted, still shown, and still worth chasing — they just score nothing. (#59) Docs: Field Day → Bonus points, Contest rules reference → Bonus points (7.3)
2026-08-23
Added
- Documentation inside the app
Display— Served at/docs. The operator who needs the troubleshooting page on a field server with no internet is exactly the one who cannot reach GitHub. (#54) Docs: EzFD documentation → Reading these in the app - Band and mode coordination for contests
Display— A station can claim a band and mode, and another is warned rather than silently doubling up. (#47) Docs: Operating → Who else is on - Operator quick start
Display— For someone who has a join code and a radio and nothing to administer. (#56) Docs: Quick start
Changed
- New events default to section MN
Display— Matching the example callsign rather than an unrelated section. (#55)
Fixed
- Contacts lost during a CW run — The CW keying window logged straight to the server with no local copy, so an outage destroyed contacts rather than delaying them. It now shares the offline queue. (#51) Docs: Rig control → CW keying, Operating → Working offline
- The offline queue after a server-only outage — A restart or a brief 502 leaves the browser's own connection up, so nothing told the queue to retry. Contacts sat unsent until somebody noticed the badge. (#49) Docs: Operating → Working offline, Troubleshooting → The pending counter won't clear
- WSJT-X contacts the server refused — The relay printed an error and moved on. It now spools them to disk and retries. (#52) Docs: Digital modes → If the server goes away
- Two radios, one operator
Display— Presence and call checkouts are keyed per radio, so a second logging window no longer overwrites the first's band or releases its slot. (#53) Docs: Special event stations → Running two radios at once
2026-08-22
Added
- Recoverable deletes with an audit trail
DisplayExports— A contact missing from the log could not be distinguished from one never logged. Deletes are now reversible and record who made them. (#45) Docs: Operating → Editing and deleting, Troubleshooting → A QSO was deleted by mistake - Event export and import over HTTP
Setup— Moving an event between instances no longer needs shell access on the server. (#44) Docs: HTTP API → Import and export, Administration → Restoring from JSON - Server clock skew detection
Display— A field server with no RTC and no NTP stamps every contact with a plausible-looking wrong time, which is invisible unless something checks. (#33) Docs: Administration → Server time, Troubleshooting → The server's clock - Unit suites for scoring, exports and the section list — The highest-consequence logic in the project was covered only indirectly. (#39, #43) Docs: Development → Tests
- Lint and shellcheck as CI gates — A backlog of findings was cleared and the gates turned on, so the next one fails a pull request rather than accumulating. (#38, #40) Docs: Development → CI
Fixed
- Cabrillo claimed score and transmitter numbers
ScoringExports— The header ignored the power multiplier, dropped bonus points entirely, and stamped every contact with the same transmitter. (#31) Docs: Field Day → Submitting, Troubleshooting → Cabrillo claimed score looks wrong - Typos counting as sections
Scoring— A handful of unrecognised exchanges could read as a clean sweep.DXis accepted as a valid exchange without being counted as a section. (#36) Docs: Field Day → Sections, Troubleshooting → A section is rejected as invalid - The ARRL/RAC section list
Scoring— RAC's recent splits and renames were missing, so the sections-worked denominator was wrong. (#33) Docs: Field Day → Sections
Security
- Next.js updated
Setup— Clears four high-severity CVEs. (#42) Docs: Deployment → Updating
2026-08-21
Added
- Special event station support
Display— One callsign across many operators, with band and mode checkout enforced by a database constraint rather than by convention. Docs: Special event stations - Full documentation under
docs/, with screenshots — Guides for operating, deployment, administration and the API, replacing a single README. (#25, #26) Docs: EzFD documentation - 24-hour clock pickers and a schedule timeline
Display— Event start and end times are set on a UTC clock rather than typed, and the schedule is shown as a timeline. (#27, #28, #29) Docs: Special event stations → The call checkout
Earlier history is in the commit log. This file starts where the work became continuous enough to be worth summarising.