Skip to content

Releases: i7Gamer/MySpeed

Release 1.5.0

Choose a tag to compare

@github-actions github-actions released this 30 Aug 19:38

The multi-target release: named measurement targets replace the single global provider, iperf3 measures against a server of your own, and the statistics learned to compare like with like — alongside eight new languages and the fixes three review campaigns turned up. Upgrading runs one automatic migration: your provider settings become the first target and every stored test is stamped with it, nothing to redo by hand. One behaviour changes on its own: LibreSpeed runs at its own 15-second default instead of the 5 seconds it was pinned to, so its figures firm up and its tests take a little longer.

✨ New

  • Measurement targets — named provider+server pairings instead of one global choice: add several, reorder them, switch one off, give each its own alerts and optimal values. The chips filter the dashboard, the statistics and the export; Prometheus carries per-target series; a backup names each row's target so a history survives the move to another instance; and a read-only viewer never sees a private server's address. Pages grade against the target their rows actually belong to — a mixed view falls back to the instance-wide settings rather than judging one line by another's optimum.
  • iperf3 — a fourth provider, for measuring against an operator-run server: host[:port], IPv6 in brackets, one invocation per direction with TCP slow-start left out of the figure, and latency sampled separately since iperf3 reports none. The binary is fetched on first use, not at boot.
  • The period comparison is honest — a range still running is compared against the same elapsed share of the previous window, cut at the same time of day (daylight-saving changes included), instead of seven full days standing against six and a half — which read every count as systematically down. The note says when the comparison window was cut, and tests-per-day divides by the days that have actually happened.
  • Medians beside the averages — ping, jitter, download and upload each carry one now, on the value panes and the latency row: one spike drags an average, and the middle value says whether the line or the afternoon was slow.
  • Data used by the testing — what the tests themselves cost in traffic over the range, down/up/total with its change against the previous period, in the overview detail panel. Worth a look before tightening a schedule on a metered line.
  • Peak-hour latency — the rise from the calmest hour's average ping to the busiest hour's, beside the peak-hour slowdown: the per-hour latency was computed all along and displayed nowhere, and it is the half of an evening slowdown a call actually feels.
  • Eight new languages — Czech, Japanese, Korean, Norwegian Bokmål, Swedish, Traditional Chinese, Irish and Catalan bring the count to 23, alongside a 379-correction sweep of the existing ones and the Spanish, Turkish, Italian and Russian repairs taken from upstream.
  • Sign-in from a forward-auth proxy (#767) — trusted-header authentication for setups where Authelia or friends already decided who you are.

🔒 Security

  • The installer keeps the data directory private — created with the service account as owner and nobody else inside, on every path that can create it; the ownership handover no longer follows a symlink; and anything already at the data path that is not a real directory is refused before the running service is touched.
  • The HTTPS listener is held to the HTTP one's limits — the same header split and a ceiling on both, instead of one door stricter than the other.

🐛 Fixes

  • A figure an older node spells as text is a reading everywhere — and the -1 placeholder is a reading nowhere: proxied payloads no longer print "-1%" beside a delta computed from it, hide a measured value as N/A, or let one corrupt stored number drag an average. A poisoned row in a restored history degrades that figure, not the page.
  • Every latency prints at one decimal — the stability card said "5.23 ms" one panel from a detail pane saying "5.2 ms" for the same measurement.
  • Charts show honest gaps. The line was drawn straight across the nulls a failed test leaves, so an outage rendered as a continuous curve — the gaps break the line now, and a lone reading in a bad hour gets a dot instead of being invisible.
  • The CLI runner is tougher — output is decoded across chunk boundaries, so a multi-byte character split mid-number no longer corrupts a parse; a wedged CLI's streams are bounded instead of growing the heap for the whole timeout; and shutdown waits for the tracked child instead of leaving it writing into a closed database.
  • One dead endpoint no longer delays every other notification — integrations are notified together, not queued behind the slowest timeout.
  • An invalid stored cron schedules the default instead of nothing, and says so.
  • The countdown counts to the run that is sleeping, not the slot after it — with the schedule offset on, a poll during the offset sleep rolled the bar forward as if the test had been skipped.
  • A missing static asset answers 404 instead of the app page wearing a script's name — which browsers reported as a MIME refusal and caches remembered.
  • Switching the active node under an open statistics page re-fetches everything for the node now shown.
  • Interfaces the connectivity probe cannot vouch for are listed anyway (#806) — a probe failure hid real adapters from the dropdown.
  • A restored row no run could have produced is skipped, counted, and said — not stored to poison the averages later.
  • ntfy and Gotify meet their APIs — an out-of-range or non-numeric ntfy priority is dropped instead of sent as NaN, and Gotify's longer v3 tokens are accepted.
  • The storage dialog's export hint holds its height — ticking "Include credentials" swapped in a longer sentence and moved the toggle out from under the pointer that clicked it.
  • Small edges from 1.4.1's theme work — the system-theme listener falls back where older browsers lack addEventListener, the settings menu closes on the same keydown treaty as every other overlay, and the language list stops cropping the last entry's border.

Downloads

Platform x86-64 ARM64
Windows EXE · MSI · EXE (no AVX2) · MSI (no AVX2)
Linux Binary · No AVX2 Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.5.0

What's Changed

  • Fix/status countdown and language list by @i7Gamer in #34
  • feat: multiple measurement targets and iperf3 support by @i7Gamer in #37
  • chore(deps): bump the server-minor-and-patch group with 4 updates by @dependabot[bot] in #35
  • chore(deps): bump the client-minor-and-patch group in /client with 7 updates by @dependabot[bot] in #36

Full Changelog: v1.4.1...v1.5.0

Release 1.4.1

Choose a tag to compare

@github-actions github-actions released this 25 Aug 16:45

The appearance release: the interface follows the machine's theme, comes in four colour palettes, and the preferences dialog was redesigned around the new choices — alongside the fixes an audit of the release legs turned up, the macOS binaries first among them. Upgrading is drop-in: no migrations, no configuration changes, and a theme you chose by hand is kept exactly as it was. The one behaviour that changes on its own: an instance nobody ever told stops assuming dark and follows its machine.

✨ New

  • The interface follows the machine's theme — System is the new default and tracks the OS live, and the right colours are on the document before the first paint, in the browser chrome and on the installed app's splash, instead of a dark flash and a blue that left the stylesheet in January. Light mode was re-measured on the way: every grade, accent and chart mark now clears the contrast floor its role answers to (the old green read 2.42:1 as a verdict on a light page), and the chart marks stay apart under the two common colour blindnesses.
  • Four colour palettes — Nord, Carbon and Ember join Slate, each previewed in its own colours before saving, and everything follows: the surfaces, the accents, the verdict colours, the charts, the browser chrome.
  • Grade colours are a choice — the icon alone carries the verdict, or the figure is coloured with it.
  • Full-resolution charts are a preference — the chart detail that could only be set from an expanded chart's toolbar is a default now, set beside everything else.

🐛 Fixes

  • MySpeed-macos-x64 starts. Both macOS targets were built on one arm64 runner, so the x64 binary was compiled against a native binding that was never on disk — dead on arrival, and nothing in the pipeline ever ran it. Each architecture now builds on a runner of its own, and every macOS binary is booted and probed before it may reach a release, as Windows and Linux already were.
  • A missing image renderer no longer stops the server. The social-preview renderer is a native addon, loaded when an image is first asked for rather than at boot; if it cannot load, the preview falls back to the project banner and everything else runs.
  • An import that skipped rows says so — the counts, in orange, instead of the same green as a clean restore; and a refused configuration import names the stored key it choked on, instead of leaving the whole file to bisect by hand.
  • Email subjects offer the template variables they always accepted — the chips appeared under the message and not under the subject right above it, which read as the subject taking none.
  • The scrollbar has a thumb you can see. It wore a border colour that measured 1.30:1 against a dialog's surface, painted its track in the page colour inside lighter surfaces, and under 730px had no room to draw a thumb at all; Firefox, which none of those rules ever reached, is themed for the first time.
  • The settings dialogs agree on one width, and the language list shows more than six of its fifteen languages.
  • Every language is complete again — the strings 1.4.0 shipped in English and German only, and everything new in this release, are translated across all fifteen languages; no more fallbacks. With repairs on the way: the Dutch preferences menu item rendered a translation-pipeline artifact verbatim, six translation improvements are taken from upstream, and the measurement units are guarded against the mangling upstream ships today (a German jitter of "M" beside a ping of "ms").

Downloads

Platform x86-64 ARM64
Windows EXE · MSI · EXE (no AVX2) · MSI (no AVX2)
Linux Binary · No AVX2 Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.4.1

What's Changed

  • Feat/upstream issue backlog by @i7Gamer in #30
  • feat(i18n): complete every shipped locale, and hold them there by @i7Gamer in #31
  • refactor(i18n): take the untranslatable strings out of the locales by @i7Gamer in #32
  • Themes, colour palettes, and the audit findings behind them by @i7Gamer in #33

Full Changelog: v1.4.0...v1.4.1

Release 1.4.0

Choose a tag to compare

@github-actions github-actions released this 24 Aug 12:06

A feature release worked out of the upstream issue backlog: MQTT and Home Assistant, email alerts, a timezone for the schedule, and hosting under a path prefix — fifteen upstream reports answered, alongside the fixes a whole-project review of 1.3.5 turned up. Upgrading is drop-in: no migrations, no configuration changes, every new setting off until it is set. One thing to know first: the speedtest CLIs are now checked against a pinned digest before they are unpacked, so an instance fetching them through a mirror that repackages the archives will refuse the download and say why.

✨ New

  • Results are published to MQTT (#1190) — the flat JSON the webhook, the CSV export and the API already use, so a sensor reads value_json.download. Retain is offered, because without it an entity reads "unknown" from every restart until the next test; failures go to their own topic, <topic>/error, because a sensor fed both payload shapes goes unavailable.
  • Home Assistant finds the sensors by itself (#807) — seven retained discovery messages: the two throughputs, ping, jitter, packet loss and the two loaded latencies, each with a unit and state_class: measurement so long-term statistics work at all. Off unless asked for.
  • Alerts by email (#1259) — the one channel missing from eight integrations, and it takes more than one recipient. A list is refused whole if any address in it is malformed; loopback and LAN relays stay allowed, and the address guard, the deadline and the activity note apply as to every other send.
  • Telegram notifications can land in a topic (#1176) — without message_thread_id every one of them arrives in a supergroup's lobby.
  • The schedule keeps its own timezone (#1115, #748) — the cron, the quiet window and the countdown all read it, where before they ran on whatever clock the host kept and the Docker image pins TZ=Etc/UTC. Picked in the pause dialog beside those hours; an instance that sets none behaves exactly as it did.
  • The whole application can be served from a subdirectory (#771) — BASE_PATH, for a proxy that routes on a prefix without stripping it off, and the only setting involved: the client works the prefix out for itself and a request without one passes through untouched. Fixes the PWA manifest's three absolute icons with it.
  • A server ID can be typed without picking one from the list first (#1455) — the field appeared only once a listed server had been selected, and the twenty on offer are the twenty nearest the instance.
  • A database that cannot be read says what to do about it (#1549) — an integrity check ahead of the migrations, reported rather than fatal, naming the three ways out: restore a backup, run sqlite's own .recover, and only then delete the file. Exiting is what produced that report's 138 restarts.

🔒 Security

  • The downloaded speedtest CLIs are verified against a pinned digest. All 27 platform entries carry the SHA-256, checked between download and extraction and failing closed. They are spawned on a schedule, so a replaced upstream asset was arbitrary code on the host once an hour; 1.3.5 stopped them running as root, and this is the other half. It catches a later change, not an archive that was never trustworthy.
  • The installer no longer gives up its privilege separation quietly. Under root's 027 or 077 umask it created an installation directory the service account could not enter and fell back to root, one line of text as the only sign; uninstall.sh --keep-data recreated the same mask. Both state the mode now.
  • A URL credential no longer leaves by three side doors — the configUpdated event carried the LibreSpeed URL verbatim to the webhook and Discord modules, a run against a custom LibreSpeed backend left it in data/servers/libre_custom.json, and the InfluxDB endpoint shipped in clear beside the token that had just been blanked.
  • A LibreSpeed URL is judged by its protocol, not by whether new URL() can parse it — which is true of javascript:, data: and file:.
  • The IPv6 metadata address is refused. AWS answers IMDS at fd00:ec2::254, Unique-Local rather than link-local, so it walked past every guard that allows the RFC1918 ranges on purpose.
  • An outbound post no longer follows a redirect, which delivered the request, method and body intact to a destination the guard never saw.
  • The container's ownership pass no longer follows symlinks, which handed a link's target to the account the server runs as.
  • FRAME_ANCESTORS is checked before it reaches the header. A semicolon appends a whole directive to the policy; a newline threw on every request — 500 to everything, the health endpoint included.
  • The failed-password throttle no longer evicts a lockout that is still running.

🐛 Fixes

  • A speed below zero is refused instead of recorded: one impossible figure beside two good ones dragged the average, coloured a grade, went out in the CSV export and raised an outage that never happened (#875, #792).
  • A Cloudflare run that measured nothing no longer fabricates a zero for the direction that never ran — 0 Mbps is what an outage looks like.
  • A provider answering "too many requests" is held fifteen minutes, doubling to a two-hour cap, instead of one refused request and one alert per minute for as long as the limit stands (#846, #1092). Never for a run started by hand.
  • A CLI the boot could not fetch is fetched on the next run, instead of failing every test for the life of the process.
  • The shutdown kill no longer spawns a replacement that outlives the server and writes into a closed database handle.
  • English travels with the bundle, so a locale that fails to load can no longer render an error page that reloads into the same failed request (#725, #1330). A missing other locale falls back as configured.
  • The dashboard survives a browser refusing session storage — Incognito, third-party cookies off, the embed in Homepage or Heimdall that FRAME_ANCESTORS exists for.
  • A session expiring on a node asks for the password instead of dropping you on an empty node list.
  • Signing in no longer reports success for a 401, or a 503 from a proxy in front of a stopped container.
  • Hovering a failed test says why it failed. The tooltip filter dropped the only dataset with a point at that index, so the branch naming the reason could never run.
  • A Cloudflare row with no latency average is no longer averaged as a 0 ms reading, which the alert gate had refused all along.
  • One failed request no longer blanks a statistics page whose other half arrived.
  • A node address ending in a slash works. Concatenation asked the child for //api/config, so every proxied request 404'd and a healthy node read as broken.
  • A node backup fits through the relay, which was refusing the one endpoint whose size grows with faithful use behind a bare "Internal server error".
  • A node card stops polling a hidden tab — some 4,300 requests an hour for six nodes.
  • The none interface sentinel is read as unset, not as a pinned adapter: it earned a three-round protection wait that failed every scheduled test for three hours.
  • The ping threshold keeps its fraction. "Use recommended" on a fibre line stored 0 instead of 0.4 — a threshold no latency is ever under — behind a dialog saying the change applied.
  • The welcome wizard stopped rewriting all three thresholds with an integer parse on its way past.
  • A factory reset is one transaction, so it can no longer half-happen and leave nodes polling behind a configuration that says "factory fresh".
  • InfluxDB lines carry packet loss and the two loaded latencies, which the stored row and the webhook payload had all along.
  • A long Telegram template whose formatting the 4096-character trim cut in half is sent unformatted rather than not at all.
  • One unreadable value no longer answers 500 to every Prometheus scrape until a newer test arrives, which reads as the exporter being down.
  • Restoring a history no longer freezes the server. await between rows never left the microtask queue, so nothing else was served — including the healthcheck that then restarts the container mid-write.
  • The history export is streamed, instead of built as one allocation beside every row it read.
  • The two routes that read every row are capped like the date-ranged export beside them, and a restored configuration cannot pack a million recommendations into one 50 MB body.
  • The progress fill glides again, instead of being hard-stepped once a second by the toolbar's fit walk.
  • The status bar is driven from memory, not by polling two database queries, three config reads and a cron computation every second. Its responses can no longer overtake each other and resurrect a finished run.
  • install.sh -d myspeed no longer hangs, and a relative path no longer writes a unit systemd refuses.
  • The uninstaller stops choosing its whole removal on a substring — a MySpeedBackup container sent a native systemd host down the Docker branch.
  • A host holding both a container and a service now gets both, instead of "MySpeed has been uninstalled" over a service still running and enabled t...
Read more

Release 1.3.5

Choose a tag to compare

@github-actions github-actions released this 19 Aug 16:46

A maintenance release from a review of the interface a keyboard reaches, the account the service runs as, and the statistics page at every width. Upgrading is drop-in: no migrations, no configuration changes. On Linux, running install.sh again is what moves the service off root, and an existing database keeps working.

✨ New

  • The overlays behave like modals. Focus moves into a dialog, stays there, and returns to whatever opened it — it used to sit on the control behind the backdrop while Tab walked the whole page underneath. It stays through a save, too: a button that disables itself while it works took focus out of the dialog with it, and the browser says nothing when it does. Menus hand it back the same way, so choosing an integration to add no longer drops you at the top of the document.
  • Every dialog can be closed without a mouse. The close control was a bare icon carrying a click: not in the tab order, and announced as nothing, so a reader had to know to press Escape.
  • The date range is reachable. Its trigger answered a pointer and nothing else, and the presets sit inside the popover it opens, so a keyboard-only reader was left with whatever range the page loaded with. The month and year arrows behind it announced as nothing.
  • The settings menu opens its nine dialogs, and the export menu its two formats; every entry was click-only.
  • A change states its direction. All that was announced was a bare magnitude — "5%", with no word for which way, where on ping and packet loss the two directions are opposite verdicts.

🔒 Security

  • The service no longer runs as root. Nothing MySpeed does needs a privilege, and it downloads a third-party speedtest CLI at first boot and then spawns it — so running that as uid 0 meant a replaced upstream asset, or any remote-code flaw in the server, executing with full access to the host. The Docker image already dropped privileges; the native installer did not. It now creates a dedicated myspeed account and runs the unit under the standard systemd sandbox — falling back to root, and saying so, where the account cannot be created or the chosen directory is one it could not reach.
  • The uninstaller removes that account, instead of leaving an entry in /etc/passwd whose home directory was the path it had just deleted. Not under --keep-data: data that is kept keeps its owner.

🐛 Fixes

  • Optimal values1.2.3, .. and a lone . were all accepted as thresholds, because the check asked whether every character was a digit or a dot rather than whether the value was a number. No server code reads them, so one was stored behind a success and handed to the browser, where it cannot be read: every speed on the dashboard turned grey.
  • Integrations — a display name longer than the column no longer answers 500. No module declares that field, so the loop holding every length cap never saw it: MySQL refused it with a stack in the operator's log, sqlite stored it whole and reported success.
  • Restoring a backup — a refused restore says which stored value it could not read. It is abandoned whole on the first one the validator turns down, taking the nodes, the integrations and the history with it, and the only answer was "Error importing config" — leaving every stored value to check by hand.
  • Translations — the stability card prints its jitter in milliseconds again. It read "0.8 M" in German, "0.8 m" in Spanish and "0.8 Mms" in French, and Russian gave the same unit in two scripts in one card. All four are held against each other from here.
  • The statistics page — a reading is no longer stated without its verdict. The grade is worn by the icon, and the icon was the first thing a tight card gave up; it is kept at every width now, and the figure steps instead — once for all four panels rather than four times at four different widths. The top row squares from 1417px too, where the summary's descriptions stop fitting and it was left holding room it had nothing to put in: at 1300px all three cards read at full size on 365px each, where two had been a step down.
  • Installing on Windows — the installed entry credits the project, with About, Help and Update links, where Add/Remove Programs had shown a bare given name as Publisher and no link back. Not the "Unknown publisher" line on the UAC prompt: that one is read from the file's signature and nothing else changes it.

Downloads

Platform x86-64 ARM64
Windows EXE · MSI · EXE (no AVX2) · MSI (no AVX2)
Linux Binary · No AVX2 Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.3.5

What's Changed

Full Changelog: v1.3.4...v1.3.5

Release 1.3.4

Choose a tag to compare

@github-actions github-actions released this 18 Aug 07:18

Changes since 1.3.3

A maintenance release from a second whole-project review, covering the integrations' outbound side, the node proxy, the install and release scripts, and the statistics page at every width and in every language. Upgrading is drop-in: one migration runs by itself on MySQL, and sqlite needs nothing.

🔒 Security

  • An integration can only reach a real destination. A webhook, healthchecks, gotify, ntfy or InfluxDB URL pointing at a link-local address — 169.254.169.254 is the cloud metadata service — is refused before the request leaves and reported as a failed send. Loopback still works, so InfluxDB or gotify on the same host is unaffected.
  • A node can no longer decide what runs at your origin. Proxied answers are limited to JSON, CSV and binary; anything else arrives as bytes rather than as a document your instance renders.
  • Your ntfy topic is redacted like a password. With no token set it is the whole of publish and subscribe control, and it used to be visible on a demo instance and in a configuration export marked "secrets redacted".
  • Integration credentials stay out of the log. A failed database write used to print the telegram token, the webhook URL and the influx token to disk.
  • A demo instance answers for less. The exports, the adapter list and the node proxy are closed to strangers, and the welcome dialog can be dismissed.
  • The statistics endpoint has its own rate limit, like the export that reads the same rows.
  • Release inputs never reach a shell as code, anywhere in the publishing chain.

🐛 Fixes

  • Nodes — removing a stored node password works, instead of reporting success while the old one was kept and reused. A revalidated request is no longer reported as "the node redirected the request". A password prompt that cannot reach the node says so.
  • Backups — restoring a history no longer silently discards the rows whose ids clash with tests recorded since, and the result says how many were imported and how many skipped.
  • Network interfaces — an adapter that disappears for one refresh, as a VPN restart does, no longer repoints every later measurement onto the wrong link. Detection is also faster.
  • Integrations — a Discord display name over 80 characters no longer blocks every notification.
  • In the browser — the dashboard works where localStorage is blocked: Incognito, third-party cookies off, and the embedded frames it is meant to sit in on Homepage or Heimdall. Signing in and out can no longer hang with nothing on screen.
  • The statistics page — the summary card takes its own row from 1250px. Opened, it uses the width of the screen instead of a fixed column, wraps long descriptions instead of cutting them, and keeps every value on its own line. Row labels are no longer cut in any of the fifteen languages. A failed test's reason gets a second line on a phone.
  • Installing — a failed or interrupted download no longer overwrites a working install and reports success. Published x86-64 binaries are started and asked to serve a page before they are uploaded.

Downloads

Platform x86-64 ARM64
Windows EXE · MSI · EXE (no AVX2) · MSI (no AVX2)
Linux Binary · No AVX2 Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.3.4

What's Changed

Full Changelog: v1.3.3...v1.3.4

Release 1.3.3

Choose a tag to compare

@github-actions github-actions released this 17 Aug 03:40

Changes since 1.3.2

MySpeed now runs on two things it could not before: older x86-64 CPUs without AVX2, and the Cloudflare provider inside the Docker image. Upgrading is drop-in — no migrations, no configuration changes.

New

  • Builds for CPUs without AVX2. Older x86-64 machines crashed at startup with Illegal instruction, or — under the MSI — installed cleanly and left a service that never started. There is now a baseline download beside each default one: MySpeed-linux-x64-baseline, MySpeed-windows-x64-baseline.exe and MySpeed-installer-baseline.msi. On Linux, install.sh picks the right one for you; on Windows, go by the symptom, or check first with [System.Runtime.Intrinsics.X86.Avx2]::IsSupported in PowerShell 7. The two installers are one product, so running the other one switches the build and keeps your database.
  • The Cloudflare provider works in Docker. Its CLI could not start on the Alpine-based image, so every scheduled test on that provider failed with a spawn error. The image now ships a CLI built for it — and a provider that cannot start says why in the test result instead.

Fixed

  • Speeds and dates print as themselves. A slash came out as an escape code, so the stability card read Between 19.66 MB&#x2F;s and 296.93 MB&#x2F;s. Dates were hit the same way wherever the language writes them as 8/15/2026, as was any server or node name containing an "&".
  • The overview rows are no longer taller than they need to be. Each row's time was prefixed with a translated "At" or "On" — one short word in English, but "A las", "W dniu" or "Zamanında" elsewhere, which wrapped the cell onto a second line and set the height of every row in the list. The word is gone; the time and the clock icon said it already, and it was mistranslated in five languages besides. The date column had been sized for a 24-hour clock while it was there, so it also holds a 12-hour one now — "10:44 AM" used to wrap where "10:44" fitted.
  • The toolbar and the statistics page reflow properly on narrow screens. The toolbar measures its own row instead of guessing from the viewport, so it holds one line down to 420px even with a custom date range selected. The statistics page gained a two-cards-a-row step between three and one, and its cards no longer cut their labels or overhang the page.
  • Nothing runs out of its column. The overview's ping figures no longer draw over the grade badge, the detail facts pane keeps two columns on much narrower windows, and a failed test's message stays inside its card.

Thanks

Both platform fixes in this release came from @underw8 — the AVX2 baseline build (#13) and the Docker Cloudflare fix (#14). Thank you!


Downloads

Platform x86-64 ARM64
Windows EXE · MSI · EXE (no AVX2) · MSI (no AVX2)
Linux Binary · No AVX2 Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.3.3

What's Changed

  • feat: ship linux x64 baseline binary for non-AVX2 CPUs by @underw8 in #13
  • fix(docker): ship a musl-built Cloudflare CLI so the provider runs on Alpine by @underw8 in #14
  • feat: ship a windows-x64-baseline binary and MSI for non-AVX2 CPUs by @i7Gamer in #16

Full Changelog: v1.3.2...v1.3.3

Release 1.3.2

Choose a tag to compare

@github-actions github-actions released this 15 Aug 10:53

A maintenance release built around two whole-project reviews. Every fix ships with a test that fails without it — the suite grew from 2313 to 2998 checks, and ESLint now runs across the repo. Upgrading is drop-in: no migrations, no configuration changes.

✨ New

  • Deleting a test asks first. The button sits in the panel that opens under the row you were reading, there is no undo, and it used to delete on the click.
  • The whole interface answers a keyboard. Nine cards on the statistics page and every node card were click-only, so the expanded charts and the node switcher could not be reached without a mouse.
  • A grade is stated on the glyph, consistently across the overview, node cards, detail pane and statistics panels — with a new preference to colour the figure as well.
  • Dates and numbers follow the app's language, not the browser's.

🔒 Security

  • Preview mode is genuinely read-only. Clearing the history, factory resetting, deleting a test, changing any setting and pausing the schedule were all reachable without a password on a demo instance. Worst was the node proxy: it reaches a different machine and substitutes that machine's stored password. One shared guard now covers every mutating route, and a test walks the route table so a new one cannot miss it. Running a test still works.
  • The failed-password limit counts simultaneous attempts, not just sequential ones, across the password header, the sign-in route and the Prometheus scrape. A server that is merely busy comparing now says so, instead of reporting a correct password as wrong.

🐛 Fixes

Statistics — the failure count in the status bar and on the statistics page were computed by two different rules; an unrecorded test duration dragged a chart bucket's average down; the "showing N of M" note could claim a reduced view of a chart that was drawn in full; the bufferbloat trend could be drawn out of order; the expanded overview printed latency to two decimals where every other panel printed one; the two target bars ignored the grade palette.

Speedtests — a provider CLI printing an empty JSON array crashed the server outright rather than recording a failed test; a CLI download that extracted nothing reported success; a failure that could not be recorded left the instance marked "running" and silenced the integration keep-alive.

Integrations — Pushover rejects messages over 1024 characters, so the failures with the most to say never arrived; a stray backtick in a provider's error broke Discord's formatting, and a link in provider output became a real one; outbound responses are now read to completion instead of left open.

Nodes & configuration — deleting the node you were viewing left the app pointed at it; the proxy asked children for compressed replies it could not decode; a non-string password answered 500 instead of 400; a configuration change told the integrations and revoked every session before the write it described; the storage dialog could report a database size of zero; the OpenGraph image left out the current day east of UTC.


Downloads

Platform x86-64 ARM64
Windows EXE · MSI
Linux Binary Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.3.2

Release 1.3.1

Choose a tag to compare

@github-actions github-actions released this 14 Aug 15:36

Changes since 1.3.0

New

  • A password can be reset from the command line. An instance whose password is set but no longer known had no way back in — the setup token only applies while no password is set, so every route was refused by the very credential that was lost. MySpeed --reset-password clears it and hands the instance back to the first-run setup flow; under Docker, docker exec <container> bun server/index.js --reset-password.
  • A new password has to be typed twice. The server hashes what it is given and never sees it again, so a typo caught afterwards is already the password. A mismatch is shown under the box as it is typed, not only once Save is pressed.
  • The stability card grades its ping deviation, and a deviation smaller than the millisecond it prints says so rather than claiming a line that never wavered.
  • A failed speedtest reaches Prometheus as a metric. The scrape used to answer 500 when the newest test had failed, which Prometheus reads as the exporter being down — so every myspeed_* series went stale and the alert blamed the wrong thing. myspeed_test_failed carries it now.
  • The header and toolbar hold together on a narrow screen. The start button drops to its gauge where the row runs out, the export keeps its label where there is room for it, and the opened test's facts are gathered into one row of the details grid.
  • The navigation and the integration menu answer the keyboard. Both were plain divs that Tab walked past — and on a healthy instance the pagination was the only route to the statistics, as that menu is the only route to adding an integration.

Fixed

  • A read-only visitor is no longer told the schedule. A shared dashboard withholds the cron and the quiet hours, but the status endpoint handed back the next test time drawn from them — one poll recovered the schedule, and polling across an evening recovered the quiet window.
  • An imported history is held to being numbers. Four of the ten measurement columns went unchecked, so a text value survived the write and returned the whole range's jitter average as nothing at all.
  • An interrupted server-list download no longer outlives itself. A container restarted mid-write left a file that could not be read, and the unreadable bytes were then handed out as the server list until the process was restarted.
  • One Escape closes one overlay. A metric's help alert opened over an expanded chart took the chart down with it.
  • The statistics show the range that was asked for. Choosing a wide range and then a narrow one let the slow query write its numbers under the heading of the range actually chosen, and an abandoned request that timed out replaced a page that had rendered correctly with an error.
  • The app no longer holds on its loading screen after an upgrade.
  • The date range popover opens where it sits rather than a third of the way off the right edge of a phone.
  • Also: a measurement that was never taken is no longer counted as a zero; Gotify posts to one slash however the URL was pasted; integration credentials are checked against the whole value rather than a fragment of it; and uninstall.sh removes the installation that is actually there, and says so when it cannot.

Under the hood

  • More tests, and a single shared answer to "did this test fail" in place of four that disagreed.

Downloads

Platform x86-64 ARM64
Windows EXE · MSI
Linux Binary Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.3.1

What's Changed

  • chore(deps): Bump the server-minor-and-patch group with 2 updates by @dependabot[bot] in #11
  • chore(deps): Bump the client-minor-and-patch group in /client with 2 updates by @dependabot[bot] in #12

Full Changelog: v1.3.0...v1.3.1

Release 1.3.0

Choose a tag to compare

@github-actions github-actions released this 13 Aug 17:17

Changes since 1.2.1

New

  • Quiet hours. A daily window — say 23:00 to 08:00 — during which scheduled tests are skipped, set from the pause dialog and honoured everywhere a test is promised: the scheduler refuses occurrences inside it, the status bar counts down to the first one outside it, and the frequency dialog's "next test" preview says the same thing the countdown does. Manual tests still run; the window governs only the schedule. A publicly shared read-only dashboard is not told the window's bounds — they say when nobody is home.
  • Notifications only when a test misses a limit. Each notifier integration can set its own thresholds — ping above, download or upload below — and stay silent for every test that meets them. A test that could not be measured counts as worth telling: an unmeasurable line is not evidence that the line is well, including the all-zero result Cloudflare's CLI produces when it prints nothing usable.
  • Integrations receive everything the test recorded, and the dialog now lists every variable a message template accepts, with a live preview of the payload the integration will get. Template values are written verbatim, once each — a value containing a variable's name no longer expanded twice.
  • Every test records where its server is, not only who runs it. Ookla keeps the sponsor and the city apart, and now so does the history: the opened test names the server, then where it is, then the address that answered — each line carrying only what the one above it does not.
  • The overview row says what the line is like, not just how fast it is. Bufferbloat is graded A+ to F right on the row, packet loss and jitter sit beside the ping with the same colour grading the opened panel uses, and the latency under load has its own place on each direction's card. Every icon is a real button that explains its own figure — by mouse or by keyboard.
  • The consistency card reports the spread a typical test sees. The ping figure was a standard deviation labelled a variance: one 26 ms spike among 170 four-to-seven-millisecond pings nearly doubled it. It is now the median absolute deviation — half the tests sit within it of the middle one — labelled "Typical deviation", and no single test speaks for the range.
  • Latencies keep their decimals. The column stores two; every view prints one — the same one, everywhere, with the colour graded from the printed figure, so a reading never wears one value and a different colour's worth of another.
  • Ukrainian translation.

Fixed

  • The statistics time axis fills the plot again. A lone test was drawn on an axis spanning six years; a year of history was squeezed into half the plot between epoch-anchored ticks with months of dead margin either side. The axis now spans the data.
  • Saving the quiet window could half-apply it. The two ends were written in separate requests with no rollback, so a failure between them left the server silencing tests across a band nobody configured — while the dialog went on showing the pair the operator meant. A failed save now restores the first end, and the dialog re-reads and shows what the server actually holds.
  • A reschedule landing at the wrong moment no longer fires one last test from the schedule it replaced — the quiet-hours check had reopened, for the length of two config reads, the exact race the schedule generation counter exists to close.
  • One Escape, one overlay. Escaping the remove-password confirmation also closed the settings dialog under it, discarding the password typed there — both overlay systems listened on the document and neither knew the other was stacked above. The topmost overlay now answers alone.
  • The opened test names its server once. With a location but no name, the city was printed on both server lines and the address that actually answered on neither; an empty Ookla server name is also stored as the null it means, like the location beside it always was.
  • Delivery failures are recorded while an integration is quiet, a payload size the speedtest skipped is ignored rather than parsed, and the announced next test is tied to the quiet hours that govern it.
  • Failed tests sit level with the rows around them — they had kept the old height floor while measurement rows grew, so the list jogged as it scrolled past one.
  • Also: the integrations dialog grows into the room it already has; the header sits centred in its own band; the button that deletes the password says so, and its prompt can be left without taking the settings dialog along; the dev-mode page works under the app's own security policy; the floating date leaves when its list does; the Ukrainian optimal-speed labels no longer name each other's direction; and the frequency dialog no longer re-walks its cron preview on every status poll while closed.

Under the hood

  • Chart labels are parsed once per recompute instead of once per dataset

Downloads

Platform x86-64 ARM64
Windows EXE · MSI
Linux Binary Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.3.0

Release 1.2.1

Choose a tag to compare

@github-actions github-actions released this 12 Aug 15:22

A bug-fix release. No migrations, no configuration changes — upgrade in place.

Scheduled tests

  • A provider CLI that ignores SIGTERM no longer wedges the scheduler. The timeout escalates to SIGKILL; before this, no scheduled test ran again for the life of the process.
  • Changing the schedule no longer leaves one last test to fire from the schedule it replaced.
  • A run that fails immediately reports its own reason instead of a three-minute timeout.

Export, backup and restore

  • Exports download on Firefox and Safari. The blob URL was revoked too early, and only Chrome survived it.
  • A backup with one malformed row skips that row instead of rolling back every good row already written.
  • CSV exports keep ping, download and upload numeric. The formula-injection guard was turning the -1 failure placeholder into the text '-1.
  • Statistics survive a test whose stored timestamp cannot be parsed. The row still counts and still averages; only the instant it claims is set aside.

Integrations

  • ntfy notifications with a newline, an emoji or non-Latin text in the title or tags are delivered, rather than failing silently.
  • InfluxDB tag values containing "=" are stored whole instead of truncated at the second one.
  • A health check against a URL with a trailing slash no longer 404s while reporting the integration as working.
  • An integration removed in an upgrade no longer crashes the integrations dialog, so the leftover row can be deleted from the interface.

Interface

  • Ping, jitter and packet loss now sit together on the ping card in the test details pane, rather than in one card and two rows of the facts grid. Jitter is no longer drawn twice. No locale gains a string.
  • The opened detail panel is capped at the page's own width; at the widest tier it stood 840px wider than the overview's own view of the same record.
  • A read-access instance with a remote node selected keeps its configuration across the redirect to /nodes, which was leaving it with no header and no node list.
  • Toasts raised while the previous one is animating out appear. Two in one tick no longer close the first early.
  • The header identifies the local instance correctly, and a partial configuration PATCH is accepted.

Install and uninstall

  • uninstall.sh --keep-data keeps the data. A leftover from an interrupted uninstall made the restore put the database back one level too deep to be found.
  • All four scripts exit non-zero when they refuse a non-root run, instead of printing "ABORTED" and reporting success to the pipeline.

Build, startup and resources

  • A filename containing a quote no longer produces a generated registry that fails to parse. The build succeeded and the server then died on startup with a SyntaxError.
  • Binary builds serve assets whose names contain a space or a non-ASCII character, instead of answering an image request with index.html.
  • Provider CLI downloads clean up their temporary archives, however the attempt ended.
  • The startup server-list download has its own timeout rather than one sized for a webhook body, and recognises a rate-limited 200 carrying an error object.
  • A factory reset completes when the configuration holds a key from an older version, including the session revocation it was skipping.

Downloads

Platform x86-64 ARM64
Windows EXE · MSI
Linux Binary Binary
macOS Binary Binary

Source Distribution

Docker Images

  • i7gamer/myspeed:latest
  • i7gamer/myspeed:1.2.1