Releases: axpdev-lab/aeroftp
AeroFTP v3.6.10
[3.6.10] - 2026-04-28
MEGA Native canonical crypto, 2FA modal hookup, TOTP QR, mouse Back button
Same-day rollup on top of v3.6.8. The originally-tagged v3.6.9 build was deleted before a Linux release was published because the GitHub Actions Snap step was dying silently during LXD setup on three consecutive reruns; the work that was already in v3.6.9 (2FA modal hookup, TOTP QR, mouse Back button) is rolled into this v3.6.10 cut together with a critical MEGA Native crypto interop fix that landed in parallel. The Snap workflow step is marked continue-on-error: true so the rest of the Linux release pipeline (deb / rpm / AppImage signing and upload) is no longer blocked when the Snap action-build crashes; Snap Store stays on v3.6.8 until we either pin a newer action ref or move the Snap build to its own job (tracked for v3.7.0).
Fixed
- MEGAcmd login on Linux/macOS (critical) — saved-profile connect against the MEGAcmd backend was failing every time on Unix with
Extra args required in non-interactive mode. Usage: login [...] email password | exportedfolderurl#key | session. The Unix path was passing only the email as a CLI arg and piping the password to stdin, on the assumption (carried forward from AUTH-01 audit notes) that this kept it out ofpslistings. In realitymega-loginis a non-interactive one-shot wrapper that talks to the backgroundmega-cmd-serverand does not read stdin at all in that mode; the stdin trick only worked inside the interactivemega-cmdshell, which AeroFTP never invokes. Fixed by passing email + password as CLI args on every platform, matching what rclone, the official MEGA Sync, and every other MEGAcmd integration already does. The briefpsexposure window during login (~1s) is the same surface area that env var or password-file alternatives would have, so there is no real security regression. Reproduced by @aleimob on a fresh Ubuntu install with a saved MEGA-CMD profile againstdev@aeroftp.app. - MEGA Native upload interoperability with MEGA Web (critical) — file node keys uploaded by AeroFTP through the MEGA Native API were stored in a non-canonical layout (raw 16-byte file key followed by nonce + meta-MAC), so AeroFTP could decrypt its own uploads but MEGA Web, MEGA Mobile, and any other official MEGA client derived the AES key from the canonical obfuscated layout and decrypted the payload with the wrong key, producing what looked like ciphertext-on-display. The fix in
mega_crypto.rs::pack_node_key()now stores the MEGA-compatible obfuscated 32-byte node key,unpack_node_key_with_mac()derives the AES key by XOR-ing the first 16 bytes against the nonce / meta-MAC half,compute_attr_key()uses the canonical key for file attributes, andmeta_mac()condenses the chunk MAC with the required[mac[0..4] ^ mac[4..8], mac[8..12] ^ mac[12..16]]XOR pairs. Aunpack_node_key_legacy()fallback is kept so AeroFTP can still read files uploaded by the buggy older builds; download paths verify the canonical MAC first, then fall back to the legacy layout if needed. Existing legacy uploads must be re-uploaded if they need to open in MEGA Web (MEGA Web cannot use the legacy fallback). Independently verified end-to-end withmegajsdecoding the file name and plaintext from a fresh share link, and visually verified by the user in MEGA Web. - 2FA prompt modal did not open from saved-card click (issue #128 follow-up) — saved-card connect goes through
onSavedServerConnectand tab reconnect goes throughswitchSession. Neither path calledtryShowTwoFactorPrompt, so the modal advertised in v3.6.8 never appeared when users clicked a saved MEGA / Filen / Internxt card whose persisted session had expired. The dispatcher is now wired into all three catch handlers; pattern matching against the backend's E_MFAREQUIRED / ENOTOKEN messages is shared so the matching surface is identical across paths. Test reproduction confirmed by the original reporter (@EhudKirsh).
Added
- Mouse Back button (button code 3) closes the topmost modal — the side button on gaming and productivity mice (the one that fires
event.button === 3andevent.buttons === 8in the HTML mouse events spec) now triggers a synthetic Escape keydown event that bubbles to the topmost open modal, dialog, dropdown or popover. Every dialog in the app already wires Esc (TwoFactorPromptDialog, HostKeyDialog, OverwriteDialog, SettingsPanel, VaultPanel, AISettingsPanel, ConnectionScreen, the delete-profile confirmation, the Quick Connect form, etc.), so re-routing Back through the same channel gives correct stacked-modal behavior with zero per-component churn: the topmost handler closes and self-removes, the next Back closes the next layer, all the way back to the My Servers home. Implemented as a singleuseMouseBackButton()hook mounted at the App root: listens formousedown,mouseupandauxclickin the capture phase, suppresses the WebKitGTK history-back default that was beginning to fire on recent builds, and synthesizes the Escape onmouseupso the gesture timing matches what the user expects on releasing the side button. (reported by @EhudKirsh) - AeroFTP master password TOTP setup QR code — Settings > Security > Two-Factor Authentication > Setup now renders the actual QR code (
qrcode.reactQRCodeSVG, level M, 180px on a white tile so it scans cleanly under the Cyber / Tokyo Night themes) instead of just the otpauth URI as copyable text, so the user can point Authy / Google Authenticator / 1Password / Bitwarden directly at the dialog. Account name in the URI changed from "AeroFTP Vault" to "Desktop 2FA" because Authy was rendering the entry as the awkward "AeroFTP : AeroFTP Vault" duplication. The URI also now carries a Google-extensionimage=https://docs.aeroftp.app/web-app-manifest-512x512.pngparameter so authenticators that honor it (FreeOTP+, Yubico Authenticator, Bitwarden, recent Google Authenticator) can show the AeroFTP logo. Authy ignoresimage=and pulls icons from a Twilio-internal database, so an AeroFTP logo there would require a separate submission to Twilio support; until then Authy will fall back to its generic icon, which is a vendor limitation, not a URI problem.
Changed
- Agent / MCP / CLI internal cleanup pass — coordinated set of refactors across
agent_session.rs,ai_core/{agent_tools, gui_tools, tools}.rs,ai_tools.rs,bin/aeroftp_cli.rs(+326 lines, the largest single delta),cross_profile_transfer.rs,lib.rs,mcp/tools.rs,profile_auth_state.rs,providers/{azure, jottacloud, xml_text}.rs, andspeedtest.rs. Same-batch cleanup that landed alongside the MEGA Native crypto fix; behavior-preserving changes that improve consistency across the agent, MCP and CLI surfaces. No public API change.
Notes on accessibility
The Back-button hook is the first slice of a broader keyboard-and-pointer accessibility pass that landed on the v3.6.x roadmap after @EhudKirsh's feedback on issue #133. The remaining slices (full Tab-order audit on the Settings panel, Arrow-key navigation between My Servers cards, Enter to connect / Shift to multi-select, and "Forward mouse button (button code 4) → repeat last action") are scheduled for v3.7.0 because each one needs its own focus-management review across the 42 modals in the app, and shipping them piecemeal would create gaps where some screens listen and others do not. Tab and Enter already work where the underlying HTML controls are focusable, so the gap is concentrated in custom-rendered grids and chip lists rather than in form inputs.
Notes on the v3.6.9 tag
A v3.6.9 tag was briefly created earlier in the day on commit 78c322f with the modal-hookup / QR / mouse-back work. The Linux leg of its release pipeline failed three consecutive times because of a deterministic silent crash in snapcore/action-build@3bdaa03e during LXD setup, with no log output past the AppImage repackaging step. No Linux artifacts were ever published on that tag (only Windows .msi / .exe and macOS .dmg made it to the GitHub release page); the tag and the partial release were both deleted before any Linux user could install from them. v3.6.10 supersedes that effort with the same code plus the MEGA Native crypto interop fix and the workflow change that allows the rest of the Linux pipeline to ship even when the Snap step crashes.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.6.8
[3.6.8] - 2026-04-28
Community wishlist quick wins + MEGA 2FA + Filen TOTP persistence
A focused patch release built around the v3.6.8 wishlist thread (issue #133) plus two long-standing items from issue #128 (MEGA 2FA TOTP and the Filen TOTP-persisted-after-save bug). Twelve community-reported items land together so the rest of the thread can be triaged on a fresh baseline. No MCP surface changes; the VS Code extension does not need a bump.
Fixed
- Yandex Disk preset "Docs" link 404 — the registry entry for
yandexdisk-webdavpointed atyandex.com/support/disk-desktop-windows/start/webdav-client.html, which no longer exists. Repointed toyandex.com/support/disk/(verified 200 OK). (reported by @EhudKirsh) - WebDAV Edit form helper links — the provider footer with Create Account / Docs / Generate password buttons was hidden in Edit mode via a
editingProfileIdguard, so users editing a Yandex / OpenDrive / Koofr profile could not jump back to the docs or password generator. The guard is gone; the footer renders the same on first connect and on Edit. (reported by @EhudKirsh) - WebDAV Edit form Server Endpoint URL editable in Edit — the Server Endpoint URL and Port fields were locked on first connect for pre-configured providers but became editable on Edit, which was easy to break by accident. Both fields are now
readOnlywhenselectedProvider.defaults?.serveris set, with a muted visual state. (reported by @EhudKirsh) - OneDrive logo misshapen at small sizes — the previous SVG layered three paths on a 24x24 box, producing an outline that did not look like the canonical Microsoft cloud icon, especially in the Discover Services grid. Redrawn with the standard 4-cloud composition on a 32x24 viewBox, four fill stops. (reported by @EhudKirsh)
- Speed Test modal "Run comparison" button cropped at large font sizes — the modal used
pt-[5vh]+max-h-[90vh], leaving the footer below the fold for users on 19 / 22 px font sizes. Vertical budget expanded topt-[2vh] pb-[2vh]+max-h-[96vh]. (reported by @EhudKirsh) - Activity Log badge counter ignored the active filter — the bottom-bar Log badge counted every emitted event, so it crept up to 99+ even when the panel was filtered to Errors only. The panel now persists
filterTypeandshowCloudSyncin localStorage and broadcasts CustomEvents on every change; App.tsx subscribes and recomputes the badge using the same filter predicates as the panel. (reported by @EhudKirsh) - Provider icons in the Choose Icon dialog silently failed to select — Hetzner, MinIO, Koofr, FileLu, Blomp, OpenDrive, FeliCloud, Pixelunion, Aspnix, DriveHQ, Quotaless, Jianguoyun, Yandex Disk, Immich and other PNG-backed provider logos render as
<img>, butreactLogoToSvgDataUrl()searched for an<svg>and returnednull, so the click was a no-op. Added an<img>fallback that returns the image src as the data URL — works unchanged with the existingcustomIconUrlconsumer. (reported by @EhudKirsh) - Custom icons "No custom icons yet" placeholder shown next to an existing icon — the empty-state copy contradicted the Current / On-server section above when a user had already picked an icon. The placeholder is now suppressed when either is non-empty. (reported by @EhudKirsh)
- Filen TOTP code persisted across reconnects (issue #128) — the 6-digit 2FA Code field was saved into the profile's
options.two_factor_codeon connect, so a reconnect a few minutes later replayed yesterday's code and the API rejected it with "Wrong Two Factor Authentication code". TOTPs are single-use and rotate every 30 seconds; the saved-profile path now stripstwo_factor_codefromoptionsToSaveon both Edit and New Server flows, the same way Filen / Internxt / MEGA web clients ask for the code on every login. (reported by @EhudKirsh)
Added
- Hide username on My Servers cards — new toggle in the toolbar (AtSign icon, persists across sessions as
aeroftp_hide_server_username). When active, theuser@hostsubtitle becomes host-only across grid and list views, freeing visual space and removing the email leak. (reported by @EhudKirsh) - Drag and drop on Custom icons upload box — files dropped on the box are ingested through the same
ingestIconBytes()path as the file picker. Visual feedback while a file is hovered: solid blue border and a "Drop file to upload" label. Allowed extensions: SVG, PNG, JPG / JPEG, GIF, WEBP, ICO. (reported by @EhudKirsh) - Custom icons delete confirmation — removing an icon from the library now goes through a confirmation prompt that includes the icon's own name in the message, same surface as the existing profile delete dialog. (reported by @EhudKirsh)
- Activity Log multi-select filter — the single-pick
<select>dropdown is gone, replaced by a button + popover with one checkbox per operation (Connect, Disconnect, Upload, Download, Delete, Restore, Navigate, Errors) plus an "All" reset entry at the top. Empty selection is the new "show all" state, so users can mix Errors + File operations without losing one when they pick the other. State migrates automatically from the legacy single-pick value. (reported by @EhudKirsh) - MEGA 2FA TOTP support (issue #128) — Quick Connect MEGA gains a "2FA Code" field below Password, mirroring the Filen / Internxt block (6-digit numeric,
inputMode="numeric",autoComplete="one-time-code"). Backend:MegaConfigextended withtwo_factor_code: Option<String>deserialized fromextra["two_factor_code"]; bothlogin_v1andlogin_v2inject"mfa": <code>into theus(login) request when present. Server returns-26(E_MFAREQUIRED) when the field is absent on a 2FA-enabled account, and-9/-16on a wrong code; both surface to the user as the same generic auth failure as a wrong password, with no extra plumbing required. (reported by @EhudKirsh)
Changed
- Six new i18n keys translated to all 47 languages:
savedServers.showUsername,savedServers.hideUsername,iconPicker.confirmDelete,iconPicker.dropHere,activityPanel.filterMenu.title,activityPanel.filterMenu.selected. Validation: 46/46 locales clean, 0 missing, 0 orphan, 0 placeholder.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.6.7
[3.6.7] - 2026-04-27
Share-link reliability and UX paper cuts
A focused patch release. The headline is a silent regression on MEGA Native share-link generation that surfaced during an interoperability test session against MEGA's own infrastructure: the URL we built used the internal node handle instead of the public handle returned by the API, so links opened on mega.nz with "File cannot be accessed". Same encryption key, wrong path component, no error from our side. The fix lands together with a CLI verification flag and a few small UX wins voted up on the v3.6.7 wishlist.
Fixed
- MEGA Native share link —
aeroftp-cli link --profile <mega-native>was buildingmega.nz/file/<internal-node-handle>#<key>instead ofmega.nz/file/<public-handle>#<key>. The encryption key portion was always correct, only the 8-character handle in the path was wrong, so generated links opened on a "File cannot be accessed" page onmega.nz. Fix: capture the public handle returned by the MEGA APIl(export) command and use that in the URL.mega_native.rs::create_share_link. - S3 explicit endpoint precedence — when a saved server profile has both a configured S3 endpoint and a host populated by the GUI, the explicit endpoint now wins. Also detects bucket-addressing errors and reports them as
ProviderError::Configurationso they are actionable on the CLI side. (commit27d2ccc8, originally merged via #132 into the local tree, formally rolled into this release.) - Edit Server form, S3 Path-Style toggle — the Path-Style URLs checkbox was wired into QuickConnect via
ProtocolSelectorbut never plumbed into Settings > Servers > Edit Server, so users could not flip the toggle on a saved profile after creation. Now exposed in the S3 section of Edit Server, sharing the existingprotocol.pathStylei18n key. Surfaced by @voland-key on #132. - Edit Server form, Public URL Base scope — the "Public URL Base" field was rendered in Edit Server for every protocol including S3 / Azure / MEGA / Filen / GitHub / OAuth providers, with a caption that explicitly reads "Enter the HTTP URL that maps to your FTP root folder". The field is only meaningful for filesystem-mounted protocols whose tree maps to a plain HTTP service. Now gated to FTP / SFTP / WebDAV; other providers build their share links via native APIs and do not consume this field.
Changed (FTP layer — suppaftp 8.0.1 to 8.0.3)
- suppaftp upgraded from 8.0.1 to 8.0.3 after a fresh upstream review (analysis archived in
docs/dev/reports/2026-04-27-suppaftp-upgrade-analysis.md). The pin held since 8.0.2 introduced an ungatedstd::os::fd::AsFdcall that broke Windows builds. Upstream still ships that code in 8.0.3, but it is feature-gated behindtokio-async-native-tls, which AeroFTP does not enable (we usetokio-rustls-aws-lc-rs). Verified by readingtokio_ftp/tls/native_tls.rsin the upstream 8.0.3 source. The legacyFTP_CLIENTcrate stays pinned at 8.0.1 because it uses the broken feature. - The upgrade brings 14 upstream fixes that affect AeroFTP's hot path: undefined behavior in the tokio TLS
tcp_stream()borrow chain (PR 135); replacedunwrap()panics on server-controlled EPSV / SIZE / MDTM responses with proper error handling (PR 146); infinite loop in asyncfeat()on mid-response disconnect (PR 137) and the same fix forread_response_in()on multiline + disconnect (PR 138);data_connection_openflag now set only after successful open, removing a class of falseDataConnectionAlreadyOpenerrors (PR 136); MLSX parser acceptscdir/pdirper RFC 3659 (PR 139) and 4-digitunix.modelike0755(PR 140); DOS LIST parser handles sizes with commas like1,234,567(PR 142);parse_lstimeadjusts year for future dates the way GNUlsdoes (PR 143); DOS time parser handles a space before AM/PM (PR 144);abort()no longer appends when the server sends a direct 226 (PR 141); active mode uses EPRT for IPv6 (PR 145);cwd()accepts200 Command OKin addition to250for non-RFC-959 servers (PR 153). Allcargo check/cargo clippy --all-targets -- -D warningsclean.
Added
aeroftp-cli link --verify— optional reachability probe. After the share link is generated, the CLI runs an HTTP GET against the URL with a 15-second timeout, follows up to 5 redirects, and reports the resulting status code. Exits with code 4 on a non-2xx/3xx status. JSON output gains averified: { http_status, ok }block. Useful in CI smoke tests and post-release validation to catch silent regressions like the MEGA one above. Uses GET, not HEAD: SigV4-presigned URLs across S3-compatible providers reject HEAD when onlyhostis inSignedHeaders.- CLI smoke step for
--verifyflag —cli-smoke.ymlnow asserts thataeroftp-cli link --helpadvertises--verify, so removing the flag accidentally fails CI on every supported OS (Linux / macOS / Windows). Live reachability probes against credentialed profiles stay an operator-side check; the smoke verifies surface, not transport. - BLAKE3 in the File Properties Checksum tab — fifth row alongside MD5 / SHA-1 / SHA-256 / SHA-512. The
blake3crate was already vendored for the Hash Forge Cyber tool, so this is purely surface plumbing: backendcalculate_checksumaccepts"blake3"(alias"b3"), the dialog renders a new row, the props type widens accordingly. Output is the standard hex-encoded digest. - Esc clears narrowing on My Servers — pressing Escape while focused on the My Servers grid (no input selected, no modal open) clears the search query and resets the active filter chip back to "All". Mirrors the v3.6.6 Esc gesture that clears file selections in AeroFile panels and answers a wishlist follow-up.
Changed
- File Properties dialog widened from 420 px to 560 px (capped at 92 vw), and the Checksum row no longer truncates the digest. SHA-512 (128 hex characters) and BLAKE3 (64 hex characters) are now readable on a single line where the viewport allows, wrapping cleanly otherwise. The
(label / value / copy)row usesbreak-allanditems-startso long hashes do not overflow the column or push the copy button off-screen.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.6.6
[3.6.6] - 2026-04-27
Agent surface, onboarding polish, server speedtest, community wishlist
The full v3.6.6 cycle ran in roughly 36 hours and is shaped by two parallel community feedback waves. Issue #125 (@EhudKirsh) — a clean reproduction of CLI/Terminal bugs — prompted a 4-Sonnet black-box audit of the AI-agent surface that surfaced 13 friction points; the same thread invited Ehud to point an AI assistant at agent-info --json and agent-bootstrap --json, and the audit closes that loop. Issue #130 (@scottonanski) reframed the first-run empty state, and most of the onboarding polish ships in response. Issue #129 stayed open as the v3.6.6 wishlist thread, and many of the small UX wins below answer specific items voted up there.
Added
aeroftp-cli agent-connect <profile>+ MCP toolaeroftp_agent_connect— single-shot connect surface for AI agents. Returns one JSON payload with per-block status (connect,capabilities,quota,path), replacing the boilerplate sequenceconnect → about → df → ls /. Block status values areok/unsupported/unavailable/error; agents readconnect.statusfor the go/no-go decision and degrade gracefully on the rest. Live-connect allowlist (FTP / FTPS / SFTP / WebDAV / S3 / GitHub / GitLab) is documented inline in--help. For protocols outside the allowlist (pCloud, Filen, Dropbox, etc.) the response still includes validcapabilities,pathandprofileblocks; only theconnectblock reportsstatus: "unsupported"and the CLI exits 0 because the rest of the payload is still actionable. New lib moduleagent_session.rsis the single source of truth, also translating profile.options camelCase keys (bucket, region, tlsMode, privateKeyPath, …) into ProviderConfig.extra snake_case so S3 / Azure / SFTP-key / FTPS profiles connect via vault for the first time on the agent path.agent-info --jsonexposes aprotocol_featuresmap keyed by protocol → list of feature tokens (share_links,resume,server_copy,versions,thumbnails,change_tracking, etc.) plus anagent_connect_supported_protocolsarray. Collapses what used to require Nagent-connectcalls (one per profile) into a single batch query — driven by the audit's Battery D where one agent had to walk 69 profiles to discover share-link capability.head -c / --bytes <N>byte-range preview for remote files. Works on binary content (returns base64 in JSON when bytes aren't valid UTF-8) and reportsbytes_returned/total_size/truncated/encoding. Closes the audit's Battery A "no way to get the first 4KB without a full download" gap.ls --limit N,--files-only,--dirs-onlyflags. Summary now carriestruncated: boolandtotal_before_limit: intso agents can detect partial results unambiguously. Same trio added tofind, plus a--name <glob>alias for the positional pattern (the natural first-attempt form for agents who expect named args).- Cross-profile multi-select on My Servers — click a server card body to toggle Cross-Profile selection (icon button still triggers Connect). Up to two cards selected; third click drops the oldest FIFO. Indigo ring + arrow-up-right badge for source, emerald + arrow-down-left for destination. Always-visible Cross-Profile button in the toolbar with three brightness states (0 / 1 / 2 selected) and a counter badge. Right-click context menu offers Set as source / destination for direct assignment. Drag-to-reorder now works while a filter chip is active or a search query is typed.
- Server SpeedTest — new isolated benchmark dialog and CLI parity. GUI: Single / Compare tabs, multi-select up to 8 servers, parallel selector, ranked compare table with tri-state integrity, history summary card with regression warning (last < median × 0.7), Esc cancels running test. CLI:
speedsubcommand upgraded with random non-compressible payload (was zeros), SHA-256 integrity, TTFB measurement,--no-integrityflag,--json-out. Newspeed-comparesubcommand emits JSON v1, CSV, and Markdown reports.redact_url_for_display()is applied to every report and error path so passwords never appear in stdout / JSON / CSV / MD;csv_cell_safe()neutralizes spreadsheet formula triggers (=,+,-,@);md_cell_safe()escapes pipes / newlines / backslashes. Streaming download toNamedTempFilewith TTFB measurement, tempfile pre-allocation before connect to eliminate orphan/leak windows, SQLite history (WAL, 0600/0700 perms, 1000-row cap,server_nameforced NULL at insert as defense in depth), spawn_blocking for SHA-256 hashing and random payload generation. 22 speedtest lib tests + 6 CLI tests forredact_url, CSV/MD escaping. Schemaaeroftp.speedtest.v1is stable across CLI and GUI. - Selected-server chips on the Compare tab — each chip shows the display name, protocol tag, and an inline X to remove the server from the comparison without scrolling the list.
- Provider IconPicker dialog (closes Ehud's wishlist ask in #129 modeled on KeePassXC's icon library) — replaces the bare file dialog under "Choose icon" with a structured picker. "Provider icons" tab shows every entry in
PROVIDER_LOGOSgrouped by catalog category with curated popularity priority; "Custom icons" tab is the user's persisted library (localStorageaeroftp-custom-icons) with per-icon trash button. Live free-text search. SVG support throughout: Tauri file dialog accepts.svgand stores them asdata:image/svg+xml;base64,…data URLs without canvas rasterization, preserving vector fidelity at every render size. Always consumed via<img src>so embedded scripts are sandboxed. Live re-detection on dialog open: "On server" card auto-firesdetect_*_faviconand surfaces the live result, with a "Matches server" check on the In Use card when the live and saved values agree. - Server Health Check legend — dialog header shows the threshold dots inline (green 80+, yellow 50-79, red <50) instead of only on hover; ScoreGauge stacks
/100under the numeric score. Closes Ehud's specific "name the boundaries" ask in #129 for both single-server and Check All runs. - Two WebDAV provider presets (closes Ehud's #129 suggestions): OpenDrive WebDAV (
opendrive-webdav, serverhttps://webdav.opendrive.com, regular login password) and Yandex Disk WebDAV (yandexdisk-webdav, serverhttps://webdav.yandex.ru, app-specific password with deep-link toid.yandex.com/security/app-passwords). Both appear automatically in the Discover catalog under WebDAV. passwordGenUrlfield onProviderConfig— surfaces in the connection-form footer as a "Generate password" link (amber, key icon) next to the existing Create Account / Docs links. Wired up for Koofr WebDAV and Yandex WebDAV.PasswordStrengthBarin the Export Encrypted Backup flow — same component as AeroVault, gives users a 0-100 score with color-coded feedback while typing. Was previously only inside Vault; parity was the user-visible ask.- File associations for
.aeroftpand.aeroftp-keystore— TaurifileAssociationsextended withapplication/x-aeroftp(server-profiles export bundle) andapplication/x-aeroftp-keystore(full keystore). Linux MIME XML extended with both new types alongside the existing AeroVault entry. Per-format icons not shipped yet — file managers fall back to the generic AeroFTP icon. - "Close to tray instead of quitting" Settings toggle (closes Ehud's #129 follow-up). When enabled, clicking the window close button hides AeroFTP to the system tray instead of terminating, so background tasks like AeroCloud sync keep running. Independent from "Start minimized on autostart"; default off to preserve existing behaviour. Esc on either panel now clears file selections when no modal/dialog/preview is open, mirroring the right-click-empty-area gesture.
mkdir --jsonreportsalready_existed: boolso audit trails distinguish "I created it" from "it was already there" on idempotent-pinvocations.- Inline saved-profile inventory in
agent-bootstrap --jsonplus per-profileauth_state(valid/expired/needs_refresh/no_credentials/unknown) wired into all three list surfaces (profiles --json,agent-bootstrap --json, MCPaeroftp_list_servers). Eliminates the round-trip an agent had to do afteragent-bootstrapand stops connect-then-fail loops on profiles whose OAuth tokens expired silently. Newprofile_auth_statelib module is the single source of truth; pure local, never touches the network.
Changed
- First-run empty state on My Servers (issue #130,
@scottonanski) — visible "+ New" header label instead of icon-only+. Empty state now reads "Get started" with a primary CTA "Add your first server" (was "Quick Connect", which read as "rapid versus what?") and a 2x3 category grid (Protocols / S3 / WebDAV / Cloud / Media / Developer) that jumps straight into the relevant Discover slice in one click. - Cross-Profile gating — toolbar button, card click selection, and right-click "Set as source / destination" menu items are now gated on
servers.length > 1. Selecting a single server as source/destination was nonsensical and used to ship a confusing badge in the toolbar. - Discover service cards — replaced the responsive
grid-cols-1/2/3/4/5cascade withauto-fill minmax(260px, 1fr). Earlier breakpoints were truncating provider names to "Cloudflar...", "Backblaz...", "S3 Co...". The auto-fill grid keeps each card readable and only adds columns when the panel actually has room. WebDAV preset logos (OpenDrive, Yandex Disk) mapped to the existingPROVIDER_LOGOSso the new presets show proper icons everywhere (Discover, ConnectionScreen, SessionTabs, SettingsPanel, MyServers). - My Servers grid breakpoints —
grid-cols-2 md:3 lg:4 xl:5 2xl:6withp-1breathing room so the selection ring is no longer clipped at the edges. IntroHub container spans the same width as the...
AeroFTP v3.6.5
[3.6.5] - 2026-04-26
CLI polish + cross-provider correctness sweep
Drop-in patch driven by an end-to-end stress test of aeroftp-cli 3.6.4 against real provider accounts (S3, Backblaze, Storj, FTPS, Dropbox, Google Drive, OneDrive, Box, Koofr, Zoho WorkDrive, kDrive, Drime). The session surfaced four recurring correctness defects that span 8 providers, plus a dozen ergonomics rough edges that became visible only when the CLI is driven by an external AI agent rather than a human typing one command at a time. Fixes are surface-level and contained to the CLI binary plus per-provider trait impls; no architectural change.
Fixed
- FTP CLI now respects the server-provided home directory -
aeroftp-cliwas issuing a hardCWD /after login, which overrode the home directory negotiated by the FTP server (typically/home/useron non-chroot installations like default vsftpd) and landed the session at the filesystem root, which is usually non-writable. This surfaced in a head-to-head benchmark vs rclone where the sameput rel/path/file.txtworked under rclone but produced550 Permission deniedhere. Three coordinated fixes: the FTP provider skips the post-login CWD wheninitial_pathis bare/(aligning with rclone, lftp, FileZilla, ftp(1) and curl which all defer to PWD post-login); the CLI path resolver now returns empty for empty user input so the provider's canonical default kicks in instead of an artificial absolute root; andmkdir -ppreserves relative-vs-absolute semantics instead of always prefixing/, somkdir -p rel/pathissuesMKD rel/MKD rel/path(which the server can satisfy under the user's home) instead ofMKD /rel/path(which non-chroot servers reject for the same write-permission reason). Absolute paths like/etccontinue to target the filesystem root verbatim. Validated end-to-end against vsftpd in a Docker harness, no regression on the existing chroot case. - OAuth saved servers can now be renamed from the Edit dialog (issue #127) - In My Servers, hovering over an OAuth tile (Google Drive, Dropbox, OneDrive, Box, pCloud, Zoho WorkDrive, Yandex Disk, 4shared) and clicking the Edit pen now opens a form whose display name is editable, matching the behaviour of WebDAV / E2E / API providers. Two root causes: the
OAuthConnect"Active" branch (rendered when tokens already exist for the provider) skipped the Save toggle and the Connection Name input entirely, so there was no field to type into. And the OAuth save callback inConnectionScreensearched for the existing profile byname === saveName, so the moment the user changed the name the lookup failed and a brand-new profile was created next to the original instead of renaming it. Fix: render the Save toggle + name input in the Active branch (gated on the samewantToSaveflag the inactive branch uses), and prefer the expliciteditingProfileIdover the name-match heuristic when persisting an OAuth edit. OAuth-specific fields (clientId, clientSecret, scope, region) stay locked - renaming a saved server is purely a local label change. findglob matched as substring across 7 providers —aeroftp-cli find /path "*.txt"was returningreport.TXT.rtfand any file whose name contained the literaltxt, because the per-providerfind()implementations forwarded the pattern straight to the upstream search API (which is substring-by-name on most clouds) without re-applying glob semantics on the response. Affected: Dropbox, OneDrive, Box, Koofr, Zoho WorkDrive, Drime, kDrive. Fix: server-side query is now broad-prefiltered (glob characters stripped, only the literal portion sent), then the response is re-filtered client-side via the sharedmatches_find_patternhelper that powers the rest of the CLI. Google Drive (already filtered against its own catalog) gets the same belt-and-braces second pass.mkdir+putfailing on empty-prefix object stores —aeroftp-cli put file.bin s3://bucket/keyreturnedexit=2 "Parent does not exist"on a fresh bucket because the put plumbing was issuing a separatemkdircall before the upload, which is a hard error on S3 / Azure / Backblaze (no real directory primitive). Fix: skip the parent-directory pre-flight when the provider declaresis_object_store(). Validated end-to-end on Backblaze and Storj.lsof a missing FTPS path returnedexit=0with(empty directory)— the unhappy path was indistinguishable from an actually empty directory, so scripts piping the output couldn't tell. Fix: returnexit=2 "Path not found"when the upstreamLISTrejects the resolved path. The empty-directory case (path exists, no entries) still exits0.- Dropbox
getof a missing file took 3.5s + 3 retries before failing — thepath/lookup/not_foundJSON error was being classified as transient and retried, instead of as a permanent client error. Fix: detectpath/not_foundupstream and returnexit=2immediately. Drops the failure latency from ~3.5 s to ~150 ms. - Koofr WebDAV default Remote Path was
/dav/Koofr/(issue #126) — the discovery preset pairedserver: https://app.koofr.net/dav/KoofrwithbasePath: /dav/Koofr/, so the joined request URL doubled to/dav/Koofr/dav/Koofr/..., which Koofr rejected withInvalid credentials. Fix: defaultbasePathis now/. Existing saved profiles need to be edited once.
Added
- Inline rename for saved servers (issue #127) - In addition to unlocking rename via the Edit dialog, My Servers tiles now support a faster path: right-click → "Rename (F2)" turns the tile name into an editable input with a green check / grey X for confirm / cancel, plus an F2 hotkey that renames the currently hovered tile. Enter and blur commit, Escape cancels. Works in both grid and list view, and applies to every provider class (OAuth included). The right-click variant lives next to the existing Edit voice in the context menu.
- Protocol class label on My Servers tiles (issue #127 bonus) - Tiles now show a second small chip next to the protocol brand badge:
OAuth(indigo),API(sky),WebDAV(purple),E2E(emerald),S3(orange),Azure(blue),AeroCloud(cyan). FTP / FTPS / SFTP keep only the existing protocol badge to avoid duplication. The new label makes the WebDAV-Koofr vs API-Koofr distinction (and similar pairs across cloud providers) visible at a glance, parity with the Discover Services page.
Changed
- Throughput parity with rclone on FTP / SFTP / WebDAV - Head-to-head profiling on Docker loopback exposed three avoidable bottlenecks in the upload path. (1) The plain-FTP uploader was paying a 100ms-2s "TLS drain" sleep before
finalize_put_streamon every transfer; the sleep is a real workaround for a TLS/TCPclose_notifyrace but has no effect on plain FTP. Gating it behindtls_activecuts a single-100MB upload on Docker FTP from 2.74s to 0.72s (37 MB/s -> 138 MB/s, -74%) and a 500x10K bulk-of-small-files run from 14.68s to 2.72s (-82%, now ahead of rclone with--transfers 4). (2) The SFTP default buffer was 32 KiB - the conservative protocol minimum - which caps loopback throughput at ~35 MB/s. Raised to 256 KiB (OpenSSH 8+ and russh-sftp negotiate larger packets in practice anyway, and 1 MiB only adds another ~5 MB/s on top). 100MB Docker SFTP: 3.71s -> 2.37s (-36%).--chunk-size/--buffer-sizestill override. (3) The WebDAV streaming PUT body was built with the defaultReaderStreamcapacity of 8 KiB, churning syscalls. Bumped to 256 KiB. 100MB Docker WebDAV: 1.26s -> 0.84s (-33%). No protocol-level changes; tested in CI with the existing FTP/SFTP/WebDAV smoke jobs. aeroftp-clipolish pass for the AI-agent demo path:pgetis now a real subcommand (alias forget --segments 4); the banner cell stops being a lie.- Banner protocol count and the
connect"Unsupported protocol" error now derive from a singleSUPPORTED_URL_SCHEMESconstant, so they cannot drift apart again. - Subcommand help screens no longer reprint the ASCII banner. Banner is suppressed when stderr isn't a TTY, when
AEROFTP_NO_BANNERis set, or when--no-banneris on the command line. Top-level--helpstill shows it once. - 11 connection globals (
--bucket,--region,--container,--token,--tls,--key,--key-passphrase,--password-stdin,--insecure,--trust-host-key,--two-factor) move under aConnection optionsheading so per-subcommand help is no longer 30 random flags. 6 output flags move underOutput options. - 39 sentinel
default_value = "_"arguments now carryhide_default_value = trueso subcommand help stops rendering[default: _]for every URL/path positional. cleanupdescription rewritten to a single accurate sentence (was a confusing two-line run-on suggesting it handled duplicates, which it doesn't — that lives indedupe).dedupegained the missing description.tree --depthhelp no longer leaks the internal "TypeId mismatch" implementation note.Invalid URLerror now suggests--profile <name>when the input has no scheme, instead of leaking the rawurl::ParseErrormessage.
Skipped — flagged for follow-up
--excluderename:syncalready has a local--exclude(with-eshort) whoseVec<String>semantics differ from the global--exclude-global; unifying them would force a refactor of the per-command merge loop. Out of scope for a polish pass; tracked for a dedicated PR with proper test coverage.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.6.4
[3.6.4] - 2026-04-25
Patch release: Windows keystore export fix + autostart UX + update opt-out
Small but high-impact patch release driven by two community bug reports (issues #123 and #124). The headline fix unblocks the Settings → Backup → Export Keystore flow on Windows, where every export was leaving an orphan .tmp file behind and surfacing an "Access is denied (os error 5)" error. Three quality-of-life settings round out the release.
Fixed
- Windows keystore export
os error 5(issue #124) — Exporting a vault backup from Settings → Backup raised "IO error: Access is denied. (os error 5)" on every Windows machine and left anaeroftp_keystore_*.tmpfile in place of the final.aeroftp-keystore. Root cause: the atomic write helper opened the freshly-written temp file withFile::open(read-only on Windows) and then calledsync_all, which on Windows maps toFlushFileBuffersand requires aGENERIC_WRITEhandle. The flush failed before the rename ran. Fix: keep the existing write handle throughflush + sync_alland gate the parent-directory fsync to Unix only, sinceFile::openon a directory needsFILE_FLAG_BACKUP_SEMANTICSon Windows and is a no-op for durability there anyway. - Windows internal Terminal frozen on Start (issue #125) — Opening AeroTools → Terminal → Start on Windows produced a blinking cursor with no shell prompt and no response to keystrokes; the only escape was restarting the app. Root cause: the launcher passed
-Command "function prompt { ... }"to PowerShell to inject a colored prompt, but on Windows 10 with the default ExecutionPolicy the parser stalled inside-Command,spawn_commandnever returned, the Tauri invoke awaited forever, the frontend never received a session id, and every keystroke was silently dropped at the connected-tab gate. Fix: drop the prompt customization on Windows and start PowerShell as a plain-NoLogointeractive shell. Linux and macOS were unaffected. A user-level colored prompt can be added via$PROFILEif desired. - NSIS installer now reports the PATH change in the install log (issue #125 follow-up) — After registering
$INSTDIRinHKCU\Environment\Path, the installer now prints "Open a NEW terminal to run 'aeroftp-cli'" so users who installed via WinGet/UniGetUI in an already-open PowerShell are not surprised whenaeroftp-cliis "not recognized" — existing shells cache%PATH%at launch and need to be reopened.
Added
- Password strength meter on the keystore backup form — The Export Keystore section now shows the same animated 4-segment strength bar already used by AeroVault, with a 0–100 score and a colour-coded label (Weak/Fair/Strong/Excellent). Reuses
PasswordStrengthBarso future tweaks land in both places at once. - Start minimized to tray on autostart (issue #123) — New "Start minimized to tray" checkbox under Settings → General → Startup (visible only when "Launch on system startup" is enabled). When the OS launches AeroFTP from the autostart entry, the main window stays hidden and only the tray icon appears, matching the behaviour expected from background sync apps. Manual launches (double-click on the desktop or Start menu shortcut) always show the window. Detection uses a new
--autostartargument passed bytauri-plugin-autostartand surfaced to the frontend viais_autostart_launch. Default off — existing users see no change. - Don't check for updates toggle (issue #123) — New checkbox under Settings → General → Software Updates that disables both the 5-second startup check and the periodic 24-hour check. The manual "Check for Updates" button continues to work, so the option simply gives users who manage AeroFTP through external package managers (WinGet, AUR, Snap auto-refresh) a way to opt out of the in-app update prompts and bandwidth.
Changed
- NSIS installer no longer reinstates the desktop shortcut on every upgrade (issue #123) — On a fresh install the bundler still creates
Desktop\AeroFTP.lnkas before. On an upgrade, however, the installer now snapshots the desktop shortcut state in the pre-install hook and, if the user had previously deleted the shortcut, removes the one the bundler just recreated. Users who keep the shortcut see no change. - Windows NSIS post-install hooks now actually run (issue #125 root cause) — From v3.6.2 onward the installer hook file defined the four lifecycle hooks as
CUSTOM_{PRE,POST}_{INSTALL,UNINSTALL}, but Tauri's bundledinstaller.nsiinvokes them gated on!ifmacrodef NSIS_HOOK_{PRE,POST}{INSTALL,UNINSTALL}. The macro names didn't match, so!ifmacrodefreturned false and every hook in the file was skipped silently — the HKCU PATH registration, the.aerovaultfile association, the VC++ Runtime bootstrap, the new desktop-shortcut snapshot logic — none of it ran in any shipped Windows installer. Renaming the macros to the names Tauri actually checks for activates all of them in v3.6.4. Existing installs with no PATH entry will get one on the next upgrade. Diagnosed thanks to a Windows-side investigation reported indocs/dev/aeroftp-windows-path-hook-bug-report-2026-04-25.md. - Silent uninstall preserves user data — The pre-uninstall hook prompts the user via three
MessageBoxdialogs to selectively remove saved servers, AI chat history and cache. Now that the hook actually runs, those dialogs would have surfaced during every WinGet upgrade (which silently uninstalls the old version before installing the new one), either popping blocking modals or — depending on Windows version — defaulting to "yes" and wiping data. The hook now bails out early viaIfSilent, preserving everything when not in interactive mode. - Cross-Profile Transfer pre-selects the active server — When the dialog is opened from the remote pane while connected, the source profile and source path are now pre-populated from the active session, so the most common flow (copy from the server you're already looking at to another saved server) takes one click instead of three. Source/destination filtering — already in place — still excludes the chosen source from the destination list.
Translations
- All four new settings labels and descriptions translated across the 47 supported locales.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.6.3
[3.6.3] - 2026-04-25
Unified Tool Dispatcher + Cloud Provider Sweep + AeroVault Pro Foundations
The post-3.6.2 sprint consolidates three years of organically-grown AeroAgent surface into a single, audited tool engine shared by the Tauri GUI, the standalone aeroftp-cli binary and the MCP server. In parallel, eighteen cloud providers gain trash management, version history, share links and label/tag CRUD, while the desktop backend grows new pillars (server health diagnostics, TOTP 2FA scaffold, vault history) that prepare the ground for AeroVault Pro and the upcoming biometric unlock work.
Added
- Unified AI tool dispatcher (T3 Gate 2) — All AeroAgent tools (53 entries across local, system, remote and RAG/memory areas) now flow through a single
ai_core::tools::dispatch_toolwith surface-aware filtering (GUI / CLI / MCP). Per-area handler modules (local_tools.rs,system_tools.rs,remote_tools.rs,agent_tools.rs) host the canonical implementations; per-surfaceToolCtximpls (TauriToolCtx,CliToolCtx,McpToolCtx) bridge to the appropriate runtime. The legacyexecute_ai_tool/execute_cli_tool/mcp::execute_tooldispatchers stay as thin fast-path wrappers, eliminating the long-standing drift between three parallel match statements. 11 parity tests prove identical output across surfaces; the security regression suite gains an Area B check ensuringsystem_toolsalways delegatesshell_executeto the canonical denylist source. Behavioural wins:local_trashis now available on CLI,clipboard_*andarchive_compress/archive_decompressuse native Rust libraries instead of subprocess shellouts (no morexclip/zip/tar/7zruntime dependencies on CLI), andvalidate_pathadopts component-aware matching so/bootcampis no longer falsely flagged as/boot. - Provider trait expansion — 7 new optional methods:
list_trash,restore_from_trash,permanent_delete,list_versions,download_version,restore_version,create_share_link,get_storage_quota. TheRemoteEntrystruct grows MIME type, owner/group, octal permissions, symlink target, and a free-form metadata bag. Box, Google Drive, Dropbox, OneDrive, Zoho WorkDrive light up the full feature matrix. - Box Pro feature set: trash management, file move/comment/collaboration, watermark (Enterprise), folder locks (Enterprise), inline tag chips with PRO badge.
- Google Drive starring + comments + properties: star/unstar from context menu, add comments via prompt dialog, set custom key-value properties and description; file listing now includes
starred,description,propertiesfields. - Dropbox tag management + Trash Manager: full tag CRUD via Dropbox Tags API (reuses generic
BoxTagsDialog); dedicated modal for deleted files (restore + permanent delete). - OneDrive Trash Manager: full recycle bin lifecycle (move to trash, list, restore, permanent delete).
- Zoho WorkDrive labels + versioning: list team labels, get/add/remove labels on files via
ZohoLabelsDialogwith color-coded toggle list; list versions, download specific version, restore/promote version through the StorageProvider trait. - Server Health Check — Real-time diagnostics for saved servers: DNS / TCP / TLS / HTTP probes with latency stats, 0–100 health score, SVG radial gauge, latency bars, Canvas 2D area chart for trend. Right-click any server card → Connect / Edit / Duplicate / Health Check / Delete (
useContextMenuhook). Batch health check across all saved servers in parallel with healthy/degraded/unreachable summary. - TOTP 2FA scaffold (
totp.rs) — RFC 6238 authenticator core ready for the optional vault second factor: issuer, period, digits, base32 secret import/export, code verification with drift window. Will surface in Settings > Security in a follow-up release. - Vault history (
vault_history.rs) — SQLite-backed recents tracker for AeroVault Pro: last-opened timestamp, security badges, dedupe by canonical path. Powers the new VaultHome recents grid and "reopen" one-click flow. - AeroAgent server context tools registered in unified registry:
server_list_saved,server_exec, plus the fullaeroftp_*↔remote_*alias map (including newly-registeredserver_list_savedalias). - Aerorsync standalone serve binary —
bin/aerorsync_serve.rs(renamed fromrsync_proto_serve.rs) ships as a separate[[bin]]target for capture/test workflows.
Changed
rsync_native_proto/module renamed toaerorsync/— Aligns the experimental Rust-native rsync protocol implementation with the AeroFTP product naming. 41 source files, the capture harness, all fixtures and the standalone serve binary move undersrc-tauri/src/aerorsync/. CI workflows, Cargo bin section,lib.rsmodule declaration and the integration test path are updated to match. No on-the-wire changes: the protocol stays byte-identical to v3.6.x.- AeroAgent tool count: 53 unified registry entries — formerly counted as ~47 across three parallel dispatchers, now consolidated into a single
TOOL_DEFINITIONStable where the same handler runs unmodified on GUI, CLI and MCP. - CLI relative-path resolution snapshot at process start —
CliToolCtx::new()capturesstd::env::current_dir()once at construction and exposes it viaToolCtx::context_local_path(). Eliminates the silent regression where the unified dispatcher saw paths "as-is" instead of resolved against cwd. Behaviour matches the legacy CLIresolve_pathclosure exactly. shell_executeworking_dir validation hardened — The unifiedsystem_tools::shell_executenow runsvalidate_path(working_dir, ...)against the system deny-list (/etc/shadow,/proc,/sys,/boot,/root,/etc/ssh,/etc/sudoers, …) before delegating to the legacy GUI helper, restoring CLI parity that was lost during Area B migration.upload_many/delete_manynow report real elapsed seconds — the placeholderelapsed_secs: 0u64returned to MCP and AI clients is replaced withInstant::now().elapsed().as_secs()measured around the inner loop.- Vite 5 → 8 +
@vitejs/plugin-react4 → 6 — Major bump of the frontend build chain. Dev server, production build and Tauri packaging stay green.package-lock.jsonregenerated. Cargo.lockrefreshed — picks up transitive dependency churn from the v3.6.x feature work (provider crates, ai_core additions, server_health, totp, vault history, aerorsync rename).
Fixed
- SFTP symlink-aware listing —
SftpProvider::list()now follows symlinks viasftp.metadata()so directory traversal works on NAS devices that report symlinks for share roots (WD MyCloud, Synology DSM6, ASUSTOR ADM). - Azure Blob server-side copy —
Copy BlobAPI withx-ms-copy-sourceand saferesolve_blob_path()prefix resolution; eliminates the GET+PUT roundtrip for in-account moves. local_trashnow available on CLI — was GUI-only in the legacy dispatcher; the unified registry exposes it on both surfaces with the same semantics.path_styleno longer false-coalesced in App.tsx — Drop the|| falseoneffectiveParams.options?.pathStyleandcloudServer.options?.pathStyleso explicit user choices (true/false/undefined) reach the backend faithfully without being squashed.vault_remote.rsvalidation tightened — null-byte rejection on remote vault paths, symlink refusal on Unix mode lookup, parent canonicalization before opening.aerovaultfiles served from remote providers.
Security
- russh 0.59 → 0.60.1 — Closes Dependabot HIGH (GHSA-f5v4-2wr6-hqmg): pre-auth DoS via unbounded allocation in the keyboard-interactive auth handler. AeroFTP exposes a russh-based SSH server through
aeroftp-cli serve sftp, so the fix is mandatory. The bump required updating the ed25519 server-key generation call site to the newCryptoRngbound — added a narrowrand_010(rand 0.10) alias for that single line, with the rest of the codebase staying on rand 0.8. - rand 0.8.5 → 0.8.6 — Closes Dependabot LOW (GHSA-cq8v-f236-94qc): rand was unsound when paired with a custom logger using
rand::rng(). Direct dep bump only; the build-time-only transitiverand 0.7.3(pulled by phf_generator 0.8.0 / selectors 0.24.0 in the Tauri stack) is left in place — not in the runtime path and the upstream chain is locked at this combination by Tauri's wry dependency. validate_pathcomponent-aware matching —path_matches_prefix(path, prefix)helper replaces 11 occurrences ofs.starts_with(d). Eliminates false positives on prefix matches (e.g./bootcampwas being blocked as if it were under/boot).- Single source of truth for
shell_executedenylist — Both the GUI fast-path and the newsystem_tools::shell_executedelegate toai_tools::shell_executefor the canonical 35+ regex denylist + meta-character filter. The CI security regression script (security-regression.cjs) now actively verifies this delegation chain stays intact.
Internal
- 53 entries in
TOOL_DEFINITIONSdistributed: Area Alocal_*= 21, Area Bclipboard/shell/archive= 5, Area Cremote_*/aeroftp_*/server_exec= 24 (incl.server_list_savedalias), Area Drag_*/agent_memory_*= 3. tool_parity.rsintegration test suite — 11 tests proving identical output across GUI and CLI surfaces forlocal_read,local_write,local_mkdir,local_delete,local_grep,shell_execute,rag_index,rag_search, plus 3 remote alias parity tests with aFakeRemoteBackend.security-regression.cjsArea B coverage — verifiessystem_tools::shell_executecallscrate::ai_tools::shell_execute(single denylist source) and validatesworking_diragainst the deny-list before the call.- Quality bar at closure:
cargo check --features aerorsync --lib,cargo clippy --all-targets --features aerorsync -- -D warnings,cargo test --features aerorsync --lib(1018 / 1018 passed)...
AeroFTP v3.6.1
[3.6.1] - 2026-04-23
Windows First-Class Delta Sync + PR-T08 Eligibility Gate + Dependency Security
The "historical step" release: byte-level delta sync becomes a first-class citizen on Windows, with no bundled rsync.exe, no WSL requirement, and no reliance on any external binary. Windows rides the native rsync 31/32 wire protocol re-implemented in pure Rust (clean-room, GPL-3.0-or-later) that previously lived behind an opt-in cargo feature; from this release the feature is default-on and the protocol module is part of the mainline tree.
Added
- Windows cross-OS delta sync:
SftpProvider::delta_transport()dispatches cross-platform. When theproto_native_rsyncfeature is compiled in (default from this release) and a session has SFTP + SSH key auth, Windows uses the native Rust rsync implementation end-to-end; the binary-rsync classic fallback stays available on Unix through the sameDeltaTransporttrait surface. No binary shipping, no WSL dependency. - Delta Sync eligibility gate (PR-T08): before starting an AeroSync run, a cached Tauri probe (
sftp_probe_delta_eligibility, 5 s timeout) verifies that the current session can actually exercise the delta path. When it can't, a modal surfaces the sanitized reason, a "Don't show again for this server" persistent preference on the saved profile, and a "Learn more" link to the public docs page. The modal fires only when the delta toggle is on and the server is SFTP — classic-only sessions are never interrupted. - Fallback reason surfacing (PR-T05):
FileOutcomeandTransferEventcarry an optionalfallback_reasonpopulated only when the delta path was attempted for the file and fell through transparently. SyncPanel renders a greyclassicbadge (with the sanitized reason as tooltip) next to affected files — distinguishing "classic by design" from "classic after delta attempt". 7 new i18n keys translated in all 47 supported languages. - CI Windows native build lane: new
windows-nativejob indelta-sync-integration.yml—cargo checkwith and without feature, unit tests with and without feature onwindows-latest. Catches cross-OS regressions at PR time.
Fixed
- SFTP upload mtime preservation:
SftpProvider::upload()now sets remoteatime+mtimeto the local source's mtime after a successful upload, using SFTPACMODTIME. Before this fix, every sync after an upload sawremote_mtime > local_mtimeand spuriously re-uploaded unchanged files. Observed live onSSH MyCloud HD: second sync after a single-file change now reportsuploaded=1 / skipped=1where the pre-fix behavior wasuploaded=2. - Versioning archive name collision:
SyncVersioning::archive_filenow produces-1,-2, ... suffixed names when a second version lands with the same filename (unique_archive_pathhelper). Retention windows no longer silently clobber archived copies of the same file. - CloudPanel
versioning_strategytype round-trip: the field is now a tagged union that mirrors the RustCloudVersioningStrategyshape (disabled/trash_can/simple/staggered) instead of a stringly-typed sentinel, so the select round-trips without downstream parsing.
Security
- openssl crate bumped 0.10.77 → 0.10.78: closes 5 of 6 open Dependabot alerts — CVE-2026-41676 (high), GHSA-xmgf-hq76-4vx2 (low), CVE-2026-41678 (high), GHSA-hppc-g8h3-xhp3 (high), CVE-2026-41681 (high).
openssl-sys 0.9.113 → 0.9.114bumped transitively. The sixth alert (rand 0.10.0soundness) is not applicable — our lockfile carriesrand 0.7.3/0.8.5/0.9.4.
Changed
proto_native_rsyncfeature is default-on: the native rsync protocol implementation is now compiled by default. Build with--no-default-featuresif you want a leaner binary without native delta support (classic binary rsync stays available on Unix regardless).rsync_native_proto/module moved into the tree: the native implementation, previously gitignored as a scaffold, is now versioned mainline source. Large capture harnesses (Docker images, rsync reference binaries, frozen transcripts) stay out of git via targeted.gitignorepatterns on their specific subdirectories.- Native rsync toggle eligible on Windows:
native_rsync_feature_compiled()no longer requirescfg(unix)— the dispatch is now cross-platform, so the runtime toggle surfaces on any OS where the feature is compiled in. - CI retrigger surface widened:
delta-sync-integration.ymlpaths:filter now includeslib.rs,settings.rs, andCargo.lock, so future changes that affect the delta stack transitively retrigger the lane automatically.
Licensing note
The native rsync module (src-tauri/src/rsync_native_proto/) is an independent clean-room Rust re-implementation of the rsync wire protocol. No rsync source code was copied; dependencies are permissively-licensed Rust crates (russh, ssh2, zstd, xxhash-rust). The rsync project itself is GPL-3.0-or-later and AeroFTP is GPL-3.0-or-later as well, so licence compatibility is unconditional. See src-tauri/src/rsync_native_proto/README.md for full details.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.6.0
[3.6.0] - 2026-04-22
AeroSync UX Upgrade + Critical SSH Host-Key Fix
Release focused on making the AeroSync optimized-transfer path visible to the user and on a critical SSH host-key dialog fix. The optimization logic has been progressively landed over the last weeks; this release is the moment it becomes visible in the product.
Added
- Delta Savings widget in AeroSync: when a file travels through the optimized transfer path during sync, an inline badge next to the file's status marks it; at the end of the run an "Optimized transfer" summary card aggregates N files · bytes saved · speedup. When the effective speedup falls below 1.5x the card switches to a neutral "Optimized transfer" variant to avoid misleading framing. The widget is fully theme-aware across light/dark/tokyo/cyber and hidden entirely when no file benefited from the path. Translated in all 47 supported languages.
- Per-file breakdown in
aeroftp_sync_treeMCP tool: the response now carriessummary.delta_files[](array of{path, bytes_sent, total_size, speedup}) capped at 500 entries, plus asummary.delta_files_truncatedflag when a run crosses the cap. Aggregate counters insummary.delta_savingskeep counting past the cap. Both keys are omitted on runs where no file used the optimized path, preserving the absence-vs-null contract with the existingdelta_savingsblock.
Fixed
- Critical:
Host Key Changeddialog no longer reappears after Accept: the russh library reports the changed-key line number as 1-based; the internalsftp_remove_host_keycommand was treating it as a 0-based array index, which meant Accept surgically removed the line after the stale entry instead of the stale one itself. Repeated Accept clicks piled valid entries on top of the original stale one, and every subsequent connection hit the stale line first and re-triggered the dialog. The fix uses 1-based indexing correctly and now also prunes any other plaintext entry for the same(host, port, algorithm)tuple that matches the incoming key's algorithm — so files already corrupted by earlier broken attempts get cleaned up in the same pass. 11 new unit tests pin the regression cases (line zero rejected, 1-based indexing, duplicate pruning, host-mismatch corruption guard, hashed-entry tolerance).
Changed
- Optimized transfer path reachable from SyncPanel on SFTP: earlier releases had the decision wired in the unified sync core only — AeroSync UI actually iterates per-file commands that never entered that branch. This release extends the decision to
upload_file/download_file/provider_upload_file/provider_download_fileso sessions with SFTP + SSH key authentication + a capable remote now actually exercise the optimization in the day-to-day UI flow. The decision remains self-gated (silent fallback to classic when the session is not eligible); hard rejections (SSH host-key mismatch, permission denied) surface as transfer errors without silent retry. TransferEventnow propagates optionaldelta_stats: the event emitted to the frontend oncompletecarries per-file stats from the optimized path when applicable (serialized withskip_serializing_if, absent for classic transfers — no wire overhead for non-SFTP providers). Frontend accumulates these into the session-level summary client-side.
MCP (pre-release carryovers from v3.6.0 cycle)
aeroftp_read_filesoft-truncate dentro hard cap: oversized file (oltrepreview_kbma entro il cap 1 MB) ritornano contenuto troncato contruncated:trueinvece di errore duro. Il validator rifiuta solo oltre il hard cap 1 MB. Elimina i retry forzati che gli agent facevano quando incontravano file appena oltre la finestra di preview richiesta.aeroftp_check_treeesponecompare_methodanche sul gruppomatch: il JSON di ritorno ora includegroups.matchdettagliato alla pari didiffer/missing_local/missing_remote. Ogni entry portacompare_method: "checksum" | "size"così l'agent sa se il match è stato crittograficamente verificato o è fallback dimensionale (tipicamente su FTP che non ha checksum server-side).
Build / CI
- Dedicated
fallback-fixtureCI lane:delta-sync-integration.ymlgains a second job running against the password-only SFTP Docker fixture. Covers the silent-fallback branch (password-only sessions) and the hard-rejection contract so a regression on those paths cannot ship unnoticed. Protected bynick-fields/retry@v3(2 attempts, 10s wait) for Docker-on-runner flakiness. Both jobs block PRs on failure.
Internals
SyncReportdomain types expanded:DeltaSavingsSummary(aggregate) and newDeltaFileEntry(per-file breakdown) are public types;DELTA_FILES_CAP = 500exposed as a crate-level constant. The accumulator is one branch driving both aggregate and per-file tracking — impossible to drift.- Stderr sanitizer hardened:
/home/<user>,/Users/<user>,C:\Users\<user>and.ssh/*path segments redacted beforefallback_reason/hard_errormessages flow to UI, logs, or MCP responses. Message capped at 512 characters to keep response arrays bounded. RsyncStats.warningsdowngraded topub(crate): entries may contain remote file paths and must not leak without sanitization.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage
AeroFTP v3.5.9
[3.5.9] - 2026-04-20
CLI Agent Experience Polish
Follow-up patch to v3.5.8 focused entirely on the CLI surface. All items are small, independent, and addressed in one coherent pass so an agent using aeroftp-cli against a generic FTP hoster has the same UX quality the MCP server got in v3.5.8.
Added
plan[]insync --dry-run --json: every candidate operation is now listed with{op, path, local_size, remote_size, conflict_path}. Real (non-dry-run) runs omit the field viaskip_serializing_if, so legacy parsers that never asked for dry-run see the exact same shape. Agents no longer have to fall back to parsing the text-verbose output to build an execution plan.
Fixed
- Missing-parent uploads now explain the failure:
putruns a pre-flightstaton the remote parent directory. If the parent is missing the CLI errors out with "Parent directory '/X/Y' does not exist on the remote. Create it first with: aeroftp-cli mkdir -p '/X/Y'" — one crisp message instead of three retries of a generic553 Can't open that file: No such file or directoryreturned by the hoster.is_retryable_exitnow excludes exit code2(not-found) so the retry loop stops burning attempts on stable failures. - Sync remote scanner auto-reconnects on transport failure:
sync_core::scan_remote_treenow classifieslist()errors against the sameis_transport_levelpattern used by the MCP pool (variant matches onNotConnected/ConnectionFailed/Timeout/NetworkError/IoErrorplus message patterns like "Data connection is already open", "broken pipe", "connection reset"). When it fires, the provider is disconnected and reconnected, thenlist()is retried once. Previously the scanner logged a warning and treated the failure as an empty directory — which let sync silently duplicate files that actually existed on the remote. lsfollow-up hint no longer ships a literal*.extplaceholder:suggest_ls_followupnow renders"*"so agents copy-pasting the hint get real results. The*.extstring survives only in bootstrap playbook JSON where it is documented as a placeholder the user must substitute.
Build
- cli-smoke Windows runner installs Strawberry Perl: the
openssl-sysvendored build pulled in transitively byssh2 = "=0.9.5"requiresParams::Checkto configure OpenSSL, and the stockwindows-latestPerl no longer ships it. Added achoco install strawberryperlstep gated byrunner.os == 'Windows'. Linux and macOS jobs are unaffected.
Downloads:
- Windows:
.msiinstaller,.exe, or.zipportable (no installation required) - macOS:
.dmgdisk image - Linux:
.deb,.rpm,.snap, or.AppImage