Changelog
All notable changes to SkySend are documented here.
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.ch/install.shandskysend.ch/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.ch) 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