Skip to content

qwrobins/aether

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

159 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aether

A modern desktop file transfer application for AWS S3, SFTP, network filesystems, object storage, and Tailscale Taildrop, built with Electron, React, and a custom dark UI theme.

Aether provides a dual-pane file manager with drag-and-drop transfers, multiple simultaneous connections, recursive directory operations, and encrypted credential storage — all wrapped in a warm indigo-tinted interface designed to feel atmospheric rather than flat.

Electron React TypeScript Tailwind CSS License


Features

File Transfer

  • AWS S3 — browse buckets, upload/download objects, create folders, batch delete with retry
  • SFTP — password or SSH key authentication, full remote filesystem browsing
  • Network filesystems — connect to SMB, NFS, WebDAV, FTP/FTPS, and rsync targets
  • Object storage — connect to Azure Blob Storage and Google Cloud Storage alongside AWS S3
  • Tailscale Taildrop — send local files to eligible tailnet devices from a built-in destination pane
  • Drag and drop between panels or from the OS file manager
  • Recursive directory transfers — directories are automatically expanded into individual file transfers
  • Concurrent transfers — up to 3 simultaneous transfers via a managed queue (p-queue)
  • Progress tracking — real-time speed, bytes transferred, animated progress bars
  • Automatic retry — failed transfers retry up to 3 times with exponential backoff

Tailscale Taildrop

  • Tailscale is a built-in destination mode, not a saved connection profile.
  • Aether uses the local tailscale CLI and the user's existing Tailscale session; no Tailscale credentials are stored.
  • The right pane lists Taildrop targets from tailscale file cp --targets, including offline device states.
  • Sending uses tailscale file cp <file> <target>: and records file-level status in the transfer/history area.
  • On Linux, received files can be collected into the current local folder with tailscale file get <folder>.
  • On macOS and Windows, received files are handled by the Tailscale desktop client/OS Downloads flow.
  • Taildrop is documented by Tailscale as alpha; unsupported, disabled, or unavailable states are surfaced in the UI.

Connection Management

  • Save and manage multiple S3, SFTP, SMB, NFS, WebDAV, FTP/FTPS, rsync, Azure Blob Storage, and Google Cloud Storage profiles
  • S3 auth methods: access keys, IAM role assumption, named AWS profile, default credential chain
  • SFTP auth methods: password, SSH private key (with optional passphrase)
  • Network connection options: mounted share paths for SMB/NFS/WebDAV, password-based FTP/FTPS, and SSH-backed rsync
  • Object storage options: Azure account keys/SAS tokens and GCS service account keys
  • Credentials encrypted at rest via Electron safeStorage
  • Test connection before saving

Dual-Pane File Browser

  • Local panel (left) — browse the local filesystem
  • Remote panel (right) — browse S3 buckets/objects, SFTP servers, SMB/NFS/WebDAV shares, FTP/FTPS/rsync targets, Azure Blob containers, and GCS buckets
  • Sortable columns (name, size, modified date)
  • Keyboard shortcuts: Delete, Ctrl+A (select all), Ctrl+R (refresh), Ctrl+N (new folder), F2 (rename), Escape (clear selection)
  • Shift-click range selection and Ctrl-click multi-selection
  • Right-click context menu with all file operations
  • Color-coded file type icons

UI

  • Custom frameless title bar with window controls
  • Collapsible sidebar with quick-access directories and connection list
  • Resizable panels (horizontal split) and drag-to-resize transfer queue
  • Animated drop zones with glowing borders
  • Toast notifications for errors and actions
  • Dark theme with OKLCH color system (Geist Sans + Geist Mono typography)

Tech Stack

