Release 1.1.1
⚠️ Read before upgrading
Two changes are not invisible:
- An instance with no password no longer answers the network. Requests from other machines are refused until you set a password or present the one-time setup token printed in the server log at startup. Local requests are unaffected, so a LAN-only install keeps working. Set
ALLOW_NO_PASSWORD=trueto keep the old behaviour deliberately. - You will be signed out. The browser now holds a session cookie instead of your password. Your existing login carries over once; after that, a server restart means signing in again.
Docker upgrades stay a plain docker pull — the container takes ownership of an existing data volume on first start, because the server now runs as an unprivileged user. Database, exported configs, integrations and nodes carry over untouched. No migration step.
🔐 Security
- A MySpeed instance with no password set no longer serves the full admin API to anyone who can reach it. A one-time setup token printed at boot covers the first run, and new
TRUST_PROXY,FRAME_ANCESTORS,HTTPS_REDIRECTandALLOW_LOCAL_NODESsettings arrive alongside per-client rate limits, security headers and request-size caps. - Signing in now exchanges your password for an
HttpOnly,SameSite=Strictsession cookie instead of keeping the password itself in browser storage. Sessions are revoked server-side when the password changes. Thex-passwordheader still works for scripts and integrations. - Setting your password to
noneis rejected instead of quietly leaving the instance open to anyone. - Wrong-password attempts no longer stall the whole server — including
/api/health— while bcrypt runs, and repeated failures from one client are throttled. - Unexpected server errors return a plain JSON 500 instead of an HTML page with a stack trace, including on endpoints reachable without a password.
- The OpenGraph preview image reads its assets from disk or the embedded client rather than from a URL built out of the caller's
Hostheader, closing a way for a visitor to make your server issue requests to a host of their choosing. - Stored node URLs are re-checked for loopback, link-local and cloud-metadata addresses on every proxied request rather than only when the node was added, and a node answering with a redirect is refused rather than followed.
- New
ALLOWED_NODE_HOSTSrestricts which hosts a remote node may point at — a comma-separated list ofhost[:port]entries. Unset keeps existing behaviour. - The config export sends
Cache-Control: no-store, so a browser cache or shared proxy cannot retain a copy. - A URL ending in a long run of slashes no longer stalls the server.
🐞 Fixes
Speedtests
- "Pause indefinitely" actually stops the scheduler instead of silently reporting success, and resuming no longer leaves an old timer that un-pauses a later pause partway through.
- Fractional custom pauses (half an hour, and so on) are applied instead of being refused by the server while the dialog reported them saved.
- A pause longer than about 596 hours holds until you lift it, rather than silently resuming on the next tick.
- A speedtest that cannot start is recorded as a failed test with a readable reason, and the run lock is released so scheduled tests keep running instead of the instance appearing permanently "running".
- A CLI that connects and then stalls is killed after three minutes instead of holding the run lock for the life of the server.
- A test that fails and retries sends one "started" notification to integrations instead of two.
Data
- Retention actually deletes tests older than the configured window on the default SQLite backend.
- The CSV export keeps the
errorcolumn and uses RFC 4180 quote-doubling, so spreadsheets read it back correctly. - Importing a history whose rows are all unusable reports an error instead of "Tests imported", and rows with non-numeric measurements are skipped rather than written.
- A config import that is truncated or missing a section leaves your nodes, integrations and recommendations untouched instead of deleting them, and a config export imports back cleanly.
- A full config backup now includes and restores the admin password hash, so a restored instance is no longer left unprotected.
- Exporting CSV while a child node is selected downloads the real file instead of one containing the word
null.
Integrations
- The custom failure message configured for Discord, Gotify and Telegram is actually sent instead of always falling back to the built-in default.
- Changing one setting on an integration no longer wipes the others, and a failed write no longer reports success.
- Telegram failure notifications containing a stray backtick, asterisk or bracket are delivered instead of being rejected.
- InfluxDB tag values containing a backslash, and measurement names containing a space or comma, are written as valid line protocol.
- Webhook and push calls abort after 10 seconds instead of hanging the speedtest run, and failures are logged with the host and reason.
- A malformed Basic auth header on
/api/prometheus/metricsreturns 401 rather than a 500, and an admin password containing a colon can finally authenticate.
Interface
- A brand new instance no longer shows the red danger colour next to "N/A".
- Deleting a speedtest, renaming or deleting a node, and saving provider settings report an error when the server refuses instead of showing a success toast.
- A failed storage request no longer blanks the entire app, and the statistics page shows an error with a retry button instead of going blank.
- Dates and times follow the language chosen in the app rather than the browser's.
- Result details no longer label an MB/s figure as Mbps.
- The consistency score stays within 0–100% instead of showing readings like "-240%".
- On an instance restored from a backup, the overview list no longer duplicates and grows on every refresh.
- Danish: the average-speed sentence shows the download figure instead of a literal
{{down}placeholder. - After upgrading, the browser picks up the new UI on reload instead of serving the previous build until every tab is closed.
Server & Docker
- Oversized or undecodable request bodies return the correct status instead of a 500 HTML page.
- A failing retention sweep, startup speedtest or recommendation refresh logs its reason instead of terminating the server.
- Adding a node or changing a node password no longer hangs until the client times out when the database write fails.
- The Docker image runs as the unprivileged
bunuser, no longer returns stack traces to clients, and its healthcheck followsSERVER_PORT. - A server that cannot create its data folders exits with a failure code instead of reporting a clean stop, and a database that will not open points at directory permissions rather than blaming file damage.
✨ Improvements
- Clicking a speedtest expands the row in place — per-metric cards, target comparison, change since the last test, jitter, server, and raw CLI output for failures — instead of opening a one-sentence dialog.
- An expanded chart can be re-fetched at up to 1000 points, so short dips that the 300-bucket averaging hid become visible. The choice is remembered.
- The expanded chart uses the space on large displays instead of staying capped at 1400px.
- The settings export redacts credentials by default, with a new toggle for the full export that restores an instance verbatim.
- A database index on the speedtests timestamp stops statistics, the paginated list, exports and the retention sweep from full-scanning the table — noticeable on a year or more of history.
- The OpenGraph preview image is rendered at most once a minute and shared between concurrent callers.
- Updated runtime dependencies (MySQL driver, cron parser, OpenGraph renderer) and frontend packages (React, i18next, react-router, icons, PWA).
Downloads
| Platform | x86-64 | ARM64 |
|---|---|---|
| Windows | EXE · MSI | |
| Linux | Binary | Binary |
| macOS | Binary | Binary |
Source Distribution
- ZIP Archive (requires Bun runtime)
Docker Images
i7gamer/myspeed:latesti7gamer/myspeed:1.1.1
What's Changed
- ci: move every action onto the Node 24 runtime by @i7Gamer in #4
- chore(ci): bump actions/download-artifact from 7 to 8 by @dependabot[bot] in #5
- chore(ci): bump actions/upload-artifact from 6 to 7 by @dependabot[bot] in #6
- chore(deps): bump the server-minor-and-patch group with 4 updates by @dependabot[bot] in #7
- chore(ci): bump actions/github-script from 8 to 9 by @dependabot[bot] in #8
- chore(ci): bump actions/checkout from 5 to 7 by @dependabot[bot] in #9
- chore(deps): bump the client-minor-and-patch group in /client with 18 updates by @dependabot[bot] in #10
Full Changelog: v1.1.0...v1.1.1