Skip to content

Changelog ​

All notable changes to SkySend are documented here.

v3.0.0 - File and Note Requests, Notes Made of Blocks, New Themes and Security Hardening ​

Released: October 8, 2026

⚠️ Breaking: A CLI from before v3 can neither open notes created with v3 nor download password-protected files from a v3 server. Run skysend update together with the server update.

⚠️ Breaking: DEFAULT_THEME now selects the visual theme (graphite, aurora or midnight) and the color scheme moved to the new DEFAULT_COLOR_SCHEME. An instance that still sets DEFAULT_THEME to dark, light or system refuses to start and names the value to set in DEFAULT_COLOR_SCHEME instead.

💙 Thank you to everyone who reported bugs, reviewed the security of SkySend and shared ideas for this release. SkySend stays free and open source, and if it saves you time, you can now support it through GitHub Sponsors.

✨ Features ​

  • server: /api/config reports noteBlocks, so a client can tell whether the server accepts notes made of blocks.
  • server: DEFAULT_TAB accepts note, and text, password, code or sshkey open the note tab with that block already added.
  • server: DEFAULT_THEME picks the theme of the web app and the new DEFAULT_COLOR_SCHEME sets the color scheme for new visitors.
  • server: File requests with an inbox, where senders upload files that only the requester can decrypt. The requester sets how long a request stays open, how many uploads it takes and how large each may be.
  • server: FILE_REQUEST_UPLOAD_OPTIONS and FILE_REQUEST_DOWNLOAD_OPTIONS list how many uploads a request takes and how often each can be downloaded, with FILE_REQUEST_DEFAULT_UPLOADS and FILE_REQUEST_DEFAULT_DOWNLOADS as the defaults. The requester picks both per request.
  • server: FORCE_REQUEST_PASSWORD requires a password on the inbox of every file request, apart from FORCE_FILE_PASSWORD.
  • server: OIDC_PROTECT_REQUESTS puts creating a file request behind the OIDC login, apart from files and notes. Unset, it protects requests while OIDC_PROTECT_FILES or OIDC_PROTECT_NOTES is on.
  • server: GET /api/request/limit tells the caller how many new requests are left today, without counting one.
  • web: A note is made of blocks, so text, passwords, code and SSH keys can be shared together in any order. The note tab starts with a card for each block type, and blocks can be added, moved and removed.
  • web: Three visual themes, Graphite, Aurora and Midnight, each in a light and a dark color scheme. Graphite is the default.
  • web: A new How it works page explains the encryption step by step and lets visitors encrypt a sample text in their own browser.
  • web: A Request page to create file requests, with its settings as a row of pills, an upload link to hand out and an inbox link to keep, plus the inbox that lists, downloads and deletes what arrived.
  • web: An upload page for senders of a file request, which encrypts their files for the requester alone and gives them no link back.
  • web: A file request can ask for files, a note with a template of fields the sender fills in, or both as one submission. The inbox opens a note in the page and lists a submission together.
  • web: A dot beside My Links and a count on each request show uploads that arrived since its inbox was last open in this browser.
  • web: A settings page behind the gear in the header keeps defaults for sharing files and notes and for requests in this browser, like how long they stay open and whether a password starts on. Values the instance no longer offers fall back to its own, and an enforced password always wins.
  • web: Templates keep the setup of a request for the next one, with built-in ones for credentials, an SSH key, Wi-Fi and an API key. They live in the settings of this browser and move to another one as a file or a link, sealed with a password if wanted.
  • web: A field of a password block can be marked as no secret, like a username or an address, and is then shown in clear without a generator. A block with such a field is called Fields instead of Password.
  • web: Text, password and SSH key blocks take a title of their own, like Server access, in a shared note and in a request template. It heads the block for whoever fills it in or reads it.
  • web: The request form shows how many new requests are left today and offers none once the daily limit is used up.
  • client: The CLI client opens notes made of blocks and creates them on servers from v3 on. On an older server it still creates notes in the format that server knows.
  • client: Fields of a password block that are no secret show in clear in the terminal view of a note, under the title of their block.
  • cli: list, delete, stats, cleanup and config cover file requests and the files uploaded into them.
  • crypto: Encryption for file requests that only the requester can open, notes sent into them included.
  • website: The report form accepts links to file requests.
  • website: The home page has a new design in the Graphite look of the app, with a window in the hero that shows a share next to what the server stores of it. The public instances list their status and limits, like the largest file and how long a share is kept.
  • website: The blog has a new design with an illustration and a color for every post, a filter by tag, a table of contents, the GitHub avatar of the author and an RSS feed. A new post introduces file and note requests.
  • website: The roadmap runs releases and plans along one line, with the latest release, the work in progress and the next star goal on top.
  • website: The report page leads through the report step by step, picks the instance from the pasted link and refuses inbox links before sending.
  • website: The website is in German as well as English, follows the language of the browser like the app and offers Auto, English and Deutsch to choose from. Blog posts show in German where a translation exists.
  • infra: The report worker accepts links to file requests.