Layer Technology
Framework Electron 40 + Electron Forge + Vite 7
UI React 19, Tailwind CSS v4, shadcn/ui (new-york), Radix UI
Animation framer-motion, CSS keyframes
State Zustand (5 stores)
S3 @aws-sdk/client-s3, @aws-sdk/lib-storage, @aws-sdk/credential-providers
SFTP ssh2-sftp-client
Network Filesystems Local mounts, FTP/FTPS, rsync
Object Storage Azure Blob Storage, Google Cloud Storage
Taildrop Local Tailscale CLI
Transfers p-queue (concurrency control)
Storage electron-store (JSON) + Electron safeStorage (encryption)
Fonts Geist Sans, Geist Mono
Icons lucide-react

Getting Started

Prerequisites

  • Node.js >= 18
  • npm >= 9

Install

git clone https://github.com/qwrobins/aether.git
cd aether
npm install

Development

npm start

Launches the app in development mode with Vite HMR for the renderer process.

Package

npm run package

Builds the app into a platform-specific executable (not an installer).

Build Distributable

npm run make

Creates distributable installers. Configured makers:

Platform Format
Windows Squirrel
macOS DMG
Linux DEB, RPM, AppImage

Install macOS Release Builds

Download the latest macOS DMG from the GitHub Releases page, open it, and drag Aether into /Applications.

Release DMGs are named with an explicit platform and architecture, such as Aether-0.1.16-macos-arm64.dmg or Aether-0.1.16-macos-x64.dmg.

Published macOS release builds are signed with a Developer ID Application certificate and notarized by Apple, so they should open normally under Gatekeeper without clearing quarantine attributes.

Release Automation

Merging a releasable conventional commit to main runs .github/workflows/prepare-release.yml. That workflow calculates the next semantic version, updates package.json, package-lock.json, .release-please-manifest.json, and CHANGELOG.md, pushes the matching vX.Y.Z tag, then dispatches .github/workflows/release.yml for that tag. The release workflow validates that the tag matches package.json before building Linux and macOS release assets.

Signed macOS Releases

Tagged macOS releases are signed and notarized by GitHub Actions. The workflow imports a Developer ID Application certificate, signs the app bundle, submits it for Apple notarization, staples the notarization ticket, and publishes the DMG with the other release assets.

Secret Description
MACOS_CERTIFICATE_BASE64 Base64-encoded .p12 export of the Developer ID Application certificate
MACOS_CERTIFICATE_PASSWORD Password used when exporting the .p12 certificate
APPLE_ID Apple ID email for notarization
APPLE_APP_SPECIFIC_PASSWORD App-specific password for the Apple ID
APPLE_TEAM_ID Apple Developer Team ID
MACOS_SIGN_IDENTITY Optional exact Developer ID Application: ... identity name

To create MACOS_CERTIFICATE_BASE64 after exporting the certificate from Keychain Access:

base64 -i DeveloperIDApplication.p12 | pbcopy

Local macOS builds remain unsigned unless AETHER_SIGN_MACOS=true and the notarization environment variables are present.

macOS — "App is Damaged" Warning

Release DMGs should not show this warning because they are signed and notarized. Unsigned local builds or ad hoc test artifacts may still be blocked by macOS with a "damaged" error. Run this once in Terminal after installing an unsigned build:

xattr -cr /Applications/Aether.app

Install AppImage (Linux)

After building, install the latest AppImage into your applications menu:

npm run install:appimage

You can also pass a specific file:

./scripts/install-appimage.sh /path/to/Aether-0.1.2-x64.AppImage

Project Structure

