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 updatetogether with the server update.
⚠️ Breaking:
DEFAULT_THEMEnow selects the visual theme (graphite,auroraormidnight) and the color scheme moved to the newDEFAULT_COLOR_SCHEME. An instance that still setsDEFAULT_THEMEtodark,lightorsystemrefuses to start and names the value to set inDEFAULT_COLOR_SCHEMEinstead.
💙 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/configreportsnoteBlocks, so a client can tell whether the server accepts notes made of blocks. - server:
DEFAULT_TABacceptsnote, andtext,password,codeorsshkeyopen the note tab with that block already added. - server:
DEFAULT_THEMEpicks the theme of the web app and the newDEFAULT_COLOR_SCHEMEsets 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_OPTIONSandFILE_REQUEST_DOWNLOAD_OPTIONSlist how many uploads a request takes and how often each can be downloaded, withFILE_REQUEST_DEFAULT_UPLOADSandFILE_REQUEST_DEFAULT_DOWNLOADSas the defaults. The requester picks both per request. - server:
FORCE_REQUEST_PASSWORDrequires a password on the inbox of every file request, apart fromFORCE_FILE_PASSWORD. - server:
OIDC_PROTECT_REQUESTSputs creating a file request behind the OIDC login, apart from files and notes. Unset, it protects requests whileOIDC_PROTECT_FILESorOIDC_PROTECT_NOTESis on. - server:
GET /api/request/limittells 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,cleanupandconfigcover 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_COLORwas 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 updatecannot 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-serverto 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_FILESis 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
nullno 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 notewithout 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 --jsonno longer prints the auth and owner tokens of uploads. - cli:
config --jsonmasks 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/ogimage 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
sharpandbrace-expansionand added overrides forpostcss-selector-parserunder@tailwindcss/typographyandargparseunderjs-yamlto 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_SERVICESnow defaults tofile,note,request. Instances that set it explicitly addrequestto 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
blockscontent 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:viewno longer lists options it does not have. - docs: Documented
DEFAULT_THEMEandDEFAULT_COLOR_SCHEMEin both environment variable references. - docs: Described the three themes and how
CUSTOM_COLORapplies 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
devbranch. - 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_TABand thenoteBlocksflag. - 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
nullframe, 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_COLORand 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/cryptoand@skysend/note-formatare 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-versionfile pins Node 24 for local development, the version CI and the Docker image use. - infra:
scripts/setup-dev-macos.shsets 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
mainnow 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.devis ignored by git, so local values and secrets stay out of the repository. The setup scripts create.env.devfor 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-wsis 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
maincan 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
v2.12.1 - Link Preview and Branding Improvements, Security Updates and Bug Fixes
Released: September 17, 2026
✨ Features
- server: Added
CUSTOM_OG_IMAGEandCUSTOM_OG_IMAGE_STYLEto set the image and layout of link previews, and a PNG or JPEGCUSTOM_LOGOis 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_TITLEinstead of always showing SkySend. - server: A
CUSTOM_TITLEcontaining 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_IMAGEvariable, which is replaced byCUSTOM_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.envfrom 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_LOGOno 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-serverto 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
sharpandbrace-expansion, raised thepostcssoverride, and removed the unusedautoprefixerdependency 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_LOGOrefers 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:checknow lists outdated dependencies per workspace package with a summary, instead of one merged table that ends inCommand 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.denybypass 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>esbuildandwrangler>esbuild(>=0.28.1),@babel/core(>=7.29.6), and updated thevitepress>viteoverride to>=6.4.3 <7to 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-serverto 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
typecheckscripts to every TypeScript package sopnpm validateand CI now type-check the entire monorepo, not just the web app, preventing type errors from slipping through to the Docker build. - infra: The
typechecktask 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.tsto 100% coverage. - web: Brought
useUploadanduseUploadHistoryto 100% coverage. - client: Brought
lib/config.tsto 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 -
deriveKeyFromPasswordPbkdf2andPBKDF2_ITERATIONSare deleted,deriveKeyFromPasswordnow requires an Argon2id function and no longer acceptsundefinedas a fallback. All pre-v2.5.0 PBKDF2-protected uploads have expired. - web: Removed
"pbkdf2"from thepasswordAlgoZod enum in the download and note-info API schemas - responses containing this value are now treated as invalid. - client: Removed
"pbkdf2"from thepasswordAlgoZod enum in the file-info and note-info API schemas, and removed the PBKDF2 decryption branch fromprepareDownload. - server: Removed
"pbkdf2"from the acceptedpasswordAlgoupload header values - any upload attempt with this algorithm now returns HTTP 400. - crypto: Removed
ARGON2_PARAMS_LEGACY(19 MiB / 2 iterations) and theargon2Paramsoptional parameter fromderiveKeyFromPassword- 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 thepasswordAlgoZod enum in the download and note-info API schemas - responses with the legacy algorithm are now treated as invalid. - client: Removed
"argon2id"from thepasswordAlgoZod enum and the legacy decryption branch inprepareDownload- the function now always uses the current Argon2id parameters. - server: Removed
"argon2id"from the acceptedpasswordAlgoupload 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 toshowKnownErrorToast()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 toshowKnownErrorToast()for misconfiguredALLOWED_ORIGINSWebSocket 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.debugcalls 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 aBroadcastChannel("skysend-download")for all SW-to-main-thread and config messages. - web: Increased the Service Worker ReadableStream
highWaterMarkfrom2to8to 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/webtest coverage to 100% - added tests forisFirefox,isDevToolsOpen, andgetBrowserInfoin utils, dismiss-all and multi-toast UPDATE inuseToast,loadInfonon-ApiErrorpath andview()without priorloadInfoinuseNoteView, transport worker messages inuseUpload, and 410/multi-upload branch coverage inuseUploadHistory. Added/* v8 ignore next */annotations on dead-code and unreachable branches inuseUpload,useToast, anduseNoteView. - client: Brought
apps/clienttest coverage to 100% - added token storage tests (array JSON, corrupt JSON,clearStoredToken) toconfig, rejection-sampling branch test topassword-generator. Added/* v8 ignore next */annotations on dead-code branches inprogressandoidc.
🐳 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-Controlforindex.htmlanddownload-sw.jsfromno-cache, must-revalidatetono-store.no-cachepermits 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-storeis 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-storemiddleware 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/catcharound bothcrypto.subtle.decryptcalls in the Service Worker. An unauthenticated or corrupted encrypted record caused the decrypt call to throw, which rejected thepull()promise and let Firefox mark the download as completed with the incorrect partial file size. The stream is now explicitly errored viacontroller.error()and adl-errorbroadcast is sent to the UI on failure. - web: Added a
cancelledflag to the Service Worker'scancel()handler, checked at everyawaitboundary inpull(). Previously, callingcancel()whilepull()was awaitingreadMore()letpull()continue executing on an already-cancelled stream, which could reachcontroller.close()and throw.
🎨 Improvements
- web: Changed the Service Worker ReadableStream from
highWaterMark: 0tohighWaterMark: 2to address reported Firefox browser freezes during large-file downloads. WithhighWaterMark: 0,pull()is only triggered by a pending consumerread(), which may cause Firefox's download pipeline to block until a decrypted record is ready. WithhighWaterMark: 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.jsis now served withCache-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. Withno-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 everypull()call - generating ~5 GB of unnecessary allocations for a 2.4 GB file. The new implementation pushes incomingreader.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
Uint8Arrayallocation inreadFromBuf()by introducing a pre-allocatedscratchRecordbuffer (ENCRYPTED_RECORD_SIZEbytes) that is reused across allpull()calls.crypto.subtle.decryptcopies its input synchronously before returning the Promise, making the buffer safe to reuse as soon as theawaitresolves. 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 thehighWaterMark: 0pull-based contract more strictly and loses its backpressure state whenpull()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 earlyreturn, which gives Firefox's download manager time to consume each chunk before the next one is produced.
🧪 Tests
- web: Added
console.debugdiagnostic logging to the Service Worker'spull()andreadMore()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-Lengthis >= 2^31 bytes, causing the download manager to stall or cancel at the end. TheContent-Lengthheader 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-donebroadcast was fire-and-forget - Firefox terminates the SW as soon aswaitUntilresolves, abandoning the still-pendingclients.matchAll()call before the message is delivered. All completion broadcasts are now awaited beforestreamDone()is called.
🎨 Improvements
- server: The SPA entry point (
index.html) is now served withCache-Control: no-cache, must-revalidate. This prevents browsers and reverse proxies (e.g. Traefik with a caching middleware) from serving a staleindex.htmlafter a deployment. Without this, a cached oldindex.htmlreferencing 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 (nodownloadattribute), 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:imageandtwitter:imagefor rich link previews on Discord and other platforms. The image defaults to/logo.pngand can be overridden with theVITE_OG_IMAGEenvironment variable at build time. - web: The page
<title>and OG/Twitter title tags now reflectCUSTOM_TITLEat runtime. WhenCUSTOM_TITLE=MyShareis set, the preview title becomes "MyShare | Encrypted File & Note Transfer" instead of "SkySend | ...". - server: The SPA fallback route now injects
CUSTOM_TITLEinto the servedindex.htmlat 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.chtoskysend.app. All documentation, API references, and examples have been updated to reflect the new domain. The oldskysend.chURLs will continue to work and redirect toskysend.appfor the foreseeable future. - server: Added
CUSTOM_REPORT_URLenvironment 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-16tow-20so 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 oftext-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 (
#f6f8fabackground) and the Arta dark theme in dark mode (#222background), 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_PROVIDERenv var. File uploads and note creation can each be independently protected usingOIDC_PROTECT_FILESandOIDC_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_FILESorOIDC_PROTECT_NOTESenabled, the CLI automatically opens a browser for login before uploading or creating notes. Session tokens are stored per-server in~/.config/skysend/tokens.jsonand reused until they expire. Newauthsubcommands (login,logout,status) allow explicit session management.
🔒 Security
- infra: Added pnpm overrides for
@esbuild-kit/core-utils>esbuild(>=0.25.0) andvitepress>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.maphandling). - infra: Updated all dependencies to latest versions -
vite8.0.13,tailwindcss+@tailwindcss/vite4.3.0,better-sqlite312.10.0,@aws-sdk/*3.1046.0,i18next26.1.0,lucide-react1.16.0,tailwind-merge3.6.0,vitest+@vitest/coverage-v84.1.6,tsx4.22.0, and others.
🎨 Improvements
- web: The password input placeholder no longer says "(optional)" when
FORCE_FILE_PASSWORDorFORCE_NOTE_PASSWORDis 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
titleattribute.
🧪 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/verifyPkceJwtreturning null on malformed payloads, theString(err)branch in the discovery warm-up handler, and empty-claims fallback paths in all four provider adapters, bringingauth/session.ts,routes/auth.ts, and allauth/adapters/*.tsto 100% branch coverage. - client: Added tests for the
getFreePorterror path and theperformOidcLogincatch block, bringinglib/oidc.tsto 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, andauth statusto 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/typographyplugin was installed but not loaded via@plugininindex.css, soprosestyles 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_COLORbranding 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
honofrom4.12.15to4.12.18to 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 (
react19.2.6,zod4.4.3,react-router-dom7.15.0,vite8.0.11,@aws-sdk/*3.1045.0,eslint10.3.0,dompurify3.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_THEMEenv var to set the default UI theme (dark,light, orsystem) for users who have not stored a preference. The user's own choice always takes precedence. (#35) - server: Added
DEFAULT_TABenv var to set the default upload tab (file,text,password,code, orsshkey). Falls back to the first enabled tab if the configured tab is unavailable. (#35) - server: Added
FORCE_FILE_PASSWORDenv var - when set totrue, all file uploads must be password-protected. Enforced on both the server (HTTP 400) and in the UI. (#35) - server: Added
FORCE_NOTE_PASSWORDenv var - when set totrue, 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_PASSWORDandFORCE_NOTE_PASSWORDare now respected by the CLI client. When enforced, the CLI automatically prompts for a password if-pwas 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
PORTis set - theHEALTHCHECKcommand hardcoded port 3000 instead of reading thePORTenvironment 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 deniedon startup whenDATA_DIRorUPLOADS_DIRare set to a custom path - the entrypoint previously hardcoded/dataand/uploadsformkdirandchown, so custom paths were never created or made writable. The entrypoint now uses the actualDATA_DIRandUPLOADS_DIRenvironment 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 usewhenPGIDis set to a value other than the default1001-busybox'sdelgroupcannot remove a group that still has members, so the subsequentaddgroupalways failed. The entrypoint now usessedto update the GID in-place in/etc/groupand/etc/passwd, matching the existing approach used forPUID.
🐳 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 CSPin browsers behind reverse proxies that forward CSP headers - added'wasm-unsafe-eval'toscript-srcto 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_LOGOinimg-srcwhen 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 themainbranch - these workflows are only meant to run on feature branches and tags, not onmainwhere they cause noise and duplicate runs alongside the genericvalidate.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()ignoringdefaultServerand using the legacy top-levelserverfield instead - if both fields exist inconfig.json(e.g. after migrating to multi-server), the TUI and all commands now correctly preferdefaultServer. - 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 updatefailing on Windows with "Permission denied" even as Administrator. Windows locks running.exefiles, sofs.renameSyncalways threwEPERM. The fix spawns a detached, hiddencmd.exebatch script that waits 2 seconds (until the current process exits) then moves the downloaded binary into place withmove /y. - client: Fixed
install.ps1hanging silently during download. PowerShell's default$ProgressPreference = 'Continue'makesInvoke-WebRequestup to 100x slower and shows no feedback in many terminal environments. The script now sets$ProgressPreference = 'SilentlyContinue'and printsDownloading <file>... done (X.X MB)andVerifying checksum... okstep messages instead. - client: Fixed
install.shshowing no output during binary download.curl -fsSLandwget -qwere fully silent. The binary download now usescurl --progress-bar(shows a#####bar on stderr) andwgetwithout-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 wasfast-xml-parser@5.7.1introducing a regression where numeric character references (e.g.
) in XML responses could no longer be parsed. Updatedfast-xml-parseroverride to>=5.7.2which restores correct behavior. - server: Set
requestChecksumCalculationandresponseChecksumValidationtoWHEN_REQUIREDon the S3 client. AWS SDK v3 >=3.679 defaults toWHEN_SUPPORTED, causing proactive CRC checksum headers that can trigger provider-specific XML parsing issues.
🔒 Security
- infra: Added
pnpm.overridesforpostcss(>=8.5.10) to patch a moderate XSS vulnerability (GHSA-qx2v-qp2m-jg93) in transitive dependencies viaautoprefixer
🎨 Improvements
- server: Updated
@hono/node-serverfrom v1 to v2 - same public API, up to 2.3x faster body parsing via optimized direct Node.jsIncomingMessagereads, URL construction fast-path, andbuildOutgoingHttpHeadersoptimization - 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
eslintand@eslint/jsfrom v9 to v10, andcommanderfrom v13 to v14 - no API changes required, fixed two neweslint:recommendedrules (no-useless-assignmentin upload chunking code,preserve-caught-errorin 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/, andCOPY workers/instances/package.jsonto the Dockerfile build stage sopnpm install --frozen-lockfilecan resolve all workspace packages (including thefile:stub dependency in@skysend/client) beforeCOPY . .
🗑️ Removed
- server: Removed
S3_PUBLIC_URLenvironment 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, anddocs/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-Afterheader, IPs stored as ephemeral HMAC-SHA256 hashes, configurable viaPASSWORD_MAX_ATTEMPTSandPASSWORD_LOCKOUT_MS
🔒 Security
- server: Updated
honofrom4.12.12to4.12.14to fix HTML injection via improperly handled JSX attribute names in SSR (GHSA-458j-xx4x-4375) - infra: Added
pnpm.overridesforesbuild(>=0.25.0),vite(>=6.4.2), andfast-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 passexpectedPlaintextSize(from authenticated metadata) to detect a malicious server delivering fewer records than were encrypted - server: Added
Referrer-Policy: no-referrerHTTP header to prevent URL fragments from leaking viaRefererheader in misconfigured or future environments - web: Added
<meta name="referrer" content="no-referrer">toindex.htmlas defense-in-depth for the referrer policy - web: URL fragment (encryption key) is now removed from the browser address bar via
history.replaceStateonce 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
dangerouslySetInnerHTMLin code notes - defense-in-depth against any future upstream hljs vulnerability - web: Added
rehype-sanitizeplugin 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.replaceStateinNoteViewimmediately 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 fromX-Forwarded-Forwas 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_URLconfigured - 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-capablemeta tag with the standardmobile-web-app-capableequivalent - eliminates browser console warning, the PWA manifestdisplay: standalonealready 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- setstitle,description,url,source,version,revision,created,vendor, andlicensesfor 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 acrossmeta.ts,password.ts, andnote.tsroutes. - server: Added 5 tests for
startCleanupJob(interval, stop function, logging, error recovery) - bringingcleanup.tsto 100%. - server: Brought
upload-validation.tsandquota.tsto 100% - covers allcheck(),getStatus(), 413 middleware, DB key restoration, and interval behavior (rotation, expiry cleanup). - infra: Added
vitest.config.tsforserverandclient, updatedvite.config.tsforweb- all with scopedcoverage.includeand 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-v8andtest:coveragescripts toserver,web,crypto, andclientpackages for coverage report generation in LCOV format - web: Added 57 unit tests across
lib/utils,lib/password-generator, andlib/upload-store- covering formatting functions,isSafaridetection,generatePassword/calculateEntropy, and full CRUD/sorting for IndexedDB upload and note storage (using an in-memoryidb-keyvalmock) - client: Added 93 unit tests across
lib/progress,lib/url,lib/password-generator,lib/config, andlib/history- covering all formatting/parsing utilities, fullparseShareUrl/buildShareUrllogic including edge cases, password generation, config file lifecycle with filesystem isolation viatmpdir, and history CRUD with expiry cleanup
🔧 CI/CD
- infra: Updated
validate.ymltest job to run with coverage and upload reports to Codecov viacodecov/codecov-action@v5 - infra: Added
codecov.ymlwith project and patch coverage status checks - crypto: Added 14 new unit tests covering previously untested security-critical paths:
validateMetadataerror branches (invalid JSON, null payload, unknown type, malformed archive entries, negative sizes, empty names, missing MIME type),deriveKeyFromPasswordArgon2input 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. Addedvitest.config.tsto exclude the re-export barrelsrc/index.tsfrom 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-wsCLI flag, TUI Settings menu, or\"websocket\": falsein config file- client: Addedskysend lscommand to list upload and note history with age, expiry, and size - supports--server,--all, and--jsonflags
🎨 Improvements
- client: Connection error screen now offers recovery options - press
sto select another server orrto retry instead of requiring Ctrl+C exit - client: Connection errors now show a user-friendly message (
Server <url> is not reachable) instead of the rawfetch failederror - client:
skysend confignow 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--externalflag 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.shandskysend.app/install.ps1redirects 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/clientCLI 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: trueon 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
skysendtoskysend-clito 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/wsthat 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_WSenvironment variable (default:true) to enable or disable the WebSocket upload transport - server: Added
FILE_UPLOAD_WS_MAX_BUFFERenvironment 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
Originheader 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_WSandFILE_UPLOAD_WS_MAX_BUFFERto 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_CHUNKSenvironment 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_LIMITenvironment variable (default:0= unlimited) to cap upload throughput per session - supports human-readable values like100MB(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_CHUNKSandFILE_UPLOAD_SPEED_LIMITto user-guide environment variables page with new "Upload Performance" section - docs: Added
FILE_UPLOAD_CONCURRENT_CHUNKSandFILE_UPLOAD_SPEED_LIMITto 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/apisub-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
BlobtoArrayBuffer- 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
indexquery 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_BACKENDenvironment variable to switch betweenfilesystem(default) ands3storage - 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_URLfor 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_URLis 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_SIZEandS3_CONCURRENCYenvironment 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
unhealthydespite a running server - replacedwget(not available in Alpine) with Node.jsfetchand 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-srcheader is dynamically extended to allow client fetches to the configured S3 endpoint
🎨 Improvements
- server: Introduced
StorageBackendinterface 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_URLto 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/configendpoint - docs: Added service filter (All / Files / Notes) to instances page based on each instance's
enabledServicesconfiguration - 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-iconslibrary - 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
encryptNoteContentanddecryptNoteContentfor 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_SERVICESenvironment 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
0is included inNOTE_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
sshkeyas dedicatedNoteContentTypeso 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, andcleanupcommands
🔄 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/:idto/file/:idwith 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
successTailwind 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_andNOTE_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_SERVICESparsing, 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/healthendpoint 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
chownon/uploadswith a non-recursive, fault-tolerant call to prevent startup failures on NFS mounts and read-only filesystems
✨ Features
- docker: Added
SKIP_CHOWNenvironment variable to skip ownership changes on/dataand/uploadsentirely
🐳 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:
listcommand to show active uploads - cli:
deletecommand to remove an upload by ID - cli:
statscommand for storage overview - cli:
cleanupcommand to remove expired uploads - cli:
configcommand 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