🐛 Bug Fixes ​

  • server: A WebSocket upload keeps its file when recording the upload quota fails.
  • server: Expiry options longer than 100 years are refused at startup instead of failing every upload.
  • web: Messages about generating an SSH key are translated instead of always shown in English.
  • web: A code note whose language name is crafted no longer breaks the note view.
  • web: Text on a light CUSTOM_COLOR was unreadable because it was always white. It now turns white or black by itself, and the accent is darkened or lightened wherever it is used as text.
  • web: The default color scheme of the server is no longer stored in the browser on the first visit, so a later change of the default reaches returning visitors.
  • web: The color scheme menu shows the translated name of the system option instead of a fixed English "Auto".
  • web: Dialogs, menus and tooltips animate again when they open and close.
  • web: The password generator no longer makes passwords shorter than 8 characters when a smaller length is typed into its number field.
  • web: The Markdown switch of text notes is translated.
  • web: Error messages under the upload and note forms are readable in the light color scheme. They were nearly white before.
  • web: With a password forced by the server, the password field no longer disappears after starting a new upload or note, which made the next share fail.
  • web: The QR code dialog and the button that shows the password of a protected download are translated.
  • web: The share button of a generated SSH key is disabled while no part of the key is selected. It did nothing before.
  • web: Long file names in the list of an upload are shortened on a phone, so the button to remove a file stays in view.
  • web: The download speed no longer shows a negative value after a download falls back to another way of saving the file.
  • client: A note with unlimited views shows its view count correctly in the terminal.
  • client: The macOS binaries are now signed, so macOS on Apple Silicon no longer stops them as soon as they start. A macOS installation that does not start has to run the install script once more, since skysend update cannot run either.
  • client: A failed download in the CLI or the TUI no longer deletes a file that was already at the output path. Thanks @NotAFlightRisk (#81)

🔒 Security ​

  • server: The metadata of a password-protected file is released only after a correct password, so a link holder can no longer test password guesses offline. Thanks @NotAFlightRisk (GHSA-rxxj-c5wr-phqp)
  • server: A wrong token at the download of a password-protected file counts against the password lockout like a wrong password. Thanks @NotAFlightRisk (GHSA-rxxj-c5wr-phqp)
  • server: A failed OIDC sign-in and other server errors are logged without the claims of an ID token or other values attached to the error, so the log no longer holds data that identifies a user.
  • server: An IPv6 client counts per /64 network in the rate limit, the upload quota, the password lockout and the daily request limit, so a new address from the same network no longer starts a fresh count.
  • server: Updated @hono/node-server to 2.1.3 to patch a middleware bypass in static file serving (GHSA-rmxm-3fg6-px4f).
  • server: An upload reserves its size in the upload quota when it starts, so uploads running side by side can no longer store more than the quota allows.
  • server: The single-request upload requires the OIDC login when OIDC_PROTECT_FILES is on, like every other way to upload a file.
  • server: The single-request upload stops at the size it declared, so a client can no longer fill the disk with a body that never ends.
  • server: A chunked HTTP upload that receives less than 1 MiB in 10 minutes is ended like a WebSocket upload, so a stalled upload no longer holds its share of the quota or a slot of a request.
  • server: Chunked uploads refuse empty chunks and keep at most 64 chunks waiting for an earlier one.
  • server: A WebSocket upload whose first frame is null no longer crashes the server.
  • server: WebSocket uploads closed during setup or finalize leave no orphaned file behind, and uploads that deliver less than 1 MiB in 10 minutes are closed.
  • server: A WebSocket upload that stops short of 4 MB no longer keeps its data in memory.
  • server: WebSocket uploads no longer send storage error details to the client.
  • web: A crafted SSH key or code note can no longer freeze the browser that opens it.
  • web: Markdown notes no longer load images, so a note cannot make its reader's browser send requests to the instance.
  • web: A new file or note password needs at least 8 characters. Thanks @NotAFlightRisk (GHSA-rxxj-c5wr-phqp)
  • web: Notes are padded to whole kilobytes before they are encrypted, so the server no longer learns how long a password in a note is.
  • web: Updated dompurify to 3.4.16 to fix two XSS issues in in-place sanitizing (GHSA-p98j-92pf-mc4p, GHSA-6688-9rhm-gjv2).
  • web: Downloads through the service worker tell the browser not to guess the file type.
  • client: A crafted SSH key note can no longer freeze the terminal that opens it.
  • client: Escape sequences and other control characters in a note are shown as placeholders in the terminal instead of being run.
  • client: A note saved from the terminal can only be read by its owner.
  • client: The CLI and the TUI ask for at least 8 characters for a new file or note password and take the metadata from the password check. Thanks @NotAFlightRisk (GHSA-rxxj-c5wr-phqp)
  • client: The CLI and the TUI pad notes to whole kilobytes like the web app.
  • client: The CLI keeps its history, config and session files readable for the current user only, and narrows the permissions of files an older version created. Thanks @lissy93 (GHSA-5vjq-2637-p33f)
  • client: Password prompts of the CLI no longer show what is typed, and a pasted password no longer keeps its line break.
  • client: skysend note without text asks for the note at a prompt that does not show it, or reads it from a pipe, so a secret no longer has to go into the shell history.
  • client: The CLI and the TUI save a download under a cleaned file name inside the chosen folder and add a number instead of replacing an existing file, so a crafted name can no longer write elsewhere on disk. Thanks @lissy93 (GHSA-2gh8-5c87-87jx)
  • client: File names, error messages and the server title are shown with their control characters made visible, so a sender or a server can no longer rewrite terminal output or the clipboard through escape sequences.
  • client: The TUI packs a multi-file upload into a temporary file only the current user can read and removes it even when the TUI is closed with Ctrl+C.
  • cli: list --json no longer prints the auth and owner tokens of uploads.
  • cli: config --json masks the S3 keys and the OIDC secrets.
  • crypto: File metadata is padded to whole kilobytes before it is encrypted, so its length no longer reveals how long a file name is.
  • docs: Updated vue to 3.5.43 to patch an XSS in server-side rendering of attribute names (GHSA-g2v6-rqmx-r4w6).
  • website: Updated next and eslint-config-next to 16.4.0 to patch a remote code execution in next/og image responses (GHSA-vcvr-r3jv-pc5j).
  • infra: Updated wrangler to 4.148.0 and refreshed the lockfile, so undici in the local worker runtime and source-map-js in the build tooling resolve to patched versions (GHSA-rfgv-xxqx-mfg5, GHSA-w293-vg96-wgc3, GHSA-3wwx-pv8p-q78v, GHSA-pmjh-fq2x-6v4x, GHSA-3xpg-4rpp-hhhm, GHSA-2jfj-6hjv-fm6j, GHSA-rx4f-c7p8-82vq, GHSA-r53p-7pc4-xj5r, GHSA-2gqq-gqf2-x968, GHSA-8436-99hf-9mmv, GHSA-68fv-2mgg-jv7q).
  • infra: Raised the pnpm overrides for sharp and brace-expansion and added overrides for postcss-selector-parser under @tailwindcss/typography and argparse under js-yaml to clear transitive advisories (GHSA-wq5f-xc86-pv6w, GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p, GHSA-rj75-hqrm-r3gf, GHSA-hp3w-g68c-fv3c).
  • infra: The report Worker only accepts https links whose last part is a file, a note or a request, and refuses any link with an inbox in its path.

🎨 Improvements ​

  • web: Notifications match the design of the app and appear below the header instead of covering it.
  • web: Every part of a received note is shown in a frame of its own with a copy button, and code blocks can be folded one by one.
  • web: A note that cannot be read in its format is shown as it arrived instead of failing, so a note that deleted itself on opening is not lost.
  • web: The interface uses the Geist font, served by the instance itself without a request to a font service.
  • web: A redesigned interface with a floating navigation bar, a new share form and new pages for receiving files and notes.
  • web: The settings of a share are a row of pills that open their choices on a click. A sentence next to the share button says when the share will be deleted.
  • web: Expired, used up and unknown links show one page with an explanation and a way back to sharing.
  • web: My Links keeps the copy button at hand and moves opening, renaming, the QR code and deleting into a menu.
  • web: Icon buttons show tooltips and have labels for screen readers, and the code blocks of a note open and close with the keyboard.
  • web: The German message for a file that cannot be read uses the formal address.
  • web: The technical info of an upload or a download sums up what was used in one row, and opens to tiles and a timeline that match the new look.
  • web: The technical info also shows after an upload that failed, with the reason in its timeline, and on the upload page of a file request.
  • client: The terminal reveals all passwords of a note at once with a.

🔄 Changed ​

  • server: Notes created since v3 are stored with the content type blocks, so the server no longer learns whether a note holds text, a password, code or an SSH key.
  • server: Updated better-sqlite3 to 13, which ships its prebuilt binaries in the package, so an install no longer downloads or compiles one. Outside Docker the server now needs Linux, macOS or Windows on x64 or arm64.
  • server: ENABLED_SERVICES now defaults to file,note,request. Instances that set it explicitly add request to offer file requests.
  • server: Failed password attempts that led to no lockout are forgotten after PASSWORD_LOCKOUT_MS.
  • web: The share form has two tabs, File and Note, instead of one tab per note type.
  • web: My Uploads is now My Links, with one tab for what was shared and one for the file requests made in this browser.

📝 Documentation ​

  • docs: The toast system page describes the new look and position of notifications.
  • docs: The config endpoint shows its current response, and the notes API documents the blocks content type and the 32-byte salt.
  • docs: A new page describes the note format and when support for notes from before v3 is removed. The guides and the architecture page describe notes made of blocks, and note:view no longer lists options it does not have.
  • docs: Documented DEFAULT_THEME and DEFAULT_COLOR_SCHEME in both environment variable references.
  • docs: Described the three themes and how CUSTOM_COLOR applies to them, and added the steps to upgrade from v2.
  • docs: First Steps matches the new interface and points to the How it works page.
  • docs: The threat model covers an instance that serves modified code, and the instance list says what that means for trusting an operator.
  • docs: The development setup covers the macOS setup script, the Node version and the platforms the server runs on outside Docker.
  • docs: Documented the environment variables of file requests.
  • docs: The download modes page covers downloads from the inbox of a file request.
  • docs: New pages for file requests in the user guide, the API reference and the cryptography section, request templates included.
  • docs: A new page in the user guide describes the settings of a browser.
  • docs: The rate limiting page covers the daily limit of file requests, with a recommendation for public instances.
  • docs: The admin CLI commands page matches what the commands print.
  • docs: The CLI guide explains why macOS binaries up to v2.12.2 do not start on Apple Silicon and how to install one that does.
  • docs: The README is shorter and opens with a banner of the new interface on a desktop and a phone instead of a screenshot of the old one.
  • docs: A security policy explains how to report a vulnerability privately, and the contributing guide and the developer guide explain that pull requests go into the dev branch.
  • docs: The README names file and note requests among the highlights and says how independent researchers have reviewed SkySend so far, with a link to the published security advisories.
  • website: The zero-knowledge section and the blog post promise that the server holds nothing readable, not that an operator can never read along.

🧪 Tests ​

  • server: Tests for notes made of blocks, the note tab in DEFAULT_TAB and the noteBlocks flag.
  • server: Tests for the new theme and color scheme variables and for the hint a pre-v3 value gets.
  • server: Tests for file requests covering the tokens, the login for creating, parallel uploads into one request and the cleanup.
  • server: Tests for WebSocket uploads that send a null frame, close during setup or finalize, or go silent.
  • web: Tests for opening notes made of blocks and notes from before v3, for code highlighting against injected markup, and for copying to the clipboard.
  • web: Tests for building a note from its blocks, for the upload that only tells the server it is made of blocks, and for the kinds kept for My Links.
  • web: Tests that text on and next to the accent reaches a contrast of at least 4.5:1 for any color.
  • web: Tests for the color scheme choice, the accent injected for CUSTOM_COLOR and the file type badges.
  • web: Tests for creating file requests, opening their inbox with and without a password, downloading from it and uploading into a request.
  • web: Component tests for the note editors, a template filled in and the note viewer.
  • web: Tests for request templates, their storage, their export with and without a password, and every way an import is refused.
  • web: Tests for the defaults of a browser, how they read and how the forms start from them.
  • client: Tests for turning CLI input into a note, for the format sent to older servers and for a round trip through the real encryption.
  • crypto: Notes encrypted before v3 are kept as test fixtures, so a change that stops them from opening fails the tests. They cover every note type, a note password and the older 16-byte salt.
  • crypto: Tests for file requests against the official HPKE test vectors, a frozen fixture of the request format and every way to tamper with a wrapped key.
  • infra: Tests for the note format, including every format a note from before v3 can have.
  • infra: The tests of @skysend/crypto and @skysend/note-format are typechecked along with their code.

🔧 CI/CD ​

  • docker: The image keeps only the SQLite binary for its own platform, which makes it about 29 MB smaller.
  • infra: The note format the web app and the CLI client share is a workspace package of its own, built and covered in CI, in the Docker image and for the CLI binaries.
  • infra: The release thanks the author of a pull request from outside the project at the end of each of its changelog entries, with a link to the pull request.
  • infra: Updated the GitHub Actions of every workflow to their newest major versions, which run on Node 24, among them checkout 7, setup-node 7, pnpm/action-setup 6, the artifact actions 7 and 8, codecov 7 and the Docker actions.
  • infra: Updated the dependencies across the monorepo, among them ink 8 in the CLI client, vitest 5, jsdom 30, eslint 10 and TypeScript 6 on the website, and minor and patch releases of the AWS SDK, hono, radix-ui, react 19.3, react-router, i18next, lucide-react, zod 4.6, vite 8.3, tailwindcss, typescript-eslint and prettier.
  • infra: A .node-version file pins Node 24 for local development, the version CI and the Docker image use.
  • infra: scripts/setup-dev-macos.sh sets up a Mac for development with fnm, the Node of .node-version, pnpm and the dependencies.
  • infra: The release groups the entries of each changelog section by scope, from the server to the tooling, so the lines about one part of SkySend stand together.
  • infra: A pull request into main now also builds the CLI binaries for all five platforms and starts them once, after the same approval as the Docker image.
  • infra: The release fails when a macOS binary does not carry a valid signature.
  • infra: The dev environment comes from the tracked template .env.dev.example, and .env.dev is ignored by git, so local values and secrets stay out of the repository. The setup scripts create .env.dev for a new checkout or worktree.
  • infra: The repository shows a Sponsor button for GitHub Sponsors, and the Sponsors section of the README updates by itself.

🐳 Docker ​

  • Image: skyfay/skysend:v3.0.0
  • Also tagged as: latest, v3
  • Platforms: linux/amd64, linux/arm64

v2.12.2 - Download Integrity and Upload Memory Security Fixes and CI Improvements ​

Released: October 5, 2026

🐛 Bug Fixes ​

  • client: skysend upload --no-ws is now read correctly, so it keeps uploads on HTTP chunks once WebSocket uploads return to the CLI.

🔒 Security ​

  • web: A download that the server cut short at a record boundary now fails instead of being saved as a complete file. Thanks @rajnisht7 (GHSA-w3p6-2vcf-mmv9)
  • client: A download that the server cut short at a record boundary now fails in the CLI and the TUI, and the incomplete file is removed. Thanks @rajnisht7 (GHSA-w3p6-2vcf-mmv9)
  • crypto: Archive metadata now records the size of the zip, so archive downloads can be checked for completeness. Archives uploaded with older versions cannot be checked this way.
  • server: Upload chunks are now streamed to disk instead of being held in memory, so oversized or parallel chunk requests can no longer exhaust the server's memory. A chunk is limited to 16 MiB and the chunks of an upload to its declared size. Thanks @rajnisht7 (GHSA-9rmm-v3p2-c26g)
  • server: WebSocket upload messages are limited to 1 MiB instead of the 100 MiB default of the WebSocket library.
  • infra: The abuse report Worker now checks a reply address in linear time and limits it to 254 characters, so a long crafted address no longer ties up a request. Reported by CodeQL as js/polynomial-redos.

📝 Documentation ​

  • docs: Documented the size check on downloads and the archive size in the encrypted metadata.
  • docs: Documented the limits and error responses of the chunk upload endpoint and the tmp/ folder in the data directory.
  • docs: The CLI docs now state that uploads always use HTTP chunks, since WebSocket uploads are disabled in the CLI.

🧪 Tests ​

  • crypto: Added tests for downloads cut short at a record boundary and for the archive size in the metadata.
  • web: Added tests that every download tier rejects a truncated file without falling back to the next tier.
  • server: Added tests for the limits of the chunked upload and for the chunk files on disk.
  • client: Added a test for the transport the upload command picks with and without --no-ws.

🔧 CI/CD ​

  • infra: Every pull request writes its changelog entries into a file of its own, which the release collects into the changelog, so pull requests no longer conflict over it.
  • infra: Lint, type check, unit tests and the docs build now also run on pull requests into dev, and every pull request now builds the website as well.
  • infra: A pull request into main can build the Docker image for both platforms and start it once, after a maintainer approves the build.

🐳 Docker ​

  • Image: skyfay/skysend:v2.12.2
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

Released: September 17, 2026

✨ Features ​

  • server: Added CUSTOM_OG_IMAGE and CUSTOM_OG_IMAGE_STYLE to set the image and layout of link previews, and a PNG or JPEG CUSTOM_LOGO is now used there by default. (#72)

🐛 Bug Fixes ​

  • web: The light theme no longer flashes dark while the page loads, and the mobile browser bar now follows the selected theme. (#70)
  • server: Link preview images now use an absolute URL as the Open Graph standard requires, so more apps show them.
  • server: The site name in link previews now follows CUSTOM_TITLE instead of always showing SkySend.
  • server: A CUSTOM_TITLE containing quotes or angle brackets no longer breaks the page markup.

🔒 Security ​

  • server: Updated hono to 4.13.8 to patch a ReDoS in the CORS middleware, query parameters read after the URL fragment, and further advisories in parseBody() and helpers SkySend does not use (GHSA-8j4g-w8fx-2239, GHSA-g6gw-c38x-mqfc, GHSA-crvj-82cr-hjcx, GHSA-54fx-42gc-7vw4, GHSA-f23p-vx2j-j53r, GHSA-gqvv-2mrq-wpjv, GHSA-79qm-7rj5-m7r9).
  • web: Updated dompurify to 3.4.15 to fix an XSS via detached subtrees when hooks are removed during in-place sanitizing (GHSA-55q2-fjhq-7xh7).
  • website: Updated next and eslint-config-next to 16.3.5 to patch two unauthenticated remote code execution vulnerabilities in the image optimization API and on Windows hosts (GHSA-p293-qw3h-jr36, GHSA-2xp9-vwfh-vxw4).

🔄 Changed ​

  • web: The selected language is now kept in the browser's local storage instead of a cookie, so a language picked before this version has to be selected once more.
  • website: The community goal on the roadmap page now targets 500 GitHub stars, and reaching 300 stars is listed under "Already live".

🗑️ Removed ​

  • web: Removed the build-time VITE_OG_IMAGE variable, which is replaced by CUSTOM_OG_IMAGE.

📝 Documentation ​

  • docs: Removed the roadmap page from the docs site. The Roadmap link in the navigation and the README now point to the roadmap on skysend.app.
  • docs: Documented how the link preview image is chosen and how to set a separate preview image or banner.

🧪 Tests ​

  • server: Added tests for the link preview image selection and for escaping branding values in the served page.

🔧 CI/CD ​

  • infra: Added pnpm setup:worktree, which copies the local .env from the primary checkout into a new git worktree and installs dependencies.

🐳 Docker ​

  • Image: skyfay/skysend:v2.12.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.12.0 - New Website, Branding & UX Improvements, Security Hardening, and Security Patches ​

Released: July 30, 2026

✨ Features ​

  • website: Added a new public website (homepage, roadmap, blog, and a Server Instances section listing live public instances), plus a Report Abuse page for flagging file and note links.
  • infra: Moved the abuse report Cloudflare Worker into the monorepo (workers/report), alongside the existing instances worker.
  • web: The download page now shows the file name, file list, and size as soon as the link is opened, instead of only after starting the download. (#66)
  • web: Uploads in "My Uploads" can be given an optional name, and multi-file uploads now list their file names instead of only a count. (#67)
  • server: Branding assets placed in the data directory are now served by the instance itself, so a custom logo no longer needs an external URL. (#66)

🐛 Bug Fixes ​

  • web: A repeated wrong password on the download page shows the error message again instead of staying silent after the first attempt.
  • web: A custom logo that cannot be loaded now falls back to the built-in logo instead of leaving a broken image in the header.
  • web: The file list on the download page now scrolls through all files instead of cutting off after the first few, and long file names no longer hide the file size.
  • client: Notes with an unlimited view limit now show "3 / ∞" in the TUI note detail instead of "3 / 0 (-3 remaining)". Thanks @Osamaali313 (#65)
  • web: Share links whose fragment was percent-encoded by a mail security filter now open correctly instead of showing "not found", and warn that the key was exposed. (#68)

🔒 Security ​

  • server: CUSTOM_LOGO no longer accepts protocol-relative URLs such as //example.com/logo.png, which loaded an external image outside the Content Security Policy origin check.
  • server: Updated @hono/node-server to 2.0.12 to patch an unauthenticated memory-leak denial of service triggered by aborted WebSocket handshakes (GHSA-9mqv-5hh9-4cgg).
  • server: Updated hono to 4.12.32 to patch cross-request data disclosure in JSX server-side rendering, XSS via the cx() utility, and header de-duplication in the AWS Lambda adapter (GHSA-hvrm-45r6-mjfj, GHSA-w62v-xxxg-mg59, GHSA-xgm2-5f3f-mvvc).
  • web: Updated react-router to 8 to patch a CSRF bypass that allowed actions to run before a 400 response (GHSA-qwww-vcr4-c8h2).
  • web: Updated dompurify to 3.4.12 to fix a sanitizer bypass for allowed custom elements (GHSA-c2j3-45gr-mqc4).
  • infra: Added pnpm overrides for sharp and brace-expansion, raised the postcss override, and removed the unused autoprefixer dependency to clear transitive advisories (GHSA-f88m-g3jw-g9cj, GHSA-3jxr-9vmj-r5cp, GHSA-mh99-v99m-4gvg, GHSA-r28c-9q8g-f849).
  • server: The request log no longer records anything after the ID in a file, note, or download path, so a link rewritten by a mail security filter cannot leave its key in the log. (#68)

🎨 Improvements ​

  • server: Startup now points out when CUSTOM_LOGO refers to an external host, and how to serve the file from the instance instead.
  • web: The selected-files list on the upload page uses the app's own slim scrollbar instead of the browser's native one, and hovering a shortened file name shows it in full.

🔄 Changed ​

  • web: On password-protected download links, entering the password now unlocks the file details, and the download starts with a separate click. (#66)
  • web: The placeholder for uploads without metadata now reads "Protected file" instead of "Encrypted file". (#66)

📝 Documentation ​

  • docs: Documented the branding directory and switched the custom logo examples from an external URL to a local path.
  • docs: Documented how mail security filters rewrite share links, what that means for the encryption key, and what a reverse proxy logs. (#68)

🔧 CI/CD ​

  • infra: Added a GitHub Actions workflow to deploy the report worker on push, matching the existing instances worker deploy pipeline.
  • infra: pnpm update:check now lists outdated dependencies per workspace package with a summary, instead of one merged table that ends in Command failed with exit code 1.

🐳 Docker ​

  • Image: skyfay/skysend:v2.12.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.11.3 - Dependency Updates and Bug Fixes ​

Released: June 21, 2026

🐛 Bug Fixes ​

  • server: Fixed intermittent 500/502 errors for static assets behind a reverse proxy (e.g. Traefik) after a deployment by keeping server connections alive longer than the proxy's idle timeout, so the proxy no longer reuses a closed connection.

🔒 Security ​

  • server: Updated hono to 4.12.26 to patch CORS credential reflection, IP restriction bypass, cookie injection, JWT scheme bypass, path traversal in serve-static, and AWS Lambda body-limit bypass (GHSA-88fw-hqm2-52qc, GHSA-xrhx-7g5j-rcj5, GHSA-3hrh-pfw6-9m5x, GHSA-f577-qrjj-4474, GHSA-2gcr-mfcq-wcc3, GHSA-wwfh-h76j-fc44, GHSA-rv63-4mwf-qqc2, GHSA-j6c9-x7qj-28xf, GHSA-wgpf-jwqj-8h8p).
  • web: Updated vite to 8.0.16 to fix server.fs.deny bypass and NTLMv2 hash disclosure on Windows (GHSA-fx2h-pf6j-xcff).
  • web: Updated dompurify to 3.4.11 to fix multiple XSS and attribute-pollution vulnerabilities (GHSA-hpcv-96wg-7vj8, GHSA-r47g-fvhr-h676, GHSA-rp9w-3fw7-7cwq, GHSA-76mc-f452-cxcm, GHSA-gvmj-g25r-r7wr, GHSA-vxr8-fq34-vvx9, GHSA-cmwh-pvxp-8882).
  • infra: Added pnpm overrides for ws (>=8.21.0), miniflare>undici (>=7.28.0), tsx>esbuild and wrangler>esbuild (>=0.28.1), @babel/core (>=7.29.6), and updated the vitepress>vite override to >=6.4.3 <7 to address transitive advisories in dev dependencies (GHSA-96hv-2xvq-fx4p, GHSA-58qx-3vcg-4xpx, GHSA-vmh5-mc38-953g, GHSA-hm92-r4w5-c3mj, GHSA-vxpw-j846-p89q, GHSA-pr7r-676h-xcf6, GHSA-p88m-4jfj-68fv, GHSA-35p6-xmwp-9g52, GHSA-g8m3-5g58-fq7m, GHSA-g7r4-m6w7-qqqr, GHSA-4x5r-pxfx-6jf8, GHSA-fx2h-pf6j-xcff).
  • server: Updated @hono/node-server to 2.0.5 to patch a serve-static middleware path-prefix bypass on Windows.

🔧 CI/CD ​

  • infra: Bumped patch-level dependencies across the monorepo (radix-ui components, react, react-i18next, idb-keyval, tailwindcss, vue, vitest, tsx, prettier, and related type definitions). Updated i18next to 26.3.1 to satisfy the updated react-i18next peer requirement.
  • infra: Bumped minor-level dependencies across the monorepo (AWS SDK v3, react-router-dom, radix-ui react-select/slot/switch, lucide-react, better-sqlite3, ink, eslint, typescript-eslint, wrangler, @cloudflare/workers-types).
  • infra: Updated commander to v15, @types/node to v26, @tailwindcss/typography to 0.5.20, and fflate to 0.8.3.

🐳 Docker ​

  • Image: skyfay/skysend:v2.11.3
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.11.2 - Cache-Control Fiexes & View Count Bug Fix ​

Released: June 5, 2026

🐛 Bug Fixes ​

  • server: The view count returned after opening a note now reflects the authoritative stored value, and notes that have reached their view limit are handled correctly. (#59)

🎨 Improvements ​

  • server: Added Cache-Control headers so proxies no longer cache transient server errors, while content-hashed assets are served with long-term immutable caching.

🔧 CI/CD ​

  • infra: Added typecheck scripts to every TypeScript package so pnpm validate and CI now type-check the entire monorepo, not just the web app, preventing type errors from slipping through to the Docker build.
  • infra: The typecheck task now builds its workspace library dependencies first, so type checking resolves cross-package types from source and passes in a clean checkout instead of relying on previously built output.

🐳 Docker ​

  • Image: skyfay/skysend:v2.11.2
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.11.1 - Adaptive ZIP Compression & Packing Events in Debug Info Panel ​

Released: May 31, 2026

🎨 Improvements ​

  • web: ZIP packing now uses store-only compression (level: 0) for already-compressed formats (audio, video, images, archives, and office documents), reducing CPU load on mobile devices without affecting ZIP output size.
  • client: Same adaptive compression as the web app - the CLI and TUI now also apply store-only mode for pre-compressed formats when packing multiple files.
  • web: The Technical Info timeline now shows packing events for multi-file uploads - "Packing started" when the ZIP phase begins and "Packing complete · Ø X MB/s" with the average throughput when it finishes.

🧪 Tests ​

  • server: Brought routes/note.ts to 100% coverage.
  • web: Brought useUpload and useUploadHistory to 100% coverage.
  • client: Brought lib/config.ts to 100% coverage.

🐳 Docker ​

  • Image: skyfay/skysend:v2.11.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.11.0 - Password Protection Algorithm Removal, New Copy Button for Password Values, and Improvements ​

Released: May 30, 2026

⚠️ Breaking: Password-protected uploads created with PBKDF2 (prior to v2.4.0) or the legacy Argon2id parameters (v2.4.0 - v2.5.0, 4-day window) can no longer be decrypted - both algorithms have been fully removed from the decryption pipeline. All such uploads have exceeded their maximum retention period and have expired, but any password-protected file from those eras cannot be opened after upgrading to this version.

✨ Features ​

  • web: Added a copy button to the password value fields in the "password" tab, matching the behaviour of the "password protection" input.

🎨 Improvements ​

  • web: Fixed the password generator not closing after clicking "generate" in the "password" tab.

🗑️ Removed ​

  • crypto: Removed PBKDF2-SHA256 decryption support - deriveKeyFromPasswordPbkdf2 and PBKDF2_ITERATIONS are deleted, deriveKeyFromPassword now requires an Argon2id function and no longer accepts undefined as a fallback. All pre-v2.5.0 PBKDF2-protected uploads have expired.
  • web: Removed "pbkdf2" from the passwordAlgo Zod enum in the download and note-info API schemas - responses containing this value are now treated as invalid.
  • client: Removed "pbkdf2" from the passwordAlgo Zod enum in the file-info and note-info API schemas, and removed the PBKDF2 decryption branch from prepareDownload.
  • server: Removed "pbkdf2" from the accepted passwordAlgo upload header values - any upload attempt with this algorithm now returns HTTP 400.
  • crypto: Removed ARGON2_PARAMS_LEGACY (19 MiB / 2 iterations) and the argon2Params optional parameter from deriveKeyFromPassword - the return type is now the literal "argon2id-v2". All uploads created during the 4-day "argon2id" window (v2.4.0 - v2.5.0) have expired.
  • web: Removed "argon2id" from the passwordAlgo Zod enum in the download and note-info API schemas - responses with the legacy algorithm are now treated as invalid.
  • client: Removed "argon2id" from the passwordAlgo Zod enum and the legacy decryption branch in prepareDownload - the function now always uses the current Argon2id parameters.
  • server: Removed "argon2id" from the accepted passwordAlgo upload header values - new uploads with this algorithm now return HTTP 400.

🐳 Docker ​

  • Image: skyfay/skysend:v2.11.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.10.1 - S3 Download CORS Error Handling and Debug Info Panel Storage Backend Events ​

Released: May 28, 2026

✨ Features ​

  • web: Added an isS3CorsError() pattern to showKnownErrorToast() so S3/R2 CORS failures during download are shown as an enriched toast with a Copy button and a link to the troubleshooting docs.
  • web: The Debug Info Panel event timeline now shows which storage backend is active: "S3 upload active" when the server uses S3/R2 for an upload, and "S3 presigned URL received" when a download fetches directly from S3/R2 (all three download tiers covered).

🐛 Bug Fixes ​

  • web: Fixed S3 downloads via presigned URL crashing the download page. After the switch from iframe to <a> navigation for Service Worker streaming, a CORS or network error from the S3/R2 bucket caused the browser to navigate the main page away. The Service Worker now catches those errors, signals the main thread via BroadcastChannel to fall back to the next download tier, and returns a safe attachment response to prevent page navigation.

🐳 Docker ​

  • Image: skyfay/skysend:v2.10.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.10.0 - New Copy & Share Features, Download UX Improvements, and Bug Fixes ​

Released: May 24, 2026

✨ Features ​

  • web: Added a copy button and a password generator button to the "Password protection" input on all upload types.
  • web: Added a "View" button (eye icon) to note cards in "My Uploads" to open the share link directly in the browser.
  • web: The browser favicon now shows a circular progress indicator during active downloads and is restored once the download finishes, fails, or is cancelled.
  • web: Added a "Cancel Download" button that aborts the active download across all tiers (Service Worker, File System Access, Blob).
  • web: Added an in-app Debug Info Panel with runtime details (download tier, browser, DevTools status, file size, transport, event timeline) and a "Copy to clipboard" button.
  • web: Added a Firefox DevTools detection warning modal when DevTools are docked at download start, with a retry button and an escape-hatch to proceed anyway.
  • web: Average download speed is now shown in the download completion card (e.g. "Ø 85.3 MB/s"), matching the existing upload display.
  • client: Average download speed is now shown in the CLI output and TUI completion screen after a successful download.
  • web: Replaced inline error messages with a global toast notification system for upload, note, SSH key, and decryption errors.
  • web: Added a showToast() helper with optional Copy and Docs action buttons for enriched toast notifications.
  • web: Added showKnownErrorToast() that automatically enriches crypto pipeline errors (e.g. missing HTTPS) with a title, Copy button, and Docs link.
  • web: Added an isOriginNotAllowedError() pattern to showKnownErrorToast() for misconfigured ALLOWED_ORIGINS WebSocket rejections.

🐛 Bug Fixes ​

  • web: Fixed the note password input becoming permanently disabled after a wrong password attempt.
  • web: Fixed the SSH key fingerprint overflowing its container on narrow screens.
  • web: Fixed the remove button and file-size badge being pushed off-screen for files with very long names in the upload list.
  • web: Removed per-record console.debug calls from the Service Worker download pipeline that caused multi-minute Firefox UI freezes on large files.

🎨 Improvements ​

  • web: Replaced the clients.matchAll() broadcast in the Service Worker with a BroadcastChannel("skysend-download") for all SW-to-main-thread and config messages.
  • web: Increased the Service Worker ReadableStream highWaterMark from 2 to 8 to prevent brief download UI freezes on slow or jittery connections.
  • web: Added lightweight diagnostic logging to the Service Worker (stream-start summary, per-1000-record checkpoints, slow-read alerts, double-readMore() detection).

🧪 Tests ​

  • web: Brought apps/web test coverage to 100% - added tests for isFirefox, isDevToolsOpen, and getBrowserInfo in utils, dismiss-all and multi-toast UPDATE in useToast, loadInfo non-ApiError path and view() without prior loadInfo in useNoteView, transport worker messages in useUpload, and 410/multi-upload branch coverage in useUploadHistory. Added /* v8 ignore next */ annotations on dead-code and unreachable branches in useUpload, useToast, and useNoteView.
  • client: Brought apps/client test coverage to 100% - added token storage tests (array JSON, corrupt JSON, clearStoredToken) to config, rejection-sampling branch test to password-generator. Added /* v8 ignore next */ annotations on dead-code branches in progress and oidc.

🐳 Docker ​

  • Image: skyfay/skysend:v2.10.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.9.5 - Cache-Control Fixes for Stale Content and Traefik Caching ​

Released: May 20, 2026

🎨 Improvements ​

  • server: Changed Cache-Control for index.html and download-sw.js from no-cache, must-revalidate to no-store. no-cache permits storing the response and requires proxy revalidation via ETag or Last-Modified headers, which SkySend does not set - causing Traefik's caching middleware to fall back to its own TTL and serve stale HTML after a deployment. no-store is unconditional and prevents any proxy from storing the response, eliminating the need for a Traefik restart after updates.
  • server: Added a global Cache-Control: no-store middleware to all /api/* routes. Previously only the successful download stream response carried this header. Error responses from any API endpoint (e.g. a transient 500 from the download route) had no cache directive, allowing Traefik to cache them. A single cached error response caused all subsequent requests to the same URL to receive the cached error until the proxy was restarted.

🐳 Docker ​

  • Image: skyfay/skysend:v2.9.5
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.9.4 - Service Worker Download Fixes for Firefox & Improved Caching ​

Released: May 20, 2026

🐛 Bug Fixes ​

  • web: Added try/catch around both crypto.subtle.decrypt calls in the Service Worker. An unauthenticated or corrupted encrypted record caused the decrypt call to throw, which rejected the pull() promise and let Firefox mark the download as completed with the incorrect partial file size. The stream is now explicitly errored via controller.error() and a dl-error broadcast is sent to the UI on failure.
  • web: Added a cancelled flag to the Service Worker's cancel() handler, checked at every await boundary in pull(). Previously, calling cancel() while pull() was awaiting readMore() let pull() continue executing on an already-cancelled stream, which could reach controller.close() and throw.

🎨 Improvements ​

  • web: Changed the Service Worker ReadableStream from highWaterMark: 0 to highWaterMark: 2 to address reported Firefox browser freezes during large-file downloads. With highWaterMark: 0, pull() is only triggered by a pending consumer read(), which may cause Firefox's download pipeline to block until a decrypted record is ready. With highWaterMark: 2, pull() runs proactively and keeps up to two pre-decrypted records in the internal queue, reducing the chance of a synchronous stall. pull() still enqueues exactly one record per call, so the stall-at-random-percentage behavior from v2.9.2 cannot recur.
  • server: download-sw.js is now served with Cache-Control: no-cache, must-revalidate. Previously the file was served by the static file middleware with no explicit cache header, allowing browsers and intermediate proxies to apply heuristic caching. With no-cache, browsers byte-check the Service Worker file on every navigation and register any updated version immediately after a deployment, so users are never stuck running an outdated SW.
  • web: Replaced the single-buffer copy strategy in the Service Worker ECE decryption pipeline with a zero-copy chunk queue. Previously appendToBuf() copied both the leftover bytes from the previous chunk (~65 KB) and the newly received chunk (~65 KB) into a fresh combined buffer on every pull() call - generating ~5 GB of unnecessary allocations for a 2.4 GB file. The new implementation pushes incoming reader.read() chunks into a queue by reference. readFromBuf() copies only the bytes required for the current decrypt call (one record, 65552 bytes), reducing GC pressure by roughly 2.5x and eliminating the speed oscillation and browser sluggishness during large downloads.
  • web: Eliminated the per-record Uint8Array allocation in readFromBuf() by introducing a pre-allocated scratchRecord buffer (ENCRYPTED_RECORD_SIZE bytes) that is reused across all pull() calls. crypto.subtle.decrypt copies its input synchronously before returning the Promise, making the buffer safe to reuse as soon as the await resolves. This removes a further ~2.4 GB of GC-managed allocations per large file download, reducing peak heap pressure and Firefox RAM consumption.

🐳 Docker ​

  • Image: skyfay/skysend:v2.9.4
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.9.3 - Debugging Service Worker Download Stalling in Firefox ​

Released: May 17, 2026

🐛 Bug Fixes ​

  • web: Fixed Service Worker downloads stalling at a random percentage (e.g. 0%, 13%, 21%) on Firefox with fast network connections. The pull() handler was processing all buffered ECE records in a single call, enqueuing multiple chunks at once. Firefox v128+ enforces the highWaterMark: 0 pull-based contract more strictly and loses its backpressure state when pull() enqueues more than one chunk, causing the ReadableStream to stop requesting more data mid-download. The inner loop is now replaced with a single-record-per-pull() pattern followed by an early return, which gives Firefox's download manager time to consume each chunk before the next one is produced.

🧪 Tests ​

  • web: Added console.debug diagnostic logging to the Service Worker's pull() and readMore() functions.

🐳 Docker ​

  • Image: skyfay/skysend:v2.9.3
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.9.2 - Firefox Hotfix release & Browser Cache fix ​

Released: May 17, 2026

🐛 Bug Fixes ​

  • web: Fixed Firefox hanging on Service Worker-streamed downloads for files >= 2 GiB. Firefox has a long-standing signed 32-bit integer overflow in its internal download pipeline that triggers when Content-Length is >= 2^31 bytes, causing the download manager to stall or cancel at the end. The Content-Length header is now omitted for files >= 2 GiB so Firefox falls back to reading until EOF. Files below 2 GiB and all other browsers are unaffected. Thanks @Dominion0815
  • web: Fixed the Service Worker download progress not reaching 100% and the UI not transitioning to "done" after the download completes. The dl-done broadcast was fire-and-forget - Firefox terminates the SW as soon as waitUntil resolves, abandoning the still-pending clients.matchAll() call before the message is delivered. All completion broadcasts are now awaited before streamDone() is called.

🎨 Improvements ​

  • server: The SPA entry point (index.html) is now served with Cache-Control: no-cache, must-revalidate. This prevents browsers and reverse proxies (e.g. Traefik with a caching middleware) from serving a stale index.html after a deployment. Without this, a cached old index.html referencing a previous JS bundle hash would cause 500 errors for the JS assets because the old filenames no longer exist on the new server.

🐳 Docker ​

  • Image: skyfay/skysend:v2.9.2
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.9.1 - Fixes for Large File Downloads in Firefox and SEO Meta Tag Improvements ​

Released: May 16, 2026

🐛 Bug Fixes ​

  • web: Fixed large file downloads in Firefox being truncated at ~40-50% when using Service Worker streaming. The hidden <iframe> navigation trigger has been replaced with an <a> click (no download attribute), matching Mozilla Send's approach. Firefox was cutting the SW stream during the iframe-to-download-manager handoff, resulting in incomplete files.

🎨 Improvements ​

  • web: Updated SEO meta tags (title, description, Open Graph, Twitter Card) to mention both file and note sharing. Added og:image and twitter:image for rich link previews on Discord and other platforms. The image defaults to /logo.png and can be overridden with the VITE_OG_IMAGE environment variable at build time.
  • web: The page <title> and OG/Twitter title tags now reflect CUSTOM_TITLE at runtime. When CUSTOM_TITLE=MyShare is set, the preview title becomes "MyShare | Encrypted File & Note Transfer" instead of "SkySend | ...".
  • server: The SPA fallback route now injects CUSTOM_TITLE into the served index.html at request time (cached after first read) so link-preview scrapers see the correct title without a separate build-time variable.

🔄 Changed ​

  • docs: The Domain has moved from skysend.ch to skysend.app. All documentation, API references, and examples have been updated to reflect the new domain. The old skysend.ch URLs will continue to work and redirect to skysend.app for the foreseeable future.
  • server: Added CUSTOM_REPORT_URL environment variable. When set, a "Report" link appears in the footer pointing to the configured URL - intended for abuse/report pages. Instance operators who do not set this variable will not show the link.

🐳 Docker ​

  • Image: skyfay/skysend:v2.9.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.9.0 - Multi-block Code Notes and UI Improvements ​

Released: May 15, 2026

✨ Features ​

  • web: Added multi-block code notes. The Code tab now supports multiple code blocks per note, each with an optional filename/title and a language selector (Auto-detect or one of 44 supported languages). On the view page, blocks show a detected language badge and can be collapsed individually or all at once - single-block notes expand by default, multi-block notes start collapsed. (#45)

🐛 Bug Fixes ​

  • web: Fixed the password generator length input being slightly clipped for three-digit values (100+). The field width was increased from w-16 to w-20 so numbers like 106 or 128 are fully visible.
  • web: Fixed invisible burn-after-reading warning text in light mode. The warning box now uses text-destructive (colored) instead of text-destructive-foreground (white), making the text readable on the light semi-transparent red background. (#46)
  • web: Fixed syntax highlighting in code snippets for light mode. The code block now uses the GitHub light theme in light mode (#f6f8fa background) and the Arta dark theme in dark mode (#222 background), ensuring all tokens are readable in both themes. (#45)

🐳 Docker ​

  • Image: skyfay/skysend:v2.9.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.8.0 - OIDC/SSO Authentication, General Improvements, Security & Dependency Patches ​

Released: May 14, 2026

✨ Features ​

  • server: Added optional OIDC/SSO authentication via a plugin adapter system. Supports Generic, PocketID, Authentik, and Keycloak providers via the OIDC_PROVIDER env var. File uploads and note creation can each be independently protected using OIDC_PROTECT_FILES and OIDC_PROTECT_NOTES. Both HTTP and WebSocket upload transports are guarded. Sessions are stateless JWT cookies - no database changes required.
  • web: Added inline auth blocks on the upload page when OIDC is enabled and the user is not logged in. A user indicator with logout button is shown in the navigation header when authenticated.
  • client: Added OIDC authentication support for the CLI. When a server has OIDC_PROTECT_FILES or OIDC_PROTECT_NOTES enabled, the CLI automatically opens a browser for login before uploading or creating notes. Session tokens are stored per-server in ~/.config/skysend/tokens.json and reused until they expire. New auth subcommands (login, logout, status) allow explicit session management.

🔒 Security ​

  • infra: Added pnpm overrides for @esbuild-kit/core-utils>esbuild (>=0.25.0) and vitepress>vite (~6.4.2) to address GHSA-67mh-4wv8-2f99 (esbuild dev server CORS bypass) and GHSA-4w7w-66w2-5vf9 (Vite path traversal in optimized deps .map handling).
  • infra: Updated all dependencies to latest versions - vite 8.0.13, tailwindcss + @tailwindcss/vite 4.3.0, better-sqlite3 12.10.0, @aws-sdk/* 3.1046.0, i18next 26.1.0, lucide-react 1.16.0, tailwind-merge 3.6.0, vitest + @vitest/coverage-v8 4.1.6, tsx 4.22.0, and others.

🎨 Improvements ​

  • web: The password input placeholder no longer says "(optional)" when FORCE_FILE_PASSWORD or FORCE_NOTE_PASSWORD is enabled - it now correctly says "(required)".
  • web: The navigation header title now truncates with an ellipsis when a custom title is very long, preventing layout overflow on all screen sizes.
  • web: Added a fade gradient at the bottom of the language switcher dropdown to indicate that the list is scrollable. The gradient disappears automatically when scrolled to the bottom.
  • web: Added a hamburger menu for mobile screens. All navigation links, the language switcher, theme toggle, and OIDC login/logout are now accessible in a collapsible dropdown on small viewports.
  • web: The language selector dropdown now has a fixed max height with a scrollable list (using shadcn ScrollArea) and a search input at the top, so all languages are accessible on small screens without getting cut off.
  • web: The logout button tooltip in the navigation header now uses the shadcn Tooltip component instead of a native browser title attribute.

🧪 Tests ​

  • server: Added unit tests for the OIDC auth layer, all four provider adapters, the guard middleware, and auth routes, plus expanded coverage across multiple server modules - overall line coverage improved from 84.44% to 90%.
  • client: Added unit tests for the OIDC login flow and progress/password prompt utilities, bringing both modules to near-100% coverage.
  • web: Added unit tests for the auth hook, session API, and all remaining hooks that previously had 0% coverage.
  • server: Increased patch coverage for OIDC-related modules - added tests for verifySessionJwt/verifyPkceJwt returning null on malformed payloads, the String(err) branch in the discovery warm-up handler, and empty-claims fallback paths in all four provider adapters, bringing auth/session.ts, routes/auth.ts, and all auth/adapters/*.ts to 100% branch coverage.
  • client: Added tests for the getFreePort error path and the performOidcLogin catch block, bringing lib/oidc.ts to 100% line coverage.

📝 Documentation ​

  • docs: Added OIDC Authentication page to the Developer Guide API Reference, covering the full PKCE login flow, auth/callback/logout/session endpoints, the OIDC guard middleware, protected vs. always-public endpoints, provider adapters, session JWT format, and the CLI device-browser login flow.
  • docs: Added OIDC/SSO to the features list on the landing page.
  • docs: Extended the API Overview with the /auth/* endpoint table and a link to the new OIDC page.
  • docs: Added auth login, auth logout, and auth status to the client CLI command reference, including the OIDC login flow explanation and token storage details.

🐳 Docker ​

  • Image: skyfay/skysend:v2.8.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.7.1 - Two New Languages & Markdown Rendering Fixes ​

Released: May 13, 2026

✨ Features ​

  • web: Added Japanese (ja) translation. Thanks @canaria-computer (#41)
  • web: Added Brazilian Portuguese (pt-BR) translation. Thanks @magisph (#40)

🐛 Bug Fixes ​

  • web: Fixed Markdown preview and note view not rendering headings, blockquotes, lists, and other block-level elements. The @tailwindcss/typography plugin was installed but not loaded via @plugin in index.css, so prose styles had no effect. (#42)

🔄 Changed ​

  • web: Markdown prose elements (list bullets, counters, links, blockquote borders, table borders, and horizontal rules) now follow the project's primary color, including the CUSTOM_COLOR branding setting.

🐳 Docker ​

  • Image: skyfay/skysend:v2.7.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.7.0 - Native OS Share Button, Security Patches, and Dependency Updates ​

Released: May 8, 2026

✨ Features ​

  • web: Added a native OS Share button to the upload success screen. Uses the Web Share API (navigator.share()) and is only shown on devices that support it (iOS Safari, Android Chrome). The Copy button remains unchanged as fallback.

🔒 Security ​

  • server: Updated hono from 4.12.15 to 4.12.18 to patch two moderate vulnerabilities - bodyLimit() bypass for chunked requests (GHSA-9vqf-7f2p-gf9v) and unvalidated JSX tag names allowing HTML injection (GHSA-69xw-7hcm-h432).

🎨 Improvements ​

  • infra: Updated all dependencies to their latest compatible versions (react 19.2.6, zod 4.4.3, react-router-dom 7.15.0, vite 8.0.11, @aws-sdk/* 3.1045.0, eslint 10.3.0, dompurify 3.4.2, and others).

🐳 Docker ​

  • Image: skyfay/skysend:v2.7.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.6.0 - New Customization Options, Added Chinese Language and Bug Fixes ​

Released: May 6, 2026

✨ Features ​

  • web: Added Simplified Chinese (zh-CN) translation. Thanks @Liyouran-center (#36)
  • server: Added DEFAULT_THEME env var to set the default UI theme (dark, light, or system) for users who have not stored a preference. The user's own choice always takes precedence. (#35)
  • server: Added DEFAULT_TAB env var to set the default upload tab (file, text, password, code, or sshkey). Falls back to the first enabled tab if the configured tab is unavailable. (#35)
  • server: Added FORCE_FILE_PASSWORD env var - when set to true, all file uploads must be password-protected. Enforced on both the server (HTTP 400) and in the UI. (#35)
  • server: Added FORCE_NOTE_PASSWORD env var - when set to true, all note uploads (text, password, code, SSH key) must be password-protected. Enforced on both the server (HTTP 400) and in the UI. (#35)
  • client: FORCE_FILE_PASSWORD and FORCE_NOTE_PASSWORD are now respected by the CLI client. When enforced, the CLI automatically prompts for a password if -p was not provided. The interactive TUI skips the "Password protect?" question and goes directly to the password input prompt. (#35)

🐛 Bug Fixes ​

  • docker: Fixed health check failing when a custom PORT is set - the HEALTHCHECK command hardcoded port 3000 instead of reading the PORT environment variable. (#34)

🔄 Changed ​

  • client: WebSocket upload transport is globally disabled. Large file transfers via WebSocket over HTTPS connections fail mid-transfer due to an unresolved issue. HTTP chunked upload is now always used. The per-server toggle has been removed from the settings until the root cause is fixed.

🐳 Docker ​

  • Image: skyfay/skysend:v2.6.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.7 - Docker Entrypoint Custom Data/Uploads Directory Fix ​

Released: May 3, 2026

🐛 Bug Fixes ​

  • docker: Fixed EACCES: permission denied on startup when DATA_DIR or UPLOADS_DIR are set to a custom path - the entrypoint previously hardcoded /data and /uploads for mkdir and chown, so custom paths were never created or made writable. The entrypoint now uses the actual DATA_DIR and UPLOADS_DIR environment variables.

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.7
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.6 - Docker Entrypoint PGID Fix ​

Released: May 3, 2026

🐛 Bug Fixes ​

  • docker: Fixed container startup failing with addgroup: group 'skysend' in use when PGID is set to a value other than the default 1001 - busybox's delgroup cannot remove a group that still has members, so the subsequent addgroup always failed. The entrypoint now uses sed to update the GID in-place in /etc/group and /etc/passwd, matching the existing approach used for PUID.

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.6
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.5 - CSP Fix for Password-Protected Uploads, Downloads and Notes ​

Released: May 2, 2026

🐛 Bug Fixes ​

  • server: Fixed password-protected file and note uploads/downloads failing with WebAssembly.compile() blocked by CSP in browsers behind reverse proxies that forward CSP headers - added 'wasm-unsafe-eval' to script-src to allow Argon2id (hash-wasm) to compile its inline WASM binary.

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.5
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.4 - Content Security Policy Update for Custom Logo Support ​

Released: May 1, 2026

🔄 Changed ​

  • server: Content Security Policy now dynamically allows the origin of CUSTOM_LOGO in img-src when an external HTTP(S) logo URL is configured, while keeping the default image policy strict.

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.4
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.3 - WebSocket Upload Keepalive ​

Released: April 25, 2026

🐛 Bug Fixes ​

  • server: Fixed WebSocket uploads hanging at 100% on remote servers behind a reverse proxy - the server now sends a keepalive message every 5 seconds during the finalization phase so Caddy/Nginx/Traefik do not close the connection as idle while the server is still flushing data to storage and writing to the DB.

🔧 CI/CD ​

  • infra: Added branche ignore rule to stop branch-specific workflows (e.g. release.yml) from running on the main branch - these workflows are only meant to run on feature branches and tags, not on main where they cause noise and duplicate runs alongside the generic validate.yml.

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.3
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.2 - Client Improvements & Bug fixes ​

Released: April 25, 2026

🐛 Bug Fixes ​

  • client: Fixed resolveServer() ignoring defaultServer and using the legacy top-level server field instead - if both fields exist in config.json (e.g. after migrating to multi-server), the TUI and all commands now correctly prefer defaultServer.
  • client: Fixed WebSocket uploads hanging at 100% on remote servers - the progress bar reached 100% as soon as data was queued in Node.js's send buffer, not when it arrived at the server, causing the fixed 5-minute finalize timeout to expire for large files on slow connections. The send buffer is now drained to zero before sending "finalize", the timeout is dynamic (5 min + 2 s/MB), and the TUI/CLI show "Finalizing..." while waiting for the server confirmation.

🎨 Improvements ​

  • client: TUI and non-interactive CLI now display the average upload speed on the summary screen after a successful upload, matching the existing web behavior.

📝 Documentation ​

  • docs: Updated the benchmarks page with more specific details on the environment, new results and client tests.

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.2
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.1 - Bug fixes, dependency updates, and Dockerfile improvements ​

Released: April 25, 2026

🐛 Bug Fixes ​

  • client: Fixed skysend update failing on Windows with "Permission denied" even as Administrator. Windows locks running .exe files, so fs.renameSync always threw EPERM. The fix spawns a detached, hidden cmd.exe batch script that waits 2 seconds (until the current process exits) then moves the downloaded binary into place with move /y.
  • client: Fixed install.ps1 hanging silently during download. PowerShell's default $ProgressPreference = 'Continue' makes Invoke-WebRequest up to 100x slower and shows no feedback in many terminal environments. The script now sets $ProgressPreference = 'SilentlyContinue' and prints Downloading <file>... done (X.X MB) and Verifying checksum... ok step messages instead.
  • client: Fixed install.sh showing no output during binary download. curl -fsSL and wget -q were fully silent. The binary download now uses curl --progress-bar (shows a ##### bar on stderr) and wget without -q, so users see download progress.
  • server: Fixed S3 uploads failing with Cloudflare R2 and other S3-compatible providers with the error [EntityReplacer] Invalid character '#' in entity name: "#xD". The root cause was fast-xml-parser@5.7.1 introducing a regression where numeric character references (e.g. &#xD;) in XML responses could no longer be parsed. Updated fast-xml-parser override to >=5.7.2 which restores correct behavior.
  • server: Set requestChecksumCalculation and responseChecksumValidation to WHEN_REQUIRED on the S3 client. AWS SDK v3 >=3.679 defaults to WHEN_SUPPORTED, causing proactive CRC checksum headers that can trigger provider-specific XML parsing issues.

🔒 Security ​

  • infra: Added pnpm.overrides for postcss (>=8.5.10) to patch a moderate XSS vulnerability (GHSA-qx2v-qp2m-jg93) in transitive dependencies via autoprefixer

🎨 Improvements ​

  • server: Updated @hono/node-server from v1 to v2 - same public API, up to 2.3x faster body parsing via optimized direct Node.js IncomingMessage reads, URL construction fast-path, and buildOutgoingHttpHeaders optimization
  • infra: Updated patch and minor dependencies across all workspace packages - hono, @aws-sdk/client-s3, @aws-sdk/lib-storage, @aws-sdk/s3-request-presigner, better-sqlite3, tailwindcss, @tailwindcss/vite, react-router-dom, i18next, react-i18next, lucide-react, autoprefixer, vite, vue, wrangler, @cloudflare/workers-types, prettier, typescript, eslint-plugin-react-hooks, globals, typescript-eslint
  • infra: Updated eslint and @eslint/js from v9 to v10, and commander from v13 to v14 - no API changes required, fixed two new eslint:recommended rules (no-useless-assignment in upload chunking code, preserve-caught-error in upload worker)
  • web: Removed deprecated @types/dompurify - DOMPurify v3+ ships its own TypeScript declarations
  • infra: Added COPY apps/client/package.json, COPY apps/client/stubs/, and COPY workers/instances/package.json to the Dockerfile build stage so pnpm install --frozen-lockfile can resolve all workspace packages (including the file: stub dependency in @skysend/client) before COPY . .

🗑️ Removed ​

  • server: Removed S3_PUBLIC_URL environment variable. S3 downloads now exclusively use presigned URLs, which enforce expiry and download limits server-side and expire automatically. Public bucket URLs allowed clients to bypass these controls by reusing a captured URL.

📝 Documentation ​

  • docs: Removed PBKDF2-SHA256 fallback references from password-protection.md, README.md, and docs/index.md - password protection now exclusively documents Argon2id

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.5.0 - Security Audit Fixes, Test Coverage Improvements, and Docker Metadata Labels ​

Released: April 23, 2026

✨ Features ​

  • server: Added per-IP per-resource password attempt lockout - after 10 failed attempts the IP is locked out for 15 minutes with a Retry-After header, IPs stored as ephemeral HMAC-SHA256 hashes, configurable via PASSWORD_MAX_ATTEMPTS and PASSWORD_LOCKOUT_MS

🔒 Security ​

  • server: Updated hono from 4.12.12 to 4.12.14 to fix HTML injection via improperly handled JSX attribute names in SSR (GHSA-458j-xx4x-4375)
  • infra: Added pnpm.overrides for esbuild (>=0.25.0), vite (>=6.4.2), and fast-xml-parser (>=5.7.0) to patch transitive vulnerabilities in dev/docs dependencies (GHSA-67mh-4wv8-2f99, GHSA-4w7w-66w2-5vf9, GHSA-gh4j-gqv2-49f6)
  • crypto: Tightened Argon2id-to-PBKDF2 fallback logic - the fallback now only triggers on WASM availability errors (CompileError, LinkError, or matching message) and propagates all other crypto errors instead of silently swallowing them
  • crypto: Increased Argon2id parameters from 19 MiB / 2 iterations to 64 MiB / 3 iterations, matching OWASP's strong recommendation and significantly raising GPU brute-force cost for new password-protected uploads
  • crypto: Increased HKDF salt length from 16 to 32 bytes for new uploads, matching the RFC 5869 recommendation to use a salt equal to the hash output length - legacy 16-byte salts from existing uploads are still accepted
  • crypto: Added stream truncation detection to createDecryptStream - callers can pass expectedPlaintextSize (from authenticated metadata) to detect a malicious server delivering fewer records than were encrypted
  • server: Added Referrer-Policy: no-referrer HTTP header to prevent URL fragments from leaking via Referer header in misconfigured or future environments
  • web: Added <meta name="referrer" content="no-referrer"> to index.html as defense-in-depth for the referrer policy
  • web: URL fragment (encryption key) is now removed from the browser address bar via history.replaceState once decryption begins, preventing key leakage through browser history
  • web: Note uploads now use Argon2id for password key derivation - previously PBKDF2 was always used for notes due to a missing argument
  • crypto: Removed Argon2id-to-PBKDF2 upload fallback entirely - if Argon2id WASM fails during an upload, an error is thrown instead of silently downgrading to PBKDF2 ("fail secure"), PBKDF2 decryption for existing uploads is unaffected
  • web: Added DOMPurify sanitization of highlight.js output before dangerouslySetInnerHTML in code notes - defense-in-depth against any future upstream hljs vulnerability
  • web: Added rehype-sanitize plugin to ReactMarkdown rendering in notes - prevents XSS from future react-markdown upstream changes that could enable raw HTML
  • web: URL fragment (encryption key) is now removed from the browser address bar via history.replaceState in NoteView immediately at page mount - previously only the Download page had this protection
  • server: Fixed IP extraction when TRUST_PROXY=true - previously the leftmost (client-controlled) value from X-Forwarded-For was used, allowing clients to spoof their IP and bypass rate limiting and upload quotas, now uses the rightmost (proxy-appended) value
  • server: Fixed S3 download with S3_PUBLIC_URL configured - previously a permanent public URL was returned, remaining valid indefinitely after DB record deletion and bypassing expiry/download-limit enforcement, now always uses presigned URLs with a TTL
  • web: Replaced deprecated apple-mobile-web-app-capable meta tag with the standard mobile-web-app-capable equivalent - eliminates browser console warning, the PWA manifest display: standalone already handles standalone mode on modern browsers

🎨 Improvements ​

  • web: Password prompt now shows a translated "too many attempts" error when the server returns 429 instead of switching away from the password screen

🔧 CI/CD ​

  • infra: Added OCI standard labels to Docker images via docker/metadata-action@v5 - sets title, description, url, source, version, revision, created, vendor, and licenses for better registry compatibility and Renovate/Dependabot integration

🧪 Tests ​

  • crypto: Expanded to 129 tests with 100% coverage - added security-property tests for HKDF domain separation, ECE reorder/truncation attacks, Argon2id error propagation, PBKDF2 known-answer verification, and legacy salt backward compatibility.
  • server: Added 24 integration tests for the chunked upload flow (/init, /chunk, /finalize), password-attempt lockout (429 + Retry-After), and invalid input handling across meta.ts, password.ts, and note.ts routes.
  • server: Added 5 tests for startCleanupJob (interval, stop function, logging, error recovery) - bringing cleanup.ts to 100%.
  • server: Brought upload-validation.ts and quota.ts to 100% - covers all check(), getStatus(), 413 middleware, DB key restoration, and interval behavior (rotation, expiry cleanup).
  • infra: Added vitest.config.ts for server and client, updated vite.config.ts for web - all with scoped coverage.include and explicit excludes for untestable files (browser workers, WASM, S3 backend, app entrypoints).

🐳 Docker ​

  • Image: skyfay/skysend:v2.5.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.4.3 - Codecov, Unit Test Improvements & Web Improvements ​

Released: April 21, 2026

✨ Features ​

  • client: TUI "Manage servers" now supports adding, deleting, and setting a server as default via a server action sub-menu

🐛 Bug Fixes ​

  • web: Fixed note view incorrectly showing "permanently deleted" warning for unlimited-view notes (maxViews === 0)

🧪 Tests ​

  • infra: Added @vitest/coverage-v8 and test:coverage scripts to server, web, crypto, and client packages for coverage report generation in LCOV format
  • web: Added 57 unit tests across lib/utils, lib/password-generator, and lib/upload-store - covering formatting functions, isSafari detection, generatePassword/calculateEntropy, and full CRUD/sorting for IndexedDB upload and note storage (using an in-memory idb-keyval mock)
  • client: Added 93 unit tests across lib/progress, lib/url, lib/password-generator, lib/config, and lib/history - covering all formatting/parsing utilities, full parseShareUrl/buildShareUrl logic including edge cases, password generation, config file lifecycle with filesystem isolation via tmpdir, and history CRUD with expiry cleanup

🔧 CI/CD ​

  • infra: Updated validate.yml test job to run with coverage and upload reports to Codecov via codecov/codecov-action@v5
  • infra: Added codecov.yml with project and patch coverage status checks
  • crypto: Added 14 new unit tests covering previously untested security-critical paths: validateMetadata error branches (invalid JSON, null payload, unknown type, malformed archive entries, negative sizes, empty names, missing MIME type), deriveKeyFromPasswordArgon2 input validation, ECE decrypt stream "record too short" path, and exact error message matching for the nonce-missing guard. Coverage: 94% → 99.29% statements, 88% → 97.82% branches. Added vitest.config.ts to exclude the re-export barrel src/index.ts from coverage.

🐳 Docker ​

  • Image: skyfay/skysend:v2.4.3
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.4.2 - WebSocket Upload Toggle and CLI History Command ​

Released: April 20, 2026

✨ Features ​

  • client: Added WebSocket transport toggle - disable WebSocket uploads via --no-ws CLI flag, TUI Settings menu, or \"websocket\": false in config file- client: Added skysend ls command to list upload and note history with age, expiry, and size - supports --server, --all, and --json flags

🎨 Improvements ​

  • client: Connection error screen now offers recovery options - press s to select another server or r to retry instead of requiring Ctrl+C exit
  • client: Connection errors now show a user-friendly message (Server <url> is not reachable) instead of the raw fetch failed error
  • client: skysend config now shows all registered servers with their per-server WebSocket status

📝 Documentation ​

  • docs: Added WebSocket transport configuration section to CLI client documentation

🐳 Docker ​

  • Image: skyfay/skysend:v2.4.2
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.4.1 - Bug Fixes for CLI Binary and Client install URL Shortening ​

Released: April 20, 2026

🐛 Bug Fixes ​

  • client: Fixed Cannot find package 'react-devtools-core' runtime error when running compiled CLI binaries - replaced --external flag with a bundled no-op stub so Ink's optional devtools import resolves inside the binary

📝 Documentation ​

  • docs: Shortened CLI install URLs from raw GitHub links to skysend.app/install.sh and skysend.app/install.ps1 redirects across README, docs, and install scripts

🐳 Docker ​

  • Image: skyfay/skysend:v2.4.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.4.0 - CLI Client, PWA Support and ZIP creating improvements ​

Released: April 19, 2026

✨ Features ​

  • web: Added PWA (Progressive Web App) support - SkySend can now be installed as an app on desktop (Chrome, Edge), Android, and iOS via "Add to Home Screen"
  • client: Added @skysend/client CLI binary for uploading and downloading files with end-to-end encryption from the terminal - supports single/multi-file uploads, encrypted notes, password protection, WebSocket and HTTP chunked transports, and cross-platform Bun-compiled binaries (Linux, macOS, Windows)
  • web: Added Argon2id password KDF support to the web frontend and upload worker using hash-wasm, enabling cross-compatibility with CLI password-protected uploads

🐛 Bug Fixes ​

  • web: Fixed WebSocket upload failing through Vite dev proxy by enabling ws: true on the API proxy config

🎨 Improvements ​

  • web: Multi-file uploads now show a determinate progress bar (0-100%) during the packing phase instead of an indeterminate spinner
  • web: Multi-file ZIP creation moved from main thread into the upload worker, reducing peak memory usage by ~50% for large uploads
  • web: Average upload speed is now displayed on the share link page after upload completes

🔄 Changed ​

  • cli: Renamed admin CLI binary from skysend to skysend-cli to avoid conflict with the new client binary (consistent with Docker and documentation)

📝 Documentation ​

  • docs: Cleaned up docker compose example in the user guide - removed redundant comments and simplified environment variable list with a link to the full reference
  • docs: Added CLI client documentation - overview, installation guide (Linux/macOS/Windows), detailed command reference for all 7 commands (upload, download, note, note:view, delete, config, update)
  • docs: Updated README, docs homepage, getting started, installation, first steps, architecture, setup, and roadmap pages with CLI client information

🔧 CI/CD ​

  • infra: Added CLI binary build pipeline to release workflow - compiles Bun binaries for 5 targets (linux-x64, linux-arm64, darwin-x64, darwin-arm64, windows-x64) with SHA-256 checksums and attaches them to GitHub Releases
  • infra: Added install scripts for Linux/macOS (install.sh) and Windows (install.ps1) with automatic platform detection and checksum verification

🐳 Docker ​

  • Image: skyfay/skysend:v2.4.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.3.0 - WebSocket Upload Transport for Improved Performance in Proxied Environments ​

Released: April 17, 2026

✨ Features ​

  • server: Added a WebSocket upload transport at /api/upload/ws that streams the encrypted payload over a single persistent connection, eliminating the HTTP/2 multiplexing bottleneck that reverse proxies (Traefik, Nginx) impose on parallel chunk uploads
  • server: Added FILE_UPLOAD_WS environment variable (default: true) to enable or disable the WebSocket upload transport
  • server: Added FILE_UPLOAD_WS_MAX_BUFFER environment variable (default: 16MB) to cap the per-session server receive buffer for WebSocket uploads
  • web: Upload worker now uses the WebSocket transport as the primary upload path and automatically falls back to the existing HTTP chunked upload when the handshake fails, is blocked, or times out (10 s)

🔒 Security ​

  • server: Added Origin header validation on WebSocket upgrade requests to prevent cross-site WebSocket hijacking (defence-in-depth, not exploitable due to token requirements)

📝 Documentation ​

  • docs: Added FILE_UPLOAD_WS and FILE_UPLOAD_WS_MAX_BUFFER to the user-guide environment variables page and the developer-guide environment reference
  • docs: Documented the WebSocket upload protocol in the upload API reference, including message shapes, close codes, and client fallback triggers
  • docs: Added Nginx and Traefik configuration snippets for the WebSocket upload transport in the reverse-proxy guide
  • docs: Updated developer-guide architecture to document both WebSocket (primary) and HTTP chunked (fallback) upload transports with flow diagrams

🐳 Docker ​

  • Image: skyfay/skysend:v2.3.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.2.4 - Max Concurrent Chunk Uploads and Speed Limit Configuration ​

Released: April 15, 2026

✨ Features ​

  • server: Added FILE_UPLOAD_CONCURRENT_CHUNKS environment variable (default: 3) to control the number of parallel chunk uploads per session - increase to improve upload speed in Chromium browsers through HTTP/2 proxies
  • server: Added FILE_UPLOAD_SPEED_LIMIT environment variable (default: 0 = unlimited) to cap upload throughput per session - supports human-readable values like 100MB (bytes per second)
  • web: Client now reads the configured concurrent chunk count from the server and adjusts parallel uploads accordingly

📝 Documentation ​

  • docs: Added FILE_UPLOAD_CONCURRENT_CHUNKS and FILE_UPLOAD_SPEED_LIMIT to user-guide environment variables page with new "Upload Performance" section
  • docs: Added FILE_UPLOAD_CONCURRENT_CHUNKS and FILE_UPLOAD_SPEED_LIMIT to developer-guide environment reference

🐳 Docker ​

  • Image: skyfay/skysend:v2.2.4
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.2.3 - Critical Bug Fixes for Chunked Uploads in HTTP/2 Proxied Environments ​

Released: April 15, 2026

  • server: Fixed rate limiter path matching - the regex anchor (^) prevented chunk requests from being recognized through the /api sub-router, causing 429 errors despite the exemption
  • server: Fixed HTTP/2 flow-control deadlock that caused uploads to stall at 0-1% in Chrome, Firefox, Brave, and Edge through reverse proxies - the server now reads each chunk body immediately instead of deferring reads in a promise chain, preventing proxy flow-control backpressure from blocking the connection

🐳 Docker ​

  • Image: skyfay/skysend:v2.2.3
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.2.2 - Bug Fixes for Chunked Uploads in Brave and Edge ​

Released: April 15, 2026

🐛 Bug Fixes ​

  • server: Chunk upload requests (/upload/:id/chunk) are now exempt from the global rate limiter - previously, uploading a large file would exceed the 60 requests/minute limit and cause 429 errors, breaking uploads entirely in production
  • web: Chunk upload body changed from Blob to ArrayBuffer - fixes uploads permanently stalling at 0% in Brave and Edge, where the browser opened HTTP/2 streams but never sent DATA frames for Blob bodies from Web Workers

🐳 Docker ​

  • Image: skyfay/skysend:v2.2.2
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.2.1 - Parallel Chunk Uploads and Performance Improvements ​

Released: April 15, 2026

🎨 Improvements ​

  • web: Upload chunks are now sent in parallel (up to 3 concurrent, 10 MB each) instead of sequentially (50 MB each), dramatically improving upload speed in Chrome, Brave, and Edge through reverse proxies like Traefik
  • server: Chunk upload endpoint accepts an index query parameter and reassembles chunks in-order on the server, ensuring data integrity with parallel client uploads
  • server: In-order chunks are now streamed directly to storage without buffering, eliminating unnecessary memory copies on the hot path
  • server: Optimized S3 multipart upload buffering to avoid full-buffer reallocation on every chunk append
  • server: S3 part uploads now run as a concurrent pool with smooth backpressure - waits for one upload slot to free up instead of draining everything, giving consistent throughput instead of burst-stop cycles

📝 Documentation ​

  • docs: Updated architecture diagram with chunked upload flow (init, parallel chunks with index, finalize)
  • docs: Added chunked upload API documentation (init, chunk, finalize endpoints) to developer guide
  • docs: Added chunked upload endpoints to API overview table

🐳 Docker ​

  • Image: skyfay/skysend:v2.2.1
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.2.0 - S3 Storage Backend ​

Released: April 15, 2026

✨ Features ​

  • server: Added S3-compatible storage backend as alternative to local filesystem storage
  • server: Added STORAGE_BACKEND environment variable to switch between filesystem (default) and s3 storage
  • server: Added S3 configuration via environment variables (S3_BUCKET, S3_REGION, S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_FORCE_PATH_STYLE, S3_PRESIGNED_EXPIRY, S3_PUBLIC_URL)
  • server: Uploads in S3 mode stream directly to S3 via multipart upload without disk buffering on the server
  • server: Added S3_PUBLIC_URL for direct download URLs via R2 custom domains or public buckets - avoids presigned URL complexity and CORS issues
  • server: Falls back to presigned URLs when S3_PUBLIC_URL is not set (for private buckets)
  • web: Client download logic transparently handles both direct file streams (filesystem), presigned URL redirects (S3 private), and public URL redirects (S3 public)
  • server: Supports all S3-compatible providers (AWS S3, Cloudflare R2, Hetzner Object Storage, MinIO, Wasabi, Backblaze B2, DigitalOcean Spaces, and more) via custom endpoint configuration
  • server: Logs storage mode on startup (filesystem path or S3 endpoint with public/presigned mode)
  • server: S3 connectivity test on startup - verifies bucket access, write, and delete permissions before accepting requests
  • server: Added S3_PART_SIZE and S3_CONCURRENCY environment variables for tuning S3 upload throughput
  • web: Download progress bar now shows real-time download speed (e.g. 42.5 MB/s) alongside the percentage, matching the upload speed display

🐛 Bug Fixes ​

  • docker: Fixed Docker healthcheck showing unhealthy despite a running server - replaced wget (not available in Alpine) with Node.js fetch and increased start period to 30s
  • web: Upload progress bar now reflects actual end-to-end upload progress instead of encryption speed - progress updates after each chunk is fully uploaded (including server-to-S3 forwarding)

🔒 Security ​

  • server: CSP connect-src header is dynamically extended to allow client fetches to the configured S3 endpoint

🎨 Improvements ​

  • server: Introduced StorageBackend interface with adapter pattern for pluggable storage implementations
  • server: Optimized S3 multipart upload performance - configurable part size (default 25MB) and parallel part uploads (default 4 concurrent) to reduce round-trip overhead

📝 Documentation ​

  • docs: Added S3 storage backend section to environment variables reference with configuration table, S3_PUBLIC_URL, and provider examples (R2, MinIO)
  • docs: Added S3 variable definitions to developer environment reference (STORAGE_BACKEND, S3_BUCKET, S3_REGION, S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_FORCE_PATH_STYLE, S3_PRESIGNED_EXPIRY, S3_PUBLIC_URL, S3_PART_SIZE, S3_CONCURRENCY)
  • docs: Updated architecture diagram and data storage section to reflect S3 backend and download flow
  • docs: Added S3 CORS configuration guide with examples for Cloudflare R2, AWS S3, and MinIO
  • docs: Added S3 Docker Compose example with S3_PUBLIC_URL to self-hosting guide
  • docs: Updated data backups guide with S3 storage note
  • docs: Added S3 storage mention to README and docs homepage feature cards

🐳 Docker ​

  • Image: skyfay/skysend:v2.2.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.1.0 - General UI and Documentation Improvements ​

Released: April 14, 2026

✨ Features ​

  • docs: Added VitePress sitemap generation (/sitemap.xml) for Google Search Console indexing
  • docs: Added Cloudflare Worker that fetches instance data (version, config, enabled services) hourly and caches it in KV
  • docs: Instances page now loads all data from a single cached API endpoint instead of querying each instance individually
  • docs: Instance limits (max file size, quota, expiry, downloads) are now fetched dynamically from each instance's /api/config endpoint
  • docs: Added service filter (All / Files / Notes) to instances page based on each instance's enabledServices configuration
  • docs: Added skeleton loading animation while instance data is being fetched
  • docs: Instance list is now maintained via docs/public/instances.json - users can add instances via pull request
  • docs: Instance cards show separate Files and Notes stats sections, each only visible when the service is enabled
  • docs: Instance cards show "Official" or "Community" badge, with official instances (skysend.app) always sorted first
  • web: Added language switcher dropdown in the navbar with country flag icons via flag-icons library
  • web: Manual language selection is persisted in a cookie so it survives page reloads and sessions
  • web: Added optional custom labels for password notes so users can describe what each password is for

🎨 Improvements ​

  • web: Redesigned theme toggle from a cycling button to a dropdown menu with Auto, Light, and Dark options
  • web: Fixed an UI issue on text and markdown note cards, when they were expanded

🐳 Docker ​

  • Image: skyfay/skysend:v2.1.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v2.0.0 - Encrypted Notes, Text, Passwords, Code Snippets, and SSH Keys ​

Released: April 13, 2026

⚠️ Breaking: All file-related environment variables have been renamed with a FILE_ prefix (e.g. MAX_FILE_SIZE -> FILE_MAX_SIZE). Old names are no longer supported. See the environment reference for the full mapping.

✨ Features ​

  • server: Added encrypted notes API with support for text, password, and code content types
  • server: Added burn-after-reading support for notes via configurable max view count
  • server: Added new NOTE_ environment variables for independent note configuration (NOTE_MAX_SIZE, NOTE_EXPIRE_OPTIONS_SEC, NOTE_DEFAULT_EXPIRE_SEC, NOTE_VIEW_OPTIONS, NOTE_DEFAULT_VIEWS)
  • crypto: Added encryptNoteContent and decryptNoteContent for AES-256-GCM note encryption
  • web: Added tab navigation on upload page (File, Text, Password, Code)
  • web: Added note creation form with content editor, expiry, view limits, and password protection
  • web: Added IndexedDB storage for created notes
  • web: Added note view page with decryption, password prompt, view counter, and burn-after-reading indicator
  • web: Added note API client functions for fetching note info, viewing content, and password verification
  • web: Added My Uploads page filter tabs (All, Files, Text, Passwords, Code, Markdown, SSH Keys) with combined chronological list
  • web: Added note cards in My Uploads with view counter, expiry, QR code, copy link, and delete
  • server: Added ENABLED_SERVICES environment variable to enable/disable file and note services independently
  • web: Upload page and My Uploads page dynamically hide tabs for disabled services
  • server: Added unlimited views option (maxViews: 0) for notes - notes expire only by time, not by view count
  • web: View selector shows "Unlimited" option when 0 is included in NOTE_VIEW_OPTIONS
  • web: Added translations for Spanish, French, Finnish, Swedish, Norwegian, Dutch, Italian, and Polish
  • web: Added syntax highlighting with line numbers for code notes (auto-detects 22 languages)
  • web: Added password generator in the Password tab with configurable length, character types, and entropy display
  • web: Added SSH Key tab with Generate/Paste modes, Ed25519 and RSA (1024/2048/4096) key pair generation, optional passphrase (PKCS#8), and sharing as encrypted note
  • web: Added Markdown mode in Text tab with Plain Text/Markdown sub-toggle, live preview, and rendered Markdown display on note view (GFM support via react-markdown)
  • crypto: Added sshkey as dedicated NoteContentType so SSH key notes display with their own icon and label in My Uploads
  • web: Redesigned Password tab with single-line input fields, per-field password generator toggle, and add/remove support for multiple passwords
  • web: Password note viewer now shows each password individually with separate reveal and copy buttons
  • cli: Added notes support to list, delete, stats, and cleanup commands

🔄 Changed ​

  • server: Renamed all file-related environment variables with FILE_ prefix for clarity
  • server: Cleanup job now also removes expired notes and notes that reached their view limit
  • web: File download URLs changed from /d/:id to /file/:id with automatic redirect from old URLs

🎨 Improvements ​

  • web: Replaced default browser scrollbar with custom styled scrollbar on textareas and code blocks
  • web: Updated footer tagline and browser tab subtitle to reflect file and note sharing
  • web: Removed primary-color border from ShareLink card and NoteView card to avoid confusion with custom color themes
  • web: Added success Tailwind color variable (fixed SkySend green) for the "Upload complete" text so it stays green regardless of custom primary color

📝 Documentation ​

  • docs: Updated environment variables reference with new FILE_ and NOTE_ variable names
  • docs: Added v1 to v2 environment variable migration table to environment reference
  • docs: Updated URL references from /d/ to /file/ in architecture and API docs
  • docs: Added comprehensive API documentation for all 5 note endpoints
  • docs: Updated API index with note endpoints overview table
  • docs: Updated user guide with note creation, viewing, and burn-after-reading instructions
  • docs: Updated README, PHILOSOPHY, and docs landing page branding to reflect file and note sharing
  • docs: Added screenshots page with overview, note types, and My Uploads views

🧪 Tests ​

  • crypto: Added 9 tests for note content encryption/decryption (round-trip, unicode, tampering, nonce uniqueness)
  • server: Added 33 tests for note API routes (CRUD, view counting, burn-after-reading, auth tokens, password verification, size/expiry/view validation)
  • server: Added 4 cleanup tests for note expiry and view limit enforcement
  • server: Added 7 config tests for ENABLED_SERVICES parsing, validation, and cross-field skip logic
  • server: Added 7 route tests for service guard middleware (403 on disabled services)
  • server: Added 3 tests for unlimited views (creation, viewing, cleanup skip)

🐳 Docker ​

  • Image: skyfay/skysend:v2.0.0
  • Also tagged as: latest, v2
  • Platforms: linux/amd64, linux/arm64

v1.0.2 - Patch Release for CORS correction on Health Endpoint ​

Released: April 13, 2026

🐛 Bug Fixes ​

  • server: Added open CORS policy (*) on /api/health endpoint so external sites can fetch instance status

📝 Documentation ​

  • docs: Added website and instances links to README navigation bar

🐳 Docker ​

  • Image: skyfay/skysend:v1.0.2
  • Also tagged as: latest, v1
  • Platforms: linux/amd64, linux/arm64

v1.0.1 - Patch Release for Docker Permissions Issue ​

Released: April 12, 2026

🐛 Bug Fixes ​

  • docker: Replaced recursive chown on /uploads with a non-recursive, fault-tolerant call to prevent startup failures on NFS mounts and read-only filesystems

✨ Features ​

  • docker: Added SKIP_CHOWN environment variable to skip ownership changes on /data and /uploads entirely

🐳 Docker ​

  • Image: skyfay/skysend:v1.0.1
  • Also tagged as: latest, v1
  • Platforms: linux/amd64, linux/arm64

v1.0.0 - First Stable Release ​

Released: April 12, 2026

This is the first stable release of SkySend, marking the completion of the initial development phase and the beginning of production use. The v1.0.0 release includes all core features, security measures, and documentation necessary for self-hosting and public deployment.

✨ Features ​

  • crypto: AES-256-GCM streaming encryption and decryption with 64KB record size
  • crypto: HKDF-SHA256 key derivation with domain-separated keys (fileKey, metaKey, authKey)
  • crypto: Metadata encryption with random IV for filenames, MIME types, and file lists
  • crypto: Password protection via Argon2id (WASM) with PBKDF2-SHA256 fallback
  • crypto: Base64url encoding, constant-time comparison, and utility functions
  • server: Hono-based REST API with streaming upload and download
  • server: Chunked upload flow with init, stream, and finalize endpoints
  • server: SQLite database with Drizzle ORM and WAL mode
  • server: Filesystem storage layer with path traversal protection
  • server: Auth middleware with constant-time token comparison
  • server: Sliding-window rate limiting per IP with X-RateLimit-* response headers
  • server: Upload quota with HMAC-SHA256 hashed IPs and daily rotating keys for privacy
  • server: Quota status endpoint (GET /api/quota) with remaining bytes and reset time
  • server: Health check endpoint (GET /api/health) with version and timestamp
  • server: Automatic cleanup job for expired uploads on startup and at configurable intervals
  • server: Static SPA serving from Vite build output
  • server: Graceful shutdown with 10-second timeout
  • server: Configurable branding via environment variables (title, color, logo, footer links)
  • web: React 19 SPA with Vite, Tailwind CSS, and shadcn/ui components
  • web: Drag-and-drop upload zone supporting files and folders
  • web: Multi-file upload with client-side zip compression (fflate)
  • web: Three-tier download strategy - Service Worker streaming, File System Access API, and in-memory blob fallback
  • web: OPFS-backed decryption pipeline via Web Worker for zero-RAM streaming downloads
  • web: Safari warning for large file downloads exceeding 256 MB
  • web: Configurable expiry time and download limits per upload
  • web: Optional password protection with show/hide toggle
  • web: Upload progress with phase indicator, percentage, and speed display
  • web: Favicon progress circle during active uploads
  • web: Share link page with one-click copy
  • web: Upload history dashboard backed by IndexedDB - no account required
  • web: Download page with metadata decryption, password prompt, and progress
  • web: Quota bar with visual progress indicator
  • web: Skeleton loading states for all pages
  • web: Version number displayed in footer from package.json
  • web: Dark mode with automatic system preference detection
  • web: Internationalization (English, German) with browser language auto-detection
  • web: Responsive design for mobile and desktop
  • web: Toast notification system for success, error, and warning messages
  • cli: list command to show active uploads
  • cli: delete command to remove an upload by ID
  • cli: stats command for storage overview
  • cli: cleanup command to remove expired uploads
  • cli: config command to display server configuration
  • docs: Full documentation site with VitePress
  • docs: Developer guide covering architecture, API reference, crypto internals, and download modes
  • docs: User guide with installation, configuration, security, and self-hosting sections
  • infra: Multi-stage Dockerfile based on Node 24 Alpine
  • infra: Docker Compose with volume persistence and health checks
  • infra: pnpm monorepo with workspaces
  • infra: ESLint and Prettier configuration
  • infra: TypeScript strict mode across all packages
  • infra: Vitest test suite with unit and integration tests

🐳 Docker ​

  • Image: skyfay/skysend:v1.0.0
  • Also tagged as: latest, v1
  • Platforms: linux/amd64, linux/arm64