src/
├── main/                          # Main process (Node.js)
│   ├── index.ts                   # App lifecycle, BrowserWindow
│   ├── ipc/                       # IPC handler registration
│   │   ├── connection.handlers.ts # Connection CRUD
│   │   ├── filesystem.handlers.ts # Local filesystem ops
│   │   ├── s3.handlers.ts         # S3 connect/disconnect + object ops
│   │   ├── sftp.handlers.ts       # SFTP connect/disconnect + file ops
│   │   ├── network-filesystem.handlers.ts # SMB/NFS/WebDAV and object storage browsing
│   │   ├── rsync.handlers.ts      # Rsync-backed browsing and transfers
│   │   ├── taildrop.handlers.ts   # Taildrop target/receive IPC
│   │   └── transfer.handlers.ts   # Transfer queue management
│   ├── services/                  # Business logic
│   │   ├── connection.service.ts  # Profile storage + encryption
│   │   ├── credential.service.ts  # Electron safeStorage wrapper
│   │   ├── filesystem.service.ts  # Local fs operations
│   │   ├── s3.service.ts          # AWS S3 client + operations
│   │   ├── sftp.service.ts        # SSH2 SFTP client + operations
│   │   ├── network-filesystem.service.ts # Mounted share, Azure Blob, and GCS operations
│   │   ├── rsync.service.ts       # Rsync command integration
│   │   ├── taildrop.service.ts    # Local Tailscale CLI operations
│   │   └── transfer.service.ts    # Transfer engine (p-queue)
│   └── utils/
│       └── store.ts               # JSON file persistence
├── preload/
│   └── preload.ts                 # contextBridge (invoke, on, removeAllListeners)
├── renderer/                      # Renderer process (React)
│   ├── App.tsx                    # Root component
│   ├── index.css                  # Tailwind v4 theme + animations
│   ├── components/
│   │   ├── connection/            # Connection manager sheet + forms
│   │   ├── layout/                # AppLayout, AppSidebar, TitleBar
│   │   ├── panels/                # LocalPanel, RemotePanel, FileList, FileItem
│   │   ├── shared/                # EmptyState, FileIcon, FileSize
│   │   ├── transfer/              # TransferQueue, TransferItem
│   │   └── ui/                    # 21 shadcn/ui primitives
│   ├── hooks/                     # useKeyboardShortcuts, useTransferEvents, useFileSystem
│   └── stores/                    # Zustand stores (connection, local, remote, taildrop, transfer, ui)
└── shared/                        # Cross-process (types only)
    ├── constants/
    │   └── channels.ts            # IPC channel name constants
    └── types/
        ├── connection.ts          # S3, SFTP, network filesystem, and object storage profile types
        ├── filesystem.ts          # FileEntry, DirectoryListing
        ├── ipc.ts                 # Fully typed IPC map
        ├── taildrop.ts            # Taildrop target/status types
        └── transfer.ts            # Transfer item/request/progress types

Architecture

Aether follows strict Electron process separation:

  • Main process handles all Node.js, filesystem, network, and AWS SDK operations. Services are never imported by the renderer.
  • Preload script exposes a minimal window.api bridge via contextBridge with invoke (request/response) and on (event subscription).
  • Renderer process is a pure React app. All backend operations go through window.api.invoke(), fully typed via IpcInvokeMap.
  • Shared directory contains only TypeScript types and constants — no runtime code with side effects.

Security: contextIsolation: true, nodeIntegration: false, sandbox: true. Credentials are encrypted via safeStorage before being written to disk.


IPC Channels

All channels are defined in src/shared/constants/channels.ts and typed in src/shared/types/ipc.ts.

Category Channels
Filesystem fs:read-dir, fs:stat, fs:mkdir, fs:delete, fs:rename, fs:get-home
Connections conn:list, conn:save, conn:delete, conn:test, conn:connect, conn:disconnect
S3 s3:list-buckets, s3:list-objects, s3:delete-object, s3:create-folder, s3:list-profiles, s3:list-roles
SFTP sftp:list, sftp:mkdir, sftp:delete, sftp:rename
Network filesystems netfs:list, netfs:mkdir, netfs:delete, netfs:rename
Rsync rsync:list, rsync:mkdir, rsync:delete, rsync:rename
Taildrop taildrop:status, taildrop:list-targets, taildrop:receive
Transfers transfer:start, transfer:cancel, transfer:clear, transfer:list
Events transfer:progress, transfer:complete, transfer:error
Window window:close, window:minimize, window:maximize

